Vue3的组件通信主要分为以下几种
- 父子通信(props和自定义事件this.$emit)
- 非父子组件通信
-
Provide/Inject -
Mitt全局事件总线
-
- 插槽Slot
- expose/ref(传的是属性或者方法)
- attrs(非props属性集合)
- v-model
- Vuex 仓库
一、 父子通信(props和自定义事件this.$emit)
在父组件向子组件传值的时候用的是props
通过在组件上注册一些自定义的attribute,父组件给这些attribute赋值,子组件通过props获取attribute的名称来获取对应的值。
props有两种用法,一种是字符串数组props:['attr名称']
一种是对象的用法,对象类型的时候,我们除了获取attribute名称的同时,指定它传递的类型,默认值,是否必须传值等等。
type的类型都可以是什么呢? String、Number、Boolean、Array、Object、Date、Function、Symbol
子组件向父组件传递数据
在子组件中监听事件,传递数据,this.$emit("事件监听",参数)
父组件中触发事件 <子组件 @事件监听="方法">
父组件:
<template>
<div>
//以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中
<HelloWorld @addnum="add"/>
<p>父组件的数值为:{{num}}</p>
<button @click="active">改变num的值</button>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
data() {
return {
num:0
}
},
methods: {
add(num){
this.num=num
},
active(){
this.num = 15
}
},
}
</script>
子组件:
<template>
<div>
<button @click="btnclick">传值</button>
<p>子组件中num的值:{{num}}</p>
</div>
</template>
<script>
export default {
data() {
return {
num:10
}
},
methods: {
btnclick(){
console.log("传值");
// 定义好触发事件的名称
this.$emit("addnum",this.num)
}
},
}
</script>
二、非父子组件通信
provide与inject
官方文档:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
project和inject主要是用于深度嵌套的时候:
祖先元素给子孙元素传值,不用关心传递者和接收者
这个时候,使用props来逐级传递可以但是没有必要,所以我们就是用到了provide和inject
但是使用对象类型的provide的时候,this不是组件实例的this,而是script的this,所以我们参考data数据的类型,使用provide的时候,一律使用函数类型,这样的话,this指的就是组件实例的this
import HelloWorld from './components/HelloWorld.vue'
import {computed} from 'vue';
export default {
name: 'App',
components: {
HelloWorld
},
data() {
return {
num:18777
}
},
methods: {
numactive(){
this.num = 265
}
},
provide(){
return{
name:"康博璇",
age:18,
height:165,
// 这里的this的script中的this,所以将provide写成一个函数
// this是provide()中的this,这个this就是组件实例
// 但是,这个num并不是双向变换的,不是响应式的
// num:this.num
num: computed(()=>this.num)
// computed 函数返回的是一个ref对象
}
}
}
</script>
Mitt全局事件总线
在Vue3中,事件总线bus被取消了,如果我们需要继续使用事件总线的话,我们就要通过第三方库mitt
安装库: npm install mitt
封装工具eventbus.js :
在项目中使用 import emitter from './eventbus'
发送:
<template>
<div>
<p>Hello组件</p>
<button @click="btnhello">Hello</button>
<p>name:{{age}}</p>
</div>
</template>
<script>
import emitter from './事件总线的使用/eventbus.js';
export default {
name: 'HelloWorld',
methods: {
btnhello(){
console.log("Hello中的按钮被点击了");
//发送事件why,传递参数name
emitter.emit("why",{name:"kangboxuan"})
emitter.emit("kebi",{name:"kebi"})
}
},
}
接收:
<template>
<div>
<p>{{name}}</p>
</div>
</template>
<script>
import emitter from './eventbus';
export default {
name: 'Vue3TextHome',
created(){
emitter.on("why",(name)=>{
console.log("why",name);
})
emitter.on("kebi",(name)=>{
console.log("kebi",name);
})
},
};
</script>
三、插槽slot
抽取共性保留,slot占位不同功能
在开发中,为了使组件具有更强得复用性,我们通常不会把特定的内容区写死,而是通过slot占位,在不同的调用情况下,由使用它的组件决定这个占位的地方到底显示什么样子的元素
默认插槽,多个插槽,具名插槽,动态插槽,作用域插槽\
1、默认插槽
在组件中通过slot占位,然后使用的时候,可以插入普通内容,HTML元素,组件元素等等,同时在slot中也可以放入默认内容,如果在其他组件中没有插入对应的元素,则显示默认内容:
<div>
<slot>
//如果在使用其他组件的时候,没有插入内容,则会显示这段话,如果有插入的内容则会替换掉这段话
<p>我是Myslot的默认内容</p>
</slot>
</div>
</template>
使用插槽:
<template>
<div>
<Myslot>
我是替换插槽中的内容的
</Myslot>
</div>
</template>
<script>
import Myslot from "./components/Myslot.vue";
export default {
name: "App",
components: {
Myslot
},
};
</script>
2、多个插槽
在组件中可以有多个插槽,如果插槽没有自己的名称的话,默认情况下就会把每一个插槽都替换掉
3、具名插槽
在使用中,通常我们会给一个组件设置多个插槽,且每一个插槽都有它自己的用处,所以我们不可以将插槽写成一样的,那么我们应该如何区分不同的插槽呢?
这个时候就用到了具名插槽,直白说,就是给插槽起名字,在组件中使用插槽的时候,使用对应名字的插槽,就可以让我们把对应的内容插到对应的插槽中了
< slot>元素有一个特殊的 attribute(属性):name;一个不带 name 的slot,会带有隐含的名字default
在使用对应插槽的时候通过v-slot="name的值"来绑定对应的插槽
具名插槽:
<template>
<div>
<slot name="slotone">
<!-- <p>我是Myslot的默认内容</p> -->
</slot>
</div>
</template>
使用具名插槽:
<template>
<div>
<Myslot>
//通过v-slot:来绑定具名插槽
<template v-slot:slotone>
我是来替换具名插槽的
</template>
</Myslot>
</div>
</template>
4、作用域插槽
在Vue中有渲染作用域的概念:
父级模板里的所有内容都是在父级作用域中编译的;
子模板里的所有内容都是在子作用域中编译的;
但是有的时候我们希望把父模板的东西通过插槽传给子模版,会发现是不可以的,因为受到作用域的限制
四、expose/ref(传的是属性或者方法)
子组件通过 expose暴露自身的方法和数据, 父组件通过 ref 获取到了子组件并调用其方法和数据
五、attrs(非props属性集合)
attrs传的是父传子的未被props接收的属性,包括class,style,id等等属性
传递数据:
<template>
<div>
<Child :title="title" content="哈哈哈哈"
class="aaa" kbx="kk"/>
</div>
</template>
接收数据:
<template>
<div>
<p>我是Header的子组件</p>
<p>{{title}}</p>
<p>{{content}}</p>
<div :class="$attrs.class">{{$attrs.class}}</div>
<p>{{$attrs.kbx}}</p>
</div>
</template>
<script>
export default {
name: 'Vue3TextChild',
//vue官网对于inheritAttrs的属性解释:默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。
//就是不被继承的意思
inheritAttrs:false,
props:{
title:String,
content:{
type:String,
default:"默认值"
},
info:{
type:Object,
default(){
return {name:"kangboxuan"}
}
}
}
};
</script>
六、v-model
数据双向绑定
七、Vuex
仓库管理