Vuex 筆記 ??

參考來源:

  1. 官方文檔

Vex 是什么?

vuex 是一個專為 vue 應(yīng)用程序開發(fā)的 狀態(tài)管理模式。保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。

Vuex 作用一覽:

vuex作用圖.png

簡單的例子??:

// Vue.use(Vuex)
const Store = new Vuex.Store({
    state: {
        count: 0,
    },
    mutations: {
        increment (state) {
            state.count ++
        }
    }
})
// 使用
store.commit('increment');
console.log(store.state.count); // -> 1

約定通過提交 mutation 的方式,而非直接改變 store.state.count,更加明確追蹤到狀態(tài)的變化。

一個 Vuex 的 Store 分為四個部分:

  1. State
  2. Getter
  3. Mutation
  4. Action

State(單一狀態(tài)樹)

在vue中使用 State 狀態(tài)的最簡單的方法就是在計算屬性(computed)中返回某個狀態(tài)值:

const Counter = {
    template: `<div>{{ count }}</div>`,
    computed: {
        count() {
            return this.$store.state.count
        }
    }
}

上述過程中,每當 store 中的 count 狀態(tài)值發(fā)生變化,computed 屬性都能監(jiān)聽得到,并且觸發(fā)相關(guān)聯(lián)的 DOM的更新。

mapState 輔助函數(shù)

當一個組件需要獲取多個狀態(tài)值,將這些狀態(tài)都聲明在計算屬性中會顯得臃腫不堪,Vuex 提供了一個 mapState 函數(shù)簡化這一操作:

import { mapState } from 'vuex';
export default {
    //...
    computed: {
        otherComputed() {},
        // ...
        ...mapState({
            count: state => state.count,
            // 傳字符串參數(shù) 'count' 等同于 `state => state.count`
            countAlias: 'count',
            // 為了能夠使用 `this` 獲取局部狀態(tài),必須使用常規(guī)函數(shù)
            countPlusLocalState(state) {
                return state.count + this.localCount
            }
        })
    }
}

Getter

有時候我們需要從 Store 中的 State 派生出一些狀態(tài),例如對列表進行過濾并記數(shù):

computed: {
    doneTodosCount() {
        return this.$store.store.todos.filter(todo => todo.done).length
    }
}

如果有多個組件需要用這個屬性,那么就需要在每個組件中都寫這么長的表達式,難免有些尷尬??。

