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

相關文章
相關標籤/搜索