因爲配置了webpack-dev-server,客戶端啓動時,就沒必要再本地生成dist目錄。可是服務器端的編譯仍是須要本地的dist目錄,因此本節咱們將會配置服務端的內容,使得服務端也不用依賴本地的dist目錄。html
本章節內容比較難,對於沒有接觸過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
獲取模板文件最簡單,因此最早解決這個問題。配置客戶端的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) }) }
獲取服務端的文件,咱們須要用到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