Vue SSR 從入門到 Case Study

最近兩個項目同時開發,使用了Vue2的SSR,這樣後端渲染頁面首屏能夠加快頁面呈現,增長SEO和用戶體驗,可是項目上線後卻發現了嚴重的性能問題,因而在三天內兩次重大調整,最後只能放棄Vue SSR,本文從Vue SSR實現開始,逐漸覆盤整個事件。
兩週前就預告了要寫一篇Vue SSR的文章,可是沒想到上週四上線以後,週六放量以後發現性能問題,這週一到週三,作了兩次重大調整,最終仍是放棄了SSR,而且作了此次事件覆盤。html

技術選型

調研Vue已經好久了,隨着Vue2正式發佈,使用Vue來作項目又燃起了但願,不是爲了一時的技術理想和情懷(瞭解個人人都知道,我不是這樣的人),主要是出於下面幾方面考慮:前端

  1. 用artTemplate+Sass+JS作的components方案已經作了好久了,沉澱了不少組件,隨着Node服務開始上線,一直想在此基礎上作同構,而公司Node框架Yog2的view層選擇偏向於Smarty模板的Swig,修改比較麻煩
  2. 既然改不了,那麼要換不如直接選擇新的components方案,此次最強烈需求是:組件化和支持SSR,而Vue2以後支持SSR
  3. 此次兩個項目同時進行,並且僅僅給兩週的開發時間,組件化有效提升工做效率,能夠把通用的組件抽象出來,多個頁面之間業務組件複用率也很高,並且業務組件在後續的運營活動也能夠直接複用
  4. 手百產品形態複雜,頁面即在手百內使用又有手百外使用,手百內頁面被多個Webview隔開,不適合SPA形式,而手百外適合SPA形式,因此一套代碼須要適配兩種狀況,Vue 能夠適應這兩種方式
  5. Vue的SPA形式能夠方便進行PWA和Hybrid改造(繼續關注本公衆號Hybrid系列)

因此,最後決定:上Vue!技術棧:Vue2+Yog2vue

再介紹下兩個項目:webpack

  • 項目A是老項目進行重構,產品需求要跟功能所有保留,架構跑通使用的是Vue2.1,因此A項目代碼相對複雜,一直沒有使用Vue2.2
  • 項目B是新項目,開始使用Vue2.1,上線後發現已經有Vue2.2,因而升級Vue2.2,而且把項目目錄結構調整一番,Webpack config等均可配
    Vue SSR入門到上線

先看下Vue SSR的實現流程圖:
web

簡單解釋一下:ajax

  1. app.js是Server和Client公用的
  2. webpack會根據server-entry.js和client-entry.js打包出來兩個文件:server-bundle和client-bundle
  3. server-bundle用於後端渲染(2.1是js文件,2.2變成json,引入更加方便)

可是這張圖沒有說明在調用API接口方面,先後端是怎麼公用代碼的。前端走的是Ajax請求,後端走的是http請求(百度內部是RAL接口服務管理),結合上圖補充完整的代碼執行流程圖以下:
vuex

webpack區分接口請求方式

在瀏覽器內使用ajax請求,而在服務端須要調用內部API請求或者直接讀取存儲(RAL)。ajax請求到達服務端依次通過:action層、model層,最後走到仍是API請求或者讀取數據(這裏重點讀三遍。。)。json

這裏咱們將服務端和客戶端API的請求方法寫在不一樣的文件內,可是封裝暴漏的接口都是同樣的(接口模式)。在webpack裏面,針對server和client提供不一樣的alias:
後端

這樣 require('api/demo') 的時候,會區分開server和client。api

server內直接使用yog2 modal內的獲取數據方法,好比:

而client內,直接使用ajax請求:

Vue內使用Vuex來獲取數據

即下圖的流程:

在渲染的時候,prefetch階段經過dispatch觸發Store的Action(Action內容許異步),Action內調用 api/demo 獲取數據成功後commit mutation,這樣整個數據就跑通了。

server.js

server.js是第一次渲染使用的入口action,核心代碼以下:

