vuex是什么?怎么用?

目录

什么是vuex 

state

 Getters

mutation

 Action

module


 什么是 vuex 

经常用vue的同学们,不管你们是否经常或者偶尔使用vuex,你们是否都了解vuex呐,这个听起来说起来还都感觉挺顺口的vuex到底是个什么东西呐?

首先,vuex是vue官方提供给我们的,所以学习的小伙伴首先应该从官方文档教程开始:

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

听起来是不是很蒙逼?继续。。。 

状态管理的诞生是为了更好的解决以下问题:

  • vue组件传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
  • 对于来自不同视图的行为需要变更同一状态的这种状态不好处理,通常会导致无法维护的代码。

那么什么是状态管理呐?首先应该弄清楚什么是状态

开发中我们通常会遇到以下的问题:

  • 需要保存一些全局性的数组,或者对象数据,如用户信息、全局性的数据、全局样式等。
  • 多个页面需要操作全局的数据,修改页面的状态等。
  • 多个页面依赖同一变量来控制页面的样式与显示。

诸如此类的需求很多,就不一一列举了, 而类似这种全局的数据便是状态,所以状态管理其实就是数据管理,也可以说是用数据来控制状态(个人理解)。

vue为了更好的管理数据,操作数据而诞生的状态管理,更加优化了数据的获取与修改操作,更方便开发。

那到底怎么使用状态管理呐?先从安装做起吧,安装部分内容请查阅官网,讲的很详细了,就不赘述了,vuex安装

vuex的核心就是store仓库,“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。上述说过状态管理其实就是数据管理,而组件data中也是数据,这两者有何区别呐?

Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

 接下来,让我们来创建一个store吧

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import getters from './getters'
import actions from './actions'

Vue.use(Vuex) // 在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex

export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
})

state

用来存储应用的所有状态数据,包含了全部的应用层级状态。如下:

// state.js文件
export default {
    userRegister: {}, // 用户数据
    showCurItem: false,//设置群聊信息窗口
    refurbish: false,//修改头像强制刷新
    isReciveNewMsg: false,//是否有新消息标记位
    reciveNewMsgSessionId: "",//新消息标记位对应的会话ID
    newConcept: '',  //tab栏默认标题
    etabsShow: true, //默认高亮
    projectMenu: [],//工程菜单
    companyMenu: [],//公司菜单
    ...
}

Vuex 使用 state 来存储应用中需要共享的状态。为了能让 Vue 组件在 state 更改后也随着更改,需要基于 state  创建计算属性。

computed: {
    count () {
      return this.$store.state.userRegister
    }
}

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:

import { mapState } from 'vuex'

export default {
    computed: {
      // 使用对象展开运算符将此对象混入到外部对象中
      ...mapState([
        "companyMenu",
        "userRegister",
      ])
    }
}

页面中直接使用:

var user = this.$store.state.userRegister;

 Getters

有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数: 

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

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Getter 接受 state,getter 作为其参数:

getters: {
  doneTodosCount: (state, getters) => {
    // return state.todos.filter(todo => todo.done)
    return getters.doneTodos.length
  }
}

在组件中使用:

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

mapGetters 辅助函数

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}
// getter.js
export default {
    get_DefaultMenus(state) {
        return state.isDefaultMenus
      },
    get_AdminMenu: state => {
        return state.isAdminMenu
    },
    currSessionMsgs: state => {
        return state.currSessionMsgs
    },
}

mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。也就是说 state  getters都是状态值本身,mutations 才是改变状态的执行者。注意:mutations只能是同步地更改状态。

Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

调用 store.commit 方法:

store.commit('increment')

提交载荷(Payload)

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

其中,第一个参数是state,后面的参数是向 store.commit 传入的额外的参数,即 mutation 的 载荷(payload)。

store.commit 方法的第一个参数是要发起的 mutation 类型名称,后面的参数均当做额外数据传入 mutation 定义的方法中。

规范的发起mutation的方式如下:

// 以载荷形式
store.commit('increment',{
  amount: 10   //这是额外的参数
})

// 或者使用对象风格的提交方式
store.commit({
  type: 'increment',
  amount: 10   //这是额外的参数
})

额外的参数会封装进一个对象,作为第二个参数传入 mutation 定义的方法中。

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

示例: 

