More articles
preface
I have not used VUE for some time, but this does not reduce my love for VUE. I would like to sort out the knowledge of VUE, check the omissions and fill the gaps, and attach the principles and codes. The length may be too long
Want to change a job, hangzhou has a small partner recommended
Vue and React
The same:
- The virtual DOM is used
- Provides responsive and componentized view components
- Keep the focus on the core library and leave other functions such as routing and global state management to the relevant libraries
- Provide lifecycle hook functions that developers can customize as needed
Differences:
Vue
The essence is the MVVM framework,React
Is a front-end componentization frameworkVue
Canonize templates, but support themJSX
Grammar,React
supportJSX
grammarVue
Provides instructions, filters, etc., for easy operation,React
There is noVue
Supports bidirectional data bindingVue
Each component’s dependencies are tracked without the need to re-render the entire component tree.React
All child components are rerendered when the state of the application is changed. throughshouldComponentUpdate
This lifecycle approach can be controlled, butVue
Consider this the default optimization- The CSS scope is in
React
Is implemented through csS-in-JS scheme,Vue
Through the scoped Vue
A neutron component can pass messages to its parent in two ways: events and callback functions,Vue
Prefer to use events. inReact
We all use callback functions in the
What is MVVM? Different from MVC?
- MVC
MVC is model-view-Controller. Model is the data Model, which is responsible for accessing data in the database. View is the View, which is what you see on the page. So it was left to the Controller to synchronize the Model and View. In the days of simple data, the Controller had no pressure to parse the data, but as the data structure became more and more complex, the Controller became very bloated. Then came the MVVM, Leave the parsing of the data to the VM
- MVVM
MVVM is the abbreviation of Model-view-view Modle, which was first proposed by Microsoft
Model represents the data layer, View represents the View layer, is responsible for displaying data, ViewModle is responsible for synchronizing the relationship between Model and View, the core of the synchronous association is DOMListeners and DataBindings tools. DOMListeners are used to monitor DOM changes in the View and selectively upload them to the Model. The DataBindings tool is used to listen for Model data changes and update them to the View
The design idea of MVVM is to get rid of the complicated DOM. Instead of how to manipulate DOM, developers have changed to how to update the JS object state
For more details, see the difference between MVC and MVVM
The life cycle
- Create a stage
- BeforeCreate(before component creation)
After instance initialization, $EL and data are undefined before data Observer and Watch/Event configuration are invoked
- Created(after the component is Created)
Data observation and event configuration are complete. The data object is accessible, but not yet accessible, and ref is undefined. It is used for data initialization, Ajax, etc
- Mount the stage
- BeforeMount(Before mounting components)
This function is called before the component is mounted, when the HTML template has been compiled, the virtual DOM exists, $EL is available, but ref is still unavailable
- Mounted(After components are Mounted)
The component is mounted and the data is rendered. DOM is accessible, and this phase is performed only once
- Update the stage
- BeforeUpdate(BeforeUpdate)
Called before data update and virtual DOM patching, the existing DOM can be accessed at this stage
- Updated
Call after data update and virtual DOM patch
- Unloading phase
- BeforeDestory(before uninstallation)
Called before instance destruction, which can be obtained through this and ref still exists, this phase is usually used to clear timers and bound listening events
- Destroyed(after uninstallation)
The instance is destroyed, all instructions are unbound, and all event listeners are removed
- Keep-alive hook function
- activated
Called when activated by a keep-alive cached component
- deactivate
Called when a component cached by keep-alive is disabled.
Child and parent components communicate
- Parent component -> Child component
- props
- provide/inject
// provide inject dependencies into all descendant components
// Provide and inject binding are not responsive
// If you pass in a listening object, the object's property is still responsive
// parent
export default {
data() {
return {
obj: {
a: 1
},
sex: "Female"}},provide(){
return {
obj: this.obj, / / obj response
sex: this.sex // sex is not responding}}}// child
export default {
inject: ['obj'.'sex']}Copy the code
- $attrs
// All parent components except class and style are accepted as attribute bindings that are not recognized as prop
// parent
<template>
<Child :sex="' woman '" :name="' Lao wang '" :style="{}" />
</template>
// Child
export default {
created() {
console.log(this.$attrs) // {sex: "female"}}}Copy the code
- $listeners
// Contains a v-ON event listener in the parent scope (without the.native modifier)
// parent
<template>
<Child1 @click1="click1" @click2="click2" @click3="click3" />
</template>
// child1
<template>
<Child2 v-on="$listeners" />// Pass down events bound to Child1</template>
export default {
created() {
console.log(this.$listeners) // { click1: f, click2: f, click3: f }
},
methods: {
click1() {
this.$emit('click1) } } } // child2 export default { created() { console.log(this.$listeners) // { click1: f, click2: f, click3: f } }, methods: { click2() { this.$emit("click2") }, click3() { this.$emit("click3") } } }Copy the code
- Child component -> Parent component
- The callback function
- $emit
- Brother components
- State is promoted to the parent component
- general
- vuex
- Bus Event bus
// main.js
Vue.prototype.$bus = new Vue()
/ / a
this.$bus.$emit('test'.Awesome!)
/ / receive
this.$bus.$on('test'.(e) = > console.log(e)) / / 666
Copy the code
Principle of bidirectional binding
Vue adopts the data hijacking + subscriber publisher model. Data hijacking involves adding get and set methods to each attribute of component data via Object.defineProperty, which triggers listener callbacks in the set to notify subscribers when data changes
Step 1: Recursively traverse the data, adding getters and setters for the property via Object.defineProperty
Observer.prototype = {
walk: function(data) {
var self = this;
Object.keys(data).forEach(function(key) {
self.defineReactive(data, key, data[key]);
});
},
defineReactive: function(data, key, val) {
var dep = new Dep();
var childObj = observe(val); // Object recursion
Object.defineProperty(data, key, {
enumerable: true.configurable: true.get: function() {},set: function(newVal) {}}); }}function observe(value, vm) {
if(! value ||typeofvalue ! = ='object') return
return new Observer(value);
};
Copy the code
Step 2: As an observer (collecting dependencies, publishing messages), the DEP registers the function when the getter is called and notifies the registered function when the setter is called
Object.defineProperty(data, key, {
get: function() {
if (Dep.target) {
dep.addSub(Dep.target); // Register functions to collect dependencies
}
return val;
},
set: function(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify(); // Trigger update to publish messages}}); Dep.prototype = {addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update(); // Watcher provides the update method}); }};Copy the code
Step 3: Compile parses the template instructions and replaces the variables with data. Then initialize render page view and update function and listen function on node binding corresponding to each instruction. The page is then updated whenever the data changes. When the page changes, the change information will be published accordingly
function Compile(el, vm) {
this.vm = vm;
this.el = document.querySelector(el);
this.fragment = null;
this.init();
}
Compile.prototype = {
init: function () {
if (this.el) {
this.fragment = this.nodeToFragment(this.el);
this.compileElement(this.fragment);
this.el.appendChild(this.fragment);
} else {
console.log('Dom element does not exist '); }},nodeToFragment: function (el) {
var fragment = document.createDocumentFragment();
var child = el.firstChild;
while (child) {
// Move the Dom element into the fragment
fragment.appendChild(child);
child = el.firstChild
}
return fragment;
},
compileElement: function (el) {
var childNodes = el.childNodes;
var self = this;
[].slice.call(childNodes).forEach(function(node) {
var reg = / \ {\ {(. *) \} \} /;
var text = node.textContent;
if (self.isElementNode(node)) {
/ / the node
self.compile(node);
} else if (self.isTextNode(node) && reg.test(text)) {
// Parse the template if it is an instruction of the form {{}}
self.compileText(node, reg.exec(text)[1]);
}
if (node.childNodes && node.childNodes.length) {
// Recursive child nodeself.compileElement(node); }}); },compile: function(node) {
var nodeAttrs = node.attributes;
var self = this;
Array.prototype.forEach.call(nodeAttrs, function(attr) {
var attrName = attr.name;
if (self.isDirective(attrName)) {
var exp = attr.value;
var dir = attrName.substring(2);
if (self.isEventDirective(dir)) {
// Event command
self.compileEvent(node, self.vm, exp, dir);
} else {
/ / v - model instructionself.compileModel(node, self.vm, exp, dir); } node.removeAttribute(attrName); }}); },compileText: function(node, exp) {
var self = this;
var initText = this.vm[exp];
// Initializes the initialized data into the view
this.updateText(node, initText);
// Generate the subscriber and bind the update function, which is triggered by updaue in Watcher
new Watcher(this.vm, exp, function (value) {
self.updateText(node, value);
});
},
compileEvent: function (node, vm, exp, dir) {
var eventType = dir.split(':') [1];
var cb = vm.methods && vm.methods[exp];
if (eventType && cb) {
node.addEventListener(eventType, cb.bind(vm), false); }},compileModel: function (node, vm, exp, dir) {
var self = this;
var val = this.vm[exp];
this.modelUpdater(node, val);
new Watcher(this.vm, exp, function (value) {
self.modelUpdater(node, value);
});
// Listen for input events, view changes update data
node.addEventListener('input'.function(e) {
var newValue = e.target.value;
if (val === newValue) {
return;
}
self.vm[exp] = newValue;
val = newValue;
});
},
updateText: function (node, value) {
node.textContent = typeof value == 'undefined' ? ' ' : value;
},
modelUpdater: function(node, value, oldValue) {
node.value = typeof value == 'undefined' ? ' ' : value;
},
isDirective: function(attr) {
return attr.indexOf('v-') = =0;
},
isEventDirective: function(dir) {
return dir.indexOf('on:') = = =0;
},
isElementNode: function (node) {
return node.nodeType == 1;
},
isTextNode: function(node) {
return node.nodeType == 3; }}Copy the code
Step 4: The Watcher subscriber acts as a bridge between the Observer and Compile. When instantiated, the Watcher subscriber adds itself to the DEP and provides an update method that triggers the corresponding function in Compile to complete the update when the data changes
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
this.value = this.get(); // Add yourself to the subscriber DEp
}
Watcher.prototype = {
update: function() {
this.run();
},
run: function() {
var value = this.vm.data[this.exp];
var oldVal = this.value;
if(value ! == oldVal) {this.value = value;
this.cb.call(this.vm, value, oldVal); // Triggers the corresponding update view function in Compile}},get: function() {
Dep.target = this; // Cache yourself
var value = this.vm.data[this.exp] // enforce get function in listener
Dep.target = null; // Release yourself
returnvalue; }};Copy the code
Differences in computed and watch
- Computed:
- Calculate attribute
- Support caching, only when the dependent data changes will be recalculated
- Asynchronous operations are not supported
- Automatically listens for changes in dependency values to dynamically return content
Usually a value is Computed depending on multiple other values. Computed by default has a getter, and setters need to be written manually, as follows:
computed: {
num: {
get() {
return this.num1 + this.num2
},
set(e) {
console.log('do somethings')}}}Copy the code
- Watch
- Listening to the
- Cache not supported
- Asynchronous operations can be performed
- Listening is a procedure that triggers a callback when the listening value changes
Watch sets immediate to true if it needs to start immediately after the component is loaded, and deep to true if it needs to listen for deep attributes
It is important to note that once deep is added, all attributes will be listened on, which is very performance expensive. Therefore, it is usually optimized to only listen on attributes that need to be listened on, such as obj. Not all operations that add deep will be listened for, such as object property obj.newAttr = 1 and array arr[0] = 1. Only those that are responsive can be listened for, such as push and pop
watch: {
obj: {
handler: function(newVal, oldVal) {
console.log(newVal, oldVal)
},
immediate: true.deep: true}}Copy the code
Asynchronous queue, $nextTick
When data changes in the Vue, the DOM is not updated immediately and is usually tested using a for loop to avoid unnecessary calculations and DOM manipulation.
Each Watcher has a unique ID. When the same Watcher is triggered several times, it will determine whether it exists according to the ID to avoid repeated push into the queue. This is optimization made by Vue. So let’s take a look at the implementation of nextTick in Vue. Now that we know about nextTick, asynchronous queue updates are pretty much done
FlushCallbacks, execute all tasks
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
// For compatibility, the use order is Promise->MutationObserver->setImmediate->setTimeout
When all synchronization tasks in flushCallbacks are completed, the asynchronous task is executed
// timerFunc
let timerFunc;
if (typeof Promise! = ="undefined" && isNative(Promise)) {
const p = Promise.resolve();
timerFunc = () = > {
p.then(flushCallbacks);
if (isIOS) setTimeout(noop);
};
isUsingMicroTask = true;
} else if (
!isIE &&
typeofMutationObserver ! = ="undefined" &&
(isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() ===
"[object MutationObserverConstructor]")) {let counter = 1;
const observer = new MutationObserver(flushCallbacks);
const textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = () = > {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
isUsingMicroTask = true;
} else if (
typeofsetImmediate ! = ="undefined" &&
isNative(setImmediate)
) {
timerFunc = () = > {
setImmediate(flushCallbacks);
};
} else {
timerFunc = () = > {
setTimeout(flushCallbacks, 0);
};
}
// nextTick
export function nextTick (cb? :Function, ctx? :Object) {
let _resolve
The collection task is waiting to be executed
callbacks.push(() = > {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')}}else if (_resolve) {
_resolve(ctx)
}
})
if(! pending) {// Prevent subsequent nextTick from repeating timerFunc
pending = true
// Execute the task
timerFunc()
}
}
Copy the code
Ok, next up is the asynchronous task force
/ / update to update
update () {
// Judgment of various circumstances...
else {
queueWatcher(this) // this is the current instance watcher}}// queueWatcher
const queue = []
let has = {}
let waiting = false
let flushing = false
let index = 0
export function queueWatcher (watcher: Watcher) {
const id = watcher.id
// Check whether the ID exists
if (has[id] == null) {
has[id] = true
if(! flushing) { queue.push(watcher) }else {
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i--
}
queue.splice(i + 1.0, watcher)
}
if(! waiting) { waiting =true
FlushSchedulerQueue is added to the callbacks in nextTick when the nextTick task is executed
nextTick(flushSchedulerQueue)
}
}
}
// flushSchedulerQueue
function flushSchedulerQueue () {
currentFlushTimestamp = getNow()
flushing = true
let watcher, id
queue.sort((a, b) = > a.id - b.id)
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
if (watcher.before) {
watcher.before()
}
id = watcher.id
has[id] = null
watcher.run() // Perform the update task}}Copy the code
As you can see, not only do we use nextTick on a daily basis, but Vue also uses its Api. To understand this you need to have some understanding of the JS implementation mechanism
The role of key in V-for
Why use key? There is no unique identifier to track whether a DOM is newly created or in a different position. The immortal is not likely to know about these changes. In other words, it gives the DOM an identity because the same tags in the DOM are: Div, P, etc. They look like clones, you can’t identify, why give an identity? In fact, it is still for optimization, optimization is the DIff algorithm, adding key and not adding key to calculate the patch is completely different, I take the example of the previous article to take a look (see Virtual Dom and diff algorithm for more information) :
const oldData = createElement('ul', { key: 'ul'},// Label ul: 0
createElement('li', {},'aaa']), // li: 1 aaa: 2
createElement('li', {},'bbb']), // li: 3 bbb: 4
createElement('li', {},'ccc']) // li: 5 ccc: 6
])
const newData = createElement('ul', { key: 'ul' }, [
createElement('li', {},'aaa']),
createElement('li', {},'aaa']),
createElement('li', {},'bbb']),
createElement('li', {},'ccc']])const patches = diff(oldData, newData)
console.log('patches----------', patches)
Copy the code
According to the patches results, we can see the changes of corresponding identification nodes. Let’s look at another set of data
const oldData = createElement('ul', { key: 'ul' }, [
createElement('li', { key: 1},'aaa']),
createElement('li', { key: 2},'bbb']),
createElement('li', { key: 3},'ccc']])const newData = createElement('ul', { key: 'ul' }, [
createElement('li', { key: 1},'aaa']),
createElement('li', { key: 4},'aaa']),
createElement('li', { key: 2},'bbb']),
createElement('li', { key: 3},'ccc']])const patches = diff(oldData, newData)
console.log('patches----------', patches)
Copy the code
It can be seen that the patch without key is indeed a lot of complex, daily complex operations and do not know how many times more complex
Another problem is that the index index is avoided in the for loop, because a change in one element can cause all subsequent DOM representations to change, making the patch more complex and the update less efficient
V-if and V-show differences
Optimization is everywhere, so distinguish between V-if and V-show for optimization
- v-if
True conditional rendering, destruction or reconstruction of the DOM, cost performance, and event listeners and subcomponents within conditional blocks are properly destroyed and reconstructed during switching
In addition, V-for has a higher priority than V-IF. Using v-FOR also causes performance problems, which are usually solved by computed or V-show
- v-show
Simple CSS switching (display), low cost, frequent switching is commonly used
Data in Vue components must be functions
In fact, this is a basic problem of JS, because the same object is reused the same address will affect each other, that is, a component is referenced for many times this data will be messed up, so use the function to return a new object
Vue initialization page flash problem
<! -- css -->
[v-cloak] { display: none; }
<! -- html -->
<div v-cloak>
{{ message }}
</div>
Copy the code
Vue and React routing modes
Front-end routes are hash routes and history routes, as well as vue-router and react-router
- Hash routing
Hash routes have hash urls that change page content by listening for hashchange events
<a href="#/page1">page1</a>
<a href="#/page2">page2</a>
<div id="app"></div>
Copy the code
const app = document.getElementById('app');
window.addEventListener('onload',hashChange() )
window.addEventListener('hashchange'.() = > hashChange())
function hashChange() {
switch (window.location.hash) {
case '#/page1':
app.innerHTML = 'page1'
return
case '#/page2':
app.innerHTML = 'page2'
return
default:
app.innerHTML = 'page1'
return}}Copy the code
- history
History adds pushState and replaceState (pushState pushes the incoming URL onto the history stack, and replaceState replaces the incoming URL with the current history stack). They provide the ability to modify the history. It’s just that when they make changes that change the current URL, the browser doesn’t immediately send requests to the back end
<a href="/page1">page1</a>
<a href="/page2">page2</a>
<div id="app"></div>
Copy the code
window.addEventListener('DOMContentLoaded', Load)
window.addEventListener('popstate', PopChange)
var routeView = null
function Load() {
routeView = document.getElementById('app')
// The popState callback is executed once by default
PopChange()
var aList = document.querySelectorAll('a[href]')
aList.forEach(aNode= > aNode.addEventListener('click'.function (e) {
e.preventDefault()
var href = aNode.getAttribute('href')
history.pushState(null.' ', href)
// PopState does not listen for changes in the address bar, so you need to manually execute the PopChange callback function
PopChange()
}))
}
function PopChange() {
switch (window.location.pathname) {
case '/page1':
routeView.innerHTML = 'page1'
return
case '/page2':
routeView.innerHTML = 'page2'
return
default:
routeView.innerHTML = 'page1'
return}}Copy the code
The history mode needs to be configured in the background. If the URL cannot match static resources, the same index.html is returned. Take nginx as an example:
location / {
try_files $uri $uri/ /index.html;
}
Copy the code
The background configuration
Vue-router Specifies the route hook function
The hook function of the route is usually called the guard of the route. The guard is the gatekeeper, who wants to let you in or not. If you meet the criteria, you will be let in. For authentication, there are many types of “gates”, such as the main gate, the small door, etc., so routing hooks are also differentiated:
- Global hooks
This is the gatekeeper of the main gate. If you can’t get through here, you won’t be able to go anywhere
// Common hook: check before jump
router.beforeEach((to, from, next) = > {
// ...
})
Copy the code
- Exclusive to hook
There were always special people in the palace, and he was special, so he had his own porters.
const router = new VueRouter({
routes: [{path: '/foo'.component: Foo,
beforeEnter: (to, from, next) = >{}}]})Copy the code
- Component hook
Each room is not so special, but there will be a door, want to come in also depends on whether there is a “doorkeeper”, “doorkeeper” mood
export default {
data() {},
beforeRouteEnter(to, from, next) {}, // You don't know who the owner is, so this is undefined at this stage
beforeRouteLeave (to, from, next) {}
}
Copy the code
How does Vue dynamically route
- Dynamic routes provided by router
Both Vue and React provide dynamic matching in /: ID mode, which is usually used to match pages of the same structure
- Dynamic permission routing
Dynamic routing is usually required for permission control, and there are two approaches:
-
The front end has a full amount of routing data, and then processes the full amount of routing data according to the permission data of the request background. The unqualified data will be filtered out and not displayed in the menu bar or other places on the page. When the page jumps, the route guard will judge whether there is permission or not
-
Front-end routes are divided into static and dynamic routes. Static routes, such as login page and 404, can be directly written into the routes, while dynamic routes are provided by the back-end and added to the front-end routes through router.addRoutes. There are two ways to deal with this problem:
// Use window.location.href
window.location.href = '/login'
// 2. Clear the routing information before adding routes
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const createRouter = () = > new Router({
mode: 'history'.routes: []})const router = createRouter()
export function resetRouter () {
const newRouter = createRouter()
router.matcher = newRouter.matcher
}
export default router
// Call resetRouter before calling addRoutes
import {resetRouter} from '@/router'
resetRouter()
router.addRoutes(routes)
Copy the code
keep-alive
A common example of caching inactive component instances rather than destroying them is when tabs are switched back and forth and the TAB state is not initialized
// include: the value can be a string or a regular
// Cache components named A and B, other components are not cached
<keep-alive include="a,b">
<component :is="view"></component>
</keep-alive>
// Exclude has the same value as include
// Max Maximum number of cache components
/ / meta
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
Copy the code
Keep-alive itself is also a component. It is a component of Vue’s internal implementation, but it will not be rendered. Keep-alive has an attribute {abstract: True}, abstract will be ignored when it is true. During the first rendering, the keep-alive component will directly return vNode if it does not meet the condition. The component that meets the condition will cache vNode through the object cache. A cached vnode componentInstance is overlaid on top of the current vnode
// keep-alive.js
export default {
name: 'keep-alive'.abstract: true.props: {
include: patternTypes,
exclude: patternTypes,
max: [String.Number]
},
created () {
this.cache = Object.create(null) // Store the cache
this.keys = []
},
destroyed () {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys) // Clear the cache through pruneCacheEntry}},// The cache is updated using pruneCache
mounted () {
this.$watch('include'.val= > {
pruneCache(this.name= > matches(val, name))
})
this.$watch('exclude'.val= > {
pruneCache(this.name= >! matches(val, name)) }) }, render () {const slot = this.$slots.default // Get the default slot
const vnode: VNode = getFirstComponentChild(slot) // Get the first element
constcomponentOptions: ? VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {
// check pattern
constname: ? string = getComponentName(componentOptions)const { include, exclude } = this
if (
// not included(include && (! name || ! matches(include, name))) ||// excluded
(exclude && name && matches(exclude, name))
) {
return vnode // Return vnode if the cache is not hit
}
const { cache, keys } = this
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
if (cache[key]) { // The cache already exists
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else { // Not cached
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])}}// pruneCache
function pruneCache (cache: VNodeCache, current: VNode, filter: Function) {
for (const key in cache) {
constcachedNode: ? VNode = cache[key]if (cachedNode) {
constname: ? string = getComponentName(cachedNode.componentOptions)// Clear the cache if there is no match
if(name && ! filter(name)) {if(cachedNode ! == current) { pruneCacheEntry(cachedNode) } cache[key] =null}}}}// pruneCacheEntry
function pruneCacheEntry (vnode: ? VNode) {
if (vnode) {
vnode.componentInstance.$destroy()
}
}
Copy the code
The first render will be like a normal render, but subsequent renders will insert the cached DOM into the parent element
// The patch process executes createComponent
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 */)}// Render vnode.componentInstance === cache[key]. ComponentInstance === cache[key]
if (isDef(vnode.componentInstance)) {
initComponent(vnode, insertedVnodeQueue)
insert(parentElm, vnode.elm, refElm) // This inserts the cached DOM (vnode.elm) into the parent element
if (isTrue(isReactivated)) {
reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
}
return true}}}Copy the code
conclusion
Continuously updated… (Feels like some modules can be pulled out for further study)