$event详解,provide和inject传递函数时的注意点
欢迎来到我的博客!
使用 Markdown 格式编写。
今天学到了什么
- $event
- provide 和inject
因为在 Vue 里,$event 不是“你代码作用域里随便来的一个变量”,而是 模板编译器在特定语境下约定的一个“特殊占位符”。
你可以把它理解为:只要你写在 v-on(@xxx)或 v-model 的更新表达式里,Vue 就知道这是一个“事件处理表达式”,并且会自动给你生成一个带参数的函数;这个参数就叫 $event。
关键点:@click="..." 里的内容不是直接执行,而是会被“编译成函数”
例如:
<button @click="count++">+</button>你写的是一段表达式,但 Vue 编译时会把它变成类似这样的处理函数(概念化):
onClick: ($event) => { count++}所以 $event 之所以“像形参”,是因为它真的是编译后函数的形参名。
那 Vue 怎么知道你用的 $event 是“形参”而不是别的变量?
因为 Vue 在编译模板表达式时,有一条规则:
- 如果表达式处在事件监听语境(
v-on/@)中 - 编译器会生成 handler 函数,并把第一个参数命名为
$event - 同时在这段表达式里,
$event会被当作“这个 handler 的入参引用”
换句话说:这是语法规则,不是运行时猜的。
类比一下就像 for (const item of list):item 为什么代表循环变量?因为语言规定了它在这个语法里就是。
对比三种写法,你会立刻明白作用域是谁给的
A. 直接传函数引用(没有 $event)
<button @click="onClick">点我</button>编译后大概是:
onClick: onClick // 事件对象会由浏览器传给 onClick此时 是否接收事件对象,取决于你函数签名:
function onClick(e: MouseEvent) { ... }B. 模板内联表达式,用 $event
<button @click="onClick($event)">点我</button>编译后大概是:
onClick: ($event) => onClick($event)此时 $event 明确就是编译器生成的形参。
C. 你自己显式声明形参(你想叫啥都行)
<button @click="(e) => onClick(e)">点我</button>这里压根不需要 $event,因为你自己写了 e 这个形参。
那 $event 是不是“全局变量”?我能在别的地方用吗?
不是全局变量。它只在这些地方有特殊含义:
v-on/@xxx的内联表达式中v-model的更新表达式中(例如@update:modelValue="x = $event")
在普通的 <script setup> 里直接写 $event 是没有意义的,也不会自动存在。
更本质的一句话
Vue 不是“识别 $event 这个名字”这么简单,而是:在事件绑定的语境下,Vue 必须生成一个 handler 函数;生成函数就必须有参数;$event 只是这个参数在模板表达式里的默认名字。
如果你愿意,我可以拿你截图里的这句:
@update:modelValue="username = $event"
把它“从模板 → 编译后 handler → 运行时调用时 $event 具体是什么值”完整串一遍(包含 v-model 触发 update:modelValue 的那条链路),你会更牢。

provide 和inject
对象里的所有键值,批量绑定到当前组件/元素上,等执行流程可以按这条链路理解,核心是“孙组件拿到的是爷爷组件里定义的那个函数本身”,不是重新创建了一个新函数。
- 爷爷组件在自己的作用域里定义了 updateMoney,并且这里的 money 也是同一个作用域里的 ref。
- 爷爷组件把 updateMoney 连同 money 一起通过 provide 往下传。
- 子组件再把这些内容透传给孙组件,孙组件通过 inject 拿到 updateMoney。
- 孙组件点击按钮时,调用的其实就是爷爷组件提供的那个 updateMoney 函数引用。
- 函数执行时,JavaScript 会沿着它创建时的词法作用域去找变量,所以 updateMoney 能直接访问到定义它时所在作用域里的 money。
所以“怎么拿到 money 变量”的答案是:不是调用时临时去找的,而是 updateMoney 在定义时就已经把外层作用域里的 money 捕获住了,这就是闭包。
但真正让孙组件能触发它的,是 provide/inject 把这个函数引用传到了孙组件。
你现在看的这个链路里,相关的透传点在 Child.vue;真正定义 updateMoney 的地方在爷爷组件里。若你愿意,我可以继续把“函数创建时的作用域”画成一条很直观的调用链。
赞助支持
如果这篇文章对你有帮助,欢迎赞助支持!
部分内容可能已过时