虽然Vue的服务器端渲染(SSR)相当快速,但是由于需要为每次请求为了避免交叉请求状态污染,都创建一个新的根Vue实例,创建组件实例和虚拟DOM节点的开销,无法与纯基于字符串拼接的模板的性能相当。在SSR性能至关重要的情况下,明智地利用缓存策略,可以极大改善响应时间并减少服务器负载,同时还可以大大减少后端接口服务器的负载。
在vue SSR指南中,缓存有两种,分为页面级缓存和组件级缓存。本次讲的是页面缓存,如果内容不是用户特定的并且在相对较短时间内,页面内容不需要更新。我们就可以使用页面缓存。对于页面级缓存我们可以通过这段亚服务器的代码大概知道缓存的思路:
const microCache=LRU ({ 马克斯:100, maxAge: 1000//重要提示:条目在1秒后过期。 }) const isCacheable=要求=比;{//实现逻辑为,检查请求是否是用户特定(特定的)。//只有非用户特定(non-user-specific)页面才会缓存 } 服务器。get(‘*’(点播,res)=比;{ const缓存=isCacheable(要求) 如果(缓存){ const打=microCache.get (req.url) 如果(){ 返回res.end(打击) } } 渲染器。renderToString((呃,html)=比;{ res.end (html) 如果(缓存){ microCache.set(点播。url, html) } }) })
流程图如下:
上面的代码为vue的ssr渲染提供了方案,但是对于使用nuxt框架的同学而言,用脚手架初始化完,框架对于vue服务端渲染的res.end()函数做了高度封装,从下图nuxt在接收到请求后进行渲染的流程可以看的出,nuxt主要是通过nuxtMiddleware调用renderRoute()来进行渲染的:
那么我们是否可以通过重写renderRoute()这个api拦截其内部渲染逻辑,在渲染之前加上缓存呢? nuxt-ssr-cache插件已经这样做了。我们来看一下这个nuxt模块核心部分的源码:
const渲染器=nuxt.renderer; const renderRoute=renderer.renderRoute.bind(渲染器); 渲染器。renderRoute=函数(路线、上下文){//希望缓存复位结束了这一点。 tryStoreVersion(缓存,currentVersion); const cacheKey=(config.cache。关键| | defaultCacheKeyBuilder)(路线、上下文); 如果(! cacheKey)返回renderRoute(路线、上下文); 函数renderSetCache () { 返回renderRoute(路线、上下文) 不要犹豫(函数(结果){ 如果(! result.error) { 缓存。setAsync (cacheKey序列化(结果)); } 返回结果; }); } 返回cache.getAsync (cacheKey) 不要犹豫(函数(cachedResult) { 如果(cachedResult) { 返回反序列化(cachedResult); } 返回renderSetCache (); }) .catch (renderSetCache);};
在这段代码中,先保存了渲染器原来的renderRoute代码,之后又重写了renderRoute代码,返回了一个通过缓存缓存来获取缓存内容的逻辑.cache返回了一个承诺,如果是解决的,并且有缓存的内容,就直接返回缓存内容。如果没有缓存内容或者拒绝,就执行renderSetCache()。而renderSetCache()中,返回了原来最初的renderRoute()处理逻辑,同样如果renderRoute()返回的承诺被解决了,那么就通过缓存的setAsync方法来进行缓存,之后返回渲染结果。
使用方法大家自行参考git中的readme文档,这里就不说了。
下面我们真正来仿真一下,看看这个模块的功效到底如何。我们通过ab命令
ab - n 4000 - c - s 120 - r http://localhost: 3000/
来进行压测:
第一种情况,没有添加页面缓存,大约持续请求了10秒钟,执行到3600个请求的时候,发生错误,不再继续请求了:
我们来通过日志看下是什么错误:
可以看到致命错误这一句,JavaScript堆内存,堆内存已经没有办法再进行分配,所以进程终止了。
我们在终止之前通过进程监视器可以看到节点进程已经彪到了1.7 gb的内存。