前端知库

Vue 2 有哪些组件通信方式?哪些写法在 Vue 3 中被移除或改变?

Vue 2 用 props/$emit、.sync、$attrs/$listeners、$parent/$refs、provide/inject、EventBus 和 Vuex 通信。Vue 3 中 $listeners 并入 $attrs,.sync 改为 v-model:xxx,$on/$off/$once 被移除,EventBus 需改用 mitt 等库。

参考答案

Vue 2 已于 2023-12-31 停止维护,本题 Vue 2 部分以 2.7 为准。props 单向数据流、插槽与 provide/inject 的取舍分别见《为什么 Vue 提倡 props 单向数据流?》和《插槽、作用域插槽和 provide/inject 各解决什么问题?》。

简答

  • 父子之间首选 props 向下、$emit 向上;需要“双向”时用 v-model 或 .sync 语法糖,本质仍是 prop + 事件。
  • 透传用 $attrs 和 $listeners;跨多层用 provide/inject;全局共享状态用 Vuex;$parent、$refs 直接访问实例,耦合最强。
  • Vue 3 中 $listeners 被移除,监听器以 onXxx 形式并入 $attrs;$attrs 也开始包含 class 和 style。
  • Vue 3 移除了 .sync 和 model 选项,改为 v-model:title;组件 v-model 默认的 prop/事件从 value/input 变为 modelValue/update:modelValue。
  • Vue 3 移除了实例上的 $on、$off、$once,基于 new Vue() 的 EventBus 不再可用,官方建议改用 mitt、tiny-emitter 等库;官方状态管理推荐也从 Vuex 变为 Pinia。

Vue 2 的通信方式

方式方向说明
props / $emit父 → 子 / 子 → 父最常用,数据流清晰
v-model / .sync双向语法糖:title.sync="t" 等价于 :title="t" @update:title="t = $event"
$attrs / $listeners透传包装组件把未声明的属性和监听器转交给内部元素
$parent / $children / $refs直接访问实例便捷但耦合重,适合调用子组件方法
provide / inject祖先 → 后代Vue 2 中注入的值默认不是响应式,除非提供的是响应式对象
EventBus任意组件const bus = new Vue(),用 $on/$emit/$off
Vuex全局集中管理共享状态

Vue 3 的变化

$listeners 并入 $attrs

Vue 3 的虚拟 DOM 中,事件监听器就是带 on 前缀的属性,因此归入 $attrs,$listeners 被移除。

vue
<!-- Vue 2 -->
<input v-bind="$attrs" v-on="$listeners" />

<!-- Vue 3 -->
<input v-bind="$attrs" />

两个版本都可以配合 inheritAttrs: false 控制透传位置。注意 Vue 3 的 $attrs 包含 class 和 style,关闭 inheritAttrs 后它们也不会自动落到根元素上。

.sync 改为 v-model 参数

vue
<!-- Vue 2 -->
<ChildComponent :title.sync="pageTitle" />

<!-- Vue 3 -->
<ChildComponent v-model:title="pageTitle" />

子组件仍然 emit('update:title', value)。Vue 3 允许同一组件绑定多个 v-model,组件的 model 选项也被移除。

$on / $off / $once 移除

组件实例不再实现事件发射器接口,EventBus 写法需要替换:

JavaScript
// Vue 3:用 mitt 替代 new Vue() 作为事件总线
import mitt from 'mitt';
export const bus = mitt();

bus.on('saved', handler);
bus.emit('saved', payload);
bus.off('saved', handler);

迁移指南同时提醒,大多数情况下不建议用全局事件总线,优先考虑 props/事件、provide/inject 或全局状态管理。$emit 本身仍保留,用于触发父组件监听的事件。

其他

  • $children 被移除,改用模板 ref 访问子组件。
  • Vuex 4 仍支持 Vue 3,但官方文档说明 Vuex 已进入维护模式,新项目推荐 Pinia。

常见追问

  • Vue 3 中子组件的 $emit 事件需要声明吗?推荐用 emits 选项或 defineEmits 声明;未声明的监听器会留在 $attrs 中,默认透传到子组件根元素。
  • 迁移时 .sync 和 $listeners 能临时保留吗?@vue/compat 迁移构建提供 COMPONENT_V_MODEL、INSTANCE_LISTENERS、INSTANCE_EVENT_EMITTER 等兼容开关,可以逐步替换。