react页面缓存

61 阅读4分钟

产品最近提出了一个 H5 页面的优化需求,具体场景是这样的:用户在首页浏览产品列表时,点击某个产品进入详情页 A,浏览完返回首页后,当前实现逻辑会让首页重新请求数据,然后根据 sessionStorage 里保存的滚动位置信息,重新将页面滚动到离开前的位置。

但产品觉得这种体验不够理想 —— 他特别提到,返回首页时能明显感觉到页面在重新加载数据,而且滚动恢复的过程有肉眼可见的 “跳动感”。为此,他还专门拿支付宝的理财页面举例:在那里从列表页进入详情页,再返回列表时,不仅不会重新加载数据,页面还能瞬间回到之前浏览的位置,整个过程几乎 “无感”,完全没有卡顿或滚动的突兀感。

说到底,产品想要的其实就是类似 Vue 中<KeepAlive>组件的页面缓存效果:让首页在跳转详情页时保持状态(包括数据和滚动位置)不被销毁,返回时直接复用已有的内容和状态,从而实现极致流畅的切换体验。

我想 React 应该也有相关技术,所以开始了一番搜索和尝试。

react-router-cache-route

GPT 第一个推荐我的插件就是 react-router-cache-route, git地址 github.com/CJY0208/rea…

我以为万事大吉,直接用就可以了。结果,Readme 上面赫然写着:⚠️⚠️⚠️ React Router v6+ is NOT supported ⚠️⚠️⚠️

也就是 react-router-cache-route 不支持 Router v6+ 版本,而我们使用的正是 Router v6+ 版本

react-activation

react-activation 是我搜到的第二个插件, git地址 github.com/CJY0208/rea…

赶紧先看看 Readme 注意事项,果然第一条:

image.png

  • 不要使用 <React.StrictMode />
  • (React v18+) 不要使用 ReactDOMClient.createRoot, 使用 ReactDOM.render

还好,还好,这两条都好解决。

按照文档把项目去掉 <React.StrictMode /> ,同时,本身就是使用 ReactDOM.render 所以没问题。

开始验证

使用

增加 AliveScope

// root.tsx
import {AliveScope} from 'react-activation'

  root.render(
    <ErrorBoundary FallbackComponent={FatalError}>
      <Provider store={store}>
          <BrowserRouter basename={process.env.BASE_URL}>
          <AliveScope>
            <Suspense fallback={<PageLoading delay={500}/>}>
                <App/>
            </Suspense>
          </AliveScope>
          </BrowserRouter>
      </Provider>
    </ErrorBoundary>,
  )

增加 KeepAlive

const Test = lazy(() => import('./features/test'))

<Routes>
    <Route
      path="test/*"
      element={<KeepAlive cacheKey="test" name="test"><Test/></KeepAlive>}
    />
</Routes>

结果发现 Test 页面一片空白,然后各种搜索,没找到问题,没办法去 react-activation git 上扒 issue 看看大家有没有遇到同样的问题。 github.com/CJY0208/rea…

果然,看到一个问题是 React18中,KeepAlive必须具名导入,然后我觉得可能问题就出在这里,所以,修改要缓存的 Test.tsx 文件导出方式:

// Test.tsx
export const Test = () => {
  return (
    <div>...</div>
  )
}

路由引入方式:

import {Test} from './features/test

<Routes>
    <Route
      path="test/*"
      element={<KeepAlive cacheKey="test" name="test"><Test/></KeepAlive>}
    />
</Routes>

页面可以正常显示啦~

踩坑

我们项目中,由于各页面顶部的标题导航样式和处理逻辑相似,因此封装了一个通用的顶部导航组合组件 <TopBackNav>。不过这个组件有个特殊处理:首页的导航返回逻辑是退出页面,而其他页面则直接通过 navigate(-1) 返回上一级。

最近在做页面缓存时遇到一个问题:从首页跳转到页面 A,再从 A 通过其他方式跳转回首页,首页能正常缓存;但如果从首页到 A 后,通过 <TopBackNav> 的返回按钮回到首页,首页却始终无法缓存。

起初我一直以为是 react-router 和 react-activation 的版本不兼容导致的,排查了很久都没找到原因。最后通过排除法一步步测试,才发现问题出在公共组件上 —— 原来是 <TopBackNav> 本身被缓存了,导致其内部的返回逻辑没有正确触发缓存机制。

具体来说,首页和页面 A 都用到了这个顶部导航组件,但问题的症结在于:首页的导航组件被缓存后,其内部绑定的返回事件(退出页面逻辑)出现了异常传导,莫名影响到了页面 A 导航组件的返回事件(本应是navigate(-1))。

这种缓存导致的事件 “串线”,让页面 A 的返回行为始终无法正确触发首页的缓存机制。最后实在找不到更优解,只好让首页放弃使用这个公共导航组件,改用单独实现的导航栏。这么一来,缓存逻辑不再受干扰,问题也就彻底解决了。

整理一下就是说,如果缓存的页面和其他页面共用了组件,还是要注意一下,是不是这个组件会导致一些意想不到的问题。

总结

react-activation 在 React v18+、React Router v6+ 版本上是可以适配使用的,需要注意的就是缓存页面的时候采用 具名导入