vuex筆記

675635708 7年前發布 | 9K 次閱讀 Vuex Vue.js開發

前言:在使用vue開發的時候數據一版通過事件分發的方式進行,在vue1.x中組件中數據交互主要包含有:

  1. 子組件事件派發:$emit(向父級),$dispatch(沿著父級向上冒泡)

  2. 父組件通過$on監聽到之后進行相應的操作

  3. 當有兄弟組件需要監聽事件,父組件通過$broadcast向下廣播。

vue2.x中取消了$dispatch,$broadcast,要實現組件之前的交互就非常蛋疼了,首先要不停的通過$emit派發到需要獲取到這個事件的父級,然后父級通過$ref來調用相應的子組件的方法,單想想就容易心態爆炸。解決辦法有2:

  • 在根目錄下data里新建一個vue對象作為eventHander,所有的事件通過這個新建的vue對象進行監聽派發,具體就不進行描述了送上關鍵字:this.$root.eventHander.$on、this.$root.eventHander.$emit;

  • 全局狀態管理工具vuex,什么是vuex?借助官網的一句話:采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。;

vuex簡介

借助一張圖片,簡短的對vuex的狀態管理進行一個描述

  • 從上圖可以看出vuex主要包含actions、mutations、state

  • 交互過程中,渲染:vue components根據state進行渲染,響應:通過觸發actions相關事件調用mutations相關函數,mutations對state進行改變,state的改變引發vue components的重繪

狀態的統一管理對大型項目提供了相當多的便利,數據的操作都可以通過vuex來進行,小型項目組件并不復雜,數據的交互層級不高,因此并不建議使用vuex進行數據交互

在對vuex進行描述之前送上vuex官網 飛機票 一張

核心概念之state

state里存儲所有應用層級的信息。是作為唯一的一個數據源存在.

state定義

const store = new Vuex.Store({
        state: {
            count: 0
        }
    })
    //上面的例子就定義了一個state,state存有一個變量count

state獲取

//網頁直接通過script標簽引入的方式不需要進行注冊,模塊化構建一定要進行Vue.use(Vuex)組件注冊

    //...state定義
    //ps:注意,state相關數據是寫在computed內,不是寫在data內
    new Vue({
            el: '#app',
            template: `<div class="app">{{count}}</div>`
            computed: {
                count() {
                    return this.$store.state.count
                }
            },
            store
    })

    //首先將store注入到組件內部,調用可直接通過this.$store.state獲取相應的值

    //當數組需要獲取多個狀態值時this.$store.state前綴就需要寫的很多,容易冗余,利用mapState輔助函數可以很好的解決這個問題

    import { mapState } from 'vuex'
    //在直接標簽引入的情況下用mapState = vuex.mapState;
    computed: mapState({
        count: state => state.count,
        countAlias: 'count',
        // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數,因為箭頭函數會進行this綁定
        countPlusLocalState (state) {
            return state.count + this.localCount
        }
    })
    //當映射的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字符串數組
    computed: mapState([
        // 映射 this.count 為 store.state.count
        'count'
    ])

    //當需要與當前組件屬性共同使用的時候,可采用es6/7的對象展開符

    computed: {
        localComputed () { /* ... */ },
        // 使用對象展開運算符將此對象混入到外部對象中
        ...mapState({
            // ...
        })
    }

核心概念之mutations

更改store中的state的唯一方法就是通過mutation,每個mutation都像是一個事件監聽,等待調用的觀察者。其由一個事件類型和一個回調函數構成,回調函數對state進行修改,事件類型名稱作為事件調用的名稱;

//聲明
    const store = new Vuex.Store({
        //...
        mutations: {
            increment (state) {
            // 變更狀態
            state.count++
            }
        }
    })
    //喚起
    store.commit('increment')

提交荷載

提交荷載可以理解為store.commit傳遞的額外參數

// ...
    mutations: {
        increment (state, n) {
            state.count += n
        }
    }

    store.commit('increment', 10)

    //在大多數情況下,載荷應該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀:

    // ...
    mutations: {
        increment (state, payload) {
            state.count += payload.amount
        }
    }
    store.commit('increment', {
        amount: 10
    })

    //對象風格的提交方式

    store.commit({
        type: 'increment',
        amount: 10
    })

相應規則

  1. 最好提前在你的 store 中初始化好所有所需屬性。

  2. 當需要在對象上添加新屬性時,你應該

    • 使用Vue.set(state.obj,'newProp','xxx')

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

使用常量代替mutation事件類型

這么做的好處是對state的操作接口一目了然,全部展現在mutation-types文件夾中,便于大項目的協作。當然這不是必須的。

// mutation-types.js
    export const SOME_MUTATION = 'SOME_MUTATION'
    // store.js
    import Vuex from 'vuex'
    import { SOME_MUTATION } from './mutation-types'

    const store = new Vuex.Store({
    state: { ... },
    mutations: {
        // 我們可以使用 ES2015 風格的計算屬性命名功能來使用一個常量作為函數名
        [SOME_MUTATION] (state) {
            // mutate state
            }
        }
    })

mutation必須是同步函數

因為異步函數會導致狀態的不確定性,造成運行和調試的出路,這里就不進行展開了。

組件的mapMutations

import { mapMutations } from 'vuex'

    export default {
        // ...
        methods: {
            ...mapMutations([
                //type1
                'increment' // 映射 this.increment() 為 this.$store.commit('increment')
            ]),
                //type2
            ...mapMutations({
                add: 'increment' // 映射 this.add() 為 this.$store.commit('increment')
            })
        }
    }

