Vuex的使用, 看完就懂了

1. 什么是vuex

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.

  • 你可以简单理解为, 这是一个全局状态下的data, 任何组件可以访问这里面的数据.

  • 两个特点:

  1. Vuex 的状态存储是响应式的.
  2. 不能直接改变store里面的数据状态, 通过提交(commit) mutation来改变数据.

2. 一个简单的vuex

  1. 定义 store/index.js 文件
import Vue from 'Vue'
import Vuex from 'Vuex'

Vue.use(Vuex)

const store = new Vuex({
    state: {
        count: 0,
        txt1: 'hello',
        txt2: 'world'
    },
    // getters 简单理解就是computed属性
    getters:{
        txtLink(state){
            return state.txt1 + state.txt2
        },
        // getter作为第二个参数
        txtLinkLength(state,getters){
            return getters.txtLink.length
        }
    },
    // mutation 只能同步操作
    mutations:{
        increment(state){
            state.count++
        },
        // 带参数
        increment1(state,n){
            state.count+=n
        },
        // 参数是对象
        increment2(state,obj){
            console.log(obj)
        },
    },
    // 通过action可以异步操作
    actions: { 
        // 这里context可以代指store,但不是store
        increment (context) {
            // 可以通过context获取 state 和 getters里的数据
            // const count = context.state.count
            // const txtLink = context.getters.txtLink
            context.commit('increment')
        },
        // 异步 {commit} 解构 context.commit
        // params 是传入的参数,可以是对象,字符串等
        incrementAsync({commit, state}, params){
            setTimeout(()=>{
                commit('increment')
            },1000)
        },

        // Promise
        actionA({commit}){
            return new Promise((resolve,reject)=>{
                 setTimeout(()=>{
                    commit('increment')
                    resolve()
                },1000)
            })
        },
        // action里面调用异步action
        actionB({commit,dispatch}){
         dispatch('actionA').then(res=>console.log(res))
        },

        // async await
        // getData(),getOtherData() 是 Promise
         async actionA ({ commit }) {
            commit('gotData', await getData())
         },
        async actionB ({ dispatch, commit }) {
            await dispatch('actionA') // 等待 actionA 完成
            commit('gotOtherData', await getOtherData())
        }
  }
})
  1. 根组件注册 store (一般是 main.js)
new Vue({
    el: '#app'
    store,
})
  1. 组件内使用


  • 注意:
  1. Mutation 必须是同步函数 (因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。)
  2. 一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

3. Module

  • 状态很多且需要分模块的时候可以使用 Module
  1. 简单的 module
const moduleA = {
  state: () => ({ 
      count:0
   }),
  mutations: { 
      increment(state){
          state.count++
      }
   },
  actions: { 
      incrementSync({commit}){
          commit('increment')
      }
   },
  getters: { 
      countGetter(state){
          return state*2
      }
   }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})


// 组件内使用各种状态
// a模块state
store.state.a.count
// a模块getters
store.getters.a.count
// a模块mutations
store.commit('a/increment')
// a模块mutations
store.dispatch('a/incrementSync')

store.state.b // -> moduleB 的状态 ...
  1. 模块的状态
const moduleA = {
  state: () => ({
    count: 0
  }),
  mutations: {
    increment (state) {
      // 这里的 `state` 对象是模块的局部状态
      state.count++
    }
  },
 // 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
  getters: {
    doubleCount (state,getters,rootState) {
      return state.count + rootState.count
    }
  },

  actions: {
    increment ({ state, commit, rootState }) {
        // rootState 是根节点的状态
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }

}
  1. 命名空间
  • 默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
  • 如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
  • 若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
modules: {
  foo: {
    namespaced: true,

    getters: {
      // 在这个模块的 getter 中,`getters` 被局部化了
      // 你可以使用 getter 的第四个参数来调用 `rootGetters`
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      },
      someOtherGetter: state => { ... }
    },

    actions: {
      // 在这个模块中, dispatch 和 commit 也被局部化了
      // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}
  • 若需要在带命名空间的模块注册全局 action,你可添加 root: true
{
  actions: { 
      // 这里是全局的action
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true, // 添加 root 属性变成全局actions
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}
  • 带命名空间的绑定函数
computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}

// 简写

computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}
  1. 使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。
import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}
  1. 模块动态注册
  • 在 store 创建之后,你可以使用 store.registerModule 方法注册模块:
  • 之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态。
import Vuex from 'vuex'

const store = new Vuex.Store({ /* 选项 */ })

// 注册模块 `myModule`
store.registerModule('myModule', {
  // ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

3.1 命名空间的一些注意点

  1. 不带命名空间(namespaced: false)
  • state的调用不会提升, getter,metations 和actions的使用会提升到最顶层
const moduleA = {
  state: () => ({ 
      count:0
   }),
  mutations: { 
      increment(state){
          state.count++
      }
   },
  actions: { 
      incrementSync({commit}){
          commit('increment')
      }
   },
  getters: { 
      countGetter(state){
          return state*2
      }
   }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})


// 组件内使用各种状态
store.state.a.count 
store.getters.a.count
store.commit('increment') // 这里路径就不是a/increment了
store.dispatch('incrementSync')
  1. 带命名空间(namespaced: true)
const moduleA = {
  namespaced: true,
  state: () => ({ 
      count:0
   }),
  mutations: { 
      increment(state){
          state.count++
      }
   },
  actions: { 
      incrementSync({commit}){
          commit('increment')
      }
   },
  getters: { 
      countGetter(state){
          return state*2
      }
   },
   moduleB{
       ...
   }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})


// 组件内使用各种状态
store.state.a.count 
store.getters.a.count // 这里路径就要加上模块路径了
store.commit('a/increment') // 这里路径也要加上模块路径了
store.dispatch('a/incrementSync')
- PS :如果模块A 里面再次嵌套b模块
a和b模块都带命名空间
store.state.a.b.count 
store.getters.a.b.count1 // 这里路径就要加上模块路径了
store.commit('a/b/increment1') // 这里路径也要加上模块路径了
store.dispatch('a/b/incrementSync1')
a模块带命名空间b模块不带命名空间(getter,metations 和actions的使用路径会依次提升,提升到有命名空间的模块时会停止,如果都没有命名空间则会上升到根状态上调用,如果上升过程中发现有重复的命名,会报错 duplicate getter key: xxx)
store.state.a.b.count 
store.getters.a.count1 
store.commit('a/increment1') 
store.dispatch('a/incrementSync1')

4. 一些规则

  1. 应用层级的状态应该集中到单个 store 对象中。

  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。

  3. 异步逻辑都应该封装到 action 里面。

5. 各部分参数说明

5.1 构造器选项

  1. state
  • 类型: Object | Function
  • 没有参数
  • module中使用对象函数,对于重用 module 来说非常有用, 类似data(){reutrn{}}
  1. mutations
  • 类型: { [type: string]: Function }
  • state 作为第一个参数 , payload 作为第二个参数(可选)就是传入参数
mutations:{
    ADD(state,payload){

    }
}
  1. actions
  • 类型: { [type: string]: Function }
  • context 作为第一个参数,payload 作为第二个参数(可选)。
actions:{
    ADD(context,payload){
        
    }
}
// 解构
actions:{
    ADD({state,commit,dispatch,getters},payload){
        
    }
}
  • context 对象包含以下属性:
{
  state,      // 等同于 `store.state`,若在模块中则为局部状态
  rootState,  // 等同于 `store.state`,只存在于模块中
  commit,     // 等同于 `store.commit`
  dispatch,   // 等同于 `store.dispatch`
  getters,    // 等同于 `store.getters`
  rootGetters // 等同于 `store.getters`,只存在于模块中
}
  1. getters
  • 类型: { [key: string]: Function }
  • 在 store 上注册 getter,getter 方法接受以下参数:
state,     // 如果在模块中定义则为模块的局部状态
getters,   // 等同于 store.getters
  • 当定义在一个模块里:
state,       // 如果在模块中定义则为模块的局部状态
getters,     // 等同于 store.getters
rootState    // 等同于 store.state
rootGetters  // 所有 getters
  1. modules
  • 类型: Object
  • 包含了子模块的对象,会被合并到 store,大概长这样:
{
  key: {
    state,
    namespaced?,
    mutations,
    actions?,
    getters?,
    modules?
  },
  ...
}

5.2 实例方法

  1. commit
  • commit(type: string, payload?: any, options?: Object)
  • commit(mutation: Object, options?: Object)
  1. dispatch
  • dispatch(type: string, payload?: any, options?: Object): Promise

  • dispatch(action: Object, options?: Object): Promise

  • 更多详情请参阅文档
    Vuex官方链接

你可能感兴趣的:(Vue,vue.js,vuex)