在vue的开发中,我们常常会构造自定义组件,多个自定义组件组合至父级组件中,父子组件的通信方式基本是:父组件通过Prop向子组件传递数据。

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
<!-- 父组件 -->
<template>
<child :msg="A"></child>
</template>
<script>
export default {
data() {
return {
A:'这是一条文本数据'
};
}
...
};
</script>
<!-- 子组件 -->
<template>
<div>{{msg}}</div>
</template>
<script>
export default {
prop: ['msg'],
...
};
</script>

以上的示例中,每当父组件的变量A发生数据变动时,都会更新子组件中接收的msg。

但是,因为Prop的数据是单向流动的,父组件可以通过Prop向子组件传递数据,子组件却不能通过Prop将数据的变动再回传给父组件。

此时有一个问题,如果我们希望子组件也可以实时改变父组件的数据,那该如何处理?

针对子父通信,vue给出了事件监听的方式,子父组件中共同定义好相应的事件名称,子组件调用内建的$emit方法并传入事件的名字,来向父级组件触发一个事件,同时$emit支持第二个参数作为数据传递。然后当在父级组件监听这个事件的时候,可以通过 $event 访问到被抛出的这个值。

以上就是官方文档给出的一个子父通信方法,具体代码详见官方文档,不是本文讲解内容。

当然了,随着vuex这样的数据管控插件的加入,组件间的通信变得更加集中化,但是比起原生的通信方式成本已增大了。

很多时候,多个组件组合使用时我们希望实现子父通信,总考虑到以下问题:

  1. 若采用官方的父子组件事件监听来实现代码较为繁琐,多个组件就要定义多个监听事件,增加代码量而且不好管控。
  2. 若引入vuex集中管理,成本较大,又感觉部分功能还达不到非使用vuex不可的程度,杀鸡焉用牛刀。

那么到底有什么最优的方法呢?

此时我不禁想到vue的一个很特殊的指令v-model,vue文档中写道:

你可以用 v-model 指令在表单 <input><textarea><select> 元素上创建双向数据绑定。

v-model是一个可以实现数据双向绑定的指令,那么我们可不可以使用它来实现子父通信呢?当然是可以啦!

仔细查询文档,其实就有提到在组件上使用v-model,建议仔细阅读理解v-model机制后再继续看以下代码!

于是我们可以尝试着修改一下之前的代码:

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
<!-- 父组件 -->
<template>
<child v-model="A"></child>
</template>
<script>
export default {
data() {
return {
A: '这是一条文本数据'
};
}
};
</script>
<!-- 子组件 -->
<template>
<input v-model="tempdata"/>
</template>
<script>
export default {
prop: {
value: {
type: String,
default: ''
}
},
data() {
return {
tempdata: ''
};
},
watch: {
value: {
// 若父组件的数据发生变动,及时更新,保持tempdata的值与value一致
handler(val) {
this.tempdata = val;
},
immediate: true
},
tempdata(val) {
// 当tempdata发生变动时,触发‘input’事件向父组件执行对value的修改
this.$emit('input', val);
},
}
};
</script>

从以上代码可以看出,主要的修改是在于子组件,针对v-model指令的特殊性,我们定义一个临时变量tempdata用于承载子组件内的数据修改,初始化时保持其值与父组件使用v-model传递来的value一致。每当tempdata发生变动时,子组件监听其变化并将该值通过$emit('input')的方式触发父组件更新value(即父组件使用v-model传递的对象变量)。

以上就是本次分享的主要内容,不难发现,其实本次的实现方式还是基于官方给出的事件监听的方式,但巧妙地使用了v-model指令为我们减少了很多代码,且更加灵活。