action类似于mutation,不同的是Action提交的是mutation,而不是直接变更状态,而且action里可以包含任意异步操作,每个mutation的参数1是一个对象,可以包含如下六个属性:
commit ;当前命名空间对应的commit
dispatch ;当前命名空间对应的dispatch
state ;当前命名空间对应的state
getters ;当前命名空间对应的getters
rootState ;根模块的state
rootGetters ;根模块的getters
类似于mutation,创建Vuex.Store()仓库实例时可以通过actions创建每个action
我们也不能直接调用一个action,而是通过 store.dispatch来调用,dispatch可以带两个参数,如下:
type ;对应的action名
payload ;传入的参数
dispatch还有一种写法,就是传入一个对象即可,该对象可以带一个type参数,type指定为action的名称,整个对象会作为参数传递给action。注意:action里可以包含异步操作
例如:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
<script src="https://unpkg.com/[email protected]/dist/vuex.js">script>
head>
<body>
<div id="app">
<p>{{no}}p>
<button @click="test">测试button>
div>
<script>
const store = new Vuex.Store({
state:{no:100},
mutations:{
increment(state,payload){state.no+=payload.no;}
},
actions:{
increment({commit},info){
return new Promise(function(resolve,reject){ //action里返回一个Promise对象
setTimeout(function(){
commit('increment',info)
resolve('ok')
},500)
})
}
}
})
var app = new Vue({
el:"#app",
store,
computed:{
no(){return this.$store.state.no}
},
methods:{
test(){
this.$store.dispatch('increment',{no:100})
}
}
})
script>
body>
html>
我们在action里不返回一个promise对象也可以,vuex内部会调用Promise.resolve自动将返回值转换为一个Promise对象
源码分析
writer by:大沙漠 QQ:22969969
在创建Vuex.Store()初始化时会执行installModule()安装根模块,和mutation相关的如下:
function installModule (store, rootState, path, module, hot) { //安装模块 /*略*/ module.forEachAction(function (action, key) { //遍历module模块的action对象,如果找到了,则执行这个匿名函数 参数1:每个action值 key:对应的键名 var type = action.root ? key : namespace + key; //对应的命名空间+key var handler = action.handler || action; //获取对应的函数 registerAction(store, type, handler, local); //调用registerAction注册action }); /*略*/ }
registerAction是用于注册action的,如下:
function registerAction (store, type, handler, local) { //注册action函数 store:Store实例 type:包含命名空间的action名 handler:函数 local:上下文相关的对象 var entry = store._actions[type] || (store._actions[type] = []); //如果store对象的_actions对应的type为空,则初始化为空数组 entry.push(function wrappedActionHandler (payload, cb) { //给store._actions push 进去一个匿名函数 var res = handler.call(store, { //该函数 dispatch: local.dispatch, commit: local.commit, getters: local.getters, state: local.state, rootGetters: store.getters, rootState: store.state }, payload, cb); //执行handler函数,上下文为store,参数1是个对象,参数2是payload数据,将返回值保存到res中 if (!isPromise(res)) { //如果res不是一个Promise res = Promise.resolve(res); //则将它转换为Promise对象 } if (store._devtoolHook) { return res.catch(function (err) { store._devtoolHook.emit('vuex:error', err); throw err }) } else { return res } }); }
从这里我们可以看到每个action对应的参数1,就是这里执行的handler函数,传入的对象,返回值如果不是Promise对象,则调用Promise.resolve()将它转换为Promise对象
等我们去调用this.$store.dispatch('increment',{no:100})触发一个action时,首先会触发Store函数内重定义的dispatch,它会以当前Store函数对象为上下文继续执行Store原型上的dispatch函数,如下:
Store.prototype.dispatch = function dispatch (_type, _payload) { //派发一个action异步操作 var this$1 = this; // check object-style dispatch var ref = unifyObjectStyle(_type, _payload); //规范一下参数,返回一个对象,这里和commit调用的是一样的 var type = ref.type; var payload = ref.payload; var action = { type: type, payload: payload }; var entry = this._actions[type]; //尝试获取type类型的action if (!entry) { //如果不存在则报错并返回 { console.error(("[vuex] unknown action type: " + type)); } return } try { this._actionSubscribers .filter(function (sub) { return sub.before; }) .forEach(function (sub) { return sub.before(action, this$1.state); }); } catch (e) { { console.warn("[vuex] error in before action subscribers: "); console.error(e); } } var result = entry.length > 1 ? Promise.all(entry.map(function (handler) { return handler(payload); })) : entry[0](payload); //执行该action,如果大于1则用Promise.all() return result.then(function (res) { try { this$1._actionSubscribers .filter(function (sub) { return sub.after; }) .forEach(function (sub) { return sub.after(action, this$1.state); }); } catch (e) { { console.warn("[vuex] error in after action subscribers: "); console.error(e); } } return res }) };
最后返回的还是一个res,也就是Promise对象,这样就实现了异步操作了。