5.开发时服务端渲染

因为配置了webpack-dev-server,客户端启动时,就没必要再本地生成dist目录。可是服务器端的编译仍是须要本地的dist目录,因此本节咱们将会配置服务端的内容,使得服务端也不用依赖本地的dist目录。html

相关依赖

  • npm i axios // http依赖,估计你们都知道
  • npm i memery-fs -D // 相关接口和node的fs同样,只不过是在内存中生成文件
  • npm i http-proxy-middleware -D // 服务器端一个代理的中间件

本章节内容比较难,对于没有接触过node和webpack的同窗,理解起来不是那么容易。我也是不知道看了多少遍,才大概知道其流程。node

开发时的服务端配置

之前server.js中要依赖本地dist中的文件,因此首先要对其进行更改。webpack

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参数。按照生产模式的处理逻辑,开发模式下的配置也分为以下几点:ios

  1. 获取打包好的入口文件,即server-entry.js文件
  2. 获取模板文件
  3. 将模板文件中的内容替换为server-entry.js中的内容,返回给客户端
  4. 对静态文件的请求进行处理。

获取模板文件

获取模板文件最简单,因此最早解决这个问题。配置客户端的devServer时,再http://localhost:8888下面就能够访问到index.html文件,调用下面getTemplate方法就能够拿到模板文件。git

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包,直接再内存中生成打包好的文件,读取速度更快,那要怎么配置呢?github

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中模块的概念),因此还须要作进一步的处理。web

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里面去获取express

const proxy = require('http-proxy-middleware')

  app.use('/public', proxy({
    target: 'http://localhost:8888'
  }))

到这里,开发时服务端渲染就完成了。npm

本小节完整代码位于仓库的2-8分支,以为有用的能够去start一下。axios

相关文章
相关标签/搜索