了解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/