SoFunction
Updated on 2025-04-04

Vue2 template command element binding event execution order analysis

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

updateDOMListenersCalled byupdateListenersMethod: 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
]

modulesWhere 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 alsocreateCalled on the internal cyclebindMethod

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 isaddEventListenerBody

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

andstopImmediatePropagationNot 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!