CodeSky 代码之空

随手记录自己的学习过程

Vue2.x 中实现父子组件间的双向绑定

2017-04-25 23:26分类: JavaScript评论: 2

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 相当于给子组件一个名为 valueprop,再接受子组件 input 事件并赋值给父组件的对应变量。

所以我们需要做以下几件事情:

  1. 子组件定义名为 valueprop
  2. 在需要改变父组件值时发送 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)

鱼米团子2017年10月19日 08:25

妙啊!

Eridanus Sora2017年4月26日 11:55

学习了,v-model这种还真不知道。。