在Vue 3中,组件间通信的方式可以分为两大类:选项式API(Options API)和组合式API(Composition API)。每种API风格都有其特点和适用场景,下面将分别介绍这两种API风格下的组件间通信方法。
选项式API(Options API)
1. props与emit
 
props用于父组件向子组件传递数据,而emit用于子组件向父组件传递数据。
父组件:
<template>
  <ChildComponent :message="parentMessage" @childEvent="handleChildEvent" />
</template>
<script>
export default {
  data() {
    return {
      parentMessage: 'Hello from Parent'
    };
  },
  methods: {
    handleChildEvent(message) {
      console.log(message); // 输出: Hello from Child
    }
  }
};
</script>
 
子组件:
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="sendToParent">Send to Parent</button>
  </div>
</template>
<script>
export default {
  props: ['message'],
  methods: {
    sendToParent() {
      this.$emit('childEvent', 'Hello from Child');
    }
  }
};
</script>
 
2. provide与inject
 
provide和inject用于祖先组件向其所有后代组件提供数据。
祖先组件:
<template>
  <ChildComponent />
</template>
<script>
export default {
  provide() {
    return {
      theme: 'dark'
    };
  }
};
</script>
 
后代组件:
<template>
  <p>The current theme is {{ theme }}</p>
</template>
<script>
export default {
  inject: ['theme']
};
</script>
 
3. ref
 
父组件可以通过ref属性来访问子组件的实例,从而直接调用子组件的方法或访问其属性。
父组件:
<template>
  <ChildComponent ref="childRef" />
</template>
<script>
export default {
  mounted() {
    this.$refs.childRef.someMethod(); // 假设子组件有一个someMethod方法
  }
};
</script>
 
子组件:
<template>
  <p>Hello from Child</p>
</template>
<script>
export default {
  methods: {
    someMethod() {
      console.log('Called from Parent');
    }
  }
};
</script>
 
组合式API(Composition API)
1. props与emit
 
父组件:
<template>
  <ChildComponent :message="parentMessage" @childEvent="handleChildEvent" />
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentMessage = ref('Hello from Parent');
function handleChildEvent(message) {
  console.log(message); // 输出: Hello from Child
}
</script>
 
子组件:
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="sendToParent">Send to Parent</button>
  </div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
  message: String
});
const emit = defineEmits(['childEvent']);
function sendToParent() {
  emit('childEvent', 'Hello from Child');
}
</script>
 
2. provide与inject
 
祖先组件:
<template>
  <ChildComponent />
</template>
<script setup>
import { provide, ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const theme = ref('dark');
provide('theme', theme);
</script>
 
后代组件:
<template>
  <p>The current theme is {{ theme }}</p>
</template>
<script setup>
import { inject } from 'vue';
const theme = inject('theme');
</script>
 
3. ref
 
父组件可以通过ref属性来访问子组件的实例,从而直接调用子组件的方法或访问其属性。
父组件:
<template>
  <ChildComponent ref="childRef" />
</template>
<script setup>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';
const childRef = ref(null);
onMounted(() => {
  if (childRef.value) {
    childRef.value.someMethod(); // 假设子组件有一个someMethod方法
  }
});
</script>
 
子组件:
<template>
  <p>Hello from Child</p>
</template>
<script setup>
function someMethod() {
  console.log('Called from Parent');
}
</script>
 
4. Vuex与Pinia
 
对于大型应用,当组件间通信变得非常复杂时,可以考虑使用状态管理库如Vuex或Pinia。
Vuex:
// store/index.js
import { createStore } from 'vuex';
export default createStore({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment({ commit }) {
      commit('increment');
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
});
 
使用Vuex:
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const count = computed(() => store.state.count);
const doubleCount = computed(() => store.getters.doubleCount);
function increment() {
  store.dispatch('increment');
}
</script>
 
Pinia:
// store/index.js
import { defineStore } from 'pinia';
export const useMainStore = defineStore('main', {
  state: () => ({
    count: 0
  }),
  actions: {
    increment() {
      this.count++;
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  }
});
 
使用Pinia:
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double Count: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>
<script setup>
import { computed } from 'vue';
import { useMainStore } from '@/store';
const store = useMainStore();
const count = computed(() => store.count);
const doubleCount = computed(() => store.doubleCount);
function increment() {
  store.increment();
}
</script>
 
总结
选项式API:适用于熟悉Vue 2的开发者,代码结构清晰,适合中小型项目。
 组合式API:提供了更灵活的代码组织方式,适合大型项目和复杂的业务逻辑。
无论是哪种API风格,Vue 3都提供了丰富的工具和方法来实现组件间的高效通信。选择合适的API风格和通信方式,可以显著提升开发效率和代码的可维护性。










