perfprmance 1x1
Performance pitfalls 性能陷阱
three.js中最重要的注意点是创建对象可能会很耗费资源,因此在挂载/卸载物体之前请三思!每个放入场景中的材质或光源都需要编译,每个创建的几何体都需要处理。如果可以的话,请共享材质和几何体,可以在全局范围或局部范围内实现。
Tips and Tricks 技巧和窍门
这里有一篇很好的总结性的文章: https://discoverthreejs.com/tips-and-tricks/
在条件允许的情况下,尽可能地使用实例去实现元素的展示。
Avoid setState in loops 不要在循环中进行setState
简而言之,不要在useFrame里面使用setState相关操作。
—
Threejs有一个渲染循环,它的工作方式与DOM不同。通过更新数据在useFrame中进行快速渲染。useFrame是每个组件的渲染循环。
—
仅仅连续设置值是不够的,需要帧时间差。例如,不要考虑position.x += 0.1,而应该考虑position.x += delta,否则你的项目将在不同速度下运行,具体取决于最终用户的系统。在threejs中,许多更新需要与更新标志(.needsUpdate = true)或命令式函数(.updateProjectionMatrix())配对使用。
—
你可能会尝试在useFrame内部使用setState,但没有理由这样做。你这样做只会导致原本简单的数据更新变成了触发复杂的React调度机制和组件渲染机制。
❌setState in loops is bad 在循环中使用setState不好
✅Instead, just mutate, use deltas 要进行改变设置的时候用deltas
❌setState in useFrame is bad 在useFrame中使用setState不好
❌setState in fast events is bad 在设置fast events时使用setState不好
所谓fast events就是react-three-fiber自己封装的,针对三维场景或者元素进行操作的一些事件,这些事件并不是DOM事件
通常情况下,您应该优先考虑使用useFrame。只要组件是唯一进行改变的实体,那么修改props是安全的。使用useFrame时,使用增量delta而不是固定值,以使您的应用程序独立于刷新率并在任何地方以相同的速度运行!
使用fast events也是类似的
如果必须使用定时器,请同时使用Ref,但请记住这不能保证独立于设备显示刷新率。
Handle animations in loops 在循环中处理动画
帧循环(frame loop)是您应该放置动画的地方。例如,可以使用 lerp 或 damp。
✅ or react-spring
或者,使用动画库。React-spring 有自己的帧循环,并在 React 外部进行动画处理。Framer-motion 是另一个受欢迎的替代方案。
Do not bind to fast state reactively
不要和快速更新状态的行为关联
使用状态管理器和选择性状态是可以的,但对于出现快速更新的情况,由于与上述原因相同,不应使用。
❌ Don't bind reactive fast-state
✅ Fetch state directly 直接获取并通过Ref方式来设置
这里使用的是Zustand这个库(和Redux类似作用)
Don't mount indiscriminately 不要随意挂载组件
在 threejs 中,通常根本不需要重新挂载组件,请参见 discover-three 中的“disposing of things”部分。这是因为缓冲区和材质会被重新初始化/编译,这可能是昂贵的。
❌ Avoid mounting runtime 会导致重新挂载
✅ use startTransition for expensive ops
对于一些耗费性能的操作可以使用startTransition来处理
React 18 引入了 startTransition 和 useTransition API 来调整和安排工作和状态更新。使用它们来降低耗费性能的操作的优先级。
自 Fiber 的第 8 版起,默认情况下 Canvas 使用了并发模式,这意味着 React 将安排和推迟耗费性能的操作。你不需要做任何事情,但你可以尝试一下实验性的调度器,并查看将操作标记为较低优先级是否有所不同。
Don't re-create objects in loops
不要在循环中重复创建三维相关数据
尽量避免给垃圾收集器创建太多的负担,可以时尽量重复使用对象!
❌ Bad news for the GC 对于垃圾收集器不好的消息
✅ Better re-use object 更好的重用方式
每秒钟创建一个新的向量,这将分配内存并迫使垃圾收集器最终介入。
在全局或本地环境中设置可重用对象,现在垃圾收集器将不会介入。
useLoader instead of plain loaders
使用useLoader代替一般加载
Three.js 加载器使您能够异步加载资源(模型、纹理等),但如果您不重用资源,它可能会很快变得有问题。
❌ No re-use is bad for perf 不重用对象会影响性能
这会为每个组件实例重新获取和解析。
相反,使用 useLoader
,它可以缓存资源并在整个场景中使它们可用。
✅ Cache and re-use objects 缓存并重用加载的数据
关于 GLTF,尽可能使用 GLTFJSX,它可以创建不可变的 JSX 模型数据,甚至允许您重用完整的模型。