1x2 精读Vue官方文档 - 自定义事件
发布于 2022年 02月 26日 16:55
精读 Vue 官方文档系列 🎉
事件名
”事件“与”组件“或”Prop“不同,它不存在大小写转换的问题,事件的监听与触发都是基于事件名的全匹配。但我们仍然推荐使用 kebab-case
风格为事件命名。
自定义组件的 v-model
v-model
本质是一个语法糖,用来实现数据的双向绑定(注意此时组件之间的数据传递不在是单向下行绑定的,因为子组件也可以修改父组件中的数据(事件触发))。v-model
在组件上封装了一个名为value
的 Prop 与名为input
的事件。
<!--v-model 完整形式-->
<base-input :value="value" @input="value=$event" />
<!--v-model 语法糖-->
<base-input v-model="value" />
但是并不是所有组件的值和事件名都是 value
与 input
,例如单选按钮、复选框以及自定义组件等。
特别是自定义组件的 v-model
功能需要开发者自己去实现。
一旦我们组件使用了 v-model
指令,Vue 会默认在这个组件上附加 value
属性用来保存值,并绑定一个 input
事件来接收和改变值,所以,利用这一默认的行为,我们可以快速的在自定义组件中实现 v-model
功能:
Vue.component("base-checkbox", {
inheritAttrs:false,
props:{
value:Boolean //v-model 指令会自动绑定一个 value 属性来向组件内部传递值,这个值会在组件外,被对应的 input 事件更新。
},
//v-model 指令会自动绑定一个 input 事件来改变 value,并循环向通过 props 向组件内部传递。
template:`<input type="checkbox" :checked="value" @change="$emit('input', $event.target.value)"`
})
现在,通过组件的 model
配置选项,我们就可以自定义 v-model
的属性与事件名称,实现更大程度的自定义。
Vue.component("base-checkbox", {
inheritAttrs:false,
model:{
prop:'checked',
event:'change'
},
props:{
checked:Boolean
},
template:`<input type="checkbox" :checked="checked" @change="$emit('change', $event.target.value)"`
})
虽然
v-model
默认 Prop 是value
或者基于model.prop
选项进行自定义的 Prop,但我们仍然需要在组件的 props 进行声明。
通过 .sync
修饰符来规范双向数据绑定
虽然 v-model
提供了强大的双向数据绑定功能,但与之带来的问题就是父子组件的数据流向变得不再清晰,因为子组件也可以修改父组件中的数据,数据传递不再遵循单向下行绑定的原则。
.sync
修饰符目的就是尽可能的在语意上进行规范,让数据流向更加直观易懂。
它分别从两个方面来规范数据的双向绑定:
- 对子组件内部触发事件的事件名称进行规定,让由内到外的数据流向更清晰(通过事件名即可知道要更新的外部属性是什么)。
- 组件使用时用已有的
v-bind
指令来替换v-model
指令,减少心智负担,并让从上到下的数据流向更直观(通过v-bind
与.sync
修饰符结合使用,可以很直观的说明当前的属性不仅会传递数据,还会被子组件同步修改)。
推荐以 update:myPropName 的格式触发事件。
Vue.component('base-input', {
props:['value'],
template:`<input type="text" :value="value" @input="$emit('update:value' $event.target.value)" />`
});
在父组件中使用时,不使用 .sync
修饰符的完整形式:
<base-input v-bind:value="value" v-on:update:value="value=$event" />
使用 .sync
修饰符的方式为:
<base-input :value.sync="value" />
注意:通过
.sync
修饰的v-bind
指令或者v-model
的值不能和表达式或函数一起使用,它只能对要绑定的 Prop 进行修改。
.native
修饰符
v-on
指令的 .native
修饰符可以为组件的根元素监听一个原生事件。
<base-button @click.navitve="handleClick">click me!</base-button>
需要注意的是,往往自定义组件内部的 DOM 结构与渲染后呈现的 UI 并不一致。为了解决这个问题,我们可以使用
$listeners
实例属性,来跨 DOM 层级进行事件绑定。
$listeners
实例属性
使用 v-on="$listeners"
的方式我们可以跨 DOM 层级为指定的目标元素绑定所有事件。这位编写高级组件提供了便利与基础。
注意
$listeners
实例属性中并不会保存带有.native
修饰符的原生事件。
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>
元素一样使用。