在 Vue 中,组件之间的通信是实现复杂功能的关键。常见的方式包括:
- Props 和自定义事件:用于在父子组件之间传递数据,实现简单的双向通信。
- Mitt:轻量级事件总线,适合跨组件的双向通信,超越了父子关系的限制。
- v-model:简化双向数据绑定,使父子组件的数据同步更为便捷。
- $attrs 和 $refs:提供灵活的通信方式,允许访问组件实例和传递属性。
- Provide 和 Inject:用于祖先组件与后代组件间的数据共享,适合全局状态管理。
- Pinia:Vue 的状态管理库,集中化管理全局状态,适用于大型应用。
- Slot:提供灵活的内容分发机制,支持组件的高度复用和动态布局。
这些工具和方法可以根据具体需求和场景选择使用,以构建高效的 Vue 应用。
**
Vue3
组件通信和
Vue2
的区别:**
移出事件总线
eventbus
,使用mitt
代替。
vuex
换成了pinia
。把
.sync
优化到了v-model
里面了。把
$listeners
所有的东西,合并到$attrs
中了。
$children
被砍掉了。
- 常见搭配形式
组件关系传递方式父传子1. props2. v-model3. $refs4. 默认插槽,具名插槽子传父1. props2. 自定义事件3. v-model4. $parent5. 作用域插槽祖传孙、孙传祖1. $attrs2. provide、inject兄弟间、任意组件间1. mitt2. pinia1. props
props
是使用频率最高的一种通信方式,常用与 :父 ↔ 子。
- 若 父传子:属性值是非函数。
- 若 子传父:属性值是函数。
父组件给子组件东西:就像父亲把车钥匙递给孩子,让他知道“这就是我的车”。
子组件给父组件东西:就像孩子拿着他的玩具车,跑过去交给父亲,“这是我的玩具车,你拿去吧”。
方法传递:父亲告诉孩子,“如果你想给我什么东西,就用这个方法(电话)打给我”,孩子打电话给父亲说,“爸爸,我给你一个玩具车”。
方法实现
单向数据流:父组件通过
props
向子组件传递数据,实现了从父到子的单向数据流。
双向通信:通过父组件传递的回调函数(
getToy
),实现了从子到父的双向通信,子组件可以把数据传回父组件。
- 父组件:
<template>
<div class="father">
<h2>父组件</h2>
<h4>我的车:{{ car }}</h4>
<h4>儿子给的玩具:{{ toy }}</h4>
<child :car="car" :sendToy="getToy"/>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import Child from "./child.vue";
const car = ref('奔驰')
const toy = ref('')
const getToy = (value: string) => {
toy.value = value
}
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
- 子组件:
<template>
<div class="child">
<h2>子组件</h2>
<h4>我的玩具:{{toy}}</h4>
<h4>爸爸给的车:{{ car}}</h4>
<button @click="sendToy(toy)">给爸爸玩具</button>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
const toy = ref('玩具车')
defineProps(['car','sendToy'])
defineOptions({name: 'Child'})
</script>
<style scoped>
.child{
width: 200px;
height: 200px;
background: skyblue;
color: black;
}
</style>
实现效果
2. 自定义事件
自定义事件常用于:子 => 父。
注意区分:原生事件、自定义事件。
- 原生事件:- 事件名是特定的(
click
、mosueenter
等等)- 事件对象$event
: 是包含事件相关信息的对象(pageX
、pageY
、target
、keyCode
)- 自定义事件:- 事件名是任意名称- 事件对象
$event
: 是调用emit
时所提供的数据,可以是任意类型!!!
事件发射(
$emit
):
- 就像孩子大声喊了一声:“爸爸,我把玩具给你了!”。
- 这个喊声就是
send-toy
事件,而孩子喊出的内容(toy
)就是玩具车。事件监听:
- 父亲听到了孩子的喊声,他知道孩子给了他一个玩具。于是,他把这个玩具记下来,并展示出来。
- 父亲用
@send-toy="toy=$event"
监听孩子的喊声,当事件触发时,把接收到的玩具($event
)赋值给父亲的toy
变量。
方法实现
单向数据流:父组件通过
props
向子组件传递数据(如车的初始值),实现从父到子的单向数据流。这种方式确保了父组件能向子组件提供数据。
双向通信:子组件通过
$emit
自定义事件(如
sendToy
),将数据(玩具)传回给父组件。父组件通过监听这个事件,并接收子组件发送的数据,实现了从子到父的双向通信。这种机制使得父组件和子组件之间能够进行数据的双向交互,保持同步。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<h4>儿子给的玩具:{{ toy }}</h4>
<child @send-toy="toy=$event"/>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import Child from "./child.vue";
const toy = ref('')
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<h4>我的玩具:{{ toy }}</h4>
<button @click="$emit('send-toy',toy)">给爸爸玩具</button>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
const toy = ref('玩具车')
const $emit = defineEmits()
defineOptions({name: 'Child'})
</script>
<style scoped>
.child {
width: 200px;
height: 200px;
background: skyblue;
color: black;
}
</style>
实现效果
3. mitt
miit
与消息订阅与发布(
pubsub
)功能类似,可以实现任意组件间通信。
**
mitt
作为事件总线**:
mitt
相当于家庭中的信使,负责在兄弟之间传递消息,而不用通过父母。
兄弟组件直接通信:
Child1
可以直接通过
mitt
给
Child2
发送消息,
Child2
监听并处理这个消息。这种方式避免了父组件的干预,使得兄弟组件的通信更加高效和简洁。
方法实现
单向数据流:子组件
Child1
通过
emitter.emit
将数据(玩具)传递出去,实现从子组件到事件总线(
mitt
)的单向数据流。此时,数据只是从子组件流出,而没有立即反馈到其他组件。
事件总线通信:
mitt
作为一个事件总线,允许组件之间进行通信。
Child1
通过
emit
触发事件,而
Child2
监听这个事件并接收数据。这种方式实现了兄弟组件之间的双向通信,
Child1
可以通过事件将玩具传递给
Child2
,而
Child2
可以响应并显示这个玩具。它使得不直接相关的组件能够通过
mitt
进行互动和数据交换。
- 安装
miit
pnpm add mitt
# 其他包管理器
npm i mitt
yarn add mitt
- 新建文件:
src\utils\emitter.ts
// 引入mitt
import mitt from "mitt";
// 创建emitter
const emitter = mitt()
// 暴露mitt
export default emitter
- 方法调用
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<div class="box">
<Child1/>
<Child2/>
</div>
</div>
</template>
<script lang="ts" setup>
import Child1 from "./child1.vue";
import Child2 from "./child2.vue";
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
.box{
display: flex;
justify-content: space-between;
}
}
</style>
- 子1组件
<template>
<div class="child">
<h2>子1组件</h2>
<h4>我的玩具:{{ toy }}</h4>
<button @click="sendToy">给弟弟玩具</button>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import emitter from "../../utils/emitter.ts";
const toy = ref('玩具车')
const sendToy = () => {
// 触发事件
emitter.emit('send-toy',toy)
}
defineOptions({name: 'Child1'})
</script>
<style scoped>
.child {
width: 180px;
height: 180px;
background: skyblue;
color: black;
}
</style>
- 子2组件
<template>
<div class="child">
<h2>子2组件</h2>
<h4>我的玩具:{{ toy }}</h4>
<h4>哥哥给的玩具:{{ toy_child1 }}</h4>
</div>
</template>
<script lang="ts" setup>
import {onUnmounted, ref} from "vue";
import emitter from "../../utils/emitter.ts";
const toy = ref('玩偶')
const toy_child1 = ref('')
// 监听事件
emitter.on('send-toy', (value: string) => toy_child1.value = value)
onUnmounted(() => emitter.off('send-toy'))
defineOptions({name: 'Child2'})
</script>
<style scoped>
.child {
width: 180px;
height: 180px;
background: skyblue;
color: black;
}
</style>
实现效果
4. v-mode
v-model
实现 父↔子 之间相互通信。
v-model
就像是一个中介,负责父组件和子组件之间的沟通和数据同步。它使得在组件之间传递和更新数据变得非常简单和直观。
想象一下,你在家里有一个车库(父组件),里面停着一辆车(
car
)。这个车库可以是你的,也可以是家人共用的。现在,你想让家里的一个小助手(子组件)来管理这个车的位置。
- 车库给助手指令:你告诉助手当前的车是“宝马”(通过
modelValue
把数据传递给子组件)。- 助手更新车的位置:当助手需要改变车的位置时(用户在子组件的输入框中输入新车名),他会告诉你这个新的车名是什么(通过
update:model-value
事件把新值返回给父组件)。- 数据同步:于是,无论助手在输入框中输入什么,车库里的车名都会随之改变(父组件的
car
数据更新),确保你和助手都看到的是同一辆车。
方法实现
单向数据流:父组件通过
v-model
向子组件传递数据(例如
car
),实现从父组件到子组件的单向数据流。父组件负责提供数据,而子组件只能接收并显示这些数据。
双向通信:通过
v-model
实现了父子组件之间的双向通信。当子组件中的数据(如输入框中的
car
值)发生变化时,子组件通过
update:model-value
事件将新数据返回给父组件,从而实现数据的同步更新。这样,子组件不仅能接收数据,还可以通过父组件更新数据,形成了双向的数据流动。
- 前序知识:
v-model
的本质
<!-- v-model指令 -->
<input type="text" v-model="userName">
<!-- v-model的本质 -->
<!-- 组件标签上的`v-model`的本质:`:moldeValue` + `update:modelValue`事件 -->
<input
type="text"
:value="userName"
@input="userName =(<HTMLInputElement>$event.target).value"
>
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<h4>我的车:{{ car }}</h4>
<!--在子组件上使用v-model-->
<!--<Child v-model="car"/>-->
<!--在子组件上使用v-model的本质-->
<child :modelValue="car" @update:model-value="car=$event"/>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import Child from "./child.vue";
const car = ref('宝马')
defineOptions({name: 'Father'});
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<input
type="text"
:value="modelValue"
@input="$emit('update:model-value',(<HTMLInputElement>$event.target).value)"
/>
</div>
</template>
<script lang="ts" setup>
const $emit = defineEmits()
defineProps(['modelValue'])
defineOptions({name: 'Child'});
</script>
<style scoped>
.child {
width: 200px;
height: 200px;
background: skyblue;
color: black;
}
</style>
实现效果
5. $attrs
$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。
具体:
$attrs
是一个对象,包含所有父组件传入的标签属性。
注意:
$attrs
会自动排除
props
中声明的属性(可以认为声明过的
props
被子组件自己“消费”了)
想象你是一位家长(父组件),你有一些财产和一辆车。这些东西你交给了你的孩子(子组件),但你希望这些东西最终能传递给你的孙子(孙组件)使用。你告诉孩子:“你把这些东西直接交给你的孩子吧。” 孩子并没有修改这些东西,而是直接转交给了孙子。
孙子收到这些东西后,决定通过努力工作来孝敬你。他把更多的钱和一辆新车送回给你,这些都直接反映在你的财产和车上。
方法实现
单向数据流:父组件通过
props
将数据(如
money
和
car
)传递给子组件,这实现了从父组件到子组件的单向数据流。数据只能由父组件流向子组件,子组件本身不会改变这些数据。
属性转发:子组件通过
$attrs
将接收到的所有
props
原封不动地传递给孙组件,而无需自己处理。这是一种简化多级组件数据传递的方式,确保父组件的数据能够顺利到达孙组件。
双向通信:孙组件通过父组件传递的回调函数(
update
),将新的数据(如增加的钱或改变的车名)返回给父组件。这实现了从孙组件到父组件的双向通信,使得数据可以在父组件和孙组件之间进行交互。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<h4>我的财产:{{ money }}</h4>
<h4>我的车:{{ car }}</h4>
<Child :money="money" :car="car" :update="update"/>
</div>
</template>
<script lang="ts" setup>
import Child from "./child.vue";
import {ref} from "vue";
const money = ref(30000)
const car = ref('宝马')
const update = (value: any) => {
money.value += value[0]
car.value += value[1]
}
defineOptions({name: 'Father'});
</script>
<style scoped>
.father {
width: 450px;
height: 450px;
background: #646cff;
overflow: hidden;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<GrandChild v-bind="$attrs"/>
</div>
</template>
<script lang="ts" setup>
import GrandChild from "./grandChild.vue";
defineOptions({name: 'Child'});
</script>
<style scoped>
.child {
width:300px;
height: 300px;
background: skyblue;
color: black;
}
</style>
- 孙组件
<template>
<div class="grandChild">
<h2>孙组件</h2>
<h4>爷爷传下来的财产:{{money}}</h4>
<h4>爷爷传下来的车:{{car}}</h4>
<button @click="update([5000,'奔驰'])">孝敬爷爷</button>
</div>
</template>
<script lang="ts" setup>
defineProps(['car','money','update'])
defineOptions({name: 'GrandChild'});
</script>
<style scoped>
.grandChild {
width: 250px;
height: 250px;
background: #2fd797;
color: black;
}
</style>
实现效果
6. $refs、$parent
$refs
用于 :父→子。获得子组件的所有实例$parent
用于:子→父。获得父组件的所有实例
属性说明
$refs
值为对象,包含所有被
ref
属性标识的
DOM
元素或组件实例。
$parent
值为对象,当前组件的父组件实例对象。
父组件:父组件有一辆车(
car
)和一个方法
share
。通过点击按钮,父组件可以使用
refs
引用子组件的实例,然后直接把车的信息传递给子组件。
子组件:子组件有一个玩具(
toy
)和一个车的属性。当用户点击子组件的按钮时,子组件会通过
$parent
访问父组件的实例,把玩具的信息传递回父组件。
方法实现
单向数据流:父组件通过
refs
引用子组件的实例,直接将
car
传递给子组件。这实现了从父组件到子组件的单向数据流,数据从父组件流向子组件,但子组件没有立即返回数据。
双向通信:子组件通过
$parent
访问父组件的实例,并将
toy
传回给父组件。这实现了双向通信,使得数据可以从子组件传递回父组件,形成了双向的数据交换。通过这种方式,父子组件可以互相交换数据,而不需要通过事件或
props
进行额外的中转。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<h4>我的车:{{ car }}</h4>
<h4>儿子分享的玩具:{{ toy }}</h4>
<button @click="share($refs)">分享车给儿子</button>
<child ref="child"/>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
import Child from "./child.vue";
const car = ref('奔驰')
const toy = ref('')
const share = (refs: any) => {
refs.child.car = car.value
}
defineExpose({toy})
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 410px;
height: 410px;
background: #646cff;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<h4>我的玩具:{{ toy }}</h4>
<h4>爸爸分享的车:{{ car }}</h4>
<button @click="share($parent)">分享玩具给爸爸</button>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
const toy = ref('玩具车')
const car = ref('')
const share = (parent: any) => {
console.log(parent)
parent.toy = toy.value
}
defineExpose({car})
defineOptions({name: 'Child'})
</script>
<style scoped>
.child {
width: 200px;
height: 200px;
background: skyblue;
color: black;
}
</style>
方法效果
7. provide、inject
provide
、
inject
实现祖孙组件直接通信
具体使用:
- 在祖先组件中通过
provide
配置向后代组件提供数据- 在后代组件中通过
inject
配置来声明接收数据
父组件:父组件通过
provide
提供了一个包含
money
和
update
方法的对象。这个对象相当于爷爷的钱袋子和给孙子钱的方法,提供给后代组件使用。
孙组件:孙组件使用
inject
获取这个共享对象,直接拿到
money
和
update
。孙组件可以访问并修改爷爷的钱袋子,甚至可以通过
update
方法向爷爷“孝敬”一些钱。
方法实现
单向数据流:父组件通过
provide
将数据和方法传递给孙组件,数据从父组件流向孙组件,但孙组件无法直接改变父组件的数据。这体现了从父到孙的单向数据流。
双向通信:孙组件通过调用
inject
的
update
方法,实际上是在调用父组件提供的方法来修改
money
。这实现了从孙组件向父组件的双向通信,孙组件不仅能接收数据,还可以触发父组件的方法,修改父组件的状态,从而实现数据的双向流动。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<h4>我的财产:{{ money }}</h4>
<Child/>
</div>
</template>
<script lang="ts" setup>
import Child from "./child.vue";
import {provide, ref} from "vue";
const money = ref(30000)
const update = (value: number) => {
money.value += value
}
// 传递数据
provide('share', {money, update})
defineOptions({name: 'Father'});
</script>
<style scoped>
.father {
width: 420px;
height: 420px;
background: #646cff;
overflow: hidden;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<GrandChild />
</div>
</template>
<script lang="ts" setup>
import GrandChild from "./grandChild.vue";
defineOptions({name: 'Child'});
</script>
<style scoped>
.child {
width:300px;
height: 300px;
background: skyblue;
color: black;
}
</style>
- 孙组件
<template>
<div class="grandChild">
<h2>孙组件</h2>
<h4>爷爷传下来的财产:{{ money }}</h4>
<button @click="update(10000)">孝敬爷爷1w</button>
</div>
</template>
<script lang="ts" setup>
import {inject} from "vue";
// 接收数据
let {money, update} = inject('share', {
money: 0, update: (value: number) => {
}
})
defineOptions({name: 'GrandChild'});
</script>
<style scoped>
.grandChild {
width: 250px;
height: 250px;
background: #2fd797;
color: black;
}
</style>
方法效果
8. pinia
简介 | Pinia (vuejs.org)
pinia
作用于各个组件通信
- Pinia 仓库:我们在
src/store/car.ts
中创建了一个小仓库useCarStore
,用于存储车的数据(如车的列表和计数)。这个仓库就像是一个存放共享数据的地方,供应用中的组件来读取或更新。- 父组件:在父组件中,我们通过
useCarStore
来访问这个仓库的数据。父组件通过carStore.cars
来获取并显示所有的车,并通过carStore.count
来显示计数器的值。点击按钮时,计数器的值会增加,并且这个更新会自动反映在视图上。Pinia 的使用就像是把数据放在一个公共的储物柜中,每个组件都可以从这个储物柜中取出数据或存放新数据。
方法实现
单向数据流:在这个例子中,数据是从 Pinia 仓库(全局状态)流向组件,实现了从全局状态到组件的单向数据流。组件只读取并显示仓库中的数据,而不会直接修改仓库中的数据结构。
双向通信:当用户点击按钮增加计数器时,组件通过
carStore.count++
修改了仓库中的数据,实现了从组件到仓库的双向通信。这意味着组件不仅可以读取仓库的数据,还可以更新仓库的数据。
- 安装pinia依赖
pnpm add pinia
# 其他包管理器
npm i pinia
yarn add pinia
- 注册插件
// main.ts
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import {createPinia} from "pinia";
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
- 创建小仓库
src/store/car.ts
// 小仓库
import {defineStore} from 'pinia'
const useCarStore = defineStore('carStore', {
state: () => {
return {
cars: ['宝马', '奔驰', '比亚迪'],
count: 100
}
},
actions: {},
getters: {},
})
export default useCarStore
- 组件调用仓库数据,并处理
<template>
<div class="father">
<h2>车</h2>
<ul>
<li v-for="(item,index) in carStore.cars" :key="index">
{{ item }}
</li>
</ul>
<h2>当前数:{{ carStore.count }}</h2>
<button @click="carStore.count++">加1</button>
</div>
</template>
<script lang="ts" setup>
import useCarStore from "../../store/car.ts";
let carStore = useCarStore()
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
方法效果
9. slot
9.1 默认插槽
默认插槽
用于:父->子
父组件:父组件通过插槽传递了一段 HTML 结构(一个包含多个汽车品牌的列表)给子组件。
子组件:子组件定义了一个默认插槽(
<slot></slot>
),这个插槽就像一个占位符,表示父组件传递的内容将会被插入到这个位置上。
插槽机制就像父母给孩子的玩具盒子,你可以放任何东西进去,孩子只需要打开盒子就能看到里面的内容。
方法实现
单向数据流:父组件通过插槽将内容传递给子组件,这是从父到子的单向数据流。子组件接收到内容并展示出来,但不会对这些内容进行修改。
双向通信:在这个例子中,插槽机制本身只是实现了单向数据流。若需要双向通信,可以结合事件机制(例如,子组件触发一个事件让父组件修改内容)来实现。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<Child>
<ul>
<li>宝马</li>
<li>奔驰</li>
<li>比亚迪</li>
</ul>
</Child>
</div>
</template>
<script lang="ts" setup>
import Child from "./child.vue";
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<slot></slot>
</div>
</template>
<script lang="ts" setup>
defineOptions({name: 'Child'})
</script>
<style scoped>
.child {
width: 300px;
height: 300px;
background: skyblue;
color: black;
}
</style>
实现效果
9.2 具名插槽
具名插槽
作用于:父->子
父组件:父组件使用
v-slot
或
#
语法,为不同的插槽命名并传递了两段内容:一段是汽车品牌的列表 (
car
),另一段是玩具的列表 (
toy
)。
子组件:子组件定义了两个具名插槽,通过
<slot name="...">
语法来接收父组件传递的内容,并在相应位置展示出来。
具名插槽就像你给孩子的多个玩具盒子,每个盒子上都有标签(名字),孩子可以打开特定的盒子(插槽)来获取里面的内容。
方法实现
单向数据流:父组件通过插槽将内容传递给子组件,这是从父到子的单向数据流。子组件接收到内容并展示出来,但不会对这些内容进行修改。
双向通信:在这个例子中,插槽机制本身只是实现了单向数据流。若需要双向通信,可以结合事件机制(例如,子组件触发一个事件让父组件修改内容)来实现。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<Child>
<template v-slot:car>
<ul>
<li>宝马</li>
<li>奔驰</li>
<li>比亚迪</li>
</ul>
</template>
<template #toy>
<ul>
<li>玩具车</li>
<li>玩偶</li>
<li>积木</li>
</ul>
</template>
</Child>
</div>
</template>
<script lang="ts" setup>
import Child from "./child.vue";
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<slot name="car"></slot>
<slot name="toy"></slot>
</div>
</template>
<script lang="ts" setup>
defineOptions({name: 'Child'})
</script>
<style scoped>
.child {
width: 300px;
height: 300px;
background: skyblue;
color: black;
}
</style>
方法效果
9.3 作用域插槽
作用域插槽
作用于:子->父
- 子组件:子组件定义了一个插槽,并通过
:toy="toy"
向该插槽传递了一个叫toy
的数据(玩具列表)。这相当于子组件准备好数据,并把它交给父组件处理。- 父组件:父组件通过
template
标签来接收这个数据,并使用v-for
循环将传递过来的玩具列表展示出来。父组件在使用插槽时,可以动态获取子组件传递的数据。作用域插槽就像是子组件把一些玩具(数据)放在盒子里,然后交给父组件,父组件再决定怎么展示这些玩具。
方法实现
单向数据流:在这个例子中,数据从子组件传递给父组件,实现了从子到父的单向数据流。子组件将数据通过插槽传递给父组件,父组件负责渲染和展示这些数据。
双向通信:如果需要实现双向通信,可以在父组件中添加回调函数,通过插槽传递给子组件,从而在子组件中调用这些函数来修改父组件的数据。
- 父组件
<template>
<div class="father">
<h2>父组件</h2>
<Child>
<template #default="params">
<ul>
<li v-for="(item,index) in params.toy" :key="index">
{{ item}}
</li>
</ul>
</template>
</Child>
</div>
</template>
<script lang="ts" setup>
import Child from "./child.vue";
defineOptions({name: 'Father'})
</script>
<style scoped>
.father {
width: 400px;
height: 400px;
background: #646cff;
}
</style>
- 子组件
<template>
<div class="child">
<h2>子组件</h2>
<slot :toy="toy"></slot>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
const toy = ref(['玩偶', '玩具车', '积木'])
defineOptions({name: 'Child'})
</script>
<style scoped>
.child {
width: 300px;
height: 300px;
background: skyblue;
color: black;
}
</style>
方法效果
蟹蟹你的浏览~~~
版权归原作者 白白的西柚珉•᷄ࡇ•᷅ 所有, 如有侵权,请联系我们删除。