// mutation.js
const mutation = {
    // 更改登录加载中状态
  update_loading(state, data) {
    state.loading = data;
    // 更改登录状态
    state.isLoading = true;
  },
  // 更改登录状态
  update_isLoading(state, data) {
    state.isLoading = data;
  },
  //通讯录选中的公司
  update_selectItem(state, data) {
    state.selectItem = data;
  },
  //通讯录选中的公司下部门
  update_depItem(state, data) {
    state.depItem = data;
  },
  // 更新当前聊天的消息列表
  update_currentMessageList(state, data) {
    // state.currSessionMsgs = state.msgs[data.id] || [];
    // let to = state.currSessionId
    let list = data.payLoad || [];
    let to = list[0] ? list[0].timelineId : state.currSessionId;
    state.sessionMapping[to] = setRepetition(state.sessionMapping[to], list);
    // state.sessionMapping[to] = state.sessionMapping[to] ? list.concat(state.sessionMapping[to]) : list;
    state.sessionMapping = Object.assign({}, state.sessionMapping);
    state.currSessionMsgs = state.sessionMapping[to]
    //当前被选择的会话信息假如有未读消息,则将未读数据清零
    state.sessionlist.forEach((item, index) => {
      if (to == item.timelineId) {
        state.totalUnReadNum = state.totalUnReadNum - item.unReadNum;
        item.unReadNum = 0;
        //清除指定timelineId里面的未读回话消息
        delete state.unread_session_list[to]
      }
    })
    let temp = {
      "payLoad": state.sessionlist
    }
    //TODO:同步未读消息列表
    window.ipcRenderer.send('setHideTwinkle',{"unReadMsgList":state.unread_session_list,"totalUnReadNum": state.totalUnReadNum})
    store.commit("update_local_sessionList", temp)
  },
}
export default mutation

 Action

想要异步地更改状态,就需要使用 action。action 并不直接改变 state ,而是发起 mutation。

注册一个简单的 action:

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

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: {
  increment ({ commit, state, getters }) {
    commit('increment')
  }
}

在action内部执行异步操作:

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

发起 action 的方法形式和发起 mutation 一样,只是换了个名字 dispatch。

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

Actions 支持同样的载荷方式和对象方式进行分发。

Action处理异步的正确使用方式

想要使用 action 处理异步工作很简单,只需要将异步操作放到 action 中执行(如上面代码中的 setTimeout )。

要想在异步操作完成后继续进行相应的流程操作,有两种方式:

store.dispatch 返回相应 action 的执行结果,而action的处理函数返回的就是Promise,所以 store.dispatch 仍然返回一个 Promise。

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

现在可以写成:

store.dispatch('actionA').then(() => {
  // ...
})

在另外一个 action 中也可以:

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

利用 async/await  进行组合action。代码更加简洁。

// 假设 getData() 和 getOtherData() 返回的是 Promise

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

Action与Mutation的区别

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作,而Mutation只能且必须是同步操作。

示例: 

// action.js
actions: {
    // 用户名登录
    LoginByUsername({ commit }, userInfo) {
      const username = userInfo.username.trim()
      return new Promise((resolve, reject) => {
        loginByUsername(username, userInfo.password).then(res => {
          if (res.code === 0) {
            commit('SET_TOKEN', res.data.token)
            setToken(res.data.token)
            resolve(res)
          } else {
            reject(res)
          }
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 获取用户信息
    GetUserInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getUserInfo(state.token).then(response => {
          if (!response.data) { // 由于mockjs 不支持自定义状态码只能这样hack
            reject('error')
          }
          const data = response.data
          if (data.user) {
            commit('SET_USERRANK', data.user)
          }
          if (data.perms && data.perms.length > 0) {
            commit('SET_PERMS', data.perms)
            commit('SET_BTNPERMS', data.perms)
          } else {
            reject('getUserInfo: perms must be a non-null array !')
          }
          /*
          const data = response.data
          if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
            commit('SET_ROLES', data.roles)
          } else {
            reject('getInfo: roles must be a non-null array !')
          }
          commit('SET_NAME', data.name)
          commit('SET_AVATAR', data.avatar)
          commit('SET_INTRODUCTION', data.introduction)
          */
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    },
    // 登出
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          removeToken()
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    },

    // 动态修改权限
    ChangeRoles({ commit }, role) {
      return new Promise(resolve => {
        commit('SET_TOKEN', role)
        setToken(role)
        getUserInfo(role).then(response => {
          const data = response.data
          commit('SET_ROLES', data.roles)
          commit('SET_NAME', data.name)
          commit('SET_AVATAR', data.avatar)
          commit('SET_INTRODUCTION', data.introduction)
          resolve()
        })
      })
    },

  }
}

export default user

module

 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

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 的状态

模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。

对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState

模块内部的 getter,根节点状态会作为第三个参数暴露出来。

const moduleA = {
  state: () => ({
    count: 0
  }),

  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },

  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  },
  
  action: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

命名空间,模块动态注册,模块重用,请看官方文档:vuex命名空间

官方文档: vuex官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值