vue2 的12种 vs vue3 的9种组件通信整理
1、vue2 vs vue3组件通信有哪些
Vue中组件通信方式有很多,其中Vue2和Vue3实现起来也会有很多差异。如下表总结
vue2 | vue3 | 说明 |
---|---|---|
props | Props/defineProps | 父传子 |
$emit / v-on | defineEmits | 子传父 |
$attrs/$listeners | attrs | 父传子、跨层级组件通信 |
$children/$parent | 无 | 父传子,子传父 |
$ref | expose / ref | 父组件访问子组件 |
v-model | v-model | 子传父,父传子 |
.sync | 无 | 子传父,父传子 |
插槽 slot | 插槽 slot | 默认插槽、具名插槽、作用域插槽 父传子, |
provide / inject | provide / inject | 跨层级组件通信 |
EventBus | mitt | 兄弟组件通信、跨层级组件通信 |
Vuex | Vuex | 复杂关系的组件数据传递,跨组件通信 兄弟组件通信、跨层级组件通信 |
$root | 无 | 跨层级组件通信 |
2、vue2组件通信
2.1 props
props是组件通信中最常用的通信方式之一。父组件通过v-bind传入,子组件通过props接收
//父组件
<template>
<div>
<Child :msg="parentMsg" />
</div>
</template>
<script>
import Child from './Child'
export default {
components:{
Child
},
data() {
return {
parentMsg: '父组件信息'
}
}
}
</script>
//子组件
<template>
<div>
{{msg}}
</div>
</template>
<script>
export default {
props:['msg']
}
</script>
2.2 $emit / v-on
子组件可以通过emit发布一个事件并传递一些参数,父组件通过v-on进行这个事件的监听
// 子组件 派发
export default {
data(){
return { msg: "这是发给父组件的信息" }
},
methods: {
handleClick(){
this.$emit("sendMsg",this.msg)
}
},
}
// 父组件 响应
<template>
<child v-on:sendMsg="getChildMsg"></child>
// 或 简写
<child @sendMsg="getChildMsg"></child>
</template>
export default {
methods:{
getChildMsg(msg){
console.log(msg) // 这是父组件接收到的消息
}
}
}
2.3 $attrs/$listeners
$attrs
:包含父作用域里除 class 和 style 除外的非 props 属性集合。通过 this.$attrs 获取父作用域中所有符合条件的属性集合,然后还要继续传给子组件内部的其他组件,就可以通过 v-bind="$attrs"。
$listeners
:包含父作用域里 .native 除外的监听事件集合。如果还要继续传给子组件内部的其他组件,就可以通过 v-on="$linteners"。
其中$attrs 和$listeners 使用法相同
// 父组件
<template>
<child :name="name" title="1111" ></child>
</template
export default{
data(){
return {
name:"小明"
}
}
}
// 子组件
<template>
// 继续传给孙子组件
<sun-child v-bind="$attrs"></sun-child>
</template>
export default{
props:["name"], // 这里可以接收,也可以不接收
mounted(){
// 如果props接收了name 就是 { title:'张三'},否则就是{ name:"小明", title:1111 }
console.log(this.$attrs)
}
}
2.4 $children / $parent
$children
:获取到一个包含所有子组件(不包含孙子组件)的 VueComponent 对象数组,可以直接拿到子组件中所有数据和方法等
$parent
:获取到一个父节点的 VueComponent 对象,同样包含父节点中所有数据和方法等
//父组件
export default{
mounted(){
this.$children[0].someMethod() // 调用第一个子组件的方法
this.$children[0].name // 获取第一个子组件中的属性
}
}
// 子组件
export default{
mounted(){
this.$parent.someMethod() // 调用父组件的方法
this.$parent.name // 获取父组件中的属性
}
}
2.5 $ref
ref 如果在普通的DOM元素上,引用指向的就是该DOM元素;
如果在子组件上,引用的指向就是子组件实例,然后父组件就可以通过 $ref 主动获取子组件的属性或者调用子组件的方法
// 子组件
export default {
data(){
return {
name:"张三"
}
},
methods:{
someMethod(msg){
console.log(msg)
}
}
}
// 父组件
<template>
<child ref="child"></child>
</template>
<script>
import Child from './Child'
export default {
components: {
Child
},
mounted(){
const child = this.$refs.child
console.log(child.name) // 张三
child.someMethod("调用了子组件的方法")
}
}
</script>
2.6 v-model
详情请看:vue2和vue3的数据双向绑定差异整理
2.7 .sync
可以帮我们实现父组件向子组件传递的数据 的双向绑定,所以子组件接收到数据后可以直接修改,并且会同时修改父组件的数据
// 父组件
<template>
<child :page.sync="page"></child>
</template>
<script>
export default {
data(){
return {
page:1
}
}
}
// 子组件
export default {
props:["page"],
computed(){
// 当我们在子组件里修改 currentPage 时,父组件的 page 也会随之改变
currentPage {
get(){
return this.page
},
set(newVal){
this.$emit("update:page", newVal)
}
}
}
}
</script>
2.8 插槽 slot
把子组件的数据通过插槽的方式传给父组件使用,然后再插回来
// 子组件
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
export default{
data(){
return {
user:{ name:"张三" }
}
}
}
//父组件
<template>
<div>
<child v-slot="slotProps">
{{ slotProps.user.name }}
</child>
</div>
</template>
2.9 provide / inject
provide:是一个对象,或者是一个返回对象的函数。里面包含要给子孙后代属性
inject:一个字符串数组,或者是一个对象。获取父组件或更高层次的组件provide的值,既在任何后代组件都可以通过inject获得
//父组件
<script>
import Child from './Child'
export default {
components: {
Child
},
data() {
return {
msg1: '子组件msg1',
msg2: '子组件msg2'
}
},
provide() {
return {
msg1: this.msg1,
msg2: this.msg2
}
}
}
</script>
//子组件
<script>
export default {
inject:['msg1','msg2'],
created(){
//获取高层级提供的属性
console.log(this.msg1) //子组件msg1
console.log(this.msg2) //子组件msg2
}
}
</script>
2.10 EventBus
兄弟组件通信可以通过一个事件中心EventBus实现,既新建一个Vue实例来进行事件的监听,触发和销毁。不管是父子组件,兄弟组件,跨层级组件等都可以使用它完成通信操作
定义方式有三种
// 方法一
// 抽离成一个单独的 js 文件 Bus.js ,然后在需要的地方引入
// Bus.js
import Vue from "vue"
export default new Vue()
// 方法二 直接挂载到全局
// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()
// 方法三 注入到 Vue 根对象上
// main.js
import Vue from "vue"
new Vue({
el:"#app",
data:{
Bus: new Vue()
}
})
使用如下,以方法一按需引入为例
// 在需要向外部发送自定义事件的组件内
<template>
<button @click="handlerClick">按钮</button>
</template>
import Bus from "./Bus.js"
export default{
methods:{
handlerClick(){
// 自定义事件名 sendMsg
Bus.$emit("sendMsg", "这是要向外部发送的数据")
}
}
}
// 在需要接收外部事件的组件内
import Bus from "./Bus.js"
export default{
mounted(){
// 监听事件的触发
Bus.$on("sendMsg", data => {
console.log("这是接收到的数据:", data)
})
},
beforeDestroy(){
// 取消监听
Bus.$off("sendMsg")
}
}
2.11 Vuex
Vuex 是状态管理器,集中式存储管理所有组件的状态。详情请看官方文档:Vuex
2.12 $root
$root是一个属性,用于访问根组件实例。它的作用是连接所有其他的 Vue 实例组件,并向子组件提供全局配置和实例方法。根实例是 Vue 的上下文环境,包含了整个 Vue 应用的数据和方法。使用$root属性,可以方便地访问根实例的方法、数据和生命周期钩子函数。
// main.js
new Vue({
data() {
return {
isUpdate: true
};
},
router,
store,
render: h => h(App)
}).$mount('#app');
// 组件 created() 或mounted(), method中
created() {
console.log(this.$root.isUpdate);
this.$root.isUpdate = false;
}
3、vue3组件通信
3.1 Props/defineProps
props是组件通信中最常用的通信方式之一。父组件通过v-bind传入,子组件通过props接收
使用方法详情看官方文档:Props
3.2 defineEmits
子组件可以通过emit发布一个事件并传递一些参数,父组件通过v-on进行这个事件的监听
// 子组件
<template>
<div>
<button @click="handleIncrement">Increment</button>
<button @click="handleDecrement">Decrement</button>
</div>
</template>
<script setup lang="ts">
const emit = defineEmits(['increment', 'decrement']);
const handleIncrement = () => {
emit('increment');
};
const handleDecrement = () => {
emit('decrement');
};
</script>
// 父组件
<template>
<div>
<p>Counter: {{ counter }}</p>
<chlid @increment="increment" @decrement="decrement" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Chlid from './chlid.vue';
const counter = ref(0);
const increment = () => {
counter.value++;
};
const decrement = () => {
counter.value--;
};
</script>
3.3 attrs
子组件使用$attrs可以获得父组件除了props传递的属性和特性绑定属性 (class和 style)之外的所有属性。
//父组件
<template>
<div>
<Child @parentFun="parentFun" :msg1="msg1" :msg2="msg2" />
</div>
</template>
<script>
import Child from './Child'
export default {
components:{
Child
},
data(){
return {
msg1:'子组件msg1',
msg2:'子组件msg2'
}
},
methods: {
parentFun(val) {
console.log(`父组件方法被调用,获得子组件传值:${val}`)
}
}
}
</script>
//子组件
<template>
<div>
<button @click="getParentFun">调用父组件方法</button>
</div>
</template>
<script>
export default {
methods:{
getParentFun(){
this.$listeners.parentFun('我是子组件数据')
}
},
created(){
//获取父组件中所有绑定属性
console.log(this.$attrs) //{"msg1": "子组件msg1","msg2": "子组件msg2"}
//获取父组件中所有绑定方法
console.log(this.$listeners) //{parentFun:f}
}
}
</script>
3.4 expose / ref
$refs可以直接获取元素属性,同时也可以直接获取子组件实例。
注意:通过ref获取子组件实例必须在页面挂载完成后才能获取。子组件必须元素或方法暴露出去父组件才能获取到
//父组件
<template>
<div>
<Child ref="child" />
</div>
</template>
<script setup>
import Child from './Child'
import { ref, onMounted } from "vue";
const child = ref() //注意命名需要和template中ref对应
onMounted(() => {
//获取子组件属性
console.log(child.value.msg) //子组件元素
//调用子组件方法
child.value.childFun('父组件信息')
})
</script>
//子组件
<template>
<div>
</div>
</template>
<script setup>
import { ref,defineExpose } from "vue";
const msg = ref('子组件元素')
const childFun = (val) => {
console.log(`子组件方法被调用,值${val}`)
}
//必须暴露出去父组件才会获取到
defineExpose({
childFun,
msg
})
</script>
3.5 v-model
可以支持多个数据双向绑定
// 父组件
<child v-model:key="key" v-model:value="value"></child>
<script setup>
import child from "./child.vue"
import { ref, reactive } from "vue"
const key = ref("1111")
const value = ref("2222")
</script>
// 子组件
<template>
<button @click="handlerClick">按钮</button>
</template>
<script setup>
// 方法一 不适用于 Vue3.2版本,该版本 useContext()已废弃
import { useContext } from "vue"
const { emit } = useContext()
// 方法二 适用于 Vue3.2版本,不需要引入
// import { defineEmits } from "vue"
const emit = defineEmits(["key","value"])
// 用法
const handlerClick = () => {
emit("update:key", "新的key")
emit("update:value", "新的value")
}
</script>
3.6 插槽 slot
把子组件的数据通过插槽的方式传给父组件使用,然后再插回来。有默认插槽、具名插槽、作用域插槽三种方式
使用方法请看官方文档:插槽slot
3.7 provide / inject
provide / inject 为依赖注入
provide
:可以让我们指定想要提供给后代组件的数据或
inject
:在任何后代组件中接收想要添加在这个组件上的数据,不管组件嵌套多深都可以直接拿来用
// 父组件
<script setup>
import { provide } from "vue"
provide("name", "张三")
</script>
// 孙组件
<script setup>
import { inject } from "vue"
const name = inject("name")
console.log(name) // 张三
</script>
3.8 mitt
Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus
详情请看文档:mitt
3.9 Vuex
Vuex 是状态管理器,集中式存储管理所有组件的状态。详情请看官方文档:Vuex