本章节需要掌握组件基础
emit我译成发射,觉得发射这个词比较形象的形容将子组件事件发射出来的一个动作。
事件名
像组件和props
,事件名也会进行自动转换,如果你在子组件里发射一个驼峰命名的事件,你就可以在父组件中添加一个短横线分隔的监听:
this.$emit('myEvent')
<my-component @my-event="doSomething"></my-component>
因为有props
情形存在,模板内DOM议使用短横线分隔命名方式。如果你使用字符串模板,则不存在这个限制。
自定义事件定义
自定义事件发射(请子组件传递到父组件)可以通过emits
选项:
app.component('custom-form', {
emits: ['inFocus', 'submit']
})
当在emits
中定义了一个原生事件(如click
)时,组件事件将会代替原生事件监听。
提示
推荐定义所有已发射的事件,以便记录组件是如何工作的
验证已射事件
同prop
类型验证一样,一个已发射事件如果通过对象语法而不是数组语法,也可以被验证。
添加一个验证,事件会分配一个函数来接收参数,并传递给$emit
调用,返回一个布尔值来确认事件是否有效。
app.component('custom-form', {
emits: {
// 无验证
click: null,
// 验证提交事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('不合法的 submit 事件加载!')
return false
}
}
},
methods: {
submitForm() {
this.$emit('submit', { email, password })
}
}
})
v-model
参数
默认组件的v-model
使用modelValue
作为prop和update:modelValue
作为事件。我们可以传递一个参数给v-model
修改这些名称:
<my-component v-model:title="bookTitle"></my-component>
app.component('my-component', {
props: {
title: String
},
emits: ['update:title'],
template: `
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)">
`
})
在这个案例中,子组件会同步接收title
prop,发射update:title
事件:
app.component('my-component', {
props: {
title: String
},
emits: ['update:title'],
template: `
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)">
`
})
<my-component v-model:title="bookTitle"></my-component>
多v-model
绑定
正如前面所学习v-model
参数,我们可以为同一个组件实例添加多个v-model参数。
每个v-model
可以与不同的prop
相同步,组件不需要额外的选项:
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
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)">
`
})
我们来写一个完整点的例子,实现一个区号-座机输入组件,结果如下图:
代码如下:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>component v-model</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<tel-input v-model:tel-number="telNumber" v-model:area-number="areaNumber"></tel-input>
<p>
{{areaNumber}}-{{telNumber}}
</p>
</div>
</body>
<script type="text/javascript">
const data_and_methods = {
data() {
return {
telNumber: '',
areaNumber: ''
}
},
methods: {
}
}
const app = Vue.createApp(data_and_methods)
app.component('tel-input',{
props: {
areaNumber:String,
telNumber:String
},
emits: ['update:areaNumber','update:telNumber'],
template: `
<input placeholder="区号" type="number" class="area-number" :value="areaNumber" @input="$emit('update:areaNumber',$event.target.value)" />
-
<input placeholder="座机号码" type="number" class="tel-number" :value="telNumber" @input="$emit('update:telNumber',$event.target.value)" />
`
})
app.mount("#app")
</script>
<style type="text/css">
.area-number {60px;}
</style>
</html>
处理v-model
修饰符
在前面我们学习输入绑定时,v-model
有一些内置的修饰符如.trim
,.number
,.lazy
等。有些情形,你也想增加一些自定义的修饰符。
让我们来创建一个capitalize
修饰符,使用v-model
绑定将输入首字母转换为大写。
组件v-model
修饰符通过modelModifers
prop提供给组件。下面的例子,我们创建了一个含有默认为空对象 的modelModifiers
prop组件。
注意在组件created
生命周期勾子触发器中,modelModifiers
prop包含capitalize
且值是true
- 当它被设置在v-model
绑定v-model.capitalize="myText"
。
<my-component v-model.capitalize="myText"></my-component>
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
对象的键,然后通过方法处理要发射出去的值。下面我们来把输入框的首字母转换为大写:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>component v-model modifiers</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<my-component v-model.capitalize="myText"></my-component>
<p>{{ myText }}</p>
</div>
</body>
<script type="text/javascript">
const data_and_methods = {
data() {
return {
myText: ''
}
}
}
const app = Vue.createApp(data_and_methods)
app.component('my-component',{
props: {
modelValue: String,
modelModifiers: {
default: () => ({})
}
},
emits: ['update:modelValue'],
template: `
<input type="text"
:value="modelValue"
@input="emitValue" />
`,
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)
}
},
created() {
console.log(this.modelModifiers)
}
})
app.mount("#app")
</script>
</html>
v-model
绑定参数,生成的prop名将会是arg+"modelModifiers"
:
<my-component v-model:description.capitalize="myText"></my-component>
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 }
}
})