Vue3 源码学习🗣 组件emit

4,092 阅读1分钟

前言

vue3增加了一个emits选项,而且之前都是调用全局钩子函数$emit来出发事件,我们使用composition API 之后,setup里也可以通过上下文暴露出来的ctx.emit来触发事件,可谓玩法姿势又增多了,我们来总结一下吧。

ps: 如果懒得看文章 可以直接点击这里查看例子

进入主题

首先,之前的方法也是可以用的,区别就是 在vue2中,我们是把$emit绑定到了全局实例上,这里就是当前组件的实例上

比如

 const Foo = defineComponent({
      render() {},
      created() {
        // the `emit` function is bound on component instances
        this.$emit('foo')
        this.$emit('bar')
        this.$emit('!baz')
      }
    })
    

这些都是可以触发事件的,这里我发现了一个并不常见的写法**!baz**,在事件触发内部会先把事件名转化成小驼峰,

  // convert handler name to camelCase. See issue #2249
  let handlerName = toHandlerKey(camelize(event)) // toHandlerKey把首字母大写然后加on前缀
  let handler = props[handlerName]
  
  const camelize = (str)=>str.replace(/-(\w)/g,(_, c) => (c ? c.toUpperCase() : ''))
  //比如event 是!baz  handlerName = on!baz 
  //这个camelize主要处理了 -x => X

所以调用的时候我们需要传入对应的'onXXX'事件 所以了解了等价写法,下面这些都是等价的

// 触发
emit('foo-bar')
emit('fooBar')

// 属性
onFooBar


// v-model相关

emit(update:fooProp)
'onUpdate:fooProp' === 'onUpdate:foo-prop'

emits选项

emits用于声明当前组件实例上可以触发的事件 声明的好处 可以让我们在emit()的时候有更好的代码补全,而且如果你触发了一个没有在emits中声明的事件,会有警告,当然ts的话也会有报错,比如下面的

emits: ["welcome"],
setup(props, ctx) {
    ctx.emit("hh"); // Argument of type '"hh"' is not assignable to parameter of type '"welcome"'
              ~~~~
},

但是 当我们不在emits声明任何事件的时候,我们是自由的,可以自由触发任意事件,所以这个选项的使用见仁见智

props

我们注意到我们声明的事件名, 父组件在传入的时候都会在前面加上一个on, 所以onXXX (必须小驼峰) 的prop就是XXX事件的等价写法

 props: ['onFoo'],
emits: [],
render() {},
created() {
  this.$emit('foo')
}

所以我们定义了一个等价的onFoo,上面的代码也是不会报错的,但是在jsx/tsx中,我们必须显式的在组件中定义props才能传入对应的自定义组件 所以当你这么使用的时候 ,通过属性和方法都可以调用

// 子组件
const child = defineComponent({
 props: ['onFoo'],// 用['on-foo'] 也可
emits: [],
render() {},
created() {
  this.$emit('foo')
}

})

// parent.vue
<Child :onFoo="foo" > // 1  
function foo(){alert('1')}

//jsx
<Child onFoo={foo} > 

! 必须是小驼峰式,这里和:on-foo不等价