在 setupContext 中提供 emit 并触发父组件传入的事件
2025-10-29 11:35:33
上一节实现了组件 props 的更新。
除了接收 props,子组件还需要能够向父组件触发事件。
import { createApp, h } from '../dist/vue.esm.js'
const Child = {
setup(props, { emit }) {
return () => {
return h(
'button',
{
onClick() {
emit('fn', 1, 2, 3, 4)
}
},
'child'
)
}
}
}
const Com = {
setup() {
return () => {
return h(Child, {
onFn(a, b, c, d) {
console.log('fn', a, b, c, d)
}
})
}
}
}
createApp(Com, { msg: '消息' }).mount('#app')
点击子组件中的按钮时,会执行:
emit('fn', 1, 2, 3, 4)
最终触发父组件传入的 onFn:
onFn(a, b, c, d)
{
console.log('fn', a, b, c, d)
}
组件事件的触发流程为:
子组件调用 emit('fn')
-> 将事件名 fn 转换成 onFn
-> 从组件 vnode.props 中取出 onFn
-> 执行
实现
1. 在 setup 中提供 emit
emit 是通过 setup 的第二个参数传进去的。
之前的 setupContext 只提供了 attrs:
function createSetupContext(instance) {
return {
attrs: instance.attrs
}
}
现在需要在 setupContext 中增加 emit:
function createSetupContext(instance) {
return {
get attrs() {
return instance.attrs
},
emit(event, ...args) {
emit(instance, event, ...args)
}
}
}
attrs使用get,每次访问时都从当前实例上读取最新的attrsemit内部需要拿到组件实例,所以这里用闭包把instance传进去
这样,用户在 setup 中就可以通过第二个参数拿到 emit:
setup(props, { emit })
{
emit('fn')
}
2. 实现 emit
拿到事件属性名后,就可以从 instance.vnode.props 中读取对应的事件处理函数。
import { isFunction } from '@vue/shared'
function emit(instance, event, ...args) {
// 转换事件名
const eventName = `on${event[0].toUpperCase() + event.slice(1)}`
const handler = instance.vnode.props?.[eventName]
if (isFunction(handler)) {
handler(...args)
}
}
emit 中只需要关心三件事:
- 根据事件名生成事件属性名
- 从
vnode.props中取出事件处理函数 - 执行事件处理函数并透传参数
// packages/runtime-core/src/componentEmits.ts
import { isFunction, toHandlerKey } from '@vue/shared'
export function emit(instance, event, ...args) {
const eventName = `on${event[0].toUpperCase() + event.slice(1)}`
const handler = instance.vnode.props?.[eventName]
if (isFunction(handler)) {
handler(...args)
}
}
最后在创建 setupContext 时引入这个 emit:
// packages/runtime-core/src/component.ts
import { emit } from './componentEmits'
function createSetupContext(instance) {
return {
get attrs() {
return instance.attrs
},
emit(event, ...args) {
emit(instance, event, ...args)
}
}
}
这样,组件就可以通过 setup(props, { emit }) 触发父组件传入的事件。
区别在于:
- 普通
props用来给子组件传递数据 onXxx形式的props用来给子组件传递事件处理函数