Initialization of vue data rendering (vue source code analysis 1)

The following code and analysis process need to be combined with Vue JS source code view, through the break point one by one comparison.

I code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="vue.js"></script>
</head>
<body>
    <div id="app">
        <!--this is comment--> {{ message }}
    </div>
    <div id="app1">
        <div>
            <!--count={{count}}-->
            <!--reversedCount={{reversedCount}}-->
        </div>
    </div>

    <script>
        debugger;
        var app = new Vue({
            el: '#app',
            beforeCreate() {},
            created() {},
            beforeMount() {},
            mounted: () => { //Mount the element and get the DOM node
                setTimeout(() => {
                    //this.count+=1;
                    //console.log('this.count='+this.count)
                }, 1000)
            },
            beforeUpdate() {},
            updated() {},//Mount the element and get the DOM node
            beforeDestroy() {},
            destroyed() {},
            data: function () {
                return {
                    count: 0,
                    message: 'Hello Vue!1111111'
                }
            },
        })
        })
    </script>

</body>

</html>

II Perform steps

1. initMixin, initialize vue, Mount_ init method

Execute initmixin (Vue). Vue is a constructor.

Mount one on the Vue prototype_ init method, this step is only to mount the method, and it is not executed.

2. stateMixin, data binding, $watch method

2-1. Execute stateMixin(Vue) Vue is a constructor.

Mount one on the Vue prototype_ init method. This step is only to mount the method and does not execute it.

Define a data object and a props object, and add get and set method attributes to the two objects respectively

2-2. Then execute object Defineproperty, which monitors data changes by setting the set/get method of object properties,

Dependency collection is carried out through get, and each set method is an observer to notify the subscriber to update the view when the data changes.

 Object.defineProperty(Vue.prototype, '$data', dataDef); 
 Object.defineProperty(Vue.prototype, '$props', propsDef);

Vue There are only newly mounted on prototype_ init method

At this time, dataDef has only get and set methods

Object. After the defineproperty is executed, look again at this time. At this time, the get, set methods and $data and $props properties have been mounted on Vue.

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-yjiwpdye-1625476714253)( https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f75a7bcac78409d825f623f48687aa3 ~tplv-k3u1fbpfcp-watermark. image)]

2-3 then execute

  Vue.prototype.$set = set;
  Vue.prototype.$delete = del;

Later, we'll look at the definitions of set and del methods.

2-4 then mount $watch

We will also analyze this method later. ok, now let's see which methods Vue mounts:

stateMixin execution completed

3 eventsMixin, initialize the event binding method

3-1. Mount $on, $once, $off, $emit and method attributes in turn

These methods are only mounted, and we will analyze them later

Vue mounts these properties:

4 lifecycleMixin, initialize the update destroy function

4-1. Mount in sequence_ update, $forceUpdate, $destroy, method properties

These methods are only mounted, and we will analyze them later

  • _ update in [14. Definition instruction section]

Vue mounts these properties:

5 renderMixin, initialize vue the function to be rendered

5-1. First execute installRenderHelpers

//Mount rendering related tool functions on Vue's prototype
installRenderHelpers(Vue.prototype)

The input parameter of the function is the prototype of vue, as shown in the figure:

After the installRenderHelpers function is executed, the Vue prototype has more render tool method attributes

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-pp8go0g1-1625476714260)( https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/21a8f9af8ddd42cebdb297638bf527b1 ~tplv-k3u1fbpfcp-watermark. image)]

5-1. Then mount $nextTick and_ render

  • nextTick deferred callback
  • _ Render render function

6 declare some variables

  • patternTypes / / type array
  • KeepAlive / / defines the properties of built-in components in the form of components
  • builtInComponents / / built in components with saved status

7 initglobal api (Vue), which initializes the global api and exposes some static methods

7-1 mount basic properties

Mount a get attribute to the configuration object configDef, which is a function that returns the previously defined config object. At this time, it is only mounted and not executed, so there is only one get method.

Then mount a set method and prompt some warnings.

Then execute

/**
 * Here, add an object to be passed through the constructor of Vue Defineproperty listens to the property config,
 * Associate the set and get methods in configDef to config.
 * When you get the config object described above, if you directly change the config object,
 * You will be prompted "do not replace the vue.config object, but set a single field". Note:,
 * vue We don't want to directly replace and change the entire config object. If necessary, we want to directly modify the value we need to modify
*/
Object.defineProperty(Vue, 'config', configDef);
  • Then mount util, set, delete and nextTick methods on the Vue constructor and mount an empty options.

  • Then traverse the instruction set in Vue Options mount the corresponding command and set it_ The base attribute value is Vue

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ejncvdec-1625476714261)( https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f33c90dd98384ca18f0d96638d0527fd ~tplv-k3u1fbpfcp-watermark. image)]

Merge KeepAlive components into components

extend(Vue.options.components, builtInComponents)

7-2 initUse

Initialize vue install plug-in function

Mount a use method on Vue.

7-3 initMixin$1

initMixin$1(Vue); // Initialize vue mixin function

Mount a mixin method on Vue to overwrite the passed in mixin (passed in during execution) on the options item.

Mount a use method on Vue.

7-4 initExtend

initExtend / / initializes the vue extend function

Mount an extend method on Vue,

7-5 initAssetRegisters

initAssetRegisters / / add static methods component, direct and filter to vue

This completes the execution of the initGlobalAPI method.

8. Object.defineProperty some properties of the response

//Monitor whether it is a server environment
    Object.defineProperty(Vue.prototype, '$isServer', {
        get: isServerRendering //Determine whether it is a node server environment
    });
    // Get $ssrContext
    Object.defineProperty(Vue.prototype, '$ssrContext', {
        get: function get() {
            return this.$vnode && this.$vnode.ssrContext
        }
    });

    //Create virtual dom vonde rendering slots for the public FunctionalRenderContext for the ssr runtime helper installation
    Object.defineProperty(Vue, 'FunctionalRenderContext', {
        value: FunctionalRenderContext
    });

Observe Vue prototype:

9. Set version number

Vue.version = '2.5.16'; //Version number

10. Define judgment attribute correlation function

// The following is a function returned through the makeMap function to find out whether there is a val in the map. If there is a val, it returns true, otherwise it returns false
isReservedAttr // Judge 'style,class'
acceptValue    // Judge 'input,textarea,option,select,progress'
mustUseProp    // Determine whether the attribute and label match
isEnumeratedAttr    // Judge 'contenteditable, draggable, spellcheck'
isBooleanAttr  // Check whether it is a Boolean attribute in html (the attribute values are only true and false)
isXlink        // Determine whether it is an xmlns attribute
getXlinkProp   // Get properties of xml link
isFalsyAttrValue   // Determine whether val is null or false
var namespaceMap = {
     svg: 'http://www.w3.org/2000/svg', //svg tag naming xmlns attribute
      math: 'http://www.w3. xmlns attribute declaration XHTML file in org / 1998 / math / MathML '/ / math
};
isFalsyAttrValue   // Determine whether val is the original tag in html
isSVG              // Determine svg tag and svg sub element tag
isPreTag           // Determine whether the tag is pre
isReservedTag      // Determine whether it is a native html tag or svg tag
getTagNamespace    // Determine whether it is an svg or math tag
unknownElementCache    // Determine whether it is an svg or math tag
isTextInputType    // Determine whether it is a text input box. Determine the attributes: text,number,password,search,email,tel,url

11. nodeOps freeze node

