Preface
Hello, everyone, I’m Lin Sanxin, uses the most easy-to-understand knowledge to explain the most difficult knowledge points is my motto, and is the prerequisite for advanced is my original intention.
In retrospect, when I first started writing, I wrote the Vue source code series , which are all included in my Nuggets column Vue source code analysis :
- "Vue source code learning (1)" What you don't know-data response principle
- Vue source code learning (2)" you don't know-template compilation principle
- "Vue source code learning (3)" You don't know-the principle of first rendering
- "Vue source code learning (4)" is determined to write a computed, watch principle that everyone can understand
- "Vue source code learning (5)" interviewers like to ask-Vue common methods source code analysis
- Vue source code learning" Do you want to know the implementation principle of Vuex?
- "Vue source code learning" Do you really know how the slot is "plugged"
- 15 pictures, 20 minutes to understand the core principle of the Diff algorithm, I said! ! !
- Lin Sanxin drew 8 pictures, the most easy-to-understand Vue3 responsive core principle analysis
- 7 pictures, a simple version of Vue-Router is realized from scratch, which is too easy to understand!
Today, I will tell you the basic principles keep-alive
Scenes
Maybe you will often encounter such a scenario in your usual development: there is a list page List.vue
that can be filtered. When you click on an item, you enter the corresponding detail page. When you return to List.vue
from the detail page, you find that the list page is refreshed. Up! The filter criteria just now are gone! ! !
keep-alive
What is it?
keep-alive
is aVue global component
keep-alive
itself will not be rendered, nor will it appear in the parent component chainkeep-alive
wrapping dynamic components, inactive components are cached instead of destroying them
how to use?
keep-alive
receives three parameters:
include
strings, regular expressions, and arrays can be passed, and the components whose names match successfully will be cached
exclude
strings, regular expressions, and arrays can be passed. Components whose names match successfully will not be cached
max
number can be passed to limit the maximum number of cache components
include
and exclude
, arrays of 161c9020bdbdfc
Dynamic component
<keep-alive :include="allowList" :exclude="noAllowList" :max="amount">
<component :is="currentComponent"></component>
</keep-alive>
Routing component
<keep-alive :include="allowList" :exclude="noAllowList" :max="amount">
<router-view></router-view>
</keep-alive>
Source code
Component foundation
keep-alive
earlier, 061c9020bdbe5d is a Vue global component, which receives three parameters:
include
strings, regular expressions, and arrays can be passed, and components whose names match successfully will be cached
exclude
strings, regular expressions, and arrays can be passed. Components whose names match successfully will not be cached
max
numbers can be passed to limit the maximum number of cache components. If the number exceeds
max
it will be replacedLRU algorithm
By the way keep-alive
in each life cycle:
created
: Initialize acache、keys
, the former is used to store the virtual dom collection of the cache component, and the latter is used to store the key collection of the cache componentmounted
: Real-time monitoringinclude、exclude
these changes, and take appropriate actiondestroyed
: Delete all cache related things
keep-alive
before, 061c9020bdbf5b will not be rendered on the page, so the attribute ofabstract
// src/core/components/keep-alive.js
export default {
name: 'keep-alive',
abstract: true, // 判断此组件是否需要在渲染成真实DOM
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
created() {
this.cache = Object.create(null) // 创建对象来存储 缓存虚拟dom
this.keys = [] // 创建数组来存储 缓存key
},
mounted() {
// 实时监听include、exclude的变动
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
destroyed() {
for (const key in this.cache) { // 删除所有的缓存
pruneCacheEntry(this.cache, key, this.keys)
}
},
render() {
// 下面讲
}
}
pruneCacheEntry function
In the life cycle destroyed
we implemented above, we executed the operation of delete all caches, and this operation was implemented by calling
pruneCacheEntry
, let’s talk about what pruneCacheEntry
// src/core/components/keep-alive.js
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy() // 执行组件的destory钩子函数
}
cache[key] = null // 设为null
remove(keys, key) // 删除对应的元素
}
To sum up, three things have been done:
- 1. Traverse the collection and execute the
$destroy
method of all cache components - 2.
key
the content ofcache
corresponding tonull
- 3. Delete the corresponding element in
keys
render function
Hereafterinclude
white list,exclude
blacklistrender
function mainly does these things:
- Step 1: Get the first component of the
keep-alive
component name
- Step Two: whether or not this
component name if that can be
whitelist, blacklist match, if
can not be matched white list blacklist || be matched, the direct return
VNode
, does not execute down, if not met, Executestep 3 down
- Step 3: Generate the
cache key
component ID and tag, and check whether the component has been cached in the cache collection. If it has been cached, take out the cache component directly and update the
cache key in
keys
(this isLRU algorithm), if it has not been cached, continue to the fourth step of
- Step 4: Save the
component and its
cache key
cache、keys
respectively, and check whether the number exceedsmax
, and delete it according to theLRU algorithm if the number exceeds 061c9020bdc128
- Step 5:
keepAlive
property of this component instance to true. This is very important, as I will talk about below!
// src/core/components/keep-alive.js
render() {
const slot = this.$slots.default
const vnode: VNode = getFirstComponentChild(slot) // 找到第一个子组件对象
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) { // 存在组件参数
// check pattern
const name: ?string = getComponentName(componentOptions) // 组件名
const { include, exclude } = this
if ( // 条件匹配
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
const key: ?string = vnode.key == null // 定义组件的缓存key
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) { // 已经缓存过该组件
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key) // 调整key排序
} else {
cache[key] = vnode // 缓存组件对象
keys.push(key)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) { // 超过缓存数限制,将第一个删除
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
vnode.data.keepAlive = true // 渲染和执行被包裹组件的钩子函数需要用到
}
return vnode || (slot && slot[0])
}
Rendering
Let's first take a look at how a component of Vue is rendered. Let's start with render
:
render
: This function will convert the component toVNode
patch
: This function will be directly rendered during the first rendering. According to theVNode
, it will be directly rendered intoreal DOM. At the beginning of the second rendering,
VNode
will be compared with the old VNodeand patched (the diff algorithm comparison occurs at this stage) , And then rendered into
real DOM
keep-alive itself rendering
As I just said, keep-alive
own components will not be rendered on the page. How is that done? In fact, by judging abstract
on the component instance, if it is true
, the instance is skipped, and the instance will not appear on the parent chain.
// src/core/instance/lifecycle.js
export function initLifecycle (vm: Component) {
const options = vm.$options
// 找到第一个非abstract的父组件实例
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
// ...
}
Package component rendering
Let's talk about how the components wrapped keep-alive
I just said that the VNode -> real DOM occurred in the
patch
stage, but in fact this is also to be subdivided: VNode -> instantiation -> _update -> real DOM, and the judgment of the component using the cache occurs in the
instance In this stage, and this stage calls the
createComponent
, then let’s talk about this function:
// src/core/vdom/patch.js
function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
if (isDef(vnode.componentInstance)) {
initComponent(vnode, insertedVnodeQueue)
insert(parentElm, vnode.elm, refElm) // 将缓存的DOM(vnode.elm)插入父元素中
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true
}
}
}
- When you first load the wrapped components, because
keep-alive
ofrender
performed prior to assembly before the package is loaded, so at this timevnode.componentInstance
value isundefined
, andkeepAlive
istrue
, then the code wenti(vnode, false /* hydrating */)
not go down - When the package component is accessed again,
vnode.componentInstance
is the component instance that has been cached, then theinsert(parentElm, vnode.elm, refElm)
will be executed, so that the last DOM is directly inserted into the parent element.
Concluding remarks
I am Lin Sanxin, an enthusiastic front-end rookie programmer. If you are motivated, like the front-end, and want to learn the front-end, then we can make friends and fish together haha, fish school
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。