React Compiler 用 Rust 重写了,编译提速 10 倍——手写 useMemo 的日子到头了

几周前,微软把 TypeScript 编译器用 Go 重写,速度暴增 10 倍。

几周后,Meta 把 React Compiler 用 Rust 重写,核心转换逻辑同样快了 10 倍。

两件事隔着大洋接力发生,指向同一个结论:前端工具链的核心引擎,正在全面逃离 JavaScript。

那么 React Compiler 的 Rust 重写到底做了什么、怎么用、有什么影响。

一、事件还原:PR #36173 合并主仓

2026 年 6 月 18 日,GitHub 上 facebook/react 仓库合并了一个编号为 #36173 的 Pull Request------React Compiler 的完整 Rust 移植版正式进入主干

主导这次移植的是 Meta 的 React 核心团队成员 Joseph Savona。整个 PR 包含 330 个 commits,新增超过 123,000 行 Rust 代码。

在 PR 描述的最后一段,他写了一句话:

"开发者花了大量时间设定架构、测试策略、增量迁移方案,反复打磨代码质量......但大部分代码是 AI 生成的。"

这句话在前端圈讨论了一周。架构是人设计的,代码是 AI 写的,1725 个测试用例一个不少地卡着质量线。这个模式后面再聊。

关键时间线

时间 事件
2024 React Compiler(原名 React Forget)首次公开,TS 实现
2025 末 React Compiler v1.0 稳定发布,已用于 Instagram 生产环境
2026-06-18 PR #36173 合并主仓,Rust 移植版进入主干
2026-07-23 InfoQ 英文报道发出,中文圈尚未跟进

7 月 23 日------InfoQ 发了英文报道,Hacker News 和 Reddit r/reactjs 都有热帖。中文内容几乎为零,这就是写这篇文章的理由。

二、React Compiler 是什么?为什么需要它?

如果你还没接触过 React Compiler,先搞清楚它解决什么问题。

手动 memoization 的痛苦

React 开发者对下面这段代码应该不陌生:

jsx 复制代码
import { useState, useMemo, useCallback } from 'react';

function TodoList({ todos }) {
  const [filter, setFilter] = useState('all');
  
  // 手动 memoization:每次写依赖数组都要小心翼翼
  const filteredTodos = useMemo(() => {
    return todos.filter(t => t.status === filter);
  }, [todos, filter]);

  const handleAdd = useCallback(() => {
    console.log('add todo');
  }, []);

  return (
    <div>
      <button onClick={handleAdd}>添加</button>
      {filteredTodos.map(t => (
        <div key={t.id}>{t.text}</div>
      ))}
    </div>
  );
}

useMemouseCallbackReact.memo------这三个 API 是 React 性能调优的"三板斧"。问题是:

  1. 心智负担大:每个变量都要想依赖数组写对没有
  2. 容易写错:漏一个依赖 → stale closure,多写一个 → 白跑
  3. 代码可读性差:逻辑被 hook 包了一层又一层
  4. 大多人干脆不写:反正不写也能跑,性能差一点而已

React Compiler 的目标就是让你不用再手动写 memoization。编译器在构建时自动分析组件依赖,自动插入缓存逻辑。

开启 Compiler 后,只需要这样写:

jsx 复制代码
import { useState } from 'react';

// 不写 useMemo、useCallback,干净利落
function TodoList({ todos }) {
  const [filter, setFilter] = useState('all');
  
  const filteredTodos = todos.filter(t => t.status === filter);

  const handleAdd = () => {
    console.log('add todo');
  };

  return (
    <div>
      <button onClick={handleAdd}>添加</button>
      {filteredTodos.map(t => (
        <div key={t.id}>{t.text}</div>
      ))}
    </div>
  );
}

代码更干净,性能不降反升------因为编译器能分析的维度比人手写依赖数组多得多。

三、为什么用 Rust 重写?

React Compiler 原本用 TypeScript 写的,跑在 Babel 上。功能没问题,但性能不够快。尤其大型项目(Next.js 企业应用动辄上千个组件),编译时间被 Compiler 拖长是常见抱怨。

Rust 版本的性能数据如下(来自 PR 描述和 Rspack 团队的基准测试):

场景 TS 版 → Rust 版 提升
Babel 插件模式总耗时 基准 → 快 3 倍 3x
核心转换逻辑(扣除序列化) 基准 → 快 10 倍 10x
Rspack 集成(SWC 原生) --- 7-13 倍

序列化瓶颈

Rust 版本在 Babel 插件模式下"只"快 3 倍,但核心转换逻辑快了 10 倍。差距主要来自跨语言 FFI 数据转换开销。

Babel 体系下 AST 是 JavaScript 对象,Rust 编译器需要把这些 JS 对象转换(Marshal)成 Rust 结构体才能处理,处理完再原路转换回去。这个数据编组(Marshal/Unmarshal)的过程占总耗时的 70% 左右,成了最大的性能瓶颈。

打个通俗的比方:Rust 编译器是一位超级快厨,但食材(Babel AST)得先完成"日译英"才能下锅,做完再"英译日"端给客人。翻译时间比颠勺炒菜还长。

这也是为什么原生集成(SWC、OXC、Rolldown)才是 Rust 版本真正的发力点------绕开 Babel 的跨语言 FFI 转换开销后,实测性能提升直接飙到 7-13 倍。

生产环境数据

这不是纸上谈兵的数据,React Compiler 已经在 Meta 的产品里跑了:

  • Instagram:首屏加载快 12%,交互响应快 2.5 倍
  • Meta Quest Store:类似提升
  • Sanity Studio:渲染时间降低 20-30%

四、架构拆解:不是重写,是逐 Pass 移植

Rust 版本和 TS 版本的架构完全相同,这不是重新设计,而是一次"逐 Pass 移植"。

Rust 版本与 TS 版本在编译流水线(Pipeline)和 Pass 设计上完全相同,是一次逐 Pass 移植;但在底层数据表示上,为了适配 Rust 所有权模型,Rust 版全面采用了 Arena + 索引模式,放弃了 GC 引用。

编译器内部流程

React Compiler 的核心流程分三步:

第一步:AST → HIR(高层中间表示)

编译器不直接操作 AST,而是先把 AST 转换成自己的中间表示 HIR(High-level Intermediate Representation)。HIR 使用控制流图(CFG)+ 静态单赋值(SSA)形式。

如果你不知道 CFG 和 SSA 是什么,简单来说:

  • CFG(控制流图):把代码的执行路径画成图,if/else、循环、异常都变成图的分支
  • SSA(静态单赋值):每个变量只赋值一次,再次修改等于创建新变量。这让数据流分析变得非常精确

这两个概念来自编译器经典理论,不是 React 发明的。Java 编译器、LLVM 都用这套。

第二步:多轮 Pass 分析

在 HIR 上,编译器跑多轮 Pass:

  • 依赖分析:哪些变量被哪些代码用到
  • 可变性问题检测:哪些值可能被修改
  • 自动 memoization 插入:决定哪些需要缓存

第三步:HIR → 代码生成

分析完后,生成最终代码(Babel AST 或目标格式)。

Rust 版的差异:Arena + 索引

架构相同,但数据表示做了调整。为了适配 Rust 的借用检查器(borrow checker),团队大量使用 arena 分配 + 索引模式。

简单来说:不用 Rust 的引用 &T 来指向数据,而是把所有数据放在一个连续内存池(arena)里,用整数索引来引用。这样做的原因是 Rust 的所有权模型在编译器这种"到处互相引用"的场景下会非常痛苦,arena + 索引绕开了这个痛点。

这是个务实的选择。不少 Rust 编译器项目(包括 rustc 自己)都采用类似模式。

公共 API:Babel AST 进、Babel AST 出

对外接口设计为"Rust Babel AST 进、Babel AST 出"。各集成方(Babel、OXC、SWC)负责在自己原生表示和这个中间格式之间做转换。

这意味着什么?不管你用什么构建工具,Compiler 的输出格式不变,集成方只需要处理 AST 转换层。

五、编译产物对比:Compiler 到底干了什么?

这是整篇文章最实在的部分。我跑了一个真实的编译对比,不是截图搬运。

原始代码(开启 Compiler 前)

上面那个 TodoList 组件,不经 Compiler 编译,输出如下:

js 复制代码
function TodoList(_ref) {
  var todos = _ref.todos;
  var filter = _ref.filter;
  
  var filteredTodos = (0, _react.useMemo)(function () {
    return todos.filter(function (t) {
      return t.status === filter;
    });
  }, [todos, filter]);  // ← useMemo 运行时调用
  
  var handleAdd = (0, _react.useCallback)(function () {
    console.log('add todo');
  }, []);  // ← useCallback 运行时调用
  
  // 每次渲染都重新创建 JSX
  return /*#__PURE__*/React.createElement("div", null, 
    /*#__PURE__*/React.createElement("button", { onClick: handleAdd }, "添加"), 
    filteredTodos.map(function (t) {
      return /*#__PURE__*/React.createElement("div", { key: t.id }, t.text);
    })
  );
}

可以看到,useMemouseCallback 都保留了------它们是运行时 hook,依赖 React 的 hook 系统来工作。

编译后(开启 React Compiler)

同一个组件,经过 React Compiler 编译后:

js 复制代码
var _compilerRuntime = require("react/compiler-runtime");
var _react = require("react");

function TodoList(t0) {
  var $ = (0, _compilerRuntime.c)(10);  // ← 创建 10 个缓存槽
  var todos = t0.todos;
  
  // useState 调用保留,但 setFilter 被忽略(未使用就不解构出来)
  var _useState = (0, _react.useState)("all"),
      _useState2 = _slicedToArray(_useState, 1),
      filter = _useState2[0];
  
  // ① filteredTodos 的缓存逻辑
  var t1;
  if ($[0] !== filter || $[1] !== todos) {
    // 依赖变了,重新计算
    var _t;
    if ($[3] !== filter) {
      _t = function _t(t) { return t.status === filter; };
      $[3] = filter;
      $[4] = _t;
    } else {
      _t = $[4];  // filter 没变,复用回调
    }
    t1 = todos.filter(_t);
    $[0] = filter;
    $[1] = todos;
    $[2] = t1;  // 缓存结果
  } else {
    t1 = $[2];  // 依赖没变,直接用缓存
  }
  var filteredTodos = t1;
  
  // ② handleAdd 被提取到模块作用域(见下方 _temp)
  var handleAdd = _temp;
  
  // ③ button 元素也缓存了(sentinel = 首次创建标记)
  var t2;
  if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
    t2 = React.createElement("button", { onClick: handleAdd }, "添加");
    $[5] = t2;  // 首次创建,存起来
  } else {
    t2 = $[5];  // 后续渲染直接复用
  }
  
  // ④ map 结果也缓存
  var t3;
  if ($[6] !== filteredTodos) {
    t3 = filteredTodos.map(_temp2);  // _temp2 被提取到模块作用域
    $[6] = filteredTodos;
    $[7] = t3;
  } else {
    t3 = $[7];
  }
  
  // ⑤ 最外层 div 也缓存
  var t4;
  if ($[8] !== t3) {
    t4 = React.createElement("div", null, t2, t3);
    $[8] = t3;
    $[9] = t4;
  } else {
    t4 = $[9];
  }
  return t4;
}

// 提取到模块作用域的函数
function _temp2(t_0) {
  return React.createElement("div", { key: t_0.id }, t_0.text);
}
function _temp() {
  console.log('add todo');
}

产物分析:Compiler 做了 5 件事

1. 创建缓存池 $

c(10) 创建了 10 个缓存槽,用来存中间计算结果和 JSX 元素。组件每渲染一次,就检查这些缓存槽,看依赖有没有变。

2. 替代 useMemo

原来的 useMemo 变成了 $[0] !== filter || $[1] !== todos 的手动检查。逻辑一样:依赖变了就重算,没变就取缓存。但省掉了 React hook 的运行时开销。

3. 替代 useCallback

handleAdd 没有任何依赖,被直接提取成模块级函数 _temp。不管渲染多少次,同一个函数引用,不需要 useCallback 包了。

4. 缓存 JSX 元素

button 元素用 sentinel(哨兵值)标记首次创建,后续渲染直接复用。<div> 包裹层也是同理,只有 t3(map 结果)变了才重建。

5. 提取回调函数

.map() 的回调和 reduce 的回调被提取到模块作用域(_temp2_temp),不再每次渲染都创建新的函数实例。

一句话总结useMemouseCallbackReact.memo 这三个 API 在 Compiler 开启后基本不需要手写了。编译器比人更清楚该缓存什么。

六、实战:怎么在项目里用?

方式一:Babel 插件(通用,所有 React 19+ 项目可用)

