【问题标题】:Converting from create-react-app to NextJS, problem with ThreeJS从 create-react-app 转换为 NextJS,ThreeJS 的问题
【发布时间】:2021-07-18 14:06:07
【问题描述】:

我在 react 中有一个可以工作的 3d 模型查看器,我现在正尝试将其移植到 Next。我需要 GLTFLoader 和 OrbitControls,这给我带来了反应问题,我像这样加载:

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

不能这样做,因为我会在下一个得到这个错误:

SyntaxError: Cannot use import statement outside a module
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source

.next\server\pages\index.js (1:0) @ Object.three/examples/jsm/loaders/GLTFLoader

> 1 | module.exports = require("three/examples/jsm/loaders/GLTFLoader");

然后我尝试使用三标准库并从那里导入控件和加载器。同样的错误。然后我尝试使用 require('') 来导入它,但我又遇到了同样的错误。我在google上发现了一些几乎类似的问题,但没有容易理解的解决方案。

完整代码:

import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
// import Model3d from '../assets/centered.gltf'
// import Model3d from '../assets/3dmodel.gltf'
import D3d from '../assets/images/3d.svg'

import React from 'react'

// let GLTFLoader
// let OrbitControls

const VisA = () => {
  // GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader').GLTFLoader
  // OrbitControls = require('three/examples/js/controls/OrbitControls')
  //   .OrbitControls

  const { useRef, useEffect } = React
  const mount = useRef(null)
  const hitbox = useRef(null)
  const controls = useRef(null)

  useEffect(() => {
    let width = mount.current.clientWidth
    let height = mount.current.clientHeight
    let frameId

    const scene = new THREE.Scene()
    const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
    const loader = new GLTFLoader()
    const camcontrols = new OrbitControls(camera, hitbox.current)


    // loader.load(Model3d, function (gltf) {
    loader.load('./3dmodel/3dmodel.gltf', function (gltf) {
      gltf.scene.scale.set(14, 14, 14)
      var box = new THREE.Box3().setFromObject(gltf.scene)
      var center = new THREE.Vector3()
      box.getCenter(center)
      gltf.scene.position.sub(center)
      scene.add(gltf.scene)
    })

    const hemiLight = new THREE.HemisphereLight(0xffeeb1, 0x80820, 20)
    const light2 = new THREE.DirectionalLight(0xfdeee1, 20)
    const spotLight = new THREE.SpotLight(0xfdeee1, 4)
    const spotLight2 = new THREE.SpotLight(0xfdeee1, 4)
    scene.add(hemiLight)
    scene.add(spotLight)
    scene.add(spotLight2)
    light2.position.set(10, -50, 500)
    scene.add(light2)
    scene.rotation.y = -1.65
    camera.position.z = 4
    // scene.add(cube)
    // renderer.setClearColor('#FFFFFF')
    renderer.setSize(width, height)

    const renderScene = () => {
      renderer.render(scene, camera)
    }

    const handleResize = () => {
      width = mount.current.clientWidth
      height = mount.current.clientHeight
      renderer.setSize(width, height)
      camera.aspect = width / height
      camera.updateProjectionMatrix()
      renderScene()
    }

    const animate = () => {
      spotLight.position.set(
        camera.position.x + 10,
        camera.position.y + 10,
        camera.position.z + 10
      )
      spotLight2.position.set(
        camera.position.x - 10,
        camera.position.y - 10,
        camera.position.z - 10
      )

      renderScene()
      frameId = window.requestAnimationFrame(animate)
    }

    const start = () => {
      if (!frameId) {
        frameId = requestAnimationFrame(animate)
      }
    }

    const stop = () => {
      cancelAnimationFrame(frameId)
      frameId = null
    }

    mount.current.appendChild(renderer.domElement)
    window.addEventListener('resize', handleResize)
    start()

    controls.current = { start, stop }


    return () => {
      stop()
      window.removeEventListener('resize', handleResize)
      mount.current.removeChild(renderer.domElement)


    }
  }, [])

  return (
    <div
      className="relative flex items-center justify-center w-full h-full "
      ref={mount}
      // onClick={() => setAnimating(!isAnimating)}
    >
      <div className="absolute w-full h-full">
        <D3d className="w-10 h-10 mt-10 text-gray-800 fill-current " />
      </div>
      <div className="absolute w-3/5 h-4/5" ref={hitbox}></div>
    </div>
  )
}
export default VisA

【问题讨论】:

    标签: javascript reactjs three.js next.js


    【解决方案1】:

    问题

    您正在导入以 ESM 格式编写的模块,并且当 NextJS 尝试在服务器端呈现您的代码节点时,您的代码节点不理解 ESM——Node.js 通常需要 CJS (CommonJS)。

    解决方案

    视情况而定。

    1。你想让你的代码使用three 在服务器端运行吗?

    (假设three 可以在节点中运行,我不能说是哪一种)

    我认为这里有几个选项:

    a) 使用与 Node.js 和浏览器兼容的 three 版本。 看了this issue,可以考虑试试three-universal(我没用过)。

    b) 自定义您的 Webpack 行为以将 three 代码捆绑并转换为服务器的 CJS。就个人而言,我不建议将此作为第一步。自定义 Webpack 很快就会变得非常复杂,而在 NextJS 中这样做会带来额外的复杂性。

    2。使用three 修改您的代码以仅在客户端运行。

    使用dynamic import。从那个参考:

    您可能并不总是希望在服务器端包含模块。例如,当模块包含仅在浏览器中工作的库时。

    其他反馈

    请注意,上面的答案是概念性的。如果您能够通过存储库提供复制,或者我可以提供更具体的解决方案。

    【讨论】:

    • 非常感谢您的建设性回答,您不知道我有多欣赏它。我会看看动态导入,这似乎是最简单且最有意义的。
    • 我得到了它的工作......你的评论极大地帮助了我,作为一个新手真的很难涉足一半的信息。解决方案非常简单。
    【解决方案2】:

    在 Mcy 的帮助下,我发现在我的情况下添加动态导入是最简单的。这很简单,我没有更改我从 react 中获得的查看器组件中的任何内容,并将组件加载到 pages/index.js 上:

    import dynamic from 'next/dynamic' 
    const Dynamic3dViewr = dynamic(() => import('../components/Viewer3d.js'), {
      ssr: false,
    })

    【讨论】:

      猜你喜欢
      • 2021-05-03
      • 2020-01-25
      • 2020-04-14
      • 1970-01-01
      • 2022-01-19
      • 2020-07-03
      • 2020-09-20
      • 2020-11-08
      • 1970-01-01
      相关资源
      最近更新 更多