Skip to content

Hooks 与组合式能力

src/hooks/core 集中了跨页面复用的组合式能力。Hook 的价值不只是减少代码行数,而是把状态、生命周期、事件清理和项目约定封装成稳定入口,让页面聚焦业务字段和交互流程。

现有能力

Hook主要职责
useAuth角色和按钮权限判断
useTable列表请求、分页、搜索、缓存和刷新
useTableColumns表格列显示、排序与偏好
useTableHeight根据布局计算表格可用高度
useColumnSearchHistory列搜索历史及用户隔离
useChartECharts 初始化、响应式调整和销毁
useTheme主题状态与主题能力
useThemeBootstrap应用启动时的主题初始化
useLayoutHeight页面布局高度计算
useLayoutDirection布局方向与响应式处理
useResponsiveMenuLayout菜单布局随视口变化
useDialogMotion对话框动效与状态协调
usePageFocusMode页面专注模式
useFastEnter快捷入口行为
useHeaderBar顶部栏组合逻辑
useQuickActionDialog快捷操作弹窗流程
useCommercial商业能力入口和版本相关判断

权限判断

useAuth 从当前用户 Store 读取 rolesbuttons,并结合路由 permissionPrefix 解析权限码:

ts
const { hasAuth, hasRole, hasAnyAuth } = useAuth()

const canCreate = hasAuth('create')
const canManage = hasRole('R_ADMIN')
const canEditOrDelete = hasAnyAuth(['edit', 'delete'])

模板中也可以使用项目指令。Hook 更适合需要参与计算、请求或流程分支的权限判断,指令更适合单个元素的展示控制。

前端判断不能代替后端鉴权,详见路由与权限

表格 Hook

useTable 是项目列表页的核心能力,提供:

  • API 请求和响应适配
  • 分页参数同步
  • 搜索与防抖
  • loading、error 和空数据状态
  • 可选请求缓存
  • 新增、编辑、删除后的刷新策略
  • 表格列配置协作
  • 移动端分页适配

基础示例:

ts
const {
  data,
  loading,
  pagination,
  searchParams,
  getData,
  resetSearch,
  refreshUpdate
} = useTable({
  core: {
    apiFn: fetchCustomerList,
    apiParams: {
      current: 1,
      size: 20,
      keyword: ''
    },
    columnsFactory: createCustomerColumns
  },
  performance: {
    debounceTime: 300,
    enableCache: false
  }
})

业务页面负责字段、接口和弹窗流程,Hook 负责通用列表状态。完整设计见表格与列表体系

图表 Hook

图表页面应优先使用 useChart 管理实例,而不是在每个页面重复处理:

  • 容器挂载后初始化
  • 数据或主题变化后更新
  • 视口变化时 resize
  • 组件卸载时销毁实例

图表数据转换仍应留在业务模块或专用适配函数中,不要把业务指标含义写入通用 Hook。

主题与布局 Hooks

主题和布局相关 Hook 共同处理响应式界面:

text
useThemeBootstrap -> 启动时恢复主题
useTheme          -> 页面读取和切换主题能力
useLayoutHeight   -> 计算内容区高度
useLayoutDirection -> 横向/纵向布局行为
useResponsiveMenuLayout -> 小屏菜单策略

页面需要跟随框架布局变化时,应复用这些入口,避免自己读取零散 CSS 变量或绑定重复的 resize 监听器。

交互类 Hooks

useDialogMotionusePageFocusModeuseFastEnteruseQuickActionDialog 等 Hook 把交互状态和生命周期集中管理。使用时需要保持边界:

  • 页面决定何时触发和展示什么业务内容。
  • Hook 负责通用状态、动画、快捷键和清理。
  • 全局监听必须在卸载时移除。
  • 同一交互不要同时存在页面实现和 Hook 实现两套状态源。

何时新增 Hook

适合新增 Hook:

  • 同一段响应式逻辑在多个页面重复出现。
  • 逻辑包含生命周期、监听器或资源清理。
  • 可以通过清晰参数和返回值表达能力。
  • 它不依赖某个页面的 DOM 结构和具体业务字段。

不适合新增 Hook:

  • 只在一个页面使用的十几行简单逻辑。
  • 只是给一个函数换名字。
  • 内部直接操作大量页面组件引用。
  • 同时处理请求、弹窗、路由和多个不相关业务域。

推荐结构

ts
interface UsePollingOptions {
  interval?: number
  immediate?: boolean
}

export function usePolling(task: () => Promise<void>, options: UsePollingOptions = {}) {
  const { interval = 30_000, immediate = true } = options
  const running = ref(false)
  let timer: ReturnType<typeof setInterval> | undefined

  const execute = async () => {
    if (running.value) return
    running.value = true
    try {
      await task()
    } finally {
      running.value = false
    }
  }

  onMounted(() => {
    if (immediate) void execute()
    timer = setInterval(() => void execute(), interval)
  })

  onUnmounted(() => {
    if (timer) clearInterval(timer)
  })

  return { running: readonly(running), execute }
}

设计要点:参数有默认值、返回值稳定、异步状态可观察、资源能够清理。

使用检查

  • 页面是否已经存在同类 Hook。
  • Hook 是否暴露最小且稳定的 API。
  • 是否在卸载时清理监听器、定时器和实例。
  • 是否避免把业务字段硬编码到通用层。
  • 响应式值是否保持只读或通过方法修改。
  • 异步任务是否处理重复执行和错误状态。

页面级开发方式见扩展开发指南,可复用视图元素见通用组件

根据 MIT 许可证发布