在浏览器中跑DeepSeek-R1:用React+WebGPU实现端侧AI推理

无需API调用,无需云端算力,15亿参数模型就在你的浏览器里

写在前面

你有没有想过,有一天我们可以在浏览器里直接运行一个15亿参数的大语言模型?不需要购买昂贵的GPU,不需要配置复杂的Python环境,甚至不需要联网调用API------打开网页,模型就在本地加载,推理就在本地完成。

这就是我今天要分享的项目:deepseek-r1-webgpu

它利用WebGPU技术,在浏览器端加载并运行DeepSeek-R1-Distill-Qwen-1.5B模型,实现了真正意义上的端侧AI推理。整个项目基于React + TypeScript + Vite构建,代码风格现代,架构清晰,非常适合作为AI时代前端开发的参考项目。

接下来,我将从技术选型、项目搭建、核心实现三个维度,带你完整拆解这个项目。


一、为什么要在浏览器端跑模型?

在开始写代码之前,我们先想清楚一个问题:端侧模型的价值在哪里?

传统的AI应用开发,通常采用API调用方式------用户输入问题,请求发送到远程服务器,LLM在云端完成推理后返回结果。这种模式有几个明显的痛点:

  • :API按token计费,高频调用成本可观
  • 不安全:用户数据、对话内容会经由第三方服务器
  • 依赖网络:断网或弱网环境下无法使用
  • 延迟:每次交互都要经过网络往返

而端侧模型(on-device model)直接把模型部署在用户端------手机、汽车、浏览器,用小参数模型完成特定任务,成本低、响应快、数据隐私有保障。

WebGPU 的出现,让浏览器端运行深度学习模型成为可能。它利用GPU加速,让原本需要云端算力的任务,在用户的笔记本、手机甚至车载系统上就能完成。

现代浏览器的重要特性:navigator.gpu 是WebGPU的入口,通过它可以检测浏览器是否支持这一能力。

typescript

arduino 复制代码
// 检测浏览器是否支持WebGPU
// 双重否定(!!)将值转为布尔类型
// navigator.gpu 不支持时为 undefined,!undefined 为 true,再取反得到 false
const IS_WEBGPU_AVAILABLE = !!navigator.gpu;

二、技术栈:为什么选React + TypeScript + Vite?

项目技术栈的选型逻辑非常清晰,每一层都有明确的考量:

技术 作用 选型理由
React UI框架 AI时代大型项目的首选,生态丰富,组件化开发
TypeScript 类型系统 大型项目必备,提升代码健壮性和可维护性
Vite 构建工具 极速冷启动,HMR热更新,现代前端构建体验
Tailwind CSS 样式方案 原子类CSS,几乎不用写CSS,语义化好
ESLint 代码约束 大公司标配,保证团队代码风格一致
WebGPU 底层计算 浏览器GPU加速,端侧AI推理的核心

React 比 Vue 入门门槛稍高,但在大型项目、AI训练可视化等领域,React的生态和社区资源更丰富。TSX(TypeScript + JSX)的组件化写法,让HTML、CSS、JS逻辑封装在一个函数组件中,符合"高内聚、低耦合"的设计原则。


三、项目初始化与配置

3.1 创建项目

bash

sql 复制代码
# 使用Vite创建React + TypeScript项目
npm create vite@latest deepseek-r1-webgpu -- --template react-ts

cd deepseek-r1-webgpu
npm install

3.2 安装Tailwind CSS

Tailwind CSS 是一个原子类CSS框架,提供大量预定义的CSS类名,开发者只需在HTML/JSX中组合这些类名,几乎不需要手写CSS。

bash

bash 复制代码
npm install tailwindcss @tailwindcss/vite

3.3 配置Vite插件

vite.config.ts 中注册插件:

typescript

javascript 复制代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    react(),      // 支持React组件和JSX语法
    tailwindcss() // 让Vite识别Tailwind原子类
  ],
})

3.4 引入Tailwind

src/index.css 中只需一行:

css

scss 复制代码
@import "tailwindcss";

就这么简单,整个项目的样式系统就搭建好了。

3.5 ESLint配置

bash

sql 复制代码
npm install eslint @eslint/js @types/eslint__js typescript-eslint --save-dev

ESLint负责约束代码风格,保证团队协作时代码的一致性。在AI时代的大型项目中,规范的代码风格比以往任何时候都重要。


四、Tailwind CSS 运行原理

很多人第一次接触Tailwind时可能会困惑:这些原子类名是怎么工作的?为什么不用写CSS了?

4.1 原子类CSS

Tailwind提供的是原子类------每个类名只做一件事,语义化非常清晰:

html

xml 复制代码
<!-- 传统CSS:写选择器 + 样式规则 -->
<div class="container">
  <h1 class="title">Hello</h1>
</div>

<!-- Tailwind:直接写原子类 -->
<div className="flex justify-center items-center bg-white">
  <h1 className="text-3xl font-bold underline">Hello</h1>
</div>

4.2 原子类的语义化

原子类 含义 对应的CSS
flex 弹性布局 display: flex
justify-center 主轴居中 justify-content: center
items-center 侧轴居中 align-items: center
h-screen 高度100vh height: 100vh
mx-auto 水平居中 margin-left: auto; margin-right: auto
text-gray-800 字体颜色 color: #1f2937
bg-white 背景白色 background-color: #fff
max-w-[400px] 最大宽度400px max-width: 400px

Tailwind的一个关键设计是:数字单位对应关系text-4xlmb-1 这类类名遵循一套设计系统,比如 1单位 = 4px4xl 对应特定的字体大小。

4.3 Vite插件的工作机制

@tailwindcss/vite 插件会在构建时扫描代码中使用的原子类名,提取对应的CSS样式,生成最终的样式文件。这个过程是编译时的,所以生产环境只会包含实际使用到的样式,体积非常小。

4.4 为什么不用 class 而是 className

在React中,我们使用 className 而不是 class

tsx

ini 复制代码
<div className="flex items-center">  // ✅ 正确
<div class="flex items-center">      // ❌ 错误

原因很简单:JSX是在JavaScript中写HTML,而 class 是JavaScript中声明类的关键字(OOP中的类)。为了不混淆,React使用 className 来指代HTML的class属性。


五、React组件化开发

5.1 什么是组件化?

React的核心思想是组件化开发------用"搭积木"的方式构建页面。每个组件是一个功能单元,由一组HTML、CSS、JS组合而成。

Vue通过 templatescriptstyle 三明治结构组织一个文件,而React的方式更纯粹:函数就是组件,函数返回的HTML就是组件的UI。

tsx

javascript 复制代码
// 一个React组件本质上就是一个函数
function MyComponent() {
  // 这里可以写JS逻辑
  const message = "Hello World";
  
  // 返回JSX作为UI
  return <div>{message}</div>;
}

5.2 响应式数据与Hooks

React最核心的特性之一是响应式数据------数据状态变化时,界面自动更新,不需要手动操作DOM。

tsx

scss 复制代码
import { useState, useEffect } from 'react'

function App() {
  // useState 是 React 的 Hook,用于声明响应式数据状态
  // status 是数据,setStatus 是修改数据的函数
  // 调用 setStatus 修改数据,界面会自动重新渲染
  const [status, setStatus] = useState(null); // null: 初始状态
  const [error, setError] = useState('出错了');
  const [loadingMessage, setLoadingMessage] = useState('');
  const [progressItems, setProgressItems] = useState([]);
  
  // useEffect 是生命周期钩子,组件挂载时执行
  useEffect(() => {
    console.log('组件已经挂载完成');
    setStatus('ready');
  }, []); // 空依赖数组表示只在挂载时执行一次
  
  return (
    // JSX UI
  )
}

5.3 数据驱动视图

在React中,我们不做"命令式"的DOM操作,而是"声明式"地描述UI应该长什么样,由数据状态驱动视图变化。

数据状态驱动界面状态 ------ 变量/常量 → 数据(数据绑定data binding & data driving)→ 不需要DOM编程。修改状态,界面跟着变。


六、项目核心代码解析

6.1 入口文件 main.tsx

tsx

javascript 复制代码
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'  // 引入Tailwind
import App from './App.tsx'

// 渲染根组件
createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

StrictMode 是React的严格模式,用于在开发阶段发现潜在问题。

6.2 主组件 App.tsx

接下来看核心的 App.tsx,它包含了页面UI和模型加载逻辑。

完整的UI结构:

tsx

ini 复制代码
function App() {
  // 响应式数据状态
  const [status, setStatus] = useState(null);
  const [error, setError] = useState(null);
  const [loadingMessage, setLoadingMessage] = useState('');
  const [progressItems, setProgressItems] = useState([]);

  // 检测WebGPU可用性
  const IS_WEBGPU_AVAILABLE = !!navigator.gpu;

  // 组件挂载后的副作用
  useEffect(() => {
    console.log('组件已经挂载完成');
    setStatus('ready');
  }, []);

  // 条件渲染:如果WebGPU不可用,展示提示
  if (!IS_WEBGPU_AVAILABLE) {
    return <div>你的浏览器还不支持WebGPU</div>;
  }

  return (
    <div className="flex flex-col h-screen mx-auto items-center justify-end text-gray-800 bg-white">
      <div className="h-full overflow-auto flex justify-center flex-col relative">
        
        {/* 头部区域 */}
        <div className="flex flex-col items-center mb-1 max-w-[400px] text-center">
          <h1 className="text-4xl font-bold mb-1">
            DeepSeek-R1-WebGPU
          </h1>
          <h2 className="font-semibold">
            A next generation reasoning model that runs locally in
            your browser with WebGPU acceleration.
          </h2>
        </div>

        {/* 模型信息区域 */}
        <div className="flex flex-col items-center px-4">
          <p className="max-w-[510px] mb-4">
            You are about to load{' '}
            <a 
              href="https://huggingface.co/onnx-community/Deepseek-R1-Distill-Qwen-1.5B"
              target="_blank"
              rel="noreferrer"
              className="font-medium underline"
            >
              DeepSeek-R1-Distill-Qwen-1.5B
            </a>
            , a 1.5B parameter reasoning LLM optimized for in-browser 
            inference. Everything runs entirely in your browser with{' '}
            <a
              href="https://huggingface.co/docs/transformers.js"
              target="_blank"
              rel="noreferrer"
              className="font-medium underline"
            >
              Transformers.js
            </a>
            .
          </p>

          {/* 错误信息展示 */}
          {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>
          )}
        </div>
      </div>
    </div>
  );
}

export default App

6.3 关键设计解读

1. WebGPU可用性检测

typescript

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

这行代码通过双重否定将 navigator.gpu 转换为布尔值。如果浏览器不支持WebGPU,navigator.gpuundefined!!undefinedfalse

2. 条件渲染

React支持在JSX中使用条件语句:

tsx

javascript 复制代码
{error && (
  <div className="text-red-500">
    <p>{error}</p>
  </div>
)}

error 为真值时,渲染错误提示;否则不渲染。

3. 外部链接

tsx

ini 复制代码
<a 
  href="https://huggingface.co/..."
  target="_blank"
  rel="noreferrer"
  className="font-medium underline"
>
  • target="_blank" 在新窗口打开
  • rel="noreferrer" 是安全属性,防止新页面通过 window.opener 访问原页面

4. 模型来源

项目使用的模型是 DeepSeek-R1-Distill-Qwen-1.5B

  • 基于DeepSeek-R1蒸馏,使用Qwen架构
  • 参数量15亿,适合本地轻量推理
  • 来自HuggingFace(抱抱脸)------全球最大的开源模型社区

5. Transformers.js

tsx

arduino 复制代码
// transformers 是 HuggingFace 推出的 JS 库,用于在浏览器中加载和推理模型

这是HuggingFace官方提供的JavaScript库,让我们能够在浏览器中直接运行Transformer模型。


七、JSX:React的UI表达方式

7.1 什么是JSX?

JSX是React专用的模板语法,允许在JavaScript代码中直接写HTML标签。

tsx

ini 复制代码
const element = <h1 className="text-3xl">Hello World</h1>;

JSX = JavaScript with XML,它让我们能用非常自然的方式表达UI界面。

7.2 JSX的编译

JSX在编译时会被转换为原生的React.createElement调用,最终生成DOM操作。所以本质上,JSX是一种语法糖,让UI代码更直观、更可读。

tsx

css 复制代码
// 写起来像HTML
<div className="flex">
  <h1>Hello</h1>
</div>

// 编译后变成
React.createElement('div', { className: 'flex' }, 
  React.createElement('h1', null, 'Hello')
)

7.3 为什么选择JSX?

相比Vue的模板语法,JSX更灵活------你可以在JSX中使用完整的JavaScript能力(条件、循环、函数调用等),这让构建复杂UI变得非常方便。


八、项目结构一览

text

csharp 复制代码
deepseek-r1-webgpu/
├── public/              # 静态资源
├── src/
│   ├── assets/          # 图片等资源
│   ├── App.css          # 组件样式(已迁移到Tailwind)
│   ├── App.tsx          # 主应用组件 ⭐
│   ├── index.css        # 全局样式(引入Tailwind)
│   └── main.tsx         # 入口文件
├── .gitignore
├── eslint.config.js     # ESLint配置
├── index.html           # HTML模板
├── package.json
├── pnpm-lock.yaml
├── README.md
├── tsconfig.app.json    # TypeScript配置(应用)
├── tsconfig.json        # TypeScript配置(根)
├── tsconfig.node.json   # TypeScript配置(Node)
├── vite.config.ts       # Vite构建配置
└── readme68.md          # 项目文档

九、写在最后

这个项目虽然还在开发中,但它展示的技术方向非常清晰:端侧AI + 现代前端技术栈

传统的LLM开发依赖于Python + PyTorch + 云端GPU,而deepseek-r1-webgpu展示了另一种可能------用React + TypeScript + WebGPU,直接在浏览器中运行大语言模型。

这种模式的优势在于:

  1. 零成本:不需要购买API额度,不需要租用GPU
  2. 隐私安全:数据完全在本地,不会上传到任何服务器
  3. 离线可用:模型下载一次后,可完全离线使用
  4. 跨平台:只要有现代浏览器,就能运行

当然,端侧模型也有其局限性------小参数模型的推理能力不如大模型,但对于特定任务(如代码补全、简单对话、分类等),15亿参数的蒸馏模型已经足够。

未来,随着WebGPU标准的成熟和浏览器性能的提升,我们会在端侧看到越来越多的AI应用。前端开发者的技能边界,也正在从"写好UI"扩展到"跑得动模型"。


项目地址:基于 React + TypeScript + Vite + Tailwind + WebGPU

技术关键词:DeepSeek-R1、端侧模型、WebGPU、React、TypeScript、Tailwind CSS、Transformers.js

适合场景:AI应用开发、端侧推理、浏览器AI、React学习参考


如果这篇文章对你有帮助,欢迎点赞、收藏、转发。也欢迎在评论区交流讨论,一起探索前端与AI的更多可能性。

相关推荐
梅头脑8 小时前
Embedding模型和LLM不是同一个东西!RAG核心组件深度剖析
ai编程
早期的虫儿有鸟吃8 小时前
vue2--Vuex 模块化
开发语言·前端·javascript
春风野草8 小时前
AI Agent 长任务实战:取消、重试、中断恢复,不是加几个按钮这么简单
aigc·ai编程
C++、Java和Python的菜鸟8 小时前
第5章 后端Web基础 (MySQL基础)
前端·mysql·adb
门前大桥下.8 小时前
HTML-01我的第一个网页
前端·html
JavaGuide8 小时前
Kimi K3 实战:全栈项目、Java 项目改造与 3A 游戏 Demo
后端·ai编程
我是大卫8 小时前
【图】React源码解析-从数据结构、依赖追踪机制、值传播与更新触发、以及性能陷阱与优化,深挖useContext的底层原理
前端·react.js·源码
谭光志8 小时前
深入浅出 RAG:用一个可运行的 Demo 讲透完整链路
前端·后端·ai编程
Cobyte8 小时前
使用 JavaScript 实现有限状态机的经典问题
前端·javascript·vue.js