這篇文章主要介紹了vuex的使用及持久化state的方式詳解,現在分享給大家,也給大家做個參考。
Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式。它採用集中式儲存管理應用的所有元件的狀態,並以相應的規則保證狀態以一種可預測的方式變更。
當我們接觸vuex的時候,這是我們最先看到的一句官方引導。
從這句話中,我們可以得到以下幾個資訊:
1、vuex是一個為vue而存在的特化的Flux,如資料庫中的弱實體一樣,離開了vue,vuex就用不了。反之可以看到redux就不存在,無論是vue或react,redux都可以使用。所以這裡體現的vuex的“特性”,redux則具備“普適性”
2、集中式的管理說明vue中所有的組件的狀態都是存在於vuex中
3.使用vuex你就要遵循我的規則,這樣組件中狀態的變化我才能追蹤的到。
上圖是我在這篇文章中,vue整體專案骨架的局部。
vuex使用的是單一的狀態樹,我們的vue應用程式將只包含一個 store 的實例。所以當我們將store掛載到vue的實例上以後,我們可以透過this.$store取到vuex裡面的各個部分。
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這個檔案中,我們會去定義我們需要在vuex中儲存的狀態初始值。
比如說,我在上面的state物件中去儲存了一個isLoading屬性,該屬性我準備用它來標識我請求backend API的時候顯示,在請求完成後消失的這樣一個loading的效果,來緩解一下用戶的等待心理。
一般來說,我們在專案中最常用的就是mutation.js裡面的方法了。因為更改vuex中的store裡的state的唯一的方式就是提交mutation。
在vuex中,每個mutation都有一個字串的事件類型(mutation-type)和一個回呼函數(handler)。
這個回呼函數可接受兩個參數,第一個參數為state,第二參數是mutation的載重(payload)。
//... mutations: { /** * @method:只传入state,修改loading状态 * @param {bool} isLoading:loading状态 */ changeLoading(state) { state.isLoading = !state.isLoading } } store.commit('changeLoading') mutations: { /** * @method:传入state和payload,修改loading状态 * @param {bool} isLoading:loading状态 */ changeLoading(state,payload) { state.isLoading = payload.isLoading } } store.commit('changeLoading',{isLoading: true})
還有一種提交mutation的方式是直接使用包含 type 屬性的對象,不過我不是很推薦這樣的方式,因為用上面的方式來處理的話,程式碼的易讀性會更高。
store.commit({ type: 'changeLoading', isLoading: true })
在需要多人協作的專案中,我們可以使用常數來取代mutation 事件類型。這在各種 Flux 實作中是很常見的模式。同時把這些常數放在單獨的文件中可以讓協作開發變得清晰。
// 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 }, }
對於定義mutation-type裡面的事件類型,我大致遵循我自己定義的如下規範:
1、因為mutation類似事件,所以以動詞開頭
2、單字間以下劃線進行連接
3、儲存到vuex裡面的狀態用RECORD標識
4、快取到本地的狀態用SAVE標識
當然,這個規範的話大家可以自己定義,只要能透過mutation-type就能知道mutation的意圖就是極好的。
有時候我們需要從store 中的state 衍生出一些狀態,例如我上面提到的需要在非同步請求的時候去顯示一個有遮罩層的loading,然後我loading的下面需要依照state去展示loading的狀態。在不使用getter的情況下,我們會選擇使用計算屬性去處理。
computed: { loadingTxt () { return this.$store.state.isLoading ? '加载中' : '已完成'; } }
但是,我們這個loading需要在很多的元件中去使用它。那麼,我們要么複製這個函數,要么抽取到一個共享函數然後在多個地方導入它——無論哪種方式都不是很理想。
如果使用Getter,那麼一切都變得美好了。
//getter.js export default { loadingTxt:(state) =>{ return state.isLoading ? '加载中' : '已完成'; } };
就像計算屬性一樣,getter 的回傳值會根據它的依賴被快取起來,並且只有當它的依賴值發生了改變才會被重新計算。
並且,Getter 也可以接受其他getter 作為第二個參數:
export default { loadingTxt:(state) =>{ return state.isLoading ? '加载中' : '已完成'; }, isLoading:(state,getters) => { return 'string' === typeof getters.loadingTxt ? true : false; } };
透過mapGetters輔助函數可以將store 中的getter 對應到局部計算屬性
//组件中 import { mapGetters } from 'vuex' export default { data(){ return { //... } }, computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'loadingTxt', 'isLoading', // ... ]) } }
action的功能和mutation是類似的,都是去變更store裡的state,不過action和mutation有兩點不同:
#1、action主要處理的是非同步的操作,mutation必須同步執行,而action就不受這樣的限制,也就是說action中我們既可以處理同步,也可以處理非同步的操作
2、action改變狀態,最後是透過提交mutation
就拿購物車來說,當我們去添加一個商品的時候,我們需要先和後台去通訊一次,這裡涉及到sku或者說是如果用戶只添加了但是沒有去下單。
如果後台加入成功,前端再去展示新加入的商品,如果失敗,我們需要告訴使用者新增失敗了。
const actions = { checkout ({ state, commit, //rootState }, products) { const savedCartItems = [...state.added] commit(SET_CHECKOUT_STATUS, null) // 置空购物车 commit(SET_CART_ITEMS, { items: [] }) shop.buyProducts( products, //成功 () => commit(SET_CHECKOUT_STATUS, 'successful'), //失败 () => { commit(SET_CHECKOUT_STATUS, 'failed') commit(SET_CART_ITEMS, { items: savedCartItems }) } ) } }
当我们的项目足够大的时候,单一的状态树这个时候就会显得很臃肿了。因为需要用vuex进行状态管理的状态全部集中在一个state对象里面。
所以,当一个东西大了以后,我们就要想办法进行分割,同样的道理,我们熟知的分冶法和分布式其实也是基于这样的一个思想在里面。而vuex提供了module,我们就可以去横向的分割我们的store。
比如说,我在项目中需要去做一个购物车这样的东西,这在电商的项目中也是常见的需求。
//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,会暴露到全局 * @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,会暴露到全局 * @param {Object} rootState:全局(根)state */ const actions = { checkout ({ state, commit, //rootState }, products) { const savedCartItems = [...state.added] commit(SET_CHECKOUT_STATUS, null) // 置空购物车 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中,后三个都会暴露到全局的store中去,这样使得多个模块能够对同一 mutation 或 action 作出响应。就不需要在其他的模块中去定义相同的mutation或action了。
而这里的state是局部的。这也导致后来的持久化无法去处理用module分割后的state。
如同上面的module =》shopCart,
当我们无论是在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 }
如果说,我就想让我的module里面的定义的全部都是独享的。我们可以使用module的命名空间,通过设置namespaced: true。
//test.js const getters = { // 在这个模块的 getter 中,`getters` 被局部化了 // 你可以使用 getter 的第四个参数来调用 `rootGetters` someGetter (state, getters, rootState, rootGetters) { getters.someOtherGetter // 'test/someOtherGetter' rootGetters.someOtherGetter // 'someOtherGetter' }, someOtherGetter: state => { ... } }; const actions = { // 在这个模块中, dispatch 和 commit 也被局部化了 // 他们可以接受 `root` 属性以访问根 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 }
用过vuex的肯定会有这样一个痛点,就是刷新以后vuex里面存储的state就会被浏览器释放掉,因为我们的state都是存储在内存中的。
而像登录状态这样的东西,你不可能一刷新就让用户重新去登录吧!所以,我们会去选择把状态存储到本地。
这样一来问题貌似是解决了,但是当我们需要使用的时候,我们就需要不断的从本地,通过getStore这样的方法去取得我们state。如果需要更新的话,我们又要在mutation里面通过setStore这样的方法去处理它。
虽然,我们的setStore都是在操作了state以后再去调用的,也就是说无论是通过vuex的logger或者vue的dev tool我们都是可以对local里面的状态进行跟踪的,但是,我们无法保证我们每次都记着去写setStore。
这样一来,在共享state的组件中,我们的代码可能就会是这样的。
import { getStore } from '@/util' //组件中 mounted(){ this.foo = getStore('foo'); this.bar = getStore('bar'); //.... }
那么,如何去改进呢?
我们能想到的就是,能不能让state不是保存在内存中,而是存储在本地。
而vuex-persistedstate做了这样的事情,它帮我们将store里面的state映射到了本地环境中。这样一来,我通过提交mutation改变的state,会动态的去更新local里面对应的值。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在vue 2.x 中使用axios如何封装的get 和post方法
使用node应用中timing-attack存在哪些安全漏洞
以上是使用vuex以及持久化的詳細內容。更多資訊請關注PHP中文網其他相關文章!