vuex 必会用法

355 阅读3分钟

vuex 是一个状态管理模式,来集中管理组件的状态。此篇文章换种方式来总结vuex的使用方法。

安装

在创建项目的时候可以选择安装vuex,若未安装,则可以使用如下命令:

// 若使用 vue2 版本,需要加上 vuex@3 版本号需要 3.x
// npm 
npm install vuex --save
​
// yarn
yarn add vuex
基本结构

一般在src目录下会有一个store文件夹来存放vuex相关文件,基本结构如下:

store
  |-- index.js     # 我们组装模块并导出 store 的地方
  |-- state.js     # 根级别的 state 自己添加
  |-- getters.js   # 根级别的 getter 自己添加
  |-- actions.js   # 根级别的 action
  |-- mutations.js # 根级别的 mutation
  |-- modules
    |-- app.js  # app 模块
    |-- cart.js  # 购物车模块
部分代码示例

store/index.js

// 该文件用于创建 Vuex 中最为核心的 store
import Vue from 'vue'
import Vuex from 'vuex'// 这个是使用 Module 会使用到的文件
import app from './module/app'
import settings from './module/settings'import getters from './getters'Vue.use(Vuex)
​
const state = {
  count: 1
}
const mutations = {}
const actions = {}
​
// 创建并导出 store 
// store 核心概念都总结在了此处
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  modules: { // 当应用十分复杂时,会经常使用到
    app,
    settings
  }
})

getters以及modules下的文件举例:

// getters
const getters = {
  doList: state => state.app.doList
}
export default getters
​
// store/module/app.js
const state = {
  sidebar: ''
}
​
const mutations = {
  TOGGLE_SIDEBAR: (state, payload) => {
    state.sidebar = payload
  }
}
​
const actions = {
  doList({ commit }, payload) {
    commit('TOGGLE_SIDEBAR', payload)
  },
}
​
export default {
  namespaced: true, // 使用命名空间
  state,
  mutations,
  actions
}

必须在main.js中引入

// 此引入的是 store 文件夹下的 index.js
import store from './store'new Vue({
  el: '#app',
  store
})

使用

vuex本质上是通过获取或修改state值来管理组件的展示形式。

定义state,上面的代码示例已经有了。以下主要是修改与获取:

获取

获取state值的方式:

1)直接state获取

// 在模板语法中
<div>{{$store.state.count}}</div>
​
// <script>
this.$store.state.count// 计算属性中获取(推荐)
computed:{
  count(){
    return this.$store.state.count
  }
}

2)经过getter处理

getterstate经过一定规则处理完再给组件使用。例如,statecount加一后再返回等等...

getter 中的基本语法:

const getters = {
  doList (state) {
    return state.count + 1
  }
  
  // 如果只有一条语句也可以简写为
  // doList: state => state.count + 1
}

getter接受其他的getter作为第二个参数:

doList (state, getters) {
  return getters.doList.length
}

组件中使用getter

// 在模板语法中
<div>{{$store.getters.doList}}</div>
​
// <script>
this.$store.getters.doList// 计算属性中获取(推荐)
computed:{
  count(){
    return this.$store.getters.doList
  }
}

跟计算属性差不多,允许给getter传递参数:

getTodoById(state){
  return (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
​
// 或者
getTodoById: (state) => (id) => {
  return state.todos.find(todo => todo.id === id)
}
// 在组件上使用时,传递参数
$store.getters.getTodoById(2)
修改

vuex中只有mutation能修改state状态值。使用action是通过间接修改mutation来修改state

1)mutation

注意事项:

// mutation 必须是同步函数

mutations基本语法:

// 在 mutatiions 中定义的方法
const mutations = {
  "MT_COUNT"; function(state, payload){
     state.count = state.count + 1
  }
}

组件中触发mutation方法:

// 需要 commit 触发执行
this.$store.commit('MT_COUNT')
​
// 触发 mutation 传参,第一个参数动作类型, 第二个参数值 
// 参数可以传递对象
this.$store.commit('MT_COUNT', 10)
​
// 另一种提交方式,使用对象 type
toAdd () {
  this.$store.commit({
    // type 为 moutations 定义的方法名
    type: 'MT_COUNT', 
    // 传递的参数
    xxx: ‘fad’
  })
}

如果使用常量替代 Mutation 事件类型:

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

2)action

为什么要使用action ?

action中允许写入异步函数,如果未写入异步函数,是可以直接使用mutation

action基本语法:

const actions = {
  addCount: function(context, payload){
    // 调用 commit,触发 mutation 中的方法
    // context = {state, commit, getters}
    // 传参与 mutation 中一样
    context.commit('MT_COUNT', 2)
  }
}

组件中触发action

// 需要 dispatch 触发执行
this.$store.dispatch('addCount')
​
// 触发 action 传参
// 也可以传递对象
this.$store.dispatch('addCount', 10)
​
// 另一种提交方式,使用对象 type
this.$store.dispatch({
  // type 为 actions 定义的方法名
  type: 'addCount', 
  // 传递的参数
  xxx: ‘fad’
})

