组件通信可以分为:
- 父子组件之间通信
- 非父子组件之间通信(兄弟组件、隔代关系组件等)
props
/ $emit
父子组件通信最常用的是父组件通过props
的方式向子组件传递数据,而子组件通过$emit
可以向父组件通信。
props
prop
只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 prop
只读,不可被修改,所有修改都会失效并警告。如果prop
是对象则可以修改其属性值。
// 父组件
<template>
<Child message="Hello,child!"></Child>
</template>
<script>
import Child from './child'
export default {
components: {
Child
}
}
</script>
//子组件
<template>
<div>{{message}}</div>
</template>
<script>
export default {
props: ['message']
}
</script>
$emit
子组件通过$emit
触发一个自定义事件,并将要传递数据作为$emit
的参数,父组件通过v-on
监听这个事件并接收参数。
// 父组件
<template>
<div>
<Child @sendMessage="reciveMessage"></Child>
{{text}}
</div>
</template>
<script>
import Child from "./child";
export default {
components: {
Child
},
methods: {
reciveMessage(message) {
this.text = message;
}
},
data() {
return {
text: ""
};
}
};
</script>
//子组件
<template>
<button @click="postmessage">click</button>
</template>
<script>
export default {
methods: {
postmessage(){
this.$emit('sendMessage', 'Hi,parent!');
}
}
}
</script>
$children
/ $parent
子组件通过$parent
可以访问父组件的实例,父组件通过$children
可以访问所有子组件的实例。$parent
是一个对象,$children
是一个数组。需要注意 $children
并不保证顺序,也不是响应式的。
//父组件
<template>
<div>
<Child></Child>
<button @click="changeChild">change</button>
</div>
</template>
<script>
import Child from "./child";
export default {
components: {
Child
},
data() {
return {
text: "Parent's text~"
};
},
methods: {
changeChild() {
this.$children[0].text = "Parent change";
}
}
};
</script>
//子组件
<template>
<div>
<p>{{parentText}}</p>
<p>{{text}}</p>
</div>
</template>
<script>
export default {
computed: {
parentText() {
return this.$parent.text;
}
},
data() {
return {
text: `Child's text`
};
}
};
</script>
provide
/ inject
父组件中通过provide
来提供变量, 然后再子组件中通过inject
来注入变量。不论子组件嵌套有多深, 只要调用了inject
那么就可以注入provide
中的数据。
// 父组件
<template>
<div>
<child></child>
</div>
</template>
<script>
import child from "./child";
export default {
components: {
child
},
provide: {
text: 'parent'
}
};
</script>
//子组件
<template>
<div>
<grandchild></grandchild>
Child get message: {{this.text}}
</div>
</template>
<script>
import grandchild from "./grandchild";
export default {
components: {
grandchild
},
inject: ['text']
};
</script>
//孙子组件
<template>
<div>Grandchild get message: {{this.text}}</div>
</template>
<script>
export default {
inject: ['text']
};
</script>
ref
/ refs
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。ref
如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
当 v-for
用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
因为 ref
本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们,$refs
也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
//父组件
<template>
<div>
<child ref="child"></child>
<button @click="change">change</button>
</div>
</template>
<script>
import child from "./child";
export default {
components: {
child
},
methods: {
change(){
this.$refs.child.text = 'parent change'
}
}
};
</script>
//子组件
<template>
<div>{{text}}</div>
</template>
<script>
export default {
data(){
return {
text: `child's text`
}
}
};
</script>
事件总线 EventBus
EventBus
又称为事件总线,可以使用它来作为沟通的桥梁,所有组件都可以向该中心触发事件或监听事件。
EventBus
也有不方便之处,当项目较大,就容易造成难以维护的灾难。
// EventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
//父组件
<template>
<div>
<child1></child1>
<child2></child2>
</div>
</template>
<script>
import child1 from "./child";
import child2 from "./child2";
export default {
components: {
child1,
child2
}
};
</script>
// 子组件child1
<template>
<div>
<button @click="send">child1</button>
</div>
</template>
<script>
import {EventBus} from './EventBus.js'
export default {
methods: {
send(){
EventBus.$emit('send','Hello,bro!');
}
}
};
</script>
// 子组件child2
<template>
<div>{{text}}</div>
</template>
<script>
import {EventBus} from './EventBus.js'
export default {
mounted() {
EventBus.$on("send", message => this.text = message);
},
data() {
return {
text: "im child2"
};
}
};
</script>
如果想移除事件的监听, 可以像下面这样操作:
EventBus.$off(event, callback);
参数:
event
:string
|Array<string>
callback
:Function
用法:
-
如果没有提供参数,则移除所有的事件监听器;
-
如果只提供了事件,则移除该事件所有的监听器;
-
如果同时提供了事件与回调,则只移除这个回调的监听器。
Vuex
Vuex
是一个专为 Vue.js 应用程序开发的状态管理模式。
Vuex
有几个核心概念:
state
:用于数据的存储,是store
中的唯一数据源。在state存放的数据是响应式的,数据发生改变,依赖这个数据的组件也会发生更新。getters
:可以认为是state
的计算属性,基于state
数据进行二次包装。mutations
:改变state
数据的唯一途径,但不能用于处理异步事件,需要使用store.commit()
[kəˈmɪt]触发。它会接受state
作为第一个参数。actions
:用来改变state
数据,内部也是运用了mutation
,可以包含任意异步操作,通过store.dispatch()
方法触发。modules
:modules
允许我们将store
分割成模块(module)。每个模块拥有自己的state
、mutation
、action
、getter
、甚至是嵌套子模块。
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
text: 'hello'
},
mutations: {
changeText(state, newText){
state.text = newText;
}
}
})
//父组件
<template>
<div>
<child></child>
<button @click="change">parentchangetext</button>
</div>
</template>
<script>
import child from "./child";
export default {
components: {
child
},
methods: {
change(){
this.$store.commit('changeText', 'im parent');
}
}
};
</script>
<template>
<div>Child: {{this.$store.state.text}}</div>
</template>
<script>
export default {
};
</script>
$attrs
与 $listeners
$attrs
包含了父作用域中不作为 prop
被识别 (且获取) 的特性绑定 (class
和 style
除外)。当一个组件没有声明任何 prop
时,这里会包含所有父作用域的绑定 (class
和 style
除外),并且可以通过 v-bind="$attrs"
传入内部组件。
$listeners
包含了父作用域中的 (不含 .native
修饰器的) v-on
事件监听器。它可以通过 v-on="$listeners"
传入内部组件。
//父组件
<template>
<div>
<child
message1="hi,grandchild!"
message2="hello,grandchild!"
@message="sayhi"
></child>
</div>
</template>
<script>
import child from "./child";
export default {
components: {
child
},
methods: {
sayhi(message) {
console.log(message);
}
}
};
</script>
//子组件
<template>
<div>
<grandchild v-bind="$attrs" v-on="$listeners"></grandchild>
{{message1}}
</div>
</template>
<script>
import grandchild from "./grandchild";
export default {
components: {
grandchild
},
props: ['message1'],
inheritAttrs: false
};
</script>
//孙子组件
<template>
<div>
<p>{{message2}}</p>
<button @click="postmessage">click</button>
</div>
</template>
<script>
export default {
props: ["message2"],
methods: {
postmessage(){
this.$emit('message', 'hello,grandparent');
}
}
};
</script>
总结
常见使用场景可以分为三类:
父子组件通信: props
/ $emit
,$parent
/ $children
,provide
/ inject
,ref
/ $refs
,EventBus
,Vuex
兄弟组件通信: EventBus
,Vuex
跨级通信: EventBus
,Vuex,provide
/ inject
,$attrs
/ $listeners