从“加载模型”界面到端侧推理:拆解一个 React + WebGPU 大模型 Demo
这个项目还没有真正把大模型跑起来但它已经把端侧 AI 应用最重要的 UI 骨架搭好了能力检测、加载状态、错误出口、下载进度和输入交互。本文从现有代码出发梳理它已经完成的部分以及接入真实推理链路还差什么。每次调用云端大模型 API输入内容都要离开浏览器还会受到网络、额度和服务可用性的影响。另一条路线是把模型和推理放在用户设备上模型首次下载到浏览器缓存后续由本机 GPU 或 CPU 执行用户的提示词不必为了推理而发送到业务服务器。deepseek-r1-webgpu正是在探索这条路线。项目的页面描述指向DeepSeek-R1-Distill-Qwen-1.5B-ONNX、Transformers.js、ONNX Runtime Web 和 WebGPU代码则先实现了一个 React TypeScript Vite Tailwind CSS 的交互骨架。先说明本文的边界当前仓库尚未安装或调用huggingface/transformers、onnxruntime-web也没有模型下载与文本生成代码。点击Load Model只会把status从0改为loading。因此它是一个很适合学习前端状态设计的端侧 AI 页面原型而不是已经可推理的完整应用。项目先解决了什么问题入口src/main.tsx使用 React 19 的createRoot挂载App并由 Vite 插件启用 React 与 Tailwind CSS。页面主逻辑在src/App.tsx可先把它看成一台由状态驱动的界面机器const [inputValue, setInputValue] useState(); const [status, setStatus] useState(0); const [error, setError] useState(null); const [loadingMessage, setLoadingMessage] useState(开始加载); const [progressItems, setProgressItems] useState([]); const isWebGpuAvailable !!navigator.gpu;这些变量对应的并不是某几个散落的 DOM 节点而是页面的业务状态状态当前用途应驱动的界面isWebGpuAvailable检测浏览器是否暴露 WebGPU API主界面或“不支持 WebGPU”提示status初始、加载、就绪等模型生命周期按钮可用性、输入框可用性、进度区error失败原因错误文案与重试入口progressItems模型文件下载事件多条下载进度条inputValue用户正在输入的问题受控textarea与 Enter 发送这种写法的价值在于界面不再由“找到某个元素再改样式”的命令式 DOM 操作拼出来。状态变化后React 重新执行组件函数再根据条件渲染对应 UIdisabled{status ! 0 || error ! null} {status loading LoadingPanel /} textarea disabled{status ! ready} /Load Model只有在初始状态且没有错误时可点击模型未就绪时文本框保持禁用。即便真实模型逻辑还没接入这两个约束已经避免了“模型还没加载完用户就发送请求”的无效交互。第一层降级先判断 WebGPU 能力项目使用下面的判断const isWebGpuAvailable !!navigator.gpu;navigator.gpu是浏览器提供 WebGPU 接口时暴露的入口。双重否定把可能为undefined的值转换成清晰的布尔值随后组件在支持与不支持之间选择不同视图return isWebGpuAvailable ? MainPage / : div您的浏览器还不支持 WebGPU/div;这是合理的第一步不过它只回答“浏览器有没有 API”并不保证设备一定能完成模型推理。真实接入时还应调用navigator.gpu.requestAdapter()并处理拿不到 adapter 或设备创建失败的情况。错误状态应该把这些失败呈现给用户而不是只停留在控制台。这里也有一个产品取舍如果应用目标必须依赖 GPU明确告知不支持是最诚实的方案如果希望覆盖更多设备可以把 WebGPU 作为优先后端再提供 WASM/CPU 作为性能较低的回退。是否回退取决于模型体积、内存占用和可接受的响应时间不是一条单纯的 API 判断。进度条为何要拆成组件src/components/Progress.tsx把一条下载项的展示抽成组件Progress key{index} text{text} percentage{percentage} total{total} /组件内部把百分比映射为宽度并将字节数格式化成用户可读的单位percentage ?? 0; div style{{ width: ${percentage}% }} classNamebg-blue-400 h-2 /这对应模型加载的真实形态一个“模型”通常不是一份孤立文件配置、分词器、权重分片和 ONNX 图都可能分别下载。父组件持有下载项数组子组件只按 props 渲染最适合表达这种一对多关系。后续对接运行库时进度回调应只更新父组件的数组。下面是与当前页面结构匹配的示意代码并非仓库已有实现type ProgressItem { text: string; percentage?: number; total?: number; }; function updateProgress(item: ProgressItem) { setProgressItems((previous) { const index previous.findIndex((entry) entry.text item.text); if (index -1) return [...previous, item]; return previous.map((entry, currentIndex) currentIndex index ? { ...entry, ...item } : entry, ); }); }注意这里使用函数式更新。下载事件可能连续到达下一次数组取决于前一次数组时用setProgressItems(previous ...)能避免闭包拿到旧值。渲染列表的key也建议使用稳定的文件名或路径而不是数组下标下载项顺序变化时稳定 key 才能让 React 正确复用节点。受控输入与发送时机底部输入框采用受控组件textarea value{inputValue} disabled{status ! ready} onInput{(event) { const target event.target as HTMLTextAreaElement; setInputValue(target.value); }} onKeyDown{(event) { if (inputValue.length 0 event.key Enter !event.shiftKey) { event.preventDefault(); onEnter(); } }} /它的行为很符合聊天产品普通 Enter 发送Shift Enter 保留换行。代码还在事件中通过类型断言把event.target视为HTMLTextAreaElement这样 TypeScript 才能安全读取value。当前onEnter只将状态设置为loading没有真正把输入交给模型。接上推理后建议把“模型正在下载”和“模型正在生成”拆开而不是共用一个模糊的loading。例如type ModelStatus idle | downloading | ready | generating | error;用联合类型代替字符串0可以让编辑器检查拼写也能让状态分支更易读。生成期间禁用重复发送按钮结束或失败后回到ready聊天体验会更完整。真正的端侧推理链路长什么样要让“Load Model”不再只是切换页面状态需要把页面状态与模型运行时连接起来。结合项目已有的模型链接最小闭环可以拆为六步检查 WebGPU 并请求 adapter/device失败时进入可见的错误或回退状态。首次点击后加载模型配置、分词器和 ONNX 权重利用运行库的进度回调更新progressItems。创建可复用的模型或pipeline实例完成后将状态设为ready。用户提交文本后将 prompt 经过 tokenizer 编码为 token。推理后端在浏览器的本机设备上逐步生成 token生成过程可持续刷新输出区。解码为文本并追加到会话缓存命中时后续访问可跳过或减少下载。“端侧”不等于模型文件永远不接触网络。首次加载仍需从模型托管服务获取资源除非它们已经在浏览器缓存中真正的边界是完成加载后推理提示词和生成计算可留在本机执行。生产应用仍需审视模型来源、资源完整性、缓存策略以及浏览器存储配额。当前工程还需要补的两处基础工作执行npm run build时当前项目会在 TypeScript 检查阶段停止。原因包括App.tsx中setError、setLoadingMessage、setProgressItems还没有被使用Progress.tsx的 props 没有类型并且该文件保留了未使用的zod导入。项目的tsconfig.app.json启用了strict、noUnusedLocals和noUnusedParameters所以这些教学阶段的占位代码会被严格检查出来。这是一个很好的提醒TypeScript 不是等所有功能做完才开启的装饰。把状态模型和组件 props 定义清楚能让“UI 正在等待什么、谁可以修改这个数据”在编译期就被约束。例如进度条可以先定义一个明确的契约type ProgressProps { text: string; percentage?: number; total?: number; }; function Progress({ text, percentage 0, total }: ProgressProps) { // render... }同时未接入的 setter 应暂时移除或在真正的模型加载、报错和回调逻辑接入后再使用。这样npm run build才能成为可信的交付检查。小结这个项目最值得学习的不是“已经在浏览器跑通 DeepSeek-R1”而是它为端侧 AI 建立了正确的前端分层用 WebGPU 能力检测决定入口体验。用 React 状态驱动加载、就绪、错误与交互禁用。用父组件数组和Progress子组件表达多个文件的下载反馈。用受控输入把键盘行为收束为可预测的状态更新。用 TypeScript 严格检查为真实运行时接入留出边界。模型运行时接入以后UI 层不需要推倒重来只需让运行库的下载、加载和生成事件进入现有的状态机。对学习端侧大模型应用而言这正是最可靠的起点先把用户能看见、能恢复、能理解的状态做对再把模型接进去。

相关新闻

电商视频互动转商品评价的技术实现与优化

电商视频互动转商品评价的技术实现与优化

1. 项目背景与核心思路最近在做一个很有意思的功能改造——把视频平台的点赞、收藏、评论行为映射到电商场景中。简单说,就是让用户在观看商品展示视频时,这些互动行为能直接转化为对商品的评价数据。这个需求源于一个很实际的场景:现在很多电…

2026/7/31 5:58:37 阅读更多 →
Spark的分区数、Executor数、Task数之间的逻辑关系

Spark的分区数、Executor数、Task数之间的逻辑关系

本文从Apache Spark 的底层原理和架构来剖析:分区数、Executor 数、Task 数分别由谁决定,以及它们之间的逻辑关系。算作是一个小总结。 为了方便理解,我们先建立两个核心铁律: 一个 Task 处理一个 Partition(分区)。 Task 的并行执行数量,取决于 Executor 可用的 CPU 核…

2026/7/31 5:58:49 阅读更多 →
高速信号眼图核心参数解析:从幅度、时序到系统诊断

高速信号眼图核心参数解析:从幅度、时序到系统诊断

1. 项目概述:从“看热闹”到“看门道”眼图,对于很多刚接触高速数字信号或通信领域的朋友来说,可能是个既熟悉又陌生的概念。说熟悉,是因为但凡涉及信号完整性测试,这个词的出现频率极高;说陌生&#xff0c…

2026/7/31 5:59:08 阅读更多 →

最新新闻

算法练习4

算法练习4

今日完成 4 道二叉树高频题,覆盖两类核心模型:BFS:使用队列,按层处理节点。 递归:先获得左右子树结果,再合并为当前节点结果。1. 二叉树的最大深度题目:给定二叉树根节点,求从根节点…

2026/7/31 5:58:54 阅读更多 →
解决MySQL安装缺失VCRUNTIME140_1.dll:VC++运行库依赖详解与实战

解决MySQL安装缺失VCRUNTIME140_1.dll:VC++运行库依赖详解与实战

1. 问题现象与根源剖析 如果你在Windows系统上尝试安装MySQL,尤其是从官方下载的独立安装包(比如 .msi 安装程序),大概率会遇到一个拦路虎:安装程序弹出一个错误提示框,内容大概是“无法启动此程序&…

2026/7/31 5:58:54 阅读更多 →
PVID与Native VLAN核心区别:从原理到实战排错详解

PVID与Native VLAN核心区别:从原理到实战排错详解

1. 从一次“诡异”的通信故障说起那天下午,运维同事急匆匆地跑过来,说新上线的视频会议系统和内网办公区之间通信时好时坏,数据包像幽灵一样时隐时现。我们检查了防火墙策略、路由表,甚至怀疑过网线质量,折腾了大半天&…

2026/7/31 5:58:54 阅读更多 →
Python打包分发工具setuptools、pip与wheel核心原理与实战指南

Python打包分发工具setuptools、pip与wheel核心原理与实战指南

1. 项目概述:为什么我们需要打包分发工具? 如果你写过Python脚本,大概率用过 pip install 命令来安装别人写好的库。你有没有想过,自己写的代码,如何能像 requests 或 numpy 一样,让别人也能通过一句…

2026/7/31 5:58:54 阅读更多 →
x86汇编移位与循环指令:从比特操作到性能优化的核心技术

x86汇编移位与循环指令:从比特操作到性能优化的核心技术

1. 指令集里的“搬运工”:为什么我们需要移位与循环指令?在x86汇编的世界里,数据处理是核心任务。我们经常需要把数据在寄存器或内存中“挪来挪去”,但有时不仅仅是简单的复制粘贴,而是需要一种更精细、更结构化的“搬…

2026/7/31 5:58:53 阅读更多 →
基于51单片机的电子密码锁设计与实现:从原理到实践

基于51单片机的电子密码锁设计与实现:从原理到实践

1. 项目概述:从零打造一个硬核的电子密码锁最近在整理工作室的旧物,翻出来一堆大学时期玩剩下的STC89C52RC芯片和一堆共阳数码管,看着它们,一个念头就冒了出来:用这些最基础的元件,能不能做一个既实用又有趣…

2026/7/31 5:57:53 阅读更多 →

日新闻

物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:34 阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:34 阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:34 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