自定義事件
事件名
不同於組件和 prop,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名需要完全匹配監聽這個事件所用的名稱。舉個例子,如果觸發一個 camelCase 名字的事件:
this.$emit('myEvent')
則監聽這個名字的 kebab-case 版本是不會有任何效果的:
<!-- 沒有效果 -->
<my-component v-on:my-event="doSomething"></my-component>
不同於組件和 prop,事件名不會被用作一個 JavaScript 變量名或屬性名,所以就沒有理由使用 camelCase 或 PascalCase了。並且 v-on 事件監聽器在 DOM 模板中會被自動轉換成全小寫(因爲HTML是大小寫不敏感的),所以 v-on:myEvent 將會變成 v-on:myevent --這將會導致 myEvent 不可能被監聽到。
因此,我們推薦你始終使用 kebab-case 的事件名。
自定義組件的 v-model
2.2.0+ 新增
一個組件上的 v-model 默認會被利用名爲 value 的 prop 和 名爲 input 的事件,但是像單選框、複選框等類型的輸入空間可能會將 value 用於不同的目的。 model 選項可以用來避免這樣的衝突:
Vue.component('base-checkbox',{
model:{
prop:'checked',
event:'change'
},
props:{
checked:Boolean
},
template:`
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
現在在這個組件上時引用 v-model 的時候:
<base-checkbox v-model='lovingVue'></base-checkbox>
這裏的 lovingVue 的值將會被傳到 名爲 checked 的 prop。同時,當<base-checkbox> 觸發一個 change 事件並附帶一個新的值的時候,這個 lovingVue 的屬性將會被更新。
注意:我們仍然需要在組件的 props 選項裏聲明 checked 這個 prop。
將原生事件綁定到組件
你可能有很多次想要在一個組件的根元素上直接監聽一個原生事件。這時,你可以使用 v-on
的 .native
修飾符:
<base-input v-on:focus.native="onFocus"></base-input>
在有的時候這是很有用的,不過在你嘗試監聽一個類似 <input>
的非常特定的元素時,這並不是個好主意。比如上述 <base-input>
組件可能做了如下重構,所以根元素實際上是一個 <label>
元素:
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
這時,父級的 .native
監聽器將靜默失敗。它不會產生任何報錯,但是 onFocus
處理函數不會如你預期地被調用。
爲了解決這個問題,Vue 提供了一個 $listeners
屬性,它是一個對象,裏面包含了作用在這個組件上的所有監聽器。例如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
有了這個 $listeners
屬性,你就可以配合 v-on="$listeners"
將所有的事件監聽器指向這個組件的某個特定的子元素。對於類似 <input>
的你希望它也可以配合 v-model
工作的組件來說,爲這些監聽器創建一個類似下述 inputListeners
的計算屬性通常是非常有用的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 將所有的對象合併爲一個新對象
return Object.assign({},
// 我們從父級添加所有的監聽器
this.$listeners,
// 然後我們添加自定義監聽器,
// 或覆寫一些監聽器的行爲
{
// 這裏確保組件配合 `v-model` 的工作
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
現在 <base-input>
組件是一個完全透明的包裹器了,也就是說它可以完全像一個普通的 <input>
元素一樣使用了:所有跟它相同的特性和監聽器的都可以工作。
.sync
修飾符
2.3.0+ 新增
在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”。不幸的是,真正的雙向綁定會帶來維護上的問題,因爲子組件可以修改父組件,且在父組件和子組件都沒有明顯的改動來源。
這也是爲什麼我們推薦以 update:myPropName
的模式觸發事件取而代之。舉個例子,在一個包含 title
prop 的假設的組件中,我們可以用以下方法表達對其賦新值的意圖:
this.$emit('update:title', newTitle)
然後父組件可以監聽那個事件並根據需要更新一個本地的數據屬性。例如:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
爲了方便起見,我們爲這種模式提供一個縮寫,即 .sync
修飾符:
<text-document v-bind:title.sync="doc.title"></text-document>
注意帶有 .sync
修飾符的 v-bind
不能和表達式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’”
是無效的)。取而代之的是,你只能提供你想要綁定的屬性名,類似 v-model
。
當我們用一個對象同時設置多個 prop 的時候,也可以將這個 .sync
修飾符和 v-bind
配合使用:
<text-document v-bind.sync="doc"></text-document>
這樣會把 doc
對象中的每一個屬性 (如 title
) 都作爲一個獨立的 prop 傳進去,然後各自添加用於更新的 v-on
監聽器。
將 v-bind.sync
用在一個字面量的對象上,例如 v-bind.sync=”{ title: doc.title }”
,是無法正常工作的,因爲在解析一個像這樣的複雜表達式的時候,有很多邊緣情況需要考慮。