Vue3
组件通信和Vue2
的区别:
- 移出事件总线,使用
mitt
代替。 vuex
换成了pinia
。- 把
.sync
优化到了v-model
里面了。 - 把
$listeners
所有的东西,合并到$attrs
中了。 $children
被砍掉了。
1. props
- 若 父传子:属性值是非函数。
- 若 子传父:属性值是函数。
父组件:
<template><div class="father"><h3>--父组件,</h3><h4>我的车:{{ car }}</h4><h4>儿子给的玩具:{{ toy }}</h4><Son:car="car":getSonToy="getSonToy"/></div>
</template><script setup lang="ts">
import Son from './Son.vue'
import { ref } from "vue";
// 数据
const car = ref('奔驰')
const toy = ref()
// 方法
function getSonToy(value: string) {toy.value = value
}
</script>
子组件:
<template><div class="child"><h3>--子组件--</h3><h4>我的玩具:{{ toy }}</h4><h4>父给我的车:{{ car }}</h4><button @click="getSonToy(toy)">玩具给父亲</button></div>
</template><script setup lang="ts">
import { ref } from "vue";
const toy = ref('奥特曼')defineProps(['car', 'getSonToy'])
</script>
2. 自定义事件
常用于:子 => 父。
原生事件:
- 事件名是特定的(
click
、mosueenter
等等) - 事件对象
$event
: 是包含事件相关信息的对象(pageX
、pageY
、target
、keyCode
)
自定义事件:
- 事件名是任意名称
- 事件对象
$event
: 是调用emit
时所提供的数据,可以是任意类型
父组件:
<template><div class="father"><h3>--父组件,</h3><h4>我的车:{{ car }}</h4><h4>儿子给的玩具:{{ toy }}</h4><Son@get-son-toy="getSonToy"/></div>
</template><script setup lang="ts">
import Son from './Son.vue'
import { ref } from "vue";
// 数据
const car = ref('奔驰')
const toy = ref()
// 方法
function getSonToy(value: string) {toy.value = value
}
</script>
子组件:
<template><div class="child"><h3>--子组件--</h3><h4>我的玩具:{{ toy }}</h4><!-- <h4>父给我的车:{{ car }}</h4> --><button @click="emit('get-son-toy', toy)">玩具给父亲</button></div>
</template><script setup lang="ts">
import { ref } from "vue";
const toy = ref('奥特曼')const emit = defineEmits(['get-son-to:y'])
</script>
扩展一个知识点:$event
<template><div class="child"><h3>--子组件--</h3><h4>我的玩具:{{ toy }}</h4><!-- <h4>父给我的车:{{ car }}</h4> --><!-- <button @click="emit('get-son-toy', toy)">玩具给父亲</button> --><button @click="test('1', $event)">test</button></div>
</template><script setup lang="ts">
import { ref } from "vue";
const toy = ref('奥特曼')// const emit = defineEmits(['get-son-toy'])
const test = (a: string, e: Event) => {console.log(a, e);
}
</script>
$event 为事件对象。
3. mitt
任意组件之间通信。
emitter.ts
// 引入mitt
import mitt from "mitt";// 创建emitter
const emitter = mitt()/*// 绑定事件emitter.on('abc',(value)=>{console.log('abc事件被触发',value)})emitter.on('xyz',(value)=>{console.log('xyz事件被触发',value)})setInterval(() => {// 触发事件emitter.emit('abc',666)emitter.emit('xyz',777)}, 1000);setTimeout(() => {// 清理事件emitter.all.clear()}, 3000);
*/// 创建并暴露mitt
export default emitter
子组件:
<template><div class="child"><h3>--子组件--</h3><h4>我的玩具:{{ toy }}</h4><button @click="emitter.emit('send-toy', toy)">玩具给父亲</button></div>
</template><script setup lang="ts">
import { ref } from "vue";
import emitter from '../utils/emitter'
const toy = ref('奥特曼')</script>
父组件:
<template><div class="father"><h3>--父组件,</h3><h4>我的车:{{ car }}</h4><h4>儿子给的玩具:{{ toy }}</h4><Son/></div>
</template><script setup lang="ts">
import Son from './Son.vue'
import { ref, onUnmounted } from "vue";
import emitter from '../utils/emitter'// 数据
const car = ref('奔驰')
const toy = ref()
// 绑定事件
emitter.on('send-toy', (data) => {toy.value = dataconsole.log('send-toy事件被触发', data)
})onUnmounted(() => {// 解绑事件emitter.off('send-toy')
})
</script>
4. v-model
组件库底层父子传参常用。
MyInput 组件:
<template><div class="box"><inputtype="text":value="modelValue"@input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)"></div>
</template><script setup lang="ts">
// 接收props
defineProps(['modelValue'])
// 声明事件
const emit = defineEmits(['update:modelValue'])
</script>
父组件:
<template><div class="father"><h3>--父组件--</h3><MyInput v-model="username"/><!-- 原理: --><!-- <MyInput :modelValue="username" @update:modelValue="username = $event"/> --></div>
</template><script setup lang="ts">
import MyInput from './MyInput.vue'
import {ref} from 'vue'
const username = ref('name')
</script>
- 对于原生事件, $event就是事件对象=====>能使用.target
- 对于自定义事件,$event就是触发事件时,所传递的数据==>不能.target
此外,还可以进行部分自定义修改:
父组件:
<MyInput v-model:myname="username"/>
MyInput 组件:
<template><div class="box"><inputtype="text":value="myname"@input="emit('update:myname', (<HTMLInputElement>$event.target).value)"></div>
</template><script setup lang="ts">
// 接收props
defineProps(['myname'])
// 声明事件
const emit = defineEmits(['update:myname'])
</script>
5. $attrs
用于祖孙传参。
具体说明:$attrs
是一个对象,包含所有父组件传入的标签属性。
$attrs
会自动排除props
中声明的属性
Father.vue
<template><div class="father"><h3>--Father--</h3><div>a: {{ a }}</div><div>b: {{ b }}</div><Son :a="a" :b="b" :addA="addA" v-bind="{x: 100, y: 200}" ></Son></div>
</template><script setup lang="ts">
import {ref} from 'vue'
import Son from './Son.vue'const a = ref(1)
const b = ref(2)const addA = (data: number)=> {a.value += data
}</script>
Son.vue
<template><div class="box"><h3>--Son--</h3><div>a: {{ a }}</div><GrandSon v-bind="$attrs"/></div>
</template><script setup lang="ts">
import GrandSon from './GrandSon.vue';defineProps(['a'])
</script>
GrandSon.vue
<template><div class="box"><h3>--GrandSon--</h3><div>b: {{ b }}</div><div>x: {{ x }}</div><div>y: {{ y }}</div><button @click="addA(1)">点我将a加1</button></div>
</template><script setup lang="ts">defineProps(['b', 'x', 'y' ,'addA'])
</script>
6. $refs
和 $parent
概述:
$refs
用于 :父→子。$parent
用于:子→父。
原理如下:
属性 | 说明 |
---|---|
$refs | 值为对象,包含所有被ref 属性标识的DOM 元素或组件实例。 |
$parent | 值为对象,当前组件的父组件实例对象。 |
Father.vue
<template><div class="father"><h3>父组件</h3><h4>房产:{{ house }}</h4><button @click="changeToy">修改Child1的玩具</button><button @click="changeComputer">修改Child2的电脑</button><button @click="getAllChild($refs)">让所有孩子的书变多</button><Child1 ref="c1" /><Child2 ref="c2" /></div>
</template><script setup lang="ts" name="Father">
import Child1 from './Child1.vue'
import Child2 from './Child2.vue'
import { ref, reactive } from "vue";
let c1 = ref()
let c2 = ref()// 注意点:当访问obj.c的时候,底层会自动读取value属性,因为c是在obj这个响应式对象中的
// 解释了为什么refs[key].book等写法后面不加.value
/* let obj = reactive({a:1,b:2,c:ref(3)
})
let x = ref(4)console.log(obj.a)
console.log(obj.b)
console.log(obj.c)
console.log(x) */// 数据
let house = ref(4)
// 方法
function changeToy() {c1.value.toy = '小猪佩奇'
}
function changeComputer() {c2.value.computer = '华为'
}
function getAllChild(refs: { [key: string]: any }) {console.log(refs)for (let key in refs) {refs[key].book += 3}
}
// 向外部提供数据
defineExpose({ house })
</script><style scoped>
.father {background-color: rgb(165, 164, 164);padding: 20px;border-radius: 10px;
}.father button {margin-bottom: 10px;margin-left: 10px;
}
</style>
Child1.vue
<template><div class="child1"><h3>子组件1</h3><h4>玩具:{{ toy }}</h4><h4>书籍:{{ book }} 本</h4><button @click="minusHouse($parent)">干掉父亲的一套房产</button></div>
</template><script setup lang="ts" name="Child1">
import { ref } from "vue";
// 数据
let toy = ref('奥特曼')
let book = ref(3)// 方法
function minusHouse(parent: any) {parent.house -= 1
}// 把数据交给外部
defineExpose({ toy, book })</script><style scoped>
.child1 {margin-top: 20px;background-color: skyblue;padding: 20px;border-radius: 10px;box-shadow: 0 0 10px black;
}
</style>
Child2.vue
<template><div class="child2"><h3>子组件2</h3><h4>电脑:{{ computer }}</h4><h4>书籍:{{ book }} 本</h4></div>
</template><script setup lang="ts" name="Child2">
import { ref } from "vue";
// 数据
let computer = ref('联想')
let book = ref(6)
// 把数据交给外部
defineExpose({ computer, book })
</script><style scoped>
.child2 {margin-top: 20px;background-color: orange;padding: 20px;border-radius: 10px;box-shadow: 0 0 10px black;
}
</style>
7. provide、inject
实现祖孙组件直接通信。
- 在祖先组件中通过
provide
配置向后代组件提供数据 - 在后代组件中通过
inject
配置来声明接收数据
Father 组件:
<template><div class="father"><h3>父组件</h3><h4>银子:{{ money }}万元</h4><h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4><Child/></div>
</template><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})function updateMoney(value:number){money.value -= value}// 向后代提供数据provide('moneyContext',{money,updateMoney})provide('car',car)</script><style scoped>.father {background-color: rgb(165, 164, 164);padding: 20px;border-radius: 10px;}
</style>
GrandChild 组件:
<template><div class="grand-child"><h3>我是孙组件</h3><h4>银子:{{ money }}</h4><h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4><button @click="updateMoney(6)">花爷爷的钱</button></div>
</template><script setup lang="ts" name="GrandChild">import { inject } from "vue";let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})let car = inject('car',{brand:'未知',price:0})
</script><style scoped>.grand-child{background-color: orange;padding: 20px;border-radius: 10px;box-shadow: 0 0 10px black;}
</style>
8. pinia
【Pinia】快速入门及数据持久化-CSDN博客
9. slot
【Vue3】slot 插槽全家桶_vue 多个匿名slot-CSDN博客