keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染,其有以下特性:
- 一般结合路由和动态组件一起使用,用于缓存组件;
- 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存, exclude 表示任何名称匹配的组件都不会被缓存,其中 exclude 的优先级比 include 高;
- 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated ,当组件被移除时,触发钩子函数 deactivated
- 2.5.0 新增 max 属性,表示最多可缓存多少组件实例,一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
使用场景
在动态组件上使用 keep-alive
当我们使用动态组件使用 is 在组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
<!-- 失活的组件将会被缓存!--><keep-alive><component v-bind:is="currentTabComponent"></component></keep-alive>
在 vue-router 中应用
<keep-alive :include="whiteList" :exclude="blackList" :max="amount"><router-view></router-view></keep-alive>
源码
// src/core/components/keep-alive.js/* @flow */import { isRegExp, remove } from 'shared/util'import { getFirstComponentChild } from 'core/vdom/helpers/index'type VNodeCache = { [key: string]: ?VNode };function getComponentName (opts: ?VNodeComponentOptions): ?string {return opts && (opts.Ctor.options.name || opts.tag)}function matches (pattern: string | RegExp | Array<string>, name: string): boolean {if (Array.isArray(pattern)) {return pattern.indexOf(name) > -1} else if (typeof pattern === 'string') {return pattern.split(',').indexOf(name) > -1} else if (isRegExp(pattern)) {return pattern.test(name)}/* istanbul ignore next */return false}function pruneCache (keepAliveInstance: any, filter: Function) {const { cache, keys, _vnode } = keepAliveInstancefor (const key in cache) {const cachedNode: ?VNode = cache[key]if (cachedNode) {const name: ?string = getComponentName(cachedNode.componentOptions)if (name && !filter(name)) {pruneCacheEntry(cache, key, keys, _vnode)}}}}function pruneCacheEntry (cache: VNodeCache,key: string,keys: Array<string>,current?: VNode) {const cached = cache[key]if (cached && (!current || cached.tag !== current.tag)) {cached.componentInstance.$destroy()}cache[key] = nullremove(keys, key)}const patternTypes: Array<Function> = [String, RegExp, Array]export default {name: 'keep-alive',abstract: true,props: {include: patternTypes,exclude: patternTypes,max: [String, Number]},created () {this.cache = Object.create(null)this.keys = []},destroyed () {for (const key in this.cache) {pruneCacheEntry(this.cache, key, this.keys)}},mounted () {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.defaultconst vnode: VNode = getFirstComponentChild(slot)const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {// check patternconst name: ?string = getComponentName(componentOptions)const { include, exclude } = thisif (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode}const { cache, keys } = thisconst 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.keyif (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshestremove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}return vnode || (slot && slot[0])}}
参考文档:
- keep-alive实现原理有源码剖析
