react-three-fiber 完全指南:用 React 声明式渲染 Three.js 3D 场景
【免费下载链接】react-three-fiber🇨🇭 A React renderer for Three.js项目地址: https://gitcode.com/GitHub_Trending/re/react-three-fiber
react-three-fiber(简称 R3F)是一个面向 Three.js 的 React renderer(渲染器),它让你可以用声明式的 JSX 组件构建 3D 场景,组件可复用、自带状态、可交互,并且能无缝融入 React 的整个生态系统。本文将基于本仓库(@react-three/fiberv9)的官方 README 与核心源码,系统讲解它的安装配置、核心概念、JSX/TypeScript/React Native 三端实战示例,并深入其 Canvas、Hooks、Store 等底层实现原理,读完即可上手写出自己的第一个可交互 3D 应用。
什么是 react-three-fiber
一句话概括:react-three-fiber 是一个把 Three.js 变成 React 组件的渲染器。它的核心理念是——用声明式、可复用、自带状态的组件来构建场景,这些组件能够响应状态变化、天然可交互,并参与 React 生态:
- 复用 React 的组件化思想组织 3D 对象树;
- 组件内部的
useState、useRef、useFrame等让场景拥有自己的行为逻辑; - 与 React 的 Suspense、事件系统、状态管理库无缝配合。
Three.js 中的所有导出类型对 three-fiber 而言都是"原生"的:<mesh />会动态地变成new THREE.Mesh(),<ambientLight />变成new THREE.AmbientLight(),无需任何手动注册。
安装与版本配对
安装只需一条命令:
npm install three @types/three @react-three/fiber这里安装了三个包:Three.js 本体、它的 TypeScript 类型定义,以及 react-three-fiber 渲染器。
警告:版本配对是硬性要求Three-fiber 是一个 React 渲染器,它必须与某个 React 大版本配对使用,就像 react-dom、react-native 一样。例如:
@react-three/fiber@8配对react@18,@react-three/fiber@9配对react@19。
本仓库中的 packages/fiber/package.json 给出了当前版本(9.6.1)的精确配对关系,可作为对照:
{ "peerDependencies": { "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" } }可以看到,v9 要求 React 19(>=19 <19.3)、Three.js>=0.156;react-dom与react-native均为可选 peer 依赖,这正是同一份代码可以同时跑在 Web 与 React Native 上的原因。安装前请务必检查你的 React 主版本,避免渲染器与运行时版本错配导致的异常。
常见疑问解答(FAQ)
README 用三个问答快速回应了开发者最常见的顾虑:
Q:它有局限吗?
没有。凡是在 Three.js 中能工作的东西,在这里都能工作,无一例外。
Q:它比纯 Three.js 慢吗?
不慢。没有额外开销,组件在 React 之外渲染。得益于 React 的调度能力,它在规模上反而优于 Three.js。
Q:它能跟上 Three.js 频繁的版本更新吗?
能。它只是把 Three.js 表达为 JSX,
<mesh />动态地变成new THREE.Mesh()。如果 Three.js 新版本新增、移除或修改了某个特性,它会立刻可用,无需等待本库发版。
第三条从源码上可以得到印证:在 packages/fiber/src/web/Canvas.tsx 中,Canvas 首次渲染时会执行extend(THREE as any),把整个 THREE 命名空间注册进 JSX 元素目录(catalogue),之后所有 three 类型都可以直接以 JSX 标签使用;用户也可以通过extend把自己的自定义类注册为 JSX 元素。这也是"新特性即时可用"的底层原因——目录是动态、运行时构建的。
它长什么样:从零构建可交互 3D 组件
README 用一张动图和一段代码展示了核心体验:一个自带状态、响应鼠标输入、参与渲染循环的可复用组件。
Web(JSX)示例
import { createRoot } from 'react-dom/client' import React, { useRef, useState } from 'react' import { Canvas, useFrame } from '@react-three/fiber' function Box(props) { // This reference gives us direct access to the THREE.Mesh object const ref = useRef() // Hold state for hovered and clicked events const [hovered, hover] = useState(false) const [clicked, click] = useState(false) // Subscribe this component to the render-loop, rotate the mesh every frame useFrame((state, delta) => (ref.current.rotation.x += delta)) // Return the view, these are regular Threejs elements expressed in JSX return ( <mesh {...props} ref={ref} scale={clicked ? 1.5 : 1} onClick={(event) => click(!clicked)} onPointerOver={(event) => hover(true)} onPointerOut={(event) => hover(false)}> <boxGeometry args={[1, 1, 1]} /> <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} /> </mesh> ) } createRoot(document.getElementById('root')).render( <Canvas> <ambientLight intensity={Math.PI / 2} /> <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} /> <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} /> <Box position={[-1.2, 0, 0]} /> <Box position={[1.2, 0, 0]} /> </Canvas>, )逐段拆解这段"麻雀虽小五脏俱全"的示例:
useRef()拿到 Three.js 实例:ref直接指向底层的THREE.Mesh对象,这是操作原生 three 对象的标准通道;useState()承载交互状态:hovered(悬停)与clicked(点击)驱动颜色与缩放变化;useFrame((state, delta) => ...)订阅渲染循环:每一帧执行回调,delta是距上一帧的时间差,用它做旋转动画可保证不同帧率下速度一致;- JSX 事件绑定:
onClick、onPointerOver、onPointerOut直接写在元素上,与 DOM 事件写法一致; - 构造参数用
args传入:<boxGeometry args={[1, 1, 1]} />等价于new THREE.BoxGeometry(1, 1, 1); - props 即属性:
position={[10, 10, 10]}、angle={0.15}、intensity={Math.PI}等会直接应用到对应 three 实例上。
useFrame 的底层实现
从源码看,useFrame的核心逻辑位于 packages/fiber/src/core/hooks.tsx:
export function useFrame(callback: RenderCallback, renderPriority: number = 0): null { const store = useStore() const subscribe = store.getState().internal.subscribe const ref = useMutableCallback(callback) useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]) return null }它通过useStore()拿到 Canvas 的全局 Store,把回调注册进internal.subscribers订阅列表;组件卸载时自动取消订阅。renderPriority参数值得一提:默认0表示"只订阅更新逻辑、渲染仍由 R3F 自动驱动";若传入正数(如useFrame(cb, 1)),则该组件会接管渲染优先级——订阅者按优先级从低到高排序、最高优先级最后渲染,同时内部自动渲染会被暂停(见 packages/fiber/src/core/store.ts 中internal.priority的手动标志位逻辑),这一机制常用于实现"后处理覆盖层"等需要自定义渲染顺序的场景。
TypeScript 示例
安装类型后即可获得完整的类型提示与检查:
npm install @types/threeimport * as THREE from 'three' import { createRoot } from 'react-dom/client' import React, { useRef, useState } from 'react' import { Canvas, useFrame, ThreeElements } from '@react-three/fiber' function Box(props: ThreeElements['mesh']) { const ref = useRef<THREE.Mesh>(null!) const [hovered, hover] = useState(false) const [clicked, click] = useState(false) useFrame((state, delta) => (ref.current.rotation.x += delta)) return ( <mesh {...props} ref={ref} scale={clicked ? 1.5 : 1} onClick={(event) => click(!clicked)} onPointerOver={(event) => hover(true)} onPointerOut={(event) => hover(false)}> <boxGeometry args={[1, 1, 1]} /> <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} /> </mesh> ) } createRoot(document.getElementById('root') as HTMLElement).render( <Canvas> <ambientLight intensity={Math.PI / 2} /> <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} /> <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} /> <Box position={[-1.2, 0, 0]} /> <Box position={[1.2, 0, 0]} /> </Canvas>, )关键差异点:
ThreeElements['mesh']提供 JSX 元素的 props 类型(ThreeElements是 R3F 导出的元素类型映射);useRef<THREE.Mesh>(null!)显式声明 ref 指向原生 Mesh 实例;- 事件回调、几何体、材质全部享受 TypeScript 推导。
React Native 示例
同样的代码可以跑在 React Native 上,只需把导入路径换成@react-three/fiber/native。该示例基于 React 18 与expo-cli(也可用 RN 模板或react-nativeCLI 创建裸项目):
# Install expo-cli, this will create our app npm install expo-cli -g # Create app and cd into it expo init my-app cd my-app # Install dependencies npm install three @react-three/fiber@beta react@rc # Start expo start如果使用useLoader或 Drei 的useGLTF、useTexture等抽象加载资源,可能需要配置 Metro 打包器来识别资产文件:
// metro.config.js module.exports = { resolver: { sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs'], assetExts: ['glb', 'png', 'jpg'], }, }Native 端组件与 Web 端几乎一致(useFrame订阅渲染循环、onClick/onPointerOver/onPointerOut交互、args构造参数),唯一区别是入口组件与导入路径。从仓库结构看,Native 支持由 packages/fiber/src/native/Canvas.tsx 与 packages/fiber/src/native/events.ts 提供,@react-three/fiber/native的入口在 packages/fiber/src/native.tsx,Web 入口则在 packages/fiber/src/index.tsx,二者共享同一套 core(reconciler、store、hooks、loop),这正是"一次编写、两端运行"的架构基础。
开始前的准备:First Steps
README 提醒,上手 R3F 前你需要同时熟悉 React 与 Three.js。建议按以下顺序学习:
- 掌握 Three.js 基础:理解 scene(场景)、camera(相机)、mesh(网格)、geometry(几何体)、material(材质)这几个核心概念;
- 对照学习:理解了基础概念后,动手改造上文示例中的
<mesh />、<ambientLight />等 JSX 元素——Three.js 的所有导出对 three-fiber 都是原生可用的; - 查阅 API:尝试修改参数,翻阅官方 API 文档了解各个设置项与 Hooks 的作用;
- 推荐资料:Three.js 官方文档与示例、Discover Three.js 的 Tips and Tricks 章节、Bruno Simon 的 Three.js Journey 课程(其内容包含专门的 R3F 章节)都是不错的学习资源。
核心 API 与工作原理速览
Canvas:一切的入口
<Canvas>是最顶层的组件,它负责创建 WebGL 渲染器、场景、相机与渲染循环。从 packages/fiber/src/web/Canvas.tsx 可以看到它实际渲染的结构:外层 div(position: relative; width/height: 100%)内嵌测量容器与<canvas>元素。它使用react-use-measure自动测量父容器尺寸并响应 resize,因此Canvas 的父元素必须拥有确定的宽高,这是新手最常见的"黑屏"原因。
关键配置项(Canvas props)
结合 packages/fiber/src/core/renderer.tsx 中RenderProps的定义,常用配置如下:
| 配置项 | 类型 | 说明 |
|---|---|---|
gl | Renderer \| 函数 \| 部分 WebGLRenderer 属性 | 自定义渲染器实例,或传入构造默认渲染器的属性(如{ antialias: true }) |
shadows | boolean \| 'basic' \| 'percentage' \| 'soft' \| 'variance' \| 部分属性 | 启用阴影,默认 PCFSoft,可传字符串或gl.shadowMap选项细调 |
dpr | number \| [min, max] | 目标像素比,可用数组限定范围(如[1, 2])实现高分屏适配 |
frameloop | 'always' \| 'demand' \| 'never' | 渲染模式:始终渲染 / 仅在状态变化时按需渲染 / 完全手动控制 |
camera | 相机实例或属性对象 | 可传{ fov, near, far, position }或manual: true手动接管投影 |
orthographic | boolean | 使用正交相机而非默认透视相机 |
legacy/linear/flat | boolean | 分别用于关闭 r139 颜色管理、关闭 sRGB 编码与伽马校正、改用NoToneMapping |
performance | Partial<Performance> | 自适应性能参数(min、max、debounce等) |
raycaster/scene/events | 各种 | 自定义 Raycaster / Scene / 事件管理器 |
onCreated | (state) => void | 画布渲染完成(尚未提交)后的回调,常在此处访问state |
onPointerMissed | (event) => void | 点击未命中任何对象时的回调 |
Store:响应式的内部状态
R3F 的内部状态(RootState)是一个基于zustand的响应式 Store,字段定义在 packages/fiber/src/core/store.ts,主要包括:
gl:THREE.WebGLRenderer实例;camera、scene、raycaster、clock:默认相机、场景、射线拾取器与时钟;size:画布响应式像素尺寸(width、height、top、left);viewport:Three.js 世界单位下的视口尺寸(含factor、distance、aspect,以及按相机/目标点/尺寸重新计算的getCurrentViewport());pointer:归一化后的指针坐标(mouse为弃用别名);frameloop、performance:渲染模式与自适应性能;invalidate()/advance():手动请求重绘 / 手动推进一帧(frameloop='demand'与'never'模式下配合使用)。
常用 Hooks
useThree(selector, equalityFn):以选择器读取 Store 状态,如const { gl, camera } = useThree();useFrame(cb, priority):订阅渲染循环(见上文源码分析);useLoader(Loader, input, extensions?, onProgress?):基于 Suspense 同步加载并缓存资源,useLoader.preload可预加载、useLoader.clear可清理缓存(见 packages/fiber/src/core/hooks.tsx);useGraph(object):从已加载对象构建nodes/materials命名图(GLTF 场景常用);useStore():获取 zustand Store 本身,适合做瞬时更新;useInstanceHandle(ref):访问元素的底层 R3F Instance(react 内部字段的逃生舱口,注意跨版本可能变化)。
生态与使用案例
README 展示了围绕 three-fiber 形成的庞大生态,此处列举与本主题最直接相关的一批(均为社区项目,可自行检索):
- 场景增强:
@react-three/drei(实用辅助组件集合,本身即是一个生态)、@react-three/gltfjsx(把 GLTF 转成 JSX 组件)、@react-three/postprocessing(后处理特效); - 交互与 UI:
@react-three/uikit(WebGL 渲染的 UI 组件)、@react-three/flex(flexbox 布局)、@react-three/a11y(无障碍)、use-gesture(鼠标/触摸手势)、leva(GUI 调试面板)、triplex(可视化编辑器); - 物理与渲染:
@react-three/rapier、@react-three/cannon、@react-three/p2(3D/3D/2D 物理)、@react-three/gpu-pathtracer(路径追踪)、lamina(分层着色器材质); - XR 与离屏:
@react-three/xr(VR/AR 控制器与事件)、@react-three/offscreen(worker 离屏画布)、@react-three/test-renderer(Node 环境单元测试,仓库内实现见 packages/test-renderer); - 状态与动画:
zustand、jotai、valtio(三种状态管理风格)、react-spring(弹簧物理动画)、framer-motion-3d; - 工程化:
create-r3f-app next(Next.js 启动模板)、maath(数学工具)、miniplex(ECS 实体管理)、composer-suite(着色器/粒子/游戏机制组合)。
在实际生产中,react-three-fiber 已被设计机构(如 vercel、basement studio、studio freight、14 islands、ueno)、PCB 设计工具(flux.ai)、3D 建模器(colorful.app、bezi)、头像配置器(readyplayer.me)、房产平台(zillow)、AI 模型与天空盒生成(lumalabs.ai/genie、skybox.blockadelabs)、CAD 软件(buerli.io、getencube、glowbuzzer)以及编辑器(triplex、theatrejs)等大量项目采用。
小结
react-three-fiber 的价值在于:它把 Three.js 庞大的命令式 API 折叠进 React 的声明式组件模型,让你用熟悉的 JSX、Hooks 与状态管理就能构建高性能、可维护的 3D 应用。记住三个要点即可快速上手:
- 版本配对:v9 配对 React 19、Three.js ≥ 0.156,安装前先核对;
- 一切皆组件:
<mesh />、<boxGeometry />、<ambientLight />就是 three 对象的 JSX 表达,args传构造参数、props 即属性、on*即事件; - 从 Canvas 开始:给
<Canvas>一个确定尺寸的父容器,用useFrame驱动动画、用useThree访问全局状态、用useLoader配合 Suspense 加载资源,即可逐步构建完整的 3D 应用。
【免费下载链接】react-three-fiber🇨🇭 A React renderer for Three.js项目地址: https://gitcode.com/GitHub_Trending/re/react-three-fiber
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考