likes
comments
collection
share

Next.js 14 App Router 的基础用法

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

前言

本文专为刚接触Next.js 14的读者量身定制,以简明易懂的语言介绍Next.js的App Router机制,帮助你快速掌握其核心概念和用法

demo源码地址:next-ts-seed

1. 静态路由

Next.js与React不同,Next.js提供了一套内置的文件路由系统,文件的命名与路由的生成有着直接的对应关系

具体来说,app目录下的page.tsx文件会自动映射为页面的路由,例如,app/page.tsx会被解析为根路由“/”。同样地,app/login/page.tsx则会代表着“/login”这一路由。

Next.js 14 App Router 的基础用法

目录结构对应路由
app/page.js/
app/dashboard/page.js/dashboard
app/dashboard/settings/page.js/dashboard/settings

2. 嵌套路由

layout.tsx会自动映射为多个路由之间共享的UI组件

1. 根布局

在app目录下放置的layout.tsx文件将作为根布局,它是所有路由共用的UI基础。在这个文件中,可以引入全局样式、公共组件等,确保它们能够应用到整个应用程序的每一个页面。

根布局必须包含html和body标签

//app/layout.tsx
const RootLayout = ({ children }: React.PropsWithChildren) => (
    <html lang="en">
      <body>
        {children}
      </body>
    </html>
  );
  
  export default RootLayout;

2. 嵌套布局

在app目录的各个子文件夹下创建layout.tsx文件。这些子文件夹下的layout.tsx文件将作为该路由下的共享UI组件,仅在该子文件夹下的页面之间共享。

  1. 新建src/app/home/layout.tsx
export default function HomeLayout({ children }: { children: React.ReactNode }) {
    return (
      <section>
        布局页面
        {children}
      </section>
    );
  }
  1. 新建src/app/home/user/page.tsx
const User = () => <div>用户页面</div>;

export default User;
  1. 此时的目录结构为:

Next.js 14 App Router 的基础用法

上面的路由形式可以理解为react-router-dom V6的嵌套路由配置

const routes = [
    {
      element: <HomeLayout />,
      children: [
        {
          path: '/home/user',
          element: <Manage />,
        },
      ],
    },
  ];
  1. 访问http://localhost:3001/home/user 查看效果(默认端口号为3000,我这里启动端口修改成了3001)

3. 动态路由

在Next.js中,要实现动态路由,你需要在pages目录的相应位置使用方括号来创建特殊的文件夹名称,例如[id]或[slug]。动态字段将作为参数传递给layout、page、route和generateMetadata函数

1. [folderName]

动态路由的基本用法,新建src/app/blog/[id]/page.tsx

const Blog = ({ params }: { params: { id: string } }) => {
    return <div>{params.id}博客页面</div>;
  };
  
  export default Blog;

访问路由“/blog/2024”,页面将呈现“2024博客页面”

2. [...folderName]

如果访问路由“/blog/2024/1”,则页面会显示404。可在方括号内添加省略号[...folderName],捕获后面所有的路由片段

  1. 新建src/app/blog/[...year]/page.tsx
const Blog = ({ params }: { params: { year: string[] } }) => {
    return (
      <div>
        {params.year.map((o, index) => (
          <div key={index}>{o}</div>
        ))}
      </div>
    );
  };
  
  export default Blog;

访问路由“/blog/2024/1”,页面将会显示20241

目录结构,对应路由与-参数的关系如下所示:

