起因:前段时间用react完成了个人博客,部署到线上,加载速度,首屏体验是在是感人,哎。
决定:之前听说过ssr,想试试但是一直没有去深入研究,ssr对于首屏速度,加载到,还有SEO都很友好,于是决定试试。
过程:ssr确实挺多坑的,首先你得领会它的原理,其实就是快照,把当前快照反馈给用户。对于一些样式文件、资源之类的,服务器是不识别的,需要进行预处理。所以我就去找第三方包,用的也不是特别顺畅,就去读他们的源码,才是弄明白,反正这个过程是断断续续弄了一个月多,挺艰辛的。
技术栈:webpack4.x + react16.x + redux + less
tips:对于服务端渲染不是特别理解的,可以看下vue服务端渲染
准备工作
一份build好的react的项目文件
正式开始
1. 本地起node服务,把build文件本地跑起来
//server/server.js
const express = require("express");
const userRoute = require("../src/route");
const app = express();
const path = require('path');
// 映射到build后的路径
//设置build以后的文件路径 项目上线用
app.use((req, res, next) => {
if (req.url.startsWith('/static/')) {
return next()
}
return res.sendFile(path.resolve('build/index.html'))
})
app.use('/',express.static(path.resolve(__dirname,'../build')))
app.listen("9000",function(){
console.log("open Browser http://localhost:9000");
});
在pageage.json配置下
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"server": "nodemon server/server.js"
},
运行npm run server,浏览器查看localhost:9000
在node文件中我们要引入react等,需要用的ES6/7的语法,与node的commonjs规范违背。所以我们需要解析语法转化成commonjs。同时安装cross-en包,设置运行环境为test,不然会报错。
安装babel-node
cnpm i @babel/core @babel/node -g
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"server": "cross-env NODE_ENV=test nodemon --exec babel-node server/server.js"
},
2.ReactDOMServer.renderToString
React.createElement把React类进行实例化,实例化后的组件就可以进行mount操作了,在浏览器环境我们是使用ReactDOM.render()来进行挂载操作的。ReactDOMServer.renderToString则是把React实例渲染成HTML标签。接下里就需要吧index.js里面代码搬到server.js里面,渲染成html返回给前端就OK! 进行代码改造,完成后如下:
//server/server.js
const express = require("express");
const Route = require("../src/route");
const app = express();
const path = require('path');
// 映射到build后的路径
//设置build以后的文件路径 项目上线用
app.use((req, res, next) => {
if (req.url.startsWith('/static/')) {
return next()
}
const context = {
}
const ReactSSR = renderToString(
(<Provider store={
store}>
<StaticRouter
location={
req.url}
context={
context}>
<Route />
</StaticRouter>
</Provider>)
)
res.send(ReactSSR)
})
app.use('/',express.static(path.resolve(__dirname,'../build')))
app.listen("9000",function(){
console.log("open Browser http://localhost:9000");
});
此时代码用renderToString()处理后返回给前端,npm run start 运行看看,发现控制台报错。
[外链图片转存失败(img-9bI0dooH-1569157416526)(https://raw.githubusercontent.com/lourain/pic-bed/master/E8A66803-9D29-4DDE-9D9D-50B016139776.png)]
是css报错,服务端中不识别样式文件,我们要处理下。
2. 处理样式文件
我们需要安装一个包css-modules-require-hook
cnpm i css-modules-require-hook -S-D
安装好依赖以后,我们需要引入配置,并新建crmh.conf.js钩子文件进行配置
[外链图片转存失败(img-xlU335HS-1569157491605)(https://raw.githubusercontent.com/lourain/pic-bed/master/QQ20190314-150117@2x.png)]
const lessParser = require('postcss-less').parse;//把less解析成postcss语法
module.exports = {
extensions: ['.less','.css'],
generateScopedName: '[path][name]__[local]--[hash:base64:5]',//class名称必须与webpack内的设置保持一致
processorOpts: {
parser: lessParser},
}
//server/server.js
// 处理css
//一定要放置在文件最前面
import csshook from 'css-modules-require-hook/preset';
...
...
处理好css后,运行cnpm run server,此时看到报错:
[外链图片转存失败(img-mgtZc6rx-1569157575940)(https://raw.githubusercontent.com/lourain/pic-bed/master/QQ20190314-171123%402x.png)]
图片,资源类文件报错
3. 处理图片资源文件
需要安装包
cnpm install asset-require-hook --save
对server.js进行配置
//server/server.js
// 处理css
//一定要放置在文件最前面
import csshook from 'css-modules-require-hook/preset';
// 处理图片
import assethook from 'asset-require-hook';
...
...
assetHook({
extensions: ['jpg', 'png', 'webp', 'ttf'],
name: 'static/media/[name].[hash:8].[ext]'
})
···
然后我们再运行一下,查看浏览器network的response
[外链图片转存失败(img-CNLquPQn-1569157282817)(https://raw.githubusercontent.com/lourain/pic-bed/master/QQ20190314-171123%402x.png)]
发现返回的仅仅是一个div&#