服务端渲染开发
- 在server/server.js中,没有进行开发环境和线上环境区分,现在进行区分下。
...省略
const isDev = process.env.NODE_ENV === 'development';
if(!isDev){
const serverEntry = require('../dist/server-entry').default
const template = fs.readFileSync(path.join(__dirname,'../dist/index.html'),'utf8')
app.use('/public',express.static(path.join(__dirname,'../dist')))
app.get('*',(req,res)=>{
const data = ReactSSR.renderToString(serverEntry)
res.send(template.replace('<app></app>',data))
})
}else{
const devStatic = require('./util/dev-static')
devStatic(app)
}
- 在server目录下创建util文件夹,在util文件夹新建dev-static.js
cd server
mkdir util
cd util
touch dev-static.js
- 安装下面几个包
npm i axios -S
npm i memory-fs -D
npm i http-proxy-middleware -D
- 在dev-static.js中配置服务端端渲染
const axios = require('axios')
const MemoryFs = require('memory-fs')
const path = require('path')
const webpack = require('webpack')
const proxy = require('http-proxy-middleware')
const serverConfig = require('../../build/webpack.config.server')
const ReactDomServer = require('react-dom/server')
const getTemplate = () => {
return new Promise((resolve,reject)=>{
axios.get('http://localhost:8888/public/index.html')
.then(res=>{
resolve(res.data)
})
.catch(reject)
})
}
const Module = module.constructor
const mfs = new MemoryFs
const serverCompiler = webpack(serverConfig)
serverCompiler.outputFileSystem = mfs
let serverBundle
serverCompiler.watch({},(err,stats)=>{
if(err) throw err
stats = stats.toJson()
console.log('stats',stats.error)
stats.errors.forEach(err=>console.error(err))
stats.warnings.forEach(warn=>console.warn(warn))
const bundlePath = path.join(
serverConfig.output.path,
serverConfig.output.filename
)
const bundle =mfs.readFileSync(bundlePath,'utf-8')
const m = new Module()
m._compile(bundle,'server-entry.js')
serverBundle = m.exports.default
})
module.exports = function(app) {
app.use('/public',proxy({
target:'http://localhost:8888'
}))
app.get('*',(req,res)=>{
getTemplate().then(template=>{
const content = ReactDOMServer.renderToString(serverBundle)
res.send(template.replace('<!-- app -->',content))
})
})
}
- 在package.json中的start改成dev:server
"dev:server": "cross-env NODE_ENV=development node server/server.js"
此时服务端渲染配置就好了。分别执行npm run dev:client和npm run dev:server 在浏览器中打开http://localhost:8888 就能看到页面的内容了。
优化工程架构
- 抽取webpack.config.client.js和webpack.config.server.js的公共代码
- 新建webpack.base.js并写入公共代码
const path = require('path'); module.exports = { output:{ path: path.join(__dirname,'../dist'), publicPath: '/public/' }, module: { rules: [ { test: /.jsx$/, loader: 'babel-loader' }, { test: /.js$/, loader: 'babel-loader', exclude: [ path.join(__dirname,'../node_modules') ] } ] }, }- 在webpack.config.client.js和webpack.config.server.js中,将上述代码删除,安装并引入webpack-merge包
npm i webpack-merge -Dconst webpackMerge = require('webpack-merge') const baseConfig = require('./webpack.base') ...省略 //webpack.config.client.js const config = webpackMerge(baseConfig,{ entry: { app: path.join(__dirname,'../client/app.js') }, output: { filename: '[name].[hash].js', }, plugins:[ new HTMlPlugin({ template:path.join(__dirname,'../client/template.html') }) ] }) //webpack.config.server.js module.exports = webpackMerge(webpackBase,{ target:'node', entry: { app: path.join(__dirname,'../client/server-entry.js') }, output: { filename: 'server-entry.js', libraryTarget: 'commonjs2' }, }) - 热加载服务端代码
- 安装nodemon
npm i nodemon -D- 在根目录下创建nodemon.json
{ "restartable":"rs", "ignore": [ ".git", "node_modules/**/node_modules", ".eslintrc", "client", "buildd" ], "env":{ "NODE_ENV":"development" }, "verbose":true, "ext":"js" }- 修改package.json
"dev:server": "nodemon server/server.js",
详细代码github地址