Appearance
服务请求 createIO
TIP
如果你已经了解了《必知必会 —— 服务请求 - createIO》》的内容,就可以跳过这部分。
功能
- 将表操作、云函数调用、接口调用统一封装到 io 内,这样做的好处有
- 抛弃 io.invoke('cloudFunctionName') 形式调用云函数,改为 io.faas.cloudFunctionName,减少魔法值的使用
- 可以将云函数、api 归并到一起,避免在不同的地方进行对云函数、api 重复声明
- 增加 typescript declaration,可以更加便捷高效地调用 io
- 对 @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>
}