react-three/test-rendererRTTR版本演进与 API 全解析为 react-three-fiber 场景图编写 Node 环境测试【免费下载链接】react-three-fiber A React renderer for Three.js项目地址: https://gitcode.com/GitHub_Trending/re/react-three-fiberreact-three/test-renderer简称 RTTR是 react-three-fiber 官方仓库中用于在 Node 环境下测试 Three.js 场景图的实验性 React 渲染器它基于react-three/fiber的 reconciler把mesh、boxGeometry等 JSX 元素渲染成真实的THREE对象树而无需 WebGL 与浏览器。本文以 packages/test-renderer/CHANGELOG.md 的版本演进为脉络结合源码与测试完整讲解 RTTR 的安装使用、全部 API、底层实现原理以及从 v6 到 v9 每个关键版本背后的技术决策帮助你为自己的 R3F 应用写出可维护的单元测试。一、RTTR 要解决的问题使用react-three/fiber开发 WebGL 体验后你想用react-dom测试组件——但立刻会发现一个矛盾THREE元素根本不在 DOM 里。即使能拿到容器和canvas也看不到场景内部的对象树。原因是 R3F 用自己的 reconciler 渲染到独立的 React root 中见 fiber/src/core/reconciler.tsxreact-dom无从感知 scene graph。RTTR 的解法见 packages/test-renderer/README.md是在react-three/fiber之上再套一层测试渲染器把 R3F 渲染出的场景图包装成带查询工具的 test instance。本质上它让你无需 WebGL 和浏览器即可抓取 Three.js 场景图的快照并对其断言默认不会创建真实的THREE.WebGLRenderer也没有渲染循环但会完整渲染 scene graph所有 R3F 元素都会被实例化为真实的THREE对象测试库无关可与 jest、jasmine 等任意断言框架配合。从源码看RTTR 的入口 src/index.tsx 会在模块加载时调用extend(THREE as any)把全部 Three.js 类注册进 R3F 的 catalogue随后通过createRoot(canvas)_root.configure({ frameloop: never, ... })挂载到 R3F reconciler 上再以act()包裹_root.render(element)完成首轮渲染。二、安装与最小示例RTTR 以独立包发布安装时需同时安装其 peer 依赖见 packages/test-renderer/package.json# 运行时依赖 yarn add react-three/fiber three # 测试依赖 yarn add -D react-three/test-renderer当前仓库中 RTTR 的 peerDependencies 为react ^19.0.0、react-three/fiber 9.0.0、three 0.156说明其 v9 系列要求 React 19 与 fiber v9。仓库根目录的 jest.config.js 与 babel.config.js 展示了 monorepo 内测试的基础配置方式。最小示例来自 READMEimport ReactThreeTestRenderer from react-three/test-renderer const renderer await ReactThreeTestRenderer.create( mesh boxGeometry args{[2, 2]} / meshStandardMaterial args{[ { color: 0x0000ff, }, ]} / /mesh, ) // 通过 TestInstance 与 Scene Graph 进行断言 console.log(renderer.toGraph())create是异步的返回的renderer对象提供了scene、toTree()、toGraph()、fireEvent()、advanceFrames()、update()、unmount()、getInstance()等能力类型定义见 src/types/public.ts。三、Renderer 核心 API 详解以下 API 的完整说明位于 packages/test-renderer/markdown/rttr.md。3.1create(element, options)const renderer ReactThreeTestRenderer.create(element, options)创建一个渲染器实例。默认无真实 WebGL、无循环但场景图会被完整渲染。CreateOptions由CreateCanvasParameters与 R3F 的RenderPropsHTMLCanvasElement合并而成interface CreateOptions extends RenderPropsHTMLCanvasElement { width?: number // canvas 宽度默认 1280 height?: number // canvas 高度默认 800 }在 src/index.tsx 的create实现中默认以frameloop: never关闭循环以{ width: options?.width ?? 1280, height: options?.height ?? 800, top: 0, left: 0 }初始化尺寸并显式将events: undefined传入事件系统由 RTTR 自己的fireEvent模拟驱动。3.2scenerenderer.scene返回根 React Three Test Instance是后续所有查询的起点。实现上取自 store 中场景对象的__r3f实例并经wrapFiber包装src/createTestInstance.ts 用WeakMap保证同一 fiber 只包装一次。3.3getInstance()renderer.getInstance()返回根 three 元素对应的实例若根元素是函数组件无实例则不可用。源码实现沿 fiber 树向下遍历child直到找到带stateNode的节点再通过reconciler.getPublicRootInstance(root)取出公开实例。3.4toTree()与toGraph()renderer.toTree() renderer.toGraph()toTree()返回类似react-test-renderer的树结构对象含所有 React 组件元素、props 与 children见 src/helpers/tree.ts。节点type为对象类型名首字母小写如meshprops保留原始 props。toGraph()返回场景图Scene Graph结构对象不含attach等附加元素见 src/helpers/graph.ts。每个节点包含type、name、children三元组name为空时使用空字符串。3.5fireEvent(testInstance, eventName, mockEventData)renderer.fireEvent(testInstance, eventName, mockEventData)在树中指定元素上触发事件。事件名遵循 camelCase如pointerUp也可直接传 handler 名如onPointerUp。第三个参数会并入传给事件 handler 的MockSyntheticEventtype MockSyntheticEvent { camera: Camera // 渲染场景的默认相机 stopPropagation: () void target: ReactThreeTestInstance currentTarget: ReactThreeTestInstance sourceEvent: MockEventData ...mockEventData // 你的自定义数据展开在此 }事件机制由 src/fireEvent.ts 与 src/helpers/events.ts 实现基于 R3F 的 store 构造并派发模拟事件。3.6advanceFrames(frames, delta)renderer.advanceFrames(frames, delta)手动推进 N 帧从而触发useFrame等 GL 渲染循环订阅者。delta为传给订阅者的增量时间可为数字或数组数组时逐帧取对应值。实现上遍历 store 的internal.subscribers对每个订阅者按帧数逐个调用其ref.current(state, delta)。3.7update(element)与unmount()renderer.update(element) renderer.unmount()update用新根元素重新渲染整棵树模拟一次 React 更新同 type 与 key 时原地更新而非重挂载unmount卸载整棵树并触发相应生命周期。两者均以act()包裹且更新已卸载 root 时会输出console.warn(RTTR: attempted to update an unmounted root!)提示。3.8act(callback)ReactThreeTestRenderer.act(callback)与react-test-renderer的act()语义一致用于准备组件以便断言。与 react-test-renderer 不同的是你无需手动用act包裹create和update它们内部已处理只需在涉及advanceFrames、异步更新等场景时显式调用。官方文档中的 jest 示例const Mesh () { const meshRef React.useRef() useFrame((_, delta) { meshRef.current.rotation.x delta }) return ( mesh ref{meshRef} boxGeometry args{[2, 2]} / meshBasicMaterial / /mesh ) } const renderer await ReactThreeTestRenderer.create(Mesh /) expect(renderer.scene.children[0].instance.rotation.x).toEqual(0) await ReactThreeTestRenderer.act(async () { await renderer.advanceFrames(2, 1) }) expect(renderer.scene.children[0].instance.rotation.x).toEqual(2)delta1、推进 2 帧rotation.x从 0 变为 2精确验证了useFrame的回调累加逻辑。3.9waitFor(callback, options)import { waitFor } from react-three/test-renderer await waitFor( () renderer.scene.findByProps({ ready: true }), { interval: 50, timeout: 5000 }, )v8.2.0 新增见 CHANGELOGa5ffb08e: feat(RTTR): waitFor util。轮询执行回调直到返回真值或耗尽超时默认每 50ms 一次、超时 5000ms全程包裹在act中适合等待异步加载完成。实现见 src/helpers/waitFor.ts回调返回true或undefined即视为满足超时抛出Timed out after ${timeout}ms.。四、ReactThreeTestInstance 查询 APIrenderer.scene及所有派生的节点都是ReactThreeTestInstance其全部属性和方法与react-test-renderer的 TestInstance 高度对齐完整文档见 packages/test-renderer/markdown/rttr-instance.md。4.1 属性属性说明instance返回该节点对应的 THREE 实例对象如THREE.MeshtypeTHREE 类型名如Scene、Meshprops当前传给元素的 props含attachgeometry这类由 reconciler 自动附加的隐藏 propsparent父 test instance无则返回nullchildren直接子节点不含geometry、material 等通过attach附加的对象默认exhaustive: false过滤allChildren全部子节点深度与toTree()一致包含所有 React 组件源码实现src/createTestInstance.tschildren默认过滤child.props.attach的节点同时当节点类型为primitive时会把 THREE.js 对象自身的object.children递归包装成虚拟实例一并返回type转小写以贴合 R3F 约定。4.2 方法方法行为find(predicate)找到恰好一个满足谓词的实例0 个或多个都会抛错findAll(predicate)找到所有满足谓词的实例无匹配返回[]findByType(type)按 THREE 类型名精确查找单个实例非恰好一个则抛错findAllByType(type)按类型名查找全部实例findByProps(props)按 props 子集匹配查找单个实例支持 RegExp 匹配值如{ name: /^mesh/i }findAllByProps(props)按 props 子集匹配查找全部实例匹配逻辑在 src/helpers/testInstance.tsexpectOne负责恰好一个的约束并生成带上下文的报错信息如with node type: Mesh、matching custom checker: ...matchProps递归比对 props 子集且支持正则。五、版本演进史从 CHANGELOG 看 RTTR 的技术路线packages/test-renderer/CHANGELOG.md 记录了 6.1.1 至 9.1.0 的全部变更。逐条阅读可以发现三条清晰的演进主线底层兼容性、渲染正确性、查询与工具能力。5.1 v9React 19 与 primitives 支持9.0.0Majorfeat: React 19 support——同步 R3F v9 全面切换 React 19因此 9.x 的 peerDependencies 固定为react ^19.0.0、react-three/fiber 9.0.0。9.1.0Minorfeat(RTTR): handle primitives in test-renderer and fix queries in TestInstances——这是源码中createVirtualInstance与toGraph/getChildren里primitive分支的直接来源见 src/createTestInstance.ts 与 src/helpers/graph.ts。此前primitive object{...}的 THREE 子对象无法通过 TestInstance 查询v9.1.0 之后它们会被包装成虚拟实例并纳入children、toGraph()。5.2 v8React 18 兼容与稳定性修复8.0.0Majorv8 major, react-18 compat与react-three/fiber8.0.0同步发布。8.1.x 系列集中补齐 Node 环境缺失的浏览器能力——support WebGL28.1.3、fallback to canvas shim8.1.4、implement HTMLCanvasElement.getContext8.1.5、transpile class properties8.1.2、backport traverse, update fixes8.1.1。这些正是 src/createTestCanvas.ts 与 src/WebGL2RenderingContext.ts 的成因Node 下无document时构造假 canvas 对象并兜底globalThis.WebGLRenderingContext/WebGL2RenderingContext。8.2.x 系列narrow React peer dep range8.2.2、republish with types8.2.3、include types in output8.2.4以及核心工具feat(RTTR): waitFor util8.2.0与set initial size for NaN in viewport8.2.1。8.0.x 补丁多与 fiber 8.0.x 的联动修复相关例如update viewport on camera changes8.0.15、infinite loop updating cam viewport8.0.16、allow invalidate to preempt more than 1 frame8.0.12、Add support for recoverable errors8.0.11体现了 RTTR 与 R3F 版本严格同步的发布策略。5.3 v7事件与 attach 体系的早期打磨7.0.xfix rttr didnt work with r1307.0.1适配 three r130、Add controls state field7.0.1、Allow elements to define attachFns7.0.4、Add useLoader.clear(Loader, input)7.0.5、Simplify useframe, support instanced event cancelation7.0.7、Fix diffProps dashed keys7.0.8、cleanup captured pointers when released7.0.23等。7.0.0Majorfix javascript interpreting renderpriority as positive——处理渲染优先级数值被 JS 解释为正数的问题。事件能力在此阶段成形6.2.2 的use more helpful name with event handling in rttr与 6.1.3 的exclude event functions from event data说明fireEvent机制在 v6 末期已具备雏形。5.4 测试用例对版本能力的验证仓库内的测试直接对应上述能力见 packages/test-renderer/src/testsRTTR.core.test.tsx验证 JSX 渲染、带 hooks 的组件、useTransition、空场景、复合组件与toGraph()结构expect(renderer.scene.children[0].type).toEqual(Mesh)等RTTR.events.test.tsx覆盖fireEvent与MockSyntheticEventRTTR.hooks.test.tsx覆盖useFrame与advanceFrames的帧推进行为RTTR.methods.test.tsx覆盖find/findAll/findByType/findByProps等查询方法的约束与错误信息快照文件 RTTR.core.test.tsx.snap 展示了toTree()/toGraph()的实际输出结构。六、底层实现原理6.1 无 WebGL 的 canvas shimsrc/createTestCanvas.ts 是 RTTR 能在 Node 运行的关键若环境有document.createElement直接创建真实 canvas否则构造假 canvas 对象提供style、空事件监听、clientWidth/clientHeight与返回WebGL2RenderingContext的getContext若存在globalThis.HTMLCanvasElement如 jsdom 环境则改写其原型getContext以webgl开头的 context 请求一律返回 mock 的WebGL2RenderingContext其余透传兜底注入globalThis.WebGLRenderingContext与globalThis.WebGL2RenderingContext避免 three 在初始化时因缺少全局 WebGL 类而报错。6.2 场景图序列化toGraph()src/helpers/graph.ts沿 R3F 实例的children递归输出{ type, name, children }遇到primitive节点时额外把 THREE 对象自身的object.childrenTHREE.Object3D[]处理进 children。toTree()src/helpers/tree.ts输出{ type, props, children }类型名经lowerCaseFirstLettersrc/helpers/strings.ts转为小写开头。6.3 渲染流程create的关键调用链src/index.tsxcreateCanvas(options) → 得到 mock canvas createRoot(canvas) → R3F reconciler 创建 root _root.configure({ frameloop:never, size, events: undefined }) mockRoots.get(canvas).store → 取 R3F 全局 store act(() _root.render(element)) → 在 act 中完成首次渲染 wrapFiber(scene.__r3f) → 得到 renderer.sceneadvanceFrames直接驱动store.getState().internal.subscribers逐帧回调这正是 R3F 渲染循环在测试环境下的手动时钟替代品。七、实战建议优先用findByType/findByProps而不是索引访问children顺序受attach过滤与 primitive 展开影响v9.1.0 起 primitive 子对象也会进入 children语义化查询更稳定。异步场景交给waitFor模型加载、useTransition等异步更新用waitFor(() ...)轮询而非固定setTimeout。动画逻辑用advanceFrames精确控制指定确定性的delta配合act断言避免真实时钟带来的 flaky 测试。注意版本对齐RTTR 与react-three/fiber严格同版发布CHANGELOG 中大量Updated dependencies条目可证升级时两者必须保持匹配当前 v9 系列要求 React 19。toGraph()与toTree()分工断言场景结构用toGraph()不含 attach 元素贴近 three 视角断言React 组件树用toTree()含全部 props 与组件。八、参考文件索引版本演进packages/test-renderer/CHANGELOG.md快速开始packages/test-renderer/README.md渲染器 APIpackages/test-renderer/markdown/rttr.md实例 APIpackages/test-renderer/markdown/rttr-instance.md核心实现packages/test-renderer/src/index.tsx、packages/test-renderer/src/createTestInstance.ts、packages/test-renderer/src/createTestCanvas.ts、packages/test-renderer/src/WebGL2RenderingContext.ts测试用例packages/test-renderer/src/tests/RTTR.core.test.tsx、RTTR.events.test.tsx、RTTR.hooks.test.tsx、RTTR.methods.test.tsx【免费下载链接】react-three-fiber A React renderer for Three.js项目地址: https://gitcode.com/GitHub_Trending/re/react-three-fiber创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考