Vue中事件绑定的原理

之前我搜这个原理的时候,好多文章,都只写了俩句话:

原生事件绑定是通过addEventListener绑定给真实元素的。
组件事件绑定是通过Vue自定义的key$on实现的。

那具体是怎么实现的呢, 没有说?
就现在具体看一下。

// 原生事件绑定
<div @click="fn()"></div>

// 组件绑定
<my-component @click.native="fn" @click="fn1"></my- component>

原理:
事件的编译:

let compiler = require('vue-template-compiler'); //vue-loader
let r1 = compiler.compile('<div @click="fn()"></div>'); 
let r2 = compiler.compile('<my-component @click.native="fn" @click="fn1"></my- component>'); console.log(r1); 
// {on:{click}} console.log(r2); 
// {nativeOn:{click},on:{click}}

两者编译出来不一样

// 前者
with (this){
    
    return _c('div',{
    
    on:{
    
    "click":function($event){
    
    return fn()}}})}

// 后者
with (this){
    
    return _c('my-component',{
    
    on:{
    
    "click":fn1},nativeOn:{
    
    "click":function($event){
    
    return fn($event)}}})}

在这里插入图片描述

1.1 原生 dom 的绑定

  • Vue 在创建真是 dom 时会调用 createElm ,默认会调用 invokeCreateHooks
  • 会遍历当前平台下相对的属性处理代码,其中就有 updateDOMListeners 方法,内部会传入 add 方法

源码:

function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
    
     
	if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {
    
     return }
	const on = vnode.data.on || {
    
    } const oldOn = oldVnode.data.on || {
    
    } 
	target = vnode.elm 
	normalizeEvents(on) 
	updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context) 
	target = undefined 
}
function add ( 
	name: string, 
	handler: Function, 
	capture: boolean, 
	passive: boolean 
) {
    
    
	target.addEventListener( // 给当前的dom添加事件 
		name, 
		handler, 
		supportsPassive 
		? {
    
      capture, passive  } 
		: capture 
	) 
}

1.2 组件中绑定事件

export function updateComponentListeners ( 
	vm: Component, 
	listeners: Object, 
	oldListeners: ?Object 
) {
    
    
	target = vm updateListeners(
		listeners, oldListeners || {
    
    }, 
		add, 
		remove,
		createOnceHandler, vm)
		target = undefined 
}

function add (event, fn) {
    
     
	target.$on(event, fn) 
}

组件绑定事件是通过 vue 中自定义的 $on 方法来实现的

1.3 $on 是怎么实现的

vm.$on( event, callback )

作用:

监听当前实例上的自定义事件。事件可以由vm.$emit触发。回调函数会接收所有传入事件触发函数的额外参数。

原理:

$on是采用了经典的发布订阅者设计模式,首先定义一个事件中心,通过$on订阅事件,将事件存储在事件中心里面,然后通过$emit触发事件中心里面存储的订阅事件。

Vue.prototype.$on = function (event, fn) {
    
    
    const vm: Component = this
    if (Array.isArray(event)) {
    
    
        for (let i = 0, l = event.length; i < l; i++) {
    
    
            this.$on(event[i], fn)
        }
    } else {
    
    
        (vm._events[event] || (vm._events[event] = [])).push(fn)
    }
    return vm
}

看代码,逻辑很简单,$on函数接收俩个参数,第一个是订阅的事件名,可以是多个,如果是多个就传入一个事件名数组。另一个是回调函数。 首先判断传入的事件是不是一个数组,如果是,那么遍历这个数组,将数组中的每一个事件都递归调用$on方法将其作为单个事件订阅。如过不是数组,那就当做单个事件名来处理,以该事件名作为key,先尝试在当前实例的_events属性中获取其对应的事件列表,如果获取不到就给其赋空数组为默认值,并将第二个参数回调函数添加进去。

多说一句,实例的_events是什么?这就是在事件初始化的时候,initEvents函数中绑定了_event属性并给其赋值为空对象。这个_events属性就是用来作为当前实例的事件中心,所有绑定在这个实例上的事件都会存储在事件中心_events属性中。

这就是$on的内部原理。

猜你喜欢

转载自blog.csdn.net/Beth__hui/article/details/114089801