背景:
正常情况下,vue是由父到子的单向数据流。但总会碰到一些操蛋的需求,想直接在子组件去修改对应数据。这时候就会发现,报警告️了。只能写子组件通知父组件修改对应数据,代码就又大又不优雅。
这时候就会想,v-model怎么实现的,自己封装的组件能不能用?还有没有别的方式。
于是乎有了这篇憨批文章。
其实相关文章也有很多了,但总觉得还是要自己总结一下比较好。
先上官网链接
这里我想举好几个例子,但是又不知道怎么分点,因此我就随意分了
一.默认value和input的例子
子组件如下,test1.vue
<!--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>
父组件如下
<!---->
<!---->
<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>{{value}}</p>) 体现在代码中就是。
效果如下图
可以看到,跟随变化,且右边没有报警告
二.非默认值,自己写
因为value本身可能是某些组件的'关键字',我们更需要的是能自定义的值
子组件test2.vue
<!--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
test3.vue
<!--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>
父组件
<!---->
<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就知道,肯定是绑定了。