我們知道,在做 Vue 大型項目時,可以使用 Vuex 做狀態管理,它是一個專爲 Vue.js 開發的狀態管理模式,用於集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。但是由於Vuex的包很大,一些小型項目,組件之間的通信其實沒有必要用Vuex。
與ref 和 $parent / $children不同的是,ref 和 $parent / $children主要是父子之間的通信,當組件 A 和組件 B 中間隔了數代(甚至不確定具體級別)時。以往會藉助 Vuex 或 Bus 這樣的解決方案,不得不引入三方庫來支持。這時候就就可以用provide和inject。
實現 Vuex
一般在 webpack 中使用 Vue.js,都會有一個入口文件 main.js,裏面通常導入了 Vue、VueRouter、iView 等庫,通常也會導入一個入口組件 app.vue 作爲根組件。一個簡單的 app.vue 可能只有以下代碼:
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
使用 provide / inject 替代 Vuex,就是在這個 app.vue 文件上做文章。我們把 app.vue 理解爲一個最外層的根組件,用來存儲所有需要的全局數據和狀態,甚至是計算屬性(computed)、方法(methods)等。因爲你的項目中所有的組件(包含路由),它的父組件(或根組件)都是 app.vue,所以我們把整個 app.vue 實例通過 provide 對外提供。
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
provide () {
return {
app: this
}
}
}
</script>
上面,我們把整個 app.vue 的實例 this 對外提供,命名爲 app(這個名字可以自定義,推薦使用 app,使用這個名字後,子組件不能再使用它作爲局部屬性)。接下來,任何組件(或路由)只要通過 inject 注入 app.vue 的 app 的話,都可以直接通過 this.app.xxx 來訪問 app.vue 的 data、computed、methods 等內容。
app.vue 是整個項目第一個被渲染的組件,而且只會渲染一次(即使切換路由,app.vue 也不會被再次渲染),利用這個特性,很適合做一次性全局的狀態數據管理,例如,我們將用戶的登錄信息保存起來。
<script>
export default {
provide () {
return {
app: this
}
},
data () {
return {
userInfo: null
}
},
methods: {
getUserInfo () {
// 這裏通過 ajax 獲取用戶信息後,賦值給 this.userInfo,以下爲僞代碼
$.ajax('/user/info', (data) => {
this.userInfo = data;
});
}
},
mounted () {
this.getUserInfo();
}
}
</script>
這樣,任何頁面或組件,只要通過 inject 注入 app 後,就可以直接訪問 userInfo 的數據了。
除了直接使用數據,還可以調用方法。比如在某個頁面裏,修改了個人資料,這時一開始在 app.vue 裏獲取的 userInfo 已經不是最新的了,需要重新獲取。可以這樣使用:
<template>
<div>
{{ app.userInfo }}
</div>
</template>
<script>
export default {
inject: ['app'],
methods: {
changeUserInfo () {
// 這裏修改完用戶數據後,通知 app.vue 更新,以下爲僞代碼
$.ajax('/user/update', () => {
// 直接通過 this.app 就可以調用 app.vue 裏的方法
this.app.getUserInfo();
})
}
}
}
</script>
不過需要注意的是:provide
和 inject
主要在開發高階插件/組件庫時使用。並不推薦用於普通應用程序代碼中。