From 989b810a6c6e04b69e1f429bbbfdc9b0ddb86e66 Mon Sep 17 00:00:00 2001 From: Alena Yuryeva Date: Mon, 14 Oct 2019 02:20:55 +0300 Subject: [PATCH] src/core/components/keep-alive.js from Vue https://github.com/vuejs/vue/blob/215f877/src/core/components/keep-alive.js --- keep-alive.js | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 keep-alive.js diff --git a/keep-alive.js b/keep-alive.js new file mode 100644 index 0000000..fb4cf1e --- /dev/null +++ b/keep-alive.js @@ -0,0 +1,124 @@ +/* @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, 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, + 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 = [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]) + } +}