从 Vuex 操作返回 Promise
Returning Promises from Vuex actions
我最近开始将一些东西从 jQ 迁移到一个更结构化的框架 VueJS,我喜欢它!
从概念上讲,Vuex 对我来说有点范式转变,但我相信我现在知道它的全部内容,并且完全理解它!但是存在一些小的灰色区域,主要是从实现的角度来看。
这个我感觉设计的不错,不知道和Vuex的单向数据流cycle是否矛盾
基本上,从操作中 return promise(-like)对象被认为是好的做法吗?我将它们视为异步包装器,具有失败状态等,因此看起来很适合 return 承诺。相反,突变体只是改变事物,并且是 store/module.
中的纯结构
actions
在 Vuex 中是异步的。让调用函数(动作的发起者)知道动作已完成的唯一方法是 returning 一个 Promise 并稍后解析它。
这是一个示例:myAction
returns a Promise
,进行 http 调用并稍后解析或拒绝 Promise
- 全部异步
actions: {
myAction(context, data) {
return new Promise((resolve, reject) => {
// Do something here... lets say, a http call using vue-resource
this.$http("/api/something").then(response => {
// http success, call the mutator and change something in state
resolve(response); // Let the calling function know that http is done. You may send some data back
}, error => {
// http failed, let the calling function know that action did not work out
reject(error);
})
})
}
}
现在,当你的 Vue 组件启动时 myAction
,它会得到这个 Promise 对象,并且可以知道它是否成功。下面是 Vue 组件的一些示例代码:
export default {
mounted: function() {
// This component just got created. Lets fetch some data here using an action
this.$store.dispatch("myAction").then(response => {
console.log("Got some data, now lets show something in this component")
}, error => {
console.error("Got nothing from server. Prompt user to check internet connection and try again")
})
}
}
正如您在上面看到的,它对 actions
到 return 和 Promise
非常有益。否则,操作发起者无法知道发生了什么,也无法知道什么时候事情足够稳定,可以在用户界面上显示一些东西。
关于 mutators
的最后一点说明 - 正如您正确指出的那样,它们是同步的。它们更改 state
中的内容,通常从 actions
中调用。无需将 Promises
与 mutators
混合,因为 actions
处理该部分。
编辑:我对单向数据流的Vuex循环的看法:
如果您在组件中访问 this.$store.state["your data key"]
之类的数据,则数据流是单向的。
action 的 promise 只是让组件知道 action 已经完成。
组件可以从上例中的 promise resolve 函数中获取数据(不是单向的,因此不推荐),或者直接从 $store.state["your data key"]
中获取数据,这是单向的并且遵循 vuex 数据生命周期。
以上段落假定您的修改器使用 Vue.set(state, "your data key", http_data)
,一旦您的操作完成了 http 调用。
仅针对封闭主题的信息:
你不必创建一个承诺,axios returns 一个本身:
示例:
export const loginForm = ({ commit }, data) => {
return axios
.post('http://localhost:8000/api/login', data)
.then((response) => {
commit('logUserIn', response.data);
})
.catch((error) => {
commit('unAuthorisedUser', { error:error.response.data });
})
}
另一个例子:
addEmployee({ commit, state }) {
return insertEmployee(state.employee)
.then(result => {
commit('setEmployee', result.data);
return result.data; // resolve
})
.catch(err => {
throw err.response.data; // reject
})
}
async-await
的另一个例子
async getUser({ commit }) {
try {
const currentUser = await axios.get('/user/current')
commit('setUser', currentUser)
return currentUser
} catch (err) {
commit('setUser', null)
throw 'Unable to fetch current user'
}
},
操作数
ADD_PRODUCT : (context,product) => {
return Axios.post(uri, product).then((response) => {
if (response.status === 'success') {
context.commit('SET_PRODUCT',response.data.data)
}
return response.data
});
});
组件
this.$store.dispatch('ADD_PRODUCT',data).then((res) => {
if (res.status === 'success') {
// write your success actions here....
} else {
// write your error actions here...
}
})
TL:DR; return 承诺您仅在必要时采取行动,但 DRY 链接相同的行动。
很长一段时间以来,我也认为 returning 操作与 uni-directional 数据流的 Vuex 循环相矛盾。
但是,在边缘案例中,return从你的行为中得到的承诺可能是"necessary"。
想象这样一种情况,其中可以从 2 个不同的组件触发一个操作,并且每个组件以不同的方式处理故障情况。
在那种情况下,需要将调用者组件作为参数传递以在商店中设置不同的标志。
哑示例
用户可以在导航栏和 /profile 页面(包含导航栏)中编辑用户名的页面。两者都触发一个动作 "change username",这是异步的。
如果承诺失败,页面应该只在用户试图更改用户名的组件中显示错误。
当然这是一个愚蠢的例子,但我没有找到解决这个问题的方法,而不是复制代码并在 2 个不同的操作中进行相同的调用。
actions.js
const axios = require('axios');
const types = require('./types');
export const actions = {
GET_CONTENT({commit}){
axios.get(`${URL}`)
.then(doc =>{
const content = doc.data;
commit(types.SET_CONTENT , content);
setTimeout(() =>{
commit(types.IS_LOADING , false);
} , 1000);
}).catch(err =>{
console.log(err);
});
},
}
home.vue
<script>
import {value , onCreated} from "vue-function-api";
import {useState, useStore} from "@u3u/vue-hooks";
export default {
name: 'home',
setup(){
const store = useStore();
const state = {
...useState(["content" , "isLoading"])
};
onCreated(() =>{
store.value.dispatch("GET_CONTENT" );
});
return{
...state,
}
}
};
</script>
我最近开始将一些东西从 jQ 迁移到一个更结构化的框架 VueJS,我喜欢它!
从概念上讲,Vuex 对我来说有点范式转变,但我相信我现在知道它的全部内容,并且完全理解它!但是存在一些小的灰色区域,主要是从实现的角度来看。
这个我感觉设计的不错,不知道和Vuex的单向数据流cycle是否矛盾
基本上,从操作中 return promise(-like)对象被认为是好的做法吗?我将它们视为异步包装器,具有失败状态等,因此看起来很适合 return 承诺。相反,突变体只是改变事物,并且是 store/module.
中的纯结构actions
在 Vuex 中是异步的。让调用函数(动作的发起者)知道动作已完成的唯一方法是 returning 一个 Promise 并稍后解析它。
这是一个示例:myAction
returns a Promise
,进行 http 调用并稍后解析或拒绝 Promise
- 全部异步
actions: {
myAction(context, data) {
return new Promise((resolve, reject) => {
// Do something here... lets say, a http call using vue-resource
this.$http("/api/something").then(response => {
// http success, call the mutator and change something in state
resolve(response); // Let the calling function know that http is done. You may send some data back
}, error => {
// http failed, let the calling function know that action did not work out
reject(error);
})
})
}
}
现在,当你的 Vue 组件启动时 myAction
,它会得到这个 Promise 对象,并且可以知道它是否成功。下面是 Vue 组件的一些示例代码:
export default {
mounted: function() {
// This component just got created. Lets fetch some data here using an action
this.$store.dispatch("myAction").then(response => {
console.log("Got some data, now lets show something in this component")
}, error => {
console.error("Got nothing from server. Prompt user to check internet connection and try again")
})
}
}
正如您在上面看到的,它对 actions
到 return 和 Promise
非常有益。否则,操作发起者无法知道发生了什么,也无法知道什么时候事情足够稳定,可以在用户界面上显示一些东西。
关于 mutators
的最后一点说明 - 正如您正确指出的那样,它们是同步的。它们更改 state
中的内容,通常从 actions
中调用。无需将 Promises
与 mutators
混合,因为 actions
处理该部分。
编辑:我对单向数据流的Vuex循环的看法:
如果您在组件中访问 this.$store.state["your data key"]
之类的数据,则数据流是单向的。
action 的 promise 只是让组件知道 action 已经完成。
组件可以从上例中的 promise resolve 函数中获取数据(不是单向的,因此不推荐),或者直接从 $store.state["your data key"]
中获取数据,这是单向的并且遵循 vuex 数据生命周期。
以上段落假定您的修改器使用 Vue.set(state, "your data key", http_data)
,一旦您的操作完成了 http 调用。
仅针对封闭主题的信息: 你不必创建一个承诺,axios returns 一个本身:
示例:
export const loginForm = ({ commit }, data) => {
return axios
.post('http://localhost:8000/api/login', data)
.then((response) => {
commit('logUserIn', response.data);
})
.catch((error) => {
commit('unAuthorisedUser', { error:error.response.data });
})
}
另一个例子:
addEmployee({ commit, state }) {
return insertEmployee(state.employee)
.then(result => {
commit('setEmployee', result.data);
return result.data; // resolve
})
.catch(err => {
throw err.response.data; // reject
})
}
async-await
的另一个例子 async getUser({ commit }) {
try {
const currentUser = await axios.get('/user/current')
commit('setUser', currentUser)
return currentUser
} catch (err) {
commit('setUser', null)
throw 'Unable to fetch current user'
}
},
操作数
ADD_PRODUCT : (context,product) => {
return Axios.post(uri, product).then((response) => {
if (response.status === 'success') {
context.commit('SET_PRODUCT',response.data.data)
}
return response.data
});
});
组件
this.$store.dispatch('ADD_PRODUCT',data).then((res) => {
if (res.status === 'success') {
// write your success actions here....
} else {
// write your error actions here...
}
})
TL:DR; return 承诺您仅在必要时采取行动,但 DRY 链接相同的行动。
很长一段时间以来,我也认为 returning 操作与 uni-directional 数据流的 Vuex 循环相矛盾。
但是,在边缘案例中,return从你的行为中得到的承诺可能是"necessary"。
想象这样一种情况,其中可以从 2 个不同的组件触发一个操作,并且每个组件以不同的方式处理故障情况。 在那种情况下,需要将调用者组件作为参数传递以在商店中设置不同的标志。
哑示例
用户可以在导航栏和 /profile 页面(包含导航栏)中编辑用户名的页面。两者都触发一个动作 "change username",这是异步的。 如果承诺失败,页面应该只在用户试图更改用户名的组件中显示错误。
当然这是一个愚蠢的例子,但我没有找到解决这个问题的方法,而不是复制代码并在 2 个不同的操作中进行相同的调用。
actions.js
const axios = require('axios');
const types = require('./types');
export const actions = {
GET_CONTENT({commit}){
axios.get(`${URL}`)
.then(doc =>{
const content = doc.data;
commit(types.SET_CONTENT , content);
setTimeout(() =>{
commit(types.IS_LOADING , false);
} , 1000);
}).catch(err =>{
console.log(err);
});
},
}
home.vue
<script>
import {value , onCreated} from "vue-function-api";
import {useState, useStore} from "@u3u/vue-hooks";
export default {
name: 'home',
setup(){
const store = useStore();
const state = {
...useState(["content" , "isLoading"])
};
onCreated(() =>{
store.value.dispatch("GET_CONTENT" );
});
return{
...state,
}
}
};
</script>