123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- /* @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 } = keepAliveInstance
- for (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] = null
- remove(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.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
- if (cache[key]) {
- vnode.componentInstance = cache[key].componentInstance
- // make current key freshest
- remove(keys, key)
- keys.push(key)
- } else {
- cache[key] = vnode
- keys.push(key)
- // prune oldest entry
- if (this.max && keys.length > parseInt(this.max)) {
- pruneCacheEntry(cache, keys[0], keys, this._vnode)
- }
- }
- vnode.data.keepAlive = true
- }
- return vnode || (slot && slot[0])
- }
- }
|