雖然 Vue 的伺服器端渲染 (SSR) 相當快速,但是由於需要為每次請求為了避免交叉請求狀態汙染,都建立一個新的根Vue例項,建立元件例項和虛擬 DOM 節點的開銷,無法與純基於字串拼接的模板的效能相當。在 SSR 效能至關重要的情況下,明智地利用快取策略,可以極大改善響應時間並減少伺服器負載。同時還可以大大減少後端介面伺服器的負載。
在vue SSR指南中,快取有兩種,分為頁面級快取和元件級快取。本次講的是頁面快取,如果內容不是使用者特定的並且在相對較短時間內,頁面內容不需要更新。我們就可以使用頁面快取。對於頁面級快取我們可以通過這段koa伺服器的程式碼大概知道快取的思路:
const microCache = LRU({
max: 100,
maxAge: 1000 // 重要提示:條目在 1 秒後過期。
})
const isCacheable = req => {
// 實現邏輯為,檢查請求是否是使用者特定(user-specific)。
// 只有非使用者特定 (non-user-specific) 頁面才會快取
}
server.get('*', (req, res) => {
const cacheable = isCacheable(req)
if (cacheable) {
const hit = microCache.get(req.url)
if (hit) {
return res.end(hit)
}
}
renderer.renderToString((err, html) => {
res.end(html)
if (cacheable) {
microCache.set(req.url, html)
}
})
})
複製程式碼
流程圖如下:
上面的程式碼為vue的ssr渲染提供了方案,但是對於使用nuxt框架的同學而言,用腳手架初始化完,框架對於vue服務端渲染的res.end()函式做了高度封裝,從下圖nuxt在接收到請求後進行渲染的流程可以看出,nuxt主要是通過nuxtMiddleware呼叫renderRoute()來進行渲染的:
那麼我們是否可以通過重寫renderRoute()這個api攔截其內部渲染邏輯,在渲染之前加上快取呢?nuxt-ssr-cache外掛已經這樣做了。我們來看一下這個nuxt模組核心部分的原始碼:
const renderer = nuxt.renderer;
const renderRoute = renderer.renderRoute.bind(renderer);
renderer.renderRoute = function(route, context) {
// hopefully cache reset is finished up to this point.
tryStoreVersion(cache, currentVersion);
const cacheKey = (config.cache.key || defaultCacheKeyBuilder)(route, context);
if (!cacheKey) return renderRoute(route, context);
function renderSetCache(){
return renderRoute(route, context)
.then(function(result) {
if (!result.error) {
cache.setAsync(cacheKey, serialize(result));
}
return result;
});
}
return cache.getAsync(cacheKey)
.then(function (cachedResult) {
if (cachedResult) {
return deserialize(cachedResult);
}
return renderSetCache();
})
.catch(renderSetCache);
};
複製程式碼
在這段程式碼中,先儲存了renderer原來的renderRoute程式碼,之後又重寫了renderRoute程式碼,返回了一個通過cache快取來獲取快取內容的邏輯。cache返回了一個promise,如果是resolve的,並且有快取的內容,就直接返回快取內容。如果沒有快取內容或者reject,就執行renderSetCache()。而renderSetCache()中,返回了原來最初的renderRoute()處理邏輯,同樣如果renderRoute()返回的promise被resolve了,那麼就通過cache的setAsync方法來進行快取,之後返回渲染結果。
使用方法大家自行參考git中的readme文件,這裡就不說了。
下面我們真正來模擬一下,看看這個模組的功效到底如何。我們通過ab命令
ab -n 4000 -c 50 -s 120 -r http://localhost:3000/
複製程式碼
來進行壓測:
第一種情況,沒有新增頁面快取,大約持續請求了10秒鐘,執行到3600個請求的時候,發生錯誤,不再繼續請求了:
我們來通過日誌看下是什麼錯誤:
可以看到FATAL ERROR這一句,JavaScript heap out of memory。堆記憶體已經沒有辦法再進行分配,所以程式終止了。
我們在終止之前通過程式監視器可以看到node程式已經彪到了1.7GB的記憶體。
第二種情況,我們新增了頁面快取,通過server端的日誌,我們可以看出,只請求了一次後端的api資料介面,說明快取已經成功攔截了頁面請求。請求資料如下:
在2秒鐘之內,就順利結束了4000個請求,記憶體沒有任何明顯波動,優化效果顯而易見。
更多精彩文章請關注公眾號: