最近兩個項目同時開發,使用了Vue2的SSR,這樣後端渲染頁面首屏能夠加快頁面呈現,增長SEO和用戶體驗,可是項目上線後卻發現了嚴重的性能問題,因而在三天內兩次重大調整,最後只能放棄Vue SSR,本文從Vue SSR實現開始,逐漸覆盤整個事件。
兩週前就預告了要寫一篇Vue SSR的文章,可是沒想到上週四上線以後,週六放量以後發現性能問題,這週一到週三,作了兩次重大調整,最終仍是放棄了SSR,而且作了此次事件覆盤。html
調研Vue已經好久了,隨着Vue2正式發佈,使用Vue來作項目又燃起了但願,不是爲了一時的技術理想和情懷(瞭解個人人都知道,我不是這樣的人),主要是出於下面幾方面考慮:前端
因此,最後決定:上Vue!技術棧:Vue2+Yog2。vue
再介紹下兩個項目:webpack
先看下Vue SSR的實現流程圖:
web
簡單解釋一下:ajax
可是這張圖沒有說明在調用API接口方面,先後端是怎麼公用代碼的。前端走的是Ajax請求,後端走的是http請求(百度內部是RAL接口服務管理),結合上圖補充完整的代碼執行流程圖以下:
vuex
在瀏覽器內使用ajax請求,而在服務端須要調用內部API請求或者直接讀取存儲(RAL)。ajax請求到達服務端依次通過:action層、model層,最後走到仍是API請求或者讀取數據(這裏重點讀三遍。。)。json
這裏咱們將服務端和客戶端API的請求方法寫在不一樣的文件內,可是封裝暴漏的接口都是同樣的(接口模式)。在webpack裏面,針對server和client提供不一樣的alias:
後端
這樣 require('api/demo')
的時候,會區分開server和client。api
server內直接使用yog2 modal內的獲取數據方法,好比:
而client內,直接使用ajax請求:
即下圖的流程:
在渲染的時候,prefetch階段經過dispatch觸發Store的Action(Action內容許異步),Action內調用 api/demo
獲取數據成功後commit mutation,這樣整個數據就跑通了。
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是vue「全家桶」的後遺症,項目太急沒辦法去掉。咱們項目的目錄結構以下:
項目須要兩次打包:
vue-src
文件夾內容根據 server-entry
和 client-entry
打包出來,分別放進yog2的client和server對應的文件,以後 vue-src
在執行環境就不須要了這裏有個細節:webpack打包出來的靜態資源路徑須要跟FIS3打包的靜態資源路徑一致,否則就無法經過FIS3進行靜態資源定位,好比替換爲CDN地址。
因爲vue2.2打出來的server-bundle是json格式文件,因此FIS沒法將json內的靜態資源進行統一管理,須要webpack判斷生產環境直接替換爲CDN地址
手百的通用庫Bdbox是client代碼,代碼中有一些window
全局變量的使用,而咱們知道Node是沒有 window
的,在Node執行SSR的時候,會報錯,好比下面的代碼:
// 自執行
isAndroid: /(Android);?[\s\/]+([\d.]+)?/.test(navigator.userAgent)複製代碼
有兩種改法:
.isAndroid
由屬性變成方法:.isAndroid()
,放到mount
內執行navigator.userAgent
的context目錄結構深了,尤爲是Vue裏面還須要調用yog model的代碼,會各類../../
很蛋疼,能夠利用alias簡化寫法:
須要注意的是static
的寫法是:<img src=「~static/img/logo.png」
訂好接口請求參數和返回數據格式以後,後端RD進行API的編寫同時,咱們能夠利用Yog2的Mock功能,對ral返回的數據進行假數據測試,實現後端和前端RD解耦,大大提升開發效率。
如今來複盤下整個事件:
從週一到週三通過兩次大的調整,終於服務穩定了,其中代碼review階段,咱們也發現了不少代碼不規範的現象。下面來講下咱們使用vue ssr的一些壓測等數據。
從上線以後,內存積累到必定時間就飆升,內存飆升同時,CPU也進行飆升,具體曲線以下:
從12日(週三19點)上線以後,就開始平穩了,13日中午縮容後,CPU稍有上揚。
同期QPS的數據以下:
查看全文,關注微信公衆號:「三水清」(sanshuiqing123)