由于配置了webpack-dev-server,客户端启动时,就不必再本地生成dist目录。但是服务器端的编译还是需要本地的dist目录,所以本节我们将会配置服务端的内容,使得服务端也不用依赖本地的dist目录。相关依赖npm i axios // http依赖,估计大家都知道npm i memery-fs -D // 相关接口和node的fs一样,只不过是在内存中生成文件npm i http-proxy-middleware -D // 服务器端一个代理的中间件本章节内容比较难,对于没有接触过node和webpack的同学,理解起来不是那么容易。我也是不知道看了多少遍,才大概知道其流程。开发时的服务端配置以前server.js中要依赖本地dist中的文件,所以首先要对其进行更改。const static = require(’./util/dev-static’)const isDev = process.env.NODE_ENV === ‘development’ // 增加环境的判断const app =express()if(!isDev) { // 生产环境,和以前的处理方式一样 const serverEntry = require(’../dist/server-entry’).default // 配置静态文件目录 app.use(’/public’, express.static(path.join(__dirname, ‘../dist’))) const template = fs.readFileSync(path.join(__dirname, ‘../dist/index.html’), ‘utf-8’) // https://blog.csdn.net/qq_41648452/article/details/80630598 app.get(’’, function(req, res) { const appString = ReactSSR.renderToString(serverEntry) res.send(template.replace(’<!– <app /> –>’, appString)) })} else { // 开发环境,进行单独的处理 static(app)}开发环境中的static方法,位于server/util/dev-static.js中,接受一个app参数。按照生产模式的处理逻辑,开发模式下的配置也分为如下几点:获取打包好的入口文件,即server-entry.js文件获取模板文件将模板文件中的内容替换为server-entry.js中的内容,返回给客户端对静态文件的请求进行处理。获取模板文件获取模板文件最简单,所以最先解决这个问题。配置客户端的devServer时,再http://localhost:8888下面就可以访问到index.html文件,调用下面getTemplate方法就可以拿到模板文件。const axios = require(‘axios’)const getTemplate = () => { return new Promise((resolve, reject) => { axios.get(‘http://localhost:8888/public/index.html’) .then(res => { resolve(res.data) }) .catch(reject) })}获取server-entry.js文件获取服务端的文件,我们需要用到memory-fs包,直接再内存中生成打包好的文件,读取速度更快,那要怎么配置呢?const path = require(‘path’)const webpack = require(‘webpack’)const MemoryFs = require(‘memory-fs’)const serverConfig = require(’../../build/webpack.config.server’) // 读取配置文件// webpack(serverConfig)和我们的build:server命令类似const serverCompile = webpack(serverConfig) // webpack处理const mfs = new MemoryFs()serverCompile.outputFileSystem = mfs // 将文件的输出交给mfs;默认应该是node的fs模块// 监听文件的变化serverCompile.watch({}, (err, stats) => { if(err) throw err // stats对象有一些状态信息,如我们编译过程中的一些错误或警告,我们直接将这些信息打印出来 stats = stats.toJson() stats.errors.forEach(err => console.err(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’)})所以服务端文件也获取到了?其实还是有问题的,我们获取的仅仅是字符串,并不是node中的一个模块(如果听不懂,先去补补node中模块的概念),所以还需要做进一步的处理。const Module = module.constructor// node中,每个文件中都有一个Module变量,不懂的就要多学习了const m = new Module() m._compile(bundle, serverConfig.output.filename) // 将字符串编译为一个模块serverBundle = m.exports.default // 这才是我们需要的内容替换内容app.get(’’, function (req, res) { getTemplate().then(template => { const content = ReactDomSSR.renderToString(serverBundle) res.send(template.replace(’<!– <app /> –>’, content)) }) })静态资源处理和模板文件一样,静态资源我们将会代理到localhost:8888里面去获取 const proxy = require(‘http-proxy-middleware’) app.use(’/public’, proxy({ target: ‘http://localhost:8888’ }))到这里,开发时服务端渲染就完成了。本小节完整代码位于仓库的2-8分支,觉得有用的可以去start一下。