浅谈Vue页面级缓存解决方案feb-alive(上)

2020-06-14 06:24:38易采站长站整理

继续看下keep-alive在缓存的存储和读取的具体实现,先用一个简单的demo来描述keep-alive对于组件的缓存以及恢复缓存的过程


let Foo = {
template: '<div class="foo">foo component</div>',
name: 'Foo'
}
let Bar = {
template: '<div class="bar">bar component</div>',
name: 'Bar'
}
let gvm = new Vue({
el: '#app',
template: `
<div id="#app">
<keep-alive>
<component :is="renderCom"></component>
</keep-alive>
<button @click="change">切换组件</button>
</div>
`,
components: {
Foo,
Bar
},
data: {
renderCom: 'Foo'
},
methods: {
change () {
this.renderCom = this.renderCom === 'Foo' ? 'Bar': 'Foo'
}
}
})

上面例子中,根实例的template会被编译成如下render函数


function anonymous(
) {
with(this){return _c('div',{attrs:{"id":"#app"}},[_c('keep-alive',[_c(renderCom,{tag:"component"})],1),_c('button',{on:{"click":change}})],1)}
}

可使用在线编译:https://cn.vuejs.org/v2/guide/render-function.html#模板编译

根据上面的render函数可以知道,vnode生成的过程是深度递归的,先创建子元素的vnode再创建父元素的vnode。
所以首次渲染的时候,在生成keep-alive组件vnode的时候,Foo组件的vnode已经生成好了,并且作为keep-alive组件vnode构造函数(_c)的参数传入。


_c('keep-alive',[_c(renderCom,{tag:"component"})

生成的keep-alive组件的vnode如下


{
tag: 'vue-component-2-keep-alive',
...
children: undefined,
componentInstance: undefined,
componentOptions: {
Ctor: f VueComponent(options),
children: [Vnode],
listeners: undefined,
propsData: {},
tag: 'keep-alive'
},
context: Vue {...}, // 调用 $createElement/_c的组件实例, 此处是根组件实例对象
data: {
hook: {
init: f,
prepatch: f,
insert: f,
destroy: f
}
}
}

此处需要注意组件的vnode是没有children的,而是将原本的children作为vnode的componentOptions的children属性,componentOptions在组件实例化的时候会被用到,同时在初始化的时候componentOptions.children最终会赋值给vm.$slots,源码部分如下


// createComponent函数
function createComponent (Ctor, data, context, children, tag) {
// 此处省略部分代码
...
var vnode = new VNode(
("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),
data, undefined, undefined, undefined, context,