Skip to content

@shivip/mp-core 工具参考

@shivip/mp-core 是公司内部面向 Vue 3、uni-app 和微信小程序项目的基础工具包。自 @hfyidu/api@1.0.1 起,其公开成员由 @hfyidu/api/core 转发。

ts
import { Future, ModuleUseCase, createSingleton } from '@hfyidu/api/core'

业务项目不需要、也不应额外安装 @shivip/mp-core

响应式模块用例

ModuleUseCase<Params, Response>

BaseUseCase 基础上增加 Vue 响应式执行状态和错误信息,适合页面初始化、详情加载、提交操作等单次异步任务。

公开状态:

成员类型功能
statusShallowRef<ModuleUseCaseStatus | null>当前运行状态
errorstring | null捕获后的错误信息
dataUseCaseResponse<Response>最近一次成功结果及更新时间
run(params)Promise<UseCaseResponse<Response>>执行用例
ModuleUseCase.from(fn)静态工厂将 Promise 函数快速包装成模块用例

ModuleUseCaseStatus 包含:

  • running:正在执行
  • success:执行成功
  • failure:执行失败
ts
import { ModuleUseCase, ModuleUseCaseStatus } from '@hfyidu/api/core'

interface Profile {
  id: number
  nickname: string
}

const profileCase = ModuleUseCase.from<null, Profile>(
  () =>
    new Promise((resolve, reject) => {
      uni.request({
        url: '/api/profile',
        success: ({ data }) => resolve(data as Profile),
        fail: reject,
      })
    }),
)

await profileCase.run(null)

if (profileCase.status.value === ModuleUseCaseStatus.success) {
  console.log(profileCase.data.data)
}

if (profileCase.status.value === ModuleUseCaseStatus.failure) {
  console.error(profileCase.error)
}

错误处理

ModuleUseCase 会捕获 onRun 抛出的错误并写入 error,状态改为 failure。调用方应观察状态或检查 error,不能只依赖外层 catch

useModuleData(useCase, defaultValue?)

ModuleUseCase.data.data 映射为 Vue Ref。当用例状态变为 success 时,Ref 自动更新。

ts
import { ModuleUseCase, useModuleData } from '@hfyidu/api/core'

const countCase = ModuleUseCase.from(async () => {
  const { data } = await uni.request<{ count: number }>({ url: '/api/count' })
  return data.count
})

const count = useModuleData(countCase, 0)

await countCase.run(null)
console.log(count.value)

分页用例

PaginatorUseCase<Params, ItemDto, Response>

统一管理分页请求、列表拼接和加载状态。子类只需实现 onRun(params)

成员功能
list当前累计列表
currentPage当前页码,初始为 1
pageSize每页数量,默认从过滤参数读取,否则为 15
refreshing是否正在刷新
hasMore是否可能还有下一页
loadedCount已加载条目数量
isEmpty首次加载后列表是否为空
error最近一次分页错误文本
extra响应中除 list 外的附加字段
refresh()清空状态并重新加载当前初始页
loadCurrentPage()尚未加载时触发刷新
loadNextPage()hasMore 为真时加载下一页
loadPrevPage()尝试加载上一页
insertAt(item, index?)插入一条本地数据
removeAt(index)删除一条本地数据

loadPrevPage() 的 1.0.1 已知限制

当前内联版本的 loadPrevPage() 包含恒真保护条件,调用后会直接返回,不会发起上一页请求。业务代码暂时不要依赖该方法;向前分页场景应等待内部工具包修复并由 @hfyidu/api 后续版本重新打包。

相关类型:

ts
type PagedResultDto<T> = {
  list: T[]
}

interface PagedQueryParams {
  currentPage: number
  pageSize: number
}

interface PaginationUseCaseConfigOption {
  currentPage: number
  pageSize: number
  makeParams(query: Record<string, any>): Record<string, any>
}

实际用法:

ts
import { PaginatorUseCase, type PagedResultDto } from '@hfyidu/api/core'

interface Message {
  id: number
  content: string
}

interface MessageQuery {
  page?: number
  limit?: number
  type?: string
}

class MessageListUseCase extends PaginatorUseCase<
  MessageQuery,
  Message,
  PagedResultDto<Message>
> {
  protected async onRun(params: MessageQuery) {
    const { data } = await uni.request<PagedResultDto<Message>>({
      url: '/api/messages',
      data: params,
    })
    return data
  }
}

// 分页用例必须只创建一次并复用,不能每次渲染都 new。
const messages = new MessageListUseCase()

await messages.refresh()
await messages.loadNextPage()

console.log(messages.list)
console.log(messages.refreshing.value, messages.hasMore.value)

usePaginatorData(useCase)

