VUE3-组件通信

(1)Props ------ 父传子(单向、只读)

是什么 :父组件把数据通过属性传给子组件。子组件通过 props 读取。
什么时候用:父组件要把显示数据或配置传给子组件时(比如标题、初始值、只读配置)。

要点/注意:

  • Props 是 单向(父 → 子),子不能直接修改父传下来的 props(在子里 props 是只读的)。

  • 如果子要"改变"显示值,用本地 ref/computed 拷贝一份,或通过 emit 通知父改变数据(见下)。

  • 在 TypeScript 的 script setup 中用 defineProps<T>() 声明类型(你示例正确)。

示例(父传初始值,子显示):

javascript 复制代码
<!-- 父 -->
<ChildComponent :message="parentMessage" />

<!-- 子 -->
<script setup lang="ts">
interface Props { message: string }
const props = defineProps<Props>()
</script>
<template>
  <div>{{ props.message }}</div>
</template>

快捷提示:如果你需要在子里编辑父的数据,不要直接修改 props,而是 emit 一个事件让父去改。

(2)Emits ------ 子传父(事件机制)

是什么 :子组件用 emit 向父发送事件(带数据),父在模板上通过**@事件名** 监听并处理。
什么时候用:子组件触发行为需要父响应或改变父的数据(例如:子里的按钮点了要通知父更新列表、输入框更新等)。

要点/注意:

  • script setup 里用 const emit = defineEmits<...>() 做类型校验(你的示例也很好)。

  • 事件名在模板里通常用 kebab-case(@update@update-value),在 JS/TS 中可以用 camelCase。

  • 常见约定:父负责数据,子负责 UI/事件 -> "props down, events up"。

示例(子告诉父更新值):

javascript 复制代码
<!-- 子 -->
<script setup lang="ts">
const emit = defineEmits<{ (e: 'update', value: string): void }>()
const handleClick = () => emit('update', 'new value')
</script>

<!-- 父 -->
<ChildComponent @update="handleUpdate" />

v-model 快捷约定 :对表单组件常用 v-model。实现时子接收 modelValue(prop)并 emit update:modelValue 事件:

javascript 复制代码
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits(['update:modelValue'])
// 子在输入时:
emit('update:modelValue', newVal)

(3)Provide / Inject ------ 跨层级传值(祖先 → 后代)

是什么 :祖先组件 provide 一个值,任意深度的后代 inject 获取该值,不需要逐层传 props。
什么时候用:当数据要被很多深层子组件共享,但不想层层透传 props(比如主题、语言、表单上下文、库内部共享状态)。

要点/注意:

  • 默认不是响应式 :如果你 provide('key', 123),之后改变祖先里的普通变量不会自动通知后代。要响应式传递请 provide('k', ref(...) )provide('k', reactive(...))

  • inject 可以带默认值:const theme = inject('theme', 'light')

  • 不要把 provide/inject 当成全局状态管理(复杂逻辑、多个组件频繁读写时用 Pinia/Vuex 更合适)。

示例(响应式主题):

javascript 复制代码
// 祖先
import { ref, provide } from 'vue'
const theme = ref('dark')
provide('theme', theme) // 提供 ref

// 后代
import { inject } from 'vue'
const theme = inject('theme', ref('light')) // theme 是一个 ref,可以直接解构使用

(4)小贴士 & 最佳实践(干货)

  1. 首选规则:Props(向下) + Emits(向上)。这是最清晰的组件边界与职责分配方式。

  2. 不要修改 props :若需要本地编辑,用 const local = ref(props.value)computed({ get: ()=>props.x, set: v => emit('update', v) })

  3. 命名一致性 :事件名用语义化如 save, delete, update:modelValue;props 用清晰名如 items, visible

  4. Provide/Inject 的使用门槛:适合库级别或跨很多层的共享,不适合替代父子通信或做频繁的读写共享(那用 Pinia)。

  5. TypeScriptdefineProps<T>()defineEmits<...>() 能把类型错误在编译时暴露出来,推荐始终加类型。

  6. 事件大小写 :模板中监听事件最好写 kebab-case(@update-value),传递/emit 时在 JS 可写 camelCase。这样兼容模板解析。

相关推荐
JieE212几秒前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE21217 分钟前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
爱勇宝1 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒4 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen4 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
Larcher5 小时前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
默_笙5 小时前
🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别
javascript
牧艺5 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
jump_jump5 小时前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化
红尘散仙5 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust