Vue 中 provide 與 inject 的使用方法
在 Vue 組件樹中,當需要跨多層級傳遞數據時,一層層用 props 傳遞會變得繁瑣,就像接力賽要經過多個人傳遞一樣低效。這時候 provide 與 inject 就像一對 “數據快遞通道”,能讓父組件直接把數據 “發送” 給任意層級的子組件,跳過中間層,讓深層級通信更簡潔。
最基礎的用法是父組件通過 provide 提供數據,任意子組件(無論層級多深)用 inject 接收。比如在根組件提供用户登錄狀態,讓深層的按鈕組件能直接使用:
<!-- 父組件(如App.vue) -->
<template>
<div class="app">
<h3>根組件</h3>
<button @click="isLogin = !isLogin">
{{ isLogin ? '退出登錄' : '登錄' }}
</button>
<ChildComponent />
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import ChildComponent from './ChildComponent.vue'
// 登錄狀態數據
const isLogin = ref(false)
// 提供數據:第一個參數是注入名,第二個是要提供的數據
provide('userLoginStatus', isLogin)
</script>
<!-- 子組件(中間層,無需處理數據) -->
<template>
<div class="child">
<h4>子組件</h4>
<GrandChildComponent />
</div>
</template>
<script setup>
import GrandChildComponent from './GrandChildComponent.vue'
</script>
<!-- 孫組件(深層級組件) -->
<template>
<div class="grand-child">
<h5>孫組件</h5>
<button v-if="isLogin" class="operate-btn">
編輯內容(僅登錄可見)
</button>
<p v-else>請登錄後操作</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
// 接收數據:參數是父組件提供的注入名
const isLogin = inject('userLoginStatus')
</script>
這裏根組件通過provide('userLoginStatus', isLogin)提供登錄狀態,中間的子組件無需任何處理,孫組件直接用inject('userLoginStatus')就能拿到數據,實現了跨層級的數據傳遞,避免了 props 的層層傳遞。
provide 與 inject 還支持傳遞響應式數據,當提供的數據變化時,所有注入該數據的組件都會自動更新。比如實現一個主題切換功能,讓所有組件同步響應主題變化:
<!-- 主題提供組件 -->
<template>
<div :class="theme">
<h3>主題切換</h3>
<button @click="theme = theme === 'light' ? 'dark' : 'light'">
切換到{{ theme === 'light' ? '深色' : '淺色' }}模式
</button>
<DeepComponent />
</div>
</template>
<script setup>
import { ref, provide } from 'vue'
import DeepComponent from './DeepComponent.vue'
// 響應式主題數據
const theme = ref('light')
// 提供響應式數據
provide('appTheme', theme)
</script>
<style>
.light {
background: white;
color: #333;
}
.dark {
background: #333;
color: white;
}
</style>
<!-- 深層組件 -->
<template>
<div class="deep">
<p>當前主題:{{ theme }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
// 接收響應式主題數據
const theme = inject('appTheme')
</script>
當點擊切換按鈕改變theme的值時,提供組件和所有注入appTheme的組件都會實時更新樣式,因為注入的數據保持了響應式特性,這種方式特別適合全局狀態的共享。
為了讓注入更可靠,可以給 inject 設置默認值,當找不到對應的數據時,會使用默認值而不是返回 undefined。比如給用户角色設置默認值:
<!-- 子組件 -->
<template>
<div>
<p>用户角色:{{ userRole }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue'
// 設置默認值:當沒有提供'userRole'時使用'visitor'
const userRole = inject('userRole', 'visitor')
</script>
如果默認值需要是一個複雜對象(如對象、數組),最好用工廠函數返回,避免多個組件實例共享同一個默認值對象:
<script setup>
import { inject } from 'vue'
// 複雜默認值用工廠函數
const config = inject('appConfig', () => ({
size: 'medium',
layout: 'grid'
}))
</script>
provide 與 inject 還支持注入別名,當注入名可能衝突時,可以通過對象形式指定別名,讓代碼更清晰:
<!-- 提供組件 -->
<script setup>
import { provide } from 'vue'
const userInfo = { name: '張三', age: 25 }
provide('user', userInfo)
</script>
<!-- 注入組件 -->
<script setup>
import { inject } from 'vue'
// 給注入的數據起別名
const currentUser = inject('user')
</script>
需要注意的是,provide 與 inject 主要用於跨層級的深層通信,比如插件開發、全局配置等場景。對於父子組件或相鄰層級的通信,使用 props 和 emits 會更合適,因為它們的數據流更明確。另外,雖然子組件可以修改注入的數據,但最好遵循單向數據流原則,在子組件中通過 emits 通知父組件修改,保持數據變化可追蹤。
總的來説,provide 與 inject 就像組件樹中的 “數據隧道”,讓深層級通信變得簡單直接,尤其在大型應用中能減少 props 傳遞的冗餘代碼,讓組件結構更清晰。合理使用這對工具,能讓 Vue 的組件通信方式更加靈活多樣。