vue中 用事件对象$event 添加属性_解读Vue3中废弃组件事件的$on,$off 和 $once 实例方法...
本文要讲解的内容原文档地址点我传送Vue3.0中事件API$on,$off 和 $once 实例方法已被移除,应用实例不再实现事件触发接口。接下来我们分析下为什么要在Vue3中去掉,如果需要继续使用此功能改为使用第三方 mitt 库替代。2.x语法在 2.x 中,Vue 实例可用于触发通过事件触发 API 强制附加的处理程序 ($on,$off 和 $once),这用于创建 event hub,以
本文要讲解的内容原文档地址点我传送
Vue3.0中事件API
$on
,$off
和 $once
实例方法已被移除,应用实例不再实现事件触发接口。
接下来我们分析下为什么要在Vue3中去掉,如果需要继续使用此功能改为使用第三方 mitt 库替代。
2.x语法
在 2.x 中,Vue 实例可用于触发通过事件触发 API 强制附加的处理程序 ($on
,$off
和 $once
),这用于创建 event hub,以创建在整个应用程序中使用的全局事件侦听器:
eventHub.js
// eventHub.js
const eventHub = new Vue()
export default eventHub
ChildComponent.vue
// ChildComponent.vue
import eventHub from './eventHub'
export default {
mounted() {
// 添加 eventHub listener
eventHub.$on('custom-event', () => {
console.log('Custom event triggered!')
})
},
beforeDestroy() {
// 移除 eventHub listener
eventHub.$off('custom-event')
}
}
ParentComponent.vue
// ParentComponent.vue
import eventHub from './eventHub'
export default {
methods: {
callGlobalCustomEvent() {
eventHub.$emit('custom-event') // 如果ChildComponent mounted,控制台中将显示一条消息
}
}
}
上述代码示例中 ChildComponent.vue 和 ParentComponent.vue 都引用了同一个 Vue 的实例对象 eventHub。使用 eventHub 的目的就是在调用 $on
, $off
, $emit
时是同一个对象上的方法调用。
我们知道组件中的 this
也就是一个 Vue 的实例对象。
说到这里,善于思考的同学可能会想到,我们是不是可以将上述的eventHub.xx
改为this.xx
?
答案是不可以的,因为如此一来 ChildComponent.vue 中的 this 和 ParentComponent.vue 的 this 将不会是同一个 Vue 实例对象,也就是说 (new ChildComponent) !== (new ParentComponent)
上述代码实际可以改为如下
ChildComponent.vue
// ChildComponent.vue
export default {
mounted() {
// 添加 eventHub listener
this.$parent.$on('custom-event', () => {
console.log('Custom event triggered!')
})
},
beforeDestroy() {
// 移除 eventHub listener
this.$parent.$off('custom-event')
}
}
ParentComponent.vue
// ParentComponent.vue
export default {
methods: {
callGlobalCustomEvent() {
this.$emit('custom-event') // 如果ChildComponent mounted,控制台中将显示一条消息
}
}
}
父组件使用 this.$emit
去触发事件,子组件中使用 this.$parent
去获取父组件的实例对象。但如果是多层级的子孙组件,这就比较难受了,需要 this.$parent.$parent...
。
所以在Vue2.x中 this.$on
this.$off
this.$emit
通常只是在一个组件内进行使用,父子组件使用的时候要考虑实例对象是否是同一个。而在一个组件内的时候,我们可以直接this.
来调用其他方法,不需要这么麻烦。因此将这几个方法从Vue3.0中移除。$emit
仍然是现有 API 的一部分,因为它用于触发由父组件以声明方式附加的事件处理程序
Vue3.x推荐使用外部库mitt来代替 $on $emit $off
接下来部分是解读 mitt 源码
mitt源码使用的是typescript编写的,源码加注释一共不到90行,阅读起来比较轻松。typescript不是本次的重点,所以我将mitt源码以js的形式展示如下。
/**
* 向外暴露的默认函数
* @param 入参为 EventHandlerMap 对象 (ts真香,我们能清楚的知道参数的类型是什么,返回值是什么)
* @returns 返回一个对象,对象包含属性 all,方法 on,off,emit
*/
export default function mitt (all) {
/*
此处实参可传一个EventHandlerMap对象,实现多个 mitt 的合并。例如:
const m1 = mitt();
m1.on('hi', () => { console.log('Hi, I am belongs to m1.'); });
const m2 = mitt(m1.all);
m2.emit('hi') // Hi, I am belongs to m1.
m2.on('hello', () => { console.log('Hello, I am belongs to m2.'); });
m1.emit('hello'); // Hello, I am belongs to m2.
m1.all === m2.all // true
*/
all = all || new Map();
return {
// 事件键值对映射对象
all,
/**
* 注册一个命名的事件处理
* @param type 事件名,官方表示事件名如是 *,用来标记为通用事件,调用任何事件,都会触发命名为 * 的事件
* @param handler 事件处理函数
*/
on (type, handler) {
// 根据type去查找事件
const handlers = all.get(type);
// 如果找到有相同的事件,则继续添加,Array.prototype.push 返回值为添加后的新长度,
const added = handlers && handlers.push(handler);
// 如果已添加了type事件,则不再执行set操作
if (!added) {
all.set(type, [handler]); // 注意此处值是数组类型,可以添加多个相同的事件
}
},
/**
* 移除指定的事件处理
* @param type 事件名,和第二个参数一起用来移除指定的事件,
* @param handler 事件处理函数
*/
off (type, handler) {
// 根据type去查找事件
const handlers = all.get(type);
// 如果找到则进行删除操作
if (handlers) {
// 这里用了个骚操作,其实就是找到了,则删除(多个相同的只会删除找到的第一个),没找到则不会对原数组有任何影响
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
},
/**
* 触发所有 type 事件,如果有type为 * 的事件,则最后会执行。
* @param type 事件名
* @param evt 传递给处理函数的参数
*/
emit (type, evt) {
// 找到type的事件循环执行
(all.get(type) || []).slice().map((handler) => { handler(evt); });
// 然后找到所有为*的事件,循环执行
(all.get('*') || []).slice().map((handler) => { handler(type, evt); });
}
};
}
代码还是相当的精简的,麻雀虽小,五脏俱全。
接下来我们写几个例子来小牛试刀。
// emit 不是单例
const m1 = mitt();
const m2 = mitt();
m1 === m2; // false
// 多个mitt之间可以实现合并,合并后的mitt的all属性指向的是同一个内存地址
const m3 = mitt(m2.all);
m3.all === m2.all; // true
m2.on('hi', () => { console.log('我是m2'); });
m3.emit('hi'); // 我是m2
m2.on('*', () => { console.log('我是公共的'); });
m2.emit('hi');
// 我是m2
// 我是公共的
到此我们已经学会如果使用 emitt 以及如何使用它了。
那么这里留个作业给大家发挥下,我们如何基于它实现一个单例的 mitt 呢?
更多推荐
所有评论(0)