我正在尝试为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"
}
有什么想法吗?
答案 0 :(得分:0)
我安装了节点LTS(v12.13.0),但是在使用import
而不是require
时遇到了相同的错误。即使在最新的节点版本中,es6 imports
似乎仍然是一项实验性功能。
如果要测试此功能,则需要执行以下步骤:
"type": "module"
中添加package.json
--experimental-modules
。例如NODE_ENV=production node --experimental-modules server/bootstrap.js
答案 1 :(得分:0)
import
和export
语法在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()