選單快捷導航
- Vuex是什么東東,有什么應用場景?localStorage和sessionStorage能否替代它?
- Vuex知識點State、Getter、Mutaion、Action
- Vuex模塊化(Module)
1、Vuex概念和應用場景
首先,Vuex是什么,官網介紹說Vuex 是一個專為 Vue.js 應用程式開發的狀態管理模式,我的理解就是Vuex就是類似于sessionStorage這樣管理資料(本地存和取)的一種技術方案,
既然vuex類似于sessionStorage,那為何我們還要學習vuex,直接用sessionStorage和localStorage不就好了?這個問得好,我來描述一種場景:多個視圖(view)組件都要用到某一條資料(狀態),當這條資料發生變化的時候,依賴于該資料(狀態)的相關視圖(view)都要跟著即時更新,這種場景在作業中非常常見,我說一個自己碰到的例子,以前有一個react專案,其中有個功能是在pc頁面自定義小程式頁面,然后整個PC頁面有三個組件組成,在三個組件中還有其他的很多子組件,然后一開始的做法就是通過事件和組件間傳值來進行整個頁面資料同步更新,后面隨著組件越來越多,功能越來越復雜,麻煩和問題也就越來越多,然后每一個后面來接手的同事看代碼都要看好一陣,長痛不如短痛...
對的,在作業中這種常見的多個組件依賴于同一條資料(狀態),需要即時回應更新的情況,vuex的價值就體現出來了,這種情況下,vuex相比其他實作手段,就要簡單干脆方便多了!先看一個小例子,看看vuex和localStorage、sessionStorage的區別,上圖:

如圖,vuexPageA頁面中參考了三個組件,每個組件都分別從localStorage、sessionStorage、vuex中取了一個值,點擊按鈕加1的時候,vuex的值是及時更新了,其他需要重繪才能更新,總結一下:
- localStorage存盤的值能夠永久的存盤在瀏覽器上,不管是重新打開新視窗還是重啟,同一個瀏覽器上的相同域名下,localStorage的值一直在,
- sessionStorage存盤的值依賴于當前視窗(當前會話), 只要當前視窗不關閉,它存盤的資料就一直在,一旦關閉視窗或者打開新視窗,sessionStorage之前存盤的資料就會消失,
- 相比localStorage和sessionStorage,vuex存盤的資料可以即時更新到,當前專案下的所有參考了該資料的組件,但是如果重繪頁面的話,vuex存盤的值會重置,而localStorage和sessionStorage存盤的值不會重置,
相關代碼見:https://github.com/xiaotanit/tan_vue/blob/master/src/views/vuex/VuexPageA.vue
2、Vuex知識點State、Getter、Mutaion、Action
2.1 Vuex之State和mapState
每一個Vuex應用的核心就是store(倉庫),“store"基本上就是一個容器,Vuex使用單一狀態樹,相當于用一個物件(store)就包含了全部的應用層級狀態,也就是說每個應用也只包含一個store實體,因此Vuex的使用從new一個Vuex.Store實體(store實體)開始,store實體中的State屬性就是用來存放Vue應用的所有的狀態,先來看要給最簡單的包含State屬性的store實體:
import Vuex from 'vuex' import Vue from 'vue' Vue.use(Vuex) export default new Vuex.Store({ state: { count: 0, }, })
后面的mutations、getters、actions再慢慢往里面加入代碼,
store實體創建,如何應用?Vue實體創建時,提供了一個store選項,可以讓Vuex通過store選項,將store實體物件從根組件”注入“到每一個子組件中:
import Vue from 'vue' import App from './App.vue' import router from './router.js' //vuex 之 store實體物件 import store from './api/store/index' new Vue({ router, store, // 把 store 物件提供給 “store” 選項,這可以把 store 的實體注入所有的子組件 render: h => h(App) }).$mount('#app')
store實體注入根組件后,應用中的每個組件中通過this.$store指的就是該store實體物件,那么現在如何在Vue組件中展示store中的state狀態(資料)呢?由于Vuex的狀態存盤是即時回應的,從store實體中讀取狀態最簡單的方法就是在Vue組件中”計算屬性“computed中回傳某個狀態,每當store.state中某個狀態變化的時候,都會重新求取計算屬性,并且觸發更新相關聯的DOM,
mapState是一個輔助函式,當我們應用中一個組件需要獲取store中多個狀態的時候,使用mapState輔助函式可以幫助我們更加方便生成計算屬性,看看下面的應用測驗代碼:
import { mapState } from 'vuex';
export default {
data(){
return {
localCount: 88
}
},
mounted(){
console.log("...store物件:", this.$store);
},
computed:{
localStorage_count(){
return localStorage.getItem('localStorage_count')
},
//使用物件展開符"...",可以將物件目標物件混入到外部物件中
...mapState({
sessionStorage_count(){
return sessionStorage.getItem('sessionStorage_count')
},
vuex_count: state => state.count, //箭頭函式可以使代碼更簡練
vuex_count_alias: 'count', //傳字串引數'count'等同于 state => state.count
// 為了能夠使用 `this` 獲取區域狀態,必須使用常規函式
countPlusLocalState (state) {
return state.count + this.localCount
}
}),
},
}
View Code
2.2 Vuex之Getter和mapGetters
有時我們需要從store中的state種派生出一些狀態,比如對store中的某一個狀態(資料)進行篩選過濾,然后特別是當有多個組件需要用到這種狀態(資料)時,“getter"就出場了!Vuex允許我們在store中定義”getter"(可以認為是store物件的計算屬性),就像計算屬性一樣,getter的回傳值會根據它的依賴被快取起來,且只有當它的依賴值發生了改變才會被重新計算,Getter接受state作為其第一個引數:
export default new Vuex.Store({ state: { count: 0, todos: [ { id: 1, text: '金戈鐵馬,氣吞萬里如虎', done: true }, { id: 2, text: '老驥伏櫪,志在千里', done: false }, { id: 3, text: '周公吐哺,天下歸心', done: true }, { id: 4, text: '但使龍城飛將在,不教胡馬度陰山', done: false }, ] }, //Vuex允許我們再store中定義"getter"(可以認為是store的計算屬性), // 就像計算屬性一樣,getter的回傳值會根據它的依賴被快取起來,且只有當它的依賴值發生了改變才會被重新計算, getters: { doneTodos: state => { console.log('...state.getters.donwTodos...') return state.todos.filter(todo => todo.done) }, //Getter也可以接受其他getter作為第二個引數 //getter在通過屬性訪問時是作為Vue的回應式系統的一部分快取其中的 doneTodosCount: (state, getters) => { console.log('...state.getters.doneTodosLength...', getters.doneTodos) return getters.doneTodos.length; }, //通過方法訪問:通過讓getter回傳一個函式,來實作給getter傳參, //getter在通過方法訪問時,每次都會去進行呼叫,而不會快取結果, getTodoById: (state) => (id) => { console.log('...state.getters.getTodoById...: ', id); return state.todos.find(todo => todo.id === id); } }, })View Code
Getter應用:Getter會暴露為 store.getters 物件,然后在組件中,我們可以通過this.$store.getters來得到getter,getter里面的屬性,可以回傳屬性,也可以回傳方法,如果getter通過屬性訪問時是作為Vue的回應式系統的一部分快取,首次呼叫后再次呼叫時就會呼叫快取,只有該屬性的依賴值變化時,再次呼叫該屬性才會重新呼叫重新快取,如果getter通過方法訪問時,每次都會去進行呼叫,而不會快取結果,組件中應用測驗代碼:
methods:{ //state.getters呼叫 stateGettersProperty(){ //getters屬性呼叫, 屬性呼叫會被快取 console.log(this.$store.getters.doneTodos); console.log(this.$store.getters.doneTodosCount); }, stateGettersMethod(){ //方法呼叫,每次都會去進行呼叫,而不會快取結果, console.log(this.$store.getters.getTodoById(2).text); console.log(this.$store.getters.getTodoById(3).text); }, addTodo(){ //增加資料 let count = this.$store.state.todos.length; let obj = { id: count + 1, text: (count+1) + '***' + (count+1), done: count % 2 } this.$store.commit('addTodos', obj); }, }View Code
mapGetters也是一個輔助函式,可以將store物件中的getter映射到區域計算屬性:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用物件展開運算子將 getter 混入 computed 物件中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
View Code
如果你想將一個 getter 屬性另取一個名字,使用物件形式:
mapGetters({ // 把 `this.doneCount` 映射為 `this.$store.getters.doneTodosCount` doneCount: 'doneTodosCount' })View Code
2.3 Vuex之Mutation和mapMutations
上面說的mapState、getters、mapGetters都是對store物件中的狀態(state)進行應用,如果想更改Vuex的store物件中的狀態(state),必須要用mutation,Vuex中的mutation非常類似于事件:每個mutation都有一個字串的事件型別(type)和一個回呼函式(handler) ,這個回呼函式就是我們實際進行狀態更改的地方,并且它會接受state作為第一個引數:
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 變更狀態 state.count++ } } })View Code
mutation里面handler呼叫通過store.commit來呼叫,呼叫方式有“載荷(payload)"和“物件風格”兩種方式:
- 載荷提交方式:
// ... mutations: { increment (state, n) { state.count += n } }
store.commit('increment', 10) - 物件風格提交方式:
mutations: { increment (state, payload) { state.count += payload.amount } }
store.commit({ type: 'increment', amount: 10 })
一條重要的原則:mutation必須是同步函式,在組件中使用this.$store.commit('***')提交mutation,或者使用mapMutations輔助函式將組件中的methods映射為store.commit呼叫,
2.4 Vuex之Action和mapActions
Action類似于mutation,但是Action提交的是mutation,不能直接變更狀態;另外Action可以包含任意異步操作,在組件中使用this.$store.dispatch('***')呼叫action,或者使用mapActions輔助函式將組件中的methods映射為store.dispatch呼叫,
State、Getter、Mutation、Action的一些應用測驗代碼見:https://github.com/xiaotanit/tan_vue/blob/master/src/views/vuex/VuexPageB.vue
3、Vuex之模塊化(Module)
由于使用單一狀態樹,應用的所有狀態(資料)會集中到一個比較大的物件,當應用變得非常復雜時,store物件就有可能變得相當臃腫,為了解決這種問題,Vuex允許我們將store分隔成模塊(module),每個模塊都有自己的state、mutation、action、getter、甚至是嵌套子模塊,
默認情況下,模塊內容的action、mutation和getter是注冊在全域命名空間的,這樣使得多個模塊能夠對同一mutation或action作出回應,因此為了讓模塊具有更高的封裝度和復用性,我們可以在每個子模塊中添加namespaced: true屬性,這樣表示該模塊成為了帶命名空間的模塊,這樣后面再呼叫該模塊的getter、action和mutation時需要帶上該模塊名稱+呼叫的屬性或方法,下面寫一個示例代碼:
新建三個js檔案moduleA.js、moduleB.js、moduleStore.js,其中moduleA和moduleB分別為子模塊,
moduleA.js:
const state = { countA: 99 } const mutations = { increment(state){ state.countA++ }, decrement(state){ state.countA-- } } const getters = { doubleCount(state){ return state.countA * 2 } } const actions = { add({ commit }){ setTimeout(function(){ commit('increment') }, 50) }, minus({ commit }){ setTimeout(()=>{ commit('decrement') }, 500) } } export default { namespaced: true, //表示設定命名空間 state, mutations, getters, actions }View Code
moduleB.js:
const state = { countB: 11 } const mutations = { increment(state){ state.countB++; }, decrement(state){ state.countB--; } } //getters類似state里面屬性的計算屬性 const getters = { doubleCount(state){ return state.countB * 2; } } const actions = { add({ commit }){ commit('increment') }, minus({ commit }){ commit('decrement') } } export default { namespaced: true, state, getters, mutations, actions }View Code
moduleStore.js:
/* * 當專案大了后,為了責任清晰,目標明確,更易管理,將store拆成多個module形式 * */ import moduleCountA from './moduleA' import moduleCountB from './moduleB' import vuex from 'vuex' import vue from 'vue' vue.use(vuex) export default new vuex.Store({ modules: { moduleCountA, moduleCountB } })
再新建一個VuexPageC.vue頁面,測驗呼叫,js代碼如下:
import { mapGetters, mapActions, mapMutations } from 'vuex'
export default {
computed:{
...mapGetters({
doubleCountA: 'moduleCountA/doubleCount',
doubleConunB: 'moduleCountB/doubleCount'
})
},
methods: {
...mapActions({
//moduleA模塊的actions
addCountA: 'moduleCountA/add',
minusCountA: 'moduleCountA/minus',
//moduleB模塊的actions
addCountB: 'moduleCountB/add',
minusCountB: 'moduleCountB/minus'
}),
...mapMutations({
//moduleA模塊的mutions
incrementA: 'moduleCountA/increment',
decrementA: 'moduleCountA/decrement',
//moduleB模塊的mutions
incrementB: 'moduleCountB/increment',
decrementB: 'moduleCountB/decrement'
}),
}
}
頁面效果如圖:
完整VuexPageC.vue頁面代碼見:https://github.com/xiaotanit/tan_vue/blob/master/src/views/vuex/VuexPageC.vue
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/147688.html
標籤:JavaScript
上一篇:JavaScript之DOM基礎
