I went to an interview that day and was asked when the corresponding hook functions of these five attributes would be called respectively. I answered according to the answer on the official website of Vue
-
Bind: Called only once, the first time a directive is bound to an element. This is where you can perform one-time initialization Settings.
-
Inserted: Called when the bound element is inserted into a parent (the parent is guaranteed to exist, but not necessarily inserted into the document).
-
Update: called when the component’s VNode is updated, but may occur before its child VNodes are updated. The value of the instruction may or may not have changed
-
ComponentUpdated: Invoked when the VNode of the component where the directive resides and its child VNodes are all updated.
-
Unbind: Called only once, when an instruction is unbound from an element.
Finally, the interviewer asked me a dumb question, how are these properties implemented in the source code, so I came back to the code
< INPUT V-if ="show" V-focus V-model =" MSG "/> directives: {focus: {// Definition of directive: function(el) {debugger; el.focus(); }, bind: function(el) { debugger; }, update: function(el) { debugger; }, componentUpdated: function(el) { debugger; }, unbind: function(el) { debugger; }}},Copy the code
If a cache called Focus is now defined, then focus will be resolved and bound to one of the corresponding VNode.data. diretives array, where def is the custom hook function. And each time the Patch method is executed, an updateDirectives method is executed, which is where the hook function is originally called
Look at the updateDirectives method. If both VNode and oldVnode do not define directives, you can skip the _UPDATE step
function updateDirectives (oldVnode, vnode) { if (oldVnode.data.directives || vnode.data.directives) { _update(oldVnode, vnode); }}Copy the code
Otherwise, the _update method will be executed and I’ll comment out the following methods as much as I can and get rid of the non-essential ones
Function _update (oldVnode, vnode) {// isCreate specifies whether to render for the first time. Var isCreate = oldVnode === emptyNode; // isDestroy is used to determine whether to destroy the unbind hook. Var isDestroy = vnode === emptyNode; / / get oldVnode above directives array oldDirs var oldDirs = normalizeDirectives $1 (oldVnode. Data. Directives, oldVnode. Context). NewDirs var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); //dirsWithInsert This array is used to install the inserted method on this component and pass it to the parent component. Var dirsWithPostpatch = []; var dirsWithPostpatch = []; var key, oldDir, dir; For (key in newDirs) {// newDirs oldDir = oldDirs[key]; dir = newDirs[key]; if (! OldDir) {// If this instruction does not exist on oldVnode, it is considered to be the first time that vnode calls it, CallHook $1(dir, 'bind', vnode, oldVnode) for the custom bind method corresponding to cache. def; // Call bind hook if (dir.def && dir.def. Inserted) {// If at the same time this directives define the INSERTED method, Push this method into the dirsWithInsert array dirsWithInsert.push(dir); OldValue = olddir.value;}} else {// If the new node and the old node have the same instruction, it is the instruction update, call the update function dir.oldvalue = olddir.value; dir.oldArg = oldDir.arg; callHook$1(dir, 'update', vnode, oldVnode); // Call the update hook if (dir.def && dir.def.componentupdated) {// If the directive also defines componentUpdated, I'm just going to push this method into dirsWithPostpatch dirswithPostpatch.push (dir); } // If dirsWithInsert has a value, create a callInsert method, Insert.length) {var callInsert = function () {for (var I = 0; i < dirsWithInsert.length; i++) { callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); }}; If (isCreate) {// If it is rendering for the first time, you need to define this method directly on the insert property corresponding to vNode.data.hooks. mergeVNodeHook(vnode, 'insert', callInsert); } else { callInsert(); }} // If dirsWithPostpatch has a value, bind it to the postpatch property of the vnode. Call if (dirsWithPostPatch.length) {mergeVNodeHook(vnode, 'postpatch', function () {for (var I = 0; i < dirsWithPostpatch.length; i++) { callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); }}); } // If not first render if (! isCreate) { for (key in oldDirs) { if (! NewDirs [key]) {// And there is no such cache on Vnode, the directive is considered to be removed, CallHook $1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); }}}}Copy the code
Go through the code above
The INSERTED hook of VNode’s directives will be bound to vNode.data.hooks. Insert during the first render invocation.
In updating stage, can the vnode componentUpdated hooks binding in the directives to vnode. The data. The hooks. Postpatch above
When createElm(vnode), invokeCreateHooks(vnode, insertedVnodeQueue) are implemented; The execution of this method puts the vNode in the insertedVnodeQueue array
InvokeInsertHook (vnode, insertedVnodeQueue, isInitialPatch) is executed before the last return of patch(oldVode,vnode). This method, which executes all vNodes in the insertedVnodeQueue array with its vNode.data.links.insert method (which it would not execute without), ensures that the method will not execute until the vnode itself has rendered
And vnode. Data. The hooks. Postpatch method in patchVnode (oldVnode vnode) final stages, namely vnode and vnode chiildren are finished, If the vnode vnode. Data. The hooks. Postpatch method is to carry out the method, which is why componentUpdated hook can in itself and its nodes are finished update to perform