Vue2 vs Vue3 组件通信方法对比
1. 父子组件通信
1.1 Props 传递
Vue2
vue
<!-- 父组件 -->
<template>
<child-component :message="message"></child-component>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
}
}
</script>
<!-- 子组件 -->
<script>
export default {
props: {
message: String
}
}
</script>
Vue3
vue
<!-- 父组件 -->
<template>
<child-component :message="message"></child-component>
</template>
<script setup>
import { ref } from 'vue'
const message = ref('Hello')
</script>
<!-- 子组件 -->
<script setup>
defineProps({
message: String
})
</script>
1.2 自定义事件
Vue2
vue
<!-- 子组件 -->
<template>
<button @click="handleClick">点击</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('custom-event', { data: 'value' })
}
}
}
</script>
<!-- 父组件 -->
<template>
<child @custom-event="handleEvent"></child>
</template>
<script>
export default {
methods: {
handleEvent(data) {
console.log(data)
}
}
}
</script>
Vue3
vue
<!-- 子组件 -->
<template>
<button @click="handleClick">点击</button>
</template>
<script setup>
const emit = defineEmits(['custom-event'])
const handleClick = () => {
emit('custom-event', { data: 'value' })
}
</script>
<!-- 父组件 -->
<template>
<child @custom-event="handleEvent"></child>
</template>
<script setup>
const handleEvent = (data) => {
console.log(data)
}
</script>
2. 跨层级组件通信
2.1 provide/inject
Vue2
vue
<!-- 祖先组件 -->
<script>
export default {
provide() {
return {
theme: this.theme,
updateTheme: this.updateTheme
}
},
data() {
return {
theme: 'dark'
}
},
methods: {
updateTheme(value) {
this.theme = value
}
}
}
</script>
<!-- 后代组件 -->
<script>
export default {
inject: ['theme', 'updateTheme']
}
</script>
Vue3
vue
<!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue'
const theme = ref('dark')
const updateTheme = (value) => {
theme.value = value
}
provide('theme', {
theme,
updateTheme
})
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'
const { theme, updateTheme } = inject('theme')
</script>
2.2 事件总线
Vue2
javascript
// eventBus.js
import Vue from 'vue'
export const eventBus = new Vue()
// 组件 A
this.eventBus.$emit('custom-event', data)
// 组件 B
mounted() {
this.eventBus.$on('custom-event', this.handleEvent)
},
beforeDestroy() {
this.eventBus.$off('custom-event', this.handleEvent)
}
Vue3
typescript
// eventBus.ts
import mitt from 'mitt'
export const emitter = mitt()
// 组件 A
import { emitter } from './eventBus'
emitter.emit('custom-event', data)
// 组件 B
import { onMounted, onUnmounted } from 'vue'
import { emitter } from './eventBus'
onMounted(() => {
emitter.on('custom-event', handleEvent)
})
onUnmounted(() => {
emitter.off('custom-event', handleEvent)
})
3. 访问组件实例
3.1 ref 引用
Vue2
vue
<!-- 父组件 -->
<template>
<child-component ref="childRef"></child-component>
</template>
<script>
export default {
mounted() {
console.log(this.$refs.childRef.someData)
this.$refs.childRef.someMethod()
}
}
</script>
<!-- 子组件 -->
<script>
export default {
data() {
return {
someData: 'value'
}
},
methods: {
someMethod() {
// ...
}
}
}
</script>
Vue3
vue
<!-- 父组件 -->
<template>
<child-component ref="childRef"></child-component>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const childRef = ref(null)
onMounted(() => {
console.log(childRef.value.someData)
childRef.value.someMethod()
})
</script>
<!-- 子组件 -->
<script setup>
import { ref } from 'vue'
const someData = ref('value')
const someMethod = () => {
// ...
}
defineExpose({
someData,
someMethod
})
</script>
3.2 父组件访问
Vue2
vue
<!-- 子组件 -->
<script>
export default {
methods: {
accessParent() {
console.log(this.$parent.parentData)
this.$parent.parentMethod()
}
}
}
</script>
Vue3
vue
<!-- 子组件 -->
<script setup>
import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
const accessParent = () => {
console.log(proxy.$parent.parentData)
proxy.$parent.parentMethod()
}
</script>
4. 主要差异总结
-
语法差异
- Vue2 使用选项式 API
- Vue3 推荐使用组合式 API 和
<script setup>
-
事件总线
- Vue2 可以使用 Vue 实例作为事件总线
- Vue3 移除了 $on, $off 等方法,需要使用第三方库(如 mitt)
-
组件实例访问
- Vue2 可以直接访问组件实例的所有属性和方法
- Vue3 需要通过 defineExpose 显式暴露要访问的属性和方法
-
响应式系统
- Vue2 使用 Object.defineProperty
- Vue3 使用 Proxy,提供了更好的响应式支持
-
性能优化
- Vue3 提供了更好的性能优化机制
- 组件通信的性能开销更小
5. 最佳实践建议
-
迁移策略
- 逐步从 Vue2 迁移到 Vue3
- 优先使用 Vue3 的新特性
- 保持代码的一致性
-
代码组织
- Vue3 中更推荐使用组合式 API
- 将相关的逻辑组织在一起
- 使用 hooks 复用逻辑
-
性能优化
- 合理使用响应式数据
- 避免不必要的组件通信
- 及时清理事件监听器