背景
為了首屏渲染更快,讓使用者更快看到內容,網易易盾開啟了移動端官網同構建設,即一套程式碼兩端執行, 客戶端程式碼直接執行在服務端, 無需編寫冗餘的渲染檢視。
改造前,官網專案的技術棧較大眾,以 egg.js 為後端框架,Nunjuck 為模板,前端採用 jQuery。
本次則採用Vue 作為前端框架進行了部分改進,團隊成員的技術儲備也基本上建立在 Vue 的技術體系上,從以下3方面出發,重整技術選型,全面改善易盾的官網生態。
統一技術棧:Nunjuck 模板很強大,但和我們習慣用的 Vue 語法還是有些不同。而 jQuery 屬於舊時代的產物,資料驅動檢視的思想已經深入人心。
使用者體驗:同構後的前端能兼顧 SEO 和 SPA 的互動體驗。
開發體驗:更易和現代的開發構建工具整合,如 webpack、ESLint、SASS 等。
技術選型
方案一:Vue SSR 官方指南 https://ssr.vuejs.org/zh/
專案地址:https://github.com/vuejs/vue-hackernews-2.0/
優缺點分析:
1.自由度最高,不依賴 node 框架和 vue 相關任何生態
2.需要的工作量最大,主要是 vue-ssr 通用邏輯 & 前端構建配置的實現
3.可複用目前基於 eggjs 的後端邏輯
方案二:nuxt.js
優缺點分析:
1.完善的解決方案,提供了許多開箱即用的功能,能大大減少很多配置和通用邏輯實現
2.不依賴 node 框架,主要關注的是應用的 UI 渲染
3.需要完成 egg.js 後端 api 介面的邏輯遷移
方案三:egg.js 和 nuxt.js 結合
以 egg.js 為主,nuxt.js 降級為 egg.js 一箇中介軟體。這種方案可行得益於 nuxt.js 不依賴 node web 框架。
1.複用目前 node 端邏輯,僅對檢視做遷移
2.利用 nuxt.js 提供的開箱即用的特性,無需關注 vue-ssr 通用邏輯實現和前端複雜的構建配置
3.工作量之一需要完成 eggjs 和 nuxt.js 的結合,提供一個 eggjs 中介軟體
方案三既能利用了 nuxt.js 的能力,且無需對目前 node 端邏輯做改動,很好的滿足了我們的需求。基於該方案的流程圖如下:
落地過程
【egg-nuxt-render 外掛的實現】
外掛中介軟體的實現關注點:
請求未命中 egg 路由時會返回 404,中介軟體處理 404 請求,修改 status 為 200,後續交於 nuxt.js 處理。如果 nuxt 路由中也未命中,由 nuxt 提供 404 頁面。
傳遞 egg ctx,使得 nuxt 中能獲取到 egg 的上下文。
呼叫 nuxt.render,即完成將當次請求交於 nuxt.js 處理。
【專案目錄】
至此,我們可以這樣設計我們的目錄結構。僅需在 eggjs 根目錄下新增 client 目錄,來放置 nuxt.js 的所需的各種目錄(每個目錄的含義可參考 nuxt 官方文件)。nuxt.config.js 為 nuxt 的配置檔案,會被 egg-nuxt-render 外掛讀取。
【頁面遷移】
到了這一步,我們就可以開始遷移我們的頁面了,即 Nunjuck 模板重構為 vue 模板。刪除原先 egg router 中的頁面路由定義,然後在 client/pages 裡新增對應的頁面.vue。這一步是體力活,也是本次同構最費時間的工作。看到這裡有人可能會疑惑,這種方案需要要求所有的頁面一次性重構完成,如果專案很龐大,沒辦法一次性重構完或者或者一次性重構完風險極高的情況下,是不是就很難實施了?考慮到這種合理的情況,設計了另一個相容方案,給大家提供一種思路。
【相容方案】
因為我們團隊的專案規模可控,故採用一次性重構完所有頁面。
方案流程圖如下:
注意:本方案未經線上測試,僅供參考
這種方案的核心想法是先渲染 Nunjuck 模板,然後透過 ctx 將渲染結果傳遞到 nuxt 的 vue 模板完成最終渲染。主要可以關注以下幾點:
1.保留 eggjs 頁面路由的定義,呼叫 htmlRender 將 Nunjuck 模板轉化成 html 字串,並且儲存到ctx.__HTML_RENDER__。此時需要將 status 置為 404。
2.由於 status 為 404,會執行 egg-nuxt-render 中介軟體,進入 nuxt 路由。但此時 nuxt 中並沒有該路由(因為該頁面未重構),所以我們將其指到同一個元件,即 NjkWraper.vue。
3.NjkWraper.vue 負責渲染 __HTML_RENDER__ 內容。__HTML_RENDER__ 應僅包含每個頁面的主體部分,不包含頁面通用部分(如導航、底部等),此部分由 vue 實現。
4.由於所有的路由都會命中 NjkWraper.vue,會導致真實的 404 請求丟失。所以我們還需要做一步判斷該請求是不是真實的 404 請求。如果是,則需要重定向到 404 頁面。這一步可在 vue router 守衛中實現。
至此,我們只要先將頁面通用部分,如導航、底部等,透過 vue 重構,具體頁面後面可以慢慢遷移。
相容方案存在的弊端:
頁面間跳轉必須重新整理頁面。因為最終輸出到瀏覽器的頁面中會包含 vue 元件 和 Nunjuck 渲染出來的內容,其中 Nunjuck 內容中可能存在 jquery、regular 等各種庫,重複引入恐有問題。
所有頁面重構完後,相容程式碼需要刪除。
總結
本次的同構實踐本質上是對已有專案的重構,考慮了系統穩定性和重構成本,故未對 egg.js 主框架做調整。當開啟一個新的專案時,直接採用 nuxt.js 提供的完整的解決方案會更加純粹。