从原生事件到 React 组件化用状态驱动模型加载进度 UI在浏览器端运行 AI 模型时页面通常会经历“点击加载 → 显示加载提示 → 展示多个文件进度 → 成功或报错”的过程。这个小型 WebGPU Demo 目前还没有接入真正的模型下载和推理但已经完成了一个很典型的 React UI 骨架用状态管理界面、用map渲染进度列表、把每一行进度抽成独立组件。本文基于当前代码梳理运行未验证。1. 原生事件onclick与addEventListener原生 HTML 可直接在标签上写事件buttononclickconsole.log(点击了)按钮/button这种方式叫内联事件。问题是 HTML 和 JavaScript 耦合在一起逻辑复杂后难维护。另一种是 DOM APIconstbuttondocument.getElementById(btn)button.addEventListener(click,(){console.log(点击了)})addEventListener支持同一元素、同一事件绑定多个监听器也方便拆分逻辑因此原生开发中更常用。2. React 为什么不用手动找 DOMReact 更常见的写法是button onClick{() setStatus(loading)} Load Model /button这里不需要getElementById也不用手动操作 DOM。点击后只修改状态setStatus(loading)React 会根据新状态重新计算页面应该长什么样。可以把两种思路对比为原生 DOM点击 → 找元素 → 手动修改页面 React 点击 → 修改状态 → React 更新页面React 的onClick使用的是 React 的合成事件机制。日常开发只需要按照 JSX 事件属性的方式使用即可。3. 用状态表达模型加载过程组件中定义了多类状态const [status, setStatus] useState(null) const [error, setError] useState(null) const [loadingMessage, setLoadingMessage] useState(开始加载)它们分别表示status当前页面阶段例如未开始、加载中、完成error发生异常时的错误内容loadingMessage显示给用户的加载提示。当状态变为loading时才显示进度区域{status loading ( div p{loadingMessage}/p /div )}是 JavaScript 的短路逻辑左侧为真才返回右侧 JSX左侧为假React 不渲染该内容。这是 React 条件渲染的常见写法。4. 禁用按钮避免重复加载当前按钮使用状态控制disabledbutton disabled{status ! null || error ! null} onClick{() setStatus(loading)} Load Model /button含义是一旦开始加载或已经发生错误就不能再点“加载模型”。这不是单纯的样式控制而是浏览器按钮的真实禁用能力。Tailwind 还能为禁用状态添加样式classNamedisabled:cursor-not-allowed5. 用map把进度数据变成页面进度不是单个值而是一组文件const [progressItem] useState([ { text: model.onnx, percentage: 0, total: 123456 }, { text: model2.onnx, percentage: 10, total: 123456 }, ])使用map可以将每项数据转换成一个组件{progressItem.map(({ text, percentage, total }) ( Progress key{text} text{text} percentage{percentage} total{total} / ))}核心过程进度数据数组 → map 加工 → Progress 组件数组 → React 渲染列表其中key{text}用于标识每一项。React 依赖稳定且唯一的key来判断列表更新时“哪一项对应哪一项”。6. 为什么要抽取 Progress 组件单个文件进度的展示被拆到Progressconst Progress ({ text, percentage, total }) { return ( div p{text}/p p{percentage}%/p p{total}/p /div ) }父组件负责保存状态响应用户点击遍历进度数据。Progress组件负责接收text、percentage、total展示一条文件进度。这就是组件化的价值父组件管理业务和数据子组件负责独立 UI。未来想把文本换成真正的进度条只需要修改Progress组件。7. 当前 Demo 还差哪些关键步骤当前代码已经能表达加载 UI 状态但真正的端侧模型加载尚未接入。后续可以逐步补充点击后下载或初始化模型下载事件中更新progressItem加载成功后将status改为ready失败时调用setError(...)为 Props 声明 TypeScript 类型。此外列表渲染应保留稳定key并将mx-w-[510px]修正为max-w-[510px]。总结这个页面体现了 React 的核心开发模式不直接操作 DOM而是维护状态再让 UI 根据状态自动变化。原生事件推荐addEventListenerReact 中使用onClickuseState负责保存加载状态实现条件渲染map把数组转换为 JSX 列表Props 将数据从父组件传给可复用的Progress子组件。