目录
自定义属性props(父向子)
自定义事件(子向父)
ref获取子组件实例
$parent获取父组件实例
作用域插槽向父组件传值
兄弟组件传值
父子双向数据绑定
useAttrs获取父组件传来的属性和事件
深层组件传值
Pinia实现任意组件之间通信
-
首先,我们要明白,数据流是自顶向下的,也就是说,只能上层的修改下层的,不能下层的修改上层的(正确的做法,应该是下层的通知上层的,让上层的去修改)
自定义属性props(父向子)
- 可以用于父向子传值
-
vue3中,子组件用
defineProps
这个hook来接收父组件传来的数据。可以是一个数组,也可以是一个对象 -
defineProps
不需要导入,defineProps
接收到的数据,可以直接进行使用
父组件
<template><div class="father"><h1>father</h1><Son :count="count"></Son></div>
</template><script setup>
import { ref } from 'vue'
import Son from './Son.vue'const count = ref(100)
</script><style lang="scss" scoped>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>
子组件
<template><div class="son"><h3>son</h3><p>父组件传来的数据: {{ count }}</p></div>
</template><script setup>
// 1. 数组
// const props = defineProps(['count'])
// 2. 对象,可以对父组件传来的数据进行一下校验
const props = defineProps({count: {type: Number,default: 0,},
})
</script><style lang="scss" scoped>
.son {height: 100px;background-color: #91beff;
}
</style>
自定义事件(子向父)
可以用于子向父传值
-
子组件通过
emit
向父组件传递一个自定义事件 -
父组件,在子组件标签上接收自定义事件,并声明处理函数
子组件
<template><div class="son"><h3>son</h3><button @click="sendData">点击向父组件传值</button></div>
</template><script setup>
// 子组件通过 defineEmits 这个hook声明自定义事件
const $emit = defineEmits(['sendData'])const sendData = () => {// 1. 子组件通过 emit 向父组件传递一个自定义事件(第一个参数是自定义事件名,第二个参数是传递的参数)$emit('sendData', 999)
}
</script><style lang="scss" scoped>
.son {height: 100px;background-color: #91beff;
}
</style>
父组件
<template><div class="father"><h1>father --- 接收到的子组件传过来的值:{{ count }}</h1><!-- 2. 父组件,在子组件标签上接收自定义事件,并声明处理函数 --><Son @sendData="sendData"></Son></div>
</template><script setup>
import { ref } from 'vue'
import Son from './Son.vue'let count = ref(0)// 3. 处理函数,如果子组件传递了参数,就可以写形参进行接收
const sendData = (val) => {console.log(val) // 接收到的子组件的参数// 可以把参数转存到声明的变量中,这样就可以直接使用了count.value = val
}
</script><style lang="scss" scoped>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>
补充:vue2和3的自定义事件
-
在
vue2
中,如果在组件标签上写click
,mouseEnter
,focus
等等这些原生的事件,它会以为就是自定义事件,不会以为是原生事件。如果想要它以原生的事件执行,则需要添加.native
修饰符
<组件名 @click.native = 'handleClick'></组件名>
-
而
vue3
,如果绑定原生事件,它就会以为就是原生事件。只要在子组件内,条件满足就会触发
<组件名 @click = 'handleClick'></组件名>
ref获取子组件实例
-
ref 可以获取真实的
DOM
节点,也可以获取组件实例。获取子组件实例后,就可以通过子组件实例,直接修改和使用子组件定义的变量,也可以直接调用子组件的方法 -
组件内部的数据是封闭保护的,如果向让外界使用,可以使用
defineExpose
这个hook
进行暴露
父组件
<template><main class="father"><h1>father</h1><Son ref="son"></Son></main>
</template><script setup>
import { onMounted, ref } from 'vue'
import Son from './Son.vue'// 子组件的实例化
let son = ref()
// 只有挂载到父组件之后,才能使用子组件的属性和方法
onMounted(() => {console.log(son.value.num)
})
</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>
子组件
<template><div class="son"><h2>son</h2></div>
</template><script setup>
import { ref } from 'vue'let num = ref(10)
// 需要使用 defineExpose 进行对外暴露(方法和属性都可以)
defineExpose({num,
})
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>
$parent获取父组件实例
-
可以在子组件中通过
$parentz
这个参数获取父组件的实例 -
组件内部的数据是封闭保护的,如果向让外界使用,可以使用
defineExpose
这个hook
进行暴露
子组件
<template><div class="son"><h2>son</h2><button @click="getParentData($parent)">获取父组件的数据</button></div>
</template><script setup>
const getParentData = (parent) => {console.log('获取到了父组件实例', parent)
}
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>
父组件
<template><main class="father"><h1>father</h1><Son></Son></main>
</template><script setup>
import { ref } from 'vue'
import Son from './Son.vue'let count = ref(100)// 需要使用 defineExpose 进行对外暴露(方法和属性都可以)
defineExpose({count,
})
</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>
作用域插槽向父组件传值
-
子组件可以利用作用域插槽向父组件传值
-
作用域插槽就是可以传递数据的插槽,子组件可以将数据回传给父组件,父组件可以决定这些回传的数据,是以何种结构或者外观在子组件内部去展示
子组件
<template><div class="son"><h2>son</h2><p>--------默认插槽---------</p><slot></slot><p>--------具名插槽。name指定插槽的名字---------</p><slot name="superman"></slot><p>--------作用域插槽。通过属性绑定,向父组件提供数据---------</p><slot name="dc" :list="list"></slot></div>
</template><script setup lang="ts">
import { ref } from 'vue'const list = ref([{name: '张三',age: 21,color: 'pink'},{name: '里斯',age: 22,color: 'skyblue'},{name: '王五',age: 23,color: 'hotpink'},
])
</script><style>
.son {height: 150px;background-color: skyblue;
}
</style>
父组件
<template><main class="father"><h1>father</h1><Son><p>默认插槽里面的内容</p><template #superman>具名插槽中的内容</template><template #dc="{ list }"><ul><li v-for="i in list" :key="i.name" :style="{ backgroundColor: i.color }">{{ i.name }} -- {{ i.age }}</li></ul></template></Son></main>
</template><script setup lang="ts">
import Son from './Son.vue'
</script><style>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>
兄弟组件传值
可以利用第三方插件 mitt 实现
首先进行安装
yarn add mitt
使用:
-
在
src
目录下创建一个bus
的文件夹,里面创建一个mitt.js
的文件。(文件名随便起)
import mitt from 'mitt'const emitter = mitt()export default emitter
-
发送方:导入
mitt.js
并用emit
发送数据
<template><div class="son"><h3>son</h3><button @click="givingGifts">点我,给妹妹送一件礼物</button></div>
</template><script setup>
import emitter from '@/bus/mitt'const givingGifts = () => {// 发送方 emitter.emit('事件名', 参数)emitter.emit('foo', { car: 'QQ车' })
}
</script><style lang="scss" scoped>
.son {height: 100px;background-color: #91beff;
}
</style>
-
接收方:导入
mitt.js
并用on
接收兄弟组件发来的数据
<template><div class="sister"><h2>sister</h2><p>接收到了哥哥的礼物:{{ gifts }}</p></div>
</template><script setup>
import { ref } from 'vue'
import emitter from '@/bus/mitt'let gifts = ref()// 接收方 emitter.on('事件名', (参数) => { ... })
emitter.on('foo', (val) => {gifts.value = val.car
})
</script><style lang="scss" scoped>
.sister {height: 100px;background-color: #91beff;
}
</style>
父子双向数据绑定
使用 v- model 进行父子组件之间的双向数据绑定
-
v-model
不光用在表单上,也可以用在组件上。用在组件上绑定值后,子组件只需要defineProps
接收即可 -
v-model
用在组件上,默认就是v-model:modelValue
,也就是默认绑定modelValue
-
组件上可以通过v-model传递多个值
父组件
<template><main class="father"><h1>father</h1><Son v-model="count" v-model:str="str"></Son></main>
</template><script setup lang="ts">
import { ref } from 'vue'
import Son from './son.vue'let count = ref(100)
let str = ref('str')</script><style>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>
子组件
<template><div class="son"><h2>son</h2><p>接收到的父组件传来的值:{{ modelValue }} -- {{ str }}</p></div>
</template><script setup lang="ts">
const props = defineProps({modelValue: {type: Number},str: String
})
</script><style>
.son {height: 100px;background-color: skyblue;
}
</style>
这只是单纯的父向子传递了数据,如果子组件想要修改,则还需要向父组件发送自定义事件,通知父组件进行修改
实现父子通信
父组件
<template><main class="father"><h1>father</h1><Son v-model="count" v-model:str="str" @changeCount="changeCount"></Son></main>
</template><script setup lang="ts">
import { ref } from 'vue'
import Son from './son.vue'let count = ref(100)
let str = ref('str')const changeCount = (val: number) => {count.value = count.value + val
}</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>
子组件
<template><div class="son"><h2>son</h2><p>接收到的父组件传来的值:{{ modelValue }} -- {{ str }}</p><button @click="changeCount">修改父组件传来的数值</button></div>
</template><script setup lang="ts">
const props = defineProps({modelValue: {type: Number},str: String
})const $emits = defineEmits(['changeCount'])const changeCount = () => {$emits('changeCount',1)
}
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>
useAttrs获取父组件传来的属性和事件
-
可以获取到父组件传递过来的属性和事件
-
没有被
definedProps
声明接收的,都会被useAttrs
兜底接收
父组件
<template><main class="father"><h1>father</h1><Son type="primary" size="small"></Son></main>
</template><script setup>
import Son from './Son.vue'
</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>
子组件
<template><div class="son"><h2>son</h2></div>
</template><script setup>
import { useAttrs } from 'vue'let $attrs = useAttrs()console.log($attrs)
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>
封装第三方的组件时
-
可以用
v-bind
绑定$attrs
对象。多用于对第三方ui库的组件进行二次开发
<el-button :='$attrs'><slot></slot>
</el-button>
也可以接收事件
<Son type="primary" size="small" @click="clickSon"></Son>
深层组件传值
深层组件,就是有很多层的嵌套关系。类似于祖先和子孙的关系
使用 provide 和 inject 进行深层组件的传值
-
祖先组件通过
provide
这个hook
向子孙组件提供数据。它下面的所有子孙组件都可以使用提供的数据 -
子孙组件通过
inject
这个hook
接收祖先提供的数据
祖先组件
<template><main class="father"><h1>father</h1><Son></Son></main>
</template><script setup lang="ts">
import { ref,provide } from 'vue'
import Son from './son.vue'let count = ref(100)// 提供者:向子孙组件提供数据
provide('count',count)
</script><style>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>
中间组件
<template><div class="son"><h2>son</h2><GrandSon></GrandSon></div>
</template><script setup lang="ts">
import GrandSon from './GrandSon.vue';
</script><style>
.son {height: 100px;background-color: skyblue;
}
</style>
子孙组件
<template><div class="son"><h3>grandSon</h3><p>从祖先组件接收过来的数据:{{ count }}</p></div>
</template><script setup lang="ts">
import { inject } from 'vue'let count = inject('count')
</script><style>
.son {height: 100px;background-color: skyblue;
}
</style>
Pinia实现任意组件之间通信
-
核心:
state
,actions
,getters
选项式用法
定义:
import { defineStore } from 'pinia'let userInfoStore = defineStore('user', {state: () => {return {name: '张三',}},getters: {},actions: {changeName(name: String) {this.name = name},},
})// 对外暴露
export default userInfoStore
使用:
<template><main><p>从数据仓库获取到的数据:{{ userInfo.name }}</p><button @click="changeName">修改数据仓库提供的name的值</button></main>
</template><script setup lang="ts">
import userInfoStore from '@/stores/counter'const userInfo = userInfoStore()const changeName = () => {// 1. 直接进行修改// userInfo.name = '李四'// 2. 通过 $patch 方法进行调用// userInfo.$patch({// name: '李四',// })// 3. 通过仓库调用自身的方法去修改仓库的数据userInfo.changeName('李四')
}
</script>
组合式用法
定义:
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'const useCounterStore = defineStore('counter', () => {const count = ref(0)const doubleCount = computed(() => count.value * 2)function increment() {count.value++}return { count, doubleCount, increment }
})export default useCounterStore
使用:
<template><main><h1>首页</h1><p>数据仓库提供的数据:{{ useCounter.count }} -- {{ useCounter.doubleCount }}</p><button @click="changeCount">使用数据仓库提供的方法改变count值</button></main>
</template><script setup lang="ts">
import useCounterStore from '@/stores/counter'const useCounter = useCounterStore()const changeCount = () => {useCounter.increment()
}
</script>