您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Vuex的含義以及作用是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
Vuex是用于對(duì)復(fù)雜應(yīng)用進(jìn)行狀態(tài)管理用的(官方說(shuō)法是它是一種狀態(tài)管理模式)。
“殺雞不用宰牛刀”。對(duì)于簡(jiǎn)單的項(xiàng)目,根本用不著Vuex這把“宰牛刀”。那簡(jiǎn)單的項(xiàng)目用什么呢?用Vue.js官方提供的“事件總線”就可以了。
我們使用Vuex的時(shí)候怎么用呢?通常都是這樣:
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); new Vuex.Store({ state: { //放置state的值 count: 0, str:"abcd234" }, getters: { //放置getters方法 strLen: state => state.str.length }, // mutations只能是同步操作 mutations: { //放置mutations方法 increment(state, payload) { //在這里改變state中的數(shù)據(jù) state.count = payload.number; } }, // actions可以是異步操作 actions: { //放置actions方法 actionName({ commit }) { //dosomething commit('mutationName') }, getSong ({commit}, id) { api.getMusicUrlResource(id).then(res => { let url = res.data.data[0].url; }) .catch((error) => { // 錯(cuò)誤處理 console.log(error); }); } } }); new Vue({ el: '#app', store, ... });
這里import進(jìn)來(lái)的Vuex是個(gè)什么東西呢?我們用console.log把它輸出一下:
console.log(Vuex)
通過(guò)輸出,我們發(fā)現(xiàn)其結(jié)構(gòu)如下:
可見(jiàn),import進(jìn)來(lái)的Vuex它實(shí)際上是一個(gè)對(duì)象,里面包含了Store這一構(gòu)造函數(shù),還有幾個(gè)mapActions、mapGetters、mapMutations、mapState這幾個(gè)輔助方法(后面再講)。
除此之外,還有一個(gè)install方法。我們發(fā)現(xiàn),import之后要對(duì)其進(jìn)行Vue.use(Vuex)的操作。根據(jù)這兩個(gè)線索,我們就明白了,Vuex本質(zhì)上就是一個(gè)Vue.js的插件。
Vuex 通過(guò) store 選項(xiàng),提供了一種機(jī)制將狀態(tài)從根組件“注入”到每一個(gè)子組件中(需調(diào)用 Vue.use(Vuex)):
const app = new Vue({ el: '#app', // 把 store 對(duì)象提供給 “store” 選項(xiàng),這可以把 store 的實(shí)例注入所有的子組件 store, components: { Counter }, template: ` <div class="app"> <counter></counter> </div> ` })
通過(guò)在根實(shí)例中注冊(cè) store 選項(xiàng),該 store實(shí)例會(huì)注入到根組件下的所有子組件中,且子組件能通過(guò) this.$store 訪問(wèn)到。
1. State
這個(gè)很好理解,就是狀態(tài)數(shù)據(jù)。Vuex所管理的就是狀態(tài),其它的如Actions、Mutations都是來(lái)輔助實(shí)現(xiàn)對(duì)狀態(tài)的管理的。Vue組件要有所變化,也是直接受到State的驅(qū)動(dòng)來(lái)變化的。
可以通過(guò)this.$store.state來(lái)直接獲取狀態(tài),也可以利用vuex提供的mapState輔助函數(shù)將state映射到計(jì)算屬性computed中去。
2. Getters
Getters本質(zhì)上是用來(lái)對(duì)狀態(tài)進(jìn)行加工處理。Getters與State的關(guān)系,就像Vue.js的computed與data的關(guān)系。 getter的返回值會(huì)根據(jù)它的依賴(lài)被緩存起來(lái),且只有當(dāng)它的依賴(lài)值發(fā)生了改變才會(huì)被重新計(jì)算。
可以通過(guò)this.$store.getters.valueName對(duì)派生出來(lái)的狀態(tài)進(jìn)行訪問(wèn)?;蛘咧苯邮褂幂o助函數(shù)mapGetters將其映射到本地計(jì)算屬性中去。
3. Mutations
更改 Vuex的 store中的狀態(tài)的唯一方法是提交 mutation。Vuex中的 mutation非常類(lèi)似于事件:每個(gè) mutation都有一個(gè)字符串的 事件類(lèi)型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方。
你不能直接調(diào)用一個(gè) mutation handler。這個(gè)選項(xiàng)更像是事件注冊(cè):“當(dāng)觸發(fā)一個(gè)類(lèi)型為 increment 的 mutation時(shí),調(diào)用此函數(shù)?!币獑拘岩粋€(gè)mutation handler,你需要以相應(yīng)的 type 調(diào)用store.commit方法,并且它會(huì)接受 state 作為第一個(gè)參數(shù),也可以向 store.commit 傳入額外的參數(shù),即 mutation 的 載荷(payload):
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 變更狀態(tài) state.count++ } } }) store.commit('increment')
// ... mutations: { increment (state, n) { state.count += n } } store.commit('increment', 10)
在大多數(shù)情況下,載荷應(yīng)該是一個(gè)對(duì)象,這樣可以包含多個(gè)字段并且記錄的 mutation 會(huì)更易讀:
// ... mutations: { increment (state, payload) { state.count += payload.amount } } store.commit('increment', { amount: 10 }) // ***或者以對(duì)象風(fēng)格提交*** mutations: { increment (state, payload) { state.count += payload.amount } } store.commit({ type: 'increment', amount: 10 })
既然 Vuex的 store中的狀態(tài)是響應(yīng)式的,那么當(dāng)我們變更狀態(tài)時(shí),監(jiān)視狀態(tài)的 Vue組件也會(huì)自動(dòng)更新。這也意味著 Vuex中的 mutation也需要與使用 Vue一樣遵守一些注意事項(xiàng):
最好提前在你的 store中初始化好所有所需屬性。
當(dāng)需要在對(duì)象上添加新屬性時(shí),你應(yīng)該:
使用 Vue.set(obj, 'newProp', 123), 或者
以新對(duì)象替換老對(duì)象。例如,利用對(duì)象展開(kāi)運(yùn)算符 我們可以這樣寫(xiě):
state.obj = { ...state.obj, newProp: 123 }
一條重要的原則就是要記住 mutation必須是同步函數(shù)。為什么?請(qǐng)參考下面的例子:
mutations: { someMutation (state) { api.callAsyncMethod(() => { state.count++ }) } }
現(xiàn)在想象,我們正在 debug一個(gè) app并且觀察 devtool中的 mutation日志。每一條 mutation被記錄,devtools都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中 mutation中的異步函數(shù)中的回調(diào)讓這不可能完成:因?yàn)楫?dāng) mutation觸發(fā)的時(shí)候,回調(diào)函數(shù)還沒(méi)有被調(diào)用,devtools不知道什么時(shí)候回調(diào)函數(shù)實(shí)際上被調(diào)用——實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。
除了這種使用 this.$store.commit('xxx') 提交 mutation的方式之外,還有一種方式,即使用 mapMutations 輔助函數(shù)將組件中的 methods映射為 this.$store.commit。例如:
import { mapMutations } from 'vuex' export default { // ... methods: { ...mapMutations([ 'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')` // `mapMutations` 也支持載荷: 'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')` }) } }
經(jīng)過(guò)這樣的映射之后,就可以通過(guò)調(diào)用方法的方式來(lái)觸發(fā)其對(duì)應(yīng)的(所映射到的)mutation commit了,比如,上例中調(diào)用add()方法,就相當(dāng)于執(zhí)行了this.$store.commit('increment')了。
4. Actions
Action類(lèi)似于 mutation,不同在于:
Action提交的是 mutation,而不是直接變更狀態(tài)。
Action可以包含任意異步操作。
Action函數(shù)接受一個(gè)與 store實(shí)例具有相同方法和屬性的 context對(duì)象,因此你可以調(diào)用 context.commit 提交一個(gè) mutation,或者通過(guò) context.state 和 context.getters 來(lái)獲取 state和 getters。
Action 通過(guò) store.dispatch 方法觸發(fā):
store.dispatch('increment')
Actions 支持同樣的載荷方式和對(duì)象方式進(jìn)行分發(fā):
// 以載荷形式分發(fā) store.dispatch('incrementAsync', { amount: 10 }) // 以對(duì)象形式分發(fā) store.dispatch({ type: 'incrementAsync', amount: 10 })
另外,你需要知道, this.$store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 this.$store.dispatch 仍舊返回 Promise。
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit('someMutation') resolve() }, 1000) }) } } store.dispatch('actionA').then(() => { // ... })
5. Module
Module是什么概念呢?它實(shí)際上是對(duì)于store的一種切割。由于Vuex使用的是單一狀態(tài)樹(shù),這樣整個(gè)應(yīng)用的所有狀態(tài)都會(huì)集中到一個(gè)比較大的對(duì)象上面,那么,當(dāng)應(yīng)用變得非常復(fù)雜時(shí),store對(duì)象就很可能變得相當(dāng)臃腫!Vuex允許我們將 store分割成一個(gè)個(gè)的模塊(module)。每個(gè)模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割。
對(duì)于每個(gè)模塊內(nèi)部的 mutation 和 getter,接收的第一個(gè)參數(shù)就是模塊的局部狀態(tài)對(duì)象,對(duì)于模塊內(nèi)部的 getter,根節(jié)點(diǎn)狀態(tài)會(huì)作為第三個(gè)參數(shù)暴露出來(lái)。同樣,對(duì)于模塊內(nèi)部的 action,局部狀態(tài)通過(guò) context.state 暴露出來(lái),根節(jié)點(diǎn)狀態(tài)則為 context.rootState:
const moduleA = { state: () => ({ count: 0 }), mutations: { increment (state) { // 這里的 `state` 對(duì)象是模塊的局部狀態(tài) state.count++ } }, getters: { doubleCount (state) { return state.count * 2 }, sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } }, actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } }
默認(rèn)情況下,模塊內(nèi)部的 action、mutation和 getter是注冊(cè)在全局命名空間的——這樣使得多個(gè)模塊能夠?qū)ν?mutation或 action作出響應(yīng)。
如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過(guò)添加 namespaced: true 的方式使其成為帶命名空間的模塊。當(dāng)模塊被注冊(cè)后,它的所有 getter、action及 mutation都會(huì)自動(dòng)根據(jù)模塊注冊(cè)的路徑調(diào)整命名。例如:
const store = new Vuex.Store({ modules: { account: { namespaced: true, // 模塊內(nèi)容(module assets) state: { ... }, // 模塊內(nèi)的狀態(tài)已經(jīng)是嵌套的了,使用 `namespaced` 屬性不會(huì)對(duì)其產(chǎn)生影響 getters: { isAdmin () { ... } // -> getters['account/isAdmin'] }, actions: { login () { ... } // -> dispatch('account/login') }, mutations: { login () { ... } // -> commit('account/login') }, // 嵌套模塊 modules: { // 繼承父模塊的命名空間 myPage: { state: { ... }, getters: { profile () { ... } // -> getters['account/profile'] } }, // 進(jìn)一步嵌套命名空間 posts: { namespaced: true, state: { ... }, getters: { popular () { ... } // -> getters['account/posts/popular'] } } } } } })
啟用了命名空間的 getter 和 action 會(huì)收到局部化的 getter,dispatch 和 commit。換言之,你在使用模塊內(nèi)容(module assets)時(shí)不需要在同一模塊內(nèi)額外添加空間名前綴。更改 namespaced 屬性后不需要修改模塊內(nèi)的代碼。
如果你希望使用全局 state和 getter,rootState 和 rootGetters 會(huì)作為第三和第四參數(shù)傳入 getter,也會(huì)通過(guò) context 對(duì)象的屬性傳入 action。
若需要在全局命名空間內(nèi)分發(fā) action或提交 mutation,將 { root: true } 作為第三參數(shù)傳給 dispatch 或 commit 即可。
modules: { foo: { namespaced: true, getters: { // 在這個(gè)模塊的 getter 中,`getters` 被局部化了 // 你可以使用 getter 的第四個(gè)參數(shù)來(lái)調(diào)用 `rootGetters` someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // -> 'foo/someOtherGetter' rootGetters.someOtherGetter // -> 'someOtherGetter' }, someOtherGetter: state => { ... } }, actions: { // 在這個(gè)模塊中, dispatch 和 commit 也被局部化了 // 他們可以接受 `root` 屬性以訪問(wèn)根 dispatch 或 commit someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter // -> 'foo/someGetter' rootGetters.someGetter // -> 'someGetter' dispatch('someOtherAction') // -> 'foo/someOtherAction' dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction' commit('someMutation') // -> 'foo/someMutation' commit('someMutation', null, { root: true }) // -> 'someMutation' }, someOtherAction (ctx, payload) { ... } } } }
若需要在帶命名空間的模塊注冊(cè)全局 action,你可添加 root: true,并將這個(gè) action 的定義放在函數(shù) handler 中。例如:
{ actions: { someOtherAction ({dispatch}) { dispatch('someAction') } }, modules: { foo: { namespaced: true, actions: { someAction: { root: true, handler (namespacedContext, payload) { ... } // -> 'someAction' } } } } }
當(dāng)使用 mapState, mapGetters, mapActions 和 mapMutations 這些函數(shù)來(lái)綁定帶命名空間的模塊時(shí),寫(xiě)起來(lái)可能比較繁瑣:
computed: { ...mapState({ a: state => state.some.nested.module.a, b: state => state.some.nested.module.b }) }, methods: { ...mapActions([ 'some/nested/module/foo', // -> this['some/nested/module/foo']() 'some/nested/module/bar' // -> this['some/nested/module/bar']() ]) }
對(duì)于這種情況,你可以將模塊的空間名稱(chēng)字符串作為第一個(gè)參數(shù)傳遞給上述函數(shù),這樣所有綁定都會(huì)自動(dòng)將該模塊作為上下文。于是上面的例子可以簡(jiǎn)化為:
computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', // -> this.foo() 'bar' // -> this.bar() ]) }
在 store創(chuàng)建之后,你可以使用 store.registerModule 方法注冊(cè)模塊:
import Vuex from 'vuex' const store = new Vuex.Store({ /* 選項(xiàng) */ }) // 注冊(cè)模塊 `myModule` store.registerModule('myModule', { // ... }) // 注冊(cè)嵌套模塊 `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... })
之后就可以通過(guò) store.state.myModule 和 store.state.nested.myModule 訪問(wèn)模塊的狀態(tài)。
模塊動(dòng)態(tài)注冊(cè)功能使得其他 Vue 插件可以通過(guò)在 store 中附加新模塊的方式來(lái)使用 Vuex 管理狀態(tài)。例如,vuex-router-sync插件就是通過(guò)動(dòng)態(tài)注冊(cè)模塊將vue-router 和 vuex結(jié)合在一起,實(shí)現(xiàn)應(yīng)用的路由狀態(tài)管理。
你也可以使用 store.unregisterModule(moduleName) 來(lái)動(dòng)態(tài)卸載模塊。注意,你不能使用此方法卸載靜態(tài)模塊(即創(chuàng)建 store時(shí)聲明的模塊)。
注意,你可以通過(guò) store.hasModule(moduleName) 方法檢查該模塊是否已經(jīng)被注冊(cè)到 store。
保留 state
在注冊(cè)一個(gè)新 module時(shí),你很有可能想保留過(guò)去的 state,例如從一個(gè)服務(wù)端渲染的應(yīng)用保留 state。你可以通過(guò) preserveState 選項(xiàng)將其歸檔:store.registerModule('a', module, { preserveState: true })。
當(dāng)你設(shè)置 preserveState: true 時(shí),該模塊會(huì)被注冊(cè),action、mutation和 getter會(huì)被添加到 store中,但是 state不會(huì)。這里假設(shè) store的 state已經(jīng)包含了這個(gè) module的 state并且你不希望將其覆寫(xiě)。
有時(shí)我們可能需要?jiǎng)?chuàng)建一個(gè)模塊的多個(gè)實(shí)例,例如:
創(chuàng)建多個(gè) store,他們共用同一個(gè)模塊 (例如當(dāng) runInNewContext 選項(xiàng)是 false 或 'once' 時(shí),為了在服務(wù)端渲染中避免有狀態(tài)的單例 (opens new window))
在一個(gè) store中多次注冊(cè)同一個(gè)模塊
如果我們使用一個(gè)純對(duì)象來(lái)聲明模塊的狀態(tài),那么這個(gè)狀態(tài)對(duì)象會(huì)通過(guò)引用被共享,導(dǎo)致?tīng)顟B(tài)對(duì)象被修改時(shí) store 或模塊間數(shù)據(jù)互相污染的問(wèn)題。
實(shí)際上這和 Vue組件內(nèi)的 data 是同樣的問(wèn)題。因此解決辦法也是相同的——使用一個(gè)函數(shù)來(lái)聲明模塊狀態(tài)(僅 2.3.0+ 支持):
const MyReusableModule = { state: () => ({ foo: 'bar' }), // mutation, action 和 getter 等等... }
當(dāng)在嚴(yán)格模式中使用 Vuex時(shí),在屬于 Vuex的 state上使用 v-model 會(huì)比較棘手:
<input v-model="obj.message">
假設(shè)這里的 obj 是在計(jì)算屬性中返回的一個(gè)屬于 Vuex store 的對(duì)象,在用戶(hù)輸入時(shí),v-model 會(huì)試圖直接修改 obj.message。在嚴(yán)格模式中,由于這個(gè)修改不是在 mutation 函數(shù)中執(zhí)行的, 這里會(huì)拋出一個(gè)錯(cuò)誤。
用“Vuex 的思維”去解決這個(gè)問(wèn)題的方法是:給 <input> 中綁定 value,然后偵聽(tīng) input 或者 change 事件,在事件回調(diào)中調(diào)用一個(gè)方法:
<input :value="message" @input="updateMessage"> computed: { ...mapState({ message: state => state.obj.message }) }, methods: { updateMessage (e) { this.$store.commit('updateMessage', e.target.value) } }
下面是 mutation函數(shù):
// ... mutations: { updateMessage (state, message) { state.obj.message = message } }
必須承認(rèn),這樣做比簡(jiǎn)單地使用“v-model + 局部狀態(tài)”要啰嗦得多,并且也損失了一些 v-model 中很有用的特性。另一個(gè)方法是使用帶有 setter 的雙向綁定計(jì)算屬性:
<input v-model="message"> computed: { message: { get () { return this.$store.state.obj.message }, set (value) { this.$store.commit('updateMessage', value) } } }
“Vuex的含義以及作用是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。