链接和导航 3个月前

编程语言
606
链接和导航

以下是 Next.js 中的四种路由导航方式:

本文将详细介绍如何使用这些选项,并深入探讨路由导航的工作原理。

<Link> 是一个内置组件,它扩展了 HTML 的 <a> 标签,以提供 预取 和客户端路由导航。它是推荐的路由导航方式。

你可以通过从 next/link 中导入并将 href 属性传递给组件来使用它:

import Link from 'next/link';

export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>;
}

你还可以向 <Link> 传递其他可选属性。有关更多信息,请参见 API 参考

示例

链接到动态段

当链接到 动态段 时,你可以使用 模板字面量和插值 来生成链接列表。例如,生成博客文章列表:

import Link from 'next/link';

export default function PostList({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  );
}

检查活动链接

你可以使用 usePathname() 来判断链接是否处于活动状态。例如,为活动链接添加一个类,可以检查当前 pathname 是否与链接的 href 匹配:

'use client';
import { usePathname } from 'next/navigation';
import Link from 'next/link';

export function Links() {
  const pathname = usePathname();
  return (
    <nav>
      <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
        Home
      </Link>
      <Link className={`link ${pathname === '/about' ? 'active' : ''}`} href="/about">
        About
      </Link>
    </nav>
  );
}

滚动到 id

Next.js App Router 的默认行为是 在新路由上滚动到页面顶部,或者在向前和向后导航时保持滚动位置

如果你想在导航时滚动到特定的 id,可以在 URL 中附加 # 哈希链接或直接将哈希链接传递给 href 属性。这是可能的,因为 <Link> 渲染为 <a> 元素。

<Link href="/dashboard#settings">Settings</Link>
// 输出 <a href="/dashboard#settings">Settings</a>

值得注意 :

  • Next.js 将滚动到 页面,如果它在导航时未在视口中可见。

禁用滚动恢复

Next.js App Router 的默认行为是 在新路由上滚动到页面顶部,或者在向前和向后导航时保持滚动位置。如果你想禁用此行为,可以将 scroll={false} 传递给 <Link> 组件,或将 scroll: false 传递给 router.push()router.replace()

// next/link
<Link href="/dashboard" scroll={false}>Dashboard</Link>
// useRouter
import { useRouter } from 'next/navigation';

const router = useRouter();
router.push('/dashboard', { scroll: false });

useRouter() 钩子

useRouter 钩子允许你从 客户端组件 编程地更改路由。

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

export default function Page() {
  const router = useRouter();
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  );
}

有关 useRouter 方法的完整列表,请参见 API 参考

推荐:除非有特定需求,否则使用 <Link> 组件在路由之间导航。

redirect 函数

对于 服务器组件,请使用 redirect 函数。

import { redirect } from 'next/navigation';

async function fetchTeam(id: string) {
  const res = await fetch('https://...');
  if (!res.ok) return undefined;
  return res.json();
}

export default async function Profile({ params }: { params: { id: string } }) {
  const team = await fetchTeam(params.id);
  if (!team) {
    redirect('/login');
  }
  // ...
}

值得注意 :

  • redirect 默认返回 307(临时重定向)状态码。当在服务器操作中使用时,它返回 303(查看其他),通常用于重定向到 POST 请求的成功页面。
  • redirect 内部抛出错误,因此应在 try/catch 块之外调用。
  • redirect 可以在客户端组件的渲染过程中调用,但不能在事件处理程序中调用。你可以使用 useRouter 钩子 代替。
  • redirect 也接受绝对 URL,可以用于重定向到外部链接。
  • 如果你想在渲染过程之前重定向,请使用 next.config.js中间件

有关更多信息,请参见 redirect API 参考

使用原生 History API

Next.js 允许你使用原生 window.history.pushStatewindow.history.replaceState 方法来更新浏览器的历史记录堆栈,而无需重新加载页面。

pushStatereplaceState 调用会集成到 Next.js 路由中,允许你与 usePathnameuseSearchParams 同步。

window.history.pushState

使用它向浏览器的历史记录堆栈添加新条目。用户可以返回到先前的状态。例如,排序产品列表:

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

export default function SortProducts() {
  const searchParams = useSearchParams();
  
  function updateSorting(sortOrder: string) {
    const params = new URLSearchParams(searchParams.toString());
    params.set('sort', sortOrder);
    window.history.pushState(null, '', `?${params.toString()}`);
  }
  
  return (
    <>
      <button onClick={() => updateSorting('asc')}>Sort Ascending</button>
      <button onClick={() => updateSorting('desc')}>Sort Descending</button>
    </>
  );
}

window.history.replaceState

使用它来替换浏览器历史记录堆栈中的当前条目。用户无法返回到先前的状态。例如,切换应用程序的语言环境:

'use client'
 
import { usePathname } from 'next/navigation'
 
export function LocaleSwitcher() {
  const pathname = usePathname()
 
  function switchLocale(locale: string) {
    // e.g. '/en/about' or '/fr/contact'
    const newPath = `/${locale}${pathname}`
    window.history.replaceState(null, '', newPath)
  }
 
  return (
    <>
      <button onClick={() => switchLocale('en')}>English</button>
      <button onClick={() => switchLocale('fr')}>French</button>
    </>
  )
}

路由和导航的工作原理

App Router 使用混合方法进行路由和导航。在服务器上,你的应用程序代码会自动 代码分割 为路由段。在客户端,Next.js 预取缓存 路由段。这意味着,当用户导航到新路由时,浏览器不会重新加载页面,仅会重新渲染发生变化的路由段,从而提高导航体验和性能。

1. 代码分割

代码分割允许你将应用程序代码拆分成更小的包,以便由浏览器下载和执行。这减少了每次请求传输的数据量和执行时间,从而提高了性能。

服务器组件 允许你的应用程序代码按路由段自动进行代码分割。这意味着只有当前路由所需的代码在导航时被加载。

2. 预取

预取是一种在用户访问之前后台预加载路由的方式。

Next.js 中有两种预取路由的方法:

  • <Link> 组件:当页面首次加载或通过滚动进入视口时,路由会自动预取。预取发生在页面首次加载时,或者当它通过滚动进入视口时。
  • **router.prefetch()**:useRouter 钩子可以用来以编程方式预取路由。

<Link> 的默认预取行为(即,当 prefetch 属性未指定或设置为 null 时)会根据你使用 loading.js 的方式而有所不同。只有共享布局,在渲染的组件 "树" 下直到第一个 loading.js 文件的部分会被预取和缓存 30s。这减少了获取整个动态路由的成本,并意味着你可以展示 即时加载状态 以更好地向用户反馈视觉信息。

你可以通过将 prefetch 属性设置为 false 来禁用预取。或者,通过将 prefetch 属性设置为 true,可以预取超出加载边界的完整页面数据。

有关更多信息,请参见 <Link> API 参考

值得注意 :

  • 预取在开发环境中是禁用的,仅在生产环境中启用。

3. 缓存

Next.js 具有一个称为 Router Cache 的内存客户端缓存。当用户在应用中导航时,预取的路由段和访问过的路由的 React 服务器组件负载会存储在缓存中。

这意味着在导航时,缓存会尽可能被重用,而不是向服务器发出新请求,从而通过减少请求次数和数据传输量来提高性能。

了解更多关于 Router Cache 如何工作及其配置方式的信息。

4. 部分渲染

部分渲染意味着在导航时,仅重新渲染发生变化的路由段,共享段会被保留。

例如,当在两个兄弟路由之间导航时,/dashboard/settings/dashboard/analyticssettingsanalytics 页面将被渲染,而共享的 dashboard 布局将被保留。

image

如果没有部分渲染,每次导航都会导致整个页面在客户端重新渲染。仅渲染发生变化的段可以减少传输的数据量和执行时间,从而提高性能。

5. 软导航

浏览器在页面之间导航时执行“硬导航”。Next.js App Router 实现了页面之间的“软导航”,确保仅重新渲染发生变化的路由段(部分渲染)。这使得客户端 React 状态在导航过程中得以保留。

6. 后退和前进导航

默认情况下,Next.js 将在向后和向前导航时保持滚动位置,并重用 Router Cache 中的路由段。

7. 在 pages/app/ 之间的路由

当从 pages/ 逐步迁移到 app/ 时,Next.js 路由器将自动处理两者之间的硬导航。要检测从 pages/app/ 的转换,存在一个客户端路由过滤器,利用路由概率检查,这可能会偶尔导致误判。默认情况下,此类情况应该非常少见,因为我们将误判的可能性配置为 0.01%。这个可能性可以通过在 next.config.js 中设置 experimental.clientRouterFilterAllowedRate 选项来进行自定义。需要注意的是,降低误判率会增加客户端包中生成的过滤器的大小。

另外,如果你更愿意完全禁用此处理,并手动管理 pages/app/ 之间的路由,可以在 next.config.js 中将 experimental.clientRouterFilter 设置为 false。当此功能被禁用时,默认情况下,任何与应用路由重叠的页面中的动态路由将不会被正确导航。

image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2223782
累计阅读

热门教程文档

Rust
84小节
MyBatis
19小节
Typescript
31小节
Gin
17小节
Spring Cloud
8小节