Object. The freeze () method can freeze an object.

  • A frozen object can no longer be modified;
  • If an object is frozen, you cannot add new properties to the object
  • You cannot delete an existing property
  • Enumerability, configurability and Writeability of existing properties of the object cannot be modified
  • And the value of an existing attribute cannot be modified.
  • In addition, after freezing an object, the prototype of the object cannot be modified.
  • Free () returns the same object as the passed in parameter.
var nodeOps = Object.freeze({
        createElement: createElement$1, //Create a real dom
        createElementNS: createElementNS, //Create a real dom svg way
        createTextNode: createTextNode, // Create text node
        createComment: createComment,  // Create an annotation node
        insertBefore: insertBefore,  //Insert node inserts a node in front of XXX dom
        removeChild: removeChild,   //Delete child node
        appendChild: appendChild,  //Add child node tail
        parentNode: parentNode,  //Get parent-child node dom
        nextSibling: nextSibling,     //Get the next sibling node
        tagName: tagName,   //Get dom tag name
        setTextContent: setTextContent, //  //Set dom text
        setStyleScope: setStyleScope  //Set the scope of the component style
    });

Next, analyze each item of the object one by one

11-1 createElement$1

Create a real dom

function createElement$1(tagName, vnode) {
        //Create a real dom
        var elm = document.createElement(tagName);
        //If it is not a select tag, it returns dom and exits the function
        if (tagName !== 'select') { 
            return elm
        }
        // false or null will remove the attribute but undefined will not
        // false or null will delete the property, but undefined will not
        // Otherwise, if it is the select tag, judge whether the multiple attribute is set. Reset if set
        if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {
            elm.setAttribute('multiple', 'multiple');
        }
        return elm
    }

11-2 createElementNS

Create a real dom svg mode, document The createelementns method creates an element node with the specified namespace.

    //Create a real dom svg way
    function createElementNS(namespace, tagName) {
        /**  namespaceMap,The previously defined value is{
             svg: 'http://www.w3.org/2000/svg',
             math: 'http://www.w3.org/1998/Math/MathML'
            };
            For example: document createElementNS(' http://www.w3.org/2000/svg ','svg');
        */ 
        return document.createElementNS(namespaceMap[namespace], tagName)
    }

document.createElementNS usage:

var c=document.createElementNS('http://www.w3.org/2000/svg','svg') / / create an SVG node
document.body.appendChild(c);

You can see that a pair of svg tags are inserted into the dom

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-w7e7sgbm-1625476714264)( https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4bf1af7ba63f432e97f6c889c79c6490 ~tplv-k3u1fbpfcp-watermark. image)]

11-3 createTextNode

Create text node

function createTextNode(text) {
   return document.createTextNode(text)
}

11-4 createComment

Create annotation node

function createTextNode(text) {
   return document.createTextNode(text)
}

document. Createstatement usage:

var c=document.createComment("My personal comments"); // Create note
document.body.appendChild(c); //Insert node

You can see that a comment has been inserted into the dom

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-hexm7pm9-1625476714265)( https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6a52060cfaf84f078ff37695824617aa ~tplv-k3u1fbpfcp-watermark. image)]

11-5 insertBefore

Insert a new node before a child node of the parent node

function insertBefore(parentNode, newNode, referenceNode) {
        parentNode.insertBefore(newNode, referenceNode);
}

insertBefore usage:

/**
node.insertBefore(newnode,existingnode) Method can insert a new child node before an existing child node.
node: The inserted parent node.
newnode: must. Node object to insert
existingnode must. The child node before the new node is to be added.
For example:
*/
<ul id="List">
    <li>Shanghai</li>
    <li>Shenzhen</li>
</ul>
var newCity = document.createElement("li")  //Create element
var textnode = document.createTextNode("Beijing") // Create element
newCity.appendChild(textnode)  // Add text
var list = document.getElementById("List")
// Add a newCity node before the first node li of the parent node ul
list.insertBefore(newCity,list.childNodes[0]);

After execution:
<ul id="List">
    <li>Beijing</li>
    <li>Shanghai</li>
    <li>Shenzhen</li>
</ul>

11-6 other node operations

The rest are relatively simple. Let's talk about them together

    //Delete child node
    function removeChild(node, child) {
        node.removeChild(child);
    }

    //Add child node tail
    function appendChild(node, child) {
        node.appendChild(child);
    }

    //Get parent-child node dom
    function parentNode(node) {
        return node.parentNode
    }

    //Get the next sibling node
    function nextSibling(node) {
        return node.nextSibling
    }

    //Get dom tag name
    function tagName(node) {
        return node.tagName
    }

    //Set dom text
    function setTextContent(node, text) {
        node.textContent = text;
    }


    //Set the scope of the component style. After setting, it will only take effect in the current component
    function setStyleScope(node, scopeId) {
        node.setAttribute(scopeId, '');
    }

That's all for nodeOps freezing nodes

12. Define ref create, update and destroy events

var ref = {
        create: function create(_, vnode) {
            //Create and register a ref
            registerRef(vnode);
        },
        update: function update(oldVnode, vnode) {
            //Update ref
            if (oldVnode.data.ref !== vnode.data.ref) {

                registerRef(oldVnode, true); //Delete first
                registerRef(vnode);  //Add in
            }
        },
        destroy: function destroy(vnode) {
            registerRef(vnode, true); //Delete destroy ref
        }
    }

12-1. create

ref is used to register reference information for elements or subcomponents. The reference information will be registered on the $refs object of the parent component. If it is used on an ordinary DOM element, the reference points to the DOM element; If used on a child component, the reference points to the component instance.

/**
     * Register refs or delete refs. For example, ref='abc 'is set on the label 
     * Then the function is this$ refs. ABC registers ref to save the real dom
     */
    function registerRef(
        vnode, // Virtual dom object
        isRemoval // Destroy ref
    ) {
        debugger
        var key = vnode.data.ref;  //Gets the string of vnode ref
        if (!isDef(key)) {  //If there is no definition, exit the function. You can see the following analysis
            return
        }
        var vm = vnode.context; //Context context
        //Get the component instance of vonde first (for components), or EL (the DOM node corresponding to the Vnode, for non components)
        var ref = vnode.componentInstance || vnode.elm; 
        var refs = vm.$refs;
        // Destroy ref s if necessary
        if (isRemoval) {
            if (Array.isArray(refs[key])) {
                remove(refs[key], ref); 
            } else if (refs[key] === ref) { 
                refs[key] = undefined;  
            }
        } else {
            if (vnode.data.refInFor) {  //When it is within v-for, it is saved as an array
                if (!Array.isArray(refs[key])) { //refs[key] becomes an array if it is not an array
                    refs[key] = [ref];
                } else if (refs[key].indexOf(ref) < 0) { //If ref does not exist, refs is added
                    // $flow-disable-line
                    refs[key].push(ref);
                }
            } else {
                refs[key] = ref; //If it is a single direct assignment
            }
        }
    }

isDef:

//Judge whether the data is defined (note! The value of 0 or false is also defined)
function isDef(v) {
    return v !== undefined && v !== null
}

12-2. update

update: function update(oldVnode, vnode) {
            //If the old and new refs are inconsistent, perform the operation of updating the ref
            if (oldVnode.data.ref !== vnode.data.ref) {
                registerRef(oldVnode, true); //Delete first
                registerRef(vnode);  //Add in
            }
        },

12-3. destroy

destroy: function destroy(vnode) {
            //Delete destroy ref
            registerRef(vnode, true); 
        }

13. Define empty vnode(emptyNode) and hooks

Let's first look at the properties of vnode:

