导入不起作用nodeJs应用程序React SSR

时间:2019-11-03 18:49:26

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

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

bootstrap.js包含可移植到es5的babel配置

index.js创建一个快速应用程序和快速路由器

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"
  }

有什么想法吗?

4 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

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

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

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

我也建议您阅读this页。

答案 2 :(得分:0)

“ 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组件内部具有带有click事件的按钮,则该click事件将不起作用。由于服务器未交付任何JavaScript代码。第二个缺陷是我们在这里使用了jsx:

const content =renderToString(<Home/>).,因此,当节点执行此文件时,它将无法识别此语法,并且会出错。

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

因此,我们需要重新组织index.js中的代码,这一次由于webpack将转换代码,因此我们可以使用“导入”语法。

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文件夹位于应用程序的根目录中。)然后我们在构建目录中执行“ bundle.js”文件。

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

答案 3 :(得分:-1)

首先检查dotenv软件包是否已安装。如果没有,您可以在以下命令中安装它-

  

npm我-保存dotenv

要使用dotenv,您不需要先导入它然后进行配置。 而是使用以下语法-

  

require('dotenv')。config()