Hooks 与组合式能力
src/hooks/core 集中了跨页面复用的组合式能力。Hook 的价值不只是减少代码行数,而是把状态、生命周期、事件清理和项目约定封装成稳定入口,让页面聚焦业务字段和交互流程。
现有能力
| Hook | 主要职责 |
|---|---|
useAuth | 角色和按钮权限判断 |
useTable | 列表请求、分页、搜索、缓存和刷新 |
useTableColumns | 表格列显示、排序与偏好 |
useTableHeight | 根据布局计算表格可用高度 |
useColumnSearchHistory | 列搜索历史及用户隔离 |
useChart | ECharts 初始化、响应式调整和销毁 |
useTheme | 主题状态与主题能力 |
useThemeBootstrap | 应用启动时的主题初始化 |
useLayoutHeight | 页面布局高度计算 |
useLayoutDirection | 布局方向与响应式处理 |
useResponsiveMenuLayout | 菜单布局随视口变化 |
useDialogMotion | 对话框动效与状态协调 |
usePageFocusMode | 页面专注模式 |
useFastEnter | 快捷入口行为 |
useHeaderBar | 顶部栏组合逻辑 |
useQuickActionDialog | 快捷操作弹窗流程 |
useCommercial | 商业能力入口和版本相关判断 |
权限判断
useAuth 从当前用户 Store 读取 roles 和 buttons,并结合路由 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
useDialogMotion、usePageFocusMode、useFastEnter、useQuickActionDialog 等 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。
- 是否在卸载时清理监听器、定时器和实例。
- 是否避免把业务字段硬编码到通用层。
- 响应式值是否保持只读或通过方法修改。
- 异步任务是否处理重复执行和错误状态。
