Vue3 自定义事件

事件名

与组件和 prop 一样,事件名提供了自动的大小写转换。如果在子组件中触发一个以 camelCase (驼峰式命名) 命名的事件,你将可以在父组件中添加一个 kebab-case (短横线分隔命名) 的监听器。

1
this.$emit('myEvent')
1
<my-component @my-event="doSomething"></my-component>

定义自定义事件

通过 emits 选项在组件上定义发出的事件

1
2
3
app.component('custom-form', {
emits: ['inFocus', 'submit']
})

当在 emits 选项中定义了原生事件 (如 click) 时,将使用组件中的事件替代原生事件侦听器。

验证抛出的事件

与 prop 类型验证类似,如果使用对象语法而不是数组语法定义发出的事件,则可以对它进行验证。

要添加验证,请为事件分配一个函数,该函数接收传递给 $emit 调用的参数,并返回一个布尔值以指示事件是否有效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
app.component('custom-form', {
emits: {
// 没有验证
click: null,

// 验证 submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm(email, password) {
this.$emit('submit', { email, password })
}
}
})

v-model 参数

默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。我们可以通过向 v-model 传递参数来修改这些名称:

1
<my-component v-model:title="bookTitle"></my-component>

子组件将需要一个 title prop 并发出 update:title 事件来进行同步:

1
2
3
4
5
6
7
8
9
10
11
12
app.component('my-component', {
props: {
title: String
},
emits: ['update:title'],
template: `
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)">
`
})

多个 v-model 绑定

每个 v-model 将同步到不同的 prop,而不需要在组件中添加额外的选项

1
2
3
4
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
emits: ['update:firstName', 'update:lastName'],
template: `
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)">

<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)">
`
})

处理 v-model 修饰符

创建一个示例自定义修饰符 capitalize,它将 v-model 绑定提供的字符串的第一个字母大写。

添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件。在下面的示例中,我们创建了一个组件,其中包含默认为空对象的 modelModifiers prop。

TIP

当组件的 created 生命周期钩子触发时,modelModifiers prop 会包含 capitalize,且其值为 true——因为 capitalize 被设置在了写为 v-model.capitalize="myText"v-model 绑定上。

1
<my-component v-model.capitalize="myText"></my-component>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.component('my-component', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
emits: ['update:modelValue'],
template: `
<input type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)">
`,
created() {
console.log(this.modelModifiers) // { capitalize: true }
}
})

已经设置了 prop,我们可以检查 modelModifiers 对象键并编写一个处理器来更改发出的值。在下面的代码中,每当 <input/> 元素触发 input 事件时,我们都将字符串大写。

1
2
3
4
<div id="app">
<my-component v-model.capitalize="myText"></my-component>
{{ myText }}
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const app = Vue.createApp({
data() {
return {
myText: ''
}
}
})

app.component('my-component', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
emits: ['update:modelValue'],
methods: {
emitValue(e) {
let value = e.target.value
if (this.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:modelValue', value)
}
},
template: `<input
type="text"
:value="modelValue"
@input="emitValue">`
})

app.mount('#app')

对于带参数的 v-model 绑定,生成的 prop 名称将为 arg + "Modifiers"

1
<my-component v-model:description.capitalize="myText"></my-component>
1
2
3
4
5
6
7
8
9
10
11
12
app.component('my-component', {
props: ['description', 'descriptionModifiers'],
emits: ['update:description'],
template: `
<input type="text"
:value="description"
@input="$emit('update:description', $event.target.value)">
`,
created() {
console.log(this.descriptionModifiers) // { capitalize: true }
}
})