// Define some hooks related to the declaration cycle
var hooks = ['create', 'activate', 'update', 'remove', 'destroy'];

14. Definition instruction

var directives = {
        create: updateDirectives, //Create instruction
        update: updateDirectives,  //Update instruction
        destroy: function unbindDirectives(vnode) {  //Destruction instruction
            updateDirectives(vnode, emptyNode);
        }
    }

These three attributes actually use the same function, updateDirectives

//Update instruction
    function updateDirectives(
        oldVnode, //oldVnode old data
        vnode     //vnode new data 
        ) {
        // Update as long as the old and new instructions exist
        if (oldVnode.data.directives || vnode.data.directives) {
            _update(oldVnode, vnode);
        }
    }

Then analyze_ update function. This function is relatively long. Let's analyze it in detail

  /**
     * The update instruction compares oldVnode and vnode according to the situation of oldVnode and vnode 
     * Trigger instruction hook functions bind, update, inserted, insert, componentUpdated, unbind hook functions
     */
    function _update(
        oldVnode, //oldVnode old data
        vnode     //vnode new data 
    ) {
        // If the old node is an empty node, it means that the current operation is to create it for the first time
        var isCreate = oldVnode === emptyNode;
        //If the new node is empty, it means that the current operation is destroy
        var isDestroy = vnode === emptyNode;
        //Normalized instruction, which is the instruction data that is modified into the specification for the instruction attribute. Return instruction data set
        // See the following analysis for this method
        var oldDirs = normalizeDirectives$1(
            oldVnode.data.directives, //Collection of vonde instruction objects
            oldVnode.context //vm vne instantiates an object, or an object instantiated by a component
        );
        //The normalized instruction is the instruction data that becomes the specification for the instruction attribute correction. Return instruction data set
        var newDirs = normalizeDirectives$1(
            vnode.data.directives, //Collection of vonde instruction objects
            vnode.context //vm vne instantiates an object, or an object instantiated by a component
        );

        var dirsWithInsert = []; // List of instructions that trigger the inserted instruction hook function.
        var dirsWithPostpatch = []; // List of instructions that trigger the componentUpdated hook function.

        var key, oldDir, dir;
        for (key in newDirs) {      //Loop new instruction set
            oldDir = oldDirs[key];  //Gets the old single instruction value
            dir = newDirs[key];     //Gets a new single instruction value

            if (!oldDir) { //Add an instruction to trigger bind
                // new directive, bind
                callHook$1(  //Example: bind function of v-focus instruction
                    dir, //New instruction value
                    'bind', //Trigger bind hook function
                    vnode,//New vonde
                    oldVnode //Old vonde
                );
                if (dir.def && dir.def.inserted) {
                    // If instrumented, add to the list
                    dirsWithInsert.push(dir); 
                }
            } else {
                // Instruction already exists to trigger update
                // existing directive, update
                // If there is an instruction < div v-hello ='123 '> < / div > value = 123 If 123 is updated, it is the updated value
                dir.oldValue = oldDir.value; 
                callHook$1(
                    dir,
                    'update',  //Trigger update hook function
                    vnode,
                    oldVnode
                );
                 // If there is an update instruction, add it to the list
                if (dir.def && dir.def.componentUpdated) { 
                    dirsWithPostpatch.push(dir);
                }
            }
        }

        // At this point, the bind and update hook functions are completed, and the list update is completed

        if (dirsWithInsert.length) {
            // Define a function that will execute each function in dirsWithInsert
            var callInsert = function () {
                for (var i = 0; i < dirsWithInsert.length; i++) {
                    callHook$1(
                        dirsWithInsert[i], //The new instruction value is the dir (new single instruction value) above
                        'inserted', //Trigger inserted hook function
                        vnode, //New vonde
                        oldVnode //Old vonde
                    );
                }
            };
            if (isCreate) {
                //If it is initialization  
                mergeVNodeHook(
                    vnode,
                    'insert',//Merge hook function
                    callInsert
                );
            } else {
                callInsert();
            }
        }

        if (dirsWithPostpatch.length) {
            mergeVNodeHook(vnode,
                'postpatch',
                function () {
                    for (var i = 0; i < dirsWithPostpatch.length; i++) {
                        callHook$1(
                            dirsWithPostpatch[i],
                            'componentUpdated',
                            vnode, oldVnode);
                    }
                });
        }

        if (!isCreate) {
            for (key in oldDirs) {
                if (!newDirs[key]) { //There are no instructions in the new vonde
                    // no longer present, unbind no longer exists, untie
                    callHook$1(
                        oldDirs[key],
                        'unbind', //Trigger unbind hook
                        oldVnode,
                        oldVnode,
                    );
                }
            }
        }
    }

Then analyze normalizeDirectives , which modifies the instruction attribute to become the standard instruction data and returns the instruction data set

function normalizeDirectives$1(
        dirs, //vonde instruction set
        vm //vm vne instantiates an object, or an object instantiated by a component
    ) {
        //Create an empty object
        var res = Object.create(null);
        //If the instruction name dirs does not exist, an empty object is returned
        if (!dirs) {
            // $flow-disable-line
            return res
        }

        var i, dir;
        for (i = 0; i < dirs.length; i++) { //Loop through instruction set
            dir = dirs[i];
            if (!dir.modifiers) { //Determine whether there is a modifier
                // $flow-disable-line
                dir.modifiers = emptyModifiers; //Empty object
            }
            //Return instruction name or attribute name + modifier
            res[getRawDirName(dir)] = dir;
            /**
            *Mount a custom instruction attribute to the current instruction, which is customized by the user, such as 
            *bind,inserted,update,componentUpdated,unbind these
            */
            dir.def = resolveAsset(vm.$options, 'directives', dir.name, true);
        }
        // $flow-disable-line
        return res
    }

Next, look at the getRawDirName function, which returns the instruction name or attribute name + modifier

    function getRawDirName(dir) {
        //Instructions in the rawName view, such as < div v-hello > < / div > are v-hello
        //The instructions in the name view, such as < div v-hello > < / div > are hello
        //If the instruction in the name view has a modifier < div v-hello Native > < / div > is hello native
        //modifiers modifier
        return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.')))
    }

At this time, res[getRawDirName(dir)] = dir, the instruction name has been taken as the attribute of res, and the instruction has been taken as the attribute value.

Then, check whether the instruction is on the component object, and return the registered instruction or the built object

    function resolveAsset(
        options, //Parameter example: VM$ options
        type, // Type examples: 'directives',' filters', 'components'
        id,   // Instruction, key attribute of component, for example: dir name
        warnMissing //Enable warning message example: true
    ) {
        /* istanbul ignore if  If the id is not a string, exit the function */
        // Return logic [1]
        if (typeof id !== 'string') {
            return
        }
        var assets = options[type]; // Example: VM$ options['components']
        // check local registration variations first
        /**
         * First, check the change of local registration to determine whether the ID (name of component, etc.) is the self owned attribute of assets
         * Otherwise, judge whether the key after the id hump is the self owned attribute of assets
         * Otherwise, judge whether the key whose id hump is capitalized is the self owned attribute of assets
         */
        // Example: judge whether the v-modal instruction is in options['directives']
        // Example: judge whether the my header component is in options['components']
        /**
         * Therefore, when we introduce a component into Vue, we can write the component label in the template in a hump way,
         * It can also be capitalized, or the component name can be directly used as the label of the component, because such extension processing is done here
         */

        // Execute return logic [2]
        if (hasOwn(assets, id)) {
            return assets[id]
        }

        //  You can turn such an attribute v-model into vModel and hump
        var camelizedId = camelize(id);
        // Execute return logic [3]
        if (hasOwn(assets, camelizedId)) {
            return assets[camelizedId]
        }

        // Change the initial letter to uppercase, that is, VModel to VModel
        var PascalCaseId = capitalize(camelizedId);
        // Execute return logic [4]
        if (hasOwn(assets, PascalCaseId)) {
            return assets[PascalCaseId]
        }

        // fallback to prototype chain
        var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];
        // Warning if none of the above is true and it is a development environment
        if ("development" !== 'production' && warnMissing && !res) {
            warn(
                'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
                options
            );
        }
        //Return the registered instruction or the built object (on the prototype)
        return res
    }