//vue2.2
const vueServerRender = require('vue-server-renderer');
const bundle = require('../vue-ssr-bundle.json');
const renderer = vueServerRender.createBundleRenderer(bundle, {
    template: '<!--vue-ssr-outlet-->',
    cache: require('lru-cache')({
        max: 1000,
        maxAge: 1000 * 60 * 15
    })
})
// 先渲染tpl(swig模板),內容相似vue ssr demo的index.html
// 這裏渲染使用chunk,先輸出不依賴數據的頭部html
res.render('page/index.tpl', { isSendSpeedCode }, (err, html) => {
    if (!err) {
        var htmls = html.split('<!--vue-ssr-outlet-->')
        //先渲染頭部html
        res.write(htmls[0])

        // swig渲染時間
        var time1 = Date.now()
        const renderStream = renderer.renderToStream(context)
        renderStream.on('data', chunk => {
            // 邊解析,邊渲染html
            res.write(chunk)
        })
        renderStream.on('end', () => {

            if (isSendSpeedCode) {
                // 統計vue 渲染時間
                var time2 = Date.now()
                var code = ` <script>if(window.alog){ alog('speed.set', 'p_swig', ${time1 - time0}); alog('speed.set', 'p_vue', ${time2 - time1}); }</script> `
                res.write(code)
            }
            // 渲染尾部html
            res.end(htmls[1])
        }).on('error', errorHandler)
    } else {
        errorHandler(err)
    }
})複製代碼

Webpack和FIS3兩次編譯

webpack是vue「全家桶」的後遺症,項目太急沒辦法去掉。咱們項目的目錄結構以下:

項目須要兩次打包:

  1. 第一次是webpack,webpack把 vue-src文件夾內容根據 server-entryclient-entry打包出來,分別放進yog2的client和server對應的文件,以後 vue-src在執行環境就不須要了
  2. 第二次是FIS3的打包,會按照Yog2的規範release出來能夠上線的內容

這裏有個細節:webpack打包出來的靜態資源路徑須要跟FIS3打包的靜態資源路徑一致,否則就無法經過FIS3進行靜態資源定位,好比替換爲CDN地址。
因爲vue2.2打出來的server-bundle是json格式文件,因此FIS沒法將json內的靜態資源進行統一管理,須要webpack判斷生產環境直接替換爲CDN地址

碰見的其餘問題和技巧

client代碼在server上跑

手百的通用庫Bdbox是client代碼,代碼中有一些window全局變量的使用,而咱們知道Node是沒有 window的,在Node執行SSR的時候,會報錯,好比下面的代碼:

// 自執行
isAndroid: /(Android);?[\s\/]+([\d.]+)?/.test(navigator.userAgent)複製代碼

有兩種改法:

  1. .isAndroid由屬性變成方法:.isAndroid(),放到mount內執行
  2. 給vue-server-renderer傳入帶有navigator.userAgent的context

利用resolve.alias

目錄結構深了,尤爲是Vue裏面還須要調用yog model的代碼,會各類../../很蛋疼,能夠利用alias簡化寫法:

須要注意的是static的寫法是:<img src=「~static/img/logo.png」

利用Yog2 的 Mock功能進行測試

訂好接口請求參數和返回數據格式以後,後端RD進行API的編寫同時,咱們能夠利用Yog2的Mock功能,對ral返回的數據進行假數據測試,實現後端和前端RD解耦,大大提升開發效率。

Vue SSR從上線到Case Study

如今來複盤下整個事件:

  1. 4月5日,完成代碼開發,全功能提測,開始倒騰上線,晚上第一次上線成功,基本功能迴歸沒問題,
  2. 緊接着幾回bug修復上線,6號週四上線日,基本沒有問題了
  3. 4月7日開始APP審覈經過,放量開始,這時候發現隨着流量上升,服務器扛不住了
  4. 8日(週六)緊急添加實例,週末算是硬扛過去了
  5. 10(週一)排查緣由,發現內存可能存在泄漏和性能問題,增長打點統計後端渲染時間,可是VM相對來講是黑盒,性能很差排查
  6. 11日(週二)增長lru-cache,細化組件緩存,下午上線後,晚上發現內存曲線更加嚴重,因而晚上10點回滾lru和組件緩存代碼,隨版本收斂影響,流量繼續上漲,增長機器實例
  7. 12日(週三)採起降級方案,第一次進入頁面將API數據放到以變量形式放到頁面,而後增長beforeCreate階段代碼,將頁面數據直接commit給mutation進行渲染,曲線開始平緩
  8. 13日(週四)觀察一晚曲線沒有問題,中午開始縮容(下線實例)

從週一到週三通過兩次大的調整,終於服務穩定了,其中代碼review階段,咱們也發現了不少代碼不規範的現象。下面來講下咱們使用vue ssr的一些壓測等數據。

單實例QPS、內存和CPU數據

從上線以後,內存積累到必定時間就飆升,內存飆升同時,CPU也進行飆升,具體曲線以下:


從12日(週三19點)上線以後,就開始平穩了,13日中午縮容後,CPU稍有上揚。

同期QPS的數據以下:

查看全文,關注微信公衆號:「三水清」(sanshuiqing123)

相關文章
相關標籤/搜索