事件系统底层实现
Vue事件系统核心是自定义事件总线,基于发布订阅模式实现组件间通信。
事件API实现
$on 事件监听
JavaScript
// 简化版实现
Vue.prototype.$on = function(event, fn) {
const vm = this
vm._events[event] = vm._events[event] || []
vm._events[event].push(fn)
return vm
}
事件存储在 _events 对象中,支持同一事件注册多个回调。
$emit 事件触发
JavaScript
Vue.prototype.$emit = function(event) {
const vm = this
let cbs = vm._events[event]
if (cbs) {
const args = toArray(arguments, 1)
cbs = cbs.slice()
for (let i = 0, l = cbs.length; i < l; i++) {
cbs[i].apply(vm, args)
}
}
return vm
}
使用 slice() 创建副本避免运行时回调数组被修改导致的问题。
$off 事件移除
JavaScript
Vue.prototype.$off = function(event, fn) {
const vm = this
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
if (!fn) {
vm._events[event] = null
return vm
}
let cbs = vm._events[event]
let i = cbs.length
while (i--) {
if (cbs[i] === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
支持三种移除方式:移除所有、移除某事件所有、移除特定回调。
组件事件绑定流程
JavaScript
// 模板: <Child @custom="handler"/>
// 编译后
createElement(Child, {
on: { custom: handler }
})
父组件通过 on 选项传递事件回调,子组件通过 $emit 触发。
事件修饰符底层处理
JavaScript
// v-on:click.stop="handler"
function withModifiers(fn, modifiers) {
if (modifiers.includes('stop')) {
return function(e) {
e.stopPropagation()
return fn(e)
}
}
if (modifiers.includes('prevent')) {
return function(e) {
e.preventDefault()
return fn(e)
}
}
}
修饰符本质是事件对象的方法调用包装。
$once 一次性事件
JavaScript
Vue.prototype.$once = function(event, fn) {
const vm = this
function on() {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
内部创建包装函数,触发后自动移除自身。
要点总结
- Vue事件系统基于发布订阅模式,事件存储于
_events对象 $emit使用slice()副本防止运行时回调修改问题$off支持三种移除粒度:全部、某事件、特定回调- 组件事件通过
on选项传递,子组件通过$emit触发 - 修饰符本质是事件对象方法的包装处理
$once通过包装函数实现自动移除
📝 发现内容有误?点击此处直接编辑