Writing in the front
Excited to see this headline? Finally, it was the brother’s turn. As we all know, Vue’s Diff algorithm is inspired by snabbdom, which comes from the Swedish word meaning speed. This shows two things: 1. The author is Swedish; 2. 2. The author is very confident about his project. Ok, now we’re going to go inside the algorithm and explore how fast it is and what improvements Vue has made. Patch/H is used to compare and generate virtual DOM in SNabbDOM, so how can we use similar writing method to achieve the effect in Vue? Of course, you can use the data in vm.options.data to update the node using a dependency trigger, but I give you a unique trick to mount elements without going reactive:
/*
<div id="app"></div>
<button id="btn">click me</button>
*/
const vm = new Vue({
el: '#app'
})
let vnode = null
const patch = vm.__patch__
const h = vm.$createElement
vnode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D')])const oldVnode = patch(vm._vnode, vnode)
document.querySelector('#btn').onclick = () = > {
vnode = h('ul', [
h('li', { key: 'E' }, 'E'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D')
])
patch(oldVnode, vnode)
}
Copy the code
Why did I think of it this way? Because that’s how snabbDOM is called. This chapter focuses on the Diff algorithm, so this will suffice. Well, from here on out, let’s get into the realm of Diff algorithms
The four hit rules
What is the Diff algorithm? The four hit rules! I don’t want to go step by step like everyone else. Write down the first formula: new old before the | | old after new old | new before after
Before and after
The first element in the children of the old node is called the old before, the last element is called the old after and the first element in the children of the new node is called the new before, and the last element is called the new after
- Set the body of the loop -> Old before <= old after && New before <= new after
- Check whether the current policy is matched by checking whether the old and new nodes are the same node according to the preceding formula.
- If hit, the pointer before x moves down and the pointer after x moves up.
Example 1 – New before old
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / new front
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'E' }, 'E'), / / new after
])
Copy the code
Judge -> new and old -> Find all li tags with key A -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'), / / the old before
h('li', { key: 'C' }, 'C'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'), / / new front
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'E' }, 'E'), / / new after
])
Copy the code
Loop -> continue to judge -> new and old -> Find all li tags with key B -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), // Old after + old before
])
const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / new front
h('li', { key: 'D' }, 'D'),
h('li', { key: 'E' }, 'E'), / / new after
])
Copy the code
Loop -> continue to judge -> new and old -> Find all li tags with key C -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / after the old
/ / the old before
])
const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / new front
h('li', { key: 'E' }, 'E'), / / new after
])
Copy the code
Cycle – > out – > after ∵ old than before By this time the new node’s children, new and two elements, between before and after the new shows that two new – > after the two appended to the old
Example 2 – new before old before + new after old after
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / new front
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Judge -> new and old -> Find all li tags with key A -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'), / / the old before
h('li', { key: 'C' }, 'C'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'C' }, 'C'), // New after + new before
])
Copy the code
Loop -> continue to judge -> new and old -> Find different -> Not matched -> new and old -> Find all li tags with key C -> Hit -> Call patchVnode move -> Old move up one -> new move up one
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'), // Old before + old after
h('li', { key: 'C' }, 'C'),])const newVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / new after
h('li', { key: 'C' }, 'C'), / / new front
])
Copy the code
Cycle – > out – > ∵ new after more than a new position before Look at this time the children of the old node, there is an element between before and after the old old, illustrate the redundant – > delete directly
Example 3 – new before old before + new after old after + new after old before
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'), / / new front
h('li', { key: 'C' }, 'C'),
h('li', { key: 'A' }, 'A'), / / new after
])
Copy the code
Judgment -> New and old -> Not the same -> Not matched judgment -> New and old -> Not the same -> Not matched judgment -> New and old -> Found li label with key A -> Hit -> Call patchVnode to move -> Old down one bit -> New up one bit -> Move the element back after old and set the original position to undefined
const oldVNode = h('ul'[undefined,
h('li', { key: 'B' }, 'B'), / / the old before
h('li', { key: 'C' }, 'C'), / / after the old
h('li', { key: 'A' }, 'A'), // -> insert
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'), / / new front
h('li', { key: 'C' }, 'C'), / / new after
h('li', { key: 'A' }, 'A'),])Copy the code
Loop -> continue to judge -> new and old -> Find all li tags with key B -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul'[undefined,
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), // Old after + old before
h('li', { key: 'A' }, 'A'), // -> insert
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), // New after + new before
h('li', { key: 'A' }, 'A'),])Copy the code
Loop -> continue to judge -> new and old -> Find all li tags with key C -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul'[undefined,
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / after the old
h('li', { key: 'A' }, 'A'), // -> insert ()
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / new after
h('li', { key: 'A' }, 'A'), / / new front
])
Copy the code
∵ ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children
Example 4 – New before old before + new after old after + new after old before + new before old after + new before old before + new before old after + new before old before + new before old after + new before old after old in the old node does not exist
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'C' }, 'C'),
h('li', { key: 'B' }, 'B'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'), / / new front
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / new after
])
Copy the code
Judgment -> New and old -> Not the same discovery -> Not matched judgment -> New and old -> Not the same discovery -> Not matched judgment -> New and old -> Not the same discovery -> Not matched judgment -> New and old -> Not the same discovery -> Not matched judgment -> New and old -> Found li with key B Tag -> Hit -> Call patchVnode move -> Old move up one bit -> New move down one bit -> Move the element to before old and set the original position to undefined
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'C' }, 'C'), / / after the old
undefined,]),const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'), / / new front
h('li', { key: 'D' }, 'D'), / / new after
])
Copy the code
Cycle -> continue to judge -> new before old before -> Discovery is not the same -> Not hit judge -> New after old after -> Discovery is not the same -> Not hit judge -> New after old before -> Discovery is not the same -> Not hit judge -> New before old before -> Discovery is not the same -> Not hit judge -> New before old after -> Discovery is both key is The li label of C -> hit -> call patchVnode move -> move the old one bit up -> move the new one bit down -> move the element to the old one bit before and set the original position to undefined
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'A' }, 'A'), // Old before + old after
undefined.undefined,]),const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), // New after + new before
])
Copy the code
Loop -> Continue to judge -> new before old before -> Discovery is not the same -> Missed judgment -> New after old after -> Discovery is not the same -> Missed judgment -> New after old before -> Discovery is not the same -> Missed judgment -> New before old after -> Discovery is not the same -> Missed judgment -> New before old after -> Discovery is not the same -> Miss judgment -> Look for elements with the same key in the old node -> No move -> Add and append the element to the old one -> move the new one down
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), // -> insert
h('li', { key: 'A' }, 'A'), // Old before + old after
undefined.undefined,]),const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / new after
/ / new front
])
Copy the code
∵ ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children
Example 5 – New before old before + New after old after + new after old before + new before old after + old before
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'), / / new front
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> Find the element with the same key in the old node -> determine the existence -> Is the element found in the old node the same element as the new one -> Yes -> call patchVnode to move -> append the element to the old one. And set the original position to undefined -> move the new front down one bit
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'), / / the old before
undefined,
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'), / / new front
h('li', { key: 'D' }, 'D'),
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Loop -> continue to judge -> new and old -> Find all li tags with key A -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
undefined./ / the old before
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'), / / new front
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Loop -> continue to judge -> new before old -> find old is undefined move -> old move down one bit
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
undefined,
h('li', { key: 'C' }, 'C'), / / the old before
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'), / / new front
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Loop -> continue to judge -> new before old before -> Find not the same -> Miss judgment -> New after old after -> Find not the same -> Miss judgment -> new after old before -> find all li tags with key C -> hit -> call PatchVnode move -> Old one bit down -> New one bit up -> Move the element to after old and set the original position to undefined
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
undefined.undefined,
h('li', { key: 'D' }, 'D'), // Old after + old before
h('li', { key: 'C' }, 'C'),])const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'), // New before + new after
h('li', { key: 'C' }, 'C'),])Copy the code
Loop -> continue to judge -> new before old -> Find all li tags with key D -> hit -> call patchVnode move -> old move up one -> new move up one
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
undefined.undefined,
h('li', { key: 'D' }, 'D'), / / after the old
h('li', { key: 'C' }, 'C'), / / the old before
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'), / / new after
h('li', { key: 'C' }, 'C'), / / new front
])
Copy the code
∵ ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children
Example 6 – New before old before old + new after old after old + new after old before + new before old after old + old before old + Old before old + Old before old + Old
const oldVNode = h('ul', [
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'), / / new front
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> New and old -> Discovery is not the same -> Not hit judgment -> Find the element with the same key in the old node -> existence judgment -> is the element found in the old node the same element as the new one -> No -> add the element move -> append the element to the old one -> move the new one down
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'), / / the old before
h('li', { key: 'B' }, 'B'),
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'), / / new front
h('li', { key: 'D' }, 'D'),
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Loop -> continue to judge -> new and old -> Find all li tags with key A -> hit -> call patchVnode move -> old move down one -> new move down one
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'B' }, 'B'), / / the old before
h('li', { key: 'C' }, 'C'),
h('li', { key: 'D' }, 'D'), / / after the old
])
const newVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'), / / new front
h('li', { key: 'C' }, 'C'), / / new after
])
Copy the code
Cycle -> continue to judge -> new before old before -> Discovery is not the same -> Not hit judge -> New after old after -> Discovery is not the same -> Not hit judge -> New after old before -> Discovery is not the same -> Not hit judge -> New before old before -> Discovery is not the same -> Not hit judge -> New before old after -> Discovery is both key is The li label of D -> hit -> Call patchVnode to move -> Move the old one bit up -> move the new one bit down -> move the element to the old one bit before and set the original position to undefined
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'B' }, 'B'), / / the old before
h('li', { key: 'C' }, 'C'), / / after the old
undefined,]),const newVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'C' }, 'C'), // New after + new before
])
Copy the code
Loop -> continue to judge -> new and old -> Find different -> Not matched -> new and old -> Find all li tags with key C -> Hit -> Call patchVnode move -> Old move up one -> new move up one
const oldVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'),
h('li', { key: 'B' }, 'B'), // Old before + old after
h('li', { key: 'C' }, 'C'),
undefined,]),const newVNode = h('ul', [
h('li', { key: 'B' }, 'Fake B'),
h('li', { key: 'A' }, 'A'),
h('li', { key: 'D' }, 'D'), / / new after
h('li', { key: 'C' }, 'C'), / / new front
])
Copy the code
∵ ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children, ∴ children
A simple memory
- When patchVnode is hit, x is moved one bit down and x is moved one bit up
- Insert the element after the old one, and set the original position to undefined
- Insert the element before the old one, and set the original position to undefined
- Find the children of the old node:
- Insert the element before the old one, set the old position to undefined, and then move the new one down
- If you find an element with the same key but different children/text, treat it as new, create a new element before the old one and move the new one down
- If no description is found, create a new element and insert before the old one, then move the new one down
- If the children of the old node are seen after the new node is traversed first, delete all the children between the old node and the old node
- If the children of the new node are seen after the old node is traversed first, all elements between the new node and the new node are created as new elements and inserted before and before the old node
doubt
I believe that through these six cases you should understand the principle of the four hit principle, if not, I suggest you read it a few times, or draw a picture to feel the pointer change yourself. Also, I’m biased to think that people don’t remember much about when they moved elements to before or after the old. We can think about it, when a child node in a new node matches the child node that is the best in the old node, doesn’t that mean that the old node has been moved to the bottom? At this time should be used after the old, the rest of the time is used before before. Of course, I also wrote a call in advance — patchVnode, don’t worry, this is the protagonist of the next section, and listen to me slowly.
patchVnode
As we saw in examples 5 and 6 above, when all four cardinal principles fail, we go into the old node tree to find children of the same key. But after finding them, we need to compare them to see if only the keys are the same, and the rest are different. This step is required here as well as in patchVnode. First we need to know that a node is either an element node or a text node. Note Only one of the children and text attributes exists in a VNode object. So let’s think briefly first. If a node changes, there are several situations:
└ ─ patchVnode ├ ─ a new node is not a text node (no text attributes) │ ├ ─ are old node element node children attribute (present) │ ├ ─ only new node is an element node children attribute (present) │ └ ─ │ ├ ─ new node (with children) ├ ─ new node (with text)Copy the code
- If the new node is a text node, set the children of the old node to undefined, and then change the text of the old node to the text of the new node
- If the new node is not a text node
- If both the old and new nodes are element nodes, the Diff algorithm is performed to perform a fine comparison
- If only the new node is an element node, clear the text property of the old node, append the children of the new node to the old node, and update the DOM tree
- If only the old node is an element node, the children of the old node have been removed. Clear the children property of the old node and update the DOM tree
patch
const modules = platformModules.concat(baseModules)
const patch = createPatchFunction({ nodeOps, modules })
Vue.prototype.__patch__ = inBrowser ? patch : noop
Copy the code
The first thing we need to realize from this code is that the _patch_ method is a method returned internally by the higher-order createPatchFunction, which means that even though we are simply calling _patch_, But it actually does a lot of work that we can’t see. Const modules = platformmodules.concat (baseModules) The goal is to merge modules provided by the Web platform with modules provided by our source code core. So what are these two modules? If you’ve ever read the Vue source code article in one sitting, you know that it’s a two-dimensional array of 19 hooks. The second two-dimensional array contains hook methods. For the sake of saving trouble, I’ll quote directly from the previous content and modify it slightly:
├─ calling the createPatchFunction method loops through modules to initialize CBS ├─ baseModules provides the following hooks: │ ├─ Directives. Js integrated for directive**create: updateDirectives / update: updateDirectives / destroy: unbindDirectives**│ ├ ─ ref.js integrated for $refs**create / update / destroy**│ ├─ Hook │ ├─ attrs.js integrated with the Virtual DOM attrs**create: updateAttrs / update: updateAttrs**│ ├─ Class.js integrated with the tag class**create: updateClass / update: updateClass**│ ├─ domProps. Js integrated with the class and :class hooks**create: updateDOMProps / update: updateDOMProps**│ ├─ Events.js integrated with on in the virtual DOM**create: updateDOMListeners / update: updateDOMListeners**│ ├─ style.js integrated staticStyle and style for the virtual DOM**create: updateStyle / update: updateStyle**│ ├ ─ trans. Js integrated with Vue official* * * *The component's**create: _enter / activate: _enter / remove**Know hook │ └ ─ modules, a total of 19 hook hook from the modules in the same property names into the same array, and then respectively in the CBS, namely:├ ─ the create: [updateAttrs, updateClass, updateDOMListeners, updateDOMProps, updateStyle, _enter, create, updateDirectives, ] ├─ Activate: [_enter,] ├─ Update: [updateAttrs, updateClass, updateDOMListeners, updateDOMProps, updateStyle, Update, updateDirectives,] ├─ Remove: [remove,] └─ imp: [destroy, unbindDirectives,]Copy the code
All right, let’s get down to business. Despite all that, this method is the entry method to the Diff algorithm.
function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {
if (isUndef(vnode)) {
if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
return
}
let isInitialPatch = false
const insertedVnodeQueue = []
if (isUndef(oldVnode)) {
isInitialPatch = true
createElm(vnode, insertedVnodeQueue, parentElm, refElm)
} else {
const isRealElement = isDef(oldVnode.nodeType)
if(! isRealElement && sameVnode(oldVnode, vnode)) { patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) }else {
if (isRealElement) {
oldVnode = emptyNodeAt(oldVnode)
}
const oldElm = oldVnode.elm
const parentElm = nodeOps.parentNode(oldElm)
createElm(
vnode,
insertedVnodeQueue,
oldElm._leaveCb ? null : parentElm,
nodeOps.nextSibling(oldElm)
)
if (isDef(vnode.parent)) {
let ancestor = vnode.parent
const patchable = isPatchable(vnode)
while (ancestor) {
for (let i = 0; i < cbs.destroy.length; ++i) {
cbs.destroy[i](ancestor)
}
ancestor.elm = vnode.elm
if (patchable) {
for (let i = 0; i < cbs.create.length; ++i) {
cbs.create[i](emptyNode, ancestor)
}
const insert = ancestor.data.hook.insert
if (insert.merged) {
for (let i = 1; i < insert.fns.length; i++) {
insert.fns[i]()
}
}
} else {
registerRef(ancestor)
}
ancestor = ancestor.parent
}
}
if (isDef(parentElm)) {
removeVnodes(parentElm, [oldVnode], 0.0)}else if (isDef(oldVnode.tag)) {
invokeDestroyHook(oldVnode)
}
}
}
invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
return vnode.elm
}
Copy the code
In this method we can see that it makes two big judgments:
- Check whether the new node exists
- Check whether the old node exists
If the new node does not exist, the hook will be destroyed. If the old node does not exist, this is an empty mount. Create a root node. If the old node exists, the old node must be judged further:
- If it is a virtual DOM and the old and new nodes are the same node, the patchVnode method is called
- Otherwise, regardless of whether it’s a virtual DOM or not, the new node is inserted directly before the old node, and the old node is removed
doubt
So what are the 19 hooks we’re talking about? Do you want to call the destroy and Remove hooks when deleting old nodes? Should I call the UPDATE hook when updating an old node? Do I call the CREATE hook when I create a new node?
Write in the last
Although I’ve written about the Diff algorithm, there are a few details that I haven’t covered, If (isDef(I = data.hook) &&isdef (I = i.pdate)) and if (isDef(I = data.hook) &&isdef (I = i.postpatch)) The data. The hook. (update | postpatch) where did you get? This is actually defined in the createComponent method that the vm._c and vm.$createElement h functions call during the generation of the virtual DOM. This part of the content is also quite a lot, but it does not affect our understanding of the Diff algorithm itself, I will write an article to share when I have the opportunity.