深入理解Vue.js源码之事件机制
发布时间 - 2026-01-11 03:26:05 点击率:次写在前面

因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出。
文章的原地址:https://github.com/answershuto/learnVue。
在学习过程中,为Vue加上了中文的注释https://github.com/answershuto/learnVue/tree/master/vue-src,希望可以对其他想学习Vue源码的小伙伴有所帮助。
可能会有理解存在偏差的地方,欢迎提issue指出,共同学习,共同进步。
Vue事件API
众所周知,Vue.js为我们提供了四个事件API,分别是$on](https://cn.vuejs.org/v2/api/#vm-on-event-callback),[$once,$off](https://cn.vuejs.org/v2/api/#vm-off-event-callback),[$emit。
初始化事件
初始化事件在vm上创建一个_events对象,用来存放事件。_events的内容如下:
{
eventName: [func1, func2, func3]
}
存放事件名以及对应执行方法。
/*初始化事件*/
export function initEvents (vm: Component) {
/*在vm上创建一个_events对象,用来存放事件。*/
vm._events = Object.create(null)
/*这个bool标志位来表明是否存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
vm._hasHookEvent = false
// init parent attached events
/*初始化父组件attach的事件*/
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
$on
$on方法用来在vm实例上监听一个自定义事件,该事件可用$emit触发。
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
/*如果是数组的时候,则递归$on,为每一个成员都绑定上方法*/
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)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
/*这里在注册事件的时候标记bool值也就是个标志位来表明存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
$once
$once监听一个只能触发一次的事件,在触发以后会自动移除该事件。
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
/*在第一次执行的时候将该事件销毁*/
vm.$off(event, on)
/*执行注册的方法*/
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
$off
$off用来移除自定义事件
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
/*如果不传参数则注销所有事件*/
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
/*如果event是数组则递归注销事件*/
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event]
/*Github:https://github.com/answershuto*/
/*本身不存在该事件则直接返回*/
if (!cbs) {
return vm
}
/*如果只传了event参数则注销该event方法下的所有方法*/
if (arguments.length === 1) {
vm._events[event] = null
return vm
}
// specific handler
/*遍历寻找对应方法并删除*/
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
$emit
$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]) {
tip(
`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++) {
cbs[i].apply(vm, args)
}
}
return vm
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Vue.js
# 事件机制
# 浅谈Vue2.0父子组件间事件派发机制
# 一篇文章带你了解vue.js的事件循环机制
# vue @click与@click.native
# 及vue事件机制的使用分析
# 递归
# 自定义
# 遍历
# 而不
# 这样做
# 方法来
# 创建一个
# 移除
# 可以减少
# 是个
# 会有
# 也就
# 上了
# 感兴趣
# 对其
# 不存在
# 花了
# 他想
# 这几个
# 转换成
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel storage目录权限问题_Laravel文件写入权限设置
javascript中的try catch异常捕获机制用法分析
Gemini怎么用新功能实时问答_Gemini实时问答使用【步骤】
Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】
Windows家庭版如何开启组策略(gpedit.msc)?(安装方法)
Linux系统运维自动化项目教程_Ansible批量管理实战
如何用低价快速搭建高质量网站?
JavaScript中的标签模板是什么_它如何扩展字符串功能
Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】
详解CentOS6.5 安装 MySQL5.1.71的方法
Laravel怎么生成URL_Laravel路由命名与URL生成函数详解
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
Laravel Pest测试框架怎么用_从PHPUnit转向Pest的Laravel测试教程
简历在线制作网站免费版,如何创建个人简历?
javascript读取文本节点方法小结
UC浏览器如何设置启动页 UC浏览器启动页设置方法
HTML5空格在Angular项目里怎么处理_Angular中空格的渲染问题【详解】
Laravel怎么实现软删除SoftDeletes_Laravel模型回收站功能与数据恢复【步骤】
Midjourney怎样加参数调细节_Midjourney参数调整技巧【指南】
如何在IIS中新建站点并配置端口与IP地址?
标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?
Win11应用商店下载慢怎么办 Win11更改DNS提速下载【修复】
如何实现javascript表单验证_正则表达式有哪些实用技巧
网站优化排名时,需要考虑哪些问题呢?
Claude怎样写结构化提示词_Claude结构化提示词写法【教程】
手机软键盘弹出时影响布局的解决方法
今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】
免费网站制作appp,免费制作app哪个平台好?
详解Android图表 MPAndroidChart折线图
Laravel怎么创建自己的包(Package)_Laravel扩展包开发入门到发布
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
html5如何实现懒加载图片_ intersectionobserver api用法【教程】
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
昵图网官方站入口 昵图网素材图库官网入口
厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?
Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复
深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?
HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】
如何在IIS7上新建站点并设置安全权限?
Laravel如何记录自定义日志?(Log频道配置)
高配服务器限时抢购:企业级配置与回收服务一站式优惠方案
微信公众帐号开发教程之图文消息全攻略
三星网站视频制作教程下载,三星w23网页如何全屏?
Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践
Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道
Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
简单实现Android文件上传
上一篇:linux是否支持动态路由
上一篇:linux是否支持动态路由

