react的 服务端渲染有助于 seo优化 和 首屏速度快的优点 。
需要新建一个服务端的js,以供打包输出 react 字符串 到node端;
这里新建 server-entry.js
import React from "react";
import App from "./App.jsx";
// 这边输出了一个react 标签所以需要引入react
export default <App/>
然后 需要给他配一个能生产字符串的 webpack配置 在build目录下;
就是 webpack.config.server.js
然后复制一份之前的配置内容稍微 修改;
主要有几点 :1:在整个输出模块里新增target选项。
target:"node",
告诉webpack当前环境是node环境;
2,修改入口文件:
app:path.join(__dirname,"../client/server-entry.js")
3,修改打包后的文件名字
filename:"server-entry.js",
并且在 打包这一项 output对象这一项之前新增一个libraryTarget:'commonjs2',
表示打包采用的是 commonjs2的规范;
4,移除掉html-webpack-plugin
这里是服务端 所以不需要页面;
然后在package.json里把脚本段改掉:
"scripts": {
"build:client": "webpack --config build/webpack.config.js",
"build:server": "webpack --config build/webpack.config.server.js",
"clear": "rimraf dist",
"build": "npm run clear && npm run build:client && npm run build:server"
},
clear 命令是删除dist目录的 所以每次打包前 都删除dist目录;需要安装删除文件夹的node包 cnpm i -D rimraf
build:client 和build:server 分别被执行一下; 生产服务端代码和客户端代码;
执行npm run build 就会得到三个文件,打开 server-entry.js就会看到 module.exports说明执行了 node环境的编码;
新建server.js 在client 文件夹下; node服务依赖于express 所以cnpm i -S express
然后代码 如下
//引入express
const express = require("express");
//从react-dom的server文件夹下引入 关于服务端dom渲染的 方法;
const ReactSSR=require("react-dom/server");
//引入服务端关于 首屏的 前段打包压缩后的代码;由于是commonjs 所以 在default属性里了;
const serverENTRY=require("../dist/server-entry.js").default;
//调用express 生成app实例;
const app=express();
// * 页面上所有的请求统统都走 这个get方法
app.get("*",function (req,res) {
// 把 首屏的代码 使用react的服务端的渲染方法 继续处理一下;
const appString=ReactSSR.renderToString(serverENTRY);
//页面渲染前端代码
res.send(appString);
});
//监听 3444端口;
app.listen(3444,function () {
console.log("server is running on 3444");
});
然后再package.json里增加脚本,
"scripts": {
"build:client": "webpack --config build/webpack.config.js",
"build:server": "webpack --config build/webpack.config.server.js",
"clear": "rimraf dist",
"build": "npm run clear && npm run build:client && npm run build:server",
"start":"node client/server.js"
}
然后就可以初步看到服务端渲染出一些内容了;
但是 在network里看不到任何关于其他引用的js, 这样肯定是不行的。
正确的打开方式应该是 我们把原来的页面的html 拿到服务端来, 放置入 我们的打包好的内容,在输出到 页面这样 才是完整的页面输出;
所以在client下新建一个html 就叫tempalte 吧;
代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="root"><app></app></div>
</body>
</html>
新增一个div ,div里有个标签叫做app;占位用的 ;
在 app.js里把最后的render的路径改成
ReactDom.render(<App/>,document.getElementById("root");
这样就会把<app></app> 标签替换掉;
然后在 webpack.config.js 里 的plugins 里新增一个配置
//webpack的插件。 是一个数组,
plugins:[new HTMLPlugin({
// 意思就是模版的路径就是template.html,插入js用的
template:path.join(__dirname,"../client/template.html")
})]
然后在server.js里修改
// fs模块用于对系统文件及目录进行读写操作。
const fs =require("fs");
//引入path
const path =require("path");
// 这边我们同步的方法来读取 html ,也就是要等待读取完成 才继续,制定utf-8
const template = fs.readFileSync(path.join(__dirname,"../dist/index.html"),"utf8");
在get方法里 使用 这个 html模版 ,替换掉占位符 重新插入到页面
// * 页面上所有的请求统统都走 这个get方法
app.get("*",function (req,res) {
// 把 首屏的代码 使用react的服务端的渲染方法 继续处理一下;
const appString=ReactSSR.renderToString(serverENTRY);
//把原来div里的<app></app>替换成 appString
template.replace("<app></app>",appString)
//页面渲染前端代码
res.send(template);
});
重新编译 就可以跑了
但是发现所有的请求都走* 这样就不对了
所以应该是
利用express的static静态资源文件的方法去处理 匹配到public文件目录的请求 ,也就是说都变成静态文件了。
//利用express的static静态资源文件的方法去处理 匹配到public文件目录的请求 ,也就是说都变成静态文件了。
app.use("/public",express.static(path.join(__dirname,"../dist")))
这样再重新编译就对了;
应用 服务端的代码, React-dom.render()方法 就不能用了 ,对应的有一个专门用来给服务端注水的方法;
ReactDom.hydrate
ReactDom.hydrate(<App/>,document.getElementById("root"))
重新打包编译就完事