了解Vuex狀態管理模式的理解強化指南

  • 2019 年 11 月 22 日
  • 筆記

作者 | Jeskson

來源 | 達達前端小酒館

1

Vuex是什麼呢?它是Vue的狀態管理模式,在使用vue的時候,需要在vue中各個組件之間傳遞值是很痛苦的,在vue中我們可以使用vuex來保存我們需要管理的狀態值,值一旦被改變,所有引用該值的地方就會自動更新。是不是很方便,很好用呢?

vuex是專門為vue.js設計的狀態管理模式,集中式存儲和管理應用程式中所有組件的狀態,vuex也集成了vue的官方調式工具,一個vuex應用的核心是store,一個容器,store包含了應用中大部分狀態。

那麼我們在什麼時候應用vuex呢?vuex也不是隨便亂用的,小型簡單的應用就不那麼合適了,因為用了Vuex是繁瑣多餘的,更適合使用簡單的store模式;對於vuex更加適用於中大型單頁應用:多個視圖使用於同一狀態,不同視圖需要變更同一狀態。

傳參的方法對於多層嵌套的組件來說,是非常繁瑣的,並且對於兄弟組件間的狀態傳遞無能為力;採用父子組件直接引用或者通過事件來變更和同步狀態的多份拷貝,通常會導致無法維護的程式碼。

npm install vuex --save //yarn add vuex
import Vue from 'vue'  import Vuex from 'vuex'    Vue.use(Vuex)

在創建vuex實例的地方引入vue,vuex

import Vue from 'vue'//引入vue  import Vuex from 'vuex'//引入vuex    Vue.use(Vuex); //使用 vuex    import store from './store' //引入狀態管理 store

new一個Vuex.Store實例,並註冊state,mutations,actions,getters到Vuex.Store實例中:

import Vue from 'vue';  import Vuex from 'vuex'; // 引入vuex  import store from './store' // 註冊store    Vue.use(Vuex); // 使用vuex    export default new Vuex.Store({   state: {...},   mutations: {...},   actions: {...},   getters: {...}  })    // 當程式碼量大額時候寫個js文件即可  store  action.js  index.js  mutation.js
// 引入到store/index.js註冊到vuex實例中  import mutations from './mutations' // 引入mutations  import actions from './action' // 引入action  import Vue from 'vue' // 引入vue  import Vuex from 'vuex' // 引入vuex    Vue.use(Vuex);  // 創建state  const state = {   count: 0  };    export default new Vuex.Store({   state, // 註冊state   action, // 註冊actions   mutations // 註冊mutations  })

創建好vuex.store後,需要在入口文件main.js中引入store並註冊到vue實例中,這樣就可以在任何組件使用store了。

import Vue from 'vue'  import App from './App.vue'  import router from './router'  import store from './store' // 引入狀態管理store    Vue.config.productiontip = false  new Vue({   router,   store, // 註冊store   render: h => h(App)  }).$mount('#app')

在組件中使用,引入vuex中各屬性對應的輔助函數:

import {mapActions, mapState,mapGetters} from 'vuex'  //註冊 action 、 state 、getter

2

創建一個vue項目,輸入vue int webpack web,安裝vuex,命令:npm install vuex –save。

store,index.js

import Vue from 'vue' // 引入vue  import Vuex from 'vuex' // 引入vuex  // 使用vuex  Vue.use(Vuex);  // 創建Vuex實例  const store = new Vuex.store({  })  export default store // 導出store

main.js

import Vue from 'Vue'  import App from './App'  import router from './router'  import store from '.store'    Vue.config.productiontip = false    new Vue({   el: '#app',   store,   router,   components: {App},   ...  })

State,可以在頁面通過this.$store.state來獲取我們定義的數據:

import Vue from 'vue' // 引入vue  import Vuex from 'vuex' // 引入vuex  // 使用vuex  Vue.use(Vuex);    // 創建Vuex實例:  const store = new Vuex.Store({   state: {    count: 1   }  })  export default store // 導出store
{{this.$store.state.count}}

Getters相當於vue中的computed計算屬性,getter的返回值根據它的依賴被快取起來,且只有當它的依賴值發生改變時才會重新計算。

Getters可以用於監聽,state中的值的變化,返回計算後的結果。

