1134 字
6 分钟

$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 的那条链路),你会更牢。

image-20260505090603368

provide 和inject#

对象里的所有键值,批量绑定到当前组件/元素上,等执行流程可以按这条链路理解,核心是“孙组件拿到的是爷爷组件里定义的那个函数本身”,不是重新创建了一个新函数。

  1. 爷爷组件在自己的作用域里定义了 updateMoney,并且这里的 money 也是同一个作用域里的 ref。
  2. 爷爷组件把 updateMoney 连同 money 一起通过 provide 往下传。
  3. 子组件再把这些内容透传给孙组件,孙组件通过 inject 拿到 updateMoney。
  4. 孙组件点击按钮时,调用的其实就是爷爷组件提供的那个 updateMoney 函数引用。
  5. 函数执行时,JavaScript 会沿着它创建时的词法作用域去找变量,所以 updateMoney 能直接访问到定义它时所在作用域里的 money。

所以“怎么拿到 money 变量”的答案是:不是调用时临时去找的,而是 updateMoney 在定义时就已经把外层作用域里的 money 捕获住了,这就是闭包。
但真正让孙组件能触发它的,是 provide/inject 把这个函数引用传到了孙组件。

你现在看的这个链路里,相关的透传点在 Child.vue;真正定义 updateMoney 的地方在爷爷组件里。若你愿意,我可以继续把“函数创建时的作用域”画成一条很直观的调用链。

赞助支持

如果这篇文章对你有帮助,欢迎赞助支持!

赞助
$event详解,provide和inject传递函数时的注意点
https://firefly.cuteleaf.cn/posts/study-everyday/2026-05-05今日学习-event详解provide和inject传递函数时的注意点/
作者
LJC
发布于
2026-05-05
许可协议
CC BY-NC-SA 4.0
最后更新于 2026-05-05,距今已过 119 天

部分内容可能已过时

目录