所谓server render 即服务端渲染,这是为了解决现代前端框架下的单页应用在SEO方面不友好的问题。
react 的SSR主要思路就是
1.将应用的根组件导出 如 <App />
2.在服务器node端 引用 <App /> ,通过'react-dom/server'下的ReactSSR将组件转换成真实的DOM字符串,像这种
<div data-reactroot="">this is app component</div>
3.然后通过node 的fs 模块 导入要渲染的HTML文件,注意,此处要指定编码格式
const template = fs.readFileSync(path.join(__dirname,'../../dist/index.html'),'utf8')
该HTML文件即前端打包后的HTML文件,比如这种
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"><!-- app --></div> <script type="text/javascript" src="/public/vendor.197e0591260e3f2808db.js"></script><script type="text/javascript" src="/public/app.197e0591260e3f2808db.js"></script></body> </html>
这里面的<!-- app -->左右都不能有空格有换行,否则会有一个warning
4.通过fs模块取到的html是个字符串,这里面包含了打包后的js,我们的目的是不让js去渲染页面,也就是一打开就必须有内容,不能白屏,所以返回的页面中要包含之前取到的真实DOM字符串
,那么只要将html中的<!-- app -->替换了就行了
最后启动node 又报了个warning
react-dom.development.js:10253 Warning: render(): Calling ReactDOM.render() to hydrate server-rendered markup will stop working in React v17. Replace the ReactDOM.render() call with ReactDOM.hydrate() if you want React to attach to the server HTML.
意思就是要把之前的render换成hydrate,由于返回的页面已经带有内容,这个方法会检查页面上的内容跟js要生成的DOM是否一直,如果不一致则会按jS中的重新渲染。
代码组织
由于要使用node的服务器返回页面,所以会有两套环境,一套前端一套后端
由于在服务器端没有dom,
render(<App />,document.getElementById('app'))
所以这段代码是不能运行在Node上的,只需要将<App />导出就行了
由于服务器端和前端用的js不一样 所以webpack会有两个,服务器用的webpack.config.js可在之前的文件上修改
1.添加target:"node",
2.打包后的文件名不需要加hash
3.在output中添加libraryTarget:'commonjs2'
4.删除多余元素
最后像这样
import path from "path" export default { target:"node", entry:{ serverEntry: path.join(__dirname,"../src/client/server-entry.js") }, output:{ filename:"[name].js", path: path.join(__dirname,"../dist"), publicPath:'/public', libraryTarget:'commonjs2' }, module:{ rules:[ { test:/\.jsx$/, loader:'babel-loader' }, { test: /\.js$/, exclude: /node_modules\/(?!(dom7|ssr-window|swiper)\/).*/, loader: "babel-loader" }, ] } }
最后运行脚本
s谁在唱kr,温暖了寂寞~