最近在写组件的时候,遇到了 v-model 的使用问题,在 Vue 官方文档中,有两小端内容是关于 v-model 指令在组件中的使用,查阅文档后,依然不得要领,最后几番折腾,理论结合实践,终于领悟其精髓,遂成文分享之。
v-model 通常都是运用在表单组件中,在这里我们以一个 select组件为例,组件命名为 a-select。
v-model写在哪?
第一个问题就是 v-model 指令是写在子组件里还是父组件里。
我在最开始写组件时就遇到这个问题,归根结底是对在组件中使用 v-model 指令的了解还处于混沌的状态。
文档中有提到
要让组件的 v-model 生效,它应该 (在 2.2.0+ 这是可配置的):
- 接受一个 value 属性
- 在有新的值时触发 input 事件
所以我们需要通过触发事件来实现 value 的更新,而 Vue 中:
父子组件的关系可以总结为 props down, events up
那么很明显,我们是在父组件里写 v-model。
子组件怎么更新父组件的值?
那在父组件中我们可以这么写:
复制代码
文档 告诉我们,v-model 只是一个语法糖,实际的含义是:
那在子组件中,怎么更新父组件的值(parentValue)呢?我翻遍了文档,也没找到,但我找到了一段看似相关的 定制组件的 v-model,因为其中说了:
默认情况下,一个组件的 v-model 会使用 value 属性和 input 事件,但是诸如单选框、复选框之类的输入类型可能把 value 属性用作了别的目的
同样,我们的 select 组件的 value 值也被占用,而且没有 input事件。
看来我们需要定制 v-model 了, 开始之前,我们先来把例子看懂。
Vue.component('my-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean, // this allows using the `value` prop for a different purpose value: String }, // ... })
新增的 model 属性值里有两个key,分别为 prop、event,值分别为 checked、change,看到这里,我们弯起嘴角,会心一笑。
model 属性值(model这个名称真是取得简明扼要啊)里的两个key其实就是 v-model 这个语法糖所代表的 prop 和 event,分别表示 该表单元素的值 和 改变元素值时触发的事件, 在 input 中,这两个值是value 和 input(默认值),在 checkbox 中表示 checked 和 change。
以此类推,在 select 中就表示 selected 和 change。
到这里,我就需要指出我们上面说的一个错误了,此时的 v-model 在父组件中的实际含义是:
那么我们可以这么来写子组件:
{{item.text}} export default() { model: { prop: 'selected', event: 'change' }, props: { selectData: { type: Array }, }, methods: { emitChange(value){ this.$emit('change', value); } } }
唯一的问题在于,我们需要在初始化时设置选中项,该怎么办?我们还有一个 selected 属性值没有呢。甚至官网也温馨提示我们:
注意你仍然需要显性声明 checked 属性。
所以这里我们需要显性声明 selected 属性,不过,因为有 v-model 的存在,我们可以不用在父组件里传入 selected值,是不是少了一点工作量呢?
所以子组件里是这么写的: {{item.text}} export default() { model: { prop: 'selected', event: 'change' }, props: { selectData: { type: Array }, selected: { type: [String,Number] } }, methods: { emitChange(value){ this.$emit('change', value); } } }
当然了,作为一个完整的 select 组件,上面的示例其实是很简陋的。
完整的 select 组件代码可以看iview
最后分享两个最近在读的掘金小册,大牛果然还是人家的大牛,现阶段膜拜还是要有的,我读了感觉收货很大,推荐给我的读者小伙伴们。好看不贵还实惠(其实也是我的),大概就是人家掘金小册的定位了吧
链接文章:
https://juejin.im/post/598bf7a3f265da3e252a1d6a