Vue 事件系统

在模板里写一行 @click="handler",背后却是一套从编译器到渲染器、再到 DOM 事件再绑定的精密流水线。本文带你拆解事件在 Vue 内部的完整生命周期。

一、事件的「身份」:props 的一种

模板:

vue 复制代码
<p @click="clickHandler">text</p>

经过编译器后,事件被翻译成虚拟节点里的一个普通属性:

js 复制代码
const vnode = {
  type: 'p',
  props: {
    onClick: clickHandler   // 注意首字母大写
  },
  children: 'text'
}

因此,在渲染器眼里,事件与 idclass 没有本质区别,只是 key 以 on 开头。

二、首次挂载

渲染器在 patchProps 阶段发现 props key 以 on 开头,就进入事件分支:

js 复制代码
if (/^on/.test(key)) {
  const eventName = key.slice(2).toLowerCase() // onClick -> click
  el.addEventListener(eventName, nextValue)
}

此时事件处理器被直接绑定到 DOM 节点,首屏即生效。

三、更新策略

如果每次更新都「先 remove 再 add」,会带来无谓的性能损耗。Vue 采用 invoker 代理 解决这一问题:

  1. 在元素上新建一个隐藏属性 el._vei(Vue Event Invoker),它是一个对象。
  2. 对象的键是事件名,值是一个固定引用的代理函数 invoker
  3. invoker 内部持有真正的事件数组或函数,更新时只需替换引用,无需重新绑定。

核心逻辑:

js 复制代码
const invokers = el._vei || (el._vei = {})
let invoker = invokers[eventName]

if (!invoker) {
  invoker = invokers[eventName] = (e) => {
    const fns = invoker.value
    Array.isArray(fns) ? fns.forEach(fn => fn(e)) : fns(e)
  }
  el.addEventListener(eventName, invoker)
}
invoker.value = nextValue   // 指向最新处理器

nextValue 是空数组或 null 时,直接 removeEventListener 即可卸载。

四、多处理器与同类型多事件

模板支持数组语法:

vue 复制代码
<button @click="[fn1, fn2]" @contextmenu="fn3" />

invoker 内部检测到 Array.isArray(value),就循环执行所有函数,实现「同类型多处理器」零成本扩展。

总结

Vue 的事件系统用一句话概括:

事件即 props,props 即数据,数据变化即更新,更新即最小化 DOM 操作。

理解 invoker 代理模式,你就拥有了在源码层面定位事件问题、优化事件性能的全部钥匙。

相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A1 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
晨米酱1 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
honkun61 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
彧azz1 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试