Vue组件间通信的9种实现方式
组件的通信
父子组件通信
父子组件通信可以理解成:
父组件向子组件传值。
父组件调用子组件的方法。
子组件向父组件传值。
子组件调用父组件的方法。
1.props: 利用props属性实现父组件向子组件传值。
parent.vue文件
<template>
<div>
父组件
<!-- 子组件在不同的父组件中传递 -->
<children data="我是父组件传递的值" ></children>
<children data="我是父组件传递的值" :num="18"></children>
</div>
</template>
<script setup lang="ts">
import children from '@/components/children.vue'
</script>
children.vue 文件
<template>
<div>
子组件:{{data}}
<div>年龄: {{num}}</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: 'children',
props: {
data: String,
num: {
type: Number,
default: () => 20
},
}
})
</script>
props不仅可以传字符串、数字、数组、对象、boolean的值,在传递时props也可以定义数据校验,以此来限制接收的数据类型等。
父传子的方式形成了一个单向下行绑定,叫做单向数据流。父级props的更新也会向下流程到接收这个props的子组件中,触发对应的更新,但是反过来则不行。如果遇到确实需要改变props值的应用场合,则采用下面的解决方法:
children.vue文件
<template>
<div>
<div>
必须被修改的值第一种: {{myData}}
</div>
<div>
必须被修改的值第二种: {{childData}}
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref } from "vue";
export default defineComponent({
name: 'children',
props: {
data: String,
num: {
type: Number,
default: () => 20
},
},
setup(props) {
// 第一种
const myData = ref(props.data)
myData.value = '通过初始化的方式进行修改'
// 第二种
const childData = computed(() => {
return props.data + '通过computed属性实现修改'
})
return {
myData,
childData
}
}
})
</script>
2. $refs: 利用$refs属性可以实现父组件调用子组件的方法。通常用于直接操作子组件,但不推荐用于数据传递。
Vue2的写法
<refChild ref="childRef"></refChild>
this.$refs.refChild?.childFn()
Vue3的写法
parent.vue文件
<template>
<div class="mt15">
$refs通信的子组件
<refChild ref="compontentRef"></refChild>
</div>
</template>
<script lang="ts">
import refChild from '@/components/refChild.vue'
import { defineComponent, onMounted, ref } from 'vue'
export default defineComponent({
name: 'parent',
components: {
refChild
},
setup() {
const compontentRef = ref(null)
const getRefChild = () => {
compontentRef.value?.childFn();
}
onMounted(() => {
getRefChild();
})
return {
compontentRef,
getRefChild
}
}
})
refChild.vue文件
<template>
<div>$refs紫组件</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: 'refChild',
setup(props, context) {
const childFn = () => {
console.log('fn的方法被调用')
}
// 第一种写法
context.expose({ childFn: childFn })
// 第二种写法 defineExpose不需要引入,直接使用
// defineExpose({ childFn: childFn })
}
})
</script>
3.slot: 通过插槽实现父组件向子组件传值。关于插槽
// 父组件
<div>
<slotChild>
默认传值的方式
</slotChild>
</div>
// 子组件
<template>
<div>
<slot></slot>
</div>
</template>
4.$emit: 触发当前组件实例上的事件,可以理解成子组件触发了绑定在父组件上的自定义事件。
parent.vue文件
<children data="我是父组件传递的值" :num="18" @fromGreet="fromGreet"></children>
import { defineComponent, onMounted, ref } from 'vue'
import children from '@/components/children.vue'
import refChild from '@/components/refChild.vue'
import slotChild from '@/components/slotChild.vue'
export default defineComponent({
name: 'parent',
components: {
children,
},
setup() {
const fromGreet = (data) => {
console.log('来自子组件的调用', data)
}
return {
fromGreet
}
}
})
children.vue文件
<template>
<div>
子组件:{{data}}
<a-button type="primary" @click="greet">打招呼</a-button>
</div>
</template>
import { computed, defineComponent, ref } from "vue";
export default defineComponent({
name: 'children',
props: {
data: String,
},
setup(props, context) {
const greet = () => {
context.emit('fromGreet', 'hello')
}
return {
greet
}
}
})
5.$parent: 在子组件直接获取父组件的实例,从而调用父组件中的方法,类似于$refs获取子组件的方法。
因Vue3的组合式API设计原则,应该尽量避免直接使用$parent,可能会导致组件之间的耦合度过高。推荐下面的实现方法:
parent.vue文件
<children @call-parent-method="toChildClick"></children>
const toChildClick = (data) => {
console.log('来自子组件的调用', data)
}
children.vue文件
<a-button danger @click="getParentFn">调用父组件的方法</a-button>
import { computed, defineComponent, ref } from "vue";
export default defineComponent({
name: 'children',
emits: ['call-parent-method'],
setup(props, context) {
const getParentFn = () => {
context.emit('call-parent-method');
}
return {
getParentFn
}
}
})
Vue2的写法可参考$refs。
6. 事件总线EventBus/mitt实现兄弟组件通信
在Vue2中,可以采用EventBus这种方法。利用一个空的Vue实例来作为桥梁,实现事件分发,它的工作原理是发布/订阅方法,通常称为Pub/Sub,也就是发布和订阅的模式。
var bus = new Vue()
// 组件A
bus.$emit('event', 'hello from component A')
// 组件B
bus.$on('event', (message) => {
console.log(message); // 'hello from component A'
})
在Vue3中,采用第三方事件总线库mitt取代了EventBus事件。mitt的使用方法和EventBus非常类似,同样是基于Pub/Sub模式,并且更加简单,可以在需要进行通信的地方直接使用。
首先需要安装:
npm install mitt
创建一个工具包 eventBus.ts
import mitt from 'mitt';
export const emitter = mitt();
在componentA组件
<a-button type="primary" @click="sendMessage">发送消息给兄弟组件</a-button>
import { emitter } from '@/utils/eventBus'
export default defineComponent({
setup(props, context) {
const sendMessage = () => {
// 发布一个事件
emitter.emit('sendMessage', 'hello from component A')
}
return {
sendMessage
}
}
})
在componetB组件
<div>
message from component A: {{ message }}
</div>
import { defineComponent, onMounted, onUnmounted, ref } from "vue";
import { emitter } from '@/utils/eventBus'
export default defineComponent({
setup(props, context) {
const message = ref('')
onMounted(() => {
emitter.on('sendMessage', (msg) => {
message.value = msg;
})
})
onUnmounted(() => {
emitter.off('sendMessage')
})
return {
message
}
}
})
事件总线的方式进行通信使用起来非常简单,可以实现任意组件之间的通信,其中没有多余的业务逻辑,只需要在状态变化组件触发一个事件,随后在处理逻辑组件监听该事件即可。
7. provide/inject AP实现跨级组件
provide/inject是一种跨组件传递数据的方式,允许祖先组件向其所有子孙后代注入依赖,而不必一层层地通过props传递。
import { defineComponent, provide, ref } from 'vue'
export default defineComponent({
setup(props, context) {
const theme = ref('dark')
provide('theme', theme)
}
})
<div>
主题: {{ theme }}
</div>
import { defineComponent, ref, inject } from "vue";
export default defineComponent({
setup(props, context) {
const theme = inject('theme')
return {
theme
}
}
})
8.vuex状态管理
对于一些大型的项目,要实现复杂的组件通信和状态管理。
组合式API的写法
import { createStore } from 'vuex'
export default createStore({
state: {
message: 'vue'
},
mutations: {
UPDATE_MESSAGE(state, newMessage) {
state.message = newMessage
},
},
actions: {
updateMessage({ commit }, message) {
commit('UPDATE_MESSAGE', message)
},
},
getters: {
message: state => state.message;
}
})
// 在组件中获取信息
const store = useStore()
const message = computed(() => store.getters.message)
const updateMessage = () => {
store.dispatch('updateMessage','New Message')
}
9.pinia状态管理
Pinia主打简单和轻量,其大小仅有1KB。
组合式API写法
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
return { count, doubleCount, increment }
})
// 在文件中引入
import { useCounterStore } from '@/stores/counter'
export default defineComponent({
setup() {
const counter = useCounterStore()
counter.count++;
// 两种写法
// 第一种
counter.$patch({ count: counter.count + 1})
// 第二种
counter.increment();
}
})
前3种解释了父组件向子组件传值的不同写法,4,5解释了子组件向父组件传值及调用方法。6解释了兄弟组件之间的通信,7解释了不同组件之间通信,8,9状态管理适合用于大型应用,组件众多,状态零散地分布在需要组件和组件之间的交互操作中,复杂度也不断增长的项目。
原文地址:https://blog.csdn.net/friend_ship/article/details/143475614
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!