vue3组件通信方式
vue3学了一直没用上,今天来复习一下vue3的组件通信方式有哪些,不喜勿喷
1.props
props 是 Vue 中最常见的父子通信方式,使用起来也比较简单。根据上面的demo,我们在父组件中定义了数据和对数据的操作,子组件只渲染一个列表。
父组件代码如下:
<template>
<!-- child component --> <child-components :list="list"></child-components> <!-- parent component -->
<div class="child-wrap input-group">
<input v-model="value" type="text" class="form-control" placeholder="Pleasenter"/> <div class="input-group-append">
<button @click="handleAdd" class="btn btn-primary" type="button">add</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')// event handling function triggered by add
const handleAdd = () => {
list.value.push(value.value)
value.value = ''
}
</script>
子组件只需要渲染父组件传递的值。
代码如下:
<template>
<ul class="parent list-group">
<li class="list-group-item" v-for="i in props.list" :key="i">{{ i }}</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
list: {
type: Array,
default: () => [],
},
})
</script>
2.Emit
Emit也是Vue中最常见的组件通信方式,用于子组件向父组件传递消息。
我们在父组件中定义列表,子组件只需要传递添加的值。
子组件代码如下:
<template>
<div class="child-wrap input-group">
<input v-model="value" type="text" class="form-control" placeholder="Pleaseenter"/> <div class="input-group-append">
<button @click="handleSubmit" class="btn btn-primary" type="button">add</button> </div>
</div>
</template>
<script setup>
import { ref, defineEmits } from 'vue'
const value = ref('')
const emits = defineEmits(['add'])
const handleSubmit = () => {
emits('add', value.value)
value.value = ''
}
</script>
点击子组件中的【添加】按钮后,我们会发出一个自定义事件,并将添加的值作为参数传递给父组件。
父组件代码如下:
<template><!-- parent component -->
<ul class="parent list-group">
<li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
</ul>
<!-- child component -->
<child-components @add="handleAdd"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])// event handling function triggered by add
const handleAdd = value => {
list.value.push(value)
}
</script>
在父组件中,只需要监听子组件的自定义事件,然后执行相应的添加逻辑即可。
3、v-model
v-model 是 Vue 中一个优秀的语法糖,比如下面的代码。
<ChildComponent v-model:title="pageTitle" />
这是以下代码的简写形式
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
这确实容易了很多。现在我们将使用 v-model 来实现上面的示例。
子组件
<template>
<div class="child-wrap input-group">
<input v-model="value" type="text" class="form-control" placeholder="Please enter"/> <div class="input-group-append">
<button @click="handleAdd" class="btn btn-primary" type="button">add</button>
</div>
</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'])// Add action
const handleAdd = () => {
const arr = props.list
arr.push(value.value)
emits('update:list', arr)
value.value = ''
}
</script>
在子组件中,我们先定义props和emits,添加完成后再发出指定的事件。
注意:update:*是Vue中固定的写法,*代表props中的一个属性名。
在父组件中使用比较简单,代码如下:
<template>
<!-- parent component -->
<ul class="parent list-group">
<li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
</ul>
<!-- child component -->
<child-components v-model:list="list"></child-components>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
</script>
4、Refs
使用API选项时,我们可以通过this.$refs.name获取指定的元素或组件,但在组合API中不行。如果我们想通过ref获取,需要定义一个同名的Ref对象,在组件挂载后可以访问。示例代码如下:
<template>
<ul class="parent list-group">
<li class="list-group-item" v-for="i in childRefs?.list" :key="i">{{ i }}</li>
</ul>
<!-- The value of the child component ref is the same as that in the <script> -->
<child-components ref="childRefs"></child-components>
<!-- parent component -->
</template>
<script setup>
import { ref } from 'vue'
import ChildComponents from './child.vue'
const childRefs = ref(null)
</script>
子组件代码如下:
<template>
<div class="child-wrap input-group">
<input v-model="value" type="text" class="form-control" placeholder="Please enter"/> <div class="input-group-append">
<button @click="handleAdd" class="btn btn-primary" type="button">add</button> </div>
</div>
</template>
<script setup>
import { ref, defineExpose } from 'vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')// event handling function triggered by add
const handleAdd = () => {
list.value.push(value.value)
value.value = ''
}
defineExpose({ list })
</script>
注意:默认情况下,setup 组件是关闭的,通过模板 ref 获取组件的公共实例。如果需要公开,需要通过defineExpose API 公开。
5、provide/inject
provide/inject是 Vue 中提供的一对 API。无论层级多深,API 都可以实现父组件到子组件的数据传递。示例代码如下所示:
父组件
<template>
<!-- child component -->
<child-components></child-components>
<!-- parent component -->
<div class="child-wrap input-group">
<input v-model="value" type="text" class="form-control" placeholder="Please enter" /> <div class="input-group-append">
<button @click="handleAdd" class="btn btn-primary" type="button"> add</button> </div>
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import ChildComponents from './child.vue'
const list = ref(['JavaScript', 'HTML', 'CSS'])
const value = ref('')// Provide data to child components.
provide('list', list.value)// event handling function triggered by add
const handleAdd = () => {
list.value.push(value.value)
value.value = ''
}
</script>
子组件
<template>
<ul class="parent list-group">
<li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>
</ul>
</template>
<script setup>
import { inject } from 'vue'// Accept data provided by parent component
const list = inject('list')
</script>
注意:使用 provide 进行数据传输时,尽量使用 readonly 封装数据,避免子组件修改父组件传递的数据。
6、eventBus Vue 3 中移除了 eventBus,但可以借助第三方工具来完成。Vue 官方推荐使用 mitt 或 tiny-emitter。在大多数情况下,不建议使用全局事件总线来实现组件通信。虽然比较简单粗暴,但是维护事件总线从长远来看是个大问题。
bus.js
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from 'mitt';
const $bus = mitt();
export default $bus;
父组件
<template>
<div class="box">
<h1>全局事件总线$bus</h1>
<hr />
<div class="container">
<Child1></Child1>
<Child2></Child2>
</div>
</div>
</template>
<script setup lang="ts">
//引入子组件
import Child1 from "./Child1.vue";
import Child2 from "./Child2.vue";
</script>
子组件
<template>
<div class="child2">
<h2>我是子组件2:曹丕</h2>
<button @click="handler">点击我给兄弟送一台法拉利</button>
</div>
</template>
<script setup lang="ts">
//引入$bus对象
import $bus from '../../bus';
//点击按钮回调
const handler = ()=>{
$bus.emit('car',{car:"法拉利"});
}
</script>
子组件
<template>
<div class="child1">
<h3>我是子组件1:曹植</h3>
</div>
</template>
<script setup lang="ts">
import $bus from "../../bus";
//组合式API函数
import { onMounted } from "vue";
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on("car", (car) => {
console.log(car);
});
});
</script>
7、vuex/pinia Vuex 和 Pinia 是 Vue 3 中的状态管理工具,使用这两个工具可以轻松实现组件通信。由于这两个工具都比较强大,这里就不一一展示了。有关详细信息,请参阅文档。
讨论:
- 什么时候用evnetbus?
- 已经有了vuex,为什么还要用pinia?
转载自:https://juejin.cn/post/7240365776738009144