Write in front: This article is from the Vue source code parsing series

Handwriting h functions: look at ts code, write JS code

1. See the TS code

First, find snabbdom in node_modules. SRC contains ts code and build contains JS code. Find h.JSCopy the code

Finally, the vnode function returns something. Let’s look at vNode.ts

So the function h returns an object

Now, if you look up, because in the previous note, the children attribute is the child element, and the child element can be nested:

So:

Call vnode() layer by layer

On the way up, the whole block is overloading functions (one function has multiple uses, different arguments for different cases).

Possible overloading of the h function:

When you rewrite, you only write three cases (not so many overloads)

The h() function of the third argument in the third case may also cover the first and second cases

Write your logic

Export default function(sel,data,c){ The second must be the attribute object / / h ({} 'div', 'text') / / h (' div ', {}, []) / / h (' div ', {}, h ()) / / that is to say, // Debugger if(arguments.length! =3){throw new Error(' sorry, h must pass three arguments, We are with low version of the function of h ')} the if (typeof c = = 'string' | | typeof c = = 'number') {/ / h function is form 1 return vnode (sel, data, undefined, c, undefined); }else if(array.isarray (c)){// let children = []; for(let i = 0; i<c.length; I ++){// check that c[I] must be an object if(! (typeof c[I] =='object' && c[I].hasOwnProperty('sel'))) Throw new Error(' passin 'an array argument that is not an h function '); Children.push (c[I])} // The loop is over and the children are collected. It has children attribute \ return vnode (sel, data, and the children, undefined, undefined) / / we made a simplified version, \}else if(typeof c == 'object' && c. hasownProperty ('sel')){// Form 3 // Let children = [c] let children = [c] let children = [c]; Return vnode (sel, data, children, undefined, undefined)} else {throw new Error (" the incoming parameters is wrong ")}}Copy the code

So far, I’ve written the function h with three different inputs

Next, the Diff algorithm is studied

Consider a scenario where the page originally had the following layout: (Scenario 1)



After the single button button, the page changes to (Scenario 2)



As we said before, the way we think about it is:

1. Use the h function to get the virtual node

2. Use patch function to make the obtained virtual node go up the tree

Let’s start with scenario 1:

1. Get the container first

2. Obtain the virtual node of scenario 1

const vnode1 = h('ul', {}, [

h('li', {}, 'A'),

h('li', {}, 'B'),

h('li', {}, 'C'),

h('li', {}, 'D'),

])
Copy the code

3. Set Vnode1 to the tree

patch(container, vnode1)
Copy the code

4. Obtain the virtual node of scenario 2

const vnode2 = h('ul', {}, [

h('li', {}, 'A'),

h('li', {}, 'B'),

h('li', {}, 'C'),

h('li', {}, 'D'),

h('li', {}, 'E'),

])
Copy the code

5. Write the button

<button id=" BTN "> press I change dom</button>Copy the code

6. Write a click event for the button button

Btn.onclick = function () {patch(vnode1, vnode2)//vnode2 replaced vnode1 (This is a problem to be solved when learning the underlying logic of patch)}Copy the code

We’ll get what we want. It should be noted that, as mentioned above, the patch function can only have one container at a time. In this case, the container has not changed, but the contents in the container have changed. How do we change the contents of the container, in this case using the diff algorithm, which means that the ABCD of scene 1 and scene 2 are the same, except that E is appented to scene 2

How to prove?

Here’s the simplest way to prove it:

Before clicking the button, we make a simple change in the browser (teacher Shao Shan Huan called the ugly monster experiment method… Ha, ha, ha)

Now click the button:

The page is not re-rendered, it is the minimum amount of update based on scene 1. The ABCD of scene 1 is retained, and the E! Of scene 2 is updated on this basis.

Note, however, that suppose I change scenario 1 to scenario 1 and change scenario 2 to the following:

const vnode2 = h('ul', {}, [

h('li', {}, 'E'),

h('li', {}, 'A'),

h('li', {}, 'B'),

h('li', {}, 'C'),

h('li', {}, 'D'),

])
Copy the code

At this point, you will find that the entire ABCDE has been rerendered. The original A becomes E of scene 2, and B of scene 1 becomes A of scene 2… Add a key to the virtual node. Let’s change scenario 1 to the following code:

const vnode1 = h('ul', {}, [

h('li', {key:'A'}, 'A'),

h('li', {key:'B'}, 'B'),

h('li', {key:'C'}, 'C'),

h('li', {key:'D'}, 'D'),

])
Copy the code

Change scenario 2 to the following code:

const vnode2 = 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'),

])
Copy the code

