【Vue3】组件通信之props

背景

随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内容并非完全原创,大多是参考其他文章资料整理所得,感谢每位技术人的开源精神。

简介

本文介绍 Vue3 中如何使用 props 实现组件间通信,即组件间相互传数据。

Vue3 中组件间通信包括:

  • 父组件向子组件传数据,实现方案有:
    • props
    • v-model
    • $refs
    • 默认插槽 / 具名插槽
  • 子组件向父组件传数据
    • props
    • v-model
    • $parent
    • 自定义事件
    • 作用域插槽
  • 父组件与子组件的子组件互传数据,即与孙子组件互传数据
    • $attrs
    • provider & inject
  • 任意组件间传数据
    • mitt
    • Pinia

开发环境

分类名称版本
操作系统WindowsWindows 11
IDEVisual Studio Code1.91.1

开发步骤及源码

1> 创建 Vue3 工程,参考:【Vue3】工程创建及目录说明

2> 删除 src 目录下 assetscomponents 目录。

3> 修改 src 目录下 main.ts

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

4> 定义子组件,接收来自父组件的数据。

<template>
    <div class="content">
        <h1>子组件</h1>
        <div class="blog" v-for="blog in blogs" :key="blog.id">
            <div class="blog-title">标题:{{ blog.title }}</div>
            <div class="blog-author">作者:{{ author }}</div>
            <div class="blog-content">{{ blog.content }}</div>
        </div>
    </div>
</template>

<script setup lang="ts">
const data = defineProps(['author', 'blogs'])
</script>

<style scoped lang="scss">
.content {
    background-color: aquamarine;
    padding: 20px;
    .blog {
        border: 1px solid gray;
        border-radius: 5px;
        padding: 0 10px;
        margin-top: 5px;
        div {
            padding: 10px 5px;
        }
        .blog-title {
            font-weight: bold;
        }
        .blog-author {
            font-size: small;
            font-style: italic;
        }
        .blog-content {
            font-size: small;
        }
    }
}
</style>

注意:需要使用 defineProps 定义接收父组件的哪些 props,如果这里没有定义,则即使父组件有传值,子组件也不能使用。defineProps 的参数是一个数组。
因为用到了 CSS 预处理器,所以需要执行 npm install -D sass 命令安装。

5> 修改 Vue 根组件 src/App.vue,引用以上子组件并向其传值。

<template>
  <div class="parent">
    <h1>父组件</h1>
    <h2>Blog数量:{{ blogs.length }}</h2>
    <Blog :blogs="blogs" :author="author" />
  </div>
</template>

<script setup lang="ts">
import Blog from './components/Blog.vue'
import { reactive, ref } from 'vue'

const author = ref('Nick')

const blogs = reactive([
  { id: '001', title: '美国大选', content: '美国大选将于...' },
  { id: '002', title: '奥运奖牌', content: '截止今日奥运奖牌榜...' },
  { id: '003', title: '俄乌战争', content: '乌克兰单方面提出希望和谈...' },
  { id: '004', title: '巴以冲突', content: '巴以冲突最新战况...' },
])
</script>

<style scoped lang="scss">
.parent {
  background-color: orange;
  padding: 20px;
}
</style>

可见,父组件向子组件传值只需在子组件标签上添加自定义属性(:blogs="blogs" :author="author")。

6> 执行命令 npm run dev 启动应用,浏览器访问:http://localhost:5173/
在这里插入图片描述
通过颜色可以辨别组件范围,可以看到子组件正确呈现出了来自父组件的数据。

7> 修改 Vue 根组件 src/App.vue,实现父组件接收来自子组件的数据。

<template>
  <div class="parent">
    <h1>父组件</h1>
    <h2>Blog数量:{{ blogs.length }}</h2>
    <h2>浏览数量:{{ readTimes }}</h2>
    <Blog :blogs="blogs" :author="author" :syncReadTimes="syncReadTimes" />
  </div>
</template>

<script setup lang="ts">
import Blog from './components/Blog.vue'
import { reactive, ref } from 'vue'

const author = ref('Nick')

const blogs = reactive([
  { id: '001', title: '美国大选', content: '美国大选将于...' },
  { id: '002', title: '奥运奖牌', content: '截止今日奥运奖牌榜...' },
  { id: '003', title: '俄乌战争', content: '乌克兰单方面提出希望和谈...' },
  { id: '004', title: '巴以冲突', content: '巴以冲突最新战况...' },
])

const readTimes = ref(0)

function syncReadTimes(value) {
  readTimes.value += value
}
</script>

<style scoped lang="scss">
.parent {
  background-color: orange;
  padding: 20px;
}
</style>

可见,父组件定义了一个方法 syncReadTimes,并将此方法作为子组件的自定义属性(:syncReadTimes="syncReadTimes")传入。

7> 修改子组件,实现向父组件传数据。

<template>
    <div class="content">
        <h1>子组件</h1>
        <div class="blog" v-for="blog in blogs" :key="blog.id">
            <div class="blog-title">标题:{{ blog.title }}</div>
            <div class="blog-author">作者:{{ author }}</div>
            <div class="blog-content">{{ blog.content }}</div>
            <button @click="syncReadTimes(1)">浏览量+1</button>
        </div>
    </div>
</template>

<script setup lang="ts">
const data = defineProps(['author', 'blogs', 'syncReadTimes'])
</script>

<style scoped lang="scss">
.content {
    background-color: aquamarine;
    padding: 20px;
    .blog {
        border: 1px solid gray;
        border-radius: 5px;
        padding: 0 10px;
        margin-top: 5px;
        div {
            padding: 10px 5px;
        }
        .blog-title {
            font-weight: bold;
        }
        .blog-author {
            font-size: small;
            font-style: italic;
        }
        .blog-content {
            font-size: small;
        }
        button {
            margin: 10px 0;
        }
    }
}
</style>

首先要在 defineProps 中定义接收来自父组件的 syncReadTimes,然后增加一个按钮 <button>,每点击一次便调用父组件的 syncReadTimes 并传入数据 1

8> 浏览器页面(http://localhost:5173/)刷新后点击按钮,会发现父组件中的 浏览数量 显示随点击次数增加。
在这里插入图片描述

总结

Vue3 中 props 可以实现父组件与子组件间的双向数据通信:

  • 子组件通过 defineProps 定义接收父组件的哪些数据;
  • 父组件将传给子组件的数据通过自定义属性添加到组件标签上;
  • 父组件如果要接收来自子组件的数据,则定义一个方法,并将此方法作为自定义属性添加到组件标签上,子组件通过 defineProps 定义接收此方法引用,通过调用此方法向父组件传数据。
  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

又言又语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值