在Next.js中实现页面级别KeepAlive

在Next.js中实现页面级别KeepAlive

前提:

  1. 基于App Router的路由模式

  2. 使用客户端组件(服务端组件没测试过)

很简单, 只需要三个文件

组件名称 作用
KeepAliveProvider.tsx 用于包裹最外层layout, 处理缓存组件和非缓存组件的渲染
KeepAliveContext.tsx 无需解释
KeepAlive.tsx 用于包裹App Router的页面组件page.tsx

KeepAliveProvider.tsx

tsx 复制代码
'use client'

import { KeepAliveContext } from '@/components/KeepAlive/KeepAliveContext'
import { usePathname } from 'next/navigation'
import React, { useState } from 'react'

interface KeepAliveProviderProps {
  children: React.ReactNode
}

// 最大缓存数量
const MAX_CACHE_SIZE = 5

export const KeepAliveProvider: React.FC<KeepAliveProviderProps> = ({ children }) => {
  const pathname = usePathname()
  const [componentList, setComponentList] = useState<Map<string, React.ReactNode>>(new Map())

  // 更新缓存组件列表的方法
  const updateComponentList = (path: string, component: React.ReactNode) => {
    setComponentList((prev) => {
      const newMap = new Map(prev)
      newMap.set(path, component)

      // 如果超过最大缓存数量,再删除最旧的
      if (newMap.size > MAX_CACHE_SIZE) {
        const oldestKey = newMap.keys().next().value

        // 如果存在最旧的key,则删除
        if (oldestKey !== undefined) {
          newMap.delete(oldestKey)
        }
      }

      return newMap
    })
  }

  return (
    <KeepAliveContext.Provider
      value={{
        componentList,
        updateComponentList,
        activePath: pathname,
      }}
    >
      {[...componentList.entries()].map(([key, node]) => (
        <div
          key={key}
          style={{
            display: key === pathname ? 'block' : 'none',
            height: '100%',
          }}
        >
          {node}
        </div>
      ))}

      <div className="h-full">{!componentList.get(pathname) && children}</div>
    </KeepAliveContext.Provider>
  )
}

KeepAliveContext.tsx

tsx 复制代码
// 创建context
import React, { createContext } from 'react'

interface IKeepAliveContext {
  // 缓存的组件列表
  componentList: Map<string, React.ReactNode>
  // 更新缓存组件列表的方法
  updateComponentList: (path: string, component: React.ReactNode) => void
  // 当前激活的路径
  activePath?: string
}

export const KeepAliveContext = createContext<IKeepAliveContext>({
  componentList: new Map<string, React.ReactNode>(),
  updateComponentList: () => {},
})

KeepAlive.tsx

tsx 复制代码
'use client'

import { usePathname } from 'next/navigation'
import React, { useContext, useEffect } from 'react'
import { KeepAliveContext } from './KeepAliveContext'

export const KeepAlive = ({ children }: { children: React.ReactNode }) => {
  const { updateComponentList } = useContext(KeepAliveContext)
  const pathname = usePathname()

  // 每当 children 变化时,更新缓存的组件列表
  useEffect(() => {
    updateComponentList(pathname, children)
  }, [children])

  // 该组件本身不渲染任何内容
  return null
}

用法

  1. 先包裹最外层layout (项目根目录的layout.tsx, 并非组件的layout.tsx)
tsx 复制代码
// layout.tsx
import { KeepAliveProvider } from '@/components/KeepAlive/KeepAliveProvider'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <KeepAliveProvider>{children}</KeepAliveProvider>
      </body>
    </html>
  )
}
  1. 在需要缓存的页面组件中使用KeepAlive包裹
tsx 复制代码
// page.tsx
import { KeepAlive } from '@/components/KeepAlive/KeepAlive'

export default function Page() {
  return (
    <KeepAlive>
      <div>这是一个需要缓存的页面内容</div>
    </KeepAlive>
  )
}
  1. 测试结果, 切换页面时, 该页面状态会被保留, 如果没保留, 请检查是否正确包裹, KeepAlive是否包裹正确

如果还是异常, 请忽略该文章, 就当没看过...

相关推荐
wearegogog1231 小时前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
Drawing stars1 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
品克缤1 小时前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
小二·2 小时前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°2 小时前
vue-设置不同环境的打包和运行
前端·javascript·vue.js
qq_419854052 小时前
CSS动效
前端·javascript·css
烛阴2 小时前
3D字体TextGeometry
前端·webgl·three.js
桜吹雪3 小时前
markstream-vue实战踩坑笔记
前端
C_心欲无痕3 小时前
nginx - 实现域名跳转的几种方式
运维·前端·nginx
花哥码天下3 小时前
恢复网站console.log的脚本
前端·javascript·vue.js