Here is the tool function hasOwn:

  /**
     * Check whether the object has the property.
     *Check whether the object property is on itself or on the prototype, and return true on itself
     */
    var hasOwnProperty = Object.prototype.hasOwnProperty;

    function hasOwn(obj, key) {
        return hasOwnProperty.call(obj, key)
    }

Then change our instruction name into the hump, camelize:

/**
     Convert the horizontal line to hump writing
     This regularity can make such an attribute v-model into a vModel
     Change the name format of "XX XX" to "xxxxx", where the current props attribute value and a string are received
     */
    var camelizeRE = /-(\w)/g;
    var camelize = cached(function (str) {
        return str.replace(camelizeRE, function (_, c) {
            /**
             * var str="Hello World!"
               str.toUpperCase() //HELLO WORLD! Convert lowercase to uppercase
             */
            return c ? c.toUpperCase() : '';
        })
    });

Next, look at the cached function:

// It encapsulates some functions we need to call into an object, and fetches the object when necessary
function cached(fn) {
        var cache = Object.create(null); //This creates an empty object without a prototype 
        return (function cachedFn(str) {
            var hit = cache[str];
            return hit || (cache[str] = fn(str))
        })
    }

In order to view the camelize function more directly, we will introduce cached and return to the previous camelize re function. Is it much clearer:

var camelizeRE = /-(\w)/g;
    var camelize = function () {
        var cache = Object.create(null); //This creates an empty object without a prototype 
        // This str is the key of some attributes, such as id and v-modal
        // In other words, it will first cache the object cache to determine whether it exists. If there is a function corresponding to the id attribute, it will return, and if there is no function, it will be set
        return (function cachedFn(str) {
            var hit = cache[str];
            return hit
            || (cache[str] =  str.replace(camelizeRE, function (_, c) { //The latter one is the cached input parameter fn
                return c ? c.toUpperCase() : '';
            })(str))
        })
        
    };

ok, at this time, we have analyzed the camelize function, and now resolveAsset has analyzed it and got the instruction set. normalizeDirectives is also executed, and the instruction attribute correction becomes the standard instruction data

Then back to our_ The update function, the old and new instruction sets, we have obtained:

At this time, we traverse the old and new instruction sets, which involves several tool functions, which we analyze in turn

_update->callHook$1

    //Trigger instruction hook function
    function callHook$1(
        dir,  //New instruction value
        hook, //Hook function example: bind
        vnode, //New vnode
        oldVnode, //Old vnode
        isDestroy  // Destroy example: true
    ) {
        var fn = dir.def && dir.def[hook]; //Get the hook function above the property
        if (fn) {
            try {
                fn(
                    vnode.elm, //Real dom
                    dir, //New instruction value
                    vnode, //New vond
                    oldVnode, //Old vonde
                    isDestroy //Do you want to destroy the tag
                );
            } catch (e) {
                handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook"));
            }
        }
    }

_ Update - > mergevnodehook, merge Vue vnodehook functions

 /*
     *  The hook function is used to save the insert as a hooks attribute to the data of the corresponding Vnode,
        When the Vnode is inserted into the parent node, the hooks will be called
     *  def[hookKey] = invoker; //Store the hook function as an object
     * */

function mergeVNodeHook(
        def,  // vnode
        hookKey,  // Function instruction example: 'insert'
        hook // Callback function example: callInsert()
        ) {
        // Reset it to vnode data. Hook, if vnode data. Hook does not exist
        // Then it is initialized to an empty object. Note: ordinary node vnode data. Hook doesn't exist.
        if (def instanceof VNode) {
            def = def.data.hook || (def.data.hook = {});
        }

        var invoker;
        //Get old oldHook hook
        var oldHook = def[hookKey];

        function wrappedHook() {
            //Callback, execute hook function
            hook.apply(this, arguments);
            // important: remove merged hook to ensure it's called only once
            // and prevent memory leak
            // Important: remove the merge hook to ensure that it is called only once
            // And prevent memory leaks
            // This function is not parsed. It is too simple. Delete the invoker wrappedHook entry in FNS array
            remove(invoker.fns, wrappedHook);
        }

        //If the old hook function is not empty, create a hook function
        if (isUndef(oldHook)) { 
            // no existing hook
            invoker = createFnInvoker([wrappedHook]);
        } else {
            // istanbul ignore if  
            // If there is an old hook function and the fns hook function exists and has been merged
            if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {
                // already a merged invoker
                invoker = oldHook; //Directly overwrite the new hook function with the old hook function
                //Add a function for the fns of the hook function
                invoker.fns.push(wrappedHook);
            } else {
                // existing plain hook
                invoker = createFnInvoker([oldHook, wrappedHook]);
            }
        }

        invoker.merged = true;
        //Store the hook function as an object
        def[hookKey] = invoker;
    }

_ Update - > mergevnodehook - > createfninvoker, create a hook function

    // If the event is just a function, add one more static class for the event, invoker fns = fns;  Put the real event in FNS.
    // invoker escapes fns and then runs fns
    function createFnInvoker(
        fns // The passed in parameter may be an array / function, and the array item is a function. For example: invoker = createFnInvoker([wrappedHook]);
        ) {
        // First look at the following assignment, and then look at the function definition
        function invoker() {
            var arguments$1 = arguments;

            //The function passed in by the static method is assigned to fns
            var fns = invoker.fns;

            //Determine whether fns is an array
            if (Array.isArray(fns)) {
                //If it is a shallow copy of an array
                var cloned = fns.slice();
                //Execute the functions in the fns array and pass the invoker arguments $1 parameter to the fns function one by one
                for (var i = 0; i < cloned.length; i++) {

                    cloned[i].apply(null, arguments$1);
                }
            } else {
                // return handler return value for single handlers
                //If fns is just a function, execute the arguments  parameter and pass it to fns function one by one
                return fns.apply(null, arguments)
            }
        }
        // Mount the incoming function array to the fns attribute of the invoker
        invoker.fns = fns;
        return invoker  //Static class
    }

Back to_ Update function, we have analyzed this function, which implements the update of old and new node instructions and the execution hook function.
Push back, the directives are also analyzed, and the instruction chapters are also analyzed.

15. Define some global objects of instructions and attributes

//Define an empty instruction decorated object
var emptyModifiers = Object.create(null);
var baseModules = [
        ref,  //For details of creating, updating and destroying functions, see Chapter: [12. Definition ref]
        directives //For details of user-defined instruction creation, update and destruction functions, see Chapter: [14. Define instruction]
]
// Attribute correlation
var attrs = {
    create: updateAttrs, //Create attribute
    update: updateAttrs  //Update properties
}
// Class correlation
var klass = {
        create: updateClass,
        update: updateClass
}

