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