在 開發中,組件通信一直是一大痛點。 當項目是很簡單的 或者多入口項目時,可以靠著 自帶的 進行組件通信;規模再大一些,可以搭配使用 匯流排進行兄弟組件通信;項目再大一些,出現更複雜的組件關係時,複雜的組件通信可以讓你寫得懷疑人生。 萬幸的是, 官方出品了 ,通過全局式的狀態管理,解決了這一痛點。 雖 ...
在 vue
開發中,組件通信一直是一大痛點。
當項目是很簡單的 SPA
或者多入口項目時,可以靠著 vue
自帶的 prop/$emit
進行組件通信;規模再大一些,可以搭配使用 bus
匯流排進行兄弟組件通信;項目再大一些,出現更複雜的組件關係時,複雜的組件通信可以讓你寫得懷疑人生。
萬幸的是, vue
官方出品了 vuex
,通過全局式的狀態管理,解決了這一痛點。
雖然 vuex
很好用,但是,很多小伙伴和我吐槽 vuex
的文檔和 vue-ssr
的文檔一樣,讓人看得一臉懵逼。
好吧,下麵就讓我來帶著大家一起入門 vuex
。
安裝並引入
正常情況下,我們使用 vue-cli3
生成項目時,可以選擇集成 vuex
到項目中。此時, vue-cli3
會自動安裝 vuex
,併在 src
文件夾下生成 store.js
完成 vuex
的引入和配置。
但是,很多同學並沒有使用 vue-cli3
或者生成項目時沒有選擇集成 vuex
。此時,就只能手動安裝並引入 vuex
了。
安裝
由於 vuex
是用於全局狀態管理的,所以,它不僅僅作用於開發環境,而且還要用於生產環境。
顯而易見,安裝 vuex
應該使用 -S
即 --save
命令。
npm install vuex -S
引入
類似於 vue-cli3
生成的項目,我們在 src
文件夾下新建 store.js
,併在其中寫入:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({})
然後,我們只需要在 vue
實例中引入 store.js
中的 Vuex.Store
實例即可:
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
// 引入store
store,
render: h => h(App)
}).$mount('#app')
Vuex的使用
完成了 vuex
的安裝和引入,接下來我們進入 Vuex
的使用。
vuex
中有三要素: state
, mutation
以及 action
。它們之間的關係可以用官網那張著名的圖來表示:
State
簡單來說, state
表示狀態,類似於 vue
中的 data
(其實本質上就是差不多的, vuex
在 vue
的 beforeCreate
鉤子中將 state
混入進 data
)。但是,它們又有很大的不同: 在使用者看來, state
是全局的,這得益於 vuex
的設計理念——單一狀態樹。這些我將在後幾篇文章中詳細,現在我們只需要知道 state
是類似於全局下的 data
。
接下來我們通過一個簡單例子來感受下 state
:
首先,我們需要修改 store.js
文件,配置 state
。可以看到,我們在生成 Vuex.Store
實例時傳入了實例化選項對象,對象包含一個 state
屬性, state
對象的屬性就是我們定義的全局狀態。
此時,我們定義了一個全局狀態——count
,並將其的初始值設為1
。
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 添加state
state: {
count: 1
}
})
接下來,我們需要在組件中引用 count
,由於它是全局狀態,我們可以在任何一個組件中使用。為了展示其威力,我們在兩個不同的組件中使用它。
首先我們在 App.vue
中使用它:
在模板中,我們使用 $store.state.count
引入該全局狀態,沒錯,使用它就是那麼簡單,只需要 以 $store.state.key
的形式調用。
// App.vue
<template>
<div id="app">
<div id="nav">
{{$store.state.count}}
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</div>
</template>
可以發現, Home
前多出了一個 1
,這代表著我們成功引入了全局狀態 count
。
接下來我們在 Home.vue
的子組件 HelloWorld.vue
中引入 count
。
相同的引用方式: $store.state.count
// HelloWorld.vue
<template>
<div class="hello">
{{$store.state.count}}
</div>
</template>
可以發現,頁面中又多出了一個 1
,代表著我們又一次引用成功。現在,是不是已經感受到了 vuex
的威力?
Mutation
但是,上面的示例有個問題,那就是全局狀態是靜態的。如果在實際應用場景中,一般來說,會經常更改狀態。
有的同學會說,我們直接在方法中修改 this.$store.state.key
的值不就行了嗎?
不好意思,當然是不行的。
state
和 data
的另一大區別在於,你不能直接改變 state
。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit
) mutation
。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用。
簡而言之,我們把 mutation
當做接收 state
作為參數並修改 state
的自定義事件即可,上一段所說的 commit
就是觸發 mutaion
這個自定義事件的方法。
光說不練假把式,接下來,我們對為 vuex
添加上 mutation
,實現 state
的動態改變:
首先,當然是修改生成 Vuex.Store
示例的選項對象,為其添加 mutations
。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1
},
// 添加mutation
mutations: {
increment (state) {
state.count++
}
}
})
在上面的代碼中,我們添加了一個名為 increment
的 mutation
。完成了自定義事件,接下來,我們只需要在組件中對 mutation
進行觸發即可。
我們在 HelloWorld.vue
添加一個按鈕,每次點擊觸發一次 increment
這個 mutation
。可以發現,觸發方式很簡單,只需要調用 store
自帶的 commit
方法,其中參數為需要觸發的 mutation
的名稱。
// HelloWorld.vue
<template>
<div class="hello">
<div>{{$store.state.count}}</div>
<button @click="$store.commit('increment')">修改count</button>
</div>
</template>
點擊頁面中的按鈕,你會發現,頁面中的兩個 count
都同時增加了1,說明我們成功實現了 state
的動態修改。
Action
action
類似於 mutation
,也相當於一種自定義事件。只不過, action
操作的是 mutation
而不是 state
。
添加 action
的方法類似,在選項對象中新增 action
屬性即可。與 mutation
的參數不同, action
的參數就是當前創建的 Vue.store
對象實例的上下文,一般將其命名為 context
。我們需要使用其自帶的 commit
方法來觸發 mutation
。
下麵我通過實際的例子來嘗試下 action
:
首先,修改選項對象,使得新添加的 action
可以觸發之前的 mutation
:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment ({ commit }) {
commit('increment')
}
}
})
由於我們一般來說僅僅需要 context
中的 commit
方法,所以可以採用解構的方式,直接調用 commit
方法,而不需要以 context.commit
的方式使用它。
接下來,只需要修改 HelloWorld.vue
,使其能夠在點擊按鈕時觸發即可。
action
的觸發方式和 mutation
類似,只不過調用的方法是 dispatch
。
// HelloWorld.vue
<template>
<div class="hello">
<div>{{$store.state.count}}</div>
<button @click="$store.dispatch('increment')">修改count</button>
</div>
</template>
點擊頁面按鈕,你會發現,實現了和之前相同的效果。
總結
學會了 vuex
三賤客: state
, mutation
, action
,我們再回過頭看看前面的那張關係圖,此時應該很容易理解了吧?
組件交互觸發 action
, 在 action
中進行非同步操作(可選)並觸發 mutation
, mutation
控制 state
的變動, state
修改之後,觸發響應式,重新渲染組件。
彩蛋
在官方文檔中,提到需要將非同步操作放入 action
中,而不能放在 mutation
中。實際上,在 mutation
中也可以進行非同步操作,而且也不會導致什麼奇怪的事情。
但是,既然官方文檔中這麼說了,我們在實際開發中,一般還是老老實實地將所有非同步操作放在 action
中。(猥瑣保平安 -_-)
另外,說了這麼多,其實在一些項目中,可以使用 provide/inject
代替 vuex
。具體的用法在此不再贅述,就當一個課後作業吧。
在項目中靈活使用 provide/inject
,有時可以起到出乎意料的作用哦。
最後的最後
篇幅有限,所以該篇文章只講述了 vuex
三賤客的基本用法,其他的進階用法,如: getter
, module
, 簡寫以及 vuex
項目結構優化,甚至 vuex
源碼解析將會在之後的文章一一講解。
如果您覺得這片文章不錯的話,不如給我的 gayhub 點個star再走唄。
這個項目個人認為對很多新手在實際開發中使用 vuex
還是很有啟發和幫助的(手動狗頭)。
歡迎交流,謝謝~