不同组件之间是如何通信的?
请永远牢记vue是单向数据流
①props / $emit
父组件通过props的方式向子组件传递数据,而通过$emit子组件可以向父组件通信。
1. 父组件向子组件传值:
接下来我们通过一个例子,说明父组件如何向子组件传递值:在子组件 Users.vue 中如何获取父组件 App.vue 中的数据 users:["Henry","Bucky","Emily"]
App.vue父组件
<template>
<div id="app">
<users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名
</div>
</template>
<script>
import Users from "./components/Users"
export default {
name: 'App',
data(){
return{
users:["Henry","Bucky","Emily"]
}
},
components:{
"users":Users
}
}
users.vue子组件
//users子组件
<template>
<div class="hello">
<ul>
<li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props:{
users:{ //这个就是父组件中子标签自定义名字
type:Array,
required:true
}
}
}
</script>
子组件通过props来接受数据
第一种方式:直接接收数组
props: ['childCom']
第二种方式:设置数组类型
props: {
childCom: String //这里指定了字符串类型,如果类型不一致会警告的哦
}
第三种方式:限制数组 设置类型 默认值
props: {
childCom: {
type: String,
default: 'sichaoyun'
}
}
2.子组件向父组件传值(通过事件形式)
vue2.0只允许单向数据传递,我们通过出发事件来改变组件的数据
接下来我们通过一个例子,说明子组件如何向父组件传递值:当我们点击“Vue.js Demo”后,子组件向父组件传递值,文字由原来的“传递的是一个值”变成“子向父组件传值”,实现子组件向父组件值的传递。
子组件 :子组件通过$emit
触发父组件上的自定义事件,发送参数
<template>
<header>
<h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
</header>
</template>
<script>
export default {
name: 'app-header',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值”
}
}
}
</script>
父组件通过绑定自定义事件,接受子组件传递过来的参数
<template>
<div id="app">
<app-header v-on:titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
// updateTitle($event)接受传递过来的文字
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
②$children / $parent
this.$children[0].msg = "hello world" //父组件修改子组件data中的数据
this.$parent.mag //子组件拿到父组件data中的数据
$children的值是数组,$parent的值是个对象
注意:$parent,$children它们的目的是作为访问数组的应急方法,更推荐用props和events实现父子组件通信。
③provide / inject
这是vue2.2.0新增的api,简单来说就是父组件中通过provide来提供变量,然后再子组件中通过inject来注入变量。
父组件
export default{
name:"A",
provide:{
for:"demo"
},
components:{
comB
}
}
子组件
export default{
name:"B",
inject:["for"],
data(){
demo:this.for
}
}
④ref / refs
ref:如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例,可通过实例直接调用组件的方法或访问数据。
父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default{
mounted(){
const comA = this.$refs.comA;
console.log(comA.name)//Vue.js
comA.sayHello() //hello
}
}
</script>
子组件
export default{
data(){
return {
name:"Vue.js"
}
},
methods:{
sayHello(){
console.log("hello")
}
}
}
⑤eventBus(Bus总线):$emit / $on
1. 初始化,全局创建$bus
注意,这种方式初始化一个 全局的事件总线
。
直接在项目中的 main.js 初始化 $bus :
// main.js
window.$bus=new Vue();
2. 发送事件
$bus.$emit("aMsg", '来自A页面的消息');
<!-- A.vue -->
<template>
<button @click="sendMsg()">-</button>
</template>
<script>
//import $bus from "../bus.js";
export default {
methods: {
sendMsg() {
$bus.$emit("aMsg", '来自A页面的消息');
}
}
};
</script>
3. 接收事件
$bus.$on("事件名",callback)
<!-- IncrementCount.vue -->
<template>
<p>{{msg}}</p>
</template>
<script>
//import $bus from "../bus.js";
export default {
data(){
return {
msg: ''
}
},
mounted() {
$bus.$on("aMsg", (msg) => {
// A发送来的消息
this.msg = msg;
});
}
};
</script>
在组件中,可以使用$emit, $on, $off
分别来分发、监听、取消监听事件
⑥Vuex
介绍vuex原理:
Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 进行,Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。
⑦LocalStorage
vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在 vuex 里数据改变的时候把数据拷贝一份保存到 localStorage 里面,刷新之后,如果 localStorage 里有保存的数据,取出来再替换 store 里的 state。
⑧$attrs / $listeners
将数据挂在到子组件的标签上去后,在子组件中使用this.$attrs直接获取到所有挂载的数据,返回的是一个对象。
多级组件嵌套需要传递数据时,通常使用的方法是通过 vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。为此 Vue2.4 版本提供了另一种方法----$attrs
/$listeners
$attrs
:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
总结:
常见使用场景可以分为三类:
- 父子通信:
父向子传递数据是通过 props,子向父是通过 events($emit
);通过父链 / 子链也可以通信($parent
/$children
);ref 也可以访问组件实例;provide / inject API;$attrs / $listeners - 兄弟通信:
Bus;Vuex - 跨级通信:
Bus;Vuex;provide / inject API、$attrs / $listeners