Skip to content
On this page

KeepAlive基本使用

const My1 = {
    render:()=>h('h1','hello')
}
// keepAlive会对渲染的组件进行缓存
 render(h(KeepAlive,null,{
     default:()=>h(My1)
 }),app);
1
2
3
4
5
6
7

创建上下文对象,存储keepAlive组件渲染时所需的属性

const instance = { // 组件的实例
    ctx:{}, // instance上下文
}
1
2
3
export const KeepAliveImpl = { // keepAlive本身没有任何功能
    __isKeepAlive:true,
    setup(props,{slots}){
        return ()=>{
            let vnode = slots.default();
            return vnode; // 渲染插槽的内容
        }
    }
}
export const isKeepAlive = vnode => vnode.type.__isKeepAlive;
1
2
3
4
5
6
7
8
9
10
const mountComponent = (vnode,container,anchor,parentComponent) =>{
     // 1) 要创造一个组件的实例
     let instance = vnode.component = createComponentInstance(vnode,parentComponent);
     if(isKeepAlive(vnode)){
         (instance.ctx as any).renderer = {
             patch,
             createElement:hostCreateElement,
             move(vnode,container){
                 hostInsert(vnode.component.subTree.el,container)
             },
             unmount
         }
     }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

缓存组件

在渲染完毕后需要对subTree进行缓存,需要保证渲染完毕后在调用mounted事件

export const KeepAliveImpl = {
    __isKeepAlive:true,
    setup(props,{slots}){
        const keys = new Set(); // 缓存的key
        const cache = new Map(); // 缓存key对应的虚拟节点
        const instance = getCurrentInstance();

        let pendingCacheKey = null;
        onMounted(()=>{
            cache.set(pendingCacheKey,instance.subTree);
        })
        return ()=>{
            let vnode = slots.default();
            // 如果
            if(!isVnode(vnode) || !(vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)){
                return vnode;
            }
            const comp = vnode.type; // 拿到组件
            // 获取组件的key
            const key = vnode.key == null ? comp : vnode.key;
            const cacheVNode = cache.get(key);
            pendingCacheKey = key;
            if(cacheVNode){
                
            }else{
                keys.add(key);
            }
            // 标识组件
            vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
            return vnode;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

复用组件

export const KeepAliveImpl = {
    __isKeepAlive:true,
    setup(props,{slots}){
        // ...
        let {createElement,move,_unmount} = instance.ctx.renderer;
        const storageContainer = createElement('div'); // 缓存盒子
        instance.ctx.activate = (vnode,container,anchor)=>{  // 激活则移动到容器中
            move(vnode,container,anchor);
        }
        instance.ctx.deactivate = (vnode)=>{ // 卸载则移动到缓存盒子中
            move(vnode,storageContainer,null);
        }
        return ()=>{
            // ...
            if(cacheVNode){ // 缓存中有
                vnode.component = cacheVNode.component; // 复用组件,并且标识不需要真正的创建
                vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let {h,render,reactive,provide,inject,Teleport,defineAsyncComponent,KeepAlive,ref} = VueRuntimeDOM

// keepAlive会对渲染的组件进行缓存
const state = ref(true)
const My1 = {
    render:()=>h('h1','hello')
}
const My2 = {
    render:()=> h('h1','world')
}
render(h(KeepAlive,null,{ // 渲染My1
    default:()=>h(My1)
}),app);

setTimeout(()=>{
    render(h(KeepAlive,null,{ // 渲染My2
        default:()=>h(My2)
    }),app);
},1000)

setTimeout(()=>{
    render(h(KeepAlive,null,{ // 在渲染My1
        default:()=>h(My1)
    }),app);
},2000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

卸载组件

const unmount = (vnode,parentComponent) =>{
    const {shapeFlag} = vnode;
    if(shapeFlag &ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE){
        parentComponent.ctx.deactivate(vnode);
        return
    }
}
1
2
3
4
5
6
7

挂载组件

const processComponent = (n1,n2,container,anchor,parentComponent) =>{ // 统一处理组件, 里面在区分是普通的还是 函数式组件
    if(n1 == null){
        if(n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE){
            parentComponent.ctx.activate(n2,container,anchor)
        }else{
            mountComponent(n2,container,anchor,parentComponent);
        }
    }else{  
        // 组件更新靠的是props
        updateComponent(n1,n2)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
const cacheSubtree = ()=>{
    cache.set(pendingCacheKey,instance.subTree);
}
onMounted(cacheSubtree);
onUpdated(cacheSubtree); // 在更新时进行重新缓存
1
2
3
4
5

include和exclude

export const KeepAliveImpl = {
    __isKeepAlive:true,
    props:{
        include:{},
        exclude:{}
    },
    setup(props,{slots}){
        return ()=>{
            // 如果有组件名字   不在缓存列表 / 在排除列表中
            // 其实这里还需要观测这两个属性的变化 
            if( name &&  (include && !include.split(',').includes(name)) || (exclude && include.split(',').includes(name))){
                return vnode;
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

max控制缓存

function unmount(vnode){
    resetShapeFlag(vnode)
    _unmount(vnode, instance)
}
function pruneCacheEntry(key){
    const cached = cache.get(key)
    _unmount(cached)
    cache.delete(key);
    keys.delete(key);
}
1
2
3
4
5
6
7
8
9
10
if(cacheVNode){ // 缓存中有
    vnode.component = cacheVNode.component; // 复用组件,并且标识不需要真正的创建
    vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
}else{
    keys.add(key);
    if(max && keys.size > max){ // 超过限制删除第一个
        pruneCacheEntry(keys.values().next().value);
    }
}
vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
1
2
3
4
5
6
7
8
9
10
function resetShapeFlag(vnode) {
    let shapeFlag = vnode.shapeFlag
    if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
        shapeFlag -= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
    }
    if (shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
        shapeFlag -= ShapeFlags.COMPONENT_KEPT_ALIVE
    }
    vnode.shapeFlag = shapeFlag
}
1
2
3
4
5
6
7
8
9
10

Released under the MIT License.