Vue custom instruction execution mechanism
version: 2.6.14
Introduction
One day, the business needs me to verify certain conditions before the button is clicked. If it does not meet, the business code in the click will not be executed. After thinking about it, just write an instruction. It is necessary to not change the original business code, but can also be ported.
<template> <button v-capture @click="handleClick">button</button> </template> <script> export default { methods: { handleClick(){ (1) } }, directives: { capture: { bind(el) { = (e) => { // Verification conditions (2) () }; ("click", ); }, unbind(el) { ("click", ); } } } } </script>
The above is the pseudo-code, and at first glance there is no problem.
Once actually running, I found that 1 and 2 were printed out, and 1 was still running before 2.
From this, the execution of the event bound on the template is before the custom instruction binds the event.
I opened Google and found no relevant cases.
DOM binding
We all know that vue's SFC will eventually be compiled into js files, and the template will eventually be compiled into vnode,
Events bound on elements will be converted into an object on vnode
{ // .... on: { click: 'handleClick' } }
Source code
Then find out where to use this object
Search in runtimeaddEventListener
, Because this event is bound to events that are only found in the DOM, it will only be in the web
// src/platforms/web/runtime/modules/ export default { create: updateDOMListeners, update: updateDOMListeners, destroy: (vnode: VNodeWithData) => updateDOMListeners(vnode, emptyNode) }
Don't worry about the specific implementation
updateDOMListeners
Called byupdateListeners
Method: bind events to elements
Also, it returns an object, including create, update, destroy. Isn't this very similar to the life cycle function naming of vue
Search up according to the file 👆
Finally inmodules/
Exported in
export default [ attrs, klass, events, domProps, style, transition ]
modules
Where did it end up using it?
It's famous
// src/core/vdom/ const { modules, nodeOps } = backend for (i = 0; i < ; ++i) { cbs[hooks[i]] = [] for (j = 0; j < ; ++j) { if (isDef(modules[j][hooks[i]])) { cbs[hooks[i]].push(modules[j][hooks[i]]) } } }
As soon as the function starts, it classifies modules and merges related objects on the original modules.
Finally cbs will become an object
const cbs = { create: [fn1, fn2, fn3], update: [fn1, fn2, fn3], destroy: [fn1, fn2, fn3], }
I won't talk about the specific execution timing
directive
Directives are a major feature of vue. It originates from the fact that there is command in angularjs, and it is still preserved in vue3.
The following methods correspond to the instructions, which can also be said to be the life cycle.
directives: { name: { bind(){}, insert(){}, inserted(){}, componentUpdated(){}, update(){}, unbind(){}, } }
Next, find out when the command is initialized
Global searchdirectives
, In fact, this is just this file, that's it
// src/core/vdom/modules/ { create: updateDirectives, update: updateDirectives, destroy: function unbindDirectives (vnode: VNodeWithData) { updateDirectives(vnode, emptyNode) } }
It can be clearly seen that it is alsocreate
Called on the internal cyclebind
Method
callHook(dir, 'bind', vnode, oldVnode)
Why call the template binding method first, and then call the instruction method
Back to, You can see that the modules are merged here, putting the platform-related modules in front, and the basic instructions and refs are executed later.
At the same time, the official also made annotations.Methods that execute built-in methods first and then execute instructions。
// src/platforms/web/runtime/ import baseModules from 'core/vdom/modules/index' import platformModules from 'web/runtime/modules/index' // the directive module should be applied last, after all // built-in modules have been applied. const modules = (baseModules)
I still didn't look at the comments carefully, how many times have this file been opened. 😭
Is it OK to change it
Still not possible.
The problem isaddEventListener
Body
Put aside vue and look at demo
const btn = ('#btn') ('click', () => { (1) }) ('click', () => { (2) })
Summarize
HTML The element repeatedly binds the same event, the latter will not overwrite the previous one, but will only have the order of bindings.
Then can the previous problem be solved
The event is executed during the capture phase, and if the criteria are not met, the event delivery is stopped.
("click", , true);
andstopImmediatePropagation
Not available
stopImmediatePropagation can prevent other events bound on the element, but it also prevents subsequent events from being executed in the order of addition.
The above is the detailed analysis of the execution order of Vue2 template instruction element binding event. For more information about the execution order of Vue2 event, please pay attention to my other related articles!