前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >梳理NextJS13两种路由下的不同渲染方式:SSG,ISR,SSR,RSC

梳理NextJS13两种路由下的不同渲染方式:SSG,ISR,SSR,RSC

作者头像
Jou
发布2023-09-01 18:01:05
1.2K1
发布2023-09-01 18:01:05
举报
文章被收录于专栏:前端技术归纳前端技术归纳

前言

NextJS是一款基于 React 进行全栈开发的框架,是当下非常火的React全栈框架之一,在去年NextJS发布了V13版本,而本文将基于V13版本的app路由,来梳理它的几种不同的渲染方式的实现,并且与pages路由做对比。

官方文档传送门:nextjs.org/docs

SSR

SSR也就是服务端渲染,页面在后端先获取到数据,然后发回前端注水渲染,如果你不是很熟悉,可以先看一下SSR相关的文章介绍。

app

在app路由下,只要我们的组件是使用 async 进行了修饰的,都会默认开启SSR.

代码语言:javascript
复制
export default async function PokemonName({ params }: { params: { name: string } }) {
  const { name } = params;
  
  const res = (await fetch('http://localhost:3000/api/pokemon?name=' + name)) as any;
  const resdata = await res.json();
  const { data } = resdata;

  return (
      //...
  );
}

pages

pages路由下,如果我们要开启SSR,需要实现getServerSideProps这个API,在请求页面的时候,提前获取到数据,然后传入组件中。

代码语言:javascript
复制
export async function getServerSideProps(context: any) {
  const data = await getPokemon(null, context.params.name);
  return {
    props: {
      data: data,
    },
  };
}

const PokemonName = ({ data }: any) => {
  
  return (
        //...
  );
};

SSG

SSG 也就是静态站点生成,在构建时生成静态页面,不同用户访问到的都是同一个页面。

app

在pages路由中,我们要实现SSG,需要先创建一个通用的模版文件,来表示所有的静态页面路由

image.png
image.png

[]中的变量,就代表访问页面时传入的变量名称,然后我们需要实现generateStaticParams这个方法

generateStaticParams方法返回静态页面所有路由变量值的数组,假如使用的是[name]这个变量做文件名,该方法就需要返回name的所有情况

和pages不同的是,app路由不需要用特定的静态方法获取数据,只需要直接在组件中获取数据即可。

代码语言:javascript
复制
export async function generateStaticParams() {
  return pokemon.map(({ name: { english } }) => ({
    name: english,
  }));
}

export default async function PokemonName({ params }: { params: { name: string } }) {
  const { name } = params;
  const res = (await fetch('http://localhost:3000/api/pokemon?name=' + name)) as any;

  return (
    //...
  );
}

pages

在pages路由中,我们需要实现getStaticPathsgetStaticProps这两个方法

  • getStaticPaths:返回静态页面所有路由变量值的数组,假如使用的是[name]这个变量,就需要返回name的所有情况。
  • getStaticProps:返回静态页面匹配成功后,需要加载的数据。
代码语言:javascript
复制
// 获取所有二级路由
export async function getStaticPaths() {
  return {
    paths: pokemon.map(({ name: { english } }) => ({
      params: {
        name: english,
      },
    })),
  };
}

// 返回匹配到后的数据
export async function getStaticProps(context: any) {
  const time = new Date().toLocaleTimeString();
  return {
    props: {
      data: pokemon.filter(({ name: { english } }) => english === context.params.name)[0],
      time,
    },
  };
}


const PokemonName = ({ data, time }: any) => {
  //...
};

ISR

SSG 的优点就是快,部署不需要服务器,任何静态服务空间都可以部署,而缺点也是因为静态,不能动态渲染,每添加一篇博客,就需要重新构建。所以有了ISR,增量静态生成,可以在一定时间后重新生成静态页面,不需要手动处理。

app

