6.1.5 react-activation

14,148 阅读2分钟

1, 简介

react 实现keep-alive功能。对比了几个,最终选择 react-activation
简单使用:

import {BrowserRouter ,Switch,Route} from "react-router-dom";
import { AliveScope,KeepAlive } from 'react-activation';

<BrowserRouter>
    <AliveScope>
       <Switch>
           <Route path="/item/:id"  render={props => (
                <KeepAlive id={props.match.params.id}>
                  <Item {...props} />
                </KeepAlive> )}/>
           <Route .../>
       </Switch>
     </AliveScope>
 </BrowserRouter>

生命周期函数:在激活和离开的时候触发

import { useActivate, useUnactivate, withActivation } from 'react-activation'

缓存控制函数:获取或删除缓存

import { withAliveScope, useAliveController } from 'react-activation'

2,实现原理

核心思想:将渲染过的组件(div.ka-content)存起来,等到再次用,从缓存拿到,使用js的 appendchild 到 KeepAlive中的 (div.ka-wrapper)

你可以观察你页面上的元素结构

  <div id="root">
     <div class="ka-wrapper">
       <div class="ka-content">
         <div class="xxx">你的组件xxx</div>
       </div>
     </div>
     <div _ka="/3YV" style="display:none;">
       <div _ka="/Mfull"></div>
       <div _ka="/Mfull"></div>
     </div>
  </div>

KeepAlive组件仅仅是一个placeholder(div.ka-wrapper),并不渲染children,children在Keeper 组件(div _ka="/Mfull")中渲染,存储在storeMap中,再次渲染时去缓存storeMap中根据id拿到children插入到placeholder

react-node-key NodeKey组件:添加key属性,babel转换(版本:0.3.5符号_ka;0.10.2符号_nk),帮助 react-activation 在运行时按渲染位置生成唯一的缓存 id 标识

可参考 一些关于react的keep-alive功能 这篇自己diy的。

3,源码核心结构

3.1,AliveScope组件 -- core/AliveScope.js

三个主要存储结构:
 store = new Map() //[cache],内置Keeper组件中控制添加
 nodes = new Map() // 主要用于render里面遍历,KeepAlive组件中控制添加
 keepers = new Map() //[KeeperRef] 用于操作Keeper
 
handleNodes 方法:执行Keeper中方法

dom结构:这里children就是对应的KeepAlive组件,nodes对应缓存的组件

<AliveScopeProvider value={this.helpers}>
        {children}
        <div style={{ display: 'none' }}>
          {[...this.nodes.values()].map(({ children, ...props }) => (
            <Keeper
              key={props.id}
              {...props}
              scope={this}
              store={this.store}
              keepers={this.keepers}
              ref={(keeper) => {
                this.keepers.set(props.id, keeper)
              }}
            >
              {children}
            </Keeper>
          ))}
        </div>
</AliveScopeProvider>

3.2,全局状态 -- core/context/index.js

如上 AliveScopeProvider 中,主要结构如下:
// 静态化节点上下文内容,防止重复渲染
  helpers = {
    keep: this.keep,
    update: this.update,
    drop: this.drop,
    refresh: this.refresh,
    getCache: this.getCache,
    getNode: this.getNode,
    ...
  }

3.3,KeepAlive组件 -- core/KeepAlive.js

  • render div className=“ka-wrapper”

组件生命周期函数:

constructor:执行init()->
     添加nodes Map、
     inject()挂载激活的node到 ka-wrapper、
     触发Keeper的[LIFECYCLE_ACTIVATE, LIFECYCLE_UNACTIVATE]方法
shouldComponentUpdate:提前触发组件更新
componentWillUnmount:执行eject()移除缓存

主要方法:

init:this.nodes.set(id, {...}]
inject: cache.nodes.forEach((node) => {
          this.placeholder.appendChild(node)
        })
eject: cache.nodes.forEach((node) => {
          this.placeholder.removeChild(node)
        })

3.4,Keeper -- core/Keeper.js

  • render div className=“ka-content”
  • Fragment组件包裹,不渲染到页面
  • nodes缓存的node,在keepAlive组件init的时候插入到KeepAlive组件对应placeholder (className=“ka-wrapper”)

每个激活的KeepAlive组件对应一个Keeper,主要用于观测组件状态,触发响应生命周期函数
组件生命周期函数:

componentDidMount:store.set(id, this.cache)
cache结构:this.cache = {
      listeners,  //使用useActivate, useUnactivate, withActivation的组件
      aliveNodesId: [],
      inited: false,
      cached: false,
      wrapper: node,
      nodes,
      [LIFECYCLE_ACTIVATE]: () => this[LIFECYCLE_ACTIVATE](),
      [LIFECYCLE_UNACTIVATE]: () => this[LIFECYCLE_UNACTIVATE](),
    }
componentWillUnmount: store.delete(id);keepers.delete(id);

主要方法:

attach:listeners订阅中心 
   listeners.set(ref, {
      [LIFECYCLE_ACTIVATE]: () => run(ref, LIFECYCLE_ACTIVATE),
      [LIFECYCLE_UNACTIVATE]: () => run(ref, LIFECYCLE_UNACTIVATE),
    })
refresh:按name刷新缓存状态下的KeepAlive组件
drop:nodes.delete(id)
;[LIFECYCLE_ACTIVATE]() {}、  ;[LIFECYCLE_UNACTIVATE]() {}:KeepAlive组件执行construct的时候触发,发布listeners事件

3.5,生命周期相关 -- core/lifecycles.js

在激活或者失活执行相应钩子函数

  • withActivation(componentDidActivate、componentWillUnactivate)
  • useActivate、useUnactivate
加载的时候 执行 Keeper的 attach 方法,订阅生命周期事件listeners.set()

3.6,缓存控制 -- core/withAliveScope.js

  • withAliveScope
  • useAliveController
借用 aliveScopeContext 里面 来控制

欢迎关注我的前端自检清单,我和你一起成长