在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是否包裹正确

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

相关推荐
学嵌入式的小杨同学4 小时前
从零打造 Linux 终端 MP3 播放器!用 C 语言实现音乐自由
linux·c语言·开发语言·前端·vscode·ci/cd·vim
weixin_425543734 小时前
TRAE CN3.3.25 构建的Electron简易DEMO应用
前端·typescript·electron·vite·nestjs
Mr Xu_5 小时前
【Vue3 + ECharts 实战】正确使用 showLoading、resize 与 dispose 避免内存泄漏
前端·信息可视化·vue·echarts
0思必得05 小时前
[Web自动化] Selenium设置相关执行文件路径
前端·爬虫·python·selenium·自动化
雯0609~5 小时前
hiprint:实现项目部署与打印1-官网提供普通html版本
前端·html
不绝1916 小时前
UGUI——进阶篇
前端
Exquisite.6 小时前
企业高性能web服务器(4)
运维·服务器·前端·网络·mysql
2501_944525547 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 账户详情页面
android·java·开发语言·前端·javascript·flutter
2601_949857437 小时前
Flutter for OpenHarmony Web开发助手App实战:快捷键参考
前端·flutter
wangdaoyin20107 小时前
若依vue2前后端分离集成flowable
开发语言·前端·javascript