Skip to content

服务请求 createIO

TIP

如果你已经了解了《必知必会 —— 服务请求 - createIO》》的内容,就可以跳过这部分。

功能

  • 将表操作、云函数调用、接口调用统一封装到 io 内,这样做的好处有
    • 抛弃 io.invoke('cloudFunctionName') 形式调用云函数,改为 io.faas.cloudFunctionName,减少魔法值的使用
    • 可以将云函数、api 归并到一起,避免在不同的地方进行对云函数、api 重复声明
  • 增加 typescript declaration,可以更加便捷高效地调用 io
    • image
  • 对 @tanstack 简易封装,可以快捷调用 @tanstack useQuery
  • 新增 IOError,所有的 io operation 执行错误时都会抛出统一的错误对象,方便错误收集上报以及错误信息提示

使用方法

createIO

js
import {createIO} from '@ifanrx/dashboard'

const io = createIO({
  // 知晓云数据表配置
  table: {
    // key 将作为该表在 io 中的引用名字,value 为该表在知晓云内表名
    userprofile: '_userprofile',
    settings: 'settings',
    likesLog: 'likes_log',
  },
  /**
   * 后端接口配置
   * api 若不配置 method,默认会使用 GET 发送请求
   * 通过 io.api.apiKey 调用
   */
  api: {
    getUserprofile: {
      method: 'POST',
      url: '/xxx',
    },
  },
  /**
   * 云函数配置
   * 通过 io.faas.faasKey 调用
   */
  faas: {
    helloWorld: 'hello_world',
  },
})

export default io

io 基础功能

example

js
// 构建知晓云 table 查询条件
// 更多可参考:https://doc.minapp.com/js-sdk/schema/frag/query.html
const query = io.query
query.compare('key', '=', 'something')

// 构建知晓云用户实例
// 更多可参考:https://doc.minapp.com/js-sdk/user.html
const user = io.user
user.get(userID)

// 构建知晓云文件实例
// 更多可参考:https://doc.minapp.com/js-sdk/file/
const file = io.file
file.upload()

// 上传单个文件到知晓云
const uploadTask = io.uploadFile(file, options)
uploadTask.onProgressUpdate(e => {
  // https://doc.minapp.com/js-sdk/file/file.html#监听上传进度变化事件和中断上传任务-仅限微信小程序
  console.log(e)
})

// 上传多个文件到知晓云
const uploadTask = io.uploadFiles(files, options)

// 调用 tanstack useQuery
// 其中 queryKey 会自动填充,也可以在 queryOptions 内传入 queryKey 覆盖默认值
// io.useQuery 的响应以及 queryOptions 的详情可参考官网:https://tanstack.com/query/v5/docs/vue/reference/useQuery
const {data, isLoading} = io.useQuery(
  io.userprofile.find,
  {query: io.query},
  {staleTime: 24 * 60 * 60 * 1000}
)

// 调用 tanstack useMutation
// io.useMutation 没有任何封装,使用方法参考官网:https://tanstack.com/query/v5/docs/vue/reference/useMutation
const {mutate, data} = io.useMutation({
  mutationFn: () => Promise.resolve({}),
  mutationKey: ['key'],
})

知晓云数据表读写操作

example

js
// 根据 record id 获取对应记录
io.userprofile.get({id: 'xxx'})

// 根据查找条件找到记录列表
io.userprofile.find({query: io.query, limit: 20, offset: 0})

// 根据查找条件找到第一条满足条件的记录
io.userprofile.first({query: io.query})

// 已知记录 id,修改数据记录
io.userprofile.update({id: 'xxx', data: {name: 'xxx'}})

// 根据查找条件修改记录列表
io.userprofile.updateMany({query: io.query, data: {name: 'xxx'}})

// 已知记录 id,删除记录
io.userprofile.delete({id: 'xxx'})

// 根据查找条件删除记录列表
io.userprofile.deleteMany({query: io.query})

// 创建单条记录
io.userprofile.create({name: 'xxx'})

// 创建多条记录
io.userprofile.createMany([{name: '张三'}, {name: '李四'}])

// 获取满足当前查找条件的记录数量
io.userprofile.count({query: io.query})

get/find/first/update/updateMany/create 都拥有 plain 参数,当 plain 为 true 时,会将响应数据格式化,返回 response.data(find 操作是个另外,当 plain 为 true 时会返回 response.data.objects),当 plain 为 false 时,则会将完整的 response 返回。plain 默认值都是 true

云函数调用

云函数的调用是对知晓云 sdk 的 BaaS.invoke 进行封装,省略了调用 invoke 时对 functionName 参数的传入,让云函数的调用更加便捷,同时支持代码提示
此处云函数调用的封装对云函数的响应内容进行简单处理,当云函数响应的 code 为 0 时(此时云函数执行成功),会只返回云函数响应结构中的 data 字段,否则会抛出 IOError

类型定义

