Vue3
组件通信和Vue2
的区别:
mitt
代替。vuex
换成了pinia
。.sync
优化到了v-model
里面了。$listeners
所有的东西,合并到$attrs
中了。$children
被砍掉了。常见搭配形式:
props
概述:
props
是使用频率最高的一种通信方式,常用与 :父 ? 子。
- 若 父传子:属性值是非函数。
- 若 子传父:属性值是函数。
父组件:
<template>
<div class="father">
<h3>父组件</h3>
<h4>父组件内的值: {{ car }}</h4>
<h4 v-show="toy">子传给父的值: {{ toy }}</h4>
<Child :car="car" :sendToy="getToy"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import { ref } from 'vue'
let car = ref('奔驰')
let toy = ref('')
function getToy(value){
toy.value = value
}
</script>
子组件:
<template>
<div class="child">
<h3>子组件</h3>
<h4>子组件内的值:{{ toy }}</h4>
<h4>父亲传过来的值:{{ car }}</h4>
<button @click="sendToy(toy)">将子组件的值传给父组件</button>
</div>
</template>
<script setup lang="ts" name="Child">
import {ref,onMounted} from 'vue'
defineProps(['car', 'sendToy'])
let toy = ref('玩具熊')
</script>
【自定义事件】
click
,mouseenter
等等)$event
:包含事件相关信息的对象(pageX
,pageY
,target
,keyCode
)$event
:是调用emit
时所提供的数据,可以是任意类型kebab-case
规范// 给子组件绑定自定义事件
<Child @send-toy="saveToy"/>
function saveToy(val:string){
toy.value = val
}
<!-- 调用自定义事件 -->
<button button @click="emit('send-toy', toy)">传递数据</button>
// 声明自定义事件
const emit =defineEmits(['send-toy'])
【mitt】
pubsub
)功能类似,可以实现任意组件间通信mitt
:npm i mitt
src\utils\emitter.ts
:import mitt from 'mitt'
// 创建emitter
const emitter = mitt()
// 绑定事件
// emitter.on('xxx', (val)=>{
// console.log(val)
// })
// 触发事件
// emitter.emit('xxx', data)
// 解绑事件
// emitter.off('xxx')
// 清空事件
// emitter.all.clear()
export default emitter
<template>
<div class="child2">
<h3>子组件2</h3>
<h4>电脑:{{ computer }}</h4>
<h4>哥哥给的玩具:{{ toy }}</h4>
</div>
</template>
<script setup lang="ts" name="Child2">
import {ref,onUnmounted} from 'vue'
import emitter from '@/utils/emitter';
// 数据
let computer = ref('联想')
let toy = ref('')
emitter.on('send-toy', (val: string)=>{
toy.value = val
})
onUnmounted(()=>{
emitter.off('send-toy')
})
</script>
<template>
<div class="child1">
<h3>子组件1</h3>
<h4>玩具:{{ toy }}</h4>
<button @click="emitter.emit('send-toy', toy)">传递数据</button>
</div>
</template>
<script setup lang="ts" name="Child1">
import {ref} from 'vue'
import emitter from '@/utils/emitter'
// 数据
let toy = ref('奥特曼')
</script>
【v-model
】
v-model
的本质<!--原生DOM输入框使用v-mdoel:双向绑定-->
<input type="text" v-model="username">
<!--v-model本质: 传一个动态属性value,并在input事件修改username值 -->
<input
type="text"
:value="username"
@input="username = (<HTMLInputElement>$event.target).value"
>
v-model
的本质::moldeValue
+ update:modelValue
事件。父组件中:
<!-- 组件身上使用v-model -->
<MyInput v-mdoel="username"/>
<!-- 本质相当于 -->
<MyInput :modelValue="username" @update:modelValue="username = $event"/>
自定义组件中
<template>
<div>
<!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
<input
type="text"
:value="modelValue"
@input="emits('update:modelValue', (<HTMLInputElement>$event.target).value)"
>
</div>
</template>
<script setup lang="ts" name="MyInput">
defineProps(['modelValue'])
// 声明自定义事件 update:modelValue
const emits = defineEmits(['update:modelValue'])
</script>
v-model
后面跟自定义名称, 相当于别名,这样的话可以使用多次v-model
父组件中:
<MyInput v-model="username" v-model:abc="abc"/>
子组件中:
<template>
<div>
<input
type="text"
:value="modelValue"
@input="emits('update:modelValue', (<HTMLInputElement>$event.target).value)"
>
<br>
<input
type="text"
:value="abc"
@input="emits('update:abc', (<HTMLInputElement>$event.target).value)"
>
</div>
</template>
<script setup lang="ts" name="MyInput">
defineProps(['modelValue','abc'])
// 声明自定义事件 update:modelValue
const emits = defineEmits(['update:modelValue', 'update:abc'])
</script>
【$attrs
】
$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。$attrs
是一个对象,包含所有父组件传入的标签属性。注意:
$attrs
会自动排除props
中声明的属性(可以认为声明过的props
被子组件自己“消费”了)(相当于:组件中没被defineProps
声明接收的属性)
父组件<Parent/>
<template>
<div class="father">
<h3>父组件</h3>
<h4>a:{{a}}</h4>
<h4>b:{{b}}</h4>
<Child :a="a" :b="b" v-bind="{x:100,y:200}" :updateA="updateA"/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import {ref} from 'vue'
let a = ref(1)
let b = ref(2)
function updateA(val :number){
a.value += val
}
</script>
子组件<Clild/>
<template>
<div class="child">
<h3>子组件</h3>
<GrandChild v-bind="$attrs"/>
</div>
</template>
<script setup lang="ts" name="Child">
import GrandChild from './GrandChild.vue'
</script>
孙组件<GrandChild/>
<template>
<div class="grand-child">
<h3>孙组件</h3>
<h4>a:{{ a }}</h4>
<h4>b:{{ b }}</h4>
<h4>x:{{ $attrs.x }}</h4>
<h4>y:{{ y }}</h4>
<button @click="updateA(6)">点我将爷爷那的a更新</button>
</div>
</template>
<script setup lang="ts" name="GrandChild">
defineProps(['a','b','y','updateA'])
</script>
【 r e f s 、 refs、 refs、parent】
$refs
用于 :父→子。$parent
用于:子→父。属性 | 说明 |
---|---|
$refs | 值为对象,包含所有被ref 属性标识的DOM 元素或组件实例。 |
$parent | 值为对象,当前组件的父组件实例对象。 |
Father.vue
<template>
<div>
<h4>父组件内的数字:{{ num }}</h4>
<!-- 需求一: 父组件内操作
子组件Child1的玩具,让玩具的值变成读书,
子组件Child2的电脑,让电脑的值变成手机,
在子组件上加ref,获取到子组件的数据,进行更改
-->
<button @click="editComputer">修改child2的电脑</button>
<button @click="editToy">修改child1的玩具</button>
<button @click="editBook1">子组件内书加几本方法一</button>
<button @click="editBook2($refs)">子组件内书加几本方法二(假如多个组件内的值修改一致)</button>
<Chilld1 ref="c1"/>
<Chilld2 ref="c2"/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Chilld1 from './Child1.vue'
import Chilld2 from './Child2.vue'
let num = ref(1)
let c1 = ref()
let c2 = ref()
function editToy(){
// 响应读取组件的值,必须在组件内暴露, 父读子需要暴露,子读父也需要暴露
c1.value.toy = '读书'
}
function editComputer(){
c2.value.computer = '手机'
}
function editBook1(){
c1.value.book += 3
c2.value.book += 3
}
// function editBook2(refs: any){
function editBook2(refs: {[key: string]: any}){
for (const key in refs) {
refs[key].book += 2
}
}
// 暴露数据
defineExpose({num})
</script>
Child1.vue
<template>
<div class="child1">
<h3>子组件1</h3>
<h4>玩具:{{ toy }}</h4>
<h4>书籍:{{ book }} 本</h4>
<!-- $parent直接获取父组件实例对象 -->
<button @click="editNum($parent)">修改父组件内的num</button>
</div>
</template>
<script setup lang="ts" name="Child1">
import { ref } from "vue";
// 数据
let toy = ref('奥特曼')
let book = ref(3)
function editNum(par: any){
par.num += 1
}
defineExpose({toy, book})
</script>
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>
【provide、inject】
provide
配置向后代组件提供数据inject
配置来声明接收数据provide
提供数据<template>
<div class="father">
<h3>父组件</h3>
{{ haha }}
<Child/>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from './Child.vue'
import {ref,reactive,provide} from 'vue'
let haha = ref('哈哈')
// 向后代提供数据
provide('haha', {haha, editHaha})
function editHaha(value: string){
haha.value = value
}
</script>
注意:子组件中不用编写任何东西,是不受到任何打扰的
【第二步】孙组件中使用inject
配置项接受数据。
inject
接收两个参数,第一个参数是provide
提供的数据名称,第二个参数是默认值(假如未找到provide
提供的数据名称就触发)
<template>
<div class="grand-child">
<h3>我是孙组件</h3>
<h3>{{ haha }}</h3>
<button @click="editHaha('嗨嗨')">修改哈哈</button>
</div>
</template>
<script setup lang="ts" name="GrandChild">
import { inject } from "vue";
const {haha,editHaha} = inject('haha', {haha:'呵呵', editHaha:(v: string)=>{}})
</script>