目录
前言
vue2 的组件间的通信可戳这里
一、父子组件通信
1、父传子——defineProps
父组件:
<template>
<children :list="list"></children>
<div>
<input v-model="value" type="text" placeholder="请输入" />
<button @click="handleAdd"> 添加 </button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import Children from '@/views/parentChildCommunicat/parentToChildren/components/children.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
const handleAdd = () => {
list.value.push(value.value)
value.value = ''
}
</script>
子组件:
<template>
<ul>
<li v-for="item in props.list" :key="item">{{ item }}</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
list: {
type: Array,
default: () => []
}
})
</script>
2、子传父——defineEmits
子组件:
<template>
<div>
<input v-model="value" type="text" placeholder="请输入" />
<button @click="handleAdd"> 添加 </button>
</div>
</template>
<script setup>
import { ref, defineEmits } from 'vue'
const value = ref('')
const emits = defineEmits(['add'])
const handleAdd = () => {
emits('add', value.value)
value.value = ''
}
</script>
父组件:
<template>
<ul>
<li v-for="item in list" :key="item">{{ item }}</li>
</ul>
<son @add="add" />
</template>
<script setup>
import { ref } from 'vue'
import Son from './components/son.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const add = (value) => {
list.value.push(value)
}
</script>
3、v-model 实现父子组件双向通信
v-model 是 Vue 中一个比较出色的语法糖,比如:
对于代码:
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
使用 v-model 可以简写为:
<ChildComponent v-model:title="pageTitle" />
下面就来看看如何用 v-model 实现父子组件双向通信。
子组件:
<template>
<div>
<input v-model="value" type="text" placeholder="请输入" />
<button @click="handleAdd"> 添加 </button>
</div>
</template>
<script setup>
import { ref, defineEmits, defineProps } from 'vue'
const value = ref('')
const props = defineProps({
list: {
type: Array,
default: () => []
}
})
const emits = defineEmits(['update:list'])
const handleAdd = () => {
const arr = props.list
arr.push(value.value)
emits('update:list', arr)
value.value = ''
}
</script>
父组件:
<template>
<ul>
<li v-for="i in list" :key="i">{{ i }}</li>
</ul>
<child-components v-model:list="list" />
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './components/child-components.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
</script>
二、跨级组件通信
1、refs 实现组件间的通信(可跨级)——defineExpose
在使用 “选项式 API” 时,可以通过
this.$refs.name
的方式获取指定元素或者组件。
在 “组合式 API” 中,必须通过定义一个同名的 Ref 响应式对象,在组件挂载后,才可以通过 ref 的方式获取组件或者元素。
父组件:
<template>
<ul>
<li v-for="item in childRefs?.list" :key="item">{{ item }}</li>
</ul>
<children-refs ref="childRefs" />
</template>
<script setup>
import { ref } from 'vue'
import ChildrenRefs from './components/children-refs.vue'
const childRefs = ref(null)
</script>
子组件:
<template>
<div>
<input v-model="value" type="text" placeholder="请输入" />
<button @click="handleAdd"> 添加 </button>
</div>
</template>
<script setup>
import { ref, defineExpose } from 'vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
const handleAdd = () => {
list.value.push(value.value)
value.value = ''
}
defineExpose({ list })
</script>
【注意】setup 组件默认是关闭的,也就是说:通过模板 ref 获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。若要公开则必须使用 defineExpose
来对外暴露。
2、Provide + Inject 组件通信(可跨级)
provide 和 inject 是 Vue 中提供的一对 API。该 API 可以实现:祖先组件向子孙组件传递数据(无论层级有多深),包括父子组件传值。
(1)、基于 Provide + Inject 的基本通信
父组件:
<template>
<son-provide-inject />
<div>
<input v-model="value" type="text" placeholder="请输入" />
<button @click="handleAdd"> 添加 </button>
</div>
</template>
<script setup>
import { ref, provide, readonly } from 'vue'
import SonProvideInject from './components/son-provide-inject.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')
// 将数据置为只读
const readonlyList = readonly(list)
// 向子组件提供数据
provide('list', readonlyList.value)
const handleAdd = () => {
list.value.push(value.value)
value.value = ''
}
</script>
子组件:
<template>
<ul>
<li v-for="item in list" :key="item">{{ item }}</li>
</ul>
</template>
<script setup>
import { inject } from 'vue'
// 接受父组件提供的数据
const list = inject('list')
</script>
【注意】使用 provide 进行数据传递时,尽量用 readonly
将数据置为只读,以避免子组件修改父级传递过去的数据。
(2)、基于 Provide + Inject 的异步通信
基于 Provide + Inject 的异步通信 与 基本通信的区别:主要是 provide 里的变动。
父组件:
<template>
<son-provide-inject-async />
<button @click="changeStatus"> 添加 </button>
</template>
<script setup>
import { ref, provide, computed } from 'vue'
import SonProvideInjectAsync from './components/son-provide-inject-async.vue'
const flag = ref(false)
// 向子组件提供数据
provide(
'asyncFlag',
computed(() => flag.value)
)
// 异步数据
const changeStatus = () => {
setTimeout(() => {
flag.value = true
}, 3000)
}
</script>
子组件:
<template>
<div>{{ asyncFlag }}</div>
</template>
<script setup>
import { inject } from 'vue'
// 接受父组件提供的数据
const asyncFlag = inject('asyncFlag')
</script>
3、Vue3 借助 mitt 来实现组件间的通信——事件总线
Vue3 中移除了“事件总线”,但是可以借助于第三方工具来完成,Vue官方推荐 mitt 或 tiny-emitter。本小节只说下 mitt。
一般不推荐使用全局事件总线的方式来实现组件通信,虽然比较简单粗暴,但是长久来说维护事件总线是一个大难题。
安装 mitt:
npm i -S mitt
在 src/utils 目录下新建 bus.js 文件:
import mitt from 'mitt'
const emiiter = mitt()
export default emiiter
使用 mitt 进行兄弟组件的通信:
<template>
<p>这是A页面</p>
<button @click="btn">点击</button>
</template>
<script setup>
import emitter from "@/utils/bus";
const str = ref("我是A组件的数据");
const btn = () => {
emitter.emit("fn", str);
};
</script>
<template>
<p>这是B页面</p>
{{ s }}
</template>
<script setup>
import emitter from "@/utils/bus";
const s = ref(""); // 等待接收
const handelEventFn = (e) => {
s.value = e.value;
};
onBeforeMount(() => {
emitter.on("fn", handelEventFn); // 开启监听,监听到fn事件后,执行handelEventFn函数
});
onUnmounted(() => {
emitter.off("fn"); // 取消fn事件的全部处理函数的监听
});
</script>
4、使用状态管理工具——Vuex 或 Pinia
Vuex 和 Pinia 是可用于 Vue3 中的状态管理工具,使用这两个工具都可以实现各种组件之间的通信。
三、子组件更新父组件的变量
1、修改父组件的 基本类型的数据
(1)、v-model
父组件中
const test1 = ref('a')
// 使用watchEffect监听test的变化
watchEffect(() => {
console.log('Test1的值发生了变化:', test1.value);
});
子组件中
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
value.value = 'A'
2、修改父组件的 引用类型的数据