查看: 3374|回復: 0

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

發表于 2018-5-12 08:00:02

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

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

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

然后引入項目第一步,安裝koa:

  1. npm i koa -S
復制代碼

安裝完之后,首先在項目根目錄下新建一個server文件夾,然后在此文件夾下新建一個server.js文件,然后在里面引入koa:

  1. const Koa = require('koa')
  2. const app = new Koa()
  3. const isDev = process.env.NODE_ENV === 'development'
復制代碼

這里為什么要聲明isDev呢?因為服務端渲染是分開發環境和生產環境兩種不同的情況。

然后我們繼續在server.js里面先寫一個中間件來記錄所有的請求和抓取的錯誤,這樣可以很好的了解到在服務端渲染的過程中是否出現了一些錯誤,并及時排查掉錯誤。

先擼為敬:

  1. app.use(async (ctx, next) => {
  2. try {
  3. console.log(`request with path ${ctx.path}`)
  4. await next()
  5. } catch (err) {
  6. console.log(err)
  7. ctx.status = 500
  8. if (isDev) {
  9. ctx.body = err.message
  10. } else {
  11. ctx.body = 'please try again later'
  12. }
  13. }
  14. })
復制代碼

簡單解釋一下:在函數前面加一個async,就代表異步處理函數,而參數next表示執行下一個 異步處理的函數。在try循環體內,console打印出請求的路徑。如果是isDev為true的情況,可以直接將錯誤信息寫到body里面,這樣就可以在頁面上直接看到錯誤信息。如果不是開發環境,可以寫一個友善的提醒文字,例如:“please try again later”。

這就是最簡單的一個koa中間件,用來記錄所有的請求及出現的錯誤,并且返回一個錯誤信息。

接下來,聊一聊如何處理服務端渲染。

在處理服務端渲染之前,首先要在terminal里面安裝一下koa-router:

  1. npm i koa-router -S
復制代碼

這是koa提供的一個路由的工具。然后在server文件夾下面新建一個routers文件夾,緊接著在里面新建兩個文件,一個是dev-ssr.js,另一個是ssr.js。前者是處理開發時服務端渲染的情況,后者是處理正式環境下的情況。

在dev-ssr.js文件中,首先要引入koa-router:

  1. const Router = require('koa-router')
復制代碼

在這里,還需要使用到兩個工具,需要安裝下:

  1. npm i axios -S
  2. npm i memory-fs -D
復制代碼

在node端發送請求的axios,當然也可以在瀏覽器端發送請求。在安裝的時候記住后面跟的是-S,因為在業務代碼中可以用到。

而memory-fs只有在開發的時候才會用到,所以后面跟的是-D。可能有童鞋要問了,這個memory-fs是用來干嘛的?別急,閏土給大家截一張官網圖片看看便一目了然了:

圖片描述

大意是:一個簡單的內存文件系統。將數據保存在JavaScript對象中。

然后,話不多說,先把這兩個工具引入進來:

  1. const axios = require('axios')
  2. const MemoryFS = require('memory-fs')
復制代碼

緊接著,再來引入兩個工具:

  1. const webpack = require('webpack')
  2. const VueServerRenderer = require('vue-server-renderer')
復制代碼

因為要在node開發環境中打包代碼,并且需要服務端渲染。

接下來,要引入serverConfig,就是入職第一天寫的那個配置文件webpack.config.server.js:

  1. const serverConfig = require('../../build/webpack.config.server')
復制代碼

然后,如何能在node開發環境中讓webpack跑起來呢?

答案是通過serverCompiler:

  1. const serverCompiler = webpack(serverConfig)
復制代碼

然后去new一個mfs實例:

  1. const mfs = new MemoryFS()
  2. serverCompiler.outputFileSystem = mfs
復制代碼

這樣就指定了webpack的輸出目錄在MemoryFS里面。

有了這些配置之后,再去聲明一個bundle:

  1. let bundle
復制代碼

用來記錄webpack每次打包生成的新的文件。

  1. serverCompiler.watch({}, (err, stats) => {
  2. if (err) throw err
  3. stats = stats.toJson()
  4. stats.erros.forEach(err => console.log(err))
  5. stats.hasWarnings.forEach(warn => console.warn(err))
  6. const bundlePath = path.join(
  7. serverConfig.output.path,
  8. 'vue-ssr-server-bundle.json'
  9. )
  10. bundle = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
  11. })
復制代碼

這里使用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的完整代碼如下:

  1. const Router = require('koa-router')
  2. const axios = require('axios')
  3. const path = require('path')
  4. const fs = require('fs')
  5. const MemoryFS = require('memory-fs')
  6. const webpack = require('webpack')
  7. const VueServerRenderer = require('vue-server-renderer')
  8. const serverConfig = require('../../build/webpack.config.server')
  9. const serverCompiler = webpack(serverConfig)
  10. const mfs = new MemoryFS()
  11. serverCompiler.outputFileSystem = mfs
  12. let bundle
  13. serverCompiler.watch({}, (err, stats) => {
  14. if (err) throw err
  15. stats = stats.toJson()
  16. stats.erros.forEach(err => console.log(err))
  17. stats.hasWarnings.forEach(warn => console.warn(err))
  18. const bundlePath = path.join(
  19. serverConfig.output.path,
  20. 'vue-ssr-server-bundle.json'
  21. )
  22. bundle = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
  23. })
  24. const handleSSR = async (ctx) => {
  25. if (bundle) {
  26. ctx.body = 'wait a moment...'
  27. return
  28. }
  29. const clientManifestResp = await axios.get(
  30. 'http://127.0.0.1:8080/vue-ssr-client-manifest.json'
  31. )
  32. const clientManifest = clientManifestResp.data
  33. const template = fs.readFileSync(
  34. path.join(__dirname, '../server.template.ejs')
  35. )
  36. const renderer = VueServerRenderer
  37. .createBundleRenderer(bundle, {
  38. inject: false,
  39. clientManifest
  40. })
  41. }
復制代碼
寫在最后

這次使用koa搭建node server的體驗只是聊到了renderer這一步,后面我會繼續聊聊如何把bundle渲染成實際的HTML內容,并把它添加到template里面。最新的文章都會第一時間更新在我的公眾號里面,歡迎關注。



回復

使用道具 舉報