> 該頁面假設你已經閱讀過了[組件基礎](818090)。如果你還對組件不太了解,推薦你先閱讀它。
## 事件名
不同于組件和 prop,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名需要完全匹配監聽這個事件所用的名稱。舉個例子,如果觸發一個 camelCase 名字的事件:
```js
this.$emit('myEvent')
```
則監聽這個名字的 kebab-case 版本是不會有任何效果的:
```html
<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` 特性用于[不同的目的](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#Value)。`model` 選項可以用來避免這樣的沖突:
```js
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` 的時候:
```html
<base-checkbox v-model="lovingVue"></base-checkbox>
```
這里的 `lovingVue` 的值將會傳入這個名為 `checked` 的 prop。同時當 `<base-checkbox>` 觸發一個 `change` 事件并附帶一個新的值的時候,這個 `lovingVue` 的屬性將會被更新。
<p class="tip">注意你仍然需要在組件的 <code>props</code> 選項里聲明 <code>checked</code> 這個 prop。</p>
## 將原生事件綁定到組件
你可能有很多次想要在一個組件的根元素上直接監聽一個原生事件。這時,你可以使用 `v-on` 的 `.native` 修飾符:
```html
<base-input v-on:focus.native="onFocus"></base-input>
```
在有的時候這是很有用的,不過在你嘗試監聽一個類似 `<input>` 的非常特定的元素時,這并不是個好主意。比如上述 `<base-input>` 組件可能做了如下重構,所以根元素實際上是一個 `<label>` 元素:
```html
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
```
這時,父級的 `.native` 監聽器將靜默失敗。它不會產生任何報錯,但是 `onFocus` 處理函數不會如你預期地被調用。
為了解決這個問題,Vue 提供了一個 `$listeners` 屬性,它是一個對象,里面包含了作用在這個組件上的所有監聽器。例如:
```js
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
```
有了這個 `$listeners` 屬性,你就可以配合 `v-on="$listeners"` 將所有的事件監聽器指向這個組件的某個特定的子元素。對于類似 `<input>` 的你希望它也可以配合 `v-model` 工作的組件來說,為這些監聽器創建一個類似下述 `inputListeners` 的計算屬性通常是非常有用的:
```js
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 的假設的組件中,我們可以用以下方法表達對其賦新值的意圖:
```js
this.$emit('update:title', newTitle)
```
然后父組件可以監聽那個事件并根據需要更新一個本地的數據屬性。例如:
```html
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
```
為了方便起見,我們為這種模式提供一個縮寫,即 `.sync` 修飾符:
```html
<text-document v-bind:title.sync="doc.title"></text-document>
```
<p class="tip">注意帶有 <code>.sync</code> 修飾符的 <code>v-bind</code> <strong>不能</strong>和表達式一起使用 (例如 <code>v-bind:title.sync="doc.title + '!'"</code> 是無效的)。取而代之的是,你只能提供你想要綁定的屬性名,類似 <code>v-model</code>。</p>
當我們用一個對象同時設置多個 prop 的時候,也可以將這個 `.sync` 修飾符和 `v-bind` 配合使用:
```html
<text-document v-bind.sync="doc"></text-document>
```
這樣會把 `doc` 對象中的每一個屬性 (如 `title`) 都作為一個獨立的 prop 傳進去,然后各自添加用于更新的 `v-on` 監聽器。
<p class="tip">將 <code>v-bind.sync</code> 用在一個字面量的對象上,例如 <code>v-bind.sync="{ title: doc.title }"</code>,是無法正常工作的,因為在解析一個像這樣的復雜表達式的時候,有很多邊緣情況需要考慮。</p>
- 寫在前面
- 基礎
- 安裝
- 介紹
- Vue實例
- 模板語法
- 計算屬性和偵聽器
- Class 與 Style 綁定
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入綁定
- 組件基礎
- 深入了解組件
- 組件注冊
- Prop
- 自定義事件
- 插槽
- 動態組件 & 異步組件
- 處理邊界情況
- 過渡 & 動畫
- 進入/離開 & 列表過渡
- 狀態過渡
- 可復用性 & 組合
- 混入
- 自定義指令
- 渲染函數 & JSX
- 插件
- 過濾器
- 工具
- 生產環境部署
- 單文件組件
- 單元測試
- TypeScript 支持
- 規模化
- 路由
- 狀態管理
- 服務端渲染
- 內在
- 深入響應式原理
- 遷移
- 從 Vue 1.x 遷移
- 從 Vue Router 0.7.x 遷移
- 從 Vuex 0.6.x 遷移到 1.0
- 更多
- 對比其他框架
- 加入 Vue.js 社區
- 開發團隊