這時候 Getter 就派上了用處(可以認為是 Store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據(jù)它的依賴被緩存起來,依賴變了,返回值也隨之改變。

const store = new Vuex.store({
    state: {
        todos: [
            { id: 1, text: '...', done: true},
            { id: 1, text: '...', done: false},
        ],
    },
    getters: {
        doneTodos: state => state.todo.filter(todo => todo.done)
    }
})

在組件中通過使用 store.getters 對象,可以以屬性的形式方為這些值:

store.getters.doneTodos
// -> [{ id: 1, text: '...', done: true }]

Getter也可以接受其他 getter 作為第二個參數(shù):

getters: {
    // ...
    doneTodosCount: (state, getters) => getters.doneTodos.length,
}
// 使用
store.getters.doneTodosCount // -> 1

通過方法訪問

可以對 getter 的返回值做些手腳:

getters: {
    // ...
    getTodosById: (state) => (id) => {
        return state.todos.find(todo => todo.id === id)
    }
}
// 使用
store.getters.getTodoById(2)
// -> { id: 2, text: '...', done: false }

使 getter 返回一個函數(shù)時,可以適應(yīng)許多常用場景。getter 在使用 方法訪問時,每次都會進行調(diào)用而不會緩存結(jié)果。

mapGetters 輔助函數(shù)

類似于 mapState ,vuex 也提供了 mapGetters 方便使用:

import { mapGetters } from 'vuex'
export default {
    //...
    computed: {
        // 使用對象展開運算符將 getter 混入 computed對象中
        ...mapGetters([
            'doneTodosCount',
            'anotherGetter',
            //...
        ])
    }
}

Mutation

更改 Vuex 的 store 的狀態(tài)的唯一方法就是提交 mutation。每個 mutation 都有字符串的 事件類型(type) 和一個 回調(diào)函數(shù)(handler)

const store = new Vuex.store({
    state: {
        count: 1,
    },
    mutation: {
        icrement (state) {
            state.count ++ 
        },
        decrement (state, { delta }) {
            state.count -= delta
        } 
    }
});

// 使用
store.commit('icrement');
store.commit('decrement', { delta: 10 });
// 對象風格的提交方式
store.commit({
    type: 'decrement',
    delta: 10,
})

Mutation 需遵守 Vue 的響應(yīng)規(guī)則:

  1. 提前初始化 store 中所有的屬性。

  2. 當需要在對象上添加新屬性時

    • 使用 Vue.set(obj, 'newProp', 123) 或者

    • 以新對象替換老對象,例如:

      state.obj = { ...state.obj, newProp: 123 }
      

使用常量替代 Mutation 事件類型

使用常量替代 mutation 事件類型在各種 Flux 實現(xiàn)中是很常見的模式。這樣可以使 linter 之類的工具發(fā)揮作用,同時把這些常量放在單獨的文件中可以讓你的代碼合作者對整個 app 包含的 mutation 一目了然

// mutation-types.js
export const SOME_MUTATION = 'SOME_' 

使用類型常量:

// store.js
import Vuex from 'vuex';
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.store({
    state: { /* ... */ },
    mutations: {
        // 使用 ES6 風格的常量作為函數(shù)名。
        [SOME_MUTATION] (state) {
            // mutate state
        }
    }
})

Mutation 必須是同步函數(shù) ??

任何回調(diào)函數(shù)中進行的狀態(tài)的改變都是不可追蹤的!在 mutation 中混合異步調(diào)用會導(dǎo)致你的程序很難調(diào)試。

在組件中提交 Mutation

  1. 在組件中使用 this.$store.commit('xxx') 提交 mutation;

  2. 使用 mapMutation 輔助函數(shù)將 mutation 方法混入組件的 methods 中:

    import { mapMutatioins } from 'Vuex';
    export default {
        // other props
        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')`
            })
        }
    }
    

Action

Action 類似于 Mutation,不同于:

  1. Action 提交的是 mutation,而不是直接變更狀態(tài);
  2. Action 可以包含任意異步操作;
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    },
    // 參數(shù)結(jié)構(gòu):
    decrement ({ commit }) {
      commit('decrement')
    }
  }
});

Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象,因此可以調(diào)用 context.commit 方法提交一個 Mutation,或者使用 context.statecontext.getters 獲取 state 和 getters。

??context 對象并不是 store 實例本身

分發(fā) Action

store.dispatch('increment');

與 Mutation 不一樣的是,在 Action 內(nèi)部是可以執(zhí)行 異步 操作:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

在組件中分發(fā) Action

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`

      // `mapActions` 也支持載荷:
      'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`
    })
  }
}

組合 Action

store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch 仍舊返回 Promise:

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  },
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}
// 使用:
store.dispatch('actionB').then(() => {
  // ...
})

以及比較復(fù)雜的使用:

// 假設(shè) getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

規(guī)則建議:

  1. 應(yīng)用層級的狀態(tài)應(yīng)該集中到單個 store 對象中。
  2. 提交 mutation 是更改狀態(tài)的唯一方法,這個過程是同步的。
  3. 異步邏輯都應(yīng)該封裝到 action 中。

相關(guān)面試題

?? 什么時候用 Vuex

  1. 多個組件依賴于同一狀態(tài)。
  2. 來自不同組件行為需要更改同一狀態(tài)。

?? Vuex 中狀態(tài)是對象時,使用時要注意什么?

對象時引用類型,復(fù)制后改變屬性還是會影響原始數(shù)據(jù),所以在賦值前,先試用DeepClone

?? 怎么在組件中批量使用Vuex的state狀態(tài)?

使用mapState輔助函數(shù), 利用對象展開運算符將state混入computed對象中

import {mapState} from 'vuex'
export default{
    computed:{
        ...mapState(['price','number'])
    }
}

?? Vuex中要從state派生一些狀態(tài)出來,且多個組件使用它,該怎么做?

使用 getter 屬性:

const store = new Vuex.store({
    state: {
        price: 10,
        number: 10,
        discount: 0.7,
    },
    getters: {
        total: state => state.price * state.number,
        discountTotal: (state, getters) => state.discount * getters.total,
    },
})

面試問題待收錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容