(Note that E is still in front of ABCD.) Test again

Click the button

Bullfrog bullfrog bullfrog bullfrog !!!!!!!! Instant smart (small key big function) minimum amount of update absolutely children

Note 1: Key (unique identifier) serves the minimum number of updates (diff), very very very very critical ~!

Let’s move on to the next: Scene 1:\

const vnode1 = h('ul', {}, [

h('li', {key:'A'}, 'A'),

h('li', {key:'B'}, 'B'),

h('li', {key:'C'}, 'C'),

h('li', {key:'D'}, 'D'),

])
Copy the code

Scenario 2:

const vnode2 = h('ol', {}, [

h('li', {key:'A'}, 'A'),

h('li', {key:'B'}, 'B'),

h('li', {key:'C'}, 'C'),

h('li', {key:'E'}, 'D'),

])
Copy the code

Note that we changed ul to OL in scenario 1, or we found that the ABCD node is not the same as the original ABCD node (even if we bound the key value to the node), because the parent ul and OL of the ABCD nodes are different, so we think they are not the same node.

Note 2: Only under the premise of the same virtual node, can be refined comparison, otherwise it is violent delete old, insert new.

Extension question: How to define the same virtual node? Answer: What does it mean that the selectors are the same and the key values are the same? It means that in our example above, there is no way to diff. Because even if you set the same key value for both scenarios, the selector will change from ul to OL, so you can’t update the minimum amount. This means that if you change the parent, the child will be rerendered entirely

Experience 3 only for the same layer comparison, not for cross-layer comparison. Even if it’s the same piece of virtual node, but across the layer, sorry, refinement is less diff you. Instead, they forcibly delete the old one and insert the new one.

What does this mean? Our scene 1: \

const vnode1 = h('div', {key:'ul-ol'}, [

h('p', {key:'A'}, 'A'),

h('p', {key:'B'}, 'B'),

h('p', {key:'C'}, 'C'),

h('p', {key:'D'}, 'D'),

])
Copy the code

Our scenario 2:

const vnode2 = h('div', {key:'ul-ol'}, h('section',{},[

h('p', {key:'E'}, 'E'),

h('p', {key:'A'}, 'A'),

h('p', {key:'B'}, 'B'),

h('p', {key:'C'}, 'C'),

h('p', {key:'D'}, 'D'),

]))
Copy the code

In other words, on the basis of the original node, a layer of section label is added, and ABCD node is wrapped with the section node. ABCD is not the same as ABCD and on the same layer of nodes, whether you are ABCD or BADC, it can be easily compared. For example, scenario 1:

const vnode1 = h('ul', {key:'ul-ol'}, [

h('li', {key:'A'}, 'A'),

h('li', {key:'B'}, 'B'),

h('li', {key:'C'}, 'C'),

h('li', {key:'D'}, 'D'),

])
Copy the code

Scenario 2:

const vnode2 = h('ul', {key:'ul-ol'}, [

h('li', {key:'E'}, 'E'),

h('li', {key:'D'}, 'D'),

h('li', {key:'C'}, 'C'),

h('li', {key:'B'}, 'B'),

h('li', {key:'A'}, 'A'),

])
Copy the code

After the ugly monster experiment, it can be found that even if the order has changed, the node is actually the same node, which has not changed before clicking the button:

After clicking the button:

So diff isn’t as smart as it should be, but will it affect efficiency? This kind of operation is rarely encountered in the actual VUE development, so it is a reasonable optimization mechanism.

Finally, let’s say the interviewer asks you, you want to talk about the Diff algorithm, you could say something like this

1. Virtual nodes

Diff comparisons can only be made on the same virtual node. What is the same virtual node: the same selector and the same key

3. Diff only compares at the same layer, does not compare across layers (even if there is the same key value). At the same layer, the minimum amount of updates is the least !!!! Efficiency is the highest!! (As long as it is the same layer, even if the order changes, but only the order of the same node before and after the change, it will not be violent to delete the old and write the new, is the same before and after)