核心概念之actions

上一章我們有提到mutation是沒有異步操作的,因此異步操作需要用到action,注意action提交的mutation而不是直接處理state

const store = new Vuex.Store({
        //...
        actions: {
            //context與store對象具有相同的方法和參數,但。。不是store本身
            increment (context) {
                context.commit('increment')
            }
        },
        //也可以寫成
        actions: {
            increment ({ commit }) {
                commit('increment')
            }
        }
    })

分發 Action

store.dispatch('increment');

    //action同樣支持荷載方式,和mutation差不多

    // 以載荷形式分發
    store.dispatch('incrementAsync', {
        amount: 10
    })

    // 以對象形式分發
    store.dispatch({
        type: 'incrementAsync',
        amount: 10
    })

組件中分發 Action

import { mapActions } from 'vuex'

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

組合 Actions

因為action是異步的,因此組合多個action將是一個大問題,要解決這個問題這里引用了promise對象。通過dispath返回的promise對象進行操作,達成順序執行

//A進行promise對象返回
actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}
//通過dispatch后的返回值執行異步操作
store.dispatch('actionA').then(() => {
  // ...
})

//actions之間的互相調用

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}


//也可以通過async/await屬性組合(await只能在async函數內部運行)action:
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

核心概念之getter

有時候我們需要從 store 中的 state 中派生出一些狀態(即對state進行一些操作過后得到的狀態),例如對列表進行過濾并計數:

//常規做法,當其他component需要用到時代碼就會出現冗余
    computed: {
        doneTodosCount () {
            return this.$store.state.todos.filter(todo => todo.done).length
        }
    }
    //Vuex 允許我們在 store 中定義『getters』(可以認為是 store 的計算屬性)。Getters 接受 state 作為其第一個參數

    const store = new Vuex.Store({
        state: {
            todos: [
            { id: 1, text: '...', done: true },
            { id: 2, text: '...', done: false }
            ]
        },
        getters: {
            doneTodos: state => {
            return state.todos.filter(todo => todo.done)
            }
        }
    })
    //Getters 也可以接受其他 getters 作為第二個參數
    getters: {
        // ...
        doneTodosCount: (state, getters) => {
            return getters.doneTodos.length
        }
    }
    store.getters.doneTodosCount // -> 1
    //組件中使用
    computed: {
        doneTodosCount () {
            return this.$store.getters.doneTodosCount
        }
    }

mapGetters 輔助函數

和之前一樣的輔助映射函數一毛一樣

import { mapGetters } from 'vuex'

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

            mapGetters({
            // 映射 this.doneCount 為 store.getters.doneTodosCount
            doneCount: 'doneTodosCount'
            })
        }
    }

核心概念之module

使用單一狀態樹,導致應用的所有狀態集中到一個很大的對象。但是,當應用變得很大時,store 對象會變得臃腫不堪。

Vuex 允許我們將 store 分割到模塊(module)。每個模塊擁有自己的 state、mutation、action、getters、甚至是嵌套子模塊。

module定義

//狀態篇

    const moduleA = {
        state: { ... },
        mutations: { ... },
        actions: { ... },
        getters: { ... }
    }

    const moduleB = {
        state: { ... },
        mutations: { ... },
        actions: { ... }
    }

    const store = new Vuex.Store({
        modules: {
            a: moduleA,
            b: moduleB
        }
    })

    store.state.a // -> moduleA 的狀態
    store.state.b // -> moduleB 的狀態

module局部狀態

對于模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態。

//即此狀態為moduleA的state
    const moduleA = {
    state: { count: 0 },
    mutations: {
        increment (state) {
        // state 模塊的局部狀態
        state.count++
        }
    },
    //對于模塊內部的 getter,根節點狀態會作為第三個參數:
    getters: {
        doubleCount (state, getters, rootState) {
            return state.count * 2
            }
        }
    },

    // 同樣,對于模塊內部的 action,context.state 是局部狀態,根節點的狀態是 context.rootState這就是之前action的context不等于當前store對象的原因
    actions: {
        incrementIfOddOnRootSum ({ state, commit, rootState }) {
            if ((state.count + rootState.count) % 2 === 1) {
                commit('increment')
            }
        }
    }

命名空間

模塊內部的 action、mutation、和 getter 現在仍然注冊在全局命名空間——這樣保證了多個模塊能夠響應同一 mutation 或 action。你可以通過添加前綴或后綴的方式隔離各模塊,以避免名稱沖突。你也可能希望寫出一個可復用的模塊,其使用環境不可控。例如,我們想創建一個 todos 模塊:

// types.js

    // 定義 getter、action、和 mutation 的名稱為常量,以模塊名 `todos` 為前綴
    export const DONE_COUNT = 'todos/DONE_COUNT'
    export const FETCH_ALL = 'todos/FETCH_ALL'
    export const TOGGLE_DONE = 'todos/TOGGLE_DONE'
    // modules/todos.js
    import * as types from '../types'

    // 使用添加了前綴的名稱定義 getter、action 和 mutation
    const todosModule = {
    state: { todos: [] },

    getters: {
        [types.DONE_COUNT] (state) {
        // ...
        }
    },

    actions: {
        [types.FETCH_ALL] (context, payload) {
        // ...
        }
    },

    mutations: {
        [types.TOGGLE_DONE] (state, payload) {
        // ...
        }
    }
    }

 

來自:https://segmentfault.com/a/1190000009108259

 

 本文由用戶 675635708 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!