ts
faas: {
  [P in keyof T]: <
    TRequestPayload extends RequestPayload = RequestPayload,
    TSync extends boolean = true
  >(
    payload?: TRequestPayload,
    sync?: TSync
  ) => IOPromise<CloudFunctionResponse<TSync>>
}
  • payload 为请求云函数参数,sync 表示是否以同步的形式调用云函数
  • 若以同步的方式执行云函数,该方法会在云函数执行完成后返回
  • 若以异步的方式执行云函数,云函数会立刻返回 {status: "ok"},且无法获取云函数的执行结果
  • 同步与异步云函数有不同的超时时间,同步云函数为 5 秒,而异步云函数为 5 分钟
  • 默认使用同步的形式调用云函数
  • 更多内容请查阅文档 BaaS.invoke

example

js
const io = createIO({
  faas: {
    helloWorld: 'hello_world',
  },
})

// 默认使用同步的形式调用云函数
io.faas.helloWorld({message: 'hello world'})

// 使用异步的形式调用云函数
io.faas.helloWorld({message: 'hello world'}, false).then(console.log) // {status: 'ok'}

后端接口调用

一般来说,在微信小程序内请求后端接口都需要依赖知晓云 sdk 的 BaaS.request,使用用户在知晓云登录获取的 token 来完成鉴权
对后端接口请求的封装主要是省去在调用接口时传入请求 url 和 method 步骤,支持与云函数一致的调用方式,同样支持代码提示
需要注意的是,在调用 createIO 配置 api 时,需要传入该请求对应的 request method,不传默认使用 GET 请求。并且只能用来处理内部的 api,内部 api 有统一的响应结构

ts
{
  status: string // 取值有 ok | error
  error_code: integer, // 错误状态码
  error_msg: string // 底层的错误信息
  display_error_msg: string // 前端显示的错误信息
  data: {} // 正常数据
}

当 status 为 error 时,抛出 IOError,否则返回 data

类型定义

ts
declare type ApiMethod =
  | 'GET'
  | 'OPTIONS'
  | 'HEAD'
  | 'POST'
  | 'PUT'
  | 'DELETE'
  | 'TRACE'
  | 'CONNECT'

declare interface ApiConfig {
  method?: ApiMethod
  url: string
}

declare type Api = Record<string, ApiConfig>

declare interface ApiRequestOptions {
  header?: object
  dataType?: string
}

declare interface ApiOperation<T extends Api> {
  api: {
    [P in keyof T]: (
      payload?: RequestPayload,
      options?: ApiRequestOptions
    ) => IOPromise<ApiResponse>
  }
}

example

js
const io = createIO({
  api: {
    getUserprofile: {
      method: 'POST',
      url: '/xxxx',
    },
  },
})

io.api.getUserprofile({message: 'hello world'}, {header: {xxx: 'xxx'}})

useQuery/useMutation

io.useQuery/io.useMutation 是对 @tanstack 的 useQuery/useMutation 进行二次封装

主要调整了 queryFn 参数的传入方式,并用 uuid 设置了默认的 queryKey。如有需要也可以传入自定义的 queryKey

其他保持不变,queryOptions 可参考官网:useQuery

io.useMutation 会将 tanstack.useMutation 原封不动抛出,具体请参照官方文档

example

js
const {isFetching, data: log} = io.useQuery(
  () => {
    return io.log.get(userId.value)
  },
  {
    enabled: enabledRequestLog,
  }
)

useRequest

如果有缓存请求的需要,可调用 io.useRequest

io.useRequest 是对 vue-query 的 useQuery 二次封装

参数与 useQuery 有很大不同,应特别留意

为了避免在使用时使用了重复/错误的 queryKey,io.useRequest 会根据 requestFn 的表名、查询方法和参数自动填充 queryKey

io.useRequest 的响应以及 queryOptions 的详情可参考官网:https://tanstack.com/query/v5/docs/react/reference/useQuery

实现上在每个 io operation 下面都会有一个 queryKey,包括数据表读写请求、云函数、api,比如说:

js
console.log(io.userprofile.find.queryKey) // ['userprofile', 'find']
console.log(io.settings.count.queryKey) // ['settings', 'count']
console.log(io.faas.helloWorld.queryKey) // ['faas', 'helloWorld']
console.log(io.api.getUserprofile.queryKey) // ['api', 'getUserprofile']

通常情况下不需要主动去使用 queryKey,在调用 io.useRequest 时,会自动取该 operation 的 queryKey,拼接请求参数生成真正的 queryKey,如下:

js
// 函数定义
useRequest(request, requestParams, queryOptions = {}) {
  const {queryKey} = request
  if (isEmpty(queryKey)) {
    throw new TypeError('request fn 只能使用 io 内的方法')
  }

  const key = queryKey.concat(requestParams)
  const queryResult = useQuery(() => request(requestParams), {
    queryKey: key,
    ...queryOptions,
  })

  return Object.assign(queryResult, {queryKey: key})
}

特别地,io.useRequest 只适用于 io 内的方法,其他场景下若需要用到 useQuery,需使用的 io.useQuery

example

js
// 基本用法
const {queryKey, data} = io.useRequest(io.faas.helloWorld, {
  message: 'hello world',
})
console.log(queryKey) //['faas', 'helloWorld', {message: 'hello world'}]

IOError

io 内所有方法执行出错时,都会抛出 IOError,方便统一错误处理以及错误上报

类型定义

js
class IOError extends Error {
  code: number
  displayMessage: string
  info: Record<string, any>
}