Vue2.x 中实现父子组件间的双向绑定
Vue2.x 中父子组件中 props 的属性不能在子组件中改变其值然后传出至父组件了,但是有的时候也会存在一些麻烦,比如我们想要实现一些自定义组件,总是会有值的传入传出改变的,比如我希望做一个 popup,那么肯定需要根据父节点的状态来确定子节点。
尽管官方推荐双向数据流,但还是有一些方法避免报错,实现或 hack 双向数据流:
Event Bus / Vuex
最简单的当然是通过状态管理来管理我们的变量,但对于数据流不复杂的父子节点交互而言,未免太过浪费,因此通常我们不会特别的去考虑它,尤其是 popup / checkbox 这种基础组件的时候。
使用 Object
Object 的值是其内存地址,只改变 Object 内部的值不改变地址将不会出现报错。
使用 Event-Emit 处理
尽管不可以直接修改值,但是通过 event emit,我们在子组件改变其值时(可以通过 watch
,事件监听等方法监听,在子组件中用 this.$emit('eventName', value)
传入,在父组件中绑定 @event-name
,在函数中修改值。但是这样如果是 input
基础组件,写函数就得写到疯——不靠谱。
v-model
所幸我们还有一个叫做 v-model 的语法糖:v-model
相当于给子组件一个名为 value
的 prop
,再接受子组件 input
事件并赋值给父组件的对应变量。
所以我们需要做以下几件事情:
- 子组件定义名为
value
的prop
值 - 在需要改变父组件值时发送
input
事件。
但是在子组件内我们要先变动一个作用域为子组件的值,才能使用监听,所以我们绑定一个 _value
,之后在父组件就能使用 v-model
了。:
1computed: {
2 _value: {
3 get() {
4 return this.value;
5 },
6 set(val) {
7 this.$emit('input', val);
8 this.$emit('change', val);
9 }
10 }
11
一个完整的基础组件代码:
1<template>
2 <label class="radio">
3 <input type="radio" :name="name" v-model="_value" :value="label" :disabled="disabled">
4 <div>{{ text }}</div>
5 </label>
6</template>
7
8<style lang="scss" scoped>
9 .radio {
10 display: block;
11 height: 50px;
12 margin-bottom: 10px;
13 input[type=radio] {
14 display: none;
15 }
16 div {
17 position: relative;
18 line-height: 30px;
19 top: -10px;
20 right: 20px;
21 }
22 div:after {
23 position: relative;
24 right: -20px;
25 top: 10px;
26 transition: .1s;
27 }
28 input[type=radio] + div:after {
29 content: '';
30 display: inline-block;
31 border: 1px solid #b3bbc2;
32 height: 30px;
33 width: 30px;
34 border-radius: 50%;
35 }
36 input[type=radio]:checked + div:after {
37 border: 10px solid #7ebcff;
38 height: 30px;
39 width: 30px;
40 }
41 }
42</style>
43
44<script>
45 export default {
46 name: 'CustomRadio',
47 props: {
48 value: {
49 type: [ Boolean, String, Number ]
50 },
51 text: {
52 type: String,
53 required: false
54 },
55 name: {
56 type: String,
57 default: ''
58 },
59 disabled: {
60 type: Boolean,
61 required: false,
62 default: false
63 },
64 label: {
65 type: [ Boolean, String, Number ],
66 required: false
67 }
68 },
69 computed: {
70 _value: {
71 get() {
72 return this.value;
73 },
74 set(val) {
75 this.$emit('input', val);
76 this.$emit('change', val);
77 }
78 }
79 }
80 };
81</script>
82
评论 (2)
妙啊!
学习了,v-model这种还真不知道。。