返回一个指向分页列表初始值的 Vue Ref

ts
import { usePaginatorData } from '@hfyidu/api/core'

const list = usePaginatorData(messages)
console.log(list.value)

使用建议

当前业务 SDK 的页面更适合直接读取 useCase.listrefreshing.valuehasMore.value。分页过程中 PaginatorUseCase 可能替换 list 数组引用,使用 usePaginatorData 前应确认它是否符合页面的响应式更新方式。

业务枚举

BaseEnumeration

用于定义同时拥有数值、代码和展示文本的类型安全业务枚举。

ts
import { BaseEnumeration } from '@hfyidu/api/core'

class PayStatus extends BaseEnumeration {
  static readonly pending = new PayStatus(0, 'pending', '待支付')
  static readonly paid = new PayStatus(1, 'paid', '已支付')
  static readonly closed = new PayStatus(2, 'closed', '已关闭')
}

PayStatus.find(1)              // PayStatus.paid
PayStatus.find('paid')         // PayStatus.paid
PayStatus.find('unknown')      // null
PayStatus.values()             // 全部枚举成员
PayStatus.keys()               // ['pending', 'paid', 'closed']
PayStatus.paid.toString()      // 'paid'
PayStatus.paid.valueOf()       // 1
PayStatus.paid.containOf(PayStatus.paid, PayStatus.closed) // true

单例与全局容器

createSingleton(Class)

使用 Proxy 保证同一个类无论 new 多少次都返回同一实例。

ts
import { createSingleton } from '@hfyidu/api/core'

class AudioManagerImpl {
  volume = 1
}

const AudioManager = createSingleton(AudioManagerImpl)

const first = new AudioManager()
const second = new AudioManager()

console.log(first === second) // true
first.volume = 0.5
console.log(second.volume) // 0.5

代理类还提供内部的 destroy() 能力以清除缓存,但该成员未体现在当前公开 TypeScript 返回类型中。业务代码通常应让单例随应用生命周期存在。

Container

globalThis 上按字符串或 Symbol 保存共享对象。

方法功能
Container.put(key, value)写入或覆盖全局值
Container.find<T>(key)查找全局值,不存在时返回 undefined
Container.secureGet(key, defaultValue?)不存在时写入默认值,再返回同一对象
ts
import { Container } from '@hfyidu/api/core'

const APP_CONFIG = Symbol.for('company.app.config')

const config = Container.secureGet(APP_CONFIG, {
  apiBaseUrl: 'https://api.example.com',
})

Container.put('currentTenant', 'tenant-a')
const tenant = Container.find<string>('currentTenant')

命名空间

Container 写入的是全局对象。key 应使用公司/项目命名空间或 Symbol.for(),避免与页面脚本、第三方库产生冲突;不要存放 token、密码等敏感信息。

createSingletonWithContainer(Class)

返回一个 getter,首次调用时创建对象,之后从 Container 返回同一实例。类需要提供稳定的静态 mark

ts
import { createSingletonWithContainer } from '@hfyidu/api/core'

class AnalyticsService {
  static mark = 'analytics-service'

  report(event: string) {
    console.log('report:', event)
  }
}

const useAnalytics = createSingletonWithContainer(AnalyticsService)

useAnalytics().report('page_view')
console.log(useAnalytics() === useAnalytics()) // true

异步工具

Future.delayed(duration, handler?)

延迟指定毫秒数,可选在延迟结束后执行函数并返回结果。

ts
import { Future } from '@hfyidu/api/core'

await Future.delayed(300)

const timestamp = await Future.delayed(300, () => Date.now())

如果回调抛错,返回的 Promise 会 reject。

Future.completer(handler, option?)

把带 successfail 回调的 uni-app API 转为 Promise。

ts
import { Future } from '@hfyidu/api/core'

const systemInfo = await Future.completer(uni.getSystemInfo)

await Future.completer(uni.setStorage, {
  key: 'theme',
  data: 'dark',
})

const value = await Future.completer(uni.getStorage, {
  key: 'theme',
})

option 中无需传入 successfailcomplete,工具会自动注入成功和失败回调。

通用类型

HigherOrderComponent<T>

描述“接收组件选项并返回组件选项”的高阶组件函数:

ts
import type { HigherOrderComponent } from '@hfyidu/api/core'

interface PageOptions {
  title: string
}

const withDefaultTitle: HigherOrderComponent<PageOptions> = option => ({
  title: option.title || '默认标题',
})

CallbackHandler

通用回调函数类型:

ts
import type { CallbackHandler } from '@hfyidu/api/core'

const callbacks: CallbackHandler[] = []
callbacks.push((payload: unknown) => console.log(payload))

内部工具包 · 未开源授权,仅限公司内部授权团队使用