【问题标题】:import is not working nodeJs application React SSR导入不起作用 nodeJs 应用程序 React SSR
【发布时间】:2019-11-03 18:49:26
【问题描述】:

我正在尝试为 React 应用程序实现 SSR,我开始创建一个包含 3 个文件(引导、索引、渲染器)的服务器目录

bootstrap.js 包含要转换为 es5 的 babel 配置

index.js 创建一个 express 应用和 express Router

renderer.js 负责将 React 应用程序渲染为字符串并将其作为 html 发送给客户端。

bootstap.js =>

require('ignore-styles');

require('@babel/register')({
  ignore: [
    function (filePath) {
      return !filePath.includes('node_modules');
    }
  ],
  presets: [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ],
    '@babel/preset-react',
    '@babel/flow'
  ],
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        "regenerator": true
      }
    ],
    "@babel/plugin-proposal-object-rest-spread",
    "@babel/plugin-syntax-dynamic-import",
    "react-loadable/babel",
    "@babel/plugin-proposal-class-properties",
    "dynamic-import-node"
  ]
});

require('./index');

index.js =>

import dotenv from 'dotenv';
import cookieParser from 'cookie-parser';


dotenv.config();

const express = require('express');
const serverRenderer = require('./middleware/renderer');


const PORT = process.NODE_ENV === 'development' ? 3000 : 7160;
const path = require('path');

const app = express();
app.use(cookieParser());
const router = express.Router();
const routes = require('../src/router/appRoutes').default;


router.use(express.static(
  path.resolve(__dirname, '..', 'build'),
  { maxAge: '30d' },
));


routes.map(path => app.get(path, serverRenderer));

app.use(router);

app.listen(PORT, (error) => {
  if (error) {
    return console.log('something bad happened', error);
  }

  console.log("listening on " + PORT + "...");
});

但是当我运行时

NODE_ENV=production node server/bootstrap.js

此命令启动服务器端应用程序我收到此错误

import dotenv from 'dotenv';
       ^^^^^^

SyntaxError: Unexpected identifier
    at Module._compile (internal/modules/cjs/loader.js:723:23)
    at Module._compile (/Users/amirtahani/projects/uneed/node_modules/pirates/lib/index.js:99:24)
    at Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Object.newLoader [as .js] (/Users/amirtahani/projects/uneed/node_modules/pirates/lib/index.js:104:7)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.<anonymous> (/Users/amirtahani/projects/uneed/server/bootstrap.js:34:1)

奇怪的是,相同的代码正在另一个项目中工作。 这是我的 devDependencies

"devDependencies": {
    "@babel/core": "7.6.4",
    "@babel/plugin-proposal-class-properties": "7.5.5",
    "@babel/plugin-proposal-object-rest-spread": "7.6.2",
    "@babel/plugin-syntax-dynamic-import": "7.2.0",
    "@babel/plugin-transform-classes": "7.5.5",
    "@babel/plugin-transform-modules-commonjs": "7.6.0",
    "@babel/plugin-transform-runtime": "7.6.2",
    "@babel/preset-env": "7.6.3",
    "@babel/preset-flow": "7.0.0",
    "@babel/preset-react": "7.6.3",
    "@babel/register": "7.6.2",
    "babel-cli": "6.26.0",
    "babel-plugin-dynamic-import-node": "2.3.0",
    "babel-plugin-transform-es2015-modules-commonjs": "6.26.2",
    "flow-bin": "0.102.0",
    "ignore-styles": "5.0.1"
  }

有什么想法吗?

【问题讨论】:

  • 嗨,Amir,请问您找到解决问题的方法了吗?

标签: node.js reactjs import babeljs server-side-rendering


【解决方案1】:

"import --from --" 是 ES15 语法,但是 node.js 使用 commonJS 模块语法。所以你需要安装和配置 webpack。 Babel 只是将新一代的 javascript 转换成旧的 javascript 代码。然而,webpack 会将您的应用程序代码捆绑到一个文件中,并且您的服务器文件将通过该 bundle.js 执行。

对于服务器端渲染,您需要 2 个 bundle.js。一个用于客户端,另一个用于服务器。没有 javascript 的 HTML 文件没有任何功能。

如果我们从服务器开始,这就是我们将代码写入 index.js 的方式。

