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

前言

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

上一篇文章介绍了Next.js14的App Router的基本使用:一文梳理Next.js 14的App Router,本篇继续讲解App Router的一些高级用法

对Next.js14项目搭建还不熟悉的,可参考我的另一篇文章:给上市公司从0到1搭建Next.js14项目

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

js 复制代码
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。都各自预渲染了相应内容

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

2. 路由加载

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

  1. 新建app/home/page.tsx

页面嵌套Suspense组件

js 复制代码
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后返回结果

js 复制代码
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测试。接口中的内容能够被正常抓取:

3. 并行路由

1. 基本使用

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

  1. 新建平行路由analytics

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

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

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

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

新建app/home/layout.tsx

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

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

2. 约定文件 default

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

  1. 新建app/home/user/page.tsx
js 复制代码
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
js 复制代码
export default function Page() {
  return <h1>分析页的默认页</h1>;
}
  1. 新建app/home/@team/default.tsx
js 复制代码
export default function Page() {
  return <h1>team的默认页</h1>;
}

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

js 复制代码
用户页
team的默认页
分析页的默认页

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

js 复制代码
export default function Page() {
  return null;
}

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

js 复制代码
用户页

3. 并行路由的子路由

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

  1. 新建app/home/@analytics/behavior/page.tsx
js 复制代码
export default function Page() {
  return <h1>行为分析</h1>;
}
  1. 新建app/home/@analytics/performance/page.tsx
js 复制代码
export default function Page() {
  return <h1>性能分析</h1>;
}
  1. 新建app/home/@analytics/layout.tsx,进行布局
js 复制代码
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
js 复制代码
export default function Page() {
  return <div>home的默认页</div>;
}

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

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

结尾

由于约定式文件路由真正上手起来还是有难度的,最起码比react-router-dom 和vue-router难,因此这里将Next.js14的App Router分开来讲解,如果对App Router感兴趣的,可先关注我,后续将继续更新相关内容

参考资料:

nextjs.org/docs/app/bu...

相关推荐
清灵xmf22 分钟前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据28 分钟前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_3901617737 分钟前
防抖函数--应用场景及示例
前端·javascript
334554321 小时前
element动态表头合并表格
开发语言·javascript·ecmascript
John.liu_Test1 小时前
js下载excel示例demo
前端·javascript·excel
Yaml41 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事1 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶1 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo1 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v1 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript