DeepSeek-R1 WebGPU (1):在浏览器里跑大模型

文章目录

    • [一、端侧模型:AI 不再只活在云端](#一、端侧模型:AI 不再只活在云端)
      • [1.1 什么是端侧模型?](#1.1 什么是端侧模型?)
      • [1.2 为什么端侧模型突然火了?](#1.2 为什么端侧模型突然火了?)
      • [1.3 本项目的模型选择](#1.3 本项目的模型选择)
    • [二、React + TypeScript:为什么是 AI 时代的首选?](#二、React + TypeScript:为什么是 AI 时代的首选?)
      • [2.1 React vs Vue:选型的底层逻辑](#2.1 React vs Vue:选型的底层逻辑)
      • [2.2 新建项目:React + TS + ESLint 一步到位](#2.2 新建项目:React + TS + ESLint 一步到位)
      • [2.3 Vite 配置:让 Tailwind 跑起来](#2.3 Vite 配置:让 Tailwind 跑起来)
    • [三、TailwindCSS:告别手写 CSS 的原子化方案](#三、TailwindCSS:告别手写 CSS 的原子化方案)
      • [3.1 传统 CSS 的痛点](#3.1 传统 CSS 的痛点)
      • [3.2 Tailwind 的思路:原子类](#3.2 Tailwind 的思路:原子类)
      • [3.3 Tailwind 运行原理](#3.3 Tailwind 运行原理)
      • [3.4 为什么是 `className` 而不是 `class`?](#3.4 为什么是 className 而不是 class?)
    • [四、React 组件:函数就是积木](#四、React 组件:函数就是积木)
      • [4.1 Vue 组件 vs React 组件](#4.1 Vue 组件 vs React 组件)
      • [4.2 入口文件:React 是怎么启动的?](#4.2 入口文件:React 是怎么启动的?)
    • [五、代码详解:App.tsx 逐段解析](#五、代码详解:App.tsx 逐段解析)
      • [5.1 导入 Hooks](#5.1 导入 Hooks)
      • [5.2 数据状态:响应式的核心](#5.2 数据状态:响应式的核心)
      • [5.3 WebGPU 检测:一行代码判断浏览器能力](#5.3 WebGPU 检测:一行代码判断浏览器能力)
      • [5.4 组件生命周期:useEffect 的执行时机](#5.4 组件生命周期:useEffect 的执行时机)
      • [5.5 JSX:在 JavaScript 里写 HTML](#5.5 JSX:在 JavaScript 里写 HTML)
      • [5.6 Tailwind 原子类实战解读](#5.6 Tailwind 原子类实战解读)
      • [5.7 模型信息展示区解析](#5.7 模型信息展示区解析)
      • [5.8 错误处理状态](#5.8 错误处理状态)
    • 六、全文总结
    • 七、核心知识点复盘
    • [八、常见问题 / 避坑指南](#八、常见问题 / 避坑指南)

一份保姆级技术复盘,覆盖端侧模型、React + TypeScript、TailwindCSS、JSX 等核心技能点,适合学习复盘和技术分享。


一、端侧模型:AI 不再只活在云端

1.1 什么是端侧模型?

平时我们使用 ChatGPT、DeepSeek、Kimi 等 AI 助手,流程是这样的:

复制代码
用户输入 → 网络请求 → 远程服务器(GPU集群) → 推理计算 → 返回结果

这种方式叫云端推理,模型跑在厂商的服务器上。它有两个绕不开的问题:

  • :厂商需要采购大量 GPU,成本最终转嫁给你(API 按 token 计费)。
  • 不安全:你的输入内容(context)会随着请求发送到远端服务器,数据隐私无法完全掌控。

端侧模型(On-Device Model)指的是模型直接运行在你的设备上------手机、电脑、汽车、甚至浏览器。数据不出设备,推理在本地完成。

1.2 为什么端侧模型突然火了?

关键推动力来自两点:

推动因素 说明
开源小参数模型成熟 Llama、Qwen、Gemma 等 1B~7B 参数模型,在特定任务上表现已经不输大模型
WebGPU 的到来 浏览器可以直接调用 GPU 做并行计算,不再依赖 WebGL 的"曲线救国"

Ollama 就是典型的端侧方案------你下载模型到本地,通过命令行或 API 调用。而本项目的更进一步:模型直接在浏览器里下载、加载、推理,用户打开网页就能用,用完即走,不占用磁盘。

1.3 本项目的模型选择

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

  • 这是 DeepSeek-R1(推理模型)的蒸馏版,参数量压缩到 15 亿。
  • 基于 Qwen 架构,专为本地轻量推理优化。
  • 模型格式为 ONNX(Open Neural Network Exchange,开放神经网络交换格式),跨平台跨框架。
  • 托管在 HuggingFace (全球最大开源模型社区),通过 Transformers.js 加载。

关键理解:蒸馏 = 用大模型"教"小模型。大模型生成高质量答案 → 小模型模仿学习 → 保留大部分推理能力但体积小很多。


二、React + TypeScript:为什么是 AI 时代的首选?

2.1 React vs Vue:选型的底层逻辑

你可能会问:Vue 上手更简单,为什么 AI 项目偏爱 React?

维度 React Vue
学习曲线 较陡(需要理解 JSX、Hooks、函数式编程) 平缓(模板语法接近 HTML)
大型项目 函数式编程天然适合抽象和复用,生态更成熟 中小项目效率极高
AI/ML 生态 Transformers.js、LangChain.js、Vercel AI SDK 都优先支持 React 社区也在跟进,但目前示例偏少
招聘市场 大厂、AI Startup 的首选 国内中小企业用得更多

一句话总结:React 的上限更高,Vue 的下限更低。做 AI 相关的复杂交互,React 的函数式思想更适合。

2.2 新建项目:React + TS + ESLint 一步到位

bash 复制代码
# 使用 Vite 创建项目(最快的构建工具)
npm create vite@latest webgpu-demo -- --template react-ts

cd webgpu-demo
npm install

创建完成后,你会得到以下关键文件:

复制代码
webgpu-demo/
├── src/
│   ├── App.tsx        # 主组件(你写代码的地方)
│   ├── App.css        # 组件样式
│   ├── main.tsx       # 入口文件(挂载 React 到页面)
│   └── index.css      # 全局样式 + Tailwind 导入
├── eslint.config.js   # ESLint 代码约束配置
├── vite.config.ts     # Vite 构建配置
├── tsconfig.json      # TypeScript 配置
└── package.json       # 依赖管理

package.json 的核心依赖解读:

json 复制代码
{
  "dependencies": {
    "@tailwindcss/vite": "^4.3.3",  // TailwindCSS Vite 插件
    "react": "^19.2.6",             // React 核心库
    "react-dom": "^19.2.6",         // React DOM 渲染(浏览器端)
    "tailwindcss": "^4.3.3"         // TailwindCSS 框架本体
  },
  "devDependencies": {
    "typescript": "~6.0.2",         // TypeScript 编译器
    "eslint": "^10.3.0",            // 代码规范检查
    "vite": "^8.0.12"               // 构建工具
  }
}

ESLint 的作用是什么?

ESLint 是代码"纪律委员"------约束团队写出一致风格的代码。比如用单引号还是双引号?结尾要不要分号?这些规则在 eslint.config.js 中统一配置。大公司必备,否则代码合并时就是灾难。

js 复制代码
// eslint.config.js 关键配置
export default defineConfig([
  globalIgnores(['dist']),           // 忽略构建产物
  {
    files: ['**/*.{ts,tsx}'],        // 对 TS 和 TSX 文件生效
    extends: [
      js.configs.recommended,         // JS 基础规则
      tseslint.configs.recommended,   // TypeScript 规则
      reactHooks.configs.flat.recommended, // React Hooks 规则
    ],
  },
])

2.3 Vite 配置:让 Tailwind 跑起来

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

export default defineConfig({
  plugins: [
    react(),       // 让 Vite 支持 React JSX
    tailwindcss(), // 让 Vite 处理 Tailwind 原子类
  ],
})

Vite 插件机制很简单 :每个插件负责一块能力,像搭积木一样拼起来。react() 负责编译 JSX,tailwindcss() 负责扫描和注入 CSS。


三、TailwindCSS:告别手写 CSS 的原子化方案

3.1 传统 CSS 的痛点

回想一下你写 CSS 的流程:

  1. 想一个 class 名(.my-cool-button
  2. 找到对应文件(或 <style> 块)
  3. 写选择器 + 规则(color: red; font-size: 16px;
  4. 反复调试样式冲突和优先级

这个过程太低效了------你在两个文件之间来回切换,还要想命名、管优先级。

3.2 Tailwind 的思路:原子类

Tailwind 的做法是:不写 CSS 规则,直接写类名

html 复制代码
<!-- 传统方式 -->
<button class="my-button">点击</button>
<style>
  .my-button {
    background: blue;
    color: white;
    padding: 8px 16px;
    border-radius: 4px;
  }
</style>

<!-- Tailwind 方式 -->
<button className="bg-blue-500 text-white px-4 py-2 rounded">
  点击
</button>

每一个 class 名 = 一条 CSS 规则。bg-blue-500 就是 background-color: bluepx-4 就是 padding-left: 1rem; padding-right: 1rem;

为什么这更好?

  • 不用命名 :不用再纠结 class 叫 btn-primary 还是 btn-main
  • 所见即所得:看到类名就知道样式,不用跳转到 CSS 文件
  • 自然语言友好:类名是用英文单词组合的,和 AI 编程(Vibe Coding)天然契合
  • 按需生成:Vite 插件只提取你用到的类名,打包体积很小

3.3 Tailwind 运行原理

Tailwind 不是原生 CSS------浏览器不认识 bg-blue-500。它的工作流程是:

复制代码
1. 你写 className="bg-blue-500 text-white"
            ↓
2. Tailwind Vite 插件扫描所有 .tsx/.jsx 文件
            ↓
3. 识别到 bg-blue-500 → 找到对应 CSS: background-color: #3b82f6;
            ↓
4. 把这条 CSS 注入到最终构建的样式文件中
            ↓
5. 浏览器正确渲染蓝色背景

核心原理一句话:Tailwind 是一个"类名到 CSS 规则"的映射字典。插件在构建时扫描代码 → 查字典 → 生成最小化的 CSS 文件。你没有用到的类名不会出现在最终产物中。

在项目中的体现:

css 复制代码
/* src/index.css --- 只需要一行! */
@import "tailwindcss";

/* 下面是项目自定义的 CSS 变量和全局样式 */
:root {
  --text: #6b6375;
  --bg: #fff;
  /* ... */
}

@import "tailwindcss" 这一行就是 Tailwind 的"入口",插件会从这里开始注入扫描到的所有原子类。

3.4 为什么是 className 而不是 class

这是一个非常经典的困惑。答案很简单:

JSX 中写 <div class="xxx"> 会出问题,因为 class 是 JavaScript 的关键字(用于定义类/面向对象编程)。

React 团队为了避免语法冲突,用 className 替代了 class

tsx 复制代码
// ❌ 错误:class 是 JS 关键字
<div class="container">

// ✅ 正确:使用 className
<div className="container">

编译后 <div className="container"> → 原生 DOM 的 <div class="container">,效果一模一样。


四、React 组件:函数就是积木

4.1 Vue 组件 vs React 组件

Vue 组件 是"三件套"------HTML、CSS、JS 分块写在一个 .vue 文件里:

vue 复制代码
<template>
  <div>{{ message }}</div>
</template>

<script setup>
const message = 'Hello'
</script>

<style scoped>
div { color: red; }
</style>

React 组件 就是一个函数,返回 HTML(JSX):

tsx 复制代码
function MyComponent() {
  const message = 'Hello'   // JS 逻辑
  // CSS 通过 import 或 Tailwind 引入
  return <div>{message}</div>  // 返回 HTML
}

两者的本质区别:

Vue React
组件形态 .vue 单文件(模板+逻辑+样式) 函数(JS + JSX)
入门难度 低(模板接近原生 HTML) 中(需要理解 JSX 和函数式编程)
抽象能力 指令体系(v-if, v-for) JavaScript 原生能力(&&, map)

React 的理念:组件就是函数,函数就是组件。所有 JavaScript 的能力(条件判断、循环、解构)都能直接在"模板"里用。

4.2 入口文件:React 是怎么启动的?

tsx 复制代码
// src/main.tsx --- React 应用的"点火开关"
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>,
)

执行流程:

  1. createRoot(...) --- 找到 index.html 中的 <div id="root">,把它变成 React 的"根容器"
  2. .render(...) --- 把 <App /> 组件渲染到这个容器里
  3. <StrictMode> --- 开发模式下的"严格检查",会帮你发现潜在问题(比如不安全的生命周期),生产环境自动失效

五、代码详解:App.tsx 逐段解析

下面逐一解析 App.tsx 的每一部分代码,确保你完全理解。

5.1 导入 Hooks

tsx 复制代码
import { useState, useEffect } from 'react'
  • useState:React 的"状态钩子"。让你在函数组件中创建响应式数据------数据变了,界面自动更新。
  • useEffect:React 的"副作用钩子"。组件渲染完成后自动执行指定代码(比如发请求、设置定时器)。
  • 这两个函数都以 use 开头,这是 React 的约定------所有 Hooks 都遵循 useXxx 命名模式。

5.2 数据状态:响应式的核心

tsx 复制代码
function App() {
  // status: 当前加载状态
  // null = 初始 / 'loading' = 加载中 / 'ready' = 模型就绪
  const [status, setStatus] = useState(null)

  // error: 错误信息(演示用 "出错了" 作为初始值)
  const [error, setError] = useState("出错了")

  // loadingMessage: 加载提示文本
  const [loadingMessage, setLoadingMessage] = useState("")

  // progressItems: 模型文件下载进度
  const [progressItems, setProgressItems] = useState([{
    file: 'model.onnx',     // 模型文件名
    progress: 0,            // 当前已下载字节数
    total: 5465458632       // 模型总大小(约 5.5GB)
  }])

useState 语法详解:

tsx 复制代码
const [值, 修改值的函数] = useState(初始值)

这是数组解构语法------useState 返回一个长度为 2 的数组:

  • 第一个元素是当前状态值(只读,不要直接修改)
  • 第二个元素是更新函数(想改状态?调它!)
tsx 复制代码
// ❌ 错误:直接修改不会触发界面更新
status = 'ready'

// ✅ 正确:调用更新函数
setStatus('ready')  // 状态变了 → React 自动重新渲染组件

为什么叫"响应式"? 数据(状态)和界面是绑定的。就像川剧变脸------你切换一张脸谱(改状态),观众看到的脸就变了(界面更新)。你不需要手动操作 DOM,React 帮你做好了。

5.3 WebGPU 检测:一行代码判断浏览器能力

tsx 复制代码
const IS_WEBGPU_AVAILABLE = !!navigator.gpu

这行代码值得拆开理解:

表达式 含义
navigator.gpu 浏览器是否暴露 GPU 接口。支持 WebGPU → 返回对象;不支持 → undefined
!navigator.gpu 取反。支持 → false;不支持 → true
!!navigator.gpu 再取反(双重否定等于肯定)。支持 → true;不支持 → false

!! 是一种将任意值强转为布尔值的 JS 技巧:

js 复制代码
!!{}          // true
!!undefined   // false
!!null        // false
!!0           // false
!!'hello'     // true

5.4 组件生命周期:useEffect 的执行时机

tsx 复制代码
useEffect(() => {
  console.log('组件已经挂载完成')
  setTimeout(() => {
    // setStatus('ready')  // 1 秒后将状态改为 ready
  }, 1000)
}, [])  // ← 空数组,只执行一次

useEffect 的第二个参数是关键:

第二个参数 执行时机
[](空数组) 组件首次渲染后执行一次
[status] 首次渲染后 + status 变化后执行
不传 每次渲染后都执行

这里的 [] 意味着"组件挂载完成时执行,只此一次"------非常适合做初始化操作(加载模型、请求数据等)。

5.5 JSX:在 JavaScript 里写 HTML

tsx 复制代码
return (
  IS_WEBGPU_AVAILABLE ? (
    <div className="flex flex-col h-screen ...">
      <h1 className="text-4xl font-bold mb-1">DeepSeek-R1 WebGPU</h1>
      {/* ... */}
    </div>
  ) : (
    <div>您的浏览器还不支持WebGPU</div>
  )
)

JSX(JavaScript XML) 是 React 最骄傲的特性之一------在 JS 代码中直接写 HTML 标签。

几个 JSX 核心规则:

① 条件渲染:三目运算符

tsx 复制代码
{condition ? <ComponentA /> : <ComponentB />}

② 列表渲染:.map()

tsx 复制代码
{items.map(item => <li key={item.id}>{item.name}</li>)}

③ 嵌入 JS 表达式:{} 大括号

tsx 复制代码
<p>计算结果:{1 + 1}</p>           // → 计算结果:2
<p>用户名:{user.name}</p>         // → 用户名:张三

④ 注释:大括号包裹

tsx 复制代码
{/* 这是 JSX 注释,和 JS 多行注释一样的写法 */}

⑤ 条件显示:&& 短路

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

error 为空字符串或 null 时,&& 右边不执行,整个 <div> 不渲染。这是 React 中极常用的条件渲染模式。

5.6 Tailwind 原子类实战解读

来看看项目中用到的关键原子类:

tsx 复制代码
<div className="flex flex-col h-screen mx-auto items-center justify-end text-gray-800 bg-white">
类名 对应 CSS 含义
flex display: flex 开启弹性布局
flex-col flex-direction: column 主轴方向为垂直(从上到下)
h-screen height: 100vh 高度 = 整个屏幕高度
mx-auto margin-left: auto; margin-right: auto 水平居中
items-center align-items: center 子元素垂直居中
justify-end justify-content: flex-end 子元素靠底部对齐
text-gray-800 color: #1f2937 文字颜色
bg-white background-color: white 背景色

自定义值的语法:

tsx 复制代码
<div className="max-w-[400px]">  {/* 方括号内是自定义值 */}

[] 允许你使用 Tailwind 预设之外的任意值。这里 max-w-[400px] 等价于 max-width: 400px

1rem = 4 是 Tailwind 的默认尺寸单位映射:p-1 = 4px,p-4 = 16px,以此类推。

5.7 模型信息展示区解析

tsx 复制代码
<p className="mx-w-[510px] mb-4">
  You are about to load
  <a
    href="https://huggingface.co/onnx-community/DeepSeek-R1-Distill-Qwen-1.5B-ONNX"
    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="underline"
  >
    🤗&nbsp;Transformers.js
  </a>
  and ONNX Runtime Web, meaning no data is sent to a server.
</p>

两个关键链接指向的技术:

  • DeepSeek-R1-Distill-Qwen-1.5B-ONNX:模型托管在 HuggingFace。HuggingFace 是全球最大的开源模型社区,被称为 AI 界的 GitHub。
  • Transformers.js:HuggingFace 推出的 JavaScript 库,让你在浏览器中加载和推理 Transformer 模型,无需后端服务。
  • ONNX Runtime Web:微软的 ONNX 运行时浏览器版,负责在 WebGPU 上高效执行模型推理。

这两个库配合 WebGPU,让"浏览器跑大模型"从不可能变成了现实。

5.8 错误处理状态

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 有值时(非空字符串),显示红色错误提示。当错误被清除(setError(null)setError('')),错误提示自动消失。这就是"响应式条件渲染"------你只需要改数据,界面自己会跟着变。


六、全文总结

本文从一个真实的浏览器端 AI 推理项目出发,系统梳理了以下技术链路:

  1. 端侧模型:模型从云端走向本地,从服务器走向浏览器。核心理念是"数据不出设备",WebGPU 是浏览器端 AI 的关键基础设施。
  2. React + TypeScript:AI 时代大型前端项目的首选技术栈。函数式组件 + Hooks 模式提供了强大的抽象能力。
  3. TailwindCSS:原子化 CSS 框架,用"堆类名"替代"写 CSS",开发效率翻倍。Vite 插件在构建时按需注入样式。
  4. React 组件化:函数 = 组件,JSX = 模板。所有 JavaScript 能力直接用于 UI 表达。
  5. 状态驱动useState + useEffect 实现响应式数据绑定,数据变化自动驱动界面更新。

七、核心知识点复盘

序号 知识点 一句话总结
1 端侧模型 LLM 运行在用户设备上,数据不出设备,隐私安全
2 ONNX 开放神经网络交换格式,跨框架跨平台的模型标准
3 HuggingFace 全球最大开源模型社区,AI 界的 GitHub
4 WebGPU 浏览器原生 GPU API,替代 WebGL 做高性能计算
5 useState React 状态钩子,创建响应式数据 [值, 更新函数]
6 useEffect React 副作用钩子,组件渲染后执行,第二个参数控制执行时机
7 !! 双重否定强转布尔值,!!undefined = false!!{} = true
8 JSX JavaScript XML,在 JS 中写 HTML,React 的核心语法
9 className JSX 中替代 class(因为 class 是 JS 关键字)
10 Tailwind 原子化 CSS 框架,类名即样式,按需生成,不写 CSS 文件
11 Vite 插件 扩展 Vite 能力(处理 JSX、Tailwind 等),像搭积木
12 ESLint 代码约束工具,确保团队代码风格一致
13 条件渲染 {condition && <Component />} 或三目运算符
14 响应式 数据变化 → 界面自动更新,无需手动操作 DOM

八、常见问题 / 避坑指南

Q1:!!navigator.gpuBoolean(navigator.gpu) 有区别吗?

没有本质区别,效果一样。!! 更简洁,是 JS 社区的惯用写法。不推荐 new Boolean()

Q2:useEffect 第二个参数传空数组 [] 时,函数什么时候执行?

组件首次挂载 到 DOM 后执行一次。类比 Vue 的 mounted() 生命周期钩子。

Q3:为什么不直接在 useState 里写 useState(null) => useState("出错了") 会怎样?

不会怎样,初始值只是"第一次渲染时"的状态。后续通过 setError 更新。这里给 "出错了" 是为了演示错误状态 UI。

Q4:Tailwind @import "tailwindcss" 报错怎么办?

检查 vite.config.ts 中是否添加了 tailwindcss() 插件。Tailwind v4 通过 Vite 插件工作,不需要手动安装 PostCSS。

Q5:为什么组件函数里 console.log 会执行多次?

React 在开发模式(StrictMode)下会故意渲染两次来帮你发现副作用问题。生产环境不会。这是正常的,不用担心。

Q6:模型文件 5.5GB,浏览器怎么存得下?

模型通过 Transformers.js 分片下载后会缓存在浏览器的 Cache Storage 中。第二次访问时直接从缓存加载,不需要重新下载。离线也能用。


项目地址github.com/onnx-community/DeepSeek-R1-Distill-Qwen-1.5B-ONNX

技术栈:React 19 + TypeScript 6 + Vite 8 + TailwindCSS 4 + WebGPU + Transformers.js + ONNX Runtime Web

相关推荐
张元清2 小时前
React useCookie Hook:把 Cookie 变成响应式状态(2026)
javascript·react.js
索西引擎3 小时前
【React】Immer.js 在现代 Redux 生态中的角色:不可变性保障的工程化实现与开发体验优化
前端·javascript·react.js
kisshyshy3 小时前
《川剧变脸 × React 状态管理?我在浏览器里跑了个端侧大模型》
前端·react.js·架构
只一3 小时前
端侧AI实战第二章:React组件工程化 + 事件系统 + 可复用进度条(WebGPU模型加载底座)
前端·react.js
GuWen_yue3 小时前
Cursor黑盒拆解!1套LangChain.js手写Mini编程Agent,自动生成React项目,效率提升60%
javascript·react.js·langchain
用户298698530143 小时前
React 项目中灵活管理 Excel 工作表:增删与移动
javascript·react.js·excel
bonechips4 小时前
React + WebGPU(二):合成事件与进度条组件封装
前端·react.js
我是大卫4 小时前
【图】React源码解析-从底层数据结构、渲染调度到闭包原理深挖useRef的核心机制
前端·react.js·源码
何时梦醒4 小时前
# ⚛️ React 19 + TypeScript 深度学习笔记 —— 从组件化思维到 WebGPU 端侧 AI 落地(续)
人工智能·react.js