15-1: now analyze: attrs = "updateAttrs", update the attribute, and compare the attribute values in the new vnode and the old oldVnode

  • If not, set the attribute;
  • If there is no in the new vnode attribute, delete the attribute
    function updateAttrs(oldVnode, vnode) {
        debugger
        var opts = vnode.componentOptions;  //Get the extension parameters of the component
        // Exit function
        if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { 
            return
        }
        // Exit function
        if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
            return
        }
        var key, cur, old;
        var elm = vnode.elm;
        var oldAttrs = oldVnode.data.attrs || {};  // Old attribute
        var attrs = vnode.data.attrs || {}; // new property
        // clone observed objects, as the user probably wants to mutate it
        // Clone the observed object because the user may want to mutate it

        if (isDef(attrs.__ob__)) {  //Re clone one
            attrs = vnode.data.attrs = extend({}, attrs);
        }

        // Set properties if not equal
        for (key in attrs) { 
            cur = attrs[key];  // New attribute value
            old = oldAttrs[key]; // Old attribute value
            if (old !== cur) { 
                //set a property
                setAttr(elm, key, cur);
            }
        }
        // #4391: in IE9, setting type can reset value for input[type=radio] 
        // #6666: IE/Edge forces progress value down to 1 before setting a max 
        /* istanbul ignore if */
        // In IE9, setting the type can reset the input value [type=radio]
        // IE/Edge will reduce the progress value to 1 before setting the maximum value

        // If it is ie browser or edge browser, the new value is not equal to the old value
        if ((isIE || isEdge) && attrs.value !== oldAttrs.value) { 
            setAttr(elm, 'value', attrs.value); //Set a new value
        }

        // If the old attribute is not in the new attribute, it means to delete it
        for (key in oldAttrs) { 
            if (isUndef(attrs[key])) {
                if (isXlink(key)) { //Determine whether it is xml
                    elm.removeAttributeNS(xlinkNS, getXlinkProp(key)); //set a property
                }//If it is not 'contentedable, draggable, spellcheck' attribute
                else if (!isEnumeratedAttr(key)) { 
                    elm.removeAttribute(key); //set a property
                }
            }
        }
    }

