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。这样兼容模板解析。

相关推荐
sunfdf19 分钟前
Next.js 新手从零部署到首跑实战指南
开发语言·javascript·ecmascript
YHHLAI23 分钟前
Agent 智能体开发实战 · 第六课:MCP 协议 —— 让 Agent 跨进程调用工具
前端·人工智能
SmartBoyW29 分钟前
前端死磕:一文彻底搞懂 JS 事件循环 (Event Loop) 与宏微任务
前端·javascript
爱勇宝1 小时前
3位工程师靠“删AI代码”创业,一周收费1万美元:以后最贵的能力,可能不是写代码
前端·后端·架构
Curvatureflight1 小时前
客户端日志上报怎么设计?让线上问题不再只靠用户截图
前端·系统架构
不法1 小时前
uniapp map地图导航/地图路线
前端·vue.js·uni-app
mONESY1 小时前
基于MCP协议搭建全链路智能Agent:地图检索+浏览器自动化+本地文件操控实战
javascript
星栈2 小时前
LiveView 页面越写越卡,我最后抓到的凶手不是数据库,是我自己乱塞 `assigns`
前端·前端框架·elixir
研☆香2 小时前
闭包实战避坑指南
javascript