本章節需要掌握組件基礎
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)">
`
})
在這個案例中,子組件會同步接收titleprop,發射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 {width:60px;}
</style>
</html>
處理v-model修飾符
在前面我們學習輸入綁定時,v-model有一些內置的修飾符如.trim,.number,.lazy等。有些情形,你也想增加一些自定義的修飾符。
讓我們來創建一個capitalize修飾符,使用v-model綁定將輸入首字母轉換為大寫。
組件v-model修飾符通過modelModifersprop提供給組件。下面的例子,我們創建了一個含有默認為空對象 的modelModifiersprop組件。
注意在組件created生命周期勾子觸發器中,modelModifiersprop包含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 }
}
})
