编程 R3F v9 配 React 19:StrictMode 继承与贴图 sRGB 是两个会改行为的破坏性变更

2026-09-26 00:04:37

R3F v9 配 React 19:StrictMode 继承与贴图 sRGB 是两个会改行为的破坏性变更

版本配对与安装

@react-three/fiber 必须和某个 React 大版本配对,规则和 react-dom / react-native 一样:

  • @react-three/fiber@8 配 react@18
  • @react-three/fiber@9 配 react@19

两者同时支持 ReactDOM 和 React Native。

npm install three @react-three/fiber

Vite 下开箱即用:

npm create vite my-app
cd my-app
npm install three @react-three/fiber
npm run dev

Next.js 基本开箱即用,但会碰到 three 生态里未转译的 add-on。Next.js 13.1 及以后,在 next.config.js 的 transpilePackages 里加 'three';Next.js 13.0 及更早用 next-transpile-modules 包。没有构建工具时,用 esm.sh 的浏览器 ES Module 加 htm 提供类 JSX 语法。

React Native 从 v8 起支持,从 @react-three/fiber/native 导入,底层用 expo-gl 和 expo-asset 做 WebGL2 绑定与 Metro、three.js loader 的衔接:

npx create-expo-app
expo install expo-gl
npm install three @react-three/fiber

如果用 useLoader 或 drei 的 useGLTF / useTexture,需要在 metro.config.js 的 resolver.assetExts 加入 'glb','gltf','png','jpg'。iOS 模拟器的 OpenGL ES 支持常不完整或不可靠,渲染 3D 会直接 EXC_BAD_ACCESS 崩溃,务必在真机(iOS 16 及以上)测试。导入路径要用 @react-three/fiber/native 或 @react-three/drei/native,否则类型提示是错的。

Canvas 与原生元素

把 `` 放进 React 树即可。Canvas 背后创建了 Scene 和 Camera,并每帧渲染,不需要自己写渲染循环。它会自适应父节点尺寸,要控制大小就改父容器的宽高。

小写 `` 等价于 new THREE.Mesh(),不需要 import:所有 three.js 对象都被当作原生 JSX 元素处理,Fiber 组件用 three.js 名称的驼峰小写形式。Mesh 承载 geometry 和 material:


这两个子元素会自动 attach 到父 mesh 上。等价的原生写法:

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(width, height)
document.querySelector('#canvas-container').appendChild(renderer.domElement)
const mesh = new THREE.Mesh()
mesh.geometry = new THREE.BoxGeometry()
mesh.material = new THREE.MeshStandardMaterial()
scene.add(mesh)
function animate(){ requestAnimationFrame(animate); renderer.render(scene, camera) }
animate()

构造参数走 args,始终是数组:new THREE.BoxGeometry(2,2,2) 对应 ``。注意每次改动 args,对象都会被重建。

灯光用法一致:、。在 Fiber 组件上设置任何 prop,就是给同名 three.js 实例属性赋值,intensity={0.1} 等价于 light.intensity = 0.1。对有 .set() 方法的属性可以简写:light.position.set(0,0,5)、light.color.set('red') 等价于直接传 position={[0,0,5]}、color="red"。

v9 迁移:Features

useLoader 现在可接收 loader 实例,便于复用外部 loader 做可控的对象池与初始化:

const loader = new GLTFLoader()
useLoader(loader, '/path/to/model.glb')

extend 签名简化:把单个 three.js 类传进去可直接产出组件,向后兼容并减少 TS 样板与 JSX 冲突。

const Controls = extend(OrbitControls)

库建议迁移到该签名,避免内部组件与用户声明冲突。

Canvas 的 gl prop 回调现在传的是构造参数而非 canvas 引用:原来 gl={(canvas) => new WebGLRenderer({ canvas })} 改为 gl={(props) => new WebGLRenderer(props)}。回调还可返回 Promise,用于异步构造的渲染器。

WebGPU 方面,新版 three 已含 WebGPU 渲染器,仍在开发中、未与 three 全部特性完全向后兼容,需要异步初始化,R3F 允许 gl 返回 Promise 来简化:

import * as THREE from 'three/webgpu'
extend(THREE)
{
const renderer = new THREE.WebGPURenderer(props)
await renderer.init()
return renderer
}} />

v9 迁移:Fixes 与 TypeScript

贴图色彩管理是行为改动最大的一处:移除了对贴图 props 的自动 sRGB 转换。内置材质的颜色贴图现在自动处理,与原生 three.js 行为对齐,避免数据贴图(法线、displacement 等)被破坏或变成非线性。自定义材质/着色器需手动标注:texture.colorSpace = THREE.SRGBColorSpace,或 JSX 里写 texture-colorSpace={THREE.SRGBColorSpace}。

Suspense 与副作用:attach 与构造函数副作用(如 controls 添加事件监听)在挂起期间不再反复触发且缺少清理。切换 args 与 primitive 时,对数组/迭代器等结构化 children 的元素切换已改进(React 支持数组与异步迭代器);此前共享同一 object 的 primitive 可能乱序更新,或连同子节点一起被移出场景。

TypeScript 变更:

  • Canvas 的 Props 改名为 CanvasProps(v8 曾做别名以兼容),v9 移除 Props。
  • 动态 JSX 类型:自 v8 起有 ThreeElements 接口目录,v9 起自动把 three API 映射为 JSX 类型。硬编码导出的 MeshProps 等已移除,改用 ThreeElements['mesh'];Color / Vector3 等辅助类型保留。
  • Node 辅助类型 Node / Object3DNode / BufferGeometryNode / MaterialNode / LightNode 合并为 ThreeElement,接受单个类型代表被扩展元素的实例。
  • 因 React 弃用了 global JSX 命名空间,自定义元素改为:
declare module '@react-three/fiber' {
interface ThreeElements {
customElement: ThreeElement
}
}
extend({ CustomElement })

StrictMode 与 act

StrictMode 现在正确地继承自父渲染器(如 react-dom)。此前在 react-dom 根里的 `` 不会影响 R3F canvas,必须在 canvas 内再声明一次,现在不需要。这个变更会影响应用行为:遇到以前能跑、现在失败的情况,先在 dev 再在 prod 里 profile;若 prod 正常,说明 strict mode 揪出了你代码里的副作用。另外 act 现在从 react 本身导出,可用于所有渲染器。

生态与取舍

辅助组件与周边基本都在 pmndrs 名下:@react-three/drei(实用辅助组件,自成生态)、@react-three/gltfjsx(GLTF 转 JSX 组件)、@react-three/postprocessing(后处理)、@react-three/test-renderer(node 单元测试)、@react-three/flex、@react-three/xr(VR/AR)、@react-three/csg、@react-three/rapier / cannon / p2(物理)、@react-three/a11y(可访问性)、@react-three/gpu-pathtracer(路径追踪)、create-r3f-app(next 模板)、lamina(着色器材质)、zustand / jotai / valtio(状态管理)、react-spring / framer-motion-3d(动画)、use-gesture(手势)、leva(GUI)、maath(数学辅助)、miniplex(ECS)。

选型上,几何体、材质、光源这类基础能力直接用 R3F 原生小写元素即可;只有需要组合封装、复用逻辑时才引入 drei 之类的抽象,避免为了几个 helper 把整条依赖链带进来。

仓库与文档:https://github.com/pmndrs/react-three-fiber ,文档 https://r3f.docs.pmnd.rs/ ,drei https://github.com/pmndrs/drei 。

推荐文章

程序员茄子在线接单