Appearance
KeepAlive
基本使用
const My1 = {
render:()=>h('h1','hello')
}
// keepAlive会对渲染的组件进行缓存
render(h(KeepAlive,null,{
default:()=>h(My1)
}),app);
1
2
3
4
5
6
7
2
3
4
5
6
7
创建上下文对象,存储
keepAlive
组件渲染时所需的属性
const instance = { // 组件的实例
ctx:{}, // instance上下文
}
1
2
3
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
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
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
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
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
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
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
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
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
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
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
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
2
3
4
5
6
7
8
9
10