摘要
这是一篇对Vue组件间通信方式的总结笔记,包含了Vue2 及 Vue3 常用通信方式,之所以写下来是因为这既是对自身学习的一种复盘,同时也希望对有需要的朋友提供一些便利。
组件通信的基础模型
根据上面这幅图,可以把组件之间需要通信的方式分为三大类,分别是:
组件通信方式详解
父子组件通信
-
Vue2 props / $emit ( $on ) 父组件
<cptx :val="123" @changeTxt="changeTxtFn">11</cptx>
methods: {
changeTxtFn (data) {
console.log('子组件传递过来的数据:', data)
}
},
子组件 <button @click="handleClick">向父组件传递数据</button>
export default {
name: 'cptx',
data () {
return {
bc: 11
}
},
props: ['val'],
mounted () {
console.log('this.val::', this.val)
this.$on('changeTxt', (val) => {
console.log('this.$on监听事件:', val)
})
},
methods: {
handleClick () {
this.$emit('changeTxt', '这是子组件向父组件穿的数据123')
}
}
}
子组件通过props 接收父组件传递的参数: 子组件通过$emit 向父组件传递数据,并通过$on 实现对事件的监听 -
Vue3 defineProps 、defineEmits、defineExpose 父组件要访问子组件自身的定义的数据以及方法,直接通过Vue2中 ref 方式是获取不到的,需要通过defineExpose 暴露出去 父组件
<template>
<PropsModal ref="abc" :propsNum="123" @changeParentPrice="handleChange">11</PropsModal>
<button @click='handleClick'>点击</button>
</template>
<script>
import { defineComponent, ref } from 'vue'
import PropsModal from '../components/propsModal.vue'
export default defineComponent({
setup() {
const abc = ref(null);
const handleChange = (data) => {
console.log('data::', data)
}
const handleClick = function () {
console.log('refAbc::', abc)
console.log('refAbc::', abc.value)
console.log('refAbc::', abc.value.bc)
console.log('refAbc::', abc.value.msg)
const {person:{value:{name,age}}} = toRefs(abc.value._obj);
console.log('refAbc::', toRefs(abc.value._obj))
console.log('refAbc::', toRefs(abc.value._obj).person.value)
console.log('refAbc::', name,age)
console.log('refAbc::', abc.value.btn)
abc.value.btn()
}
return {
handleChange,
abc,
handleClick
}
},
components: {
PropsModal
}
})
</script>
子组件 <script setup>
import {defineProps, defineEmits, defineExpose, toRefs, toRef} from 'vue';
let props = defineProps({
propsNum: {
type: [String, Number],
default: '2',
required: true
}
})
const {propsNum} = toRefs(props);
console.log('propsNum::', propsNum.value)
const emits = defineEmits(['changeParentPrice'])
const changeCarName = (e) => {
const currentVal = e.target.value;
emits('changeParentPrice', currentVal)
}
const bc = '1111111';
const msg = ref('子组件自定义数据');
const _obj = reactive({
person: {
name: '张三',
age: 18
}
});
const btn = () => {
console.log('打印输出msg', msg.value + '----')
};
defineExpose({
bc,
msg,
_obj,
btn
})
</script>
defineEmits 不能定义在局部函数中,必须定义在全局中,否则会报语法错误,例如:Uncaught ReferenceError: defineEmits is not defined 父组件无法 获取通过ref 获取子组件暴露出来的数据及函数 父组件可以 获取通过 ref 获取子组件通过 defineExpose 暴露出来的数据及函数 -
$parent(子组件使用) / $children(父组件使用) 不太建议使用 this.
p
a
r
e
n
t
和
t
h
i
s
.
parent 和 this.
parent和this.children,因为会增加维护成本
data () {
return {
aa: 112,
changeText: '祖先组件通过provide传递给后代组件的参数'
}
},
mounted () {
console.log('父组件使用 this.$children:', this.$children)
},
methods: {
homeMethods () {
console.log('父组件')
},
changeNun () {
this.changeText += '@ | '
console.log('this.changeText::', this.changeText)
},
changeTxtFn (data) {
console.log('子组件传递过来的数据:', data)
}
},
data () {
return {
bc: 11
}
},
mounted () {
console.log('子组件使用 this.$parent:', this.$parent)
},
methods: {
childMethods1 () {
console.log('子组件1')
},
childMethods2 () {
console.log('子组件2')
},
childMethods3 () {
console.log('子组件3')
},
handleClick () {
this.$emit('changeTxt', '这是子组件向父组件穿的数据123')
}
},
-
this.$refs(父组件使用) 通过this.refs 可以获取子组件实例对象,可以访问子组件data中的数据以及方法
<cptx ref="aa" :val="123">11</cptx>
<cptx ref="bb" :val="456">11</cptx>
mounted () {
console.log('this.$refs:', this.$refs);
}
-
$attrs / $listeners $attrs 用于多层次组件传递参数(组件标签的attribute,class和style除外),爷爷辈 组件向孙子辈 组件传递参数(注:参数不能被父辈prop识别,一旦被父辈prop识别且获取,则孙子辈组件不能获取到该参数) $listeners 用于多层次组件传递事件监听器,爷爷辈组件向父辈、孙子辈、曾孙子辈……组件传递事件(与 $attrs 不同,不存在半路被拦截的情况) 【注意】表示在Vue3的虚拟DOM中,事件监听器现在只是以on为前缀的attribute, 这样它就成为了$attrs对象的一部分,因此listeners被移除 了 查看官方说明
<template>
<div class="home-wrp">
<h1>{{ aa }}</h1>
<cptx :val="123" :msg1="msg1" :msg2="msg2" @handleClick1="handleClick1">11</cptx>
</div>
</template>
export default {
name: 'home',
data () {
return {
aa: '这是祖先组件',
msg1: '这是msg1的消息#####',
msg2: '这是msg2的消息@@@@@',
changeText: '祖先组件通过provide传递给后代组件的参数'
}
},
methods: {
handleClick1(data) {
console.log('执行祖先组件:', data)
}
},
}
-
子组件 作为父组件和孙子组件的传递中介(中间层),在儿子组件中给孙子组件添加v-bind="$attrs" 和 v-on="$listeners" ,这样孙子组件才能接收到数据和事件。 注意:在子组件中同样可以传递私有数据(bnc)以及事件,但是需要注意的是,单独传递的私有数据名称与祖先组件传递过来的数据对象里面的名称同名,那么将会把祖先组件传递进来的数据给覆盖掉,如果子组件传递的是同名的事件,却不会覆盖祖先组件传递的事件,但是会依次冒泡向上执行同名事件。
<template>
<div>
<h1>{{ bc }}</h1>
<h1>props传参:{{msg1}}</h1>
<button @click="handleClick2">执行父组件</button>
<PageModal1 :bnc="180" :val="11111111111" v-bind="$attrs" v-on="$listeners" @handleClick1="handleClick4"></PageModal1>
</div>
</template>
<script>
import PageModal1 from './pageModal1.vue'
export default {
name: 'cptx',
data () {
return {
bc: '这是子组件'
}
},
props: ['msg1'],
components: {
PageModal1
},
methods: {
handleClick2 (data) {
console.log('执行父组件')
this.$emit('handleClick1')
},
handleClick4 () {
console.log('执行父组件4444')
}
}
}
</script>
- 孙子组件
在孙子组件中只能使用props 接收从祖先组件传递过来的数据,一定要使传递的key保持同名。 例如在这个例子中祖先组件传递的 msg1, msg2, val
<template>
<div>
<h1>{{ mm }}</h1>
<h1>利用props获取祖先组件传递进来的val::{{val}}</h1>
<h1 style="color: red">利用props获取祖先组件传递进来的msg1::{{msg1 || '获取不到msg1数据, 因为被子组件拦截先获取'}}</h1>
<h1>利用props获取祖先组件传递进来的msg2::{{msg2}}</h1>
<button @click="handleClick3">执行孙子组件</button>
</div>
</template>
<script>
export default {
name: 'pageModal1',
data () {
return {
mm: '这是孙子组件'
}
},
props: ['val', 'msg2'],
methods: {
handleClick3 () {
console.log('执行孙子组件')
this.$emit('handleClick1', '123')
}
}
}
</script>
后面这张图看着可能有些乱,请各位同学结合代码一起看,另外上面这段示例是基于 Vue 2.5.2 版本。
兄弟组件通信
- vuex
- pinia
相关笔记一览 - eventBus
- $attrs / $listeners
主要通过消息订阅,发布的模式获取对应的数据
祖孙组件通信
-
兄弟组件同样适用于祖孙组件 -
provide / reject
-
vue2 中 provide 和 reject 的使用 注意:provide 和 inject 绑定并不是可响应的 。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
methods: {
changeNun () {
this.changeText += '@ | '
console.log('this.changeText::', this.changeText)
}
},
provide () {
return {
cd: this.changeText
}
}
export default {
name: 'pageModal1',
data () {
return {}
},
inject: ['cd'],
mounted () {
console.log('this.cd::', this.cd)
},
}
-
vue3 中provide 和 reject 的使用
<script setup>
import {provide, reactive} from 'vue'
let car = reactive({car: '奔驰',price: '50w'});
provide('params', car)
</script>
<script>
import { defineComponent, inject, toRefs } from 'vue'
export default defineComponent({
setup() {
let myParams = inject('params');
const{car, price} = toRefs(myParams);
console.log('测试provide/inject::', myParams,car.value, price.value)
return {
car,
price
}
},
})
</script>
关于Vue数据通信的方式就暂时总结到这里,后面还会保持更新。
如果本篇文章对各位同学有所帮助,欢迎大家点赞收藏 。
当然了,如果有不足之处,烦请各位同学能够指正,赵花花 将不胜感激。
|