app路由实现ISR,需要利用到fetch的缓存策略,在请求接口的时候,添加参数revalidate,来指定接口的缓存时间,让它在一定时间过后重新发起请求。

代码语言:javascript
复制
export default async function PokemonName({ params }: { params: { name: string } }) {
  const { name } = params;
  // revalidate表示在指定的秒数内缓存请求,和pages目录中revalidate配置相同
  const res = await fetch('http://localhost:3000/api/pokemon?name=' + name, {
    next: { revalidate: 60 ,tags: ['collection']},
    headers: { 'Content-Type': 'application/json' },
  });

  return (
      //...
  )
}

但是在通常情况下,我们的静态页面更新实际上没有那么频繁,但是有些情况有需要连续更新(发布博客有错别字),这个时候其实需要一种能手动更新的策略,来发布指定的静态页面。

On-demand Revalidation(按需增量生成)

NextJS提供了更新静态页面的方法,我们可以在 app 目录下新建一个 app/api/revalidate/route.ts接口,用于实现触发增量更新的接口。

为了区分需要更新的页面,这里可以在调接口的时候传入更新的页面路径,也可以传入在fetch请求中指定的collection变量。

代码语言:javascript
复制
import { NextRequest, NextResponse } from 'next/server';
import { revalidatePath, revalidateTag } from 'next/cache';

// 手动更新页面 
export async function GET(request: NextRequest) {
   
  // 保险起见,这里可以设置一个安全校验,防止接口被非法调用
  //这里的process.env.NEXT_PUBLIC_UPDATE_SSG名字要与你设置在项目中的环境变量名字相同
  
  if (request.query.secret !== process.env.NEXT_PUBLIC_UPDATE_SSG) {
      return NextResponse.json(
      { data: error, message: 'Invalid token' },
      {
        status: 401,
      },
    );
  }
  const path = request.nextUrl.searchParams.get('path') || '/pokemon/[name]';
  
  // 这里可以匹配fetch请求中指定的collection变量
  const collection = request.nextUrl.searchParams.get('collection') || 'collection';
  
  // 触发更新
  revalidatePath(path);
  revalidateTag(collection);
  
  return NextResponse.json({
    revalidated: true,
    now: Date.now(),
    cache: 'no-store',
  });
}

假如我们数据库中的内容有修改,访问http://localhost:3000/api/revalidate?path=/pokemon/Charmander就可以实现/pokemon/Charmander这个路由的手动更新。

兜底策略

我们的静态页面在生成期间,如果用户访问对应路由会报错,这时需要有一个兜底策略来防止这种情况发生。

Nextjs在组件中指定了dynamicParams的值(true默认),当dynamicParams设置为true时,当请求尚未生成的路由段时,我们的页面将通过SSR这种方式来进行渲染。

代码语言:javascript
复制
export const dynamicParams = true;

pages

pages路由实现ISR需要在getStaticProps方法中添加参数revalidate,来指定周期时间重新生成静态页面。

代码语言:javascript
复制
export async function getStaticProps(context: any) {
  const time = new Date().toLocaleTimeString();
  return {
    props: {
      data: pokemon.filter(({ name: { english } }) => english === context.params.name)[0],
      time,
    },
    // 当访问页面时,发现 20s 没有更新页面就会重新生成新的页面
    revalidate: 20,
  };
}

和app路由一样,pages路由也有手动更新策略。

On-demand Revalidation(按需增量生成)

pages路由实现增量生成和app路由类似,我们可以在 pages 目录下新建一个 pages/api/revalidate.ts接口,用于触发增量生成。

