You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js的_app.js中使用SWR钩子是否会引发性能问题及如何优化

问题根因

你在_app.js层调用useUser并透传props给Layout的方案,会触发整站级别的重渲染:当useUser返回的用户状态更新时,整个App根组件及其所有子节点(包括进度条、当前页面组件等)都会重新渲染,渲染开销远大于仅Layout组件内部更新的场景,因此会出现明显的渲染滞后。

基于SWR的优化方案

1. 保持Layout内部调用useUser,复用SWR全局缓存

SWR默认对相同key的请求做全局缓存与状态同步,无论你在应用任何位置调用useUser,只要钩子内部使用的SWR请求key一致,就只会发起一次网络请求,且所有调用处的状态会同步更新。完全不需要将useUser提到_app层透传props,直接在Layout内部调用的方案本身就是最优的,既缩小了重渲染范围,也不会产生多余的网络请求。

如果嫌每个页面单独引入Layout繁琐,可以使用Next.js官方的单页Layout模式统一封装:

  • 给每个页面组件添加静态Layout属性,赋值为你的Layout组件
  • 在_app.js中统一调用该属性包装页面,不需要在_app层引入和传递任何用户相关参数:
// pages/_app.js 改造示例
function MyApp({ Component, pageProps }) {
  const getLayout = Component.getLayout || ((page) => <Layout>{page}</Layout>)

  return (
    <UserProvider value={userState}>
      <UIProvider value={uiState}>
        <CloudinaryContext cloudName="hillfinders">
          <Head>
            <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
          </Head>
          <NextNprogress
            color="#abf581"
            startPosition={0.3}
            stopDelayMs={200}
            height={4}
            showOnShallow
          />
          {getLayout(<Component {...pageProps} />)}
        </CloudinaryContext>
      </UIProvider>
    </UserProvider>
  )
}

2. 登录后主动更新SWR缓存,消除状态更新延迟

用户登录成功后不要等待SWR自动重新验证获取用户信息,直接调用mutate方法主动更新SWR缓存,导航栏会立刻同步渲染登录后的状态,完全消除滞后:

// 登录接口调用成功后执行
mutate('/api/user', 新的用户数据, { revalidate: false })

其中第一个参数是你useUser内部使用的SWR请求key,第三个参数设为false表示不需要额外发起请求验证,直接用传入的新数据更新缓存。

3. 优化useUser钩子的SWR配置

给useUser内部的SWR请求添加如下配置,进一步提升响应速度:

// useUser 钩子内部改造示例
const useUser = () => {
  return useSWR('/api/user', fetcher, {
    dedupingInterval: 10 * 60 * 1000, // 10分钟内相同key的请求去重,避免重复发起请求
    revalidateOnFocus: false, // 窗口聚焦时不需要重新验证用户状态,按需开启
    keepPreviousData: true, // 加载新数据时保留之前的用户状态,避免界面跳变
  })
}

4. 服务端预填充SWR缓存(可选)

如果你的应用做了服务端渲染,可以在getServerSideProps中预先请求用户信息,注入到SWR的fallback中,客户端加载时直接使用预取的用户数据,不需要等待客户端请求:

// 页面级getServerSideProps示例
export async function getServerSideProps(context) {
  const user = await fetchUser(context.req) // 服务端获取用户信息
  return {
    props: {
      fallback: {
        '/api/user': user
      }
    }
  }
}

然后在_app.js中将fallback传递给SWRProvider即可。


内容的提问来源于stack exchange,提问作者Antonio Pavicevac-Ortiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 02:06:04