前言
vue是数据驱动视图更新的框架, 我们平时开发,都会把页面不同模块拆分成一个一个vue组件, 所以对于vue来说组件间的数据通信非常重要,那么组件之间如何进行数据通信的呢?
首先我们需要知道在vue中组件之间存在什么样的关系, 才更容易理解他们的通信方式。
一般我们分为如下关系:
- 父子组件之间通信
- 非父子组件之间通信(兄弟组件、隔代关系组件等)
1.props / $emit
父组件通过props
的方式向子组件传递数据,而通过$emit
子组件可以向父组件通信。
- 父组件向子组件传值
<!-- 父组件 -->
<template>
<div class="section">
<child :msg="articleList"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
name: 'HelloWorld',
components: { comArticle },
data() {
return {
msg: '阿离王'
}
}
}
</script>
<!-- 子组件 child.vue -->
<template>
<div>
{
{ msg }}
</div>
</template>
<script>
export default {
props: {
msg: String
}
}
</script>
注意:
prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。
- 第一,不应该在一个子组件内部改变 prop,这样会破坏单向的数据绑定,导致数据流难以理解。如果有这样的需要,可以通过 data 属性接收或使用
computed
属性进行转换。 - 第二,如果
props
传递的是引用类型(对象或者数组)
,在子组件中改变这个对象或数组,父组件的状态会也会做相应的更新,利用这一点就能够实现父子组件数据的“双向绑定”
,虽然这样实现能够节省代码,但会牺牲数据流向的简洁性
,令人难以理解,最好不要这样去做。 - 想要实现父子组件的数据“双向绑定”,可以使用
v-model
或.sync
。
- 子组件向父组件传值
使用 $emit
向父组件传数据,原理这样的: 父组件在子组件通过v-on
监听函数并接收参数,vue框架就在子组件监听了你v-on="fn"
的fn事件函数,在子组件使用$emit
就能触发了,下面写个例子。
<!-- 父组件 -->
<template>
<div class="section">
<child :msg="articleList" @changMsg="changMsg"></child>
</div>
</template>
<script>
import child from './child.vue'
export default {
name: 'HelloWorld',
components: { comArticle },
data() {
return {
msg: '阿离王'
}
},
methods:{
changMsg(msg) {
this.msg = msg
}
}
}
</script>
<!-- 子组件 child.vue -->
<template>
<div>
{
{ msg }}
<button @click="change">改变字符串</button>
</div>
</template>
<script>
export default {
props: {
msg: String
},
methods: {
change(){
this.$emit('changMsg', '阿离王带你学习前端')
}
}
}
</script>
2.v-model 指令
v-model
是用来在表单控件
或者组件
上创建双向绑定
的,他的本质是 v-bind
和 v-on
的语法糖
,在一个组件上使用 v-model
,默认会为组件绑定名为 value 的 prop
和名为 input
的事件。
当我们组件中的某一个 prop
需要实现上面所说的”双向绑定“时,v-model
就能大显身手了。有了它,就不需要自己手动在组件上绑定监听当前实例上的自定义事件,会使代码更简洁
。
下面以一个 input 组件实现的核心代码,介绍下 v-model
的应用。
<!--父组件-->
<template>
<base-input v-model="inputValue"></base-input>
</template>
<script>
export default {
data() {
return {
input: ''
}
},
}
</script>
<!--子组件-->
<template>
<input type="text" :value="currentValue" @input="handleInput">
</template>
<script>
export default {
data() {
return {
currentValue: this.value === undefined || this.value === null ? ''
}
},
props: {
value: [String, Number], // 关键1
},
methods: {
handleInput(event) {
const value = event.target.value;
this.$emit('input', value); // 关键2
},
},
}
</script>
上面例子看到,v-model="inputValue"
他的本质就是 v-bind 和 v-on 的语法糖
,默认为父组件绑定名为 :value="inputValue"
的属性,和@input="(v) => { this.inputValue = v }"
事件,子组件通过 this.$emit('input', value)
通知父组件
所以他原理也是利用了我们上面讲的父子组件传参 props / $emit
方式来实现双向绑定
有时,在某些特定的控件中名为 value 的属性会有特殊的含义,这时可以通过 v-model
选项来回避这种冲突。
3. .sync 修饰符
.sync
修饰符在 vue 1.x 的版本中就已经提供,1.x 版本中,当子组件改变了一个带有.sync
的prop
的值时,会将这个值同步到父组件中的值。这样使用起来十分方便,但问题也十分明显,这样破坏了单向数据流,当应用复杂时,debug 的成本会非常高。- 于是乎,在vue 2.0中移除了
.sync
。但是在实际的应用中,.sync
是有它的应用场景的,所以在vue 2.3
版本中,又迎来了全新的 .sync
。 - 新的
.sync
修饰符所实现的已经不再是真正的双向绑定,它的本质和v-model
类似,只是一种缩写。
正常封装组件例子:
<text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" />
上面的代码,使用 .sync
就可以写成
<text-document v-bind:title.sync="doc.title" />
这样,在子组件中,就可以通过下面代码来实现对这个 prop 重新赋值了。
this.$emit('update:title', newTitle)
看到这里,是不是发现 .sync
修饰符 和 v-model
很相似,也是语法糖, v-bind:title.sync
也就是 等效于 v-bind:title="doc.title" v-on:update:title="doc.title = $event"