使用场景
项目中发起请求,难免会需要用到loading,如果我们直接使用拦截器拦截请求的发起以及响应,会导致整个项目中所有的请求公用一个状态。
如果我们需要控制每一个请求的loading状态那就非常完美了
先看效果
初设
为解决这个问题,我们可以使用vuex,在所有的action中设置与当前action同名的loading状态,如下
// 此函数用于模拟ajax请求,返回一个promise,将会在2s后成功
import { axiosTwo } from '@/utils/http'
import { createStore } from 'vuex'
export default createStore({
state: {
// 存储当前模块中所有的loading
loading: {}
},
mutations: {
// 保存loading状态到状态(state)中
setLoading (state, { name, isLoading }) {
state.loading[name] = isLoading
}
},
actions: {
async getList (context) {
// 设置loading状态为true
context.commit('setLoading', {
name: 'getList',
isLoading: true
})
await axiosTwo()
console.log('请求完成')
// 设置loading状态
context.commit('setLoading', {
name: 'getList',
isLoading: false
})
}
}
})
但是这样的话我们每个action都要重复的写上这块代码,非常不友好,容易累死,所以我们可以做一个工厂函数,让每一个经过我这里的action都变成这个样子就完美了
plus版
// 用于生成带有loading的actions
function createActions (actions) {
// 用于记录装修过的actions
const newActions = {}
// 循环所有的action
for (const fnName in actions) {
if (Object.hasOwnProperty.call(actions, fnName)) {
const action = actions[fnName]
// 存储新的action
newActions[fnName] = async function (context, payload) {
// 在这个新的action中设置loading状态
context.commit('setLoading', {
name: fnName,
isLoading: true
})
// 调用原本的action函数,并记录该action执行的结果
const data = await action(context, payload)
// 设置loading状态为false
context.commit('setLoading', {
name: fnName,
isLoading: false
})
// 返回action的执行结果,这个如果不处理的话会导致原本action返回的数据丢失
return data
}
}
}
return newActions
}
// 配置vuex
export default createStore({
// state、mutations和上面相同
actions: createActions({
async getList () {
await axiosTwo()
console.log('请求完成')
}
})
})
最终版本
这种写法貌似也不错,但是还需要手动配置setLoading以及state,这对于不喜欢看文档的童鞋有些许的不友好,因此我们可以将这个函数进行究极进化,不需要手动配置这两个配置项
// 用于生成带有loading的action以及其他的配置
function createVuexConfig (config) {
// 解构对应的属性,并赋默认值
const { actions = {}, state = {}, mutations = {} } = config
// 合并并修改state
config.state = Object.assign(state, { loading: {} })
// 合并并修改mutation
config.mutations = Object.assign(mutations, {
setLoading (state, { name, isLoading }) {
// 保存loading状态到状态(state)中
state.loading[name] = isLoading
}
})
// 用于记录装修过的actions
const newActions = {}
// 循环所有的action
for (const fnName in actions) {
if (Object.hasOwnProperty.call(actions, fnName)) {
const action = actions[fnName]
// 存储新的action
newActions[fnName] = async function (context, payload) {
// 在这个新的action中设置loading状态
context.commit('setLoading', {
name: fnName,
isLoading: true
})
// 调用原本的action函数,并记录该action执行的结果
const data = await action(context, payload)
// 设置loading状态为false
context.commit('setLoading', {
name: fnName,
isLoading: false
})
// 返回action的执行结果,这个如果不处理的话会导致原本action返回的数据丢失
return data
}
}
}
// 修改actions
config.actions = newActions
// 返回配置项
return config
}
使用教程
仅需在配置vuex时使用该函数包裹即可
export default createStore(createVuexConfig({
actions: {
async getList () {
await axiosTwo()
console.log('请求完成')
return '我是dispatch的执行结果'
},
async getTwoList () {
await axiosTwo()
await axiosTwo()
console.log('两次请求完成')
}
}
}))
说明
本文原创,转载请注明出处