背景:
正常情况下,vue是由父到子的单向数据流。但总会碰到一些操蛋的需求,想直接在子组件去修改对应数据。这时候就会发现,报警告️了。只能写子组件通知父组件修改对应数据,代码就又大又不优雅。
这时候就会想,v-model怎么实现的,自己封装的组件能不能用?还有没有别的方式。
于是乎有了这篇憨批文章。
其实相关文章也有很多了,但总觉得还是要自己总结一下比较好。
先上官网链接
自定义组件使用model
使用 JavaScript 代替模板功能
这里我想举好几个例子,但是又不知道怎么分点,因此我就随意分了
子组件如下,test1.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <!--v-model:https://cn.vuejs.org/v2/api/#model--> <template> <div class="main"> <div @click="sub"> - </div> <input v-model="value" type="text" style="width:160px" @input="$emit('input', $event.target.value)" /> <!-- @input="$emit('input', $event.target.value)" --> <div @click="add"> + </div> </div> </template>
<script> export default { components: {}, props: { value: { type: [Number, String], default: 0, }, }, data: () => ({}), computed: {}, watch: {}, // created() {}, mounted() {}, methods: { add() { // this.value = this.value + 1 this.$emit('input', Number(this.value) + 1) }, sub() { this.$emit('input', Number(this.value) - 1) }, }, } </script> <style lang="less" scoped> .main { display: flex; flex-direction: row; } </style>
|
父组件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <!----> <!----> <template> <div class="main"> <Test1 v-model="nums1" /> <Test2 v-model="nums2" /> <p>num1:{{ nums1 }}</p> <p>num2:{{ nums2 }}</p> </div> </template>
<script> import Test1 from './test1.vue' import Test2 from './test2.vue' export default { // import引入的组件需要注入到对象中才能使用 components: { Test1, Test2, }, data: () => ({ nums1: 0, nums2: 0, }), computed: {}, watch: {}, mounted() {}, // 方法集合 methods: {}, } </script> <style lang="less" scoped> .main { } </style>
|
从官网链接可以看到自定义组件,默认会把value这个key作为prop,默认有个input事件。(跟我用不用input这个dom没关系,可以换成<p></p>) 体现在代码中就是。
效果如下图
可以看到,跟随变化,且右边没有报警告
二.非默认值,自己写
因为value本身可能是某些组件的’关键字’,我们更需要的是能自定义的值
子组件test2.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!--v-model:https://cn.vuejs.org/v2/api/#model--> <template> <div class="main"> <div @click="sub"> - </div> <p>{{ curvalue }}</p> <div @click="add"> + </div> </div> </template>
<script> export default { model: { prop: 'curvalue', event: 'updatecurvalue', }, props: { curvalue: { type: [Number, String], default: 0, }, }, methods: { add() { this.$emit('updatecurvalue', Number(this.curvalue) + 1) }, sub() { this.$emit('updatecurvalue', Number(this.curvalue) - 1) }, }, } </script> <style lang="less" scoped> .main { display: flex; flex-direction: row; } </style>
|
如上图,通过model下的prop接收对应的事件,去做当前值的绑定和当前值变更的事件绑定。
3.其实还有一种方式.sync
sync官方文档
test3.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <!--v-sync:https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6--> <template> <div class="main"> <div @click="sub"> - </div> <p>{{ curvalue }}</p> <div @click="add"> + </div> </div> </template>
<script> export default { props: { curvalue: { type: [Number, String], default: 0, }, }, methods: { add() { this.$emit('update:curvalue', Number(this.curvalue) + 1) }, sub() { this.$emit('update:curvalue', Number(this.curvalue) - 1) }, }, } </script> <style lang="less" scoped> .main { display: flex; flex-direction: row; } </style>
|
父组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <!----> <template> <div class="main"> <Test3 :curvalue.sync="nums1" /> <p>num1:{{ nums1 }}</p> </div> </template>
<script> import Test3 from './test3.vue' export default { // import引入的组件需要注入到对象中才能使用 components: { Test3 }, data: () => ({ nums1: 0, nums2: 0, }), computed: {}, watch: {}, mounted() {}, // 方法集合 methods: {}, } </script> <style lang="less" scoped> .main { } </style>
|
效果如下
使用你封装的控件的人,一看到这个.sync就知道,肯定是绑定了。