我们需要查看在不同的组件之间,如何进行数据的传递
传递的形式包括 父传子,子传父, 祖传孙,孙传祖,兄弟互传。
- 父子互传
其中最为简单且暴力的一种,是利用props进行传递。
在使用props传递的时候
如果父亲传递给儿子的话
直接使用即可,然后在其子之中利用defineProps进行声明即可。
比如我们在script之中定义了一个变量
const car = ref(‘奔驰’)
在上面template之中,直接
<Child :car=”car”>
而在儿子之中,利用defineProps即可
defineProps([‘car’])
而子传递给父的话,则需要定义函数,相当于声明接口由子类实现。
这里我们我们现在父类中定义一个函数
const toy = ref(‘’)
function getToy(value:string){
toy.value = value
}
<Child :sendToy=”getToy”>
于此同时在子类的defineProps之中,定义sendToy这个函数
然后进行实现即可
<button @click=”getToy(toy)”>玩具给父亲</button>
当然这种子类传递给父类的方式太过复杂,以至于每个父类都需要定义好函数来传递给子类并通过其获取到子类返回的数据。
类似这种通过props来进行传递的方式,还有$attr,其可以实现多级的向下向上传递,
如果我们给子类传递了一个属性,但是子类并没有在defineProps之中声明使用这个属性的话,这些传递下来的属性就会归纳在attr之中,总结一下,attr是一个对象,包含所有父组件传入的标签属性。
<Child :a=”a” :b=”b” :c=”c” :d=”d” v-bind=”{x:100,y:200}” :updateA=”updateA”/>
这样在子组件之中,如果我们不进行使用而直接向下传递的话,只需要利用v-bind加上$attr关键字即可。
<GrandChild v-bind=”$attrs”/>
在实际使用的组件中,利用defineProps从其中取出来。
<script setup lang=”ts” name=”GrandChild”>
defineProps([‘a’,’b’,’c’,’d’,’x’,’y’,’updateA’])
</script>
更为快捷的方式是自定义事件,来暴露给父类。
然后是一个通过第三方组件库实现组件之间通信的方式,
这里利用到的组件是mitt
其主要负责一个消息总线的概念,可以实现任意组件之间的通信
首先是mitt的安装
npm i mitt
关于这样一个组件的应用,我们可以将其首先封装在一个hook之中
import mitt from “mitt”;
const emitter = mitt()
export default emitter
这样就好比暴露了这个消息总线,其他人直接引用hook即可
之后是关于这个emitter的使用,其内部包含的函数主要由 on 绑定,off 移除,emit触发
这几个构成
对应的使用就是我们可以在一个兄弟组件之中,绑定监听这个事件
在使用对应的on函数的时候,需要传递事件名称和回调函数
emitter.on(‘send-toy’,(value)=>{
console.log(‘send-toy事件被触发’,value)
})
之后是对应的监听,这里我们直接给一个function
function sendToy(){
emitter.emit(‘send-toy’, toy.value)
}
对应的前端中常见的v-model也可以实现组件通信
v-model本质上是多个相互通信的参数的拼接,比如如下的一个指令
<input type=”text” v-model=”userName”>
本质上是和代码端进行交互
比如我们有一个子组件AtguiguInput
那么在父组件之中
import AtguiguInput from ‘./AtguiguInput.vue’
<AtguiguInput v-model:ming=”username” v-model:mima=”password”/>
对应的在子组件之中
可以在template中使用
<input
type=”text”
:value=”ming”
@input=”emit(‘update:ming’,(<HTMLInputElement>$event.target).value)”
>
需要注意的是
这个emit后的事件名需要在
defineEmits中进行预先的定义
const emit = defineEmits([‘update:ming’,’update:mima’])
然后是获取父去获取到子的组件,以及子获取到父的组件
如果想要获取到的子级的话,需要refs
而能获取到的属性,需要子组件通过defineExpose暴露
比如
let toy = ref(‘奥特曼’)
let book = ref(3)
// 把数据交给外部
defineExpose({toy,book})
然后在父类之中,就可以进行获取 直接$refs
<button @click=”getAllChild($refs)”>所有对外暴露的book+1</button>
function getAllChild(refs:{[key:string]:any}){
console.log(refs)
for (let key in refs){
refs[key].book += 3
}
}
相反,子获取到父的组件,也是一样的流程
也需要通过defineExpose({house})对外暴露
这就可以通过关键字 $parent获取到了。
<button @click=”minusHouse($parent)”>父级暴露的house属性减一</button>
function minusHouse(parent:any){
parent.house -= 1
}
对于跨级别的传入和接收,可以使用provide和inject
Provide,可以直接向下提供属性,包括属性和方法
<script setup lang=”ts” name=”Father”>
import Child from ‘./Child.vue’
import { ref,reactive,provide } from “vue”;
// 数据
let money = ref(100)
let car = reactive({
brand:’奔驰’,
price:100
})
// 用于更新money的方法
function updateMoney(value:number){
money.value += value
}
// 提供数据
provide(‘moneyContext’,{money,updateMoney})
provide(‘car’,car)
</script>
那么传递的子类或者孙类就可以声明接收
import { inject } from ‘vue’;
// 注入数据
let {money,updateMoney} = inject(‘moneyContext’,{money:0,updateMoney:(x:number)=>{}})
let car = inject(‘car’)