likes
comments
collection
share

Next.js SSR页面缓存

作者站长头像
站长
· 阅读数 1057

Next.js SSR页面缓存  

背景

  SSR相对于静态页面是非常消耗服务器资源的,所以在网站访问量较大时通常会将前端页面进行缓存,在Next.js中我们需要渲染AJAX的内容需要在前端使用getServerSideProps进行动态内容的渲染。很多网页的数据变化不是很频繁通常不需要每次都发起请求和渲染,所以本文来讲解如何缓存Next.js渲染的SSR页面。如果有同学不清楚服务器缓存的原理可以看之前发布的Express使用服务端缓存

  Next.js内置的SSR本身不提供内置的缓存方案(有兴趣同学可以了解一下ISR),但是提供了一个自定义服务器方案,我们可以自己来设置启动和网站进入到返回渲染结果的流程。

安装插件

自定义缓存使用Express来做路由系统,使用lru-cache做缓存

在网站页面数量增多时建议使用Redis代替lru-cache,使用ioredis插件即可

npm i express lru-cache nodemon
//or
yarn add express lru-cache nodemon

编写

1.首先在根目录下创建一个server.js文件(命名不固定,别的也行)

2.代码:

const express = require('express');
const next = require('next');
const LRUCache = require('lru-cache');

const port = 5678; //端口
const isDev = process.env.NODE_ENV === 'development';
const app = next({
    dev: isDev
});

// nextjs原生请求处理函数
const handle = app.getRequestHandler();

// 缓存工具初始化
const ssrCache = new LRUCache({
    max: 100,
    ttl: 1 * 60 * 60 * 1000, // 1小时缓存
});

//渲染和处理缓存
function renderAndCache(req, res) {
    let pagePath = req.path;
    let queryParams = req.query;
    const key = req.url;
    // 如果缓存中有直出的html数据,就直接将缓存内容响应给客户端
    if (ssrCache.has(key)) {
        res.send(ssrCache.get(key));
        return
    }
    // 如果没有当前缓存,调用renderToHTML生成直出html
    app.renderToHTML(req, res, pagePath, queryParams)
        .then((html) => {
            if (res.statusCode === 200) {
                // 使用缓存工具将html存放
                ssrCache.set(key, html);
            } else {
                ssrCache.delete(key);
            }
            // 响应直出内容
            res.send(html);
        })
        .catch((err) => {
            app.renderError(err, req, res, pagePath, queryParams)
        })
}
async function main() {
    await app.prepare(); //准备(初始化)

    const server = express();
    server.listen(port, (err) => {
        if (err) throw err;
        console.log(`>开始运行于: http://localhost:${port}`);
    });

    //对哪些页面进行缓存
    server.get('/article/*', (req, res) => renderAndCache(req, res));
    server.get('*', (req, res) => handle(req, res));
}
main()

讲解

主要流程也十分简单,使用缓存插件并且配置好,在每次被访问时判断缓存中是否存在,如果存在就返回缓存中的内容,如果不存在,就返回实时渲染的内容,并且缓存起来。

我在项目中使用,基本就是这个样子,我在项目中只对*/article/**路径进行了缓存。对于_next路径的静态文件或者public下的图片不要和缓存路由重合。需要使用handle函数进行渲染(handle函数是Next.js内置的服务器渲染函数)

例:

server.get('/robots.txt', (req, res) => handle(req, res));

package.json配置

  "scripts": {
    "dev": "cross-env NEXT_PUBLIC_ENV=development next dev -p 5678",
    "dev:cache": "cross-env NEXT_PUBLIC_ENV=development nodemon server.js",
    "build": "cross-env NEXT_PUBLIC_ENV=production next build",
    "build:test": "cross-env NEXT_PUBLIC_ENV=development next build",
    "start": "cross-env NEXT_PUBLIC_ENV=production nodemon server.js",
    "start:no-cache": "cross-env NEXT_PUBLIC_ENV=production next start -p 5678",
    "start:test": "cross-env NEXT_PUBLIC_ENV=development next start -p 5678",
    "analyze": "cross-env ANALYZE=true next build",
    "analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
    "analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build"
  },

我在package中设置了很多命令,开发、打包、启动运行、统计。其中包括了测试环境的打包和运行,以及两个环境的统计。server.js中的代码我使用dev:cache来启动。

需要注意的是我设置环境变量使用的是NEXT_PUBLIC_ENV而非NODE_ENV,server.js中是我为了迎合默认变量名而修改的,如果你复制package.json的代码记得修改一下,以便统一环境变量