通信方式 | 適用層級 | 數據流向 | 復雜度 |
---|---|---|---|
Props/Emits | 父子組件 | 單向/雙向 | ★☆☆ |
v-model | 父子組件 | 雙向 | ★☆☆ |
Provide/Inject | 跨層級組件 | 自上而下 | ★★☆ |
事件總線 | 任意組件 | 任意方向 | ★★★ |
Pinia/Vuex | 全局狀態 | 任意方向 | ★★☆ |
Refs模板引用 | 父子組件 | 父→子 | ★☆☆ |
作用域插槽 | 父子組件 | 子→父 | ★★☆ |
Web Workers | 跨線程通信 | 任意方向 | ★★★★ |
1. Props/Emits:基礎父子通信
適用場景:直接父子組件通信
注意:避免直接修改props,使用emit通知父組件修改
<!-- 父組件 -->
<script?setup>
import?Child?from?'./Child.vue';
const?message = ref('父組件數據');
const?handleEmit =?(data) =>?{console.log('子組件傳遞:', data);
};
</script><template><Child?:msg="message"?@child-event="handleEmit"?/>
</template><!-- 子組件 Child.vue -->
<script?setup>
const?props = defineProps(['msg']);
const?emit = defineEmits(['child-event']);const?sendToParent =?()?=>?{emit('child-event', {?time:?new?Date() });
};
</script><template><div>收到: {{ msg }}</div><button?@click="sendToParent">發送事件</button>
</template>
2. v-model 雙向綁定升級
優勢:替代Vue2的.sync
修飾符,語法更簡潔
原理:相當于?:modelValue
?+?@update:modelValue
<!-- 父組件 -->
<script?setup>
import?CustomInput?from?'./CustomInput.vue';
const?username = ref('');
</script><template><CustomInput?v-model="username"?/>
</template><!-- 子組件 CustomInput.vue -->
<script?setup>
const?model = defineModel();
</script><template><input?type="text":value="model"@input="model = $event.target.value"/>
</template>
3. Provide/Inject 跨層級通信
適用場景:多級嵌套組件共享數據
注意:避免濫用,復雜場景建議用狀態管理
// 祖先組件
<script setup>
import?{ provide, ref }?from?'vue';
const?theme = ref('dark');
provide('app-theme', {theme,toggle:?()?=>?{theme.value = theme.value ===?'dark'???'light'?:?'dark';}
});
</script>// 任意后代組件
<script setup>
import { inject } from 'vue';
const { theme, toggle } = inject('app-theme');
</script><template><button?@click="toggle">當前主題: {{ theme }}</button>
</template>
4. 事件總線替代方案(mitt)
適用場景:非父子組件通信
優勢:輕量級(僅200B),替代Vue2的$emit/$on
// eventBus.js
import?mitt?from?'mitt';
export?default?mitt();// 組件A(發布事件)
import?bus?from?'./eventBus';
bus.emit('user-login', {?user:?'admin'?});// 組件B(訂閱事件)
import?bus?from?'./eventBus';
bus.on('user-login', (userData) => {console.log('用戶登錄:', userData);
});// 組件卸載時取消訂閱
onUnmounted(()?=>?{bus.off('user-login');
});
5. pinia狀態管理(推薦)
優勢:類型安全、Devtools支持、模塊化設計
對比Vuex:更簡潔API,去除mutations概念
// stores/user.js
import?{ defineStore }?from?'pinia';
export?const?useUserStore = defineStore('user', {state:?()?=>?({?name:?'',?isLogin:?false?}),actions: {login(name) {this.name = name;this.isLogin =?true;}}
});// 組件中使用
<script?setup>
import?{ useUserStore }?from?'@/stores/user';
const?userStore = useUserStore();const?login =?()?=>?{userStore.login('張三');
};
</script><template><div>用戶名: {{ userStore.name }}</div>
</template>
6. 模板引用通信
適用場景:父組件需要直接訪問子組件方法或數據
限制:只能在父子組件間使用
<!-- 父組件 -->
<script?setup>
import?Child?from?'./Child.vue';
import?{ ref, onMounted }?from?'vue';const?childRef = ref(null);onMounted(()?=>?{// 調用子組件方法childRef.value?.childMethod();// 訪問子組件數據console.log(childRef.value?.childData);
});
</script><template><Child?ref="childRef"?/>
</template><!-- 子組件 Child.vue -->
<script?setup>
import?{ defineExpose }?from?'vue';const?childData = ref('子組件數據');
const?childMethod =?()?=>?{console.log('子組件方法被調用');
};// 暴露給父組件
defineExpose({childData,childMethod
});
</script>
7. 作用域插槽(子→父通信)
適用場景:子組件需要向父組件傳遞渲染內容
優勢:保持子組件封裝性的同時提供定制能力
<!-- 子組件 ScopedList.vue -->
<script?setup>
const?items = ref(['Vue',?'React',?'Angular']);
</script><template><ul><li?v-for="(item, index) in items"?:key="index"><slot?:item="item"?:index="index"?/></li></ul>
</template><!-- 父組件 -->
<script?setup>
import?ScopedList?from?'./ScopedList.vue';
</script><template><ScopedList?v-slot="{ item, index }"><span?:class="{ active: index === 0 }">{{ item }}</span></ScopedList>
</template>
8. Web Workers 跨線程通信
適用場景:CPU密集型任務,避免阻塞UI線程
注意:worker中無法訪問DOM
// worker.js
self.onmessage =?(e) =>?{const?result = heavyCalculation(e.data);self.postMessage(result);
};function?heavyCalculation(data)?{// 復雜計算邏輯return?data *?2;
}// 組件中使用
<script setup>
import?{ ref }?from?'vue';const?worker =?new?Worker('./worker.js');
const?result = ref(0);worker.onmessage =?(e) =>?{result.value = e.data;
};const?startCalc =?()?=>?{worker.postMessage(1000000);?// 發送大數據
};
</script><template><button @click="startCalc">開始計算</button><div>結果: {{ result }}</div>
</template>
通信方式對比指南
通信方式 | 適用場景 | 優點 | 缺點 |
---|---|---|---|
Props/Emits | 父子組件簡單通信 | 簡單直接 | 多層傳遞繁瑣 |
v-model | 表單組件雙向綁定 | 語法簡潔 | 僅適用特定場景 |
Provide/Inject | 跨層級組件共享 | 避免逐層傳遞 | 數據來源不透明 |
事件總線 | 任意組件間事件通知 | 靈活解耦 | 難以跟蹤調試 |
Pinia/Vuex | 全局狀態管理 | 集中管理,調試友好 | 學習成本較高 |
模板引用 | 父組件訪問子組件內部 | 精確訪問 | 破壞組件封裝性 |
作用域插槽 | 子組件向父組件暴露渲染數據 | 靈活定制UI | 僅適用于模板內容 |
Web Workers | 后臺計算任務 | 避免UI阻塞 | 通信成本高 |
實戰選型建議
父子組件:優先使用?
props/emits
?+?v-model
兄弟組件:采用共享父組件狀態 或?
事件總線
祖孫組件:使用?
provide/inject
復雜應用:
Pinia
?管理全局狀態性能敏感:
Web Workers
?處理計算密集型任務UI定制:
作用域插槽
?實現內容分發