深入vue2.0源码系列: 事件机制的实现与运用
前言
Vue.js 2.0使用了一种事件机制来实现组件间的通信和状态管理,本文将会介绍Vue.js 2.0事件机制的实现和运用。
事件机制的实现
Vue.js 2.0的事件机制实现依赖于一个名为 vm.$emit
的实例方法和一个名为 vm.$on
的实例方法。当组件需要向其他组件通信时,可以调用 vm.$emit
方法触发一个自定义事件,其他组件可以通过调用 vm.$on
方法来监听这个自定义事件。这样就能够实现组件间的通信。
下面我们来看一下 vm.$emit
和 vm.$on
方法的实现。
vm.$emit 的实现
vm.$emit
方法定义在 src/core/instance/events.js
文件中,其实现如下:
// Vue实例原型上定义$emit方法
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
// 开发环境下,如果事件名称存在大写字符并且存在监听该事件的回调函数,则输出警告
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
warn(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
// 通过事件名称获取当前实例上所有监听该事件的回调函数
let cbs = vm._events[event]
if (cbs) {
// 如果存在回调函数,则调用所有回调函数,并将参数传递给它们
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
for (let i = 0, l = cbs.length; i < l; i++) {
try {
cbs[i].apply(vm, args)
} catch (e) {
handleError(e, vm, `event handler for "${event}"`)
}
}
}
return vm
}
vm.$on 的实现
vm.$on
方法的实现也定义在 src/core/instance/events.js
文件中,其实现如下:
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
// 如果不存在该事件的回调函数,则初始化一个空数组来存储
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
上面的代码中,如果 vm._events
对象中不存在该事件的回调函数,就会初始化一个空数组来存储回调函数。如果该事件名称匹配了 hookRE
正则表达式,就会将 _hasHookEvent
标记为 true
,这样可以在组件更新时快速判断是否需要调用生命周期钩子函数。
事件机制的运用
事件机制在 Vue.js 2.0 中广泛应用于组件间的通信和状态管理。以下是一些事件机制的运用场景:
父子组件间的通信
父组件可以在模板中使用 v-on
指令监听子组件触发的事件,如:
<template>
<child-component @custom-event="handleCustomEvent"></child-component>
</template>
子组件在触发事件时,可以调用 $emit
方法并传递事件名称及数据:
this.$emit('custom-event', data)
非父子组件间的通信
对于非父子组件间的通信,可以使用一个名为 EventBus
的事件总线实现。EventBus
可以是一个简单的 Vue 实例,用来在组件之间进行事件传递。
首先,创建一个 EventBus
实例并导出:
import Vue from 'vue'
export const EventBus = new Vue()
然后,在需要传递事件的组件中引入 EventBus
实例,并使用 $emit
方法触发事件:
import { EventBus } from '@/event-bus.js'
// 触发事件
EventBus.$emit('custom-event', data)
在需要监听事件的组件中也需要引入 EventBus
实例,并使用 $on
方法监听事件:
import { EventBus } from '@/event-bus.js'
// 监听事件
EventBus.$on('custom-event', data => {
// 处理事件
})
Vuex 状态管理
Vue.js 2.0 提供了一个名为 Vuex 的状态管理库,它可以帮助我们更好地管理组件状态,并且能够实现跨组件的状态共享。
Vuex 中的状态存储在一个中央的 store 中,可以通过 this.$store
在组件中访问。当组件需要修改状态时,可以通过提交一个 mutation 来改变状态,或者通过分发一个 action 来触发一系列的状态变更操作。
当状态发生变化时,所有使用该状态的组件都会得到通知,并进行相应的更新。
// 定义 store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
export default store
// 在组件中使用 state 和 mutation
<template>
<div>
{{ count }}
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
computed: {
count () {
return this.$store.state.count
}
},
methods: {
increment () {
this.$store.commit('increment')
}
}
}
</script>
以上就是 Vue.js 2.0 中事件机制的实现和运用,它们在实现组件间通信和状态管理方面提供了很大的便利。
总结
在 Vue.js 2.0 中,事件机制被广泛运用于实现组件之间的通信和状态管理。对于父子组件之间的通信,可以使用 props
和 $emit
方法;对于非父子组件之间的通信,可以使用一个名为 EventBus
的事件总线实现;对于状态管理,可以使用 Vuex 库来实现状态存储和变更操作。这些机制的运用使得 Vue.js 的组件化开发更加方便和灵活。
后续会继续更新vue2.0其他源码系列,包括目前在学习vue3.0源码也会后续更新出来,喜欢的点点关注。
系列文章:
深入vue2.0源码系列:手写代码来模拟Vue2.0的响应式数据实现
转载自:https://juejin.cn/post/7210747150828912696