react-three-fiber
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Three Fiber
React Three Fiber
React Three Fiber (R3F) is a React renderer for Three.js. Write declarative, component-based 3D scenes using JSX.
Library Versions (2026)
- React Three Fiber: v9.5+
- @react-three/drei: v9.116+
- @react-three/rapier: v2+
- @react-three/postprocessing: v3+
- React: 19+ (concurrent features supported)
React Three Fiber(R3F)是Three.js的React渲染器。你可以使用JSX编写声明式、基于组件的3D场景。
库版本(2026)
- React Three Fiber: v9.5+
- @react-three/drei: v9.116+
- @react-three/rapier: v2+
- @react-three/postprocessing: v3+
- React: 19+(支持并发特性)
Decision Frameworks
决策框架
When to Use R3F vs Vanilla Three.js
何时使用R3F vs 原生Three.js
Is your app already React-based?
→ Yes: Use R3F (natural integration)
→ No: Consider vanilla Three.js
Do you need React state management?
→ Yes: Use R3F (seamless state integration)
→ No: Either works
Is the 3D scene the entire app?
→ Yes: Either works (R3F has slight overhead)
→ No, mixed with React UI: Use R3F
Performance-critical with millions of objects?
→ Consider vanilla Three.js for maximum control
→ R3F is fine for 99% of use cases你的应用是否已基于React构建?
→ 是:使用R3F(自然集成)
→ 否:考虑原生Three.js
你是否需要React状态管理?
→ 是:使用R3F(无缝状态集成)
→ 否:两者均可
3D场景是否是整个应用?
→ 是:两者均可(R3F有轻微性能开销)
→ 否,与React UI混合:使用R3F
场景对性能要求极高且包含数百万个对象?
→ 考虑原生Three.js以获得最大控制权
→ R3F适用于99%的使用场景When to Use Which State Management
何时使用哪种状态管理方案
Local component state (single mesh color, hover)?
→ useState / useRef
Shared between few components (selected object)?
→ React Context or prop drilling
Global game/app state (score, inventory, settings)?
→ Zustand (recommended for R3F)
Complex state with actions/reducers?
→ Zustand with slices or Redux Toolkit
Need state persistence?
→ Zustand with persist middleware局部组件状态(单个网格颜色、悬浮状态)?
→ useState / useRef
少量组件间共享状态(选中的对象)?
→ React Context 或属性透传
全局游戏/应用状态(分数、库存、设置)?
→ Zustand(R3F推荐方案)
带动作/ reducer的复杂状态?
→ 带切片的Zustand 或 Redux Toolkit
需要状态持久化?
→ 带持久化中间件的ZustandWhen to Use Which Drei Component
何时使用哪种Drei组件
Need camera controls?
├─ Orbit around object → OrbitControls
├─ First-person → PointerLockControls
├─ Map/top-down → MapControls
└─ Smooth transitions → CameraControls
Need environment lighting?
├─ Quick preset → <Environment preset="sunset" />
├─ Custom HDR → <Environment files="/env.hdr" />
└─ Performance-sensitive → <Environment blur={0.5} />
Need text?
├─ 3D text in scene → <Text3D />
├─ 2D text billboards → <Text />
└─ HTML overlay → <Html />
Need loading?
├─ GLTF models → useGLTF
├─ Textures → useTexture
├─ Progress UI → useProgress
└─ Preloading → <Preload all />需要相机控制?
├─ 围绕对象旋转 → OrbitControls
├─ 第一人称视角 → PointerLockControls
├─ 地图/俯视视角 → MapControls
└─ 平滑过渡 → CameraControls
需要环境光照?
├─ 快速预设 → <Environment preset="sunset" />
├─ 自定义HDR → <Environment files="/env.hdr" />
└─ 对性能敏感 → <Environment blur={0.5} />
需要文本?
├─ 场景中的3D文本 → <Text3D />
├─ 2D文本广告牌 → <Text />
└─ HTML覆盖层 → <Html />
需要资源加载?
├─ GLTF模型 → useGLTF
├─ 纹理 → useTexture
├─ 进度UI → useProgress
└─ 预加载 → <Preload all />Core Setup
核心设置
jsx
import { Canvas } from '@react-three/fiber'
function App() {
return (
<Canvas
camera={{ position: [0, 0, 5], fov: 75 }}
gl={{ antialias: true }}
shadows
>
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 5]} castShadow />
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
</Canvas>
)
}jsx
import { Canvas } from '@react-three/fiber'
function App() {
return (
<Canvas
camera={{ position: [0, 0, 5], fov: 75 }}
gl={{ antialias: true }}
shadows
>
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 5]} castShadow />
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
</Canvas>
)
}Canvas Props
Canvas属性
jsx
<Canvas
camera={{ position, fov, near, far }} // Camera config
gl={{ antialias, alpha, powerPreference }} // WebGL context
shadows // Enable shadow maps
dpr={[1, 2]} // Device pixel ratio range
frameloop="demand" // "always" | "demand" | "never"
style={{ width: '100%', height: '100vh' }}
onCreated={({ gl, scene, camera }) => {}} // Access internals
/>jsx
<Canvas
camera={{ position, fov, near, far }} // 相机配置
gl={{ antialias, alpha, powerPreference }} // WebGL上下文
shadows // 启用阴影贴图
dpr={[1, 2]} // 设备像素比范围
frameloop="demand" // "always" | "demand" | "never"
style={{ width: '100%', height: '100vh' }}
onCreated={({ gl, scene, camera }) => {}} // 访问内部实例
/>Essential Hooks
核心Hooks
useFrame - Animation Loop
useFrame - 动画循环
jsx
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'
function SpinningBox() {
const meshRef = useRef()
useFrame((state, delta) => {
// state: { clock, camera, scene, gl, pointer, ... }
meshRef.current.rotation.y += delta
meshRef.current.position.y = Math.sin(state.clock.elapsedTime)
})
return (
<mesh ref={meshRef}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
)
}jsx
import { useFrame } from '@react-three/fiber'
import { useRef } from 'react'
function SpinningBox() {
const meshRef = useRef()
useFrame((state, delta) => {
// state: { clock, camera, scene, gl, pointer, ... }
meshRef.current.rotation.y += delta
meshRef.current.position.y = Math.sin(state.clock.elapsedTime)
})
return (
<mesh ref={meshRef}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
)
}useThree - Access Internals
useThree - 访问内部实例
jsx
import { useThree } from '@react-three/fiber'
function CameraLogger() {
const { camera, gl, scene, size, viewport, pointer } = useThree()
// viewport: { width, height } in Three.js units
// size: { width, height } in pixels
// pointer: normalized mouse position (-1 to 1)
return null
}jsx
import { useThree } from '@react-three/fiber'
function CameraLogger() {
const { camera, gl, scene, size, viewport, pointer } = useThree()
// viewport: Three.js单位下的{ width, height }
// size: 像素单位下的{ width, height }
// pointer: 归一化鼠标位置(-1到1)
return null
}useLoader - Asset Loading
useLoader - 资源加载
jsx
import { useLoader } from '@react-three/fiber'
import { TextureLoader } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
function Model() {
const gltf = useLoader(GLTFLoader, '/model.glb')
const texture = useLoader(TextureLoader, '/texture.jpg')
return <primitive object={gltf.scene} />
}jsx
import { useLoader } from '@react-three/fiber'
import { TextureLoader } from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
function Model() {
const gltf = useLoader(GLTFLoader, '/model.glb')
const texture = useLoader(TextureLoader, '/texture.jpg')
return <primitive object={gltf.scene} />
}JSX to Three.js Mapping
JSX与Three.js映射
jsx
// Three.js class → camelCase JSX element
// Constructor args → args prop (array)
// Properties → props
// THREE.Mesh
<mesh position={[0, 1, 0]} rotation={[0, Math.PI, 0]} scale={1.5}>
{/* THREE.BoxGeometry(1, 2, 1) */}
<boxGeometry args={[1, 2, 1]} />
{/* THREE.MeshStandardMaterial({ color: 'red' }) */}
<meshStandardMaterial color="red" roughness={0.5} />
</mesh>
// Nested properties use dash notation
<directionalLight
position={[5, 5, 5]}
castShadow
shadow-mapSize={[2048, 2048]}
shadow-camera-far={50}
/>
// Attach to parent properties
<mesh>
<boxGeometry />
<meshStandardMaterial>
<texture attach="map" image={img} />
</meshStandardMaterial>
</mesh>jsx
// Three.js类 → 小驼峰式JSX元素
// 构造函数参数 → args属性(数组)
// 属性 → 组件props
// THREE.Mesh
<mesh position={[0, 1, 0]} rotation={[0, Math.PI, 0]} scale={1.5}>
{/* THREE.BoxGeometry(1, 2, 1) */}
<boxGeometry args={[1, 2, 1]} />
{/* THREE.MeshStandardMaterial({ color: 'red' }) */}
<meshStandardMaterial color="red" roughness={0.5} />
</mesh>
// 嵌套属性使用短横线命名法
<directionalLight
position={[5, 5, 5]}
castShadow
shadow-mapSize={[2048, 2048]}
shadow-camera-far={50}
/>
// 附加到父元素属性
<mesh>
<boxGeometry />
<meshStandardMaterial>
<texture attach="map" image={img} />
</meshStandardMaterial>
</mesh>Drei - Essential Helpers
Drei - 核心工具库
Drei provides production-ready abstractions. Install:
@react-three/dreijsx
import {
OrbitControls,
PerspectiveCamera,
Environment,
useGLTF,
useTexture,
Text,
Html,
Float,
Stage,
ContactShadows,
Sky,
Stars,
} from '@react-three/drei'Drei提供生产级别的抽象封装。安装命令:
@react-three/dreijsx
import {
OrbitControls,
PerspectiveCamera,
Environment,
useGLTF,
useTexture,
Text,
Html,
Float,
Stage,
ContactShadows,
Sky,
Stars,
} from '@react-three/drei'Common Drei Components
常用Drei组件
jsx
// Camera controls
<OrbitControls enableDamping dampingFactor={0.05} />
// Environment lighting (HDR)
<Environment preset="sunset" background />
// Presets: apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse
// Load GLTF with draco support
const { scene, nodes, materials } = useGLTF('/model.glb')
useGLTF.preload('/model.glb')
// Load textures
const [colorMap, normalMap] = useTexture(['/color.jpg', '/normal.jpg'])
// 3D Text
<Text fontSize={1} color="white" anchorX="center" anchorY="middle">
Hello World
</Text>
// HTML overlay in 3D space
<Html position={[0, 2, 0]} center transform occlude>
<div className="label">Click me</div>
</Html>
// Floating animation
<Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
<mesh>...</mesh>
</Float>
// Quick studio lighting
<Stage environment="city" intensity={0.5}>
<Model />
</Stage>
// Soft shadows on ground
<ContactShadows position={[0, -0.5, 0]} opacity={0.5} blur={2} />jsx
// 相机控制
<OrbitControls enableDamping dampingFactor={0.05} />
// 环境光照(HDR)
<Environment preset="sunset" background />
// 预设值:apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse
// 加载带draco支持的GLTF模型
const { scene, nodes, materials } = useGLTF('/model.glb')
useGLTF.preload('/model.glb')
// 加载纹理
const [colorMap, normalMap] = useTexture(['/color.jpg', '/normal.jpg'])
// 3D文本
<Text fontSize={1} color="white" anchorX="center" anchorY="middle">
Hello World
</Text>
// 3D空间中的HTML覆盖层
<Html position={[0, 2, 0]} center transform occlude>
<div className="label">点击我</div>
</Html>
// 悬浮动画
<Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
<mesh>...</mesh>
</Float>
// 快速工作室光照
<Stage environment="city" intensity={0.5}>
<Model />
</Stage>
// 地面软阴影
<ContactShadows position={[0, -0.5, 0]} opacity={0.5} blur={2} />Event Handling
事件处理
jsx
<mesh
onClick={(e) => {
e.stopPropagation()
console.log('clicked', e.point, e.face)
}}
onPointerOver={(e) => setHovered(true)}
onPointerOut={(e) => setHovered(false)}
onPointerMove={(e) => console.log(e.point)}
onPointerDown={(e) => {}}
onPointerUp={(e) => {}}
onDoubleClick={(e) => {}}
onContextMenu={(e) => {}}
>Event object includes: , , , , , , , .
pointfacefaceIndexdistanceobjecteventObjectcamerarayjsx
<mesh
onClick={(e) => {
e.stopPropagation()
console.log('点击了', e.point, e.face)
}}
onPointerOver={(e) => setHovered(true)}
onPointerOut={(e) => setHovered(false)}
onPointerMove={(e) => console.log(e.point)}
onPointerDown={(e) => {}}
onPointerUp={(e) => {}}
onDoubleClick={(e) => {}}
onContextMenu={(e) => {}}
>事件对象包含:、、、、、、、。
pointfacefaceIndexdistanceobjecteventObjectcamerarayState Management
状态管理
With Zustand (Recommended)
使用Zustand(推荐)
jsx
import { create } from 'zustand'
const useStore = create((set) => ({
score: 0,
gameState: 'idle',
addScore: (points) => set((state) => ({ score: state.score + points })),
startGame: () => set({ gameState: 'playing' }),
}))
function ScoreDisplay() {
const score = useStore((state) => state.score)
return <Text>{score}</Text>
}
function GameLogic() {
const addScore = useStore((state) => state.addScore)
useFrame(() => {
// Game logic that calls addScore
})
return null
}jsx
import { create } from 'zustand'
const useStore = create((set) => ({
score: 0,
gameState: 'idle',
addScore: (points) => set((state) => ({ score: state.score + points })),
startGame: () => set({ gameState: 'playing' }),
}))
function ScoreDisplay() {
const score = useStore((state) => state.score)
return <Text>{score}</Text>
}
function GameLogic() {
const addScore = useStore((state) => state.addScore)
useFrame(() => {
// 调用addScore的游戏逻辑
})
return null
}With React Context
使用React Context
jsx
const GameContext = createContext()
function GameProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<GameContext.Provider value={{ state, dispatch }}>
{children}
</GameContext.Provider>
)
}
// Wrap Canvas content
<Canvas>
<GameProvider>
<Scene />
</GameProvider>
</Canvas>jsx
const GameContext = createContext()
function GameProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<GameContext.Provider value={{ state, dispatch }}>
{children}
</GameContext.Provider>
)
}
// 包裹Canvas内容
<Canvas>
<GameProvider>
<Scene />
</GameProvider>
</Canvas>Performance Patterns
性能优化模式
Instancing
实例化渲染
jsx
import { Instances, Instance } from '@react-three/drei'
function Boxes({ count = 1000 }) {
return (
<Instances limit={count}>
<boxGeometry />
<meshStandardMaterial />
{Array.from({ length: count }, (_, i) => (
<Instance
key={i}
position={[Math.random() * 100, Math.random() * 100, Math.random() * 100]}
color={`hsl(${Math.random() * 360}, 50%, 50%)`}
/>
))}
</Instances>
)
}jsx
import { Instances, Instance } from '@react-three/drei'
function Boxes({ count = 1000 }) {
return (
<Instances limit={count}>
<boxGeometry />
<meshStandardMaterial />
{Array.from({ length: count }, (_, i) => (
<Instance
key={i}
position={[Math.random() * 100, Math.random() * 100, Math.random() * 100]}
color={`hsl(${Math.random() * 360}, 50%, 50%)`}
/>
))}
</Instances>
)
}Selective Rendering
选择性渲染
jsx
// Only re-render when needed
<Canvas frameloop="demand">
{/* Call invalidate() to trigger render */}
</Canvas>
function Controls() {
const { invalidate } = useThree()
return <OrbitControls onChange={() => invalidate()} />
}jsx
// 仅在需要时重新渲染
<Canvas frameloop="demand">
{/* 调用invalidate()触发渲染 */}
</Canvas>
function Controls() {
const { invalidate } = useThree()
return <OrbitControls onChange={() => invalidate()} />
}Memoization
记忆化
jsx
// Memoize expensive components
const ExpensiveModel = memo(function ExpensiveModel({ url }) {
const gltf = useGLTF(url)
return <primitive object={gltf.scene.clone()} />
})
// Memoize materials/geometries outside components
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 'red' })
function Box() {
return (
<mesh geometry={geometry} material={material} />
)
}jsx
// 记忆化开销大的组件
const ExpensiveModel = memo(function ExpensiveModel({ url }) {
const gltf = useGLTF(url)
return <primitive object={gltf.scene.clone()} />
})
// 在组件外部记忆化材质/几何体
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 'red' })
function Box() {
return (
<mesh geometry={geometry} material={material} />
)
}Adaptive Performance
自适应性能
jsx
import { PerformanceMonitor, AdaptiveDpr, AdaptiveEvents } from '@react-three/drei'
<Canvas>
<PerformanceMonitor
onDecline={() => setQuality('low')}
onIncline={() => setQuality('high')}
>
<AdaptiveDpr pixelated />
<AdaptiveEvents />
<Scene quality={quality} />
</PerformanceMonitor>
</Canvas>jsx
import { PerformanceMonitor, AdaptiveDpr, AdaptiveEvents } from '@react-three/drei'
<Canvas>
<PerformanceMonitor
onDecline={() => setQuality('low')}
onIncline={() => setQuality('high')}
>
<AdaptiveDpr pixelated />
<AdaptiveEvents />
<Scene quality={quality} />
</PerformanceMonitor>
</Canvas>Related Skills
相关技能
| When you need... | Use skill |
|---|---|
| Vanilla Three.js (no React) | → threejs |
| Optimize assets before loading | → asset-pipeline-3d |
| Debug visual/performance issues | → graphics-troubleshooting |
| 当你需要... | 使用技能 |
|---|---|
| 原生Three.js(无React) | → threejs |
| 加载前优化资源 | → asset-pipeline-3d |
| 调试视觉/性能问题 | → graphics-troubleshooting |
Reference Files
参考文件
- references/drei.md - Complete Drei component reference
- references/physics.md - @react-three/rapier integration
- references/postprocessing.md - @react-three/postprocessing effects
- references/state.md - Zustand patterns for R3F
- references/drei.md - 完整Drei组件参考
- references/physics.md - @react-three/rapier集成指南
- references/postprocessing.md - @react-three/postprocessing特效指南
- references/state.md - R3F的Zustand使用模式