组件通信
prop
子组件设置props属性,定义接收父组件传递过来的参数
<template>
<div>
<ChildA name="vue" :age="20"></ChildA>
</div>
</template>
<template>
<div>
<b>{{ name }}</b>
<br>
<b>{{ age }}</b>
</div>
</template>
<script>
export default {
name: "ChildA",
props: {
name: String,
age: {
type: Number,
default: 20,
require: true
}
},
}
</script>
$emit 触发自定义事件
子组件通过emit第二个参数为传递的数值
父组件绑定监听器获取到子组件传递过来的参数
<template>
<div>
<ChildA @test="test"></ChildA>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
export default {
components: {ChildA},
methods: {
test(msg) {
alert(msg)
}
}
}
</script>
<template>
<div>
<a @click="click">Click</a>
</div>
</template>
<script>
export default {
name: "ChildA",
methods:{
click(){
this.$emit("test","OK")
}
}
}
</script>
事件总线
事件总线(Event Bus)是一种非常常用的通信方式。它允许在不同组件之间进行通信,无论它们之间是否存在父子关系或兄弟关系。它的原理是利用 Vue 实例作为中央事件总线来进行组件之间的事件通信。
在main.js中创建一个新的 Vue 实例,作为全局事件总线实例,并将其作为 Vue 实例的属性
import Vue from 'vue';
Vue.prototype.$bus = new Vue();
发送事件的组件中使用 $emit 方法触发事件
export default {
methods: {
handleClick() {
this.$bus.$emit('my-event', 'Hello, world!');
}
}
}
在接收事件的组件中使用 $on 方法监听事件
export default {
methods: {
handleEvent(data) {
console.log(data)
}
},
mounted() {
this.$bus.$on('my-event', this.handleEvent);
},
beforeDestroy() {
# 取消对事件的监听
this.$bus.$off('my-event', this.handleEvent);
}
}
vuex
Vuex是一个专门为 Vue应用程序开发的状态管理库,它可以更好地管理应用程序中的数据,并实现组件间的通信。使用 Vuex 可以使得多个组件共享同一个状态,方便进行数据的传递和同步。
1.创建store.js
指定一个名为 message 的状态属性,以及一个名为 setMessage 的 mutation 方法,可以修改 message 的值
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
message: ''
},
mutations: {
setMessage(state, payload) {
state.message = payload;
}
}
});
export default store;
2.在需要修改状态的组件中使用 Vuex Store 中的 mutation 方法
在ComponentA.vue,使用 $store.commit 方法来调用 mutation 方法,从而修改 Vuex Store 中的状态
<template>
<div>
<input v-model="message" />
<button @click="setMessage">Set Message</button>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['message'])
},
methods: {
setMessage() {
this.$store.commit('setMessage', this.message);
}
}
};
</script>
3.在需要使用状态的组件中使用 Vuex Store 中的数据。
创建ComponentB.vue,以使用 Vue 的 computed 或 mapState 属性来访问 Vuex Store 中的数据
template>
<div>
<p>{{ message }}</p>
<p>{{this.$store.state.message}}</p>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['message'])
}
};
</script>
5.mapState
mapState是 Vuex 的一个辅助函,帮助在Vue 组件中快速的将 state 中的属性映射为组件的 computed 计算属性,可以直接在组件的 template 中使用
{{ message }} 就是通过 mapState 辅助函数生成的计算属性,它实际上等价于:
computed: {
message() {
return this.$store.state.message
}
}
root组件通信
兄弟组件之间通信可通过共同祖辈搭桥,root。
$parent:可以通过 $parent 属性来访问当前组件的父组件,从而实现父子组件之间的通信
$root:可以通过 $root 属性来访问当前组件所在的根实例,从而实现祖先和后代组件之间的通信
$children: 可以用于访问当前组件的直接子组件
注意:使用 $children 有一定的风险,因为它不保证子组件的顺序和数量。如果需要访问指定的子组件,建议使用 ref 属性来指定组件的名称。
使用 root 属性来在兄弟组件之间进行通信
定义一个父组件 Parent,它包含了两个子组件 ChildA 和 ChildB
<template>
<div>
<ChildA/>
<ChildB/>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'
export default {
components: {
ChildA,
ChildB
}
}
</script>
点击按钮,通过 $parent 属性访问父组件 Parent,然后通过父组件访问 ChildB 组件,并调用 ChildB 组件的 receiveMessage 方法,将消息传递给 ChildB 组件。
<template>
<div>
<button @click="click">兄弟组件通信</button>
</div>
</template>
<script>
export default {
name: "ChildA",
data() {
return {}
},
methods: {
click() {
// 通过 $parent 访问父组件,再通过父组件访问 ChildB
this.$parent.$children.find(child => child.$options.name === 'ChildB').receiveMessage('Hello ChildB')
}
}
}
</script>
ChildB 组件接收到消息后会将消息显示在页面上
<template>
<div>
</div>
</template>
<script>
export default {
name: "ChildB",
data() {
return {
message: 'B组件'
}
},
methods: {
receiveMessage(msg) {
alert(msg)
}
}
}
</script>
使用this.$parent.$emit与this.$parent.$on
<template>
<div>
<button @click="click">兄弟组件通信</button>
</div>
</template>
<script>
export default {
name: "ChildA",
data() {
return {}
},
methods: {
click() {
this.$parent.$emit('send', "Hello World")
}
}
}
<template>
<div>
</div>
</template>
<script>
export default {
name: "ChildB",
data() {
return {
message: 'B组件'
}
},
methods: {
onEvent(msg) {
alert(msg)
}
},
created() {
this.$parent.$on('send', this.onEvent)
}
}
</script>
$children
父组件可以通过$children访问子组件实现父子通信
当前实例的直接子组件。$children 并不保证顺序,也不是响应式的。
<script>
export default {
name: "ChildA",
data(){
return{
}
},
methods: {
test() {
alert("A组件")
}
}
}
</script>
<script>
export default {
name: "ChildB",
data() {
return {
message: 'B组件'
}
}
}
</script>
<template>
<div>
<ChildA/>
<ChildB/>
<button @click="click">获取子组件信息</button>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'
export default {
components: {
ChildA,
ChildB
},
methods: {
click() {
// 访问第一个子组件的message属性
this.$children[0].test()
// 访问第二个子组件的test方法
console.log(this.$children[1].message);
}
}
}
</script>
$attrs
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)
通过 v-bind="$attrs" 传入内部组件
$attrs能接收除了props声明外的所有绑定属性,一旦属性在props中被声明了,就不能通过$attrs获取
$ attrs可以让孙子和儿子组件访问到父组件的属性
<template>
<div>
<ChildA msg="hello world" v-bind="$attrs"/>
</div>
</template>
msg并未在props中声明
<template>
<div>
<p>{{$attrs.msg}}</p>
</div>
</template>
$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
$ listeners可以让孙子和儿子组件访问到父组件的方法
<template>
<div>
<ChildA @click1.native="click1" @click2="click2" v-bind="$listeners"/>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
export default {
components: {ChildA},
methods: {
click1() {
alert("click1")
},
click2() {
alert("click2")
}
}
}
</script>
点击click事件,则执行父组件的click2方法
<template>
<div>
<a @click="click">click</a>
</div>
</template>
<script>
export default {
name: "ChildA",
data() {
return {}
},
methods: {
click() {
this.$emit('click1')
this.$emit('click2')
}
}
}
</script>
$refs
一个对象,持有注册过 ref attribute 的所有 DOM 元素和组件实例。
父组件使用子组件的时候设置ref 父组件通过设置子组件ref获取数据
<template>
<div>
<ChildA ref="child"/>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
export default {
components: {ChildA},
mounted() {
alert(this.$refs.child.msg)
}
}
</script>
<template>
<div>
<p>{{ msg }}</p>
</div>
</template>
<script>
export default {
name: "ChildA",
data() {
return {
msg: "Hello World"
}
},
}
</script>
provide / inject
provide 和 inject 可以用来在祖先组件中指定想要提供给后代组件的数据或方法,而在任何后代组件中,都可以使用 inject 来接收 provide 提供的数据或方法
能够实现祖先和后代之间传值
<template>
<div>
<ChildA/>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
export default {
components: {ChildA},
data() {
return {
msg: "OK"
}
},
provide() {
return {
msg: "OK",
click: this.test
}
},
methods: {
test() {
alert("OK")
}
}
}
</script>
<template>
<div>
<b>{{ msg }}</b>
<hr>
<a @click="click">click</a>
</div>
</template>
<script>
export default {
name: "ChildA",
data() {
return {}
},
inject: ['msg','click'],
}
</script>
插槽
插槽语法是Vue 实现的内容分发 API,⽤于复合组件开发。
匿名插槽
<template>
<div>
<ChildA>Hello</ChildA>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
export default {
components: {ChildA},
}
</script>
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
name: "ChildA",
}
</script>
具名插槽
将内容分发到⼦组件指定位置
<template>
<div>
<ChildA>
<!-- 默认插槽⽤default做参数 -->
<template v-slot:default>默认插槽</template>
<!-- 具名插槽⽤插槽名做参数 -->
<template v-slot:content>具名插槽</template>
</ChildA>
</div>
</template>
<script>
import ChildA from './ChildA.vue'
export default {
components: {ChildA},
}
</script>
<template>
<div>
<slot></slot>
<br>
<slot name="content"></slot>
</div>
</template>
<script>
export default {
name: "ChildA",
}
</script>
作⽤域插槽
在父组件中使用作用域插槽来访问子组件中的数据
子组件定义了一个带有名为user的数据的插槽
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
name: "ChildA",
data(){
return{
user:{"name":"ChildA","age":20}
}
}
}
</script>
在父组件中,我们使用v-slot指令来访问子组件传递过来的数据,并将其显示出来。
<template>
<div>
<ChildA>
<!-- 把v-slot的值指定为作⽤域上下⽂对象 -->
<template v-slot:default="slotProps">
获取⼦组件数据:<br>
姓名: {{slotProps.user.name}}<br>
年龄: {{slotProps.user.age}}<br>
</template>
</ChildA>
</div>
</template>