reactjs中使用threejs从0到1()

搭建本地开发环境

  • 安装nodejs
  • 按照 Create React App 安装指南创建一个新的项目
npx create-react-app react-three-demo
  • 删除掉新项目中 src/ 文件夹下的所有文件。(不要删除整个 src 文件夹,删除里面的源文件。)
  • 安装路由依赖包和threejs依赖包
# tips: 注意版本兼容问题
npm install react-router@5 react-router-dom@5

npm install --save three
  • 按照下面目录结构创建文件
-public
└─src
    ├─assets
    │  └─styles
    ├─routers
    │  └─index.js
    └─views
    │  ├─home.js
    │  └─demo.js
    ├─index.js // 入口文件
    ├─app.js // 主文件

组件和路由文件

  • 路由文件
import { Route } from 'react-router-dom'
import Index from '../views/index'
import Demo from '../views/demo'

// 路由配置
const Routes = [
  {
    path: '/',
    exact: true,
    component: Index
  },
  {
    path: '/Demo',
    exact: true,
    component: Demo
  }
]

function AppRouter() {
  return (
    <main>
      {Routes.map((item, index) => {
        return <Route key={index} path={item.path} exact={item.exact} component={item.component}></Route>
      })}
    </main>
  )
}

export default AppRouter

  • 入口文件index.js
import React from 'react'
import ReactDOM from 'react-dom/client'

import './assets/styles/base.css'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
// 注意,不要使用严格模式去渲染,否则会导致 componentDidMount 钩子执行两次
root.render(<App />)

  • app.js
import { BrowserRouter as Router, Link } from 'react-router-dom'
import AppRouter from './routers'

function App() {
  return (
    <Router>
      <div className="App">
        <nav>
          <Link to="/">首页</Link>
          <Link to="/Demo">demo</Link>
        </nav>

        <AppRouter></AppRouter>
      </div>
    </Router>
  )
}

export default App

  • 首页 home.js
import { Component } from 'react'
import * as THREE from 'three'
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

export default class Index extends Component {
  render() {
    return <div id="stage"></div>
  }

  /**
   * @Description: 组件挂载完成
   * @return {*}
   */
  componentDidMount() {
    this.init()
    this.animate()
  }

  /**
   * @Description: 初始化
   * @return {*}
   */
  init = () => {
    this.scene = new THREE.Scene()
    this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / (window.innerHeight - 33), 0.1, 1000)

    this.renderer = new THREE.WebGLRenderer()
    this.renderer.setSize(window.innerWidth, window.innerHeight - 33)
    document.getElementById('stage').appendChild(this.renderer.domElement)

    const geometry = new THREE.BoxGeometry(1, 1, 1)
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    this.cube = new THREE.Mesh(geometry, material)
    this.scene.add(this.cube)

    this.camera.position.z = 5
  }

  /**
   * @Description: 旋转动画
   * @return {*}
   */
  animate = () => {
    requestAnimationFrame(this.animate)
    this.cube.rotation.x += 0.01
    this.cube.rotation.y += 0.01
    this.renderer.render(this.scene, this.camera)
  }
}

package.json文件

{
  "name": "react-three-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "set PORT=8888 && react-scripts start",
    "build": "react-scripts build"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router": "^5.3.3",
    "react-router-dom": "^5.3.3",
    "react-scripts": "5.0.1",
    "three": "^0.143.0",
  }
}

备注

搞这个小demo的过程中遇到一些问题,总结如下:

  • nodejs版本要足够新,否则可能创建项目不成功
  • npx create-react-app创建项目报错(不报错的忽略)
    npm ERR! code ENOLOCAL
    npm ERR! Could not install from “Files\nodejs\node_cache\_npx\1452” as it does not contain a package.json file.

    npm ERR! A complete log of this run can be found in:
    npm ERR! C:\Program Files\nodejs\node_cache\_logs\2022-08-04T14_46_10_318Z-debug.log
    Install for create-react-app@latest failed with code 1

    解决方法:
    检查本地用户下的 .npmrc 文件配置,去掉config配置,重新创建项目即可。
    如果还不行那就重新安装node吧

  • 路由报错,页面空白
    Uncaught Error: Invariant failed: You should not use outside a

    那可能是react-router和react-router-dom的版本不兼容,或者是路由位置放错了。
    本文使用的 v5 版本,写法可以参考本文路由文件也可自己尝试其他方式。

  • threejs创建的3D图形,canvas渲染出现两个
    // 注意入口文件中使用的是严格模式,导致 componentDidMount 钩子执行两次,init函数执行两次造成的
    // 解决方法:要么去掉严格模式,要不操作dom控制只有一个canvas元素,本文使用第一种方法
    root.render(



    )

    // 解决方法一
    root.render()

  • 使用threejs 做demo过程中注意,方法使用箭头函数方便定义和使用变量,定义变量可以直接放在当前实例上。
