vue中keep-alive的原理
一、概念:
keep-alive 实现了组件的缓存,当组件切换时不会对当前组件进行卸载。常用的2个属性 include / exclude 以及max属性,2个生命周期 activated / deactivated,以及LRU算法。
include 对哪些进行缓存
exclude 对哪些不进行缓存
max 最多缓存多少个
二、原理:
keep-alive是一个组件,这个组件中有三个属性,分别是include、exclude、max,在created中创建缓存列表和缓存组件的key列表,销毁的时候会做一个循环销毁清空所有的缓存和key,当mounted时会监控include 和 include属性,进行组件的缓存处理。如果发生变化会动态的添加和删除缓存。渲染的时候会去拿默认插槽,只缓存第一个组件,取出组件的名字,判断是否在缓存中,如果在就缓存,不在就直接return掉,缓存的时候,如果组件没有key,就自己通过组件的标签、key和cid拼接一个key。如果该组件缓存过,就直接拿到组件实例。如果没有缓存过就把当前的vnode缓存,和key做一个对应关系。这里面有一个算法叫LRU,如果有key就不停的取,如果超限了就采用LRU进行删除最近最久未使用的,从前面删除,LRU就是将当前使用的往数组的后面移,在最前面的就是最久未使用的。
三、源码:
文件位置:src/core/components/keep-alive.js
export default { name: 'keep-alive', abstract: true, props: { include: patternTypes, exclude: patternTypes, max: [String, Number] }, created () { this.cache = Object.create(null) // 创建缓存列表 this.keys = [] // 创建缓存组件的key列表 }, destroyed () { for (const key in this.cache) {// keep-alive销毁时,循环清空所有的缓存和key pruneCacheEntry(this.cache, key, this.keys) } }, mounted () {// 会监控include 和 include属性 进行组件的缓存处理 this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) }, render () { const slot = this.$slots.default // 会默认拿插槽 const vnode: VNode = getFirstComponentChild(slot) // 只缓存第一个组件 const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions if (componentOptions) { // check pattern const name: ?string = getComponentName(componentOptions) // 取出组件的名字 const { include, exclude } = this if ( // 判断是否缓存 // not included (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this const key: ?string = vnode.key == null // same constructor may get registered as different local components // so cid alone is not enough (#3269) ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key // 如果组件没key 就自己通过 组件的标签和key和cid 拼接一个key if (cache[key]) { // 如果缓存中有key vnode.componentInstance = cache[key].componentInstance // 直接拿到组件实例 // make current key freshest remove(keys, key) // 删除当前的key // LRU 最近最久未使用法 keys.push(key) // 并将key放到缓存的最后面 } else { cache[key] = vnode // 缓存vnode keys.push(key) // 将key 存入 // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { // 缓存的太多超过了max就需要删除掉 pruneCacheEntry(cache, keys[0], keys, this._vnode) // 要删除第0个 但是现在渲染的就是第0个 } } vnode.data.keepAlive = true // 并且标准keep-alive下的组件是一个缓存组件 } return vnode || (slot && slot[0]) // 返回当前的虚拟节点 } }
漫思