目录结构对应路由参数
app/blog/[...year]/page.tsx/blog/a{ year: ['a'] }
app/blog/[...year]/page.tsx/blog/a/b{ year: ['a','b'] }
app/blog/[...year]/page.tsx/blog/a/b/c{ year: ['a','b','c] }

3. [[...folderName]]

blog/[...year]能捕获/blog/a,也能捕获/blog/a/b,但不能捕获/blog,访问/blog将会得到404页面。可通过在双方括号内添加省略号的形式,捕获包括自身的路由段。

将上述[...year]修改为[[...year]],访问/blog得到的对应参数为空对象 {},因此再修改下src/app/blog/[[...year]]/page.tsx页面

const Blog = ({ params }: { params: { year: string[] } }) => {
    return (
      <div>
        {params.year ? (
          params.year.map((o, index) => <span key={index}>{o}</span>)
        ) : (
          <span>blog页面</span>
        )}
      </div>
    );
  };
  
  export default Blog;

访问/blog将看到页面显示“blog页面”

4. 路由组

路由组是为了在项目比较大时,更好的管理路由文件的一种方式。使用中括号来创建特殊的文件夹名称,例如(marketing)或(shop)。

路由组不会影响url结构,例如app/(marketing)/about/page.js对应的路由为/about

Next.js 14 App Router 的基础用法

5. 配置404页面

not-found.tsx文件用于处理页面未找到的情况

新建src/app/not-found.tsx

export default function NotFound() {
    return <div>我是自定义404页面</div>;
  }

6. 声明式、编程式导航

1. 声明式导航

使用Link组件进行路由跳转

import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

如果想要增加激活状态的样式,需使用usePathname()

'use client'
 
import { usePathname } from 'next/navigation'
import Link from 'next/link'
 
export function Links() {
  const pathname = usePathname()
 
  return (
    <nav>
      <ul>
        <li>
          <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
            Home
          </Link>
        </li>
        <li>
          <Link
            className={`link ${pathname === '/about' ? 'active' : ''}`}
            href="/about"
          >
            About
          </Link>
        </li>
      </ul>
    </nav>
  )
}

2. 编程式导航

使用hook进行路由跳转

'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

因为使用了hook,因此在这里需使用'use client',显式声明是客户端组件

7. 路由传参

1. 动态路由匹配

  1. 定义路由跳转

修改src/app/page.tsx

import Link from 'next/link';

const Home = () => (
  <div className="App">
    <Link href={'/blog/1'}>博客</Link>
  </div>
);

export default Home;
  1. 获取参数

(1)在服务端组件中,使用params获取

使用方括号来创建动态路由文件,上文已创建过src/app/blog/[id]/page.tsx

const Blog = ({ params }: { params: { id: string } }) => {
  return <div>{params.id}博客页面</div>;
};

export default Blog;

(2)在客户端组件中,使用useParams获取

修改src/app/blog/[id]/page.tsx

'use client';

import { useParams } from 'next/navigation';

export default function Blog() {
  const params = useParams();
  return <div>{params.id}博客页面</div>;
}

2. query 传参

  1. 编程式传参

修改src/app/page.tsx

import Link from 'next/link';

const Home = () => (
  <div className="App">
    <Link
      href={{
        pathname: '/new',
        query: {
          sort: 'name',
          id: '2',
        },
      }}
    >
      新闻页
    </Link>
  </div>
);

export default Home;
  1. 命令式传参

修改src/app/page.tsx

'use client';
import { useRouter } from 'next/navigation';

const Home = () => {
  const router = useRouter();
  return (
    <div className="App">
      <button onClick={() => router.push('/new?sort=name&id=2')}>新闻页</button>
    </div>
  );
};

export default Home;
  1. 获取参数

新建src/app/new/page.tsx

'use client';

import { Suspense } from 'react';

import { useSearchParams } from 'next/navigation';

function New() {
  const searchParams = useSearchParams();

  return <Suspense>Search: {searchParams.get('id')}</Suspense>;
}

export default function Page() {
  return (
    <Suspense>
      <New />
    </Suspense>
  );
}

8. 错误处理

在路由组件加载过程中发生错误时展示的元素

error.tsx文件用于处理页面报错的情况

  1. 新建src/app/error.tsx
'use client';

import { useEffect } from 'react';

export default function Error({ error }: { error: Error & { digest?: string } }) {
  useEffect(() => {
    // 收集错误日志,发送给服务端
    console.error('11', error);
  }, [error]);

  return (
    <div>
      <h2>Something went wrong!</h2>
    </div>
  );
}

提示:error.tsx只能为客户端组件

  1. 新建src/app/user/page.tsx
const obj: any = {
  a: '2',
};

export default function New() {
  return (
    <div>
      {/* 报错:因为取不到c属性 */}
      {obj.b.c}
    </div>
  );
}

访问/user路由,可以看到页面显示为“Something went wrong!”

结尾

本文介绍了App Router的基本用法,想了解App Router更多用法的,可参考我的其他文章:

参考资料:

nextjs.org/docs/app/bu…

转载自:https://juejin.cn/post/7344598656144195647
评论
请登录