likes
comments
collection
share

【Next.js 进阶】App Router的高级用法(上)

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

前言

先附上源码地址:next-ts-seed

1. 动态路由的静态参数

例如博客页面,博客1的路由为/blogs/1,博客2的路由为/blogs/2,博客的id都是固定的,不会随着用户的每次请求而变化。

对于这些动态路由,可使用 generateStaticParams 定义路由中的静态参数,这些参数在构建时会被预先计算并用于生成静态页面。从而提高页面的性能和效率。

generateStaticParams返回一个数组,数组中的每一项为页面的静态参数,页面通过params去接收。

例如返回[{ id: '1' }, { id: '2' }]。{ id: '1' }为一个页面的静态参数,在props中通过params获取到 {id: '1' }

新建src/app/blog/[id]/page.tsx

export async function generateStaticParams() {
  //const products = await fetch('https://.../products').then((res) => res.json())
  //模拟通过接口返回所有id信息
  return [{ id: '1' }, { id: '2' }];
}

export default async function Blog({ params }: { params: { id: string } }) {
  //可通过id去请求接口,获取博客详情页
  return <div>{params.id}博客页面</div>;
}

运行npm run builld进行打包,在.next/server/app/blog文件夹下,可以看到生成了2个html文件,分别为1.html,2.html。都各自预渲染了相应内容

【Next.js 进阶】App Router的高级用法(上)

相比于用户请求时再创建相应页面,在构建时预渲染页面,显然能显著的提高页面的打开速度

2. 路由加载

当进行路由跳转,进入一个新页面时,如果需要发送请求以获取页面数据。由于接口响应存在延迟,页面在这段时间内可能会呈现无法交互的状态。我们可以利用加载UI与流式传输技术,提高用户体验。

  1. 新建app/home/page.tsx

页面嵌套Suspense组件

import { Suspense } from 'react';

import { Spin } from 'antd';

import List from './components/list';

export default function Home() {
  return (
    <article>
      我是首页
      <Suspense fallback={<Spin />}>
        <List />
      </Suspense>
    </article>
  );
}
  1. 新建app/home/components/list.tsx

getProjects方法模拟了一个接口,2s后返回结果

async function getProjects(): Promise<{ name: string; id: number }[]> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        {
          name: 'zhang',
          id: 1,
        },
        {
          name: 'li',
          id: 2,
        },
      ]);
    }, 2000);
  });
}

export default async function List() {
  const projects = await getProjects();
  return (
    <div className="App">
      {projects.map((o: any) => (
        <div key={o.id}>{o.name}</div>
      ))}
    </div>
  );
}

刷新页面,浏览器将先显示loading加载状态,之后再显示页面内容

注意点:不要在最外层去请求接口,因为await等待接口时,异步组件不会去渲染,其他不依赖接口的页面也会显示不出来。正确做法应如示例中,在List子组件中请求数据,在页面组件Home中使用Suspense包裹子组件。如果页面组件中想请求接口,可在layout文件中用Suspense包裹页面组件。

提示:在Suspense中显示的服务端组件,不会影响SEO,依旧能够被获取到。可将自己的网站部署后,访问:search.google.com/test/rich-r…,进行SEO测试

我这里将getProjects里面的内容替换成接口数据,并进行SEO测试。接口中的内容能够被正常抓取:

【Next.js 进阶】App Router的高级用法(上)

【Next.js 进阶】App Router的高级用法(上)

3. 平行路由

1. 基本使用

平行路由可以使你在同一个布局中同时或者有条件的渲染一个或者多个页面。文件夹以@作为开头进行命名,这个文件夹下面的 page.js 将会自动注入文件夹同级 layout 的 props 中

  1. 新建平行路由analytics

新建app/home/@analytics/page.tsx

export default function Page() {
  return <h1>分析页</h1>;
}
  1. 新建平行路由team

新建app/home/@team/page.tsx

export default function Page() {
  return <h1>团队页</h1>;
}
  1. 新建/home的布局文件

新建app/home/layout.tsx

export default function Layout({
  children,
  team,
  analytics,
}: {
  children: React.ReactNode;
  analytics: React.ReactNode;
  team: React.ReactNode;
}) {
  return (
    <section>
      {children}
      {team}
      {analytics}
    </section>
  );
}

重启项目,访问路由/home,页面内容如下所示

【Next.js 进阶】App Router的高级用法(上)

2. 约定文件 default

default.tsx是平行路由的回退页面,也就是说当匹配不到平行路由时,会显示平行路由下default.tsx的内容

  1. 新建app/home/user/page.tsx
export default function Page() {
  return <h1>用户页</h1>;
}

此时访问路由“/home/user”,页面会显示404,明明创立了该文件,为什么访问不到?

因为app/home/layout.tsx文件中定义了三个插槽,分别为children,team,analytics。user下的page.tsx默认匹配了children,但team和analytics两个插槽没匹配上,因此会报404页面,可在平行路由下定义default.tsx

  1. 新建app/home/@analytics/default.tsx
export default function Page() {
  return <h1>分析页的默认页</h1>;
}
  1. 新建app/home/@team/default.tsx
export default function Page() {
  return <h1>team的默认页</h1>;
}

此时再访问路由“/home/user”,页面将显示

用户页
team的默认页
分析页的默认页

如果不想访问home的子路由被平行路由插槽影响时,default.tsx可返回一个null,将@analytics/default.tsx,@team/default.tsx分别改为

export default function Page() {
  return null;
}

再访问路由“/home/user”,页面将显示

用户页

3. 平行路由的子路由

在平行路由的基本使用中,实现了将analytics和team插入到home的layout中。当然使用组件也能实现同样的效果,接下来看平行路由与组件的不同点:平行路由可以创建自己的子路由

  1. 新建app/home/@analytics/behavior/page.tsx
export default function Page() {
  return <h1>行为分析</h1>;
}
  1. 新建app/home/@analytics/performance/page.tsx
export default function Page() {
  return <h1>性能分析</h1>;
}
  1. 新建app/home/@analytics/layout.tsx,进行布局
import Link from 'next/link';

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <nav>
        <Link href="/home/behavior">behavior Views</Link>
        ----------
        <Link href="/home/performance">performance Views</Link>
      </nav>
      <div>{children}</div>
    </>
  );
}

在访问路由时,平行路由@开头的文件名可省略,例如这里app/home/@analytics/behavior/page.tsx对应着路由“/home/behavior”,访问/home/behavior页面显示404,这是为什么?

因为在app/home/layout.tsx中,我们定义了三个插槽children,team,analytics,此时访问平行路由“/home/behavior”,意味着只匹配上了插槽analytics,而children,team没有匹配上,在之前我们定义了@team/default.tsx,但没有定义默认插槽children的回退内容

  1. 新建app/home/default.tsx
export default function Page() {
  return <div>home的默认页</div>;
}

再次访问路由“/home/behavior”,页面将会正常显示:

【Next.js 进阶】App Router的高级用法(上)

平行路由还是有上手难度的,建议理解后再使用

结尾

由于约定式文件路由真正上手起来还是有难度的,最起码比react-router-dom 和vue-router难,因此这里将Next.js14的App Router分开来讲解。

更新:

参考资料:

nextjs.org/docs/app/bu…