VUE 清单 - 组件通信
VUE3
文档中未明确指出版本的地方,则特指最新的 VUE3 版本。
vue 是数据驱动视图更新的框架,项目是由多个不同组件组合嵌套而成,那么组件之间的数据通信就非常重要了。
下面简单介绍几种组件间的通信方法:
父子组件通信
- 方式一:props / emits
xml
<!-- 方式一: props / emits -->
<!-- 父组件 -->
<template>
<Child :data-desc="desc" @changeDesc="changeDesc" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child'
const desc = ref('我是描述')
const changeDesc = (value) => {
desc.value = value
}
</script>
<!-- 子组件 -->
<template>
<div>
<p>From Parent: {{ dataDesc }}</p>
<button @click="changeHandle">改变父组件的描述内容</button>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
dataDesc: String,
})
const emit = defineEmits(['changeDesc'])
const changeHandle = () => {
emit('changeDesc', '我来自子元素')
}
</script>
- 方式二:v-model / emits
xml
<!-- 方式二: v-model / emits -->
<!-- 父组件 -->
<template>
<Child v-model="title" v-model:desc="desc" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child'
const title = ref('我是标题')
const desc = ref('我是描述')
</script>
<!-- 子组件 -->
<template>
<div>
<p>From Parent: {{ modelValue }} -- {{ desc }}</p>
<button @click="changeHandle">改变父组件的描述内容</button>
</div>
</template>
<script lang="ts" setup>
const props = defineProps({
modelValue: String,
desc: String,
})
const emit = defineEmits(['update:modelValue', 'update:desc'])
const changeHandle = () => {
emit('update:modelValue', 'Change Title')
emit('update:desc', '我来自子元素')
}
</script>
- 方式三:ref / emits
xml
<!-- 方式三: ref / emits -->
<!-- 父组件 -->
<template>
<Child ref="refChild" @changeDesc="changeDesc" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child'
const desc = ref('我是描述')
const refChild = ref(null)
watchEffect(() => {
if (refChild.value) {
refChild.value.sayHi = desc.value
}
})
</script>
<!-- 子组件 -->
<template>
<div>
<p>From Parent: {{ sayHi }}</p>
<button @click="changeHandle">改变父组件的描述内容</button>
</div>
</template>
<script lang="ts" setup>
const sayHi = ref('')
defineExpose({
sayHi // 明确的暴露接口
})
const emit = defineEmits(['changeDesc'])
const changeHandle = () => {
emit('changeDesc', '我来自子元素')
}
</script>
非父子组件通信
TIP
「非父子组件通信」的方式是可以适用于「父子组件通信」的,反之不成立。
- 方式四:provide / inject
provide / inject
适用范围:祖孙组件(包含父子组件)
xml
<!-- 方式四: provide / inject -->
<!-- 父组件 -->
<template>
<Child @changeDesc="changeDesc" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child'
const title = ref('我是标题')
const desc = ref('我是描述')
const changeDesc = (value) => {
desc.value = value
}
provide('parent', {
title,
desc,
changeDesc
})
</script>
<!-- 子组件 -->
<template>
<div>
<p>From Parent: {{ parentData.title }} -- {{ parentData.desc }}</p>
<button @click="changeHandle">改变父组件的描述内容</button>
</div>
</template>
<script lang="ts" setup>
const parentData = inject('parent')
const { changeDesc } = parentData
const changeHandle = () => {
changeDesc('From子元素')
}
</script>
- 方式五:EventBus(mitt)
Mitt 是一个体积极小的第三方消息发布/订阅式 JavaScript 库
第一步: 安装 Mitt;
js
// 安装 Mitt
$ npm i mitt
第二步:在 src/utils 下新建一个 bus.js 文件;
js
/* bus.js */
import mitt from 'mitt'
const bus = {}
const emitter = mitt()
bus.$on = emitter.on
bus.$off = emitter.off
bus.$emit = emitter.emit
export default bus
xml
<!-- 方式五: EventBus(mitt) -->
<!-- 父组件 -->
<template>
<Child />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import Child from './Child'
// 引入第二步中配置的bus(路径自定义)
import bus from "/@/utils/bus"
const desc = ref('我是描述')
bus.$on('desc', (data: string) => {
desc.value = data
})
onMounted(() => {
bus.$emit('bobo', desc.value)
})
</script>
<!-- 子组件 -->
<template>
<div>
<p>From Parent: {{ parentDesc }}</p>
<button @click="changeHandle">改变父组件的描述内容</button>
</div>
</template>
<script lang="ts" setup>
// 引入第二步中配置的bus(路径自定义)
import bus from "/@/utils/bus"
const parentDesc = ref('')
bus.$on('bobo', (data: string) => {
parentDesc.value = data
})
const changeHandle = () => {
bus.$emit('desc', 'From子元素')
}
</script>
- 方式六:Vuex / pinia
参考: VUE 全家桶之 Pinia
$attrs / $listeners
VUE2 中可以通过「$attrs / $listeners」来进行祖孙(包括父子)组件间通信。
但是VUE3中移除了$listeners,且调整了$attrs。