自訂事件
該頁面假設你已經閱讀過了元件基礎。如果你還對組件不太了解,推薦你先閱讀它。
目錄
.sync 修飾符
事件名稱
不同於元件和prop,事件名稱不存在任何自動化的大小寫轉換。而是觸發的事件名需要完全符合監聽這個事件所用的名稱。舉個例子,如果觸發一個camelCase 名字的事件:
則監聽這個名字的kebab-case 版本是不會有任何效果的:this.$emit('myEvent')
不同於元件和prop,事件名不會被用作一個JavaScript 變數名或屬性名,所以就沒有理由使用camelCase 或PascalCase 了。且<!-- 没有效果 -->
<my-component v-on:my-event="doSomething"></my-component>
事件監聽器在DOM 範本中會自動轉換為全小寫(因為HTML 是大小寫不敏感的),所以v-on:myEvent 將會變成
v-on:myevent——導致
不可能被監聽到。
總是使用 kebab-case 的事件名稱。
自訂元件的
v-model
2.2.0 新增
一個元件上的v-model 預設會利用名為
value
的prop 和名稱為
的事件,但是像單選框、複選框等類型的輸入控制項可能會將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
的值將會傳入這個名為
的prop。同時當<base-checkbox>
的屬性將會被更新。觸發一個
change事件並附帶一個新的值的時候,這個
lovingVue
注意你仍需要在元件的
選項裡宣告 checked 這個 prop。
將原生事件綁定到元件
<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 }”
,是無法正常工作的,因為在解析一個像這樣的複雜表達式的時候,有很多邊緣情況需要考慮。