入職第二天:使用koa搭建node server是種怎樣的體驗

碼農土哥發表於2018-05-05

今天是我入職第二天,leader跟我說,昨天配置好了服務端渲染的檔案,今天就先研究研究如何使用koa來搭建一個node server吧!

按照慣例,我去koa官網查了一下什麼是koa,結果官網很簡單的一句話介紹:koa--基於node.js平臺的下一代web開發框架。

個人感覺koa官方文件對於前端小白來說,寫的不是很友好,建議上手之前先看看阮一峰的koa框架教程和廖雪峰寫的關於koa入門文章。

然後引入專案第一步,安裝koa:

npm i koa -S
複製程式碼

安裝完之後,首先在專案根目錄下新建一個server資料夾,然後在此資料夾下新建一個server.js檔案,然後在裡面引入koa:

const Koa = require('koa')
const app = new Koa()
const isDev = process.env.NODE_ENV === 'development'
複製程式碼

這裡為什麼要宣告isDev呢?因為服務端渲染是分開發環境和生產環境兩種不同的情況。

然後我們繼續在server.js裡面先寫一箇中介軟體來記錄所有的請求和抓取的錯誤,這樣可以很好的瞭解到在服務端渲染的過程中是否出現了一些錯誤,並及時排查掉錯誤。

先擼為敬:

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”。

這就是最簡單的一個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是用來幹嘛的?別急,閏土給大家截一張官網圖片看看便一目瞭然了:

入職第二天:使用koa搭建node server是種怎樣的體驗

大意是:一個簡單的記憶體檔案系統。將資料儲存在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了。

入職第二天:使用koa搭建node server是種怎樣的體驗

在服務端渲染期間,使用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裡面。最新的文章都會第一時間更新在我的公眾號<閏土大叔>裡面,歡迎關注。

入職第二天:使用koa搭建node server是種怎樣的體驗

相關文章