@Ankit 已经就如何创建持久化组件给出了很好的答案。这是我关于为什么它有效的两分钱。
那么当您从某个页面 A(在 pages/a.js 中定义)导航到另一个页面 B(在 pages/b.js 中定义)时,究竟会发生什么?首先,导航发生在客户端。这意味着不是从服务器获取呈现的 HTML,而是在浏览器中运行一些 JavaScript 来呈现新页面。 (您可以验证它here。)页面导航的JavaScript逻辑归结为:
- 如果尚未预取新页面组件
<B /> 的 JavaScript 代码,则从服务器获取;
- Next.js 将调用
ReactDOM.render() 并带有 2 个参数:第一个是要渲染的新 React 元素(可以大致认为是更新后的 App 组件),第二个是 DOM 容器元素(总是<div id="__next"></div>)新的 React 元素被渲染到。
简而言之,这个过程大致可以认为是将更新后的App 组件渲染到<div id="__next"></div> DOM 容器元素中。然后,React 将负责区分新旧 React 元素,并决定 DOM 的哪一部分重新渲染以及更新哪一部分。
那么新旧 React 元素是什么样子的呢?嗯,App 组件的默认定义是这样的:
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
// Component will be set to the current page component
return <Component {...pageProps} />
}
export default MyApp
Component 变量将设置为当前页面组件。这意味着旧的 React 元素将如下所示:
<A {...pageProps} />
新的 React 元素将如下所示:
<B {...pageProps} />
根据React diffing algorithm,在比较新旧React元素时,如果被比较的两个元素是不同类型的,那么对应的子树将被完全销毁并重新渲染。
这正是在这种情况下发生的事情。 <A /> 和 <B /> 是两个不同的组件,被认为是不同的类型,所以 DOM 中与 <A /> 对应的部分将被销毁并重新渲染为 <B />。
这就是为什么当你导航到一个新页面时整个页面组件会被重新渲染,即使它们包括像标题这样的常见组件。
如果你把 header 放在自定义的App 组件中,像这样:
import Header from '../components/header'
function MyApp({ Component, pageProps }) {
return (
<div>
<Header />
<Component {...pageProps} />
</div>
)
}
export default MyApp
然后<Header /> 组件将在页面导航中持续存在,因为 React diffing 算法会将其视为同一类型,并且只会对子树进行最少的更新。