這一講是 Vuex 基礎篇的最後一講,也是最爲複雜的一講。若是按照官方來的話,對於新手可能有點難以接受,因此想了下,決定乾脆多花點時間,用一個簡單的例子來說解,順便也複習一下以前的知識點。html
首先仍是得先了解下 Module 的背景。咱們知道,Vuex 使用的是單一狀態樹,應用的全部狀態會集中到一個對象中。若是項目比較大,那麼相應的狀態數據確定就會更多,這樣的話,store 對象就會變得至關的臃腫,很是難管理。前端
這就比如一家公司只有老闆一我的來管理同樣。若是小公司倒還好,公司要是稍微大一點,那就麻煩了。這個時候,老闆就會成立各大部門,並給各大部門安排一個主管,把管理的任務分派下去,而後有什麼事情須要處理的話,只須要跟這幾個主管溝通,由主管再把任務分配下去就好了,這就大大提升了工做效率,也減輕了老闆的負擔。vue
那麼一樣的道理,Module 其實就承擔了部門管理員的角色,而 store 就是老闆。理解了這一層,那麼後面就好辦多了,接下來,我們就一步一步動起手來開始實踐。vue-router
1、準備工做 這裏咱們使用官方提供的 vue-cli 來建一個項目「vuex-test」。固然,先得安裝 vue-cli:vuex
npm install -g @vue/cli
# OR
yarn global add @vue/cli
複製代碼
安裝完成後,就可使用如下命令來建立項目了:vue-cli
vue create vuex-test
複製代碼
還可使用圖形化界面來建立:npm
vue ui
複製代碼
具體關於 vue-cli 的使用方法能夠去官方查看,戳此進入 。bash
項目建立完成之後,找到項目安裝的目錄,並打開控制檯執行:架構
// 先定位到項目的目錄下
cd vuex-test
// 而後安裝 vuex
npm install vuex --save
// 運行一下
npm run serve
複製代碼
運行完成,能夠打開 http://localhost:8080/ 看下效果。app
最後你們找一個本身比較比較喜歡的 IDE 來打開這個項目,方便查看和編輯。我我的比較喜歡用 WebStore,這裏也推薦給你們。
這裏,咱們只看 src
目錄,其餘的暫時無論。組件 components
不在這一講的範圍內,因此也能夠忽視,資源 assets
也沒什麼可說的,就是若是有圖片或者視頻什麼的話,都放在這個文件夾裏面就是了。
咱們打開 App.vue
文件,去掉組件的相關代碼,並編寫一點簡單的 vue 代碼。修改以下:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<h1>{{name}}</h1>
<button @click="modifyNameAction">修更名字</button>
</div>
</template>
<script>
export default {
data() {
return {
name: 'Lucy'
}
},
methods: {
modifyNameAction() {
this.name = "bighone"
}
}
}
</script>
複製代碼
如今咱們引入 Vuex ,用它來管理狀態數據,好比這裏的 name
。首先在 src
中新建一個 store.js
文件,並寫下以下熟悉的代碼:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
name: 'Lucy',
},
mutations: {
setName(state, newName) {
state.name = newName;
}
},
actions: {
modifyName({commit}, newName) {
commit('setName', newName);
}
}
});
複製代碼
而後,在 main.js
中導入 store
,並全局注入:
import store from './store';
// ...
new Vue({
store,
render: h => h(App),
}).$mount('#app')
複製代碼
最後修改 App.vue
中的代碼以下:
<script>
import {mapState, mapActions} from 'vuex';
export default {
computed: {
...mapState(['name'])
},
methods: {
...mapActions(['modifyName']),
modifyNameAction() {
this.modifyName('bighone');
}
},
}
</script>
複製代碼
想必弄懂這些代碼,應該都是沒啥問題的,由於這些都是 Vuex 很基礎的知識點,這裏實操來簡單回顧一下,加深印象。若是看不懂,那證實以前的基礎知識還沒掌握。
在前言裏面,咱們已經了 Module 的基本職責,那麼具體如何使用呢?
Vuex 容許咱們將 store 分割成大大小小的對象,每一個對象也都擁有本身的 state、getter、mutation、action,這個對象咱們把它叫作 module(模塊),在模塊中還能夠繼續嵌套子模塊、子子模塊 ……
如今在 src
裏面建個文件夾,命名爲 module
,而後再裏面新建一個 moduleA.js
文件,並編寫以下代碼:
export default {
state: {
text: 'moduleA'
},
getters: {},
mutations: {},
actions: {}
}
複製代碼
如上,再建一個 moduleB.js
文件,這裏就不重複了。
而後打開 store.js
文件,導入這兩個 module
:
import moduleA from './module/moduleA';
import moduleB from './module/moduleB';
export default new Vuex.Store({
modules: {
moduleA, moduleB,
},
// ...
}
複製代碼
這個時候,store 中已經注入了兩個子模塊 moduleA
moduleB
,咱們能夠在 App.vue
中經過 this.$store.state.moduleA.text
這種方式來直接訪問模塊中的 state
數據。以下修改:
// ...
computed: {
...mapState({
name: state => state.moduleA.text
}),
},
// ...
複製代碼
由此可知,模塊內部的 state 是局部的,只屬於模塊自己全部,因此外部必須經過對應的模塊名進行訪問。
可是注意了:
模塊內部的 action、mutation 和 getter 默承認是註冊在全局命名空間的,這樣使得多個模塊可以對同一 mutation 或 action 做出響應。
這裏以 mutation 的響應爲例,給 moduleA 和 moduleB 分別新增一個 mutations,以下:
mutations: {
setText(state) {
state.text = 'A'
}
},
複製代碼
moduleB 和上面同樣,把文本名稱修改一下便可,這裏就不重複了。而後回到 App.vue
中,修改以下:
<script>
import {mapState, mapMutations} from 'vuex';
export default {
computed: {
...mapState({
name: state => (state.moduleA.text + '和' + state.moduleB.text)
}),
},
methods: {
...mapMutations(['setText']),
modifyNameAction() {
this.setText();
}
},
}
</script>
複製代碼
運行而後點擊修改,咱們會發現模塊 A 和 B 中的 text
值都改變了。固然,action 的用法如出一轍,你們也能夠試試。
若是模塊之間的數據有交集的話,那麼咱們其實就能夠經過這種方式,來同步更新模塊之間的數據,雖然看起來很是的方便,可是用的時候可必定要謹慎,這種處理方式一旦沒用好,遇到錯誤,排查起來仍是比較有難度的。
咱們已經知曉,模塊內部的 state 是局部的,只屬於模塊自己全部。那麼若是咱們要想在模塊中訪問 store 根節點的數據 state,怎麼辦呢?
很簡單,咱們能夠在模塊內部的 getter 和 action 中,經過 rootState 這個參數來獲取。接下來,咱們給 modelA.js
文件添加一點代碼。
export default {
// ...
getters: {
// 注意:rootState必須是第三個參數
detail(state, getters, rootState) {
return state.text + '-' + rootState.name;
}
},
actions: {
callAction({state, rootState}) {
alert(state.text + '-' + rootState.name);
}
}
}
複製代碼
而後修改 App.vue
:
<script>
import {mapActions, mapGetters} from 'vuex';
export default {
computed: {
...mapGetters({
name: 'detail'
}),
},
methods: {
...mapActions(['callAction']),
modifyNameAction() {
this.callAction();
}
},
}
</script>
複製代碼
而後運行你會發現,根節點的數據已經被咱們獲取到了。這裏須要注意的是在 getters 中,rootState 是以第三個參數暴露出來的,另外,還有第四個參數 rootGetters,用來得到根節點的 getters 信息,這裏就不演示了,感興趣本身能夠去嘗試。惟一要強調的就是千萬不要弄錯參數的位置了。
固然,action 中也能接收到 rootGetters,可是在 action 中,因爲它接收過來的數據都被包在 context
對象中的,因此解包出來沒有什麼順序的限制。
前面咱們已經知道了,模塊內部的 action、mutation 和 getter 默認是註冊在全局命名空間的。若是咱們只想讓他們在當前的模塊中生效,應該怎麼辦呢?
經過添加 namespaced: true
的方式使其成爲帶命名空間的模塊。當模塊被註冊後,它的全部 getter、action 及 mutation 都會自動根據模塊註冊的路徑調整命名。
咱們在 moduleA.js
中添加 namespaced: true
。
export default {
namespaced: true,
// ...
}
複製代碼
這個時候再去運行代碼,你會發現以下錯誤:
[vuex] unknown getter: detail
在全局 getter 中已經找不到 detail
的這個方法了,由於它的路勁已經改變了,再也不屬於全局,僅僅只屬於 moduleA 了。因此,這個時候,若是咱們想要訪問它,必須帶上路勁才行。修改 App.vue
以下:
<script>
import {mapActions, mapGetters} from 'vuex';
export default {
computed: {
...mapGetters({
name: 'moduleA/detail'
}),
},
methods: {
...mapActions({
call: 'moduleA/callAction'
}),
modifyNameAction() {
this.call();
}
},
}
</script>
複製代碼
注意,若是一個模塊啓用了命名空間,那麼它裏面的 getter 和 action 中收到的 getter,dispatch 和 commit 也都是局部化的,不須要在同一模塊內額外添加空間名前綴。也就是說,更改 namespaced
屬性後不須要修改模塊內的任何代碼。
那麼咱們如何在帶命名空間的模塊內訪問全局內容呢?
經過前面的學習,咱們已經瞭解到:
若是你但願使用全局 state 和 getter,rootState 和 rootGetter 會做爲第三和第四參數傳入 getter,也會經過 context 對象的屬性傳入 action。
如今若是想要在全局命名空間內分發 action 或提交 mutation 的話,那麼咱們只須要將 將 { root: true }
做爲第三參數傳給 dispatch 或 commit 便可。
export default {
namespaced: true,
// ...
actions: {
callAction({state, commit, rootState}) {
commit('setName', '改變', {root: true});
alert(state.text + '-' + rootState.name);
}
}
}
複製代碼
接下來看看如何在帶命名空間的模塊內註冊全局 action。
若須要在帶命名空間的模塊註冊全局 action,你可添加 root: true,並將這個 action 的定義放在函數 handler 中。
寫法稍微有點變化,咱們來看看,修改 moduleA.js
,以下:
export default {
namespaced: true,
// ...
actions: {
callAction: {
root: true,
handler (namespacedContext, payload) {
let {state, commit} = namespacedContext;
commit('setText');
alert(state.text);
}
}
}
}
複製代碼
簡單解釋下,這裏的 namespacedContext
就至關於當前模塊的上下文對象,payload
是調用的時候所傳入的參數,固然也叫載荷。
示例就講到這裏,接下來看看帶命名空間的綁定函數。
關於 mapState
, mapGetters
, mapActions
和 mapMutations
這些函數如何來綁定帶命名空間的模塊,上面示例代碼中其實已經都寫過了,這裏再看看另外幾種更簡便的寫法,先看看以前的寫法。
這裏就用官方的示例代碼舉例說明:
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
},
methods: {
...mapActions([
// -> this['some/nested/module/foo']()
'some/nested/module/foo',
// -> this['some/nested/module/bar']()
'some/nested/module/bar'
])
}
複製代碼
更優雅的寫法:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
複製代碼
將模塊的空間名稱字符串做爲第一個參數傳遞給上述函數,這樣全部綁定都會自動將該模塊做爲上下文。
咱們還能夠經過使用
createNamespacedHelpers
建立基於某個命名空間輔助函數。它返回一個對象,對象裏有新的綁定在給定命名空間值上的組件綁定輔助函數:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo',
'bar'
])
}
}
複製代碼
這一章節,官網講得比較清楚,因此直接搬過來了。
在 store 建立以後,可使用
store.registerModule
方法動態的註冊模塊:
// 註冊模塊 `myModule`
store.registerModule('myModule', {
// ...
})
// 註冊嵌套模塊 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})
複製代碼
以後就能夠經過
store.state.myModule
和store.state.nested.myModule
訪問模塊的狀態。模塊動態註冊功能使得其餘 Vue 插件能夠經過在 store 中附加新模塊的方式來使用 Vuex 管理狀態。例如,
vuex-router-sync
插件就是經過動態註冊模塊將 vue-router 和 vuex 結合在一塊兒,實現應用的路由狀態管理。你也可使用
store.unregisterModule(moduleName)
來動態卸載模塊。注意,你不能使用此方法卸載靜態模塊(即建立 store 時聲明的模塊)。
在註冊一個新 module 時,你頗有可能想保留過去的 state,例如從一個服務端渲染的應用保留 state。你能夠經過
preserveState
選項將其歸檔:store.registerModule('a', module, { preserveState: true })
。
就一點,重用會致使模塊中的數據 state 被污染,因此和 Vue 中的 data 同樣,也使用一個函數來申明 state 便可。
const MyReusableModule = {
state () {
return {
foo: 'bar'
}
},
//...
}
複製代碼
演示代碼寫的沒啥邏輯,還請見諒,主要仍是爲了幫助你們更好的理解 Module 的用法,若是有不理解的地方,歡迎留言告知。
那麼到這裏,Vuex 的核心概念就已經所有講完了。不知道你們掌握的如何,雖然這套框架有點抽象,但其實只要咱們真的用心去學了,要弄懂它也是很容易的。不過光看懂仍是不夠,必定要在項目中多運用,多實操纔可以掌握的更加牢固。
轉載說明:
做者:大宏說 連接:www.jianshu.com/p/83d5677b0…
以上就是胡哥今天給你們分享的內容,喜歡的小夥伴記得點贊
、收藏
呀,關注胡哥有話說,學習前端不迷路,歡迎多多留言交流...
胡哥有話說,一個有技術,有情懷的胡哥!現任京東前端攻城獅一枚。 胡哥有話說,專一於大前端技術領域,分享前端系統架構,框架實現原理,最新最高效的技術實踐!