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); }