vue3.0组合式API-useAsync

1,942 阅读5分钟

更多文章

前言

承接上篇文章,这里分享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是否开启loadingBoolean-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触发接口调用并更新resultFunction(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得心应手