<script> export default { name: 'app-keep-alive', render() { const slot = this.$slots.default; const vnode = this.getFirstComponentChild(slot); const componentOptions = vnode && vnode.componentOptions; if (componentOptions) { // check pattern const name = this.getComponentName(componentOptions); const include = this.include; const exclude = this.exclude; const routerList = this.routerList; const route = this.$route; if ( // not included (include && (!name || !this.matches(include, name))) || // excluded (exclude && name && this.matches(exclude, name)) || (routerList && (!route.fullPath && !this.matches(routerList, route.fullPath))) ) { return vnode; } const cache = this.cache; const keys = this.keys; const key = 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 this.remove(keys, key); keys.push(key); } else { cache[key] = vnode; keys.push(key); // prune oldest entry if (this.max && keys.length > parseInt(this.max)) { this.pruneCacheEntry(cache, keys[0], keys, this._vnode); } } vnode.data.keepAlive = true; vnode.data.curPath = route.fullPath; } return vnode || (slot && slot[0]) }, props: { include: [String, RegExp, Array], exclude: [String, RegExp, Array], max: [String, Number], routerList: [Array] }, data() { return { _toString: Object.prototype.toString } }, created() { this.cache = {}; this.keys = []; }, destroyed() { for (let key in this.cache) { this.pruneCacheEntry(this.cache, key, this.keys); } }, watch: { include(val) { this.pruneCache((name) => { return this.matches(val, name); }); }, exclude(val) { this.pruneCache((name) => { return !this.matches(val, name); }); }, routerList(val) { this.pruneCache2((name) => { return !this.matches(val, name); }); } }, methods: { pruneCacheEntry(cache, key, keys, current) { const cached = cache[key]; if (cached) { cached.componentInstance.$destroy(); } cache[key] = null; this.remove(keys, key); }, pruneCache(filter) { for (let key in this.cache) { const cachedNode = this.cache[key]; if (cachedNode) { const name = this.getComponentName(cachedNode.componentOptions); if (name && !filter(name)) { this.pruneCacheEntry(this.cache, key, this.keys, this._vnode); } } } }, pruneCache2(filter) { for (let key in this.cache) { const cachedNode = this.cache[key]; if (cachedNode) { const name = cachedNode.data.curPath; if (name && filter(name)) { this.pruneCacheEntry(this.cache, key, this.keys, this._vnode); } } } }, matches(pattern, name) { if (Array.isArray(pattern)) { return pattern.findIndex(item => item.to.fullPath === name) !== -1; } else if (typeof pattern === 'string') { return pattern.split(',').indexOf(name) > -1; } else if (this.isRegExp(pattern)) { return pattern.test(name); } /* istanbul ignore next */ return false; }, getComponentName(opts) { return opts && (opts.Ctor.options.name || opts.tag) }, getFirstComponentChild(children) { if (Array.isArray(children)) { for (let i = 0; i < children.length; i++) { const c = children[i]; if (this.isDef(c) && (this.isDef(c.componentOptions) || this.isAsyncPlaceholder(c))) { return c; } } } }, isAsyncPlaceholder(node) { return node.isComment && node.asyncFactory; }, isDef(v) { return v !== undefined && v !== null; }, isRegExp(v) { return this._toString.call(v) === '[object RegExp]'; }, remove(arr, item) { if (arr.length) { const index = arr.indexOf(item); if (index > -1) { return arr.splice(index, 1); } } } } } </script>