{{this.$store.getSateCount}}
import Vue from 'vue'  import Vuex from 'vuex'  Vue.use(Vuex);  const store = new Vuex.Store({   state: {    count: 1;   },   getters: {    getStateCount: function(state){     return state.count+1;    }   }

Mutations

{{this.$store.state.count}}  <button @click="addFun">+</button>  <button @click="reductionFun">-</button>    methods: {   addFun() {    this.$store.commit("add");   },   reductionFun() {    this.$store.commit("reduction");   }  }

index.js

import Vue from 'vue'  import Vuex from 'vuex'  Vue.use(Vuex);  // 創建Vuex實例  const store = new Vuex.store({   state: {    count: 1   },   getters: {    getStateCount: function(state){     return state count+1;    }   },   mutations: {    add(state) {     state.count = state.count+1;    },    reduction(state){     state.count = state.count-1;    }   }  })  export default store // 導出store

Actions:

import Vue from 'vue'  import Vuex from 'vuex'  Vue.use(Vuex);  const store = new Vuex.Store({   state: {    count: 1;   },   getters: {    getStateCount: function(state){     return state.count+1;    }   }   mutations: {    add(state) {     state.count = state.count+1;    },    reduction(state) {     state.count = state.count-1;    }   },   actions: {    addFun(context) {     context.commit("add");    },    reductionFun(context) {     context.commit("reduction");    }   }
// vue  methods: {   addFun() {    this.$store.dispatch("addFun");    // this.$store.commit("add");   },   reductionFun() {    this.$store.dispatch("reductionFun");   }  }

傳值:

methods: {   addFun() {    this.$store.dispatch("addFun");    // this.$store.commit("add");   },   reductionFun() {    var n = 10;    this.$store.dispatch("reductionFun", n);   }  }
 mutations: {    add(state) {     state.count = state.count+1;    },    reduction(state,n) {     state.count = state.count-n;    }   },   actions: {    addFun(context) {     context.commit("add");    },    reductionFun(context,n) {     context.commit("reduction",n);    }   }

mapState、mapGetters、mapAction

this.$stroe.state.count
this.$store.dispatch('funName')
<div style="border:1px solid red; margin-top: 50px;">   {{count1}}  </div>    import {mapState,mapActions,mapGetters} from 'vuex';    computed: {   ...mapState({    count1:state=> state.count   })  }

3

state是最底層的初始數據,getters就相當於vue中的計算屬性,是對state數據進行處理和擴展的,mutations是當需要修改state時,定義的mutations,actions時當需要很多很多的mutations進行處理時,在actions進行mutations派發的,非同步處理也是在這裡定義的。

vuex時一個為vue.js應用程式開發的狀態管理模式,它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證以一種可預測的方式發生變化。

那麼狀態管理模式是怎樣的書寫格式:

new Vue({   // state 初始狀態(數據)   data() {    return {     count: 0    }   },   template: `<div>{{count}}</div>`,   methods: {    increment() {     this.count++    }   }  })

多個數組共享狀態時:

多個視圖依賴於同一狀態,來自不同視圖的行為需要變更同一狀態。

Vue.use(Vuex)    const store = new Vuex.Store({    state: {  //狀態      count: 0    },    mutations: {  //變化      increment (state) {        state.count++      }    }  })
store.commit('increment')

state初始狀態,getter相當於計算屬性,mutation狀態變更,action行動,module模組。

Vue.use(Vuex)    const app = new Vue({    el: '#app',    store,    components: { Counter },    template: `      <div class="app">        <counter></counter>      </div>    `  })
import { mapState } from 'vuex'    export default {    // ...    computed: mapState({      // 箭頭函數可使程式碼更簡練      count: state => state.count,        // 傳字元串參數 'count' 等同於 `state => state.count`      countAlias: 'count',        // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數      countPlusLocalState (state) {        return state.count + this.localCount      }    })  }
computed: mapState([    // 映射 this.count 為 store.state.count    'count'  ])

getter 的返回值會根據它的依賴被快取起來,且只有當它的依賴值發生了改變才會被重新計算。

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 => todos.done)      }    }    })
getters: {    // ...    doneTodosCount: (state, getters) => {      return getters.doneTodos.length    }  }    store.getters.doneTodosCount // -> 1

mapGetters 輔助函數是將 store 中的 getter 映射到局部計算屬性

更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation

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

this.$store.commit('xxx') 提交 mutation

mapMutations 輔助函數將組件中的 methods 映射為 store.commit 調用

import { mapMutations } from 'vuex'  export default {    methods: {        ...mapMutations([        'increment',        // 將 `this.increment()` 映射為 `this.$store.commit('increment')`        // `mapMutations` 也支援載荷:        'incrementBy'        // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`      ]),      this.incrementBy(); 調用        ...mapMutations({        add: 'increment'        // 將 `this.add()` 映射為 `this.$store.commit('increment')`      })      }    }

Action 提交的是 mutation變化,而不是直接變更狀態。Action 可以包含任意非同步操作。

store.dispatch('increment')

使用 mapActions 輔助函數將組件的 methods 映射為 store.dispatch 調用

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')`      })    }  }

安裝vue-cli cnpm install -g vue-cli

安裝webpack模板 : vue init webpack myProject

安裝依賴

cnpm install

安裝路由 cnpm install vue-router –save-dev

安裝 axios http cnpm install axios –save

4

vue和單純的全局對象區別:

vuex的狀態存儲時響應式的,改變store中的狀態的唯一途徑就是顯式地提交commit, mutation。

Vuex的核心是store,store包含著應用中大部分的狀態 (state)。

一個最簡單的store包含state與mutation,可以通過 store.state 來獲取狀態對象,以及通過 store.commit 方法觸髮狀態變更。

State,存儲著應用中的所有基礎「全局對象」,this.$store.state.XXX可訪問到。 mapState:使用此輔助函數幫助我們生成計算屬性,獲得多個state值。

Getter從 store 中的 state 中派生出一些狀態,接受 state 作為第一個參數,第二個參數可傳值計算,會暴露為 store.getters 對象,可以以屬性的形式訪問這些值。

Vuex 中的 mutation ,每個 mutation,事件類型 (type) 和 一個 回調函數 (handler)

Action 提交的是 mutation,不是直接變更狀態,可以包含任意非同步操作,通過 store.dispatch 方法觸發。

5

vuex的出現是為了解決哪些問題呢?我們知道在組件之間的作用域是獨立的父組件和子組件的通訊可以通過prop屬性來傳參,但是兄弟組件之間通訊就不那麼友好了。

首先要告訴它們的父組件,然後由父組件告訴其他組件,一旦組件很多很多的時候,通訊起來就不方便了,vuex解決了這個問題,讓多個子組件之間可以方便的通訊。

|-store/ // 存放vuex程式碼  | |-actions.js  | |-getters.js  | |-index.js  | |-mutations.js  | |-state.js
|-store/ // 存放vuex程式碼  | |-Module1  | | |-actions.js  | | |-getters.js  | | |-index.js  | | |-mutations.js  | | |-state.js  | |-Module2  | | |-actions.js  | | |-getters.js  | | |-index.js  | | |-mutations.js  | | |-state.js  | |-index.js // vuex的核心,創建一個store

vuex是什麼?

Vuex是一個專門為vue.js應用程式開發的狀態管理模式,它是採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex也集成到Vue的官方調式工具devtools extension,提供了諸如零配置的time-travel調試,狀態快照導入導出等高級調試功能。

什麼是「狀態管理模式」?

new Vue({      // state    data () {      return {        count: 0      }    },      // view    template: `      <div>{{ count }}</div>    `,      // actions    methods: {      increment () {        this.count++      }    }    })
  • state,驅動應用的數據源;
  • view,以聲明方式將 state 映射到視圖;
  • actions,響應在 view 上的用戶輸入導致的狀態變化。

我們的應用遇到多個組件共享狀態時,單向數據流的簡潔性很容易被破壞:

  • 多個視圖依賴於同一狀態。
  • 來自不同視圖的行為需要變更同一狀態。

核心概念:State,Getter,Action,Module

Vuex 和單純的全局對象有以下兩點不同:

1.Vuex 的狀態存儲是響應式的。

2.不能直接改變 store 中的狀態。

創建一個 store

// 如果在模組化構建系統中,請確保在開頭調用了 Vue.use(Vuex)    const store = new Vuex.Store({      state: {      count: 0    },      mutations: {      increment (state) {        state.count++      }    }    })

通過 store.state 來獲取狀態對象,通過 store.commit 方法觸髮狀態變更

store.commit('increment')    console.log(store.state.count) // -> 1

用一個對象包含了全部的應用層級狀態,每個應用將僅僅包含一個 store 實例。單一狀態樹。Vuex 的狀態存儲是響應式的,讀取狀態方法,即是在計算屬性中返回。

// 創建一個 Counter 組件    const Counter = {    template: `<div>{{ count }}</div>`,    computed: {      count () {        return store.state.count      }    }  }

Vuex 通過 store 選項

const app = new Vue({    el: '#app',    // 把 store 對象提供給 「store」 選項,這可以把 store 的實例注入所有的子組件    store,    components: { Counter },    template: `      <div class="app">        <counter></counter>      </div>    `  })      const Counter = {    template: `<div>{{ count }}</div>`,    computed: {      count () {        return this.$store.state.count      }    }  }

使用 mapState 輔助函數幫助我們生成計算屬性

// 在單獨構建的版本中輔助函數為 Vuex.mapState  import { mapState } from 'vuex'    export default {    // ...    computed: mapState({      // 箭頭函數可使程式碼更簡練      count: state => state.count,        // 傳字元串參數 'count' 等同於 `state => state.count`      countAlias: 'count',        // 為了能夠使用 `this` 獲取局部狀態,必須使用常規函數      countPlusLocalState (state) {        return state.count + this.localCount      }    })  }      computed: mapState([    // 映射 this.count 為 store.state.count    'count'  ]    mapState 函數返回的是一個對象。    computed: {    localComputed () { /* ... */ },    // 使用對象展開運算符將此對象混入到外部對象中    ...mapState({      // ...    })  }  

需要從 store 中的 state 中派生出一些狀態

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

getter 的返回值會根據它的依賴被快取起來,且只有當它的依賴值發生了改變才會被重新計算。

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)      }    }    })

Getter 會暴露為 store.getters 對象

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

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

getters: {    // ...    doneTodosCount: (state, getters) => {      return getters.doneTodos.length    }  }    store.getters.doneTodosCount // ->    computed: {    doneTodosCount () {      return this.$store.getters.doneTodosCount    }  }
getters: {    // ...    getTodoById: (state) => (id) => {      return state.todos.find(todo => todo.id === id)    }  }    store.getters.getTodoById(2)  // -> { id: 2, text: '...', done: false }

mapGetters 輔助函數是將 store 中的 getter 映射到局部計算屬性:

import { mapGetters } from 'vuex'    export default {    // ...    computed: {    // 使用對象展開運算符將 getter 混入 computed 對象中      ...mapGetters([        'doneTodosCount',        'anotherGetter',        // ...      ])    }  }
mapGetters({    // 把 `this.doneCount` 映射為 `this.$store.getters.doneTodosCount`    doneCount: 'doneTodosCount'  })

Vuex 的 store 中的狀態的唯一方法是提交 mutation

每個 mutation 都有一個字元串的 事件類型 (type) 和 一個 回調函數 (handler)

store.commit 傳入額外的參數,即 mutation 的 載荷(payload):

// ...  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  }    mutations: {    increment (state, payload) {      state.count += payload.amount    }  }

Action

簡單的 action:

const store = new Vuex.Store({    state: {      count: 0    },    mutations: {      increment (state) {        state.count++      }    },    actions: {      increment (context) {        context.commit('increment')      }    }  }    store.dispatch('increment')

store.dispatch 可以處理被觸發的 action 的處理函數返回的 Promise

actions: {    actionA ({ commit }) {      return new Promise((resolve, reject) => {        setTimeout(() => {          commit('someMutation')          resolve()        }, 1000)      })    }  }
store.dispatch('actionA').then(() => {    // ...  }    actions: {    // ...    actionB ({ dispatch, commit }) {      return dispatch('actionA').then(() => {        commit('someOtherMutation')      })    }  }    // 假設 getData() 和 getOtherData() 返回的是 Promise    actions: {    async actionA ({ commit }) {      commit('gotData', await getData())    },    async actionB ({ dispatch, commit }) {      await dispatch('actionA') // 等待 actionA 完成      commit('gotOtherData', await getOtherData())    }  }

文件:

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 的狀態
store.js 文件:    import Vue from 'vue'  import Vuex from 'vuex'    Vue.use(Vuex)    export default new Vuex.Store({    state: {      num: 1    },    mutations: {      changeFunction (state, num) {        state.num++      }    }  })    main.js 文件:    import Vue from 'vue'  import App from './App.vue'  import router from './router'  import store from './store'    Vue.config.productionTip = false    new Vue({    router,    store,    render: h => h(App)  }).$mount('#app')    views/demo.vue 文件:    <template>    <div>      <p>{{msg}}</p>      <button @click="getNum">getNum</button>    </div>  </template>    <script>  export default {    data () {      return {        msg: '0'      }    },    methods: {      getNum () {        this.msg = this.$store.state.num      }    }  }  </script>

想要獲得vuex里的全局數據,可以把vue看做一個類

模組化:

const moduleA = {    namespaced: true,    state: {      name: ''    },    getters: {},    mutations: {},    actions: {}  }  export default moduleA;
const moduleB = {    namespaced: true,    state: {      name: ''    },    getters: {},    mutations: {},    actions: {}  }  export default moduleB;
import moduleA from './moduleA.js';  import moduleB from './moduleB.js';  const store = new Vuex.Store({    modules: {      a: moduleA,      b: moduleB    }  })  export default store;
this.$store.state.a.name // -> moduleA 的狀態name  this.$store.state.b.name // -> moduleB 的狀態name
computed: {      ...mapState('a', {        name: state => state.name      }),      ...mapState('b', {        name: state => state.name      })  }
state示例    const state = {    name: 'weish',    age: 22  };    export default state;
getters.js 示例    export const name = (state) => {    return state.name;  }    export const age = (state) => {    return state.age  }    export const other = (state) => {    return `My name is ${state.name}, I am ${state.age}.`;  }

參考官方文檔:https://vuex.vuejs.org/