Now analyze: attrs = "updateattrs = > setattr, set attributes

    function setAttr(
        el, // vnode.elm
        key, // Attribute key example: 'value'
        value // New attribute value
        ) {
        //If the dom tag name contains' - ', it is a custom tag
        if (el.tagName.indexOf('-') > -1) {
            //set a property
            baseSetAttr(el, key, value);
        } 
        // Check whether it is a Boolean attribute in html, that is, the attribute has only true and false
        // Detailed view: [10. Define judgment attribute related functions]
        else if (isBooleanAttr(key)) { 
            // set attribute for blank value sets an attribute for a null value
            // e.g. <option disabled>Select one</option>
            if (isFalsyAttrValue(value)) { 
                el.removeAttribute(key);
            } else {
                // technically allowfullscreen is a boolean attribute for <iframe>
                // but Flash expects a value of "true" when used on <embed> tag 
                // Technically, allowfullscreen is a Boolean attribute
                // However, when Flash wants to be used on the < embedded > tag, its value is "true"
                value = key === 'allowfullscreen' && el.tagName === 'EMBED'
                    ? 'true'
                    : key;
                el.setAttribute(key, value);
            }
        } else if 
        // Judge whether it is one of the three attributes of contenteditable, draggable and spellcheck
        // Detailed view: [10. Define judgment attribute related functions]
        (isEnumeratedAttr(key)) { 
            el.setAttribute(key, isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true');
        } else if 
        // Determine whether it is an xmlns attribute, for example: < bookstore xmlns: Xlink=“ http://www.w3.org/1999/xlink ">
        (isXlink(key)) {   
            if (isFalsyAttrValue(value)) { //Value has no value
                //xml uses a method to delete attributes
                el.removeAttributeNS(xlinkNS, getXlinkProp(key));
            } else {
                //Setting xml properties
                el.setAttributeNS(xlinkNS, key, value);
            }
        } else {
            //Set basic properties
            baseSetAttr(el, key, value);
        }
    }

Now analyze: attrs = "updateattrs = > setattr = > basesetattr, set attributes and do some boundary processing

    function baseSetAttr(
        el,   // dom node
        key,  // key of attribute
        value // The value of the property
    ) {
        // Judge whether val is one of [null,undefined,false]
        if (isFalsyAttrValue(value)) {
            el.removeAttribute(key);  //Remove attribute from dom
        } else {
            // #7138: ie10 & 11 fire input event when setting placeholder on ie10 and 11 trigger input event when setting placeholder
            // <textarea>...  Block the first input event and remove the blocker
            // immediately.
            /* istanbul ignore if */
            if (
                isIE &&  //If it is
                !isIE9 &&  //If it is not ie9, ie9 is not supported
                el.tagName === 'TEXTAREA' &&  //If the label is TEXTAREA(textarea)
                key === 'placeholder' && 
                !el.__ieph
            ) {
                var blocker = function (e) {
                    /**
                     * If the event listening function of multiple events of the same type is bound to the same element, when the event of this type is triggered,
                     * They are executed in the order they are added. If one of the listening functions executes event Stopimmediatepropagation() method,
                     * Then the remaining listening functions of the current element will not be executed.
                     */
                    // Stopimmediate propagation is to prevent event bubbling
                    e.stopImmediatePropagation();
                    //Delete input event
                    el.removeEventListener('input', blocker);
                };
                //Add a new input event
                el.addEventListener('input', blocker);
                // $flow-disable-line
                //Flag has added or updated the input event
                el.__ieph = true;
                /* IE placeholder patched  Placeholder patching */
            }
            //set a property
            el.setAttribute(key, value);
        }
    }

Set attribute update updateAttrs analysis completed,

15-2: now let's analyze: klass = "updateClass", update attributes and update the calss of the real dom

    function updateClass(oldVnode, vnode) {
        var el = vnode.elm;  //Get the new dom node
        var data = vnode.data; //Obtain [new] vnode data
        var oldData = oldVnode.data; //Get [old] oldVnode data
        // Boundary treatment, [not required]
        if (
            isUndef(data.staticClass) && //If no static staticClass is defined
            isUndef(data.class) && //No calss defined
            (
                isUndef(oldData) ||
                (
                    isUndef(oldData.staticClass) && isUndef(oldData.class)
                )
            )
        ) {
            // Exit function
            return
        }
        //Class transcoding obtains the static class and dynamic class in the vonde, and escapes them into the class format required by the real dom. Then return the class string

        var cls = genClassForVnode(vnode);

        // handle transition classes
        // Processing transformation class
        var transitionClass = el._transitionClasses;
        if (isDef(transitionClass)) {
            cls = concat(cls, stringifyClass(transitionClass));
        }

        // set the class _ The previous css of prevclass indicates whether it has been updated
        if (cls !== el._prevClass) {
            el.setAttribute('class', cls);
            el._prevClass = cls;
        }
    }

klass = "updateClass =" genClassForVnode ", transcode the class, obtain the staticClass (static class) and class (dynamic class) in vonde, escape them into the class format required by the real dom, and then return the class string:

    function genClassForVnode(vnode) {
        var data = vnode.data;  //Get vnode Data tag attribute data
        var parentNode = vnode; //Get parent node
        var childNode = vnode; //Get child nodes

        while (isDef(childNode.componentInstance)) { 
            // If componentInstance (component instance) is defined, recursively merge the class es of sub components
            childNode = childNode.componentInstance._vnode; //Previous vnode
            if (childNode && childNode.data) {
                data = mergeClassData(childNode.data, data);
            }
        }
        while (isDef(parentNode = parentNode.parent)) { //Recursive parent merge parent class
            if (parentNode && parentNode.data) {
                //Merge calss data
                data = mergeClassData(data, parentNode.data);
            }
        }
        return renderClass(data.staticClass, data.class) //Render calss
    }

klass = "updateClass =" genClassForVnode = "mergeClassData", merge calss data

    function mergeClassData(child, parent) {
        return {
            staticClass: concat(child.staticClass, parent.staticClass), //Static calss
            class: isDef(child.class)  //Dynamic calss in data
                ? [child.class, parent.class]
                : parent.class
        }
    }

klass = "updateClass =" genClassForVnode = "renderClass", render calss, and get the transcoded calss here

    function mergeClassData(child, parent) {
        return {
            staticClass: concat(child.staticClass, parent.staticClass), //Static calss
            class: isDef(child.class)  //Dynamic calss in data
                ? [child.class, parent.class]
                : parent.class
        }
    }

klass = "updateClass =" genClassForVnode = "renderClass =" stringifyClass ", transcode class, and convert all the calss in array format and object format into string format

    function stringifyClass(value) {
        if (Array.isArray(value)) { //If it's an array
            // The array becomes a string, which is then spliced with spaces to become a string
            // In essence, it is a recursive call. Each item of the array calls stringifyClass
            return stringifyArray(value)
        }
        if (isObject(value)) {
            return stringifyObject(value)
        }
        //The recursion does not end until all are converted to strings
        if (typeof value === 'string') {
            return value
        }
        /* istanbul ignore next */
        return ''
    }

klass = "updateClass =" genClassForVnode = "renderClass =" stringifyclass = > stringifyarray, the array becomes a string, and then it is spliced with spaces to become a string

    function stringifyArray(value) {
        var res = '';
        var stringified;
        for (var i = 0, l = value.length; i < l; i++) {
            // If the types of value[i] are not hit, stringifyClass returns' ', that is, if stringifed is'', splicing will not be performed
            if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {
                if (res) {
                    res += ' ';
                }
                res += stringified;
            }
        }
        return res
    }

klass = "updateClass =" genClassForVnode = "renderClass =" stringifyclass = > stringifyobject, the object string becomes a string, and then it is spliced with spaces to become a string

    function stringifyObject(value) {
        var res = '';
        for (var key in value) {
            if (value[key]) {
                if (res) {
                    res += ' ';
                }
                res += key;
            }
        }
        return res
    }

stringifyClass analysis is completed, renderClass is executed (get the transcoded class), genClassForVnode (get the class format needed to escape to the real dom) is also executed, and updateClass (get the call of the real dom)

16. Define and update dom events

var events = {
    create: updateDOMListeners,
    update: updateDOMListeners
}

16-1. updateDOMListeners to update dom events

    function updateDOMListeners(oldVnode, vnode) {
        // Boundary treatment
        if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {
            return
        }
        var on = vnode.data.on || {};
        var oldOn = oldVnode.data.on || {};
        target$1 = vnode.elm; //Real dom
        normalizeEvents(on);    //Add more change or input events to the event
        // Update the data source and add functions for new values, delete functions for old values, etc
        updateListeners(
            on, //New event object
            oldOn, //Old event object
            add$1, //Add event functions for real dom
            remove$2, //Delete the event function of the real dom
            vnode.context //vue instantiated object new Vue or object instantiated by component constructor
        );
        target$1 = undefined;
    }

16-1. Updatedomlisteners = > normalizeevents. Add more change or input events to the events, but don't pay attention

    function normalizeEvents(on) {
        /* istanbul ignore if */
        if (isDef(on[RANGE_TOKEN])) {
            // IE input[type=range] only supports `change` event
            // Judge whether it is an ie browser. If yes, select the change event. If not, select the input event
            var event = isIE ? 'change' : 'input';  
            // Connection event adds a change or input event
            on[event] = [].concat(on[RANGE_TOKEN], on[event] || []); 
            delete on[RANGE_TOKEN]; //Delete old events
        }
        // This was originally intended to fix #4521 but no longer necessary
        // after 2.5. Keeping it for backwards compat with generated code from < 2.4
        /* istanbul ignore if */
        //The original goal was to repair #4521, but it is no longer necessary
        // After 2.5. Keep it for reverse comparison with the code generated by < 2.4
        //Add change event
        if (isDef(on[CHECKBOX_RADIO_TOKEN])) {

            on.change = [].concat(on[CHECKBOX_RADIO_TOKEN], on.change || []);
            delete on[CHECKBOX_RADIO_TOKEN];
        }
    }

16-1. Updatedomlisteners = > updatelisteners, update the data source, add functions for new values, delete functions for old values, etc

    function updateListeners(
        on,  //New events
        oldOn, //Old events
        add,  //Add event function
        remove$$1, //Delete event function
        vm//vue instantiated object
    ) {
        var name, def, cur, old, event;

        for (name in on) {
            def = cur = on[name];  //on new event value
            old = oldOn[name];  // Old value
            event = normalizeEvent(name);   //normalizeEvent if it is an event, filter the event modifier

            /* istanbul ignore if */
            // Isundefe value is empty, undefined | null
            if (isUndef(cur)) {
                //If it's not a production environment
                "development" !== 'production' && warn(
                    "Invalid handler for event \"" + (event.name) + "\": got " + String(cur),
                    vm
                );
            } else if (isUndef(old)) {

                if (isUndef(cur.fns)) { //If the function does not exist, bind the function
                    //Function get hook function
                    // Create function callers and re copy to cur and on[name]
                    cur = on[name] = createFnInvoker(cur); //This time cur FNS exists
                }



                name = '&' + name; // mark the event as passive
                //Add event
                add(
                    event.name, //Event name
                    cur, // Escaped event execution static class
                    event.once, //Is the status triggered only once
                    event.capture, //  Event capture or bubbling behavior
                    event.passive, // Detect whether the event modifier is' & '
                    event.params //Event parameters
                );

            } else if (cur !== old) {
                //If the new value is not equal to the old value
                //The old and new values are updated
                old.fns = cur;
                on[name] = old;
            }
        }
        for (name in oldOn) {
            //When the old value of the loop is empty
            if (isUndef(on[name])) {
                //Get event
                event = normalizeEvent(name);
                //Event to delete old values
                remove$$1(event.name, oldOn[name], event.capture);
            }
        }
    }

updateDOMListeners analysis completed.

17. updateDOMProps updates the props attribute of the real dom

var domProps = {
        create: updateDOMProps, //Update the props attribute value of the real dom
        update: updateDOMProps
}

17-1. updateDOMProps: updates the props attribute of the real dom

function updateDOMProps(oldVnode, vnode) {

        if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) {
            return
        }
        var key, cur;
        var elm = vnode.elm;
        var oldProps = oldVnode.data.domProps || {}; //Get old props property
        var props = vnode.data.domProps || {}; //Get new props
        // clone observed objects, as the user probably wants to mutate it
        // Clone observed objects because you may want to modify them
        // If props adds an observer, clone it again so that it can be modified
        if (isDef(props.__ob__)) { 
            props = vnode.data.domProps = extend({}, props);
        }

        for (key in oldProps) {
            if (isUndef(props[key])) {
                elm[key] = '';
            }
        }
        for (key in props) {
            cur = props[key]; 
            // ignore children if the node has textContent or innerHTML,
            // as these will throw away existing DOM nodes and cause removal errors
            // on subsequent patches (#3360)
            //Ignore child nodes. If the node has textContent or innerHTML,
            //This will discard existing DOM nodes and cause deletion errors
            //Subsequent patches (#3360)
            if (
                key === 'textContent' ||
                key === 'innerHTML'
            ) {
                if (vnode.children) {
                    vnode.children.length = 0;
                }
                if (cur === oldProps[key]) {
                    continue
                }
                // #6601 work around Chrome version <= 55 bug where single textNode
                // replaced by innerHTML/textContent retains its parentNode property
                // #6601 solves the bug of Chrome version < = 55, in which there is only one textNode
                //After being replaced by innerHTML/textContent, its parentNode attribute is retained
                if (elm.childNodes.length === 1) { //Text node
                    elm.removeChild(elm.childNodes[0]);
                }
            }

            if (key === 'value') {
                // store value as _value as well since
                // non-string values will be stringified
                //Store value as_ Value and since
                //Non string values will be stringed
                elm._value = cur;
                // avoid resetting cursor position when value is the same
                // When the values are the same, avoid resetting the cursor position
                var strCur = isUndef(cur) ? '' : String(cur); //Escape to string
                if (shouldUpdateValue(
                    elm,   //Real dom
                    strCur //value
                )) {
                    elm.value = strCur; //assignment
                }
            } else {
                elm[key] = cur; //Direct assignment
            }
        }
    }

