基于Nuxt.js的服务端缓存优化

4,794 阅读3分钟

前言

这个服务端优化的想法是我在一篇文章看到的,可惜不记得文章的链接了,如果有印象的读者可以评论一下文章来源,谢谢。

背景

用Nuxt.js开发的网站,因为依赖于服务端的功能,无法使用nuxt generate去生成静态文件,用Nginx托管站点。

对于部分页面数据较多的页面,请求接口以及渲染页面的时间过长,影响用户体验,所以研究了缓存优化方案,去优化部分页面性能。

缓存使用到的算法

在这里我用的是LRU库,LRU缓存的核心在于能够把最不常使用的数据移除,让给最新读取的数据。如果有想要详细了解该算法的可以参考该文章: LRU算法

实现思路

  1. 在server端判断用户所访问的路径,若该路径是所需要缓存的页面,则在server端渲染完成后,将得到的html代码缓存到内存中。
  2. 在用户访问网站时,判断访问的页面是否有缓存,如果有缓存的话,则直接返回html代码,减少服务端渲染这一步骤的时间。
  3. 当被缓存的页面的数据需要更新时,应当把该页面的缓存给清空,等到获取到新的数据后,再重新缓存。

代码实现

  1. 创建缓存对象并导出该对象,使得后续的新增缓存和清空缓存操作的是同一个缓存对象。
    // globalCache.js
    const LRU = require('lru-cache')
    const cachePage = new LRU({
        // 最大缓存数量
        max: 10,
        // 缓存过期时间(ms)
        maxAge: 1000 * 60 * 10
    })
    
    module.exports = cachePage
    
  2. 执行缓存操作
    // pageCache.js
    const cachePage = require('./globalCache')
    
    export default function (req, res, next) {
        const pathname = req.path
        
        // 选择所需要缓存的路径,以首页为例
        if (pathname === '/') {
            const existHtml = cachePage.get('index')
            
            if (existHtml) {
                return res.end(existHtml, 'utf-8')
            } else {
                // 重写res.end
                res.original_end = res.end
                
                res.end = function (data) {
                    // 在发送页面数据的同时设置缓存
                    if (res.statusCode === 200) {
                        cachePage.set('index', data)
                    }
                    
                    res.original_end(data, 'utf-8')
                }
            }
        }
        
        next()
    }
    
    // nuxt.config.js
    // 将设置的缓存逻辑应用到服务端渲染中间件中
    module.exports = {
        ...
        serverMiddleware: [
            './pageCache'
        ],
        ...
    }
    
    
  3. 更新缓存
    // server/index.js
    // 省略部分代码
    const express = require('express')
    const app = express()
    const cachePage = require('../globalCache')
    
    async function start() {
        // 简略的做个刷新首页缓存的示例,将首页缓存置空
        // 在服务端创建一个接口,在后端更新数据的同时,访问该接口刷新缓存
        app.get('/refreshCache', (req, res, next) => {
            cachePage.set('index', null)
            res.sendStatus(200)
        })
        ...
    }
    

优化效果

本地测试效果

  • 缓存前的document下载的时间
  • 缓存后的document下载时间

源码

源码地址

存在的问题

  1. 该缓存方案只适用于与用户信息无关的页面,例如纯展示数据的页面等等。
  2. 缓存只生效于首屏页面,例如现在设置了second页面进行缓存,那么从首页跳转过去时,该缓存是无效的,只有直接打开second页面才有缓存的效果。具体原因可以看ssr的原理。
  3. 该缓存方案还可以应用在接口缓存与组件缓存当中,具体实现方案可以自己研究一下~