Vue之Vuex

在Vuex的教程中提到,一些概念:

  • mutation是改变state的唯一途径。
  • mutation只能是同步的,action可以包含异步代码。

我们想要改变state,只能通过mutation,而action想要改变state则需要commit mutation,那为什么又需要那么复杂的方式呢?为什么不直接通过mutation来改变呢。原因就在于他们的执行方式,mutation只能是同步的,也就是说触发的mutation会及时响应state的改变;而action如果是在异步代码中commit mutation来改变state,这样的改变就具有了不可预测性。然而异步编程在实际应用中是非常有用和重要,所以action就是为了异步编程而生的,mutation的作用就是用来改变state。

执行顺序

在不同的模块或者组件中声明了相同名称的muation、action,他们都会被添加到store的mutations属性对象和actions属性对象下,以数组的形式存储在此对象下。当这个名字的muation或者action被触发的时候,这个同名数组的的所有的函数都会被执行。这种方式使得app可以对相同的操作做出多种响应。

这里写图片描述

在上面的例子中,我在不同的模块中分别声明的同名的ADD_NOTE mutation和action。因此他们在store实例下面被分别添加到_mutations和_actions的ADD_NOTE 数组中。当我们在代码中去commit _mutations中的ADD_NOTE 时,会逐一执行数组中的函数。同理,当我们在代码中去dispatch _actions中的ADD_NOTE 时,会逐一执行数组中的函数。

数组中函数的执行顺序与包含这些mutation和action的模块的注册顺序相同。

这里写图片描述

在上面的例子中,无论什么情况都会在顶层的mutations和actions中声明的函数。然后才会按照modules中注册模块的顺序去执行。

这里写图片描述

上面的例子更改了顺序,但跟再上面的例子执行顺序无异。只有将modules里面模块的顺序更改了,执行顺序才会有所变化。

这里写图片描述

这个的执行顺序还是先执行mutations或者actions中的函数,然后则是toolbar -> noteslist -> editor.

名称空间namespace

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。有时候我们不想要模块的东西被注册到根模块下面,这时候就可以使用名称空间。我们在模块的内部使用一个namespaced的boolean属性来表示是否使用名称空间,如下:

export default {
  namespaced: true, //true表示
  mutations,
  actions
};

使用名称空间后,模块中的包含的getter,mutation,action在根模块中注册的名字都会加上这个名称空间:

getter:
Getters

action:
actions

mutation:
mutations

名称已经发生变化,当然在使用的时候也不能按照以前的方式调用。

  • getter

    如果你希望在getter中使用全局 state 和 getter,getter 会接收rootState 和 rootGetter 作为第三和第四参数传入。

const getters = {
  getNote (state, getters, rootState, rootGetters) {}
};

action

如果你希望在**action**中使用全局 state 和 getter,action 会接收一个含有rootState 和 rootGetters 的contex对象。
const actions = {
     // 在这个模块中, dispatch 和 commit 也被局部化了
    ADD_NOTE ({ dispatch, commit, getters, rootGetters }) {
        dispatch('ADD_NOTE', null, {root: true});//调用根模块action的ADD_NOTE
        commit('ADD_NOTE', null, {root: true});//调用根模块mutation的ADD_NOTE
    }
}

contex对象
这里写图片描述

除此之外我们还可以直接通过加名称空间的mutation和action来直接使用。

this.$store.commit('toolbar/ADD_NOTE');
this.$store.dispatch('toolbar/ADD_NOTE');
this.$store.state.toolbar.note;

官方教程中有如下:

首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:

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

上面的这几句话虽简短,却需要细细品味。store.dispatch()方法是有处理Promise的能力的,并且仍旧返回一个Promise。此方法是用来触发action的,因此能够处理并返回一个Promise的前提就是action要返回一个Promise。

现在你可以:

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

由于actionA中返回的是一个Promise,并且经过store.dispatch操作后仍旧返回了一个Promise,因此这里可以直接在后面使用.then操作,这样就可以达到异步也是顺序执行的目的。

在另外一个 action 中也可以:

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

实操如下:

在一个执行删除的action中返回了一个Promise,这个Promise内部是一个执行删除的ajax请求,请求执行完成后分发一个改变state的mutation,当这些操作全部执行完成后再执行后面的操作。

//action:
export const DELETE_NOTE = ({commit}, note) => {
  return new Promise((resolve, reject) => {
    axios({
      url: 'http://localhost:9090/notes/' + note.id,
      method: 'DELETE'
    })
    .then((res) => {
      console.log('删除成功');
      commit(types.DELETE_NOTE, note);
      resolve();
    }, (res) => {
      console.log(res);
      console.log('删除失败');
      reject();
    })
    .catch((res) => {
      console.log('抛出错误');
    });
  });
};
//....触发
    deleteNote (note) {
        let index = this.$store.state.notes.indexOf(note);
        this.$store
          .dispatch(types.DELETE_NOTE, note) //触发action
          .then(() => { //等待action中的异步操作完成,然后执行
            console.log('dsadas');
            let len = this.$store.state.notes.length;
            if (len > 0) {
              if (index > len - 1) {
                index = len - 1;
              }
            } else {
              index = -1;
            }
            let newNote = this.$store.state.notes[index];
            console.log(newNote === note);
            this.$store.commit(types.SET_ACTIVE_NOTE, newNote);
          });
      },

最后,如果我们利用 async / await 这个 JavaScript 即将到来的新特性,我们可以像这样组合 action:

// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

带名称空间的绑定函数

1、mapState
简化前:

computed: {
    ...mapState({
        note: state => state.toolbar.note
    })
}

简化后:

computed: {
    ...mapState('toolbar',  {
        note: state => state.note
    })
}

2.mapGetters, mapMutations, mapActions

简化前:

computed: {
    ...mapGetters({
        note: 'toolbar/getNote'
    })
},
methods: {
    ...mapMutations({
        add: 'toolbar/ADD_Note'
    }),
    ...mapActions({
        addNote: 'toolbar/ADD_Note'
    })
}

简化后:

computed: {
    ...mapGetters('toolbar', {
        note: 'getNote'
    })
},
methods: {
    ...mapMutations('toolbar', {
        add: 'ADD_Note'
    }),
    ...mapActions('toolbar', {
        addNote: 'toolbar/ADD_Note'
    })
}

动态加载模块

注册:
store被创建后,可以使用:

store.registerModule('moduleA',{});
store.registerModule(['moduleA','moduleB'],{});

来动态注册模块。他们分别解析为:store.state.moduleA和嵌套模块:store.state.moduleA.moduleB。它们的getters,mutations以此类推。
注销:

store.unregisterModule('moduleA');

我们可以在组件的钩子函数中动态注册和注销模块:

beforeCreate () {
    this.$store.registerModule('moduleA',{});
}
destoryed () {
    this.$store.unregisterModule('moduleA');
}

除了使用vue本身的钩子来动态加载之外,还可以配合路由插件使用。比如vue-router的导航守卫来实现。

二级标题

三级标题

四级标题

注:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值