18. Format style string as object

Convert the style string into an object, such as' width:100px;height:200px;' Convert to {width:100px,height:200px}

    var parseStyleText = cached(function (cssText) {
        var res = {};
        //Match the in the string; Symbol. But it does not belong to (;) If the symbol of is in parentheses; Can't match
        var listDelimiter = /;(?![^(]*\))/g; 
        var propertyDelimiter = /:(.+)/;  //: + any string
        cssText.split(listDelimiter).forEach(function (item) {
            if (item) {
                var tmp = item.split(propertyDelimiter);
                tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim());
            }
        });
        return res
    });

19. Set attributes and styles

    var cssVarRE = /^--/; //Start with --
    var importantRE = /\s*!important$/; //To! important end

    var setProp = function (el, name, val) {
        //object.setProperty(propertyname, value, priority)
        // propertyname 	 Required. A string representing the property created or modified.
        // value 	 Optional, a new attribute value.
        // priority 	 Optional. String that specifies whether the priority of the property needs to be set. important.
        // Can be the following three values: "important", undefined, ""
        /* istanbul ignore if */
        if (cssVarRE.test(name)) { //Start with --
            el.style.setProperty(name, val); //Set realistic dom style
        } else if (importantRE.test(val)) { //To! important end
            el.style.setProperty(
                name,
                val.replace(importantRE, ''),
                'important'
            );
        } else {
            //Prefix css
            var normalizedName = normalize(name);
            if (Array.isArray(val)) {
                // Support values array created by autoprefixer, e.g.
                // {display: ["-webkit-box", "-ms-flexbox", "flex"]}
                // Set them one by one, and the browser will only set those it can recognize
                //Supports arrays of values created by automatic fixes.
                //{display: ["- WebKit box", "Ms flexbox", "soften")}
                //One by one, the browser will only set what it can recognize
                for (var i = 0, len = val.length; i < len; i++) {
                    el.style[normalizedName] = val[i]; //Cycle through setting styles one by one
                }
            } else {
                el.style[normalizedName] = val;
            }
        }
    };

    var vendorNames = ['Webkit', 'Moz', 'ms'];
    var emptyStyle;
    //Prefix css. Solve the problem of browser versatility and add prefix
    var normalize = cached(function (prop) {
        emptyStyle = emptyStyle || document.createElement('div').style; //Gets the style in the browser
        prop = camelize(prop);
        if (prop !== 'filter' && (prop in emptyStyle)) { //If the attribute is already in the style
            return prop
        }
        var capName = prop.charAt(0).toUpperCase() + prop.slice(1); //The first letter becomes uppercase
        for (var i = 0; i < vendorNames.length; i++) {
            var name = vendorNames[i] + capName; //Prefix
            if (name in emptyStyle) {
                return name
            }
        }
    });

19. Update Style

    var style = {
        create: updateStyle,
        update: updateStyle
    }

19-1. Updatestatyle, which translates the css of the virtual dom of vonde into and renders it to the css of the real dom

    function updateStyle(oldVnode, vnode) {
        var data = vnode.data; //Gets the label property of the new virtual dom
        var oldData = oldVnode.data; //Gets the label property of the old virtual dom

        if (isUndef(data.staticStyle) && isUndef(data.style) &&
            isUndef(oldData.staticStyle) && isUndef(oldData.style)
        ) {
            return
        }

        var cur, name;
        var el = vnode.elm; //Get the real dom
        var oldStaticStyle = oldData.staticStyle; //Get old static staticStyle
        var oldStyleBinding = oldData.normalizedStyle || oldData.style || {}; //Get old dynamic style

        // if static style exists, stylebinding already merged into it when doing normalizeStyleData
        //  If there are static styles, stylebinding has been merged into them when normalizestatyledata is executed
        var oldStyle = oldStaticStyle || oldStyleBinding; //Old style


        //Normalize the possible array / string values into objects. / / convert the style string into objects, such as' width:100px;height:200px;'  Convert to {width:100px,height:200px}
        var style = normalizeStyleBinding(vnode.data.style) || {};

        // store normalized style under a different key for next diff
        // make sure to clone it if it's reactive, since the user likely wants
        // to mutate it.
        //Store normalized styles under different keys for the next diff
        //If it is reactive, be sure to clone it, as users may want to
        //Mutate it
        vnode.data.normalizedStyle = isDef(style.__ob__) ? //If you join the observer
            extend({}, style) :  //Re clone, you can modify
            style; //Direct assignment
        //getStyle loops the styles of subcomponents and components, combines them all into one style object, and returns the style object. For example, {width:100px,height:200px} returns the string.
        var newStyle = getStyle(
            vnode,
            true
        );

        for (name in oldStyle) { //Gets the style of the old virtual dom
            if (isUndef(newStyle[name])) { // If the new virtual dom vonde is gone
                setProp(el, name, ''); //The setting style is empty
            }
        }
        for (name in newStyle) { //Loop through the new virtual dom vonde style
            cur = newStyle[name];
            if (cur !== oldStyle[name]) { //Set a new style if the old one is different from the new one
                // ie9 setting to null has no effect, must use empty string
                setProp(el, name, cur == null ? '' : cur);
            }
        }
    }

Updatestatyle = "normalizestatylebinding", normalize possible array / string values into objects

    function normalizeStyleBinding(bindingStyle) {
        if (Array.isArray(bindingStyle)) {
            return toObject(bindingStyle)
        }
        if (typeof bindingStyle === 'string') {
            // Convert the style string to an object, such as' width:100px;height:200px;' 
            // Convert to {width:100px,height:200px}
            // See Chapter 18 for details Style string formatted as object
            return parseStyleText(bindingStyle)
        }
        return bindingStyle
    }

updateStyle = "getStyle", the parent component style should be after the child component style, so that the parent component style can override it, cycle the child component and component style, merge them all into one style object, and return the style object, such as {width:100px,height:200px} to return the string.

    function getStyle(
        vnode, //Virtual dom
        checkChild //Flag point Boolean
    ) {
        var res = {};
        var styleData; //style data
        if (checkChild) { // Flag point Boolean
            var childNode = vnode; //Get child nodes
            while (childNode.componentInstance) { //What has been instantiated is that the child node has a vonde
                childNode = childNode.componentInstance._vnode;
                if (
                    childNode &&
                    childNode.data &&
                    (styleData = normalizeStyleData(childNode.data))
                ) {
                    extend(res, styleData);
                }
            }
        }

        if ((styleData = normalizeStyleData(vnode.data))) {
            extend(res, styleData);
        }

        var parentNode = vnode;
        while ((parentNode = parentNode.parent)) {
            if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) {
                extend(res, styleData);
            }
        }
        return res
    }

