公司动态
在浏览器跑通 15 亿参数大模型:我用 React + WebGPU 复刻了 DeepSeek-R1
端侧模型不是未来是现在。把大模型塞进浏览器顺便学一波 React Tailwind 的最佳实践。你有没有想过打开一个网页就能直接跑通 1.5B 参数的推理模型而且数据完全留在本地不用买显卡不用配环境甚至断网都能用最近我搞了个“简历级”项目 ——DeepSeek-R1 WebGPU把 DeepSeek-R1 的 1.5B 蒸馏版模型搬到了浏览器里全程使用 WebGPU 加速前端用 React TypeScript Tailwind。整个过程踩了不少坑但也沉淀了一套清晰可复用的技术方案。今天不聊虚的直接上硬货。我会从技术选型 → 项目搭建 → 核心实现一步步拆解带你看看端侧模型 现代前端技术栈到底怎么落地。一、为什么要在浏览器里跑大模型你可能已经用过 ChatGPT、DeepSeek 的 API它们方便但有三个硬伤贵按 token 计费调多了肉疼不安全每一次请求都会把上下文送到远端敏感数据不敢喂依赖网络没网就彻底歇菜。而端侧模型On-Device Model直接跑在用户设备上 —— 手机、汽车、浏览器不联网也能用。尤其是一些小参数模型1B~7B通过蒸馏和量化已经能在端侧胜任Agent 任务划分、文本摘要、代码补全等场景。端侧模型的终极形态是用户点开网页模型自动下载推理全程在本地用完即走不留痕迹。浏览器作为最轻量的分发平台搭配WebGPU新一代图形和计算 API能充分利用 GPU 进行张量运算推理速度甚至能追上 Python 本地部署。这就是我们做这个项目的底层逻辑。二、技术栈为什么是 React TS Tailwind项目立项时技术选型我几乎没有犹豫React TypeScriptAI 领域开源项目比如 Hugging Face 的 transformers.js几乎清一色用 React生态最成熟大型项目维护友好Tailwind CSS原子类 CSS 框架再也不用写一堆难维护的样式文件特别适合“自然语义编程” —— 你只需要在 className 里堆砌语义明确的工具类界面就能快速成形ESLint大公司标配保证代码风格一致减少团队协作时的无效争吵。React 比 Vue 入门稍陡但一旦你掌握了“函数式组件 Hooks”的心智模型你会发现它极其适合数据驱动 UI的场景 —— 这不正是 AI 应用最常见的形态吗三、从零搭一个 React TS Tailwind 项目我们用 Vite 快速初始化npm create vitelatest deepseek-webgpu -- --template react-ts cd deepseek-webgpu npm install接着安装 Tailwind CSS 及其 Vite 插件npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p配置tailwind.config.jsexport default { content: [./index.html, ./src/**/*.{js,ts,jsx,tsx}], theme: { extend: {} }, plugins: [], }在src/index.css中引入 Tailwind 基础指令tailwind base; tailwind components; tailwind utilities;最后配置 ESLint这里不展开直接使用 Vite 自带的插件即可。四、揭开 React 组件的“积木”本质React 的核心思想就是组件化—— 把 HTML、CSS、JS 封装成一个独立的功能单元像搭积木一样拼出页面。在 React 中组件就是一个函数这个函数返回 JSXJavaScript XML 的混合语法。你可以在函数体里写任何 JS 逻辑最后 return 一段 HTML 结构。import { useState, useEffect } from react; function App() { // 数据状态响应式 const [status, setStatus] useStatestring | null(null); const [error, setError] useStatestring | null(null); // 组件挂载后的副作用 useEffect(() { console.log(组件已挂载可以在这里初始化模型加载); }, []); return ( div classNameflex flex-col h-screen mx-auto items-center justify-end {/* 界面内容 */} /div ); }有几个要点值得新手注意useState返回一个数组[state, setState]setState会触发组件重新渲染这就是“响应式”的根基useEffect用来处理副作用比如加载模型、订阅事件第二个参数[]表示只在挂载时执行一次JSX 中的className为什么不是class因为class是 JS 的保留关键字React 只好换个名字。数据驱动 UI 的本质是你只管修改状态界面会像川剧变脸一样自动更新完全不需要手动操作 DOM。 深入理解响应式数据状态上面我们用useState定义了状态但很多同学不清楚它为什么能让 UI 自动变化。这里用一个完整的链路来解释useState返回一个数组第一个是当前值第二个是“更新函数”也叫 setter。当你调用setStatus(ready)时React 会标记这个组件需要重新渲染。重新渲染 整个组件函数重新执行函数再次执行时useState会返回新的status值现在是ready然后 JSX 里所有依赖status的部分都会用新值重新计算。React 通过“虚拟 DOM”对比新旧 UI 树只更新变化的部分比如你只改了一个h1的文字React 不会重绘整个页面只会修改那个文本节点。这就是“数据驱动 UI”的完整闭环 —— 你不需要写document.getElementById只需要关心“在什么状态下UI 应该长什么样”剩下的事 React 全包了。一个常见的误区很多人以为setStatus是同步修改状态然后立刻就能拿到新值setStatus(ready); console.log(status); // 这里还是旧值null因为状态更新是异步批处理的如果你需要在状态变化后执行逻辑要用useEffect监听它useEffect(() { if (status ready) { console.log(模型已就绪可以开始推理); } }, [status]); // 依赖 status当它变化时执行这个useEffect就是“副作用”的钩子适合处理状态变化后需要做的事情比如加载模型、存储数据、发起请求等。掌握了这套模式你就能自如地处理复杂的交互逻辑。五、Tailwind 的“自然语义”有多爽看一段我们项目里的 UI 代码div classNameflex flex-col h-screen mx-auto items-center justify-end text-gray-800 bg-white h1 classNametext-4xl font-bold mb-1Deepseek R1 WebGPU/h1 p classNamemax-w-[510px] Your are about to load a classNamefont-medium underlineDeepSeek-R1-Distill-Qwen-1.5B/a /p /div这些类名几乎不需要查文档读出来就是样式语义flex flex-col→ 弹性容器纵向排列h-screen→ 高度占满视口mx-auto→ 水平居中text-4xl font-bold→ 大号粗体文字max-w-[510px]→ 自定义最大宽度[]语法用于任意值这就是“自然语义编程”—— 不用写一行 CSS不用纠结类名命名直接在标签上组合工具类UI 就能快速成型。尤其适合 AI 项目因为我们的精力应该放在模型逻辑上而不是样式细节。六、如何检测 WebGPU 是否可用WebGPU 目前只在 Chrome113、Edge 等现代浏览器中支持所以我们第一步要检测const IS_WEBGPU_AVALABLE !!navigator.gpu;这里navigator.gpu可能的值是如果浏览器支持 WebGPU它是一个对象例如{ adapter: ... }如果不支持它是undefined。我们需要一个布尔值true/false来做条件判断但直接写if (navigator.gpu)也能工作因为if会做隐式类型转换。那为什么还要加!!呢两个原因显式转换意图清晰!!是 JavaScript 中将任意值转为布尔值的标准写法一个感叹号!先取反转为布尔并翻转第二个感叹号!再取反一次得到原始值的布尔等价。这样写比Boolean(navigator.gpu)更简洁而且阅代码的人一眼就知道“我在转布尔”。避免隐式转换的坑虽然if (navigator.gpu)也可以但如果这个值不是undefined而是0、null、等隐式转换规则不同容易出错。显式转换让代码行为完全可控。检测之后我们利用三元表达式做条件渲染return IS_WEBGPU_AVALABLE ? ( div主界面/div ) : ( div您的浏览器不支持 WebGPU请使用最新版 Chrome 或 Edge。/div );别忘了给用户一个清晰的降级方案这是用户体验的基本素养。七、模型加载中的状态管理加载一个 1.5B 的模型ONNX 格式需要时间期间我们要反馈进度、处理错误。于是我们设计了几个状态const [loadingMessage, setLoadingMessage] useStatestring(); const [progressItems, setProgressItems] useStateArray{file: string, progress: number, total: number}([]); const [error, setError] useStatestring | null(null);progressItems可以展示每个文件的下载进度配合useEffect监听加载事件实现实时更新进度条这里不展开后续会有专门的文章讲 transformers.js 的集成。错误处理也很重要当模型加载失败时我们要展示具体错误信息方便用户排查{error ( div classNametext-red-500 text-center mt-2 p无法加载模型/p p classNametext-sm{error}/p /div )}八、将模型引入浏览器的“幕后功臣”你可能注意到代码里引用了两个关键库Transformers.jsHugging Face 官方推出的 JS 库提供了一套高层次的 API让我们能像在 Python 中一样加载和推理模型ONNX Runtime Web在浏览器中执行 ONNX 格式模型的推理引擎底层支持 WebGPU 和 WebGL 加速。实际加载模型的代码简要示意import { pipeline } from huggingface/transformers; const generator await pipeline(text-generation, onnx-community/DeepSeek-R1-Distill-Qwen-1.5B-ONNX, { device: webgpu, // 指定使用 WebGPU });这个pipeline函数会自动下载模型权重约 3GB缓存到浏览器本地利用 Cache API下次访问秒开。所有计算都在本地完成绝不向服务器发送任何数据。九、总结与避坑指南这个项目虽小但覆盖了现代前端 AI 落地的多个关键点技术选型要稳React TS Tailwind 的组合兼顾开发效率和可维护性适合 AI 应用的快速迭代WebGPU 是大势所趋尽早拥抱可以提前抢占性能红利而且 API 设计清晰检测和降级都很容易状态管理是核心用好useState和useEffect把模型加载、进度、错误都映射到 UI 状态用户交互自然流畅Tailwind 是 UI 加速器不必死磕 CSS把精力留给业务逻辑。踩坑点模型文件通常很大注意设置合理的超时和重试机制不同浏览器对 WebGPU 的实现有细微差异建议在 Chrome 上开发测试内存占用较高低端设备可能加载失败需要提前告知用户硬件要求。