Vue组件通信主要分为以下几种:
- 父子级通信推荐使用:props/$emit、$parent/$children/$refs、$attrs/$listeners
- 兄弟组件通信推荐使用:$emit/$on、Vuex
- 无关系组件通信推荐使用:$emit/$on、Vuex、$attrs/$listeners、provide/inject
1、props和$emit(父子组件通信)
子组件使用props 接收父组件传递的值(父组件向子组件传值)。子组件通过 $emit ,让父组件接收事件,改变父组件的data里面的值(子组件向父组件传值)。
⚠️注意:
prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop 只读,不可被修改,所有修改都会失效并警告。
- 不应该在一个子组件内部改变 prop,这样会破坏单向的数据绑定,导致数据流难以理解。如果有这样的需要,可以通过 data 属性接收或使用 computed 属性进行转换。
- 如果 props 传递的是引用类型(对象或者数组),在子组件中改变这个对象或数组,父组件的状态会也会做相应的更新,利用这一点就能够实现父子组件数据的“双向绑定”,虽然这样实现能够节省代码,但会牺牲数据流向的简洁性,令人难以理解,最好不要这样去做。
- 想要实现父子组件的数据“双向绑定”,可以使用 v-model 或 .sync。
父组件:
<template>
<div>
<input type="text" v-model="name">
<!-- 引入子组件,:childName这个就是子组件接收的参数名 -->
<child :childName="name" @change='changeHandle'></child>
</div>
</template>
<script>
import child from './child'
export default {
components: {
child
},
data () {
return {
name: '父组件传给子组件的name'
}
},
methods: {
changeHandle(val) {
this.name = val;
}
}
}
</script>
子组件:
<template>
<div>
<span>{{inputName}}</span>
</div>
</template>
<script>
export default {
// 通过props接收父组件的值
props: {
inputName: String
},
methods: {
changeHandle() {
this.$emit('change', '123');
}
}
}
</script>
这里的props有两种写法对象和数组,用法不一样,效果都一样。这里我就直接把官方的解释贴出来吧
2、$emit和$on(兄弟、非父子组件间通信)
通过创建一个包含Vue 的实例的eventBus中间仓库,用来触发事件和监听事件,实现兄弟、非父子组件的通信。当组件A执行setValue方法触发了$emit后,会触发组件B中的$on监听事件。
bus.js:
import Vue from 'vue'
export default new Vue()
组件A:
<template>
<div>
<span>{{val}}</span>
<input type="button" value="setter" @click="setValue">
</div>
</template>
<script>
// 引入公共的bus,来做为中间传值的工具
import Bus from './bus.js'
export default {
data () {
return {
val: 123
}
},
methods: {
setValue: function () {
Bus.$emit('valKey', this.val)
}
}
}
</script>
组件B:
<template>
<div>
<span>{{val}}</span>
</div>
</template>
<script>
import Bus from './bus.js'
export default {
data () {
return {
val: 0
}
},
mounted: function () {
// 用$on事件来接收参数
Bus.$on('valKey', (data) => {
this.val = data
})
}
}
</script>
3、Vuex
Vuex 是一个单向的数据流的状态管理模式,state 存放数据,当需要改变state 的数据时,只能通过 mutation 更改,如果有异步操作,可以使用 action,最终action 还是通过mutation 更改state 的数据。
-
Vue Components:Vue 组件,可以执行dispatch 方法触发action;
-
dispatch:组件触发action 的方法;
-
actions:负责接收组件触发的行为,可以在这里面调用commit()方法更改state中的数据;
-
commit:执行mutation的方法,commit('mutation 名称');
-
mutations:更改 state 数据的方法,只能进行同步操作;
-
state:存储数据的容器对象;
4.、$parent和$children(父子组件通信)
$parent和$children 可以访问到父组件或子组件(数组)的实例,所以也可以用作通信。$children和$ref的区别在于一个是返回所有子组件,一个是指定的子组件。
<!--父组件A-->
<template>
<h1> 父组件 </h1 >
<child ref = "child" ></child>
</template >
<script >
export default {
components: {
child
},
mounted() {
console.log(this.$children);
// 子组件实例数组
console.log(this.$refs.child);
// 子组件child实例
}
}
</script>
<!--子组件child-->
<template>
<h1> 子组件 </h1>
</template>
<script>
export default{
mounted() {
console.log(this.$parent);
// 父组件实例
}
}
</script>
5、$attrs/$listeners
this.$attrs 包含了父作用域中不作为prop被识别的特性绑定(class和style除外),解释就是,父组件传递了props ,但是子组件没有全部使用props 接收,没被子组件接收的那些props,在$attrs中就可以获取到;$listeners 即是包含了父作用域中的(不含.native修饰器的)v-on 事件监听器,可以使用 v-on='$listeners'传入到内部组件;在跨级组件中传递属性和事件非常有用。
< !--顶级组件A-->
<template >
<!--组件B-->
<componentb data1="data1" data2="data2" @="" event1="handleEvent1" event2="handleEvent2"></componentb>
</template>
<script>
export default
{
data() {
return {
data1: 'data1',
data2: 'data2'
};
},
components: {
componentb
},
mounted() {
handleEvent1() {
console.log('子级组件触发的事件');
},
handleEvent2() {
console.log('孙子级组件触发的事件');
}
}
}
</script>
<!--子组件B-->
<template>
<h1>data1的值:{{data1}} </h1>
<!--组件C-->
<componentc v-bind="$attrs" v-on="$listeners"></componentc>
</template>
<script>
export default {
props: {
data1 // 这里只获取了父级传递的props的data1属性
},
components: {
componentc
},
mounted() {
console.log(this.$attrs); // 可以打印出 'data2'
}
}
</script>
<!--孙子组件c-->
<template>
</template>
<script>
export default {
props: {
'data2'
},
mounted() {
this.$emit('event2');
}
}
</script>
6. provide和inject
祖先组件通过 provide 向子孙级组件提供一个对象或返回一个对象的函数,该对象包含可以注入子孙组件的属性。
Vue 不建议直接应用于程序代码中,主要是为高阶插件/组件库提供使用