React + WebGPU:在浏览器里跑一个 DeepSeek 推理模型

摘要:不调远程API,不装Ollama,打开浏览器就能跑1.5B参数大模型?本文用ReactHooks管理加载状态,TailwindCSS零行写样式,Transformers.js加载ONNX模型,WebGPU做推理加速,手写一个端侧AI推理页面。无数据离开你的电脑,断网也能用。


目录

  1. [端侧模型的革命:比 API 更便宜、更安全](#端侧模型的革命:比 API 更便宜、更安全 "#%E4%B8%80%E7%AB%AF%E4%BE%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E9%9D%A9%E5%91%BD%E6%AF%94-api-%E6%9B%B4%E4%BE%BF%E5%AE%9C%E6%9B%B4%E5%AE%89%E5%85%A8")
  2. [React 组件骨架:从数据状态到界面状态](#React 组件骨架:从数据状态到界面状态 "#%E4%BA%8Creact-%E7%BB%84%E4%BB%B6%E9%AA%A8%E6%9E%B6%E4%BB%8E%E6%95%B0%E6%8D%AE%E7%8A%B6%E6%80%81%E5%88%B0%E7%95%8C%E9%9D%A2%E7%8A%B6%E6%80%81")
  3. [TailwindCSS:不写一行 CSS 也能做出专业 UI](#TailwindCSS:不写一行 CSS 也能做出专业 UI "#%E4%B8%89tailwindcss%E4%B8%8D%E5%86%99%E4%B8%80%E8%A1%8C-css-%E4%B9%9F%E8%83%BD%E5%81%9A%E5%87%BA%E4%B8%93%E4%B8%9A-ui")
  4. [WebGPU 检测与界面分支](#WebGPU 检测与界面分支 "#%E5%9B%9Bwebgpu-%E6%A3%80%E6%B5%8B%E4%B8%8E%E7%95%8C%E9%9D%A2%E5%88%86%E6%94%AF")
  5. [模型加载:Transformers.js + ONNX Runtime Web](#模型加载:Transformers.js + ONNX Runtime Web "#%E4%BA%94%E6%A8%A1%E5%9E%8B%E5%8A%A0%E8%BD%BDtransformersjs--onnx-runtime-web")
  6. 总结

一、端侧模型的革命:比 API 更便宜、更安全

调用 OpenAI 或 DeepSeek 的 API 是主流做法,但它有两个绕不开的短板:不安全------你的上下文数据会随着 HTTP 请求离开本地,发送到远程服务器。

端侧模型(On-Device Model)是另一条路。Ollama 可以本地部署开源模型,手机端、汽车端、浏览器端都能跑小参数模型完成特定任务。在浏览器里,这一切依靠 WebGPU------一个让网页直接调用 GPU 算力的现代浏览器 API。

这个项目的完整技术栈:

markdown 复制代码
React + TypeScript      前端框架 + 类型安全
    ↕
TailwindCSS            零 CSS 样式的原子类系统
    ↕
Transformers.js        在浏览器中加载和运行 HuggingFace 模型
    ↕
ONNX Runtime Web       跨平台模型推理引擎
    ↕
WebGPU                 调用本地 GPU 做推理加速

所有计算都在浏览器本地完成------没有数据离开你的电脑,模型下载后断网也能用。


二、React 组件骨架:从数据状态到界面状态

React 的核心哲学是"数据驱动视图"------只要修改数据状态,界面自动更新,不需要手动操作 DOM。这个理念通过 Hooks 来实现。

状态驱动的页面结构

一个 LLM 推理页面需要跟踪四种数据状态:

typescript 复制代码
import { useState, useEffect } from "react";

function App() {
    const [status, setStatus] = useState(null);           // 模型加载状态
    const [error, setError] = useState('出错了');          // 错误信息
    const [loadingMessage, setLoadingMessage] = useState(""); // 加载提示
    const [progressItems, setProgressItems] = useState([{  // 下载进度
        file: 'preinitModule.onnx',
        progress: 0,
        total: 34353543453
    }]);

    return (/* JSX 渲染 */);
}
状态变量 初始值 对应的界面状态
status null 未开始 / loading / ready
error '出错了' 错误提示区域
loadingMessage "" 加载中文案
progressItems [{file, progress, total}] 模型文件下载进度条
flowchart LR A[useState 声明数据状态] --> B[用户交互 / 生命周期触发] B --> C[setStatus / setError 修改状态] C --> D[React 自动重渲染] D --> E[界面随状态变化]

useEffect:组件挂载时自动执行

useEffect 是 React 的"副作用钩子"------组件在页面上渲染完成后自动执行的回调:

typescript 复制代码
useEffect(() => {
    console.log('组件已经挂载完成');
    // 在这里触发模型下载、WebGPU 初始化等
}, []);  // 空依赖数组 = 只在首次挂载时执行一次

组件 = 函数

React 组件本质上就是一个返回 JSX 的函数。函数体里写 JS 逻辑(声明状态、处理事件),返回值是 UI 描述。一个组件就是一个自包含的功能单元------JS 逻辑 + HTML 模板 + CSS 引用------全部封装在一个函数里。


三、TailwindCSS:不写一行 CSS 也能做出专业 UI

传统前端开发中最耗时的环节之一是写 CSS------选择器、样式规则、响应式断点、命名规范。TailwindCSS 用一套原子类系统彻底省掉了这个过程。

原子类的组合哲学

tsx 复制代码
<div className="flex flex-col h-screen mx-auto items-center justify-end text-gray-800 bg-white">

这行 className 干了什么?

原子类 对应的 CSS 效果
flex display: flex 弹性布局
flex-col flex-direction: column 主轴方向为垂直
h-screen height: 100vh 高度占满整个视口
mx-auto margin-left/right: auto 水平居中
items-center align-items: center 子元素沿交叉轴居中
justify-end justify-content: flex-end 子元素靠主轴末端对齐
text-gray-800 color: #1f2937 深灰色文字
bg-white background-color: white 白色背景

没有写一行 CSS,没有命名任何一个选择器,一个完整的布局就完成了。

为什么是 className 而不是 class?

因为在 JSX 中,class 是 JavaScript 的保留关键字(用于声明类)。React 使用 className 来代替 HTML 原生的 class 属性,避免语法冲突。

TailwindCSS 已经成为 Vibe Coding 时代的事实标准------原子类名天然适合自然语言描述,Agent 生成 UI 时写几个类名远比手写完整 CSS 规则高效。


四、WebGPU 检测与界面分支

WebGPU 不是所有浏览器都支持。打开页面的第一步是检测,然后根据检测结果走不同的渲染分支。

一行双重否定搞定检测

typescript 复制代码
const IS_WEBGPU_AVAILABLE = !!navigator.gpu;

navigator.gpu------浏览器支持 WebGPU 时返回 GPU 对象,不支持时返回 undefined!! 双重否定将任意值转换为布尔:undefined → falseGPU 对象 → true

条件渲染:两种界面两种命运

tsx 复制代码
return (
    IS_WEBGPU_AVAILABLE ? (
        <div className="flex flex-col h-screen ...">
            {/* 主界面:模型介绍 + 加载进度 + 推理输入 */}
        </div>
    ) : (
        <div>您的浏览器还不支持 WebGPU</div>
    )
);
flowchart TD A[navigator.gpu] --> B{IS_WEBGPU_AVAILABLE} B -->|true| C[渲染主界面<br/>模型加载 进度条 推理输入] B -->|false| D[渲染兜底界面<br/>提示浏览器不支持]

除了 WebGPU 检测分支,还有 错误状态的条件渲染

tsx 复制代码
{error && (
    <div className="text-red-500 text-center mb-2">
        <p className="mb-1">Unable to load model due to the following error:</p>
        <p className="text-sm">{error}</p>
    </div>
)}

error && 是 JS 的短路表达式------error 有值时渲染错误提示,为空时不渲染。同一组件支持四种界面状态:未加载 / 加载中 / 就绪 / 出错。


五、模型加载:Transformers.js + ONNX Runtime Web

在页面 UI 就绪后,真正的重头戏开始------从 HuggingFace 下载模型并在浏览器中运行。

模型选择

项目使用的是 DeepSeek-R1-Distill-Qwen-1.5B------15 亿参数量的蒸馏推理模型。为什么选这个尺寸?1.5B 的参数规模在浏览器端刚好找到了能力与速度的平衡点:足够小到可以下载到浏览器内存中(约 3GB),又足够大到能完成有意义的推理任务。

加载链路

flowchart LR A[HuggingFace<br/>模型仓库] --> B[Transformers.js<br/>下载并解析模型] B --> C[ONNX Runtime Web<br/>跨平台推理引擎] C --> D[WebGPU<br/>GPU推理加速] D --> E[浏览器输出<br/>Token 流式生成]
组件 角色
HuggingFace 全球最大开源模型社区,托管模型权重文件
Transformers.js HuggingFace 官方 JS 库,在浏览器中加载和运行 Transformer 模型
ONNX Runtime Web 微软的跨平台推理引擎,ONNX 格式让同一个模型文件可以在任何平台运行
WebGPU 调用本地 GPU 做矩阵乘法和 attention 计算,比 CPU 推理快 10-50 倍

进度追踪

progressItems 状态数组跟踪模型文件的下载进度------每个文件(如 preinitModule.onnx)有独立的 progresstotal。用户在等待时能看到具体的下载百分比,而不是一个空白的加载页面。


六、总结

一个浏览器端的 LLM 推理项目,基础骨架就这五个模块:

  1. React Hooks 做状态管理useState 声明四种状态(加载/错误/进度/就绪),useEffect 在组件挂载时触发模型下载。数据改 → 视图变,不需要任何 document.getElementById
  2. TailwindCSS 做零 CSS 样式 :原子类名组合表达完整布局,没有选择器命名没有样式文件。为什么 className 不叫 class?因为 JSX 中 class 是 JS 关键字。
  3. !!navigator.gpu 做 WebGPU 检测 :双重否定转布尔,决定渲染主界面还是兜底提示。error && 短路表达式做错误状态的条件显示。
  4. Transformers.js + ONNX Runtime Web:从 HuggingFace 下载模型权重,ONNX 引擎做跨平台推理,WebGPU 做硬件加速。
  5. 端侧模型 = 便宜 + 安全 + 离线可用:数据不离开浏览器,没有 API 费用,模型下载一次后断网也能跑。

这个项目放在简历上是"超燃"的一笔------它覆盖了 React/TypeScript/WebGPU/ONNX/Transformers.js 五项前沿技能,而实现的核心代码不过一百多行。技术栈的深度不在代码行数,在架构的组合能力。


------ 浏览器里跑大模型,不是遥远的未来。

相关推荐
crary,记忆10 小时前
React Hook 浅学 2.0 - 自定义Hook
前端·react.js·前端框架
触底反弹10 小时前
🔥 React 零基础入门(下):Props + State + useEffect 生命周期深度解析
前端·react.js·面试
breeze jiang10 小时前
从零搭建浏览器端 AI 原型:React、WebGPU 与 Tailwind CSS 的一次实践
css·人工智能·react.js
触底反弹19 小时前
🔥 React 零基础入门(上):环境搭建 + JSX 深度解析
前端·react.js·typescript
小林ixn20 小时前
在浏览器跑通 15 亿参数大模型:我用 React + WebGPU 复刻了 DeepSeek-R1
前端·react.js·前端框架
谷哥的小弟1 天前
TypeScript对象类型
javascript·typescript
索西引擎1 天前
【React】useState 状态更新机制:批量更新策略与“异步“错觉的深层解析
前端·react.js·前端框架
kyriewen1 天前
别再写useMemo了——2026年这5个React性能优化已经是反模式
前端·react.js·ai编程
大家的林语冰1 天前
🫡 见证历史,TypeScript 7 重写成功,VS Code 原地起飞,GitHub 第一语言联手 Go 破而后立!
前端·javascript·typescript