Skip to content

034. 前端工具函数、Hook 与详情页流程

学习目标

这一节开始实现 AI 简历前端的基础工具和 Hook。

学完后,你应该能理解:

  • utilshooks 为什么要分开;
  • AI 模型配置为什么先写在前端;
  • 枚举工具如何生成 Select 选项;
  • DOM 导出 PDF 依赖什么能力;
  • 文件选择、图片预览和 base64 转换怎么配合;
  • 动态加载 UMD 静态库解决什么问题;
  • 为什么需要稳定回调 Hook;
  • 详情页为什么要先生成 ID 再编辑。

utilshooks 分层

前端项目里会建立两个目录。

目录放什么
utils和框架无关的工具函数
hooksReact 相关的 Hook 函数

utils 里的函数不应该依赖 React。

例如:

  • 文件处理;
  • 图片判断;
  • 字符串转换;
  • PDF 导出;
  • 枚举转换。

hooks 里才放和 React 状态、生命周期、表单、路由有关的逻辑。

这种分层能让工具函数更容易复用。

AI 模型配置

AI 简历里会有模型选择。

前端需要展示模型选项,例如模型名称和模型值。

更正式的做法是把模型配置放到数据库里:

  • 模型标识;
  • 显示名称;
  • 平台;
  • 是否启用;
  • 排序;
  • 价格或备注。

因为模型配置会经常读取,还可以配 Redis 缓存。

当前为了先推进功能,模型选项先写在前端配置文件里。

这不是最完整的企业级做法,但足够支撑学习项目。

枚举转 Select 选项

前端经常需要把枚举配置转成 Select 组件需要的选项。

Select 通常需要:

ts
[
  { label: '显示名称', value: '实际值' }
]

所以可以封装工具,把模型枚举、状态枚举等统一转换成 Select options。

这样组件里只关心渲染,不需要每次手写转换逻辑。

DOM 导出 PDF

AI 简历需要把页面里的某个 DOM 节点导出成 PDF。

项目会封装 exportElementToPDF 这类工具。

它接收一个 DOM 元素,然后调用第三方库生成 PDF。

这里要注意依赖问题。

有些 PDF 导出库依赖 GitHub 仓库里的包,国内环境安装时可能下载失败。

课程里使用重新打包后的包,避免安装时去拉取难下载的 GitHub 依赖。

文件选择工具

浏览器里选择文件,本质上还是通过 input type="file"

工具函数可以把这个动作封装起来,让业务代码只调用:

  • 选择单个文件;
  • 选择多个文件;
  • 限制文件类型;
  • 拿到 File 对象数组。

这和 Ant Design Upload 组件底层思路类似。

前端不能直接“打开本地文件系统”,只能通过用户选择文件拿到浏览器允许访问的 File 对象。

图片 base64 预览

用户选择图片后,前端拿到的是 File 对象。

这个对象不能直接塞给 <img>src

如果要本地预览,可以把 File 转成 base64。

在 AI 对话里,图片也可以用两种方式传给多模态模型:

  • 图片网络地址;
  • base64 图片内容。

当前聊天消息不长期保存,所以适合用 base64 方式把图片传给模型。

只支持图片消息

当前上传消息先只处理图片。

如果用户选择 PDF、TXT、Word 等文件,需要额外解析文件内容。

这些解析可以放在前端,也可以放在后端,但会引入更多依赖和复杂度。

所以当前阶段先用工具函数判断是否图片。

不是图片时,直接提示用户。

动态加载静态库

AI 简历前端会用到一些比较大的静态库,例如代码编辑器相关资源。

这些库不一定要在页面初始化时全部加载。

可以通过动态加载脚本的方式,在需要时再加载。

好处是:

  • 首屏更轻;
  • 不需要一次加载所有大文件;
  • 可以控制版本;
  • 更新静态库时只改配置和版本号;
  • 避免浏览器缓存旧文件。

这类资源通常以 UMD 形式通过 script 标签加载。

为什么要预加载编辑器资源

代码编辑器加载比较慢。

如果用户点击到代码编辑区域时才开始加载,体验会有明显等待。

所以可以在某些页面提前预加载编辑器资源。

这样用户切到代码编辑时,编辑器能更快显示。

这是一种性能体验优化。

稳定回调 Hook

React 里经常会遇到异步函数和闭包问题。

普通 useCallback 需要写依赖数组。

但有些函数希望引用稳定,同时内部又能拿到最新状态。

项目会封装类似 useStableCallback 的 Hook。

它的目标是:

  • 函数引用保持稳定;
  • 避免不必要的重新渲染;
  • 异步逻辑里尽量拿到最新值;
  • 减少依赖数组带来的反复重建。

这类 Hook 和一些组件库里的 useEvent 思路接近,但命名更贴近 React 的回调语义。

生成 ID 的 Hook

前端有时需要先拿到一个后端 ID。

项目会封装 useId 之类的 Hook,调用后端接口生成 ID。

后端生成 ID 的方式来自数据库,能更好保证唯一性。

这比前端自己随机生成 ID 更适合业务数据。

为什么新建页要先有 ID

有些业务场景需要先有主表 ID,再处理子数据。

例如报销单:

  • 先有报销单 ID;
  • 子表里的备用金、附件、明细都要挂到这个 ID 上。

AI 简历也类似。

用户新建简历时,可能先上传职业照或附件,这些文件需要知道属于哪份简历。

所以进入新建页时,可以先生成一个 ID,再围绕这个 ID 编辑。

new 路由和真实 ID

详情页可以先进入 new 状态。

页面发现 ID 是 new 时:

  1. 调用后端生成真实 ID;
  2. 重定向到带真实 ID 的详情页;
  3. 再加载或初始化这条记录。

这样页面后续一直围绕真实 ID 工作。

如果这个 ID 对应的数据已经保存过,就加载已有数据;如果没保存过,就按新建状态处理。

当前做法的缺陷

如果用户手动输入一个随便的 ID,系统可能也会当成新数据保存。

更严谨的做法是后端在新建时检查 ID 来源和合法性。

当前先把主流程跑通,后续可以再增强 ID 校验。

新建默认值

新建简历时,页面可以提供默认数据。

例如:

  • 默认模板源码;
  • 默认简历文案;
  • 默认主题色;
  • 默认表单字段。

这些默认值可以通过 getNewRecord 之类的函数返回。

这样用户进入新建页时不是空白,而是有一个可编辑起点。

详情页状态

详情页 Hook 会管理很多状态。

例如:

  • 当前是新建还是更新;
  • 是否正在初始化;
  • 是否已经加载过;
  • 当前数据对象;
  • 是否正在保存;
  • 请求错误信息;
  • Ant Design 表单对象;
  • 重新加载方法。

把这些逻辑封装成 Hook,页面组件会清爽很多。

这一节的重点

这一节搭的是 AI 简历前端地基。

你需要理解:

  • 普通工具放 utils
  • React 状态逻辑放 hooks
  • 文件、图片、PDF、动态脚本都先封装;
  • 复杂详情页要有统一新建和编辑流程;
  • 很多业务需要先生成 ID 再编辑。

后面真正写 AI 简历页面时,这些工具会不断被调用。

AI Agent 课程学习文档。