背景
背景是这样的,咱们运用vue2
开发一个在线客服运用的IM运用,根本布局是左边是访客列表,右边是访客对话,为了让对话加载更友爱,咱们将对话的路由运用<keep-alive>
缓存起来。可是假如将所有对话都缓存,未必会形成缓存过多卡顿的问题。天然,就运用上了<keep-alive>
供给的max
特点,设置一个缓存对话内容组件上限,依照LRU
算法,会毁掉最旧拜访的组件,保存最近运用的组件。本认为美好按期而至,直到上线后翻大车了,实在对话量大了,内存飙升卡顿。后来具体分析内存增长点,经过vue
的devtool
检查组件树,发现对话内容组件一直是递加,并非维持在max
设置的数量上限!
各位看官稍安勿躁,下面就具体分析形成这个「大坑」的原理,现已处理它的计划。
情形模仿
为了方便模仿背景事例,这儿就用vue2
简略的写一个demo。
对话列表组件 APP.vue
,点击列表中的某个访客,加载与访客对话内容。
<template>
<div id="app">
<section class="container">
<aside class="aside">
<ul>
<li :class="{ active: active === index }" v-for="(user, index) in userList" :key="index"
@click="selectUser(index, user)">
{{ user.name }}
</li>
</ul>
</aside>
<section class="main">
<keep-alive :max="3">
<chat-content :key="currentUser.id" :user-info="currentUser"></chat-content>
</keep-alive>
</section>
</section>
</div>
</template>
<script>
import ChatContent from './views/ChatContent.vue';
export default {
components: {
ChatContent
},
data() {
return {
active: -1,
currentUser: {},
userList: [{ id: 1, name: "张三" },
{ id: 2, name: "李四" },
{ id: 3, name: "王五" },
{ id: 4, name: "老六" },
{ id: 5, name: "老八" },
{ id: 6, name: "老九" },
]
}
},
methods: {
selectUser(index) {
this.active = index
this.currentUser = this.userList[index];
}
},
}
</script>
<script>
import ChatContent from './views/ChatContent.vue';
export default {
components: {
ChatContent
},
data() {
return {
active: -1,
currentUser: {},
userList: [{ id: 1, name: "张三" },
{ id: 2, name: "李四" },
{ id: 3, name: "王五" },
{ id: 4, name: "老六" },
{ id: 5, name: "老八" },
{ id: 6, name: "老九" },
]
}
},
methods: {
selectUser(index) {
this.active = index
this.currentUser = this.userList[index];
}
},
}
</script>
这儿运用keep-alive
组件包裹的对话内容组件,需求加上key
仅有标志,这样才会缓存相同称号(不同key
)的组件,不然不会缓存。
对话内容组件ChatContent.vue
,简略加一个计数器验证组件缓存了。
<template>
<div>
<h2>{{ userInfo.name }}</h2>
<h3>{{ num }}</h3>
<button @click="increament">+1</button>
</div>
</template>
<script>
export default {
props: {
userInfo: Object,
},
data() {
return {
num: 0,
};
},
methods: {
increament() {
this.num += 1;
},
},
};
</script>
methods: {
increament() {
this.num += 1;
},
},
};
</script>
情形模仿作用
实验发现,尽管缓存组件个数上限max
为3,实际是逐一缓存了悉数内容组件,看来设置max
特点失效了。
Vue2中<keep-alive>组件完成原理
为什么缓存相同称号的组件,max
特点会失效呢?这儿就要从Vue2
中<keep-alive>
组件完成原理来看。
<keep-alive>LRU算法
-
vue
会将VNode
及组件实例(componentInstance
)存到缓存(cache
),cache
是一个Object
,一起还会保护一个keys
行列; - 依据
LRU
算法对cache
和keys
的办理:当时激活组件已存在缓存中,将组件对应key
先删去,再刺进的方法往前移动; -
当时激活组件没有再缓存中,直接存入缓存,此刻判别是否超过了缓存个数上限,假如超过了,运用
pruneCacheEntry
整理keys
第一个位置(最旧)的组件对应的缓存。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); console.log('cache: ', cache) console.log('keys: ', keys) } }
pruneCacheEntry(cache, keys[0], keys, this._vnode); console.log('cache: ', cache) console.log('keys: ', keys) } }
<keep-alive>整理缓存函数完成
下面再来看整理缓存函数
pruneCacheEntry
的完成:比对当时传入组件和在缓存中的组件tag
是否相同,假如不同,就去毁掉组件实例,不然不毁掉。function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~
获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~
和function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~
。这样咱们就能够经过function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~
算法,依据function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 。这样咱们就能够经过function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的function pruneCacheEntry ( cache, key, keys, current ) { var cached1 = cache[key]; if (cached1 && (!current || cached1.tag !== current.tag)) { cached1.componentInstance.destroy(); } cache[key] = null; remove(keys, key); }</code></pre><p>看到这儿好像也没有缺点,究竟是哪里出问题了呢?</p><h3><keep-alive>源码调试发现问题</h3><p>无妨咱们打印一下<code>cache</code>(<code>VNode</code>缓存)和<code>keys</code><br><br>发现也没什么问题,依照<code>URL</code>算法得到正确作用。<br>再看看整理缓存函数里<code>cached$$1.tag</code>和<code>current.tag</code>的打印<br><br>本相了!他两由于组件称号相同,导致持平,没有进入毁掉组件实例的判别里,这便是问题来历!为什么针对相同组件称号不去毁掉实例呢?或许是为了某些情形下组件复用吧。</p><h2>处理计划</h2><p>已然问题症结咱们现已找到,从源头上去处理问题当然最佳,可是现实是<code>vue2</code>源码层面是没有去处理的(<code>vue3</code>有处理,这个后边再说),只能从咱们运用侧再去想想方法。这儿我想到的有两种计划。</p><h3>计划一:剪枝法</h3><p>保护一个大局状况(比方<code>vuex</code>)对话<code>ids</code>行列,最大长度为<code>max</code>,相似<code>vue</code>中<code>LRU</code>算法中的<code>keys</code>,在组件<code>activated</code>钩子函数触发时更新<code>ids</code>行列。对话内容组件的子组件判别当时对话<code>id</code>是否在<code>ids</code>行列中,不在那么就会<code>v-if</code>除掉,不然缓存起来,这样很大程度程度上开释缓存。相似剪去树的枝丫,减轻分量,这儿叫做「剪枝法」好了。</p><h3>计划二:自定义整理缓存函数</h3><p>咱们不再运用<code>keep-alive</code>供给的<code>max</code>特点来整理缓存,让其将组件实例悉数缓存下来,当时激活组件,<code>activated</code>钩子函数触发,此刻经过<code>this.vnode.parent.componentInstance获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 获取组件实例,从而能够获取挂载在上面的cache和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 和keys。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 。这样咱们就能够经过LRU算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~ 算法,依据key自定义精准整理缓存了。 activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) }, 下面对照 vue的devtool东西检查作用 完全符合预期!方法二从<keep-alive>组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。 你认为这样就完了?上面我还说到在vue3中现已处理了这个问题。 vue3中<KeepAlive>组件完成原理 话不多说,先来看上面相同的事例在运用vue3写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool组件树的体现。 没有冗余缓存组件,奈斯! vue3中<KeepAlive>LRU算法 vue3中LRU算法完成思路相同,只不过cache和keys别离运用Map和Set数据结构完成,数据更洁净简练。 const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } } vue3中<KeepAlive>整理缓存函数完成 vue3中整理组件实例缓存函数也是pruneCacheEntry,不同的是,比对当时传入组件和在缓存中的组件tag是否相同,决议是否毁掉组件实例。 function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); } 再来看看cache.type和current.type究竟是什么 比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:props、render、setup、__hmrId等。 function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } } 即使是目标中所有特点相同,可是目标不是同一个引证地址,形成cache.type和current.type不持平,因此会毁掉实例目标unmount(cached)。以上便是vue3对这个问题处理计划。 总结 最终,在vue2中会呈现<keep-alive>缓存相同称号组件,max失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~
自定义精准整理缓存了。activated() { const { cache, keys } = this.$vnode.parent.componentInstance; console.log('activated cache: ', cache) console.log('activated keys: ', keys) let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) },
let cacheLen = 0 const max = 3 Object.keys(cache).forEach(key => { if (cache[key]) { cacheLen += 1 if (cacheLen > max) { const key = keys.shift() cache[key].componentInstance.$destroy() cache[key] = null } } }) },
下面对照
vue
的devtool
东西检查作用完全符合预期!方法二从
<keep-alive>
组件根就整理了缓存组件,更完全,对事务代码侵染性也更小。
你认为这样就完了?上面我还说到在vue3
中现已处理了这个问题。vue3中<KeepAlive>组件完成原理
话不多说,先来看上面相同的事例在运用
vue3
写的作用怎么呢?这儿就不“重复”贴代码了,直接看devtool
组件树的体现。没有冗余缓存组件,奈斯!
vue3中<KeepAlive>LRU算法
vue3
中LRU
算法完成思路相同,只不过cache
和keys
别离运用Map
和Set
数据结构完成,数据更洁净简练。const cache = new Map(); const keys = new Set(); // ... if (cachedVNode) { // copy over mounted state vnode.el = cachedVNode.el; // ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } }
// ... // make this key the freshest keys.delete(key); keys.add(key); } else { keys.add(key); // prune oldest entry if (max && keys.size > parseInt(max, 10)) { pruneCacheEntry(keys.values().next().value); } }
vue3中<KeepAlive>整理缓存函数完成
vue3
中整理组件实例缓存函数也是pruneCacheEntry
,不同的是,比对当时传入组件和在缓存中的组件tag
是否相同,决议是否毁掉组件实例。function pruneCacheEntry(key) { const cached = cache.get(key); if (!current || cached.type !== current.type) { unmount(cached); } else if (current) { // current active instance should no longer be kept-alive. // we can't unmount it now but it might be later, so reset its flag now. resetShapeFlag(current); } cache.delete(key); keys.delete(key); }
再来看看
cache.type
和current.type
究竟是什么比照咱们会发现,不再是简略的组件称号字符标志,而是一个目标描绘,包含了许多特点,由于在初始化组件实例时,会给每个实例加上特点:
props
、render
、setup
、__hmrId
等。function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {}; const attrs = {}; def(attrs, InternalObjectKey, 1); instance.propsDefaults = /* @PURE */ Object.create(null); setFullProps(instance, rawProps, props, attrs); // ... instance.attrs = attrs; } function isInHmrContext(instance) { while (instance) { if (instance.type.__hmrId) return true; instance = instance.parent; } }
即使是目标中所有特点相同,可是目标不是同一个引证地址,形成
cache.type
和current.type
不持平,因此会毁掉实例目标unmount(cached)
。以上便是vue3
对这个问题处理计划。总结
最终,在
vue2
中会呈现<keep-alive>
缓存相同称号组件,max
失效的问题,引荐运用自定义整理缓存函数,在获取组件实例基础上,对缓存实例毁掉。下图是我在实在项目中优化的作用。完~
本文来历:嘿,vue中keep-alive有个「大坑」你或许还不知道