代码语言:javascript
复制
export default async function handler(req: any, res: any) {
    // 防止非法调用
  if (req.query.secret !== process.env.NEXT_PUBLIC_UPDATE_SSG) {
        return res.status(401).json({ message: 'Invalid token' });
  }
  try {
     // 更新传入path路径对应的页面
    await res.revalidate(req.query.path);
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

比如我们修改了数据库中的内容,此时访问 https://localhost:3000/api/revalidate?secret=<token>&path=/pokemon/isauga,便可让/pokemon/isauga这个路由生成新的静态页面。

兜底策略

getStaticPaths?方法中还有一个参数?fallback?用于控制未生成静态页面的渲染方式。设置此变量后,我们可以指定路由未生成时的页面渲染内容,避免出现报错。

代码语言:javascript
复制
export async function getStaticPaths() {
  return {
    paths: pokemon.map(({ name: { english } }) => ({
      params: {
        name: english,
      },
    })),
    //路由存在但是页面没有生成之前,给一个标志位 
    fallback: true,
  };
}

const PokemonName = ({ data, time }: any) => {
  const router = useRouter();

  if (router.isFallback) {
    return <div>Loading...</div>;
  }
   
  return (
      //...
  )
}

RSC

Server component 是 React18 提供的能力, 与上面的 SSR 不同,相当于是流式 SSR。

传统 SSR 执行步骤
  • 在服务器上,获取整个应用的数据。
  • 在服务器上,将整个应用程序数据渲染为 HTML 并发送响应。
  • 在浏览器上,加载整个应用程序的 JavaScript 代码。
  • 在客户端,将 JavaScript 逻辑连接到服务端返回的 HTML(这就是“水合”)。

而以上每个步骤必须完成,才可以开始下一个步骤。

比如一个传统的博客页面采用 SSR 的方式使用 getServerSideProps 的方式渲染,那么就需要等 3 个接口全部返回才可以看到页面。

代码语言:javascript
复制
export async function getServerSideProps() {
  const list = await getBlogList()
  const detail = await getBlogDetail()
  const comments = await getComments()

  return { props: { list,detail,comments } }
}

如果评论接口返回较慢,那么整个程序就是待响应状态。

app

在 app 目录下的组件默认都是 React Server Components,如果你不想使用这个特性,可以在组件页面最上面添加use client的修饰表示只使用客户端渲染或者SSR。

pages

在pages目录下,可以使用 Suspense开启流渲染的能力,将组件使用 Suspense 包裹。

代码语言:javascript
复制
import { SkeletonCard } from '@/ui/SkeletonCard';
import { Suspense } from 'react';
import Comments from './Comments';

export default function Posts() {
  return (
    <BlogList />
    <section>
     <BlogDetail />
      <Suspense
        fallback={
          <div className="w-full h-40 ">
            <SkeletonCard isLoading={true} />
          </div>
        }
      >
        <Comments />
      </Suspense>
    </section>
  );
}

组件数据请求使用 use API,就可以实现流渲染了。

代码语言:javascript
复制
import { use } from 'react';

async function fetchComment(): Promise<string> {
  return fetch('http://www.example.com/api/comments').then((res)=>res.json())
}

export default function Comments() {
  let data = use(fetchComment());
  return (
    <section>
      {data.map((item)=><Item key={item.id}/>)}
    </section>
  );
}

当开启RSC后,整个渲染流程如下图。

  • 灰色部分代表 HTML 字符串返回
  • loading 状态表示当前部分还在请求
  • 绿色部分代表注水成功,页面可以交互

如图所示,如果评论部分接口还在请求中,那么页面左侧注水完成,也是可以交互可以点击的。

最后

感谢你能看到这里,本文梳理了NextJS两种路由下的不同渲染方式,希望对你有用,如果可以的话,不妨留个赞再走呢,这对我很重要。

demo地址

github.com/AdolescentJ…

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • SSR
    • app
      • pages
      • SSG
        • app
          • pages
          • ISR
            • app
              • On-demand Revalidation(按需增量生成)
              • 兜底策略
            • pages
              • On-demand Revalidation(按需增量生成)
              • 兜底策略
              • 传统 SSR 执行步骤
          • RSC
            • app
              • pages
              • 最后
              • demo地址
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
              http://www.vxiaotou.com