————————

搭建本地开发环境

  • 安装nodejs
  • 按照 Create React App 安装指南创建一个新的项目
npx create-react-app react-three-demo
  • 删除掉新项目中 src/ 文件夹下的所有文件。(不要删除整个 src 文件夹,删除里面的源文件。)
  • 安装路由依赖包和threejs依赖包
# tips: 注意版本兼容问题
npm install react-router@5 react-router-dom@5

npm install --save three
  • 按照下面目录结构创建文件
-public
└─src
    ├─assets
    │  └─styles
    ├─routers
    │  └─index.js
    └─views
    │  ├─home.js
    │  └─demo.js
    ├─index.js // 入口文件
    ├─app.js // 主文件

组件和路由文件

  • 路由文件
import { Route } from 'react-router-dom'
import Index from '../views/index'
import Demo from '../views/demo'

// 路由配置
const Routes = [
  {
    path: '/',
    exact: true,
    component: Index
  },
  {
    path: '/Demo',
    exact: true,
    component: Demo
  }
]

function AppRouter() {
  return (
    <main>
      {Routes.map((item, index) => {
        return <Route key={index} path={item.path} exact={item.exact} component={item.component}></Route>
      })}
    </main>
  )
}

export default AppRouter

  • 入口文件index.js
import React from 'react'
import ReactDOM from 'react-dom/client'

import './assets/styles/base.css'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
// 注意,不要使用严格模式去渲染,否则会导致 componentDidMount 钩子执行两次
root.render(<App />)

  • app.js
import { BrowserRouter as Router, Link } from 'react-router-dom'
import AppRouter from './routers'

function App() {
  return (
    <Router>
      <div className="App">
        <nav>
          <Link to="/">首页</Link>
          <Link to="/Demo">demo</Link>
        </nav>

        <AppRouter></AppRouter>
      </div>
    </Router>
  )
}

export default App

  • 首页 home.js
import { Component } from 'react'
import * as THREE from 'three'
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

export default class Index extends Component {
  render() {
    return <div id="stage"></div>
  }

  /**
   * @Description: 组件挂载完成
   * @return {*}
   */
  componentDidMount() {
    this.init()
    this.animate()
  }

  /**
   * @Description: 初始化
   * @return {*}
   */
  init = () => {
    this.scene = new THREE.Scene()
    this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / (window.innerHeight - 33), 0.1, 1000)

    this.renderer = new THREE.WebGLRenderer()
    this.renderer.setSize(window.innerWidth, window.innerHeight - 33)
    document.getElementById('stage').appendChild(this.renderer.domElement)

    const geometry = new THREE.BoxGeometry(1, 1, 1)
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    this.cube = new THREE.Mesh(geometry, material)
    this.scene.add(this.cube)

    this.camera.position.z = 5
  }

  /**
   * @Description: 旋转动画
   * @return {*}
   */
  animate = () => {
    requestAnimationFrame(this.animate)
    this.cube.rotation.x += 0.01
    this.cube.rotation.y += 0.01
    this.renderer.render(this.scene, this.camera)
  }
}

package.json文件

{
  "name": "react-three-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "set PORT=8888 && react-scripts start",
    "build": "react-scripts build"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router": "^5.3.3",
    "react-router-dom": "^5.3.3",
    "react-scripts": "5.0.1",
    "three": "^0.143.0",
  }
}

备注

搞这个小demo的过程中遇到一些问题,总结如下:

  • nodejs版本要足够新,否则可能创建项目不成功
  • npx create-react-app创建项目报错(不报错的忽略)
    npm ERR! code ENOLOCAL
    npm ERR! Could not install from “Files\nodejs\node_cache\_npx\1452” as it does not contain a package.json file.

    npm ERR! A complete log of this run can be found in:
    npm ERR! C:\Program Files\nodejs\node_cache\_logs\2022-08-04T14_46_10_318Z-debug.log
    Install for create-react-app@latest failed with code 1

    解决方法:
    检查本地用户下的 .npmrc 文件配置,去掉config配置,重新创建项目即可。
    如果还不行那就重新安装node吧

  • 路由报错,页面空白
    Uncaught Error: Invariant failed: You should not use outside a

    那可能是react-router和react-router-dom的版本不兼容,或者是路由位置放错了。
    本文使用的 v5 版本,写法可以参考本文路由文件也可自己尝试其他方式。

  • threejs创建的3D图形,canvas渲染出现两个
    // 注意入口文件中使用的是严格模式,导致 componentDidMount 钩子执行两次,init函数执行两次造成的
    // 解决方法:要么去掉严格模式,要不操作dom控制只有一个canvas元素,本文使用第一种方法
    root.render(



    )

    // 解决方法一
    root.render()

  • 使用threejs 做demo过程中注意,方法使用箭头函数方便定义和使用变量,定义变量可以直接放在当前实例上。