store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise

const actions = {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => { // 这里可以写异步函数
        commit('someMutation')
        resolve()
      }, 1000)
    })
  },
  
  // actionB 中也可以执行 actionA
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}
​
// 组件获取值
store.dispatch('actionA').then(() => {
  // ...
})
​
// 使用 ES6 的 async/await
const actions = {
  async actionA ({ commit }) {
    commit('xxx', await getData())
  },
  async actionB ({ dispatch, commit }) {
    // 等待 actionA 完成
    await dispatch('actionA') 
    commit('xxx', await getOtherData())
  }
}
module

如果项目比较大的情况下,如果集中在一起管理,维护会相对麻烦,所以有了分割模块功能。

模块上述代码示例也比较完整,这里就不再重复定义结构。

对于模块内部的actiongetter

// context 多了一个 rootState 代表根节点的 state

组件取模块内部的值:

// 取 state 值
// state.cart cart 为模块名称
this.$store.state.cart.xxx
​
// 取 getter 中的值
// 若未使用命名空间则与全局 getters 同一级,如果全局有定义相同方法名,会冲突
// cartProducts 为 getters 定义的方法名
this.$store.getters.cartProducts

关于命名空间:

默认情况下,模块内部的 actionmutationgetter 是注册在全局命名空间。所以,若未将namespaced设置为trueaction mutation getter 都在子类定义会发生冲突。

带命名空间的模块:

modules: {
  account: {
    namespaced: true,
    getters: {
      filterCount(){//...}
    }
  }
}
// 实际上会将名称变为 “account/getters”// 使用命名空间后
// xxx 为 module 配置的值
// state
$store.state.xxx.
//getters
this.$store.getters['xxxx/get']
// mutations
this.$store.commit('xxx/muta', newVal)
// actions
this.$store.dispatch(‘xx/action’, 2)

当前模块调用其他模块的参数

// 当前模块的 state 和 mutation 中是无法获取其它模块的state
// actions 中可通过第一个参数中的 rootState 属性获取到其它模块 state
const actions = {
  logout(content){
    content.commit
    content.dispatch
    content.rootState
    content.rootGetters
    content.state
  }
}
​
使用其他模块的 mutations
// 第三个参数传 { root: true }
content.commit('permission/reset', false {root: true}) 
​
// 使用 actions 
ontext.dispatch(‘模块名/actions方法名’, 传参, { root: true})
辅助函数

mapState

用来取state

import { mapState } from 'vuex'// mapState 在计算属性中可以是对象写法或者是数组写法
// 若计算属性中定义的名称与 state 中定义的名称不一致,则需要使用对象写法// 对象写法
computed: {
  // mapState 的参数对象的键值对为
  // {'计算属性方法名称': 'store中 state 的 key值'}
  ...mapState({count: 'count', xx: 'num'})
  
  // 说明
  ...mapState({
    // 箭头函数可使代码更简练
    count: state => state.count,
    // 别名 'count' 等同于 'state = > state.count'
    countAlias: 'count',
    
    // 使用组件中的 this 需要写成函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}
​
// 若计算属性中定义的名称与 state 中定义的名称一致,则可使用数组写法// 数组写法
computed: {
  ...mapState(['count', 'xxx'])
}

在启用命名空间后取state

computed: {
  ...mapState('app', {
    count: 'count'
  })
}

mapGetters

// 语法与 mapState 差不多
import { mapGetters } from 'vuex'
​
computed:{
  ...mapGetters([
    'doList',
    'doLength'
  ])
}
​
// 如果想使用别名 用对象语法
​
computed:{
  ...mapGetters({
    doneCount: 'doList',
    doneLength: 'doLength'
  })
}
​
// 启用命名空间
computed:{
  ...mapGetters(’app‘,{
    xxx
  })
}

mapMutations

actions语法与其差距不大,所以不重复了。

mapMutations使用方法来触发事件执行:

<button @click="incre()"></button>
​
import { mapMutations } from 'vuex'methods: {
  incre(){
    // 使用 handleMutation 来触发 mutations 执行
    // 这个方法名称若与 mutations 中定义的方法名称一致,可以使用数组语法
    // 如果需要传参,直接handleMutation(1),1即为参数
    this.handleMutation(1)
  },
  // 数组语法
  // 将 `this.handleMutation()` 映射为 `this.$store.commit('handleMutation')`
  ...mapMutations([’handleMutation‘])
  // 对象语法
  // 'jia'是 mutations 中定义的方法
  // 将 `this.handleMutation()` 映射为 `this.$store.commit('jia')`
})
  ...mapMutations({handleMutation: 'jia'})
}

在启用命名空间后触发mutations

// 模块名称,也可以使用数组语法
...mapMutations('app',{handleMutation: 'jia'})
​
//若多个模块,则使用多个mapMutations
...mapMutations('app',{handleMutation: 'jia'})
...mapMutations('cart',{handleMutation: 'jia'})

\