为什么 nextjs 持久化布局一直在重新渲染?

Why is nextjs persistent layout re-rendering all the time?

我正在尝试按照 on the official docs and Adam Wathan's article here.

中提供的持久布局示例进行操作

这是我目前所知道的:

基于以上所述,我会说持久布局有效,因为 _app.js 中使用的布局是作为其道具(子项)提供的页面。由于布局自身的状态不会改变,因此不应重新渲染布局。然而,这不是我注意到的,因此这个冗长的问题。

我很清楚,当我说重新渲染时,我是在谈论 React 为组件重新创建虚拟 dom 而不是重新绘制 html dom。我的问题是前者而不是后者。

我看到的是,每次我点击“个人资料”link(即使我已经在同一页面上):

  1. 整个布局(包括顶部导航栏、图标、搜索栏和 links)全部重新呈现。
  2. 我看到正在为每个控制台打印控制台日志消息。
  3. 我使用了“Profiler”工具,它也向我显示了所有重新渲染的组件。

我认为持久布局意味着它不会一直被重新评估?控制台日志的打印表明每次都在重新评估该组件。我知道 React.memo 会完全避免这种情况,但究竟什么是“持久性”呢?在这种情况下,我遗漏或未能理解持久布局的哪些内容?

我得到的是这样的:

/pages/profile.js(同样 /pages/anotherPage.js)

function sampleProfilePage (props) = {
   return (
      <div>I am on profile page</div>
   );
}
export default sampleProfilePage

_app.js

function MyApp({Component, pageProps}) {
   return (
      <SimpleLayout>
         <Component {...pageProps} />
      <SimpleLayout />
   );
}

SimpleLayout.js

function SimpleLayout ({children}) {
    return (
        <>
            {console.log("simpleLayout re-rendered")}
            <SimpleTopBar />
            <main>{children}</main>
        </>
    );
}
export default SimpleLayout;

SimpleTopbar.js 注意:css 可以忽略。它存在于 .module.css 文件中。

function SimpleTopBar () {
    return (
        <div className={classes.container}>
            {console.log("SimpleTopBar re-rendered")}
            <Link href="/profile">Profile</Link>
            <IconCircle />
            <SearchBar />
            <IconSquare />
            <Link href="/anotherPage">Another Page</Link>
        </div>
    );
}

export default SimpleTopBar;

IconCircle(以及类似的 IconSquare)注意:再次忽略 css。此外,我最近意识到 defaultProps 已被弃用。我正在处理 updating/writing 内联默认值。

export function IconCircle (props) {
    return (
        <svg xmlns="http://www.w3.org/2000/svg"
            className={props.name}
            ... rest of svg data ...
        </svg>
    );
}
IconCircle.defaultProps = {
    name: classes.iconCircle
}

SearchBar.js 注意:这是直接取自 Adam 的代码,以便尝试比较我所看到的内容。

function SearchBar (props) {
    return (
        <div className="mt-2">
            {console.log("Search bar rendered")}
            <input className="block w-full border border-gray-300 rounded-lg bg-gray-100 px-3 py-2 leading-tight focus:outline-none focus:border-gray-600 focus:bg-white"
                    placeholder="Search..."
            />
        </div>
    );
}

export default SearchBar;

免责声明: 我是后端工程师,刚开始学习 React 和 Nextjs。很有可能我的设计和理解是有限的,或者不完全是人们在 industry/professionally 中所期望的。所以,如果有一些通用的做法或众所周知的知识,请不要假设我正在遵循或知道它。这是我粘贴整个函数的部分原因。我仍在阅读各种 pages/questions 并尝试各种方法来排除问题,或者更好地理解 shown/told 对我来说是什么。

预先感谢您耐心阅读这个问题,对于它的长度感到抱歉。

你对正在发生的事情有很好的理解。

Next.js 中的所有页面都依赖于 _app - 由于 pageProps 或更有可能 Component 道具更改而重新呈现 - 迫使 children 重新呈现.

页面之间的布局将 'persist' - children 应在路由更改时重新呈现,但仍在页面上的组件应保持其状态。

即布局中的搜索输入应将其搜索词保留在路由更改到具有相同布局的另一个页面时。

在路由更改期间不重新呈现的唯一方法是使用 shallow routing 。但它并没有真正的路由 - 它只是允许您将查询参数添加到当前路由(不能更改页面,否则它将使用标准路由)。

正如您提到的,您可以在某些组件上使用备忘录来防止重新渲染,但只有在您知道需要它时才使用它并使用它wisley

最后,重新渲染也是 React 和虚拟 DOM 操作的一部分,在它成为问题之前我不会太担心它。