使用react-router-dom v6+react-three-fiber时页面加载异常求助
React Router 与 React Three Fiber 路由问题
我用 react-three-fiber 开发网页,想通过最新版 react-router-dom 添加子页面,但主页面没法正常运行。想问下是不是必须把 Content、Startup 这些函数拆到独立文件里,不能在 App.js 里定义,路由才能正常工作?
App.js 代码
import ReactDOM from "react-dom" import React, { Suspense, useEffect, useRef, useMemo } from "react" import { Canvas, Dom, useLoader, useFrame } from "react-three-fiber" import { TextureLoader, LinearFilter } from "three" import lerp from "lerp" import { Text, MultilineText } from "./components/Text" import Plane from "./components/Plane" import { Block, useBlock } from "./blocks" import state from "./store" import About from "./About" import { Router, Link, Route, Routes } from "react-router-dom"; import "./styles.css" export function Startup() { const ref = useRef() useFrame(() => (ref.current.material.opacity = lerp(ref.current.material.opacity, 0, 0.025))) return <Plane ref={ref} color="#0e0e0f" position={[0, 0, 200]} scale={[100, 100, 1]} /> } export function Paragraph({ image, index, offset, factor, header, aspect, text, see, title }) { const { contentMaxWidth: w, canvasWidth, margin, mobile } = useBlock() const size = aspect < 1 && !mobile ? 0.65 : 0.9 //width of picture elements const alignRight = (canvasWidth - w * size - margin) / 2 const pixelWidth = w * state.zoom * size const left = (index) const color = "#FFFFFF" return ( <Block factor={factor} offset={offset}> <group center> <Plane map={image} args={[1, 1, 32, 32,]} shift={75} size={size} aspect={aspect} scale={[w * size, (w * size) / aspect, 1]} frustumCulled={false} /> <Dom style={{ width: pixelWidth / (mobile ? 1 : 2), textAlign: left ? "left" : "right" }} position={[left || mobile ? (-w * size) / 2 : 0, (-w * size) / 2 / aspect - 0.4, 1]}> <div tabIndex={index}>{text}</div> <a href={see}>{title}</a> </Dom> <Text center size={w * 0.04} color={color}> {header} </Text> </group> </Block> ) } export function Content() { const images = useLoader( TextureLoader, state.paragraphs.map(({ image }) => image) ) useMemo(() => images.forEach(texture => (texture.minFilter = LinearFilter)), [images]) const { contentMaxWidth: w, canvasWidth, canvasHeight, mobile } = useBlock() return ( <> <group rotation-z={0.025} rotation-y={0.0} rotation-x={0.05}> <Block factor={1} offset={-0.1}> </Block> {state.paragraphs.map((props, index) => ( <Paragraph key={index} index={index} {...props} image={images[index]} /> ))} </group> </> ) } export default function App() { const scrollArea = useRef() const onScroll = e => (state.top.current = e.target.scrollTop) useEffect(() => void onScroll({ target: scrollArea.current }), []) return ( <> <Canvas className="canvas" concurrent pixelRatio={1} orthographic camera={{ zoom: state.zoom, position: [0, 0, 500] }}> <Suspense fallback={<Dom center className="loading" children="Loading..." />}> <Content /> <Routes> <Route path="/about" element={<About />} /> </Routes> <Startup /> </Suspense> </Canvas> <div className="scrollArea" ref={scrollArea} onScroll={onScroll}> {new Array(state.sections).fill().map((_, index) => ( <div key={index} id={"0" + index} style={{ height: `${(state.pages / state.sections) * 30}vh` }} />//site length ))} </div> <nav> <ul> <li> <Link to="/">Home</Link> <Link to="/about">About</Link> </li> </ul> </nav> </> ); }
index.js 代码
import "./styles.css" import React from "react"; import { BrowserRouter, Routes, Route } from "react-router-dom"; import App from "./App"; import { render } from "react-dom"; const rootElement = document.getElementById("root"); render( <BrowserRouter> <App /> </BrowserRouter>, rootElement );
问题分析与解决方案
核心问题:路由位置错误
你把 <Routes> 放在了 <Canvas> 组件内部,这是主页面失效的关键原因。react-router-dom 的路由组件属于 ReactDOM 管理的 DOM 节点,而 <Canvas> 是 react-three-fiber 用来渲染 Three.js 3D 场景的容器,两者的渲染上下文完全不同,不能混用。
无需拆分组件到独立文件
完全不需要把 Content、Startup 这些组件移到单独文件,它们可以继续留在 App.js 中,只要调整路由结构即可。
修正后的代码示例
调整后的 App.js 核心部分
export default function App() { const scrollArea = useRef() const onScroll = e => (state.top.current = e.target.scrollTop) useEffect(() => void onScroll({ target: scrollArea.current }), []) return ( <> {/* 路由放在 Canvas 外部,作为顶级组件管理页面视图切换 */} <Routes> {/* 首页路由:包含原有的 Canvas 和滚动区域 */} <Route path="/" element={ <> <Canvas className="canvas" concurrent pixelRatio={1} orthographic camera={{ zoom: state.zoom, position: [0, 0, 500] }}> <Suspense fallback={<Dom center className="loading" children="Loading..." />}> <Content /> <Startup /> </Suspense> </Canvas> <div className="scrollArea" ref={scrollArea} onScroll={onScroll}> {new Array(state.sections).fill().map((_, index) => ( <div key={index} id={"0" + index} style={{ height: `${(state.pages / state.sections) * 30}vh` }} /> ))} </div> </> } /> {/* About 页面路由:单独渲染 About 组件 */} <Route path="/about" element={<About />} /> </Routes> {/* 导航栏放在路由外部,保持全局显示 */} <nav> <ul> <li> <Link to="/">Home</Link> <Link to="/about">About</Link> </li> </ul> </nav> </> ); }
关键修正点
- 将
<Routes>移到<Canvas>外部,作为顶级组件管理整个页面的路由逻辑 - 首页路由 (
/) 包含原有的 Canvas、滚动区域等内容,保持原有 3D 场景功能 - About 路由 (
/about) 单独渲染 About 组件,实现页面切换 - 保留 Content、Startup 在 App.js 中,无需拆分
额外注意事项
- 如果 About 页面需要使用 Three.js,需要在 About 组件内部单独嵌套
<Canvas> - 检查
state对象的使用,避免路由切换时出现状态混乱
内容的提问来源于stack exchange,提问作者Lalaland124
相关产品推荐
相关产品推荐

