简体   繁体   English

从 create-react-app 转换为 NextJS,ThreeJS 的问题

[英]Converting from create-react-app to NextJS, problem with ThreeJS

I had a working 3d model viewer in react, which i am now trying to port to Next.我在反应中有一个工作的 3d model 查看器,我现在正在尝试将其移植到 Next。 I need GLTFLoader and OrbitControls which are giving me the problem in react I loaded this like:我需要 GLTFLoader 和 OrbitControls 这给了我反应的问题,我像这样加载:

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

can't do that because Ill get this error in next:不能这样做,因为接下来我会收到此错误:

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");

Then I tried to use the three-std library and import the controls and loader from there.然后我尝试使用三标准库并从那里导入控件和加载器。 Same error.同样的错误。 I then tried to use the require('') to import it but I got the same error again.然后我尝试使用 require('') 来导入它,但我又遇到了同样的错误。 I found some almost similar problems on google, but no solution that was easy to understand.我在google上发现了一些几乎类似的问题,但没有容易理解的解决方案。

full code:完整代码:

 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

The Problem问题

You're importing modules written in ESM format and when NextJS attempts to server-side render your code node doesn't understand ESM -- Node.js expects CJS (CommonJS) typically.您正在导入以 ESM 格式编写的模块,当 NextJS 尝试在服务器端呈现您的代码节点时,您的代码节点不理解 ESM——Node.js 通常需要 CJS(CommonJS)。

The Solution解决方案

It depends.这取决于。

1. Do you want your code using three to run server-side? 1.你想让你的代码使用three在服务器端运行吗?

(this assumes three can run in node, which I can't say one way or the other) (这假设three可以在节点中运行,我不能说一种或另一种)

I think there are a couple of options here:我认为这里有几个选择:

a) Use a version of three compatible with Node.js and the Browser. a)使用与 Node.js 和浏览器兼容的three版本。 Glancing over this issue , you may consider trying three-universal (I have not used it).扫了一眼这个问题,可以考虑试试三通(我没用过)。

b) Customize your Webpack behavior to bundle and transpile the three code into CJS for the server. b)自定义您的 Webpack 行为以将这three代码捆绑并转译为服务器的 CJS。 Personally, I don't recommend this as a first step.就个人而言,我不建议将此作为第一步。 Customizing Webpack can become very complicated very quickly, and doing so in NextJS comes with its own added complexities.自定义 Webpack 很快就会变得非常复杂,在 NextJS 中这样做会带来额外的复杂性。

2. Modify your code using three to run only client-side. 2.使用three修改您的代码以仅在客户端运行。

Use a dynamic import .使用动态导入 From that reference:从那个参考:

You may not always want to include a module on server-side.您可能并不总是希望在服务器端包含一个模块。 For example, when the module includes a library that only works in the browser.例如,当模块包含仅在浏览器中工作的库时。

Additional Feedback附加反馈

Please note, the answer above is conceptual.请注意,上面的答案是概念性的。 If you are able to provide a reproduction by way of a repository or otherwise I may be able to give a more concrete solution.如果您能够通过存储库提供复制,或者我可以提供更具体的解决方案。

With help of Mcy, i found that in my case adding a dynamic import would be easiest.在 Mcy 的帮助下,我发现在我的情况下添加动态导入是最简单的。 And it was pretty easy, I didn't change anything in the viewer component I had from react, and loaded the component on the pages/index.js with:这很简单,我没有更改我从 react 获得的查看器组件中的任何内容,并在 pages/index.js 上加载了该组件:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM