Skip to content

Next.js - 浅层路由

**浅路由(Shallow routing)**是 Next.js 中一个特定于 Pages Router 的功能。它允许你改变 URL 路径(包括查询参数),而无需重新运行 getServerSideProps、getStaticProps 或 getInitialProps 等数据获取方法。

当你只需要更新 URL 以反映客户端已有的状态时,此功能非常有用,例如在 URL 中跟踪过滤器、选项卡或分页状态,而无需触发完整的服务器端数据重新获取。

注意:由于 App Router 的架构不同,它专注于 Server Components 和 React Server Actions,URL 更新通常会自然地触发服务器端逻辑或按需的特定组件重新渲染,因此浅路由的概念在其中关联性较低。

要在 Pages Router 中执行浅路由,你可以使用 useRouter hook 的 router.push 或 router.replace 方法,并传递 shallow: true 选项。

让我们更新一个示例页面(例如,pages/index.js)来演示浅路由。假设此页面使用 getServerSideProps 来获取一些初始数据。

import { useRouter } from 'next/router';
import Head from 'next/head';
import { useEffect, useState } from 'react';
function HomePage(props) {
const router = useRouter();
// 我们希望在 URL 中反映的示例客户端状态
const [counter, setCounter] = useState(0);
// 在初次加载/路由改变时,根据 URL 查询参数更新计数器状态
useEffect(() => {
if (router.query.counter) {
setCounter(parseInt(router.query.counter, 10) || 0);
}
}, [router.query.counter]);
const handleIncrement = () => {
const newCounter = counter + 1;
setCounter(newCounter);
// 更新 URL 查询参数,但无需重新运行 getServerSideProps
router.push(`/?counter=${newCounter}`, undefined, { shallow: true });
};
return (
<>
<Head>
<title>Welcome to Next.js! (Shallow Routing)</title>
</Head>
<div>
<h1>Welcome to Next.js!</h1>
<p>演示浅路由</p>
<p>初始星标数 (来自 getServerSideProps): {props.stars}</p>
<p>客户端计数器: {counter}</p>
<button onClick={handleIncrement}>增加计数器 (浅更新 URL)</button>
<br />
<p>当前 URL 查询参数: {JSON.stringify(router.query)}</p>
<p>(请检查你的浏览器控制台日志,看点击按钮时是否运行了 getServerSideProps)</p>
</div>
</>
);
}
export async function getServerSideProps(context) {
console.log('getServerSideProps executed!'); // 记录日志以查看何时运行
// 示例:获取仅应在初次导航或完全刷新时加载的数据
const res = await fetch('https://api.github.com/repos/vercel/next.js');
const json = await res.json();
return {
props: { stars: json.stargazers_count || 'N/A' },
};
}
export default HomePage;

在这个例子中:

  • 我们使用 getServerSideProps 来获取 GitHub 星标数,并在它运行时记录日志到控制台。
  • 我们维护一个客户端的 counter 状态。
  • 一个 useEffect hook 会在 URL 中存在 ?counter= 查询参数时同步 counter 状态。
  • handleIncrement 函数会更新客户端状态,然后使用带有 { shallow: true } 选项的 router.push 将新的计数器值追加到 URL 的查询字符串中。

如果服务器未运行,请启动它:

npm run dev
# Example Output:
# ready - started server on 0.0.0.0:3000, url: http://localhost:3000
# event - compiled client and server successfully ...
  1. 在浏览器中打开 http://localhost:3000。检查你的服务器控制台(运行 npm run dev 的地方)。你应该看到 ‘getServerSideProps executed!’ 的日志消息。

  2. 多次点击 ‘增加计数器 (浅更新 URL)’ 按钮。

  3. 观察以下现象:

    • 页面上显示的计数器值增加。
    • 浏览器地址栏中的 URL 更新(例如,/?counter=1、/?counter=2 等)。
    • 再次检查你的服务器控制台。在点击按钮后,你不应该看到 ‘getServerSideProps executed!’ 的日志消息。这确认了数据获取函数没有重新运行,这要归功于 shallow: true。
  4. 刷新页面(例如,当 URL 是 /?counter=3 时)。现在,你会在服务器控制台看到 ‘getServerSideProps executed!’,因为发生了一次完整的页面加载。然后,useEffect hook 将从 router.query 读取 counter=3,并相应地设置初始的客户端计数器状态。