const renderToString=require("react-dom/server").renderToString //specifically created for server
const  Home=require("./components/Home").default //home component
const React=require("react")
const express=require("express")
const app=express()
app.get("/",(req,res)=>{
const content =renderToString(<Home/>)
res.send(content)
})

这就是我们为“/”路由将代码呈现给浏览器的方式。这里有2个缺陷。第一个,我们发送没有 javascript 代码的文件。 Home 组件中的任何功能都将不起作用。例如,如果您在 Home 组件中有带有单击事件的按钮,则该单击事件将不起作用。因为服务器没有发送任何 javascript 代码。第二个缺陷是我们在这里使用了 jsx:

const content =renderToString(&lt;Home/&gt;). 所以当 node 执行这个文件时,它不会识别这个语法并且会报错。

要解决这两个问题,我们需要 webpack。 webpack 会将 index.js 转换为我们指定名称和位置的文件。在服务器端,我们通常将文件 bundle.js 命名为 public 文件夹中。因此,当我们使用 node 或 nodemon 启动服务器时,我们将执行 public/bundle.js 而不是 index.js 文件。

所以我们需要重新组织 index.js 中的代码,这次由于 webpack 会转换代码,我们可以使用“import”语法。

import React from "react";
import { renderToString } from "react-dom/server";
import Home from "./components/Home"
import React from "react"
import express from "express";

const app=express()
app.use(express.static("public")) //This will make public folder publicly available so we can ship it down to the browser. 

app.get("/",(req,res)=>{
const content=renderToString(<Home/>)
//I used template strings ``
const html= `
  <html> 
  <head></head>
  <body>
  <div  id="root">${content}</div>
  <script src="bundle.js"> </script>
//since we are sending a file, express will look for bundle.js inside the public folder. So we do not need to write relative path or absolute path.
  </body></html> `
res.send(html)
})

现在我们需要一个文件来配置 webpack。我们在应用的根目录中将其命名为 webpack.config.js。

const path=require("path")
module.exports={
//in server side we keep client and server logic inside src folder
entry:"./src/index.js", //relative path
mode:"development",
output:{filename:bundle.js,
       path:path.resolve(__dirname,"build")},
//absolute path. that is why we use native node module path. also you do not need to create build folder. webpack will create automatically
module:{rules:[{test:/\.js$/,
                loader:"babel-loader",
                exclude:/node_modules/,
                options:{presets:["@babel/preset-env","@babel/preset-react"]}}]}

}

最后在 package.json 中

"scripts": {
    "dev:server": "nodemon  --watch build --exec \"node build/bundle.js\"",
    "dev:build-server": "webpack --config webpack.server.js --watch",
    "dev:build-client": "webpack --config webpack.client.js --watch"
  },

在 dev:server 中,我们观察“build”文件夹的变化。(build 文件夹位于应用程序的根目录中。)然后我们在 build 目录中执行“bundle.js”文件。

所以要回答你的问题,这是同构 javascript 应用程序的服务器端部分的基础。

【讨论】:

    【解决方案2】:

    我安装了节点 LTS (v12.13.0),但在使用 import 而不是 require 时遇到了同样的错误。似乎es6 imports 仍然是一个实验性功能,即使在最新的节点版本中也是如此。

    如果您想测试此功能,您需要执行以下步骤:

    1. 在您的package.json 中添加"type": "module"
    2. 使用此标志运行您的服务器:--experimental-modules。例如NODE_ENV=production node --experimental-modules server/bootstrap.js

    【讨论】:

      【解决方案3】:

      importexport 语法在 node js 最新版本中仍处于试验阶段,但有解决此问题的方法。

      尝试在 package.json 文件中添加 "type": "module" 并在 webpack 配置中将 .js 捆绑扩展名更改为 .mjs 并使用以下命令运行生成的文件:

      NODE_ENV=production node --experimental-modules server/bootstrap.mjs
      

      我也推荐你阅读this页面。

      【讨论】:

        【解决方案4】:

        首先检查是否安装了 dotenv 包。如果没有,你可以在下面的命令中安装它-

        npm i --save dotenv

        为了使用dotenv,你不需要导入它然后配置它。 而是使用以下语法 -

        require('dotenv').config()

        【讨论】:

        • 问题在于 babel 不理解导入和导出而不是语法,如果我使用 require 而不是 import 错误将在下一次导入时出现
        猜你喜欢
        • 2022-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 2021-02-26
        • 2021-06-24
        • 1970-01-01
        相关资源
        最近更新 更多