Writing in the front
Because of the vue. js is very interested in, and the usual work of the technology stack vue. js, these months spent some time to study the vue. js source code, and do a summary and output.
Original address of the article: github.com/answershuto… .
In the process of learning, I added Chinese annotations for Vue github.com/answershuto… And Vuex’s notes github.com/answershuto… , I hope to be helpful to other people who want to learn source code.
There may be some misunderstanding, welcome to point out, learn together, make progress together.
keep-alive
Keep-alive is a built-in component of vue.js. It can keep inactive component instances in memory rather than destroying them directly, and it is an abstract component that is not rendered into the real DOM and does not appear in the parent component chain.
It provides include and exclude attributes that allow components to cache conditionally.
For details, please refer to the official website.
use
usage
<keep-alive>
<component></component>
</keep-alive>Copy the code
The component component here is cached.
Take a chestnut
<keep-alive>
<coma v-if="test"></coma>
<comb v-else="test"></comb>
</keep-alive>
<button @click="test=handleClick">Please click on</button>Copy the code
export default {
data () {
return {
test: true}},methods: {
handleClick () {
this.test = !this.test; }}}Copy the code
When button is clicked, coma and Comb will switch, but the state of the two components will be cached. For example, coma and Comb both have an input label, so the content in the input label will not disappear because of the switch of the components.
props
The keep-alive component provides include and exclude attributes to allow the component to cache conditionally, both of which can be represented as comma-separated strings, regular expressions, or an array.
<keep-alive include="a">
<component></component>
</keep-alive>Copy the code
The component with name A will be cached.
<keep-alive exclude="a">
<component></component>
</keep-alive>Copy the code
Components whose name is a will not be cached.
Life is a hook
Keep-alive provides two life hooks, activated and deactivated.
Keep-alive stores the component in memory and does not destroy or recreate it. Therefore, the created method of the component is not called again. Instead, the activated and deactivated hooks are used to determine whether the component is active.
In-depth implementation of the Keep-alive component
The keep-alive component is used to cache the cache of the keep-alive component.
Created and Destroyed hooks
The Created hook creates a cache object that can be used as a cache container to hold vNodes.
created () {
/* Cache object */
this.cache = Object.create(null)},Copy the code
A Destroyed hook removes all component instances from the cache when the component is destroyed.
/* Destroyed all component instances in cache */
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache[key])
}
},Copy the code
render
Next comes the render function.
render () {
/* Gets the first component in the slot slot */
const vnode: VNode = getFirstComponentChild(this.$slots.default)
constcomponentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {
// check pattern
/* Get the component name, the component's name field first, otherwise the component's tag */
constname: ? string = getComponentName(componentOptions)/* Name is not in inlcude or is returned directly from vnode (no cache is taken) */ from exlude
if (name && (
(this.include && ! matches(this.include, name)) ||
(this.exclude && matches(this.exclude, name))
)) {
return vnode
}
constkey: ? string = vnode.key ==null
// same constructor may get registered as different local components
// so cid alone is not enough (#3269)
? componentOptions.Ctor.cid + (componentOptions.tag ? ` : :${componentOptions.tag}` : ' ')
: vnode.key
/* Get the component instance from the cache to vNode if it has already been cached, or cache it if it has not already been cached */
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}
/* keepAlive flag bit */
vnode.data.keepAlive = true
}
return vnode
}Copy the code
The first child component is obtained by getFirstComponentChild, and the name of the component is used (if a component name exists, otherwise a tag is used). The name is then matched with the include and exclude attributes. If the match fails (indicating that no caching is required), vnode is returned without any operation. Vnode is an object of type VNode. For those of you who are not familiar with VNode, please refer to my article “VNode Nodes”.
/* Check if name matches */
function matches (pattern: string | RegExp, name: string) :boolean {
if (typeof pattern === 'string') {
/* Strings, such as a,b,c */
return pattern.split(', ').indexOf(name) > - 1
} else if (isRegExp(pattern)) {
/ * regular * /
return pattern.test(name)
}
/* istanbul ignore next */
return false
}Copy the code
It is a simple function to detect a match between include and exclude attributes, which support comma-separated component names such as “a,b,c” and regular expressions. Matches checks for matches to the current component in both ways, respectively.
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}Copy the code
The next thing to do is simply to look up the key in this.cache and overlay the cached VNode componentInstance onto the current VNode. Otherwise, vNodes are stored in the cache.
Finally, return the VNode whose componentInstance has been replaced in the cache.
watch
Use watch to monitor the change of pruneCache and pruneCache, and modify the cached data in the cache when the property changes.
watch: {
/* Monitor include and exclude to modify the cache when it is modified */
include (val: string | RegExp) {
pruneCache(this.cache, this._vnode, name => matches(val, name))
},
exclude (val: string | RegExp) {
pruneCache(this.cache, this._vnode, name => ! matches(val, name)) } },Copy the code
So let’s look at an implementation of pruneCache.
/* Fix cache */
function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
for (const key in cache) {
/* Fetch vnode */ from cache
constcachedNode: ? VNode = cache[key]if (cachedNode) {
constname: ? string = getComponentName(cachedNode.componentOptions)/* If name does not match filter criteria and is not the currently rendered Vnode, the component instance (Vue instance) corresponding to vnode is destroyed and */ is removed from cache
if(name && ! filter(name)) {if(cachedNode ! == current) { pruneCacheEntry(cachedNode) } cache[key] =null}}}}/* Destroy the corresponding vnode component instance (Vue instance) */
function pruneCacheEntry (vnode: ? VNode) {
if (vnode) {
vnode.componentInstance.$destroy()
}
}Copy the code
All entries in the cache are iterated. If the rules specified by filter are not met, pruneCacheEntry is executed. PruneCacheEntry calls the component instance’s $destroy method to destroy the component.
The last
Vue. Js internally abstracts DOM nodes into VNode nodes, and the cache of keep-Alive components is also based on VNode nodes rather than directly storing DOM structures. The pruneCache and pruneCache components are cached in the cache object, and vNodes are removed from the cache and rendered when needed.
about
Author: Ran Mo
Email: [email protected] or [email protected]
Github: github.com/answershuto
Zhihu: www.zhihu.com/people/cao-…
Please indicate the source of reprint, thank you.
Welcome to follow my public number