性能優(yōu)化行嗤,這是面試中經(jīng)常會聊到的話題。我覺得性能優(yōu)化應(yīng)該因具體場景而異描姚,因不同項目而異,不同的手段不同的方案并不一定適合所有項目戈次,當(dāng)然這其中不乏一些普適的方案轩勘,比如耳熟能詳?shù)奈募嚎s,文件緩存怯邪,CDN绊寻,DNS 預(yù)解析,等等悬秉,但是我更希望聽到的是因為不同的項目不同的需求澄步,解決不同的問題而采取的不同的優(yōu)化手段,比如 BigPipe和泌,分段輸出頁面的各個部分村缸,對于 SNS 網(wǎng)站是非常合適的,減少了用戶的等待時間武氓;相對應(yīng)的還有一個 BigRender梯皿,這是一個大的延遲加載仇箱,360 導(dǎo)航首頁目前還在使用,京東淘寶首頁也是這個思路东羹,對于一些類門戶網(wǎng)站非常適用剂桥,但是如果你的網(wǎng)頁內(nèi)容不是非常多,就沒有必要了
今天要說的是 Nuxt属提。Nuxt 是支持 Vue SSR 的一個框架权逗,底層需要運行 Node 服務(wù)。大概描述一下 Vue 的渲染過程冤议,首先每個組件都會被編譯生成一個渲染函數(shù)(這部分基本 webpack 打包已經(jīng)做掉)斟薇,然后渲染函數(shù)生成虛擬 dom,最后虛擬 dom 通過 patch 方法將真實 dom 渲染到頁面上求类。Nuxt 其實就是將這部分放到了服務(wù)端去做,在服務(wù)端拿到渲染頁面所需要的 html屹耐,從而使得 html 能夠直出尸疆,而客戶端其實還是會運行整個 Vue 的生命周期,這就帶來了一個問題惶岭,這部分操作放在了服務(wù)端其實是非常耗 cpu 的寿弱,創(chuàng)建組件實例和虛擬 DOM 節(jié)點的開銷,無法與純基于字符串拼接的模版的性能相當(dāng)按灶,如果是不加優(yōu)化的 Nuxt 項目症革,高并發(fā)下是很脆弱的,畢竟 Node 運行在單線程下鸯旁,不適合 cpu 操作密集型的場景
使用 Nuxt 的項目無非看中了它的兩大優(yōu)點噪矛,一是服務(wù)端渲染滿足 SEO 的需求,二是首屏直出比 SPA 快铺罢,再加上如果如果公司是 Vue 系艇挨,使用 Nuxt 就更順理成章。但是不要忘了性能韭赘,高并發(fā)下 Nuxt 性能確實不樂觀缩滨,我測試了官網(wǎng)的 hackernews demo 項目,2 核 cpu + 4g 內(nèi)存泉瞻,400 并發(fā)下它的吞吐量不超過 50脉漏,就算是最簡的 Nuxt 項目,吞吐量也就 300+袖牙,這就說明如果項目不做緩存侧巨,300+ 已經(jīng)是最大的吞吐量了,而最小 express demo 可以輕松到 3000鞭达,這就決定了高流量項目并不會輕易去使用 Nuxt
我們的項目目前其實是一個不加優(yōu)化的 Nuxt 項目刃泡,因為用戶不多巧娱,平時并沒有什么問題,但是一到展會烘贴,就會有不少用戶同時訪問禁添,反饋頁面會很卡。同條件下做了壓測后桨踪,吞吐量也是 50 上下老翘,平均響應(yīng)時長七八秒,所以卡是正扯屠耄現(xiàn)象
看了一下項目代碼铺峭,發(fā)現(xiàn)了幾個問題:
項目沒做緩存,所以每次訪問都會經(jīng)歷所有 Nuxt 生命周期汽纠,消耗 cpu卫键,這點是最致命的
-
項目打包默認(rèn) gzip。Nuxt 項目打包會默認(rèn)在服務(wù)端開啟 gzip虱朵,因為我們網(wǎng)關(guān)層已經(jīng)做了 gzip莉炉,所以這里是不必要的,測試了下關(guān)掉 gzip 吞吐量和響應(yīng)時間都能提高 20% 左右碴犬。具體做法是在 nuxt.config.js 中配置(還是得看 英文文檔絮宁,會告訴你如何不設(shè)置
To disable compression, use compressor: false
,中文文檔當(dāng)時三月份我寫這文的時候還沒加這個選項服协,而目前中文文檔也沒有翻譯這一句 2020-07-16)render: { compressor: false }
API 請求比較亂绍昂。很多請求并沒有很好地區(qū)分客戶端和服務(wù)端,而是都由服務(wù)端去做了偿荷,造成服務(wù)端壓力過大窘游,其實多數(shù)和用戶有關(guān)的請求理應(yīng)放到客戶端。有的接口為了方便跳纳,一次性返回了所有內(nèi)容张峰,也沒有做客戶端/服務(wù)端區(qū)分。另外棒旗,服務(wù)端的接口請求可以并發(fā),用類似 Promise.all 的形式去控制
SEO铣揉。有的內(nèi)容頁面,很長逛拱,有五個部分,除了內(nèi)容外朽合,還有猜你喜歡等其他部分俱两,詢問了 SEO 同事饱狂,說這幾部分都是需要 SEO 的宪彩,我不是很懂 SEO休讳,但是在我看來尿孔,ssr 只應(yīng)該渲染首屏內(nèi)容俊柔,而 UI 在設(shè)計的時候應(yīng)該把主要內(nèi)容設(shè)計到首屏,從而滿足 SEO
對此我覺得可以從兩個方向去優(yōu)化:
-
緩存活合。緩存是最重要的方案雏婶,針對 Nuxt 項目可以做三級緩存白指,頁面緩存、組件緩存以及 API 緩存告嘲。頁面緩存是最重量級的緩存方案,能不能做頁面緩存可以從以下兩個點判斷:
- 同一個 URL需五,對于 登錄 / 非登錄 用戶轧坎,服務(wù)端渲染的內(nèi)容是相同的(注意是服務(wù)端渲染內(nèi)容,而非前端)
- 同一個 URL缸血,對于不同的登錄用戶械筛,服務(wù)端渲染的內(nèi)容是相同的,即沒有一些個性化的渲染(常見的個性化渲染笆豁,比如針對不同用戶渲染不同的猜你喜歡內(nèi)容等)
其實也就是返回的 html 代碼相同就好赤赊,主要關(guān)注下返回的全局 store 是否一致,另外也不能做一些服務(wù)端才能做的操作抛计,比如 set-cookie 等
控制好首屏模塊個數(shù),對返回的結(jié)果進行精簡瘦陈,最小化,保證吐出到瀏覽器的內(nèi)容足夠小晨逝。這就是前面說的并不要對所有模塊都做 ssr,需要首屏呈現(xiàn)的/需要爬蟲爬的趴生,我們直出昏翰,其他部分做 CSR 就行了
而我們的網(wǎng)站大部分頁面是滿足做頁面緩存條件的,測試了下如果做頁面緩存棚菊,吞吐量能到 500+,這個數(shù)據(jù)這個時候其實是和頁面大小有關(guān)系了检碗,頁面緩存的性能是能滿足需求的码邻。而有另一類頁面,相同的 URL 會返回不同的內(nèi)容像屋,而且整頁都是不同內(nèi)容,它的實現(xiàn)是獲取 cookie 中的不同 city-id奏甫,渲染不同城市的內(nèi)容凌受,很顯然這部分頁面做不了頁面緩存了,API 緩存和組件緩存理論上都是可以試試的
做緩存優(yōu)化挠进,至少需要訪問一次誊册,第二次才能生效,那么還有另一種情況解虱,對于這樣的路由 /store/:id
,并發(fā)打開 id 0~1000于宙,很顯然每個頁面都是不一樣的店鋪數(shù)據(jù),并不能命中緩存(可能命中組件緩存捞魁,暫時忽略),這個時候只能從 Nuxt 生命周期上去優(yōu)化了谱俭,那么以上方向的第二點,控制首屏模塊個數(shù)就能用到了县貌。所以本文一開始我就說凑懂,不同的方案是適配不同的場景的,解決不同的問題會采取不同的手段