updateStyle = "GetStyle = > normalizestatyledata, which combines static and dynamic style data on the same vnode

function normalizeStyleData(data) {
        // //Normalize the possible array / string values into objects, and convert the style string into objects, such as' width:100px;height:200px;'  Convert to {width:100px,height:200px} and return the string.
        var style = normalizeStyleBinding(data.style); //Gets the value of the style attribute in the vonde
        // static style is pre-processed into an object during compilation
        // and is always a fresh object, so it's safe to merge into it
        //Static styles are preprocessed as objects during compilation
        //It is always a fresh object, so it can be safely integrated into it
        return data.staticStyle ?
            extend(data.staticStyle, style) : //Merge static
            style
    }

Updatestatyle analysis is complete.

20. Packaging tool template

    var platformModules = [
        // attrs contains two methods, create and update, which update and set the real dom attribute value 
        // {create: updateAttrs, / * create attribute * / update: updateAttrs / * update attribute * /}
        attrs,  
        // The klass class contains two methods, create and update, both of which update calss.
        // It's actually the updateClass method. Set the class of the real dom
        klass, 
        events, //Events that update the real dom
        domProps, //Update the props attribute value of the real dom
        // Update the style attribute of the real dom. There are two methods: create and update, but both functions update style to update the style attribute value of the real dom
        // The css of the vonde virtual DOM is escaped and rendered into the css of the real dom
        style, 
        transition // Over animation
    ]
    var modules = platformModules.concat(baseModules);
    //path renders vonde as a real dom
    var patch = createPatchFunction(
        {
            nodeOps: nodeOps,
            modules: modules
        }
    );

21 . Define insert update instrumentation functions

    var directive = {
        inserted: function inserted(el, binding, vnode, oldVnode) {

            if (vnode.tag === 'select') {
                // #6903
                if (oldVnode.elm && !oldVnode.elm._vOptions) {
                    mergeVNodeHook(vnode, 'postpatch', function () {
                        directive.componentUpdated(el, binding, vnode);
                    });
                } else {
                    setSelected(el, binding, vnode.context);
                }
                el._vOptions = [].map.call(el.options, getValue);
            } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
                el._vModifiers = binding.modifiers;
                if (!binding.modifiers.lazy) {
                    el.addEventListener('compositionstart', onCompositionStart);
                    el.addEventListener('compositionend', onCompositionEnd);
                    // Safari < 10.2 & UIWebView doesn't fire compositionend when
                    // switching focus before confirming composition choice
                    // this also fixes the issue where some browsers e.g. iOS Chrome
                    // fires "change" instead of "input" on autocomplete.
                    el.addEventListener('change', onCompositionEnd);
                    /* istanbul ignore if */
                    if (isIE9) {
                        el.vmodel = true;
                    }
                }
            }
        },

        componentUpdated: function componentUpdated(el, binding, vnode) {
            if (vnode.tag === 'select') {
                setSelected(el, binding, vnode.context);
                // in case the options rendered by v-for have changed,
                // it's possible that the value is out-of-sync with the rendered options.
                // detect such cases and filter out values that no longer has a matching
                // option in the DOM.
                var prevOptions = el._vOptions;
                var curOptions = el._vOptions = [].map.call(el.options, getValue);
                if (curOptions.some(function (o, i) {
                    return !looseEqual(o, prevOptions[i]);
                })) {
                    // trigger change event if
                    // no matching option found for at least one value
                    var needReset = el.multiple
                        ? binding.value.some(function (v) {
                            return hasNoMatchingOption(v, curOptions);
                        })
                        : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions);
                    if (needReset) {
                        trigger(el, 'change');
                    }
                }
            }
        }
    };

22. Define update binding instruction function

   var show = {
        bind: function bind(el, ref, vnode) {
            var value = ref.value;

            vnode = locateNode(vnode);
            var transition$$1 = vnode.data && vnode.data.transition;
            var originalDisplay = el.__vOriginalDisplay =
                el.style.display === 'none' ? '' : el.style.display;
            if (value && transition$$1) {
                vnode.data.show = true;
                enter(vnode, function () {
                    el.style.display = originalDisplay;
                });
            } else {
                el.style.display = value ? originalDisplay : 'none';
            }
        },

        update: function update(el, ref, vnode) {
            var value = ref.value;
            var oldValue = ref.oldValue;

            /* istanbul ignore if */
            if (!value === !oldValue) {
                return
            }
            vnode = locateNode(vnode);
            var transition$$1 = vnode.data && vnode.data.transition;
            if (transition$$1) {
                vnode.data.show = true;
                if (value) {
                    enter(vnode, function () {
                        el.style.display = el.__vOriginalDisplay;
                    });
                } else {
                    leave(vnode, function () {
                        el.style.display = 'none';
                    });
                }
            } else {
                el.style.display = value ? el.__vOriginalDisplay : 'none';
            }
        },

        unbind: function unbind(el,
            binding,
            vnode,
            oldVnode,
            isDestroy) {
            if (!isDestroy) {
                el.style.display = el.__vOriginalDisplay;
            }
        }
    }

23 . Encapsulation instruction

    var platformDirectives = {
        model: directive,
        show: show
    }

24. Inspection attributes

    Vue.config.mustUseProp = mustUseProp;    //Check properties


    Vue.config.isReservedTag = isReservedTag;
    Vue.config.isReservedAttr = isReservedAttr;
    Vue.config.getTagNamespace = getTagNamespace;
    Vue.config.isUnknownElement = isUnknownElement;

    // install platform runtime directives & components
    extend(Vue.options.directives, platformDirectives);
    extend(Vue.options.components, platformComponents);

    // install platform patch function
    Vue.prototype.__patch__ = inBrowser ? patch : noop;

25. Mount $mount

    Vue.prototype.$mount = function (el, hydrating) {
        debugger
        // query(el) obtains the dom. If it is already a DOM, it returns. If it is not a DOM and cannot be obtained, a warning prompt is given and a new dev is created
        el = el && inBrowser ? query(el) : undefined;
        // Installation components
        return mountComponent(
            this, // Vue instance
            el,  // Real dom
            hydrating
        )
    };

26 .vue development tool configuration

    if (inBrowser) {
        setTimeout(function () {
            if (config.devtools) {
                if (devtools) {
                    devtools.emit('init', Vue);
                } else if (
                    "development" !== 'production' &&
                    "development" !== 'test' &&
                    isChrome
                ) {
                    console[console.info ? 'info' : 'log'](
                        'Download the Vue Devtools extension for a better development experience:\n' +
                        'https://github.com/vuejs/vue-devtools'
                    );
                }
            }
            //If it's not a production environment
            if ("development" !== 'production' &&
                "development" !== 'test' &&
                config.productionTip !== false &&
                typeof console !== 'undefined'
            ) {
                console[console.info ? 'info' : 'log'](
                    "You are running Vue in development mode.\n" +
                    "Make sure to turn on production mode when deploying for production.\n" +
                    "See more tips at https://vuejs.org/guide/deployment.html"
                );
            }
        }, 0);
    }

Keywords: Javascript html5 Vue Vue.js Interview

Added by AcidDriver on Fri, 21 Jan 2022 01:02:56 +0200