您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“如何搭建vuex”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“如何搭建vuex”這篇文章吧。
Vuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。
當(dāng)我們接觸vuex的時(shí)候,這是我們最先看到的一句官方引導(dǎo)。
從這句話中,我們可以得到如下幾個(gè)信息:
1、vuex是一個(gè)為vue而存在的特化的Flux,如同數(shù)據(jù)庫(kù)中的弱實(shí)體一樣,離開(kāi)了vue,vuex就用不了。反之可以看到redux就不存在,無(wú)論是vue還是react,redux都可以使用。所以這里體現(xiàn)的vuex的“特性”,redux則具備“普適性”
2、集中式的管理說(shuō)明vue中所有的組件的狀態(tài)都是存在于vuex中
3、使用vuex你就要遵循我的規(guī)則,這樣組件中狀態(tài)的變化我才能跟蹤的到。
上圖是我在這篇文章中,vue整體項(xiàng)目骨架的局部。
vuex使用的是單一的狀態(tài)樹(shù),我們的vue應(yīng)用將僅僅包含一個(gè) store 的實(shí)例。所以當(dāng)我們將store掛載到vue的實(shí)例上以后,我們可以通過(guò)this.$store取到vuex里面的各個(gè)部分。
import vue from 'vue' import vuex from 'vuex' import mutations from './mutation' import getters from './getter' vue.use(vuex) const state = { isLoading:false } export default new vuex.Store({ state, getters, mutations })
在index這個(gè)文件中,我們會(huì)去定義我們需要在vuex中存儲(chǔ)的狀態(tài)初始值。
比如說(shuō),我在上面的state對(duì)象中去存儲(chǔ)了一個(gè)isLoading屬性,該屬性我準(zhǔn)備用它來(lái)標(biāo)識(shí)我請(qǐng)求backend API的時(shí)候顯示,在請(qǐng)求完成后消失的這樣一個(gè)loading的效果,來(lái)緩解一下用戶的等待心理。
一般來(lái)說(shuō),我們?cè)陧?xiàng)目中最常用的就是mutation.js里面的方法了。因?yàn)楦膙uex中的store里的state的唯一的方式就是提交mutation。
在vuex中,每個(gè)mutation都有一個(gè)字符串的事件類(lèi)型(mutation-type)和一個(gè)回調(diào)函數(shù)(handler)。
這個(gè)回調(diào)函數(shù)可接受兩個(gè)參數(shù),第一個(gè)參數(shù)為state,第二參數(shù)是mutation的載荷(payload)。
//... mutations: { /** * @method:只傳入state,修改loading狀態(tài) * @param {bool} isLoading:loading狀態(tài) */ changeLoading(state) { state.isLoading = !state.isLoading } } store.commit('changeLoading') mutations: { /** * @method:傳入state和payload,修改loading狀態(tài) * @param {bool} isLoading:loading狀態(tài) */ changeLoading(state,payload) { state.isLoading = payload.isLoading } } store.commit('changeLoading',{isLoading: true})
還有一種提交mutation的方式是直接使用包含 type 屬性的對(duì)象,不過(guò)我不是很推薦這樣的方式,因?yàn)橛蒙厦娴姆绞絹?lái)處理的話,代碼的易讀性會(huì)更高。
store.commit({ type: 'changeLoading', isLoading: true })
在需要多人協(xié)作的項(xiàng)目中,我們可以使用常量代替mutation 事件類(lèi)型。這在各種 Flux 實(shí)現(xiàn)中是很常見(jiàn)的模式。同時(shí)把這些常量放在單獨(dú)的文件中可以讓協(xié)作開(kāi)發(fā)變得清晰。
// mutation-types.js export const CHANGE_LOADING= 'CHANGE_LOADING' // mutation.js import { CHANGE_LOADING} from './mutation-types' export default{ [CHANGE_LOADING](state,payload){ state.isLoading = payload.isLoading }, }
對(duì)于定義mutation-type里面的事件類(lèi)型,我大致遵循我自己定義的如下規(guī)范:
1、因?yàn)閙utation類(lèi)似于事件,所以以動(dòng)詞開(kāi)頭
2、單詞間以下劃線進(jìn)行連接
3、保存到vuex里面的狀態(tài)用RECORD標(biāo)識(shí)
4、緩存到本地的狀態(tài)用SAVE標(biāo)識(shí)
當(dāng)然,這個(gè)規(guī)范的話大家可以自己定義,只要能通過(guò)mutation-type就能知道m(xù)utation的意圖就是極好的。
有時(shí)候我們需要從 store 中的 state 中派生出一些狀態(tài),例如我上面提到的需要在異步請(qǐng)求的時(shí)候去顯示一個(gè)帶有遮罩層的loading,然后我loading的下面需要根據(jù)state去展示loading的狀態(tài)。在不使用getter的情況下,我們會(huì)選擇使用計(jì)算屬性去處理。
computed: { loadingTxt () { return this.$store.state.isLoading ? '加載中' : '已完成'; } }
但是,我們這個(gè)loading需要在很多的組件中去使用它。那么,我們要么復(fù)制這個(gè)函數(shù),要么抽取到一個(gè)共享函數(shù)然后在多處導(dǎo)入它——無(wú)論哪種方式都不是很理想。
如果使用Getter,那么一切都變得美好了。
//getter.js export default { loadingTxt:(state) =>{ return state.isLoading ? '加載中' : '已完成'; } };
就像計(jì)算屬性一樣,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來(lái),且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算。
并且,Getter 也可以接受其他 getter 作為第二個(gè)參數(shù):
export default { loadingTxt:(state) =>{ return state.isLoading ? '加載中' : '已完成'; }, isLoading:(state,getters) => { return 'string' === typeof getters.loadingTxt ? true : false; } };
通過(guò)mapGetters輔助函數(shù)可以將 store 中的 getter 映射到局部計(jì)算屬性
//組件中 import { mapGetters } from 'vuex' export default { data(){ return { //... } }, computed: { // 使用對(duì)象展開(kāi)運(yùn)算符將 getter 混入 computed 對(duì)象中 ...mapGetters([ 'loadingTxt', 'isLoading', // ... ]) } }
action的功能和mutation是類(lèi)似的,都是去變更store里的state,不過(guò)action和mutation有兩點(diǎn)不同:
1、action主要處理的是異步的操作,mutation必須同步執(zhí)行,而action就不受這樣的限制,也就是說(shuō)action中我們既可以處理同步,也可以處理異步的操作
2、action改變狀態(tài),最后是通過(guò)提交mutation
就拿購(gòu)物車(chē)來(lái)說(shuō),當(dāng)我們?nèi)ヌ砑右粋€(gè)商品的時(shí)候,我們需要先和后臺(tái)去通訊一次,這里涉及到sku或者說(shuō)是如果用戶只添加了但是沒(méi)有去下單。
如果后臺(tái)添加成功,前端再去展示新添加的商品,如果失敗,我們需要告訴用戶添加失敗了。
const actions = { checkout ({ state, commit, //rootState }, products) { const savedCartItems = [...state.added] commit(SET_CHECKOUT_STATUS, null) // 置空購(gòu)物車(chē) commit(SET_CART_ITEMS, { items: [] }) shop.buyProducts( products, //成功 () => commit(SET_CHECKOUT_STATUS, 'successful'), //失敗 () => { commit(SET_CHECKOUT_STATUS, 'failed') commit(SET_CART_ITEMS, { items: savedCartItems }) } ) } }
當(dāng)我們的項(xiàng)目足夠大的時(shí)候,單一的狀態(tài)樹(shù)這個(gè)時(shí)候就會(huì)顯得很臃腫了。因?yàn)樾枰胿uex進(jìn)行狀態(tài)管理的狀態(tài)全部集中在一個(gè)state對(duì)象里面。
所以,當(dāng)一個(gè)東西大了以后,我們就要想辦法進(jìn)行分割,同樣的道理,我們熟知的分冶法和分布式其實(shí)也是基于這樣的一個(gè)思想在里面。而vuex提供了module,我們就可以去橫向的分割我們的store。
比如說(shuō),我在項(xiàng)目中需要去做一個(gè)購(gòu)物車(chē)這樣的東西,這在電商的項(xiàng)目中也是常見(jiàn)的需求。
//shopCart.js import shop from '../../api/shop' import { ADD_TO_CART, SET_CART_ITEMS, SET_CHECKOUT_STATUS } from '../mutation-types' const state = { added: [], checkoutStatus: null } /** * module getters * @param {Object} state:模塊局部state * @param {Object} getters:模塊局部getters,會(huì)暴露到全局 * @param {Object} rootState:全局(根)state */ const getters = { checkoutStatus: state => state.checkoutStatus, cartProducts: (state, getters, rootState) => { return state.added.map(({ id, quantity }) => { const product = rootState.products.all.find(product => product.id === id) return { title: product.title, price: product.price, quantity } }) }, cartTotalPrice: (state, getters) => { return getters.cartProducts.reduce((total, product) => { return total + product.price * product.quantity }, 0) } } /** * module actions * @param {Object} state:模塊局部state * @param {Object} getters:模塊局部getters,會(huì)暴露到全局 * @param {Object} rootState:全局(根)state */ const actions = { checkout ({ state, commit, //rootState }, products) { const savedCartItems = [...state.added] commit(SET_CHECKOUT_STATUS, null) // 置空購(gòu)物車(chē) commit(SET_CART_ITEMS, { items: [] }) shop.buyProducts( products, //成功 () => commit(SET_CHECKOUT_STATUS, 'successful'), //失敗 () => { commit(SET_CHECKOUT_STATUS, 'failed') commit(SET_CART_ITEMS, { items: savedCartItems }) } ) } } /** * module mutations * @param {Object} state:模塊局部state * @param payload:mutation的載荷 */ const mutations = { //用id去查找商品是否已存在, [ADD_TO_CART] (state, { id }) { state.checkoutStatus = null const record = state.added.find(product => product.id === id) if (!record) { state.added.push({ id, quantity: 1 }) } else { record.quantity++ } }, [SET_CART_ITEMS] (state, { items }) { state.added = items }, [SET_CHECKOUT_STATUS] (state, status) { state.checkoutStatus = status } } export default { state, getters, actions, mutations }
在module的定義的局部state,getters,mutation,action中,后三個(gè)都會(huì)暴露到全局的store中去,這樣使得多個(gè)模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)。就不需要在其他的模塊中去定義相同的mutation或action了。
而這里的state是局部的。這也導(dǎo)致后來(lái)的持久化無(wú)法去處理用module分割后的state。
如同上面的module =》shopCart,
當(dāng)我們無(wú)論是在index.js里面或者其他的module中,shopCart里面的getters或者action或者mutations,我們都可以去使用。
//test.js const state = { isTest:false }; const getters = { isTest :state => state.isTest, checkTestStatus:(state,getters) => { return getters.checkoutStatus; } }; export default { state, getters, } //組件中 ...mapGetters([ 'checkTestStatus' ]) //... created(){ this.checkTestStatus ;//null }
如果說(shuō),我就想讓我的module里面的定義的全部都是獨(dú)享的。我們可以使用module的命名空間,通過(guò)設(shè)置namespaced: true。
//test.js const getters = { // 在這個(gè)模塊的 getter 中,`getters` 被局部化了 // 你可以使用 getter 的第四個(gè)參數(shù)來(lái)調(diào)用 `rootGetters` someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // 'test/someOtherGetter' rootGetters.someOtherGetter // 'someOtherGetter' }, someOtherGetter: state => { ... } }; const actions = { // 在這個(gè)模塊中, dispatch 和 commit 也被局部化了 // 他們可以接受 `root` 屬性以訪問(wèn)根 dispatch 或 commit someAction ({ dispatch, commit, getters, rootGetters }) { getters.someGetter // 'test/someGetter' rootGetters.someGetter // 'someGetter' dispatch('someOtherAction') // 'test/someOtherAction' dispatch('someOtherAction', null, { root: true }) // 'someOtherAction' commit('someMutation') // 'test/someMutation' commit('someMutation', null, { root: true }) // 'someMutation' }, someOtherAction ({ state,commit }, payload) { ... } } export default { namespaced: true, state, getters, actions, mutations }
用過(guò)vuex的肯定會(huì)有這樣一個(gè)痛點(diǎn),就是刷新以后vuex里面存儲(chǔ)的state就會(huì)被瀏覽器釋放掉,因?yàn)槲覀兊膕tate都是存儲(chǔ)在內(nèi)存中的。
而像登錄狀態(tài)這樣的東西,你不可能一刷新就讓用戶重新去登錄吧!所以,我們會(huì)去選擇把狀態(tài)存儲(chǔ)到本地。
這樣一來(lái)問(wèn)題貌似是解決了,但是當(dāng)我們需要使用的時(shí)候,我們就需要不斷的從本地,通過(guò)getStore這樣的方法去取得我們state。如果需要更新的話,我們又要在mutation里面通過(guò)setStore這樣的方法去處理它。
雖然,我們的setStore都是在操作了state以后再去調(diào)用的,也就是說(shuō)無(wú)論是通過(guò)vuex的logger或者vue的dev tool我們都是可以對(duì)local里面的狀態(tài)進(jìn)行跟蹤的,但是,我們無(wú)法保證我們每次都記著去寫(xiě)setStore。
這樣一來(lái),在共享state的組件中,我們的代碼可能就會(huì)是這樣的。
import { getStore } from '@/util' //組件中 mounted(){ this.foo = getStore('foo'); this.bar = getStore('bar'); //.... }
那么,如何去改進(jìn)呢?
我們能想到的就是,能不能讓state不是保存在內(nèi)存中,而是存儲(chǔ)在本地。
而vuex-persistedstate做了這樣的事情,它幫我們將store里面的state映射到了本地環(huán)境中。這樣一來(lái),我通過(guò)提交mutation改變的state,會(huì)動(dòng)態(tài)的去更新local里面對(duì)應(yīng)的值。
以上是“如何搭建vuex”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(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)容。