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 作用域插槽

方法实现

方法效果


<code>Vue3组件通信和Vue2的区别:

移出事件总线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>clickmosueenter等等)

事件对象$event: 是包含事件相关信息的对象(pageXpageYtargetkeyCode

自定义事件:

事件名是任意名称

事件对象$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 可以直接通过 mittChild2 发送消息,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 将数据(如 moneycar)传递给子组件,这实现了从父组件到子组件的单向数据流。数据只能由父组件流向子组件,子组件本身不会改变这些数据。

属性转发:子组件通过 $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

<code>$refs用于 :父→子。获得子组件的所有实例

$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>provideinject实现祖孙组件直接通信

具体使用:

在祖先组件中通过provide配置向后代组件提供数据

在后代组件中通过inject配置来声明接收数据

父组件:父组件通过 provide 提供了一个包含 moneyupdate 方法的对象。这个对象相当于爷爷的钱袋子和给孙子钱的方法,提供给后代组件使用。

孙组件:孙组件使用 inject 获取这个共享对象,直接拿到 moneyupdate。孙组件可以访问并修改爷爷的钱袋子,甚至可以通过 update 方法向爷爷“孝敬”一些钱。

方法实现

单向数据流:父组件通过 provide 将数据和方法传递给孙组件,数据从父组件流向孙组件,但孙组件无法直接改变父组件的数据。这体现了从父到孙的单向数据流。

双向通信:孙组件通过调用 injectupdate 方法,实际上是在调用父组件提供的方法来修改 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)

<code>pinia作用于各个组件通信

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>

方法效果


蟹蟹你的浏览~~~



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。