Writing an article is not easy, click a like brother focus on Vue source code sharing, the article is divided into vernacular version and source version, vernacular version to help understand the working principle, source version to help understand internal details, let us study together based on Vue version [2.5.17]

If you think the layout is ugly, please click the link below or pull to the following public account can also be

Vue principle Diff – source code version of the related auxiliary functions

Before we begin our main Diff, let’s look at some of the helper functions that will be used

I could have put it in the Diff, but it all adds up to too much

And these functions are more common, just pull them out and use them

So plan an independent article, first warm up, the content is not much, also very simple, light will also help our thinking


Node operation function

Here are some of the functions that Diff uses to update the DOM when comparing nodes

There would have been more, but I’ve combined some of them to make it easier to see

Three functions are described below

Insert, createElm, createChildren

A simple introduction

Insert is used to insert a node into a location

CreateElm creates a DOM node

CreateChildren also creates DOM nodes, but handles an array and creates DOM nodes and text nodes

Let’s take a closer look at these three methods

1 insert

This function is used to insert nodes

But there are two cases of insertion

1. Insert the end of the child node of the parent node directly without reference to sibling nodes

2. If there is a reference sibling node, insert it in front of the sibling node

This function is the cornerstone of Diff

function insert(parent, elm, ref) {    

    if (parent) {        

        if (ref) {            

            if(ref.parentNode === parent) { parent.insertBefore(elm, ref); }}else{ parent.appendChild(elm); }}}Copy the code

2 createElm

This function, as its name suggests, creates a node

After the node is created, insert is called to insert the node

You can have a look. It’s not hard

function createElm(vnode, parentElm, refElm) {  



    var children = vnode.children;    

    var tag = vnode.tag;    

    if(tag) {vnode.elm = document.createElement(tag) // Insert child node into vnode.elm, Then insert vnode. Elm into parent createChildren(vnode, children); Insert (parentElm, vnode.elm, refElm); }else{ vnode.elm = document.createTextNode(vnode.text); insert(parentElm, vnode.elm, refElm); }}Copy the code

CreateElm requires Vnode to determine which node to create

1. Text nodes

2. Common nodes

1 Text node

When vNode. tag is undefined, create a text node and look at the actual text vnode

Also, text nodes have no children

2 Common Node

If the vnode.tag has a value, create the corresponding DOM

But the DOM may have child nodes, so child nodes, and even descendants, are created

So a createChildren is called to complete the creation of all descendant nodes

3 createChildren

This method deals with the children, one by one, in a traversal recursive fashion

1 If the child nodes are arrays, execute createElm one by one

2 If the text attribute of the child node has data, it indicates that the vnode is a text node. Create a text node and insert it into the parent node

function createChildren(vnode, children) {    



    if (Array.isArray(children)) {      



        for(var i = 0; i < children.length; ++i) { createElm(children[i], vnode.elm, null); }}else if (        

        typeof vnode.text=== 'string' ||

        typeof vnode.text=== 'number' ||
        typeof vnode.text=== 'boolean'
    ) {
        vnode.elm.appendChild(

            document.createTextNode(vnode.text)

        )

    }
}
Copy the code

Service Diff utility functions

The following functions are specifically used by Vue to service Diff, two of which are described

CreateKeyToOldIdx, sameVnode

1createKeyToOldIdx

Receive a children array and generate a map table with key and index

function createKeyToOldIdx(

    children, beginIdx, endIdx

) {    



    var i, key;    

    var map = {};    



    for (i = beginIdx; i <= endIdx; ++i) {

        key = children[i].key;        

        if(key) { map[key] = i; }}return map

}
Copy the code

Let’s say your old array of child nodes is

[{    
    tag:"div",  key: "key_1"

},{  

    tag:"strong", key:"key_2"

},{  

    tag:"span",  key:"key_4"

}]
Copy the code

CreateKeyToOldIdx generates a map oldKeyToIdx, like this

{
    "key_1": 0."key_2": 1,"key_4"2} :Copy the code

Use the key of the vnode as the attribute name and the location of the vnode at children as the attribute value

The function in Diff is

Determine if a new vnode is in the old vNode array and get its location. Get the key of the new Vnode, and then go to the map table to match the corresponding node, if there is, return the node location

Such as

Now I have a new child array, an old child array

I get a newVnode in the new child array, and I want to determine if it’s the same as a vnode in the old child array

How to judge?? Newvnode. key==vnode.key??

Vue takes a smarter approach, using the old Vnode array to generate a map object, obj

When obj[newvNode. key] is present, the node is present in both the old and new child node arrays

And I can get the position of the node in the old child node array (property value)

Otherwise, it does not exist

This approach also gives us a solution to a similar scenario in a project, replacing array traversal with object index lookup

I hope you keep that in mind

2 sameVnode

This function also plays a very important role in the Diff, so keep that in mind

Its purpose is to determine whether two nodes are the same

Here said the same, not exactly the same, but the same key attributes, you can first look at the source code

function sameVnode(a, b) {    



    return( a.key === b.key && a.tag === b.tag && !! a.data === !! b.data && sameInputType(a, b) ) }function sameInputType(a, b) {    



    if(a.tag ! = ='input') return true

    var i;    

    var types = [

        'text'.'number'.'password'.'search'.'email'.'tel'.'url'

    ]    



    var typeA = (i = a.data) && (i = i.attrs) && i.type;    

    var typeB = (i = b.data) && (i = i.attrs) && i.type; // Input is of the same type, or both of the basic input typereturn (
        typeA === typeB ||
        types.indexOf(typeA)>-1 &&

        types.indexOf(typeB)>-1

    )
}
Copy the code

The judgment is mainly based on three points: key, tag, and whether there is data

The nodes judged here are only relative to the nodes themselves, excluding children

In other words, even if data and children are different, the two nodes may still be the same

Like these two

One special case is the input node

Input requires an additional check on whether the type of the two nodes is the same

or

The types of the two nodes can be different, but they must belong to those input types

That’s it for sameVnode, but I can’t help but wonder

Why does sameVnode judge this?

The following is purely personal fantasy ideas, only for reference

SameVnode is used in Diff to determine whether a node needs to be created

When two old and new vNodes are sameVnode false, the two vNodes are different and a DOM is inserted

That is, two nodes are created when they are fundamentally different

There is no doubt that the unique identifier key is compared to the tag name tag to see if the VNode is the same

But there’s no need to judge whether the data is the same or not, so I didn’t get it at first

Data contains attributes in the DOM, so it doesn’t matter if data is different

Because even if they’re different, they’re still based on the same DOM

Since the value of DOM attribute may be dynamically bound and dynamically updated, the corresponding data of the two VNodes before and after the change must be different, but in fact they are the same Vnode, so data is not in the judgment category

But data must be defined in both old and new nodes, or not at all

There is no definition, and an undefined Vnode will be the same

For example, this one down here will have data

There’s no data in this one

They must not belong to the same node in the template


conclusion

The functions involved fall into two main categories

Insert, createElm, and createChildren are specifically responsible for manipulating the DOM

This type of function is more general and can be used even in our own projects

One class is createKeyToOldIdx, sameVnode, for special service Diff

It will include some project solutions

Keep these functions in mind, as they will appear frequently in the source code of the next article

I’m not going to talk about it


The last

In view of my limited ability, there will inevitably be omissions and mistakes, please forgive me, if there is any improper description, welcome to contact me, thanks