今天是我入職次日,leader跟我說,昨天配置好了服務端渲染的文件,今天就先研究研究如何使用koa來搭建一個node server吧!前端
按照慣例,我去koa官網查了一下什麼是koa,結果官網很簡單的一句話介紹:koa--基於node.js平臺的下一代web開發框架。vue
我的感受koa官方文檔對於前端小白來講,寫的不是很友好,建議上手以前先看看阮一峯的koa框架教程和廖雪峯寫的關於koa入門文章。node
而後引入項目第一步,安裝koa:webpack
npm i koa -S
複製代碼
安裝完以後,首先在項目根目錄下新建一個server文件夾,而後在此文件夾下新建一個server.js文件,而後在裏面引入koa:ios
const Koa = require('koa')
const app = new Koa()
const isDev = process.env.NODE_ENV === 'development'
複製代碼
這裏爲何要聲明isDev呢?由於服務端渲染是分開發環境和生產環境兩種不一樣的狀況。web
而後咱們繼續在server.js裏面先寫一箇中間件來記錄全部的請求和抓取的錯誤,這樣能夠很好的瞭解到在服務端渲染的過程當中是否出現了一些錯誤,並及時排查掉錯誤。npm
先擼爲敬:json
app.use(async (ctx, next) => {
try {
console.log(`request with path ${ctx.path}`)
await next()
} catch (err) {
console.log(err)
ctx.status = 500
if (isDev) {
ctx.body = err.message
} else {
ctx.body = 'please try again later'
}
}
})
複製代碼
簡單解釋一下:在函數前面加一個async,就表明異步處理函數,而參數next表示執行下一個 異步處理的函數。在try循環體內,console打印出請求的路徑。若是是isDev爲true的狀況,能夠直接將錯誤信息寫到body裏面,這樣就能夠在頁面上直接看到錯誤信息。若是不是開發環境,能夠寫一個友善的提醒文字,例如:「please try again later」。axios
這就是最簡單的一個koa中間件,用來記錄全部的請求及出現的錯誤,而且返回一個錯誤信息。瀏覽器
接下來,聊一聊如何處理服務端渲染。
在處理服務端渲染以前,首先要在terminal裏面安裝一下koa-router:
npm i koa-router -S
複製代碼
這是koa提供的一個路由的工具。而後在server文件夾下面新建一個routers文件夾,緊接着在裏面新建兩個文件,一個是dev-ssr.js,另外一個是ssr.js。前者是處理開發時服務端渲染的狀況,後者是處理正式環境下的狀況。
在dev-ssr.js文件中,首先要引入koa-router:
const Router = require('koa-router')
複製代碼
在這裏,還須要使用到兩個工具,須要安裝下:
npm i axios -S
複製代碼
npm i memory-fs -D
複製代碼
在node端發送請求的axios,固然也能夠在瀏覽器端發送請求。在安裝的時候記住後面跟的是-S
,由於在業務代碼中能夠用到。
而memory-fs只有在開發的時候纔會用到,因此後面跟的是-D
。可能有童鞋要問了,這個memory-fs是用來幹嗎的?別急,閏土給你們截一張官網圖片看看便一目瞭然了:
大意是:一個簡單的內存文件系統。將數據保存在JavaScript對象中。
而後,話很少說,先把這兩個工具引入進來:
const axios = require('axios')
const MemoryFS = require('memory-fs')
複製代碼
緊接着,再來引入兩個工具:
const webpack = require('webpack')
const VueServerRenderer = require('vue-server-renderer')
複製代碼
由於要在node開發環境中打包代碼,而且須要服務端渲染。
接下來,要引入serverConfig,就是入職第一天寫的那個配置文件webpack.config.server.js:
const serverConfig = require('../../build/webpack.config.server')
複製代碼
而後,如何能在node開發環境中讓webpack跑起來呢? 答案是經過serverCompiler:
const serverCompiler = webpack(serverConfig)
複製代碼
而後去new一個mfs實例:
const mfs = new MemoryFS()
serverCompiler.outputFileSystem = mfs
複製代碼
這樣就指定了webpack的輸出目錄在MemoryFS裏面。
有了這些配置以後,再去聲明一個bundle:
let bundle
複製代碼
用來記錄webpack每次打包生成的新的文件。
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
stats.erros.forEach(err => console.log(err))
stats.hasWarnings.forEach(warn => console.warn(err))
const bundlePath = path.join(
serverConfig.output.path,
'vue-ssr-server-bundle.json'
)
bundle = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
})
複製代碼
這裏使用watch()的好處是:跟使用webpack-dev-server同樣,在client目錄下每次修改一個文件,它都會從新執行一次打包,而後就能夠拿到新的文件了。
serverCompiler.watch()的第一個參數是空對象,第二個參數是一個回調。若是有err直接拋出。
而後stats這塊我感受有點晦澀難懂,leader告訴我說,先照着作,而後有空再去看webpack的文檔。
接下來就能夠讀取生成的bundle文件了,拼接讀取文件的路徑,設置文件名字,而且制定編碼爲utf-8,最後經過JSON.parse()將字符串轉成JSON。
執行完以上步驟以後,就能夠將內容返回給HTML了。
在服務端渲染期間,使用ejs模板引擎生成HTML。經過VueServerRenderer的createBundleRenderer()方法幫助生成一個能夠直接調用renderer的函數。在這裏面接收幾個參數,第一個是inject,設置爲false,這樣它就不會執行其餘的注入的操做了。第二個是clientManifest,它會自動生成一個帶有script標籤的js文件引用的字符串,這樣能夠直接添加到ejs的內容裏面。
最後,dev-ssr.js的完整代碼以下:
const Router = require('koa-router')
const axios = require('axios')
const path = require('path')
const fs = require('fs')
const MemoryFS = require('memory-fs')
const webpack = require('webpack')
const VueServerRenderer = require('vue-server-renderer')
const serverConfig = require('../../build/webpack.config.server')
const serverCompiler = webpack(serverConfig)
const mfs = new MemoryFS()
serverCompiler.outputFileSystem = mfs
let bundle
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
stats.erros.forEach(err => console.log(err))
stats.hasWarnings.forEach(warn => console.warn(err))
const bundlePath = path.join(
serverConfig.output.path,
'vue-ssr-server-bundle.json'
)
bundle = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
})
const handleSSR = async (ctx) => {
if (bundle) {
ctx.body = 'wait a moment...'
return
}
const clientManifestResp = await axios.get(
'http://127.0.0.1:8080/vue-ssr-client-manifest.json'
)
const clientManifest = clientManifestResp.data
const template = fs.readFileSync(
path.join(__dirname, '../server.template.ejs')
)
const renderer = VueServerRenderer
.createBundleRenderer(bundle, {
inject: false,
clientManifest
})
}
複製代碼
此次使用koa搭建node server的體驗只是聊到了renderer這一步,後面我會繼續聊聊如何把bundle渲染成實際的HTML內容,並把它添加到template裏面。最新的文章都會第一時間更新在個人公衆號<閏土大叔>裏面,歡迎關注。