- 官方文档链接:https://cn.vuejs.org/v2/guide/components-custom-events.html
1.事件名
- 不同于组件和 prop,事件名不存在任何自动化的大小写转换,触发的事件名需要完全匹配监听这个时间所用的名称;
// 提交事件
this.$emit('myEvent')
<!-- 写成kebab-case没有任何效果 -->
<my-component v-on:my-event="doSomething"></my-component>
- 事件名不会被用作一个JavaScript变量名或property名;
- v-on事件监听器在DOM模板中会被自动转换为全小写(因为HTML是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到;
- 建议始终使用kebab-case的事件名而不要使用camelCase命名;
2.自定义组件的v-model
- 一个组件上的 v-model 默认会利用名为 value 的 属性和名为 input 的事件;
- 单选框、复选框等类型的输入控件可能会将 value 属性用于不同的目的。model 选项可以用来避免这样的冲突:
Vue.component('base-checkbox', {
// 自定义model属性
model: {
prop: 'checked', //对应的属性名,需要在组件的props选项里声明checked这个属性
event: 'change' //对应的事件
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
- lovingVue 的值将会传入这个名为 checked 的 prop。同时当 触发一个 change 事件并附带一个新的值的时候,这个 lovingVue 的 property 将会被更新;
<!-- 使用 -->
<base-checkbox v-model="lovingVue"></base-checkbox>
3.将原生事件绑定到组件
(1) 场景
- 想要在一个组件的根元素上直接监听一个原生事件,可以使用v-on的.native修饰符;
(2) 使用
- 如果根元素上不支持该事件,则.native监听器将静默失败。不会产生任何报错,但是对应的事件处理函数也不会执行;
- 举例:在<base-input>组件的根元素上监听focus事件;但是base-input组件根元素为label,不支持focus事件:
<!-- base-input组件内部 -->
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
<!-- 使用base-input组件 -->
<base-input v-on:focus.native="onFocus"></base-input>
(3) $listeners属性
- 解决.native在根元素不支持事件时静默失败的问题;
- 该属性是一个对象,里面包含了作用在这个组件上的所有监听器,如:
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
- 可以配合
v-on="$listeners"
将所有的事件监听器指向这个组件的某个特定的子元素; - 对于类似 <input> 的你希望它也可以配合 v-model 工作的组件来说,为这些监听器创建一个类似下述 inputListeners 的计算属性通常是非常有用的:
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` 将所有的对象合并为一个新对象
// 第二个参数:从父级添加所有的监听器
// 第三个参数:添加自定义监听器或覆写一些监听器的行为,这里确保组件配合 `v-model` 的工作
return Object.assign({}, this.$listeners, { 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> 元素一样使用了:所有跟它相同的 attribute 和监听器都可以工作,不必再使用 .native 监听器。
4. .sync
修饰符
## (1) 场景 - 在某些情况下,可能需要对一个属性进行“双向绑定”,但真正的双向绑定会带来维护上的问题,因为自组建可以变更父组件,且在父组件和自组件都没有明显的变更来源。 - 通常会使用`update:myPropName`的模式触发事件来维护数据。如:在一个包含`title`属性的组件中,可以用`this.$emit('update:title', newTitle)`来向父组件传递要修改属性的意图,然后在父组件监听该事件并更新一个本地的数据属性:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
- 为了方便起见,vue为这种模式提供一个缩写,即
.sync
修饰符;
(2) 使用
<component v-bind.sync="prop"></componentv>
<text-document v-bind:title.sync="doc.title"></text-document>
- 带有
.sync
修饰符的 v-bind 不能和表达式一起使用 (例如v-bind:title.sync="doc.title + '!' "
是无效的)。取而代之的是,只能提供想要绑定的 属性名,类似 v-model; - 当用一个对象同时设置多个property时,可以将
.sync
修饰符和v-bind
配合使用;此时会把对象中的每一个property都作为独立的rpop传进去,然后各自添加用于更新的v-on
监听器;
<!-- doc对象中的每个属性都作为一个独立的prop传递到text-document中 -->
<text-document v-bind.sync="doc"></text-document>
- 【注意】:将
v-bind.sync
用在一个字面量的对象上,例如v-bind.sync="{ title: doc.title }"
,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑;