【vue】Vuex状态管理模式

Vuex状态管理模式 多个组件依赖或修改同一个状态 - 共享
使用Vue.observable可以实现一个轻量型的状态管理

  1. 基础
  2. Modules
1 基础
  1. 基本结构与使用
  2. State
  3. Mutations
  4. Actions
  5. Gtters
  6. map
1.1 基本结构与使用
结构图
【vue】Vuex状态管理模式
文章图片

安装Vuex
npm install vuex --save

store.js文件
import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)export const store = new Vuex.Store({ mutations: { increment() { this.state.count++ }, decrement() { this.state.count-- } }, state: { count: 0 } })

组件一

//main.js import { store } from './store' new Vue({ store, render: h => h(App), }).$mount('#app') //组件二 dec() { this.$store.commit("decrement"); },

两个组件可以操作同一个值
  • 在store文件中引入Vue以及Vuex
  • Vue.use(Vuex),new Vuex.Store({})
  • 在组件中引入store并注册,可以直接使用store
  • 在根实例中引入并注册store,子组件可以通过vm.$store来访问与修改数据
1.1 State
存储状态
每个数据作为对象属性供各个组件访问
通常会使用计算属性让状态能在组件中使用,当状态变化时会重新计算
1.2 Mutations
更改Vuex的store中状态的唯一方法是提交mutation
  • mutation中的回调函数接收state作为第一个参数
    mutations: { increment (state) { state.count++ } }

  • 通过store.commit来声明使用哪个回调处理数据,可以传递额外的参数
    mutations:{ increment(state,n){ state.count +=n } } //提交 store.commit('increment',10)

    参数为一个对象
    mutations: { increment (state, payload) { state.count += payload.amount } } //提交 store.commit('increment',{amount:10})

  • 对象形式的提交
    //处理的回调与参数时对象的相同 mutations: { increment (state, payload) { state.count += payload.amount } } //整体提交一个对象 store.commit({type:'increment',amount:10})

  • 如果需要添加状态,可以使用Vue.set(obj,key,val),或者使用扩展符解构赋值
    //提交添加状态 addOne() { store.commit("addState", { name: "张三" }); }, //添加状态的回调 addState(state, obj) { const keys = Object.keys(obj) Vue.set(state, keys[0], obj[keys[0]]) }

Mutation必须是 同步函数,异步操作时不能判定是哪个操作在改变状态
1.3 Actions
类似于mutation,不同如下:
  • 提交的是mutation,不直接更改状态
  • 可以包含异步操作
store
actions: { getData(context, param) { const oneData = https://www.it610.com/article/[param,'数据1', '数据2'] context.commit('GETDATA', oneData) } }, mutations: { GETDATA(state, data) { Vue.set(state, 'myData', data) }, }, state: { myData: [], }

组件

  • Action的回调函数接收一个与store实例具有相同属性和方法的context对象作为第一个参数
    context !== this,context并不是store实例本身,mutation的state参数则是store实例上的真实的属性
    在action中主要使用contextcommit方法去提交mutation,所以可以使用解构赋值的方式简化参数,可以读state中的数据
    actions:{ getData({commit},param){ commit('GETDATA',parm) } }

  • Action通过store.dispatch进行分发,如果回调函数返回一个Promise,可以在分发的后面进行链式操作或者在其他action中进行链式操作
    actions:{ actionA({commit}){ return new Promise(async (resolve,reject)=>{ await delay(); commit('mutation1'); resolve() }) }, actionB({dispatch,commit}){ return dispatch('actionA').then(()=>{ commit('mutation2') }) } }

  • Action中可以异步请求与操作,在不同的异步阶段提交mutation,可以利用async/await组合action
1.4 Getters
类似与计算属性
在使用state中数据的同时,可能还需要由state派生出的一些状态,这时可以在组件内使用计算属性对state进行操作
但是当多个组件需要同样的派生状态,部分组件需要原state时,在每个组件内都使用计算属性显然很麻烦
getter可以将state中的数据计算后供组件访问
//store state: { user: [ { name: '张三', age: 18 }, { name: '李四', age: 15 }, { name: '王二', age: 21 }, { name: '麻子', age: 17 }, ] }, getters: { adult({ user }) { return user.filter(item => { return item.age >= 18 }) } } //组件内 computed: { adultUser() { return this.$store.getters.adult; }, },

  • getters中的回调第一个参数state,第二个参数getters
  • 可以让getter返回一个函数,这样可以向getter中传递数据,配合数组的find方法,可以对store中的数据进行查询
1.5 map
辅助函数
  1. mapState
  2. mapGetters
  3. mapMutations
  4. mapActions
1.5.1 mapState 当一个组件需要多个状态时,频繁使用计算属性让代码很臃肿,使用辅助函数可以简化操作
//store state: { a: 1, b: 2 }, //组件

引入 mapState函数,参数为数组形式,但是 不能改名,容易与data中的数据发生冲突
mapState函数返回一个 对象,使用展开运算符,可以逐个混入到计算属性中
//对象参数 computed: { ...mapState({ a: "a", bar: "b", }), },

1.5.2 mapGetters 与mapState函数类似
  • 在组件中引入mapGetters函数
  • 两种传参方式
  • 使用扩展运算符将getter混入computed对象中
  • 相当于计算属性的结果在组件中使用
1.5.3 mapMutations 将组将中的methods映射为store.commit调用
//store state: { count: 0 }, mutations: { increment(state, param) { state.count += param } } //组件

  • 在组件中引入mapMutations函数
  • 两种传递参数的方式
  • 扩展运算符将返回的对象混入到methods
  • 相当于在methods中注册的方法,调用时可以传递参数,作为回调函数的第二个参数
1.5.4 mapActions 与mapMutations类似
最终可以在组件中调用,可以使用this灵活的调用
2 Module 当状态较多时,store对象就会变得越来越臃肿
Vuex支持将store对象分割成模块。每个模块拥有自己的state、mutation、action、getter甚至是嵌套子模块
2.1 模块化的导入和导出
目录结构示例
└── store ├── index.js# 我们组装模块并导出 store 的地方 ├── actions.js# 根级别的 action ├── mutations.js# 根级别的 mutation └── modules ├── topics.js# 购物车模块 └── comments.js# 产品模块

//模块1 export const Topics = { state: { backTopic: '推荐', userTopic: '自定义' }, mutations: { modify(state, param) { state.userTopic = param } } } //模块2有同名的mutation ... //mutathions.js 有一个同名的方法 ... //index.js import Vue from 'vue' import Vuex from 'vuex' import { Topics } from './modules/topics' import { Comments } from './modules/comments' import { mutations } from './mutations'Vue.use(Vuex)export const store = new Vuex.Store({ state: { root: '根状态' }, mutations, modules: { Topics, Comments } })

子模块引入在modules对象中,无需改名可以使用简写形式
这样可以分模块操作,根state中会根据引入的模块混入 子state对象
如果在组件中直接提交mutation, 同名的mutation会同时开始操作
  • 在模块导出时添加namespaced:true,可以区分不同模块的同名操作
    export const Topics = { namespaced: true, state: { backTopic: '推荐', userTopic: '自定义' }, mutations: { modify(state, param) { state.userTopic = param } } }

  • 模块中的action的context中可以接收到跟状态rootState
  • 模块中的getter的参数:state getters rootState rootGetters
2.2 在组件中分发与提交
2.2.1 通过store直接访问
//读取state computed: { userTopics() { return this.$store.state.Topics.userTopic; }, } //提交mutation this.$store.commit("Topics/modify", "修改");

使用命名空间时,需要添加 路径才能提交mutation,action亦是如此
2.2.1 使用辅助函数
computed: { ...mapState({ a: (state) => state.Topics.userTopic, }), }, methods: { ...mapMutations({ modiT: "Topics/modify", }), },

对某一个模块的多次引用,可以进行简化
computed: { ...mapState("Topics", { a: "userTopic", b: "backTopic", }), }, methods: { ...mapMutations("Topics", { modiT: "modify", }), },

createNamespacedHelpers函数
import { createNamespacedHelpers } from 'vuex'const { mapState, mapMutations } = createNamespacedHelpers('Topics')

此时生成的辅助函数会有一个自定义的路径,可以像一般情况下使用
其他注意事项
  • 在模块中注册全局action
    将回调函数写成对象形式,添加属性 root:true,回调写放在 handler方法中
  • 动态注册模块
    【【vue】Vuex状态管理模式】创建store后使用registerModule函数
    // 注册模块 `myModule` store.registerModule('myModule', { // ... }) // 注册嵌套模块 `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... })

    推荐阅读