Vue3组件通信

在 Vue 中,组件之间的通信是实现复杂功能的关键。常见的方式包括:

  1. Props 和自定义事件:用于在父子组件之间传递数据,实现简单的双向通信。

  2. Mitt:轻量级事件总线,适合跨组件的双向通信,超越了父子关系的限制。

  3. v-model:简化双向数据绑定,使父子组件的数据同步更为便捷。

  4. $attrs 和 $refs:提供灵活的通信方式,允许访问组件实例和传递属性。

  5. Provide 和 Inject:用于祖先组件与后代组件间的数据共享,适合全局状态管理。

  6. Pinia:Vue 的状态管理库,集中化管理全局状态,适用于大型应用。

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

方法实现

方法效果


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

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. 自定义事件

自定义事件常用于:子 => 父。

注意区分:原生事件、自定义事件。

  • 原生事件:

    • 事件名是特定的(clickmosueenter等等)

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

  • 自定义事件:

    • 事件名是任意名称

    • 事件对象$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 可以直接通过 mittChild2 发送消息,Child2 监听并处理这个消息。这种方式避免了父组件的干预,使得兄弟组件的通信更加高效和简洁。

方法实现

单向数据流:子组件 Child1 通过 emitter.emit 将数据(玩具)传递出去,实现从子组件到事件总线(mitt)的单向数据流。此时,数据只是从子组件流出,而没有立即反馈到其他组件。

事件总线通信mitt 作为一个事件总线,允许组件之间进行通信。Child1 通过 emit 触发事件,而 Child2 监听这个事件并接收数据。这种方式实现了兄弟组件之间的双向通信,Child1 可以通过事件将玩具传递给 Child2,而 Child2 可以响应并显示这个玩具。它使得不直接相关的组件能够通过 mitt 进行互动和数据交换。

  1. 安装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 将数据(如 moneycar)传递给子组件,这实现了从父组件到子组件的单向数据流。数据只能由父组件流向子组件,子组件本身不会改变这些数据。

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

provideinject实现祖孙组件直接通信

具体使用:

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

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

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

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

方法实现

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

双向通信:孙组件通过调用 injectupdate 方法,实际上是在调用父组件提供的方法来修改 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++ 修改了仓库中的数据,实现了从组件到仓库的双向通信。这意味着组件不仅可以读取仓库的数据,还可以更新仓库的数据。

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">
    <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>

方法效果


蟹蟹你的浏览~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白白的西柚珉•᷄ࡇ•᷅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值