ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue3 组件通信

Vue3 组件通信 Vue3 组件通信前言Vue3 是组件化开发框架组件之间默认是相互独立的。所谓组件通信就是让不同组件能够共享数据、传递事件、协同工作。目录Props父传子Emits子传父v-model / defineModel双向绑定ref defineExpose父访问子attrs属性透传provide / inject跨层级通信Pinia全局状态共享mitt任意组件事件总线一、Props父组件向子组件传值Props 是最基础、最常用的通信方式遵循单向数据流。父组件template Child :titlepageTitle :listlist / /template script setup import { ref } from vue import Child from ./Child.vue const pageTitle ref(Vue3 组件通信) const list ref([Props, Emits, Provide]) /script子组件script setup const props defineProps({ title: { type: String, required: true }, list: { type: Array, default: () [] } }) console.log(props.title) // 父组件传来的值 /script特点数据只能从父流向子子组件不能直接修改 props。如果传入的是响应式数据ref/reactive父组件修改后子组件会自动更新。适合父子直接通信简单清晰。注意子组件直接修改 props 会报警告必须通过 emit 通知父组件修改。二、Emits子组件向父组件传值子组件不能修改父组件数据但可以通过触发自定义事件把数据或操作意图传回父组件。子组件script setup const emit defineEmits([update-title, submit]) function changeTitle() { emit(update-title, 新标题来自子组件) } function handleSubmit() { emit(submit, { id: 1, name: 张三 }) } /script template button clickchangeTitle修改标题/button button clickhandleSubmit提交/button /template父组件template Child update-titlehandleUpdateTitle submithandleSubmit / /template script setup function handleUpdateTitle(newTitle) { console.log(newTitle) // 新标题来自子组件 } function handleSubmit(payload) { console.log(payload) // { id: 1, name: 张三 } } /script特点子组件通过emit(事件名, 数据)向上传递。父组件在模板中用事件名监听。事件名推荐使用 kebab-case如update-title。三、v-model / defineModel双向绑定Vue3 支持在组件上使用v-model实现父子数据双向同步。Vue 3.4 起推荐使用defineModel()宏写法更简洁。子组件 Child.vuescript setup const model defineModel() function increment() { model.value // 自动同步到父组件 } /script template div当前值{{ model }}/div button clickincrement1/button /template父组件template Child v-modelcount / /template script setup import { ref } from vue const count ref(0) /script原理简析v-modelcount会被编译成Child :modelValuecount update:modelValuecount $event /defineModel()本质上是 Props Emits 的语法糖。多个 v-model!-- 子组件 -- script setup const firstName defineModel(firstName) const lastName defineModel(lastName) /script !-- 父组件 -- Child v-model:first-namefirst v-model:last-namelast /四、ref defineExpose父组件访问子组件有时候父组件需要直接调用子组件的方法或访问子组件的数据这时可以用ref配合defineExpose。子组件script setup import { ref } from vue const count ref(0) function reset() { count.value 0 } // 只有暴露出去的内容才能被父组件访问 defineExpose({ count, reset }) /script父组件template Child refchildRef / button clickhandleReset重置子组件/button /template script setup import { ref } from vue import Child from ./Child.vue const childRef ref(null) function handleReset() { childRef.value.reset() console.log(childRef.value.count) } /script特点打破了单向数据流父组件可以直接操作子组件。适合弹窗控制、表单校验、图表刷新等场景。不要滥用否则组件耦合会变高。五、attrs属性透传当父组件给子组件传递了子组件没有声明为 props 的属性时这些属性会进入attrs。常用于封装第三方组件或透传 class、style、事件。子组件 MyInput.vuescript setup defineProps({ label: String }) /script template label{{ label }}/label input v-bind$attrs / /template父组件template MyInput label用户名 placeholder请输入用户名 maxlength20 focusonFocus / /templateplaceholder、maxlength、focus都会通过$attrs自动绑定到input上。注意如果子组件有多个根节点需要手动指定v-bind$attrs绑到哪个元素上。六、provide / inject跨层级通信当组件层级很深用 Props 一层层传会非常麻烦这时可以用provide和inject。祖先组件script setup import { ref, provide, readonly } from vue const theme ref(dark) provide(theme, theme) provide(config, readonly({ version: 1.0 })) /script任意后代组件script setup import { inject } from vue const theme inject(theme, light) // 默认值 light const config inject(config) console.log(theme.value) // dark /script特点祖先提供数据后代注入数据中间组件不需要关心。如果提供的是 ref后代修改.value会同步更新如果不想被修改用readonly()包裹。适合主题、语言、用户信息等全局但不需要 Pinia 的场景。七、Pinia全局状态共享当多个不相邻组件需要共享同一份可变状态时Pinia 是最佳选择。定义 Store// stores/counter.jsimport{defineStore}frompiniaexportconstuseCounterStoredefineStore(counter,{state:()({count:0}),getters:{doubleCount:(state)state.count*2},actions:{increment(){this.count}}})组件中使用script setup import { useCounterStore } from /stores/counter import { storeToRefs } from pinia const store useCounterStore() const { count, doubleCount } storeToRefs(store) function add() { store.increment() } /script template div{{ count }} / {{ doubleCount }}/div button clickadd1/button /template特点全局唯一状态任何组件都可以读写。支持 devtools 调试、状态持久化等。适合用户信息、购物车、权限、主题等跨页面共享数据。注意直接解构store.count会丢失响应性必须用storeToRefs。八、mitt任意组件事件总线Vue3 移除了$on/$off官方推荐用mitt等第三方库实现事件总线。安装npminstallmitt创建事件总线// utils/eventBus.jsimportmittfrommittconstbusmitt()exportdefaultbus组件 A 发送事件importbusfrom/utils/eventBusbus.emit(user-login,{name:张三})组件 B 监听事件importbusfrom/utils/eventBusimport{onUnmounted}fromvuefunctionhandleLogin(user){console.log(用户登录,user.name)}bus.on(user-login,handleLogin)onUnmounted((){bus.off(user-login,handleLogin)// 记得清理})特点发布/订阅模式完全解耦。不会自动触发视图更新需要手动处理状态。适合完全无关的组件之间通信但不要滥用。
返回列表