# 组件缓存

功能点:

  • 缓存组件状态(不仅是数据状态,还包括页面上的状态,比如表单)
  • 增加生命周期,允许用户在不同的状态下操作

实现难点

  • 如何保存DOM节点,不被销毁掉
  • 如何保存对应的state状态(这个简单)
  • 路由维度?还是组件纬度去实现?
  • 如何注入我们自己的生命周期(这个也好说)
    • 组件内部定义实现对应的周期函数
    • 由根组件Alive去调对应的周期函数

设计:

  • KeepaliveItem组件
    • 用来包裹想要进行缓存的组件,通过传入cacheId来进行控制,同时cacheId也用来清除缓存
  • useCacheDestroy
    • 用来清除某个组件的缓存,通过传入组件id控制
    • 所以需要有一个地方缓存全局的组件信息
  • KeepaliveScope
    • 全局管理上下文

问题一:怎么缓存dom

我们可以简单的用display来控制隐藏显示,只要不销毁dom节点,对应的Fiber节点就不会消失;那如何避免组件销毁呢?要知道控制条件不成立时组件是不会渲染的,比如在!isShow && KeepaliveItem下的Content组件,这时候Content不会渲染,更不要说存活了;所以我们得找个地方存放keepaliveitem下的children,避免react的自顶向下更新影响;

所以我们的缓存组件从表面上看是在keepaliveItem中,但实际渲染是在外部渲染的,这样就不受keepaliveitem的存活影响了;

我们将外部渲染交给KeepaliveScope

问题二:由于我们将渲染交给了外部,dom节点容器外面的样式可以会定位不准

好说,渲染后将真实的dom回传给keepaliveitem,销毁的时候再回传给scope管理


总体来说:

实现react版本的keepalive还需要结合react自身的架构,以及更新流程去实现;由于自顶向下更新,我们不能单纯用状态来渲染控制隐藏节点,因为当一个节点销毁时,下面的子节点都会全部跟着销毁,于是状态重置,dom节点也移除;

针对这个问题,那只要我们的child节点不在实际的dom结构里面创建不就好了吗,脱离了reat的更新,这里我们使用官方提供的createPortal来创建child element节点,当我们的缓存容器激活时,根据id判断有没有缓存节点拿出来,没有则创建;

当然以上是可以实践的,但存在一些问题:比如child节点样式是依赖parent节点前缀的,但我们的dom不在parent里面渲染就导致样式不生效。针对这个问题,我们只需要根据将渲染好的节点再手动塞到parent容器下。

除此之处,我们还可以用reactContext上下文来提供全局缓存的信息,提供hooks来让用户决定手动清除缓存。

最后,让我们期待一下v18的offScreen!