Next.js12使用SSG与ISR实现动态增量静态页面生成

969 阅读4分钟

Next.js作为React技术栈的一员,在选择使用Next.js时,SEO以及加载速度快往往是选择Next.js的一个主要原因,至少对于Yasol我自己来说是这样的,从React切换到Next.js的时候会明显觉得Next.js有各种限制,各种规范,感觉没有React那么的自由那么的灵活。

犹如那种突然从自由职业转到了按时到点上班的那种感觉,浑身难受,但是用久了之后,就会慢慢发现Next.js其实确实是一款很强大的框架,不单单是SEO以及单方面的加载速度快,还为项目中考虑到了很多容易忽略的安全性问题,反正对于目前的我来说,确实是对Next.js越来越兴趣了

回归主题,在使用Next.js中一般都难免会使用SSG或者SSR,特别是一些有SEO需求的官网或者博客,目前yasol是项目中使用了Next.js且自己重构的Vue博客也是使用的Next.js,重构也是因为SEO问题且目前从事React方向,所以选择了Next.js

就拿博客案例来说,这里每一篇博文的前面会携带用户id,因此结构如下图所示,在pages/[uid]/[did].tsx

image-20230321171213765

这个时候,我们是使用SSG的时候就会使用到两个方法,getStaticPathsgetStaticProps

其中getStaticPaths用于获取SSG渲染的所有文章路径,可以只提供部分,后续新的自动增量

getStaticProps则会通过上面的所有路径去匹配当前的路径,然后用匹配到的uiddid去请求服务器拿到当前博客的文章

export const getStaticPaths = async () => {
    const res = await axios.get("https://ssgdemo.usemock.com/paths");
    // console.log(res.data.data);
    const list = res.data.data;
    const paths = list.map((item: { uid: any; did: any; }) => ({
        params: {
            uid: item.uid,
            did: item.did
        },
    }))
    console.log(paths);
    return {
        paths,
        // fallback: true  //设置为true,部署后不会增量更新
        fallback: "blocking" //搭配revaildate可以实现ISR动态增量
        // fallback: false //当前路由不存在在全部路由路径中,则直接返回404
    }
}

这里使用ISR,设置了页面缓存过期时间为60s,可以自己设置时间

export const getStaticProps = async ({ params }: { params: { uid: string, did: string } }) => {
    console.log(params);
    const res = await axios.get(`https://ssgdemo.usemock.com/${params.uid}/${params.did}`);
    console.log(res.data.data.time);
    return {
        props: {
            time: res.data.data.time
        },
        revalidate: 60
    }
}

至此,页面SSG静态渲染已经完成,基本达到了我们的需求,并且博文的页面会缓存60s,在这60s中,刷新或者其他用户访问,都会直接返回缓存,而不请求数据库的数据。

但是如果说我们的在60s之内修改了博文的内容,但是因为还在60s内,所以我们的更新并不会马上现在出来,这个时候我们就可以使用api的方式,让页面的缓存直接过期,这样下次访问的时候就会从新去请求新的数据

设置API
1.在.env环境变量中新增一个变量

*注意

如果环境变量名字里面有NEXT_PUBLIC则表示当前环境变量可以在服务端读取到,也可以在客户端读取到

# update ssg revalidate
NEXT_PUBLIC_UPDATE_SSG="YASOLUPDATE"

2.在pages/api文件夹下新建revalidate.ts

这里可以自己改写,yasol为了方便,在调用这个api的时候也传递了path,到时候直接根据传递的path进行更新缓存即可

export default async function handler(req, res) {
    console.log(req.query);
    // Check for secret to confirm this is a valid request
    //这里的process.env.NEXT_PUBLIC_UPDATE_SSG名字要与你设置在项目中的环境变量名字相同
    if (req.query.secret !== process.env.NEXT_PUBLIC_UPDATE_SSG) {
        return res.status(401).json({message: "Invalid token"});
    }
​
    try {
        // this should be the actual path not a rewritten path
        // e.g. for "/blog/[slug]" this should be "/blog/post-1"
        await res.revalidate(req.query.path);
        return res.json({revalidated: true});
    } catch (err) {
        // If there was an error, Next.js will continue
        // to show the last successfully generated page
        return res.status(500).send("Error revalidating");
    }
}
​

3、调用

例如在博客后台管理系统中,我们可以在调用修改接口成功,再调用当前项目的api

项目地址——>https://www.360yahoo.cn

则调动更新缓存的接口地址为——>https://www.360yahoo.cn/api/revalidate?secret=${环境变量设置的值}&path=${需要更新的路径}

调用成功后接口会返回一下信息,则表示缓存已经失效,下次访问会请求新的数据

image-20230324175705794

下面是我写的演示地址

SSG页面:nextjs-ssg-demo-rho.vercel.app/1/1

访问上面的地址后,会看到一个时间,缓存时间为60秒,在60秒呢,时间会一直停留在缓存的那个时间

清除缓存:nextjs-ssg-demo-rho.vercel.app/api/revalid…

当调用上面的清除缓存后,再次访问则会请求新的时间

github地址:github.com/izz520/next…

文章写的不是很好,如果觉得写的乱可以看看github的源码,会比较清晰

当然有任何问题欢迎大家指出来,共同学习