⚠️ 前置条件:请确保项目已升级至 React 19.0+,否则 Compiler 无法生效。React 18.x 及以下版本不兼容。

安装依赖:

bash 复制代码
npm install babel-plugin-react-compiler@1.0.0

在 Babel 配置里加插件:

js 复制代码
// babel.config.js
module.exports = {
  presets: ['@babel/preset-react'],
  plugins: [
    ['babel-plugin-react-compiler', { compilationMode: 'infer' }]
  ]
};

compilationMode: 'infer' 是默认模式,编译器自动决定哪些组件需要优化。也可以设为 'all' 强制所有组件都优化,或者 'annotation' 只优化标注了 "use memo" 的组件。

方式二:Vite + @vitejs/plugin-react

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

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          ['babel-plugin-react-compiler', { compilationMode: 'infer' }]
        ]
      }
    })
  ]
});

方式三:Next.js

js 复制代码
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    reactCompiler: true
  }
};
module.exports = nextConfig;

Next.js 16+ 内置了 React Compiler 支持,只需要一行配置。

方式四:ESLint 插件(强烈推荐配在一起用)

光开 Compiler 不够,还得装 ESLint 插件来提前发现不合规代码:

bash 复制代码
npm install eslint-plugin-react-compiler@19.1.0-rc.2
js 复制代码
// eslint.config.js
import reactCompiler from 'eslint-plugin-react-compiler';

export default [
  {
    plugins: {
      'react-compiler': reactCompiler
    },
    rules: {
      'react-compiler/react-compiler': 'warn'  // 警告级别,不阻塞构建
    }
  }
];

装完之后,ESLint 会在你写了不合规代码时直接报警。比如:

sql 复制代码
src/BadComponent.jsx
  18:31  warning  Hooks must always be called in a consistent order,
          and may not be called conditionally.

我把所有配置都实际跑了一遍,这些代码是验证过的,不是文档搬运。

七、Rules of React:哪些写法会让 Compiler 失效?

React Compiler 不是开了就万事大吉。它有一套**"Rules of React"**------组件必须遵守的规则。违反规则的组件,Compiler 要么跳过优化,要么产出有 bug 的代码。

规则一:不要在 render 中修改 state

jsx 复制代码
// ❌ 违规:render 中调用 setState
function Bad({ items }) {
  const [count, setCount] = useState(0);
  
  if (count === 0) {
    setCount(1);  // render 中改 state → 无限重渲染
  }
  
  return <div>{count}</div>;
}

正确做法:在事件处理或 useEffect 里改 state。

规则二:不要在 render 中修改 props

jsx 复制代码
// ❌ 违规:直接修改传入的 props
function Bad({ items }) {
  items.push({ id: 999, name: 'new' });  // 修改 props 引用
  return items.map(item => <div key={item.id}>{item.name}</div>);
}

正确做法:把 props 当只读的,需要修改就先复制一份。

规则三:Hooks 不能条件调用

jsx 复制代码
// ❌ 违规:条件里调 hooks
function Bad({ items }) {
  const [count, setCount] = useState(0);
  
  if (items.length > 0) {
    const [extra, setExtra] = useState(0);  // ← 条件 hook,被 ESLint 抓到
  }
  
  return <div>{count}</div>;
}

ESLint 会直接报错:

sql 复制代码
18:31  error  React Hook "useState" is called conditionally. 
              React Hooks must be called in the exact same order 
              in every component render

规则四:不要在 render 中修改 ref.current

jsx 复制代码
// ❌ 违规:render 中改 ref
function Bad() {
  const ref = useRef({ value: 0 });
  ref.current.value = computeSomething();  // render 中改 ref
  
  return <div>{ref.current.value}</div>;
}

正确做法:将 ref 的可变操作移至事件处理函数useLayoutEffect 中(如果你必须读取布局信息)。如果仅仅是为了存储实例,初始化时在 useRef 的回调中完成即可。

规则五:副作用只能放在 useEffect 里

jsx 复制代码
// ❌ 违规:render 中执行副作用
function Bad({ userId }) {
  const [data, setData] = useState(null);
  
  // 不能在 render 中发请求
  fetch(`/api/user/${userId}`).then(r => r.json()).then(setData);
  
  return <div>{data?.name}</div>;
}

这个规则不是 React Compiler 新发明的------React 一直有这些规则,只是以前没 Compiler 的时候不遵守也不会立刻炸。Compiler 会假设你的组件遵守规则来做优化分析,不遵守的组件产出可能有 bug。

我的建议 :开 ESLint 插件 + compilationMode: 'infer'(默认模式),让 Compiler 自己决定哪些组件跳过。这样即使有老代码不合规,也不会被错误优化。

八、生态集成:Rust 版本的全家桶

Rust 版本合并主仓后,前端工具链全线跟进:

工具 状态 说明
Oxlint 1.70 ✅ 已发布 新增 react/react-compiler 规则
SWC ✅ 已合并 Rust crate v68.1 发布,原生支持
Rspack 2.1 🔄 Beta 内置 builtin:swc-loader 直接启用 Compiler
Rolldown / Vite 8 ✅ PR 合并 选项已暴露,Vite 8 正式支持
Next.js 🔄 Canary v16.3.0-canary.52 集成,预计 16.4 稳定

补充:Next.js 的 experimental.reactCompiler 在最新的 Canary 中已经默认启用了 Turbopack 的 Rust 原生通道 ,这意味着如果你用 Next.js 16+ 搭配 Turbopack,整个链路(解析、转译、编译)完全绕开了 Babel 和 JS,性能提升直接拉满到 13 倍(而不再是受限于 Babel 模式的 3 倍)

简单来说,如果你用 Vite 8 或 Rspack 2.1,原生集成后不需要走 Babel 序列化了------直接吃 Rust 速度。

九、123K 行代码大部分是 AI 写的------这说明了什么?

回到 PR 描述里那句话。Joseph Savona 直接说了:架构是人设计的,但大部分代码是 AI 生成的

这不是随口一说,有具体做法:

  1. 人设计架构:HIR、CFG、SSA、arena 这套数据结构是人定的
  2. 人定测试策略:1725 个测试用例 100% 通过是硬指标
  3. 人定增量方案:不是一把梭重写,而是分 pass 逐步迁移
  4. AI 在约束下生成代码:有了架构、测试、约束,AI 来填实现

这个模式跟 Bun 用 Claude 在 11 天内把 53 万行 Zig 重写为 Rust 如出一辙。

有人可能担心"AI 写的代码靠谱吗"。这里关键在于:约束比代码更重要。当架构设计、测试用例、质量门槛都定好之后,AI 生成的代码必须通过所有测试才能合并。这比很多人写的代码还严格。

当然,也不能过度解读。这是编译器移植------一个架构清晰、测试完善的场景。不是所有项目都适合这种模式。但这个 PR 确实展示了"AI 辅助大型代码迁移"的一个标杆案例。

十、和你之前听过的 TS 7.0 是什么关系?

如果你读过 TypeScript 7.0 那篇(用 Go 重写编译器,速度暴增 10 倍),会发现 React Compiler Rust 移植是同一条故事线的第二集

维度 TS 7.0 React Compiler Rust
重写前语言 TypeScript TypeScript
重写后语言 Go Rust
性能提升 ~10 倍 3-10 倍(Babel 模式 / 核心逻辑)
主导者 微软 Meta
AI 辅助 未披露 大部分代码 AI 生成
开发者影响 编译速度 不用再写 useMemo/useCallback

前端工具链核心引擎从 JavaScript 逃离,这不是一个趋势------这是两个已经落地的事实。Babel 可能是下一个(OXC 已经在做 Rust 版了)。

十一、总结

React Compiler 的 Rust 移植做了三件事:

  1. 编译器本体从 TS 换成 Rust------架构不变,性能 3-10 倍
  2. 生态全线适配------SWC、OXC、Rspack、Vite 8、Next.js 已就位
  3. 展示了 AI 辅助大型代码迁移的范式------人设计约束,AI 填代码

对开发者来说,最直接的影响是:useMemouseCallbackReact.memo 这三个 API 正在变成历史名词。编译器比人更清楚该缓存什么。

如果你用 React 19+,今天就可以开起来。配 ESLint 插件一起用,提前发现不合规代码。

sql 复制代码
前端工具链 Rust/Go 化进度:
  TypeScript 7.0     ✅ Go 重写,已发布
  React Compiler     ✅ Rust 移植,已合并主仓
  Babel              🔄 OXC(Rust 版)进行中
  Webpack            🔄 Rspack(Rust 版)已发布
  Rollup             🔄 Rolldown(Rust 版)进行中
  ESLint             🔄 Oxlint(Rust 版)已发布
  Prettier           🅾️ 暂无 Rust 版

相关面试题

Q1:React Compiler 的核心作用是什么?它替代了哪些手动 API?

:React Compiler 是一个构建时编译器,核心作用是自动分析组件依赖关系,自动插入 memoization 逻辑。它替代了手动写的 useMemouseCallbackReact.memo。开启后,开发者不需要再手写依赖数组和包裹函数,编译器会自动缓存计算结果、回调函数和 JSX 元素。

Q2:React Compiler 的编译流程分哪几步?HIR、CFG、SSA 各是什么?

:编译流程分三步:

  1. AST → HIR:将 Babel AST 转换成编译器内部的高层中间表示(HIR)。HIR 使用控制流图(CFG)+ 静态单赋值(SSA)形式。CFG 把代码执行路径画成图,SSA 要求每个变量只赋值一次,使数据流分析更精确。
  2. 多轮 Pass 分析:在 HIR 上跑依赖分析、可变性检测、memoization 插入。
  3. HIR → 代码生成:分析完生成最终代码。

Q3:React Compiler 的缓存机制是怎么工作的?

:Compiler 为每个组件创建一个缓存池 $(通过 react/compiler-runtimec(n) 函数),包含 N 个缓存槽。每个缓存槽存一个中间结果(计算值、回调函数、JSX 元素等)。渲染时检查缓存槽对应的依赖是否变化,变了就重算并存新值,没变就直接取缓存。对于无依赖的函数用 sentinel(哨兵值)标记首次创建后永久复用。这套机制比手写 useMemo 的依赖数组更精确,因为它能追踪到每个子表达式的依赖变化。

Q4:什么是"Rules of React"?违反了会怎样?

:Rules of React 是 React 组件必须遵守的规则,包括:不要在 render 中修改 state、不要修改 props、Hooks 不能条件调用、不要在 render 中修改 ref.current、副作用只能放在 useEffect 里。违反规则后,Compiler 可能跳过该组件的优化(bailout),或者产出有 bug 的代码。推荐配 eslint-plugin-react-compiler 插件,在编码阶段就发现违规。

Q5:React Compiler 的 Rust 版本和 TS 版本在架构上有什么区别?

:编译器先将 AST 转换为基于控制流图(CFG)的 HIR ,并在该 IR 中强制使用**静态单赋值(SSA)**形式,以此为基础进行多轮 Pass 分析,Rust 版是"逐 Pass 移植",不是重新设计。主要差异在数据表示:为了适配 Rust 的借用检查器,Rust 版大量使用 arena 分配 + 索引模式(把数据放在连续内存池里用整数索引引用),而不是用 Rust 的引用 &T。公共 API 设计为"Babel AST 进、Babel AST 出",各集成方负责自己原生表示和这个中间格式之间的转换。性能上,Babel 插件模式快 3 倍(受序列化开销限制),核心转换逻辑快 10 倍,原生集成(SWC/OXC)可达 7-13 倍。

相关推荐
Ivanqhz2 小时前
Rust Lazy浅析
java·javascript·rust
寒水馨5 小时前
Windows下载、安装 Codex CLI(附安装包codex-x86_64-pc-windows-msvc.exe)
windows·rust·codex·终端工具·自动化编程·codex cli·ai编码代理
k4m7v2pz5 小时前
rvs(rust-verb-shell)v26.7.12 更新:借鉴 ripgrep、自身可观测、修了 mkdir
rust·shell
张元清6 小时前
React useDeepCompareEffect:修复 useEffect 的对象依赖问题(2026)
javascript·react.js
用户298698530147 小时前
React 前端处理 Excel 工作表复制的技术实践
javascript·react.js·excel
用户202582944507 小时前
给下载器写插件:一个 manifest.json + 30 行 JavaScript,做一个 GitHub 下载加速解析器
rust
光影少年7 小时前
RN原生交互 & 桥接
前端·javascript·react native·react.js·前端框架
GuWenyue7 小时前
踩坑无数!吃透useState 3个核心技巧,彻底解决状态旧值、性能卡顿问题
前端·javascript·react.js
无人生还7 小时前
从 Vue3 到 React · 快速上手系列第 4 篇:条件渲染与列表渲染
前端·vue.js·react.js