前言
承接上篇文章,这里分享useAsync
介绍
useAsync是对接口请求的统一封装,接收一个promise请求,对接口返回数据存储,同时使用try catch捕获服务端或语法上的错误
基础使用
request是一个promise请求,result接收接口返回数据
const { result } = useAsync({ request, isLoading: false })
初始化不调用
const { result } = useAsync({ request, init: false, isLoading: false })
默认带参
const { result } = useAsync({ request, params: { id: 1 } })
开启loading
默认loading效果开启,业务场景中大多需要loading,当不需要时将isLoading设置为false
const { result, loading } = useAsync({ request })
链式调用
const { doResult } = useAsync({ request })
const getList = () => doResult(params).then(({code, params, data, message}) => console.log('ok'))
场景一
编辑、新增共用弹窗需借助外部loading
当编辑、修改、删除共用一个弹窗时,loading不在适用useAsync内部抛出的loading,需借助外部loading,通过useAsync暴露的回调函数处理loading状态
// loading的定义可以自定义loading
const loading = ref(false)
// 或者借助useLoading,项目中为达成一致使用useLoading,此种写法更像react-hooks的写法,有两个优点:
// 1. 更新数据入口明确,均通过checkLoading方法更新数据
// 2. 数据流向明确,在useLoading中将loading更新
import useLoading from '@/hooks/useLoading'
const { loading, checkLoading } = useLoading()
// preCallback:调用接口前回调
// successCallBack:成功回调
// errorCallBack:catch捕捉到错误回调
// 新增
useAsync({
request: add,
isLoading: false,
preCallback: () => checkLoading(true),
errorCallBack: () => checkLoading(false),
successCallBack: () => checkLoading(false)
})
// 编辑
useAsync({
request: edit,
isLoading: false,
preCallback: () => checkLoading(true),
errorCallBack: () => checkLoading(false),
successCallBack: () => checkLoading(false)
})
场景二
下拉框数据异步获取
// formState.setConfig是更新下拉框数据方法,不了解userForm时无需过分关注此处
// successCallBack是获取到数据后的后续动作,这里的动作指的是一件事,比如更新下拉框、提示提交成功、触发table更新等等
useAsync({
request,
isLoading: false,
successCallBack: ({ code, data }) => code === '0' && formState.setConfig('users', 'options', data || [])
})
场景三
接口返回数据不满足需求
这里单纯指的是数据不满足需求,数据无法满足需求时在callback中可以拿到接口返回的原始数据,对原始数据处理即可,但返回的数据需满足{ code, data, message }格式
默认对接口返回数据处理如下(可约定):
// defaultCallBack为内置的默认对接口返回数据的处理
const defaultCallBack = (res = {}) => {
// res为接口返回的原始数据
if (!res) return {}
const { data: { code, data, message } } = res || {}
return {
code,
data,
message
}
}
实际案例:
// 参数回调
const callback = res => {
const { data: { data } } = res || {}
const result = Object.prototype.toString.call(data) === '[object Array]' ? data
: data && data.pagedRecords ? data.pagedRecords
: []
// 此处返回数据,需遵循{ data, code, message }格式
return {
data: result
}
}
// 获取并处理数据
const { result: dataSource, loading } = useAsync({ request, callback })
场景四
被动触发
例如:查询
const { doResult } = useAsync({ request, init: false })
const searchHandle = params => doResult(params)
场景五
接口成功后触发动作
例如: 提交表单成功后更新table
const { doResult } = useAsync({
request,
init: false,
successCallBack: ({ code, params, data }) => {
code === '0' && Message.success('添加成功')
code === '0' && emit('successHandle')
code === '0' && doSomethings(params, data)
}
})
const addHandle = params => doResult(params)
入参
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| request | 接口请求 | Promise | - | - |
| params | 默认参数 | Object | - | - |
| init | 是否初始化调用 | Boolean | - | true |
| isLoading | 是否开启loading | Boolean | - | true |
| preCallback | 调用接口前回调 | Function | - | - |
| callback | 原始数据处理回调,通常只对数据处理 | Function(res) | - | - |
| successCallBack | 接口成功、数据处理完毕后触发动作回调 | Function({ code, data, message, params } ) | - | - |
| errorCallBack | 捕获错误回调,语法错误、服务端失败等 | Function | - | - |
Callback Params
使用callback处理数据后,需将数据按格式返回,返回格式如下:{ code, data, message }
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| res | 接口返回的原始数据 | - | - | - |
SuccessCallBack Params
若之定义了```callback```函数,请以自定义```callback```返回的数据为准
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| code | 状态码 | - | - | - |
| data | 处理后的数据 | - | - | - |
| message | 接口返回的消息 | - | - | - |
| params | 请求时的入参 | - | - | - |
出参
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| loading | 加载状态(若isLoading为false,则不会抛出该值) | Boolean | - | false |
| result | 接收接口返回并处理的数据(响应式) | - | - | null |
| doResult | 触发接口调用并更新result | Function(params) | - | null |
DoResult Params
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| params | 接口入参 | Object | - | {} |
源码
对于接口返回数据部分的处理带了些许的业务性
import { ref } from '@vue/composition-api'
import useLoading from './useLoading'
/**
* 场景:适用于接口调用或promise,hooks已封装的无需使用useAsync
* @param request 接口请求
* @param params 默认入参
* @param init 是否初始化调用
* @param isLoading 是否提供loading状态,不需要需将isLoading设置为false,否则loading状态将会保存在内存中,多次调用useAsync且loading状态共用一个时设置为false,使用外部loading状态
* @param preCallback 前置回调
* @param callback 获取数据后的数据处理回调
* @param successCallBack 成功回调
* @param errorCallBack 错误回调
*/
const defaultCallBack = (res = {}) => {
if (!res) return {}
const { data: { code, data, message } } = res || {}
return {
code,
data,
message
}
}
export default ({
request,
params = {},
init = true,
isLoading = true,
preCallback = null,
callback = defaultCallBack,
successCallBack = null,
errorCallBack = null
}) => {
// 记录结果
const result = ref(null)
// 是否开启默认loading
const {
loading,
checkLoading
} = isLoading ? useLoading() : {}
const doResult = async (searchParams = {}) => {
try {
// 默认loading处理
checkLoading && checkLoading(true)
// 前置回调
preCallback && preCallback()
const res = await request({...params, ...searchParams})
// 默认loading处理
checkLoading && checkLoading(false)
// 获取接口结果处理
const data = callback ? callback(res) : res
// 存储处理接口,以便外部获取
result.value = data.data ?? null
// 成功回调
successCallBack && successCallBack(data ? { ...data, params: {...params, ...searchParams} } : {})
return data ? { ...data, params: {...params, ...searchParams} } : {}
} catch (error) {
// 默认loading处理
checkLoading && checkLoading(false)
// 错误回调
errorCallBack && errorCallBack()
console.error(`error: ${error}`)
}
}
init && doResult(params)
return isLoading ? {
loading,
result,
doResult
} : {
result,
doResult
}
}
结语
得益于之前使用过react-hooks,编写起组合式API得心应手