编程 Meta 的豪赌:React Compiler 全面迁移 Rust,性能提升 10 倍背后的工程真相

2026-08-01 12:14:09 +0800 CST views 10

Meta 的豪赌:React Compiler 全面迁移 Rust,性能提升 10 倍背后的工程真相

2026年7月,Meta 做出了一个让整个前端圈侧目的决定——将 React Compiler 从 TypeScript 重写到 Rust,并将实验性移植版本合并到了 React 主仓库。这一改,被社区称为"React 史上最大规模的技术债务置换"。TypeScript 变 Rust,3 倍基础提速、局部 10 倍峰值提升、Next.js 路由编译最高 50% 加速,以及一个被 LLM 大量参与的重写过程。

这篇文章不追热点,我们往深里挖:Rust 版本到底改了什么?Arena 分配和索引数据结构怎么把编译器提速一个数量级?Turbopack 集成为什么能消除 40% 构建开销?LLM 参与移植对工程团队意味着什么?以及,这波 Rust 化浪潮对前端工具链的生态意味着什么?


一、React Compiler 是什么,它在解决什么问题

要理解这次迁移的价值,首先得知道 React Compiler 到底是干什么的。

React Compiler 前身叫 React Forget,是 Meta 内部为了解决"大量 useMemo 和 useCallback 手动优化代码难以维护"这一问题而开发的自动化编译器。它的核心职责是:在编译期自动推断组件和 Hooks 的 memoization 需求,并把原本需要开发者手动写的 useMemo、useCallback、React.memo 全部自动生成。

1.1 从手动优化到编译器智能

一个典型的 React 性能优化代码是这样的:

// 优化前:开发者手动写
const ExpensiveList = React.memo(({ items, filter }) => {
  const filtered = useMemo(
    () => items.filter(i => i.type === filter),
    [items, filter]
  );
  const handleClick = useCallback((id) => {
    dispatch({ type: 'SELECT', id });
  }, [dispatch]);

  return filtered.map(item => (
    <Item key={item.id} item={item} onClick={handleClick} />
  ));
});

有了 React Compiler,同样的逻辑可以写成语义等价但"不手写优化"的样子:

// 优化后:编译器自动推导 memoization
const ExpensiveList = ({ items, filter }) => {
  const filtered = items.filter(i => i.type === filter);
  const handleClick = (id) => {
    dispatch({ type: 'SELECT', id });
  };

  return filtered.map(item => (
    <Item key={item.id} item={item} onClick={handleClick} />
  ));
};
// React Compiler 自动推断:filtered 需要 memoization,
// handleClick 因为依赖了 dispatch 也需要 stable 化

编译器的推导基于一套严格的规则——如果一个变量的值在给定依赖下是可推断的,且该值被多个地方引用,编译器就会自动插入缓存语义。这个过程本质上是编译器级别的依赖追踪和值域分析,对性能要求极高。

1.2 原版 TypeScript 实现的核心瓶颈

React Compiler 的原版实现用 TypeScript 编写,作为 Babel 插件运行。它的核心流程是:

  1. 解析:将 JSX/TSX 解析为 AST
  2. HIR 构建:将 AST 降级为一种中间表示——High-level IR,保留类型信息和控制流
  3. 优化遍次:在 HIR 上运行多次优化(死代码消除、公共子表达式、别名分析等)
  4. 代码生成:将优化后的 HIR 重新生成为 JavaScript/TypeScript

原版 TypeScript 实现的瓶颈在哪里?不在算法,而在内存分配模式和热路径效率。

JavaScript 的对象系统是 GC 驱动的。AST 节点、HIR 节点在每次编译过程中都会被大量创建和销毁。在一个拥有数万行代码的 Next.js 项目中,编译一次可能需要创建数十万个 AST/HIR 节点。每个节点的创建都意味着堆分配,而 V8 的 GC 虽然高效,但在高密度、短生命周期的对象场景下,GC pause 会直接影响编译吞吐量。

此外,TypeScript 编译器本身(tsc)虽然做了大量优化,但它本质上是为类型检查设计的编译器框架,而不是为这种"创建大量临时对象 → 大量优化遍次 → 生成代码"流水线设计的。


二、Rust 版本的三重提速机制

Rust 版本并不是简单地把 TypeScript 翻译成 Rust 然后编译。它做了三个关键的架构改动,直接对应三个性能提升来源。

2.1 Arena 分配:告别 GC,让节点生命周期可控

Rust 版本的第一个关键改动是全面引入 Arena 分配(也称为线性分配器或池分配器)。

Arena 的工作原理极其简单:一次申请一大块内存,所有 AST/HIR 节点都在这块内存上顺序分配,编译器结束时整块释放。 没有单独的对象分配,没有引用计数,没有增量 GC。

// Arena 分配的核心思想示意
pub struct Arena<T> {
    // 预分配一个大块内存
    memory: Vec<T>,
    // 当前分配指针位置
    next: usize,
}

impl<T> Arena<T> {
    pub fn alloc(&mut self, value: T) -> &mut T {
        let ptr = &mut self.memory[self.next];
        *ptr = value; // 原地构造,无额外分配
        self.next += 1;
        ptr
    }

    // 编译器结束时调用 drop,整个 arena 一次性释放
    // 无需逐节点追踪和 GC
}

这带来的性能收益是颠覆性的:

对比维度TypeScript/JS GC 模式Rust Arena 模式
单次节点分配成本堆分配 + GC 元数据写入指针移动(~1 ns vs ~50-100 ns)
碎片化堆碎片化随时间累积无碎片,每次从 arena 头部分配
释放成本GC 增量标记-清除编译器结束一次性 drop
缓存友好性对象散落在堆各处节点连续排列,CPU 缓存命中率高

Arena 分配为何如此高效? 因为它完美匹配了编译器场景的特性:所有对象的生命周期与编译过程相同——编译结束,全部释放。这种"有序创建、集中销毁"的模式,正是 Arena 的最佳场景。

2.2 基于索引的数据结构:消除 Box,榨干 CPU 缓存

Rust 版本第二个关键改动是用基于索引的指针替代了传统的 Box<T>(堆分配指针)和 Rc<T>(引用计数指针)。

在 TypeScript 中,每个 AST 节点都是一个独立的对象,节点之间通过引用链连接:

// TypeScript 版本的节点引用方式
interface Node {
  id: string;
  kind: NodeKind;
  left: Node | null;   // 通过引用连接
  right: Node | null;
}

在 Rust 版本中,节点存储在 Arena 中,节点之间的引用通过数组索引而非指针实现:

// Rust 版本的节点存储和引用方式
pub struct Arena<T> {
    items: Vec<T>,       // 所有节点存储在 Vec 中
}

// 通过索引(u32)而非指针引用节点
#[derive(Clone, Copy)]
pub struct NodeId(u32);

// 节点定义
pub struct HIRNode {
    pub id: NodeId,
    pub kind: HIRKind,
    // children 存储的是 NodeId 数组,而非 Vec<&HIRNode>
    pub inputs: Vec<NodeId>,
    pub outputs: Vec<NodeId>,
}

这种方式带来的性能优势体现在两个层面:

第一,节点遍历的 CPU 缓存命中率极高。 Arena 中的节点按分配顺序紧密排列,当编译器遍历依赖图时,访问模式是线性的,CPU prefetcher 可以提前加载下一个节点到 L1/L2 缓存。相比之下,堆分配的节点在内存中是散落的,每次指针跳转都可能导致缓存 miss。

第二,Clone/Copy 零成本。 NodeId 是一个 4 字节的整数,Clone 和 Copy 没有任何开销。这意味着可以随意传递节点 ID 而不用担心所有权问题,编译器可以充分展开并行化:

// 并行处理多个函数
fn compile_functions(
    nodes: &[NodeId],  // 传入的是索引,无所有权问题
    arena: &mut NodeArena,
) {
    // 完全可以并行
    nodes.par_iter().for_each(|&id| {
        compile_function(id, arena);
    });
}

2.3 序列化开销的代价:为什么不是 10 倍而是 3 倍

前面提到,作为 Babel 插件时 Rust 版本提速约 3 倍(部分转换逻辑可达 10 倍),但整体不是 10 倍。主要原因是一个被低估的瓶颈:序列化开销

当 React Compiler 作为 Babel 插件运行时,它需要:

  1. Babel 将源代码解析为 Babel AST(JavaScript 对象)
  2. Babel 将 Babel AST 序列化为某种中间格式(JSON/二进制)
  3. Rust React Compiler 反序列化读取
  4. Rust 编译处理
  5. Rust 将结果序列化为中间格式
  6. Babel 反序列化
  7. Babel 生成目标代码

步骤 2、3、5、6 的序列化/反序列化往返,在每次编译中可能消耗 40-60% 的总时间。 这就是为什么 Rust 版本提速 3 倍,而不是理想状态下的 10 倍。

这引出了下一个关键变化——直接集成到 Turbopack,跳过 Babel 序列化层


三、Turbopack 集成:消除序列化后,速度提升的另一条路

真正让 Next.js 路由编译提升 20%-50% 的,不只是 Rust 编译器本身,而是 Rust 版本直接集成到 Turbopack

3.1 为什么 Babel 插件路径是瓶颈

在此之前,React Compiler 只能作为 Babel 插件运行。这意味着它在构建流水线中的位置是:

源代码 (.tsx)
  ↓
Babel Parser → Babel AST
  ↓
Babel Plugin: React Compiler → 编译转换
  ↓
Babel Generator → 代码生成
  ↓
Turbopack / Webpack 继续处理

问题在于:Babel Parser 和 Babel Generator 处理的是 Babel 的 AST 格式,这套格式是 JavaScript 生态通用的、面向工具互操作的,设计时优先考虑的是兼容性和易用性,而不是性能。 每一次 AST → 中间格式 → AST 的转换都意味着内存复制。

而当 Turbopack 引入 SWC(基于 Rust 的 TypeScript/JavaScript 编译器)后,编译流水线变成了:

源代码 (.tsx)
  ↓
SWC Parser → SWC AST(Rust 原生)
  ↓
直接调用 Rust React Compiler(无序列化!)
  ↓
SWC 继续处理,零序列化成本
  ↓
产物生成

Rust → Rust 的直接调用,跳过了所有序列化层。 这解释了为什么 Vercel 的工程师 Andrew Imm 在 Next.js v0 应用上测量到了超过 40% 的编译速度提升——那不是 Rust 编译器的功劳,而是序列化开销被消除的功劳。

3.2 Next.js 16.3 实验性支持:改动有多大

Next.js 官方在测试中测量到路由编译提升 20%-50%,并宣布将在 Next.js 16.3 中引入实验性支持。从技术角度看,这意味着 Next.js 团队需要对构建流水线做以下改动:

  1. 检测 React Compiler 配置:在 next.config.js 中启用 Rust 版本
  2. Turbopack 集成:确保 Turbopack 加载 Rust React Compiler 插件
  3. 错误处理路径:当编译器报告"无法安全编译某个组件"时,需要 Fallback 到 Babel 路径
// next.config.js 中启用 React Compiler(Rust 版本)
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    // 启用 React Compiler
    reactCompiler: true,
    // 指定编译器类型(Rust 版本 = experimentalReactCompiler)
    compilerReactDestructure: true,
  },
};

module.exports = nextConfig;

对于开发者来说,好消息是这个配置是直接替换的。如果你的代码此前已经适配了 Babel 版本的 React Compiler(即遵循了 React Compiler 的规则约束),迁移到 Rust 版本不需要任何额外改动。编译器会像原来一样工作,只是快了很多。


四、LLM 参与移植:工程史上的第一次大规模实验

这次移植最引发社区讨论的,不是性能数字,而是移植过程中大量使用了 LLM

Meta 的工程师在 PR 中描述这次移植是"一个实验性的、正在开发中的移植版本",而这个移植过程大量借助了大语言模型。人类工程师主要负责架构设计和代码审查,而 AST 节点的逐一翻译、HIR 结构的手动迁移等机械性工作,交给了 LLM 完成。

4.1 社区的反应:期待与忧虑并存

Hacker News 上的讨论非常有代表性。一位开发者表示支持向编译型语言迁移,但警告:

"偿还认知债务将会非常痛苦,无论最终哪个团队长期维护这套代码。"

另一位开发者则把这次移植与 Bun 项目做了类比:

"继 Bun 之后,这是又一个通过大量使用 LLM 进行移植的高关注度项目。非常好奇这些重写最终会如何发展。LLM 构建出来的基础是否足够可靠,可以在其上继续构建和迭代?还是说,这会导致项目最终变得无法维护,因为没有人真正理解实现细节?"

还有一个更具体的担忧:LLM 可能为了满足 Rust 借用检查器而写出隐藏问题的代码。

4.2 为什么 LLM 可能在 Rust 移植中引入隐患

这个担忧是有技术依据的。Rust 的所有权和借用系统是它区别于大多数语言的核心特性,也是最难掌握的概念之一。LLM 在生成 Rust 代码时,可能会:

选择不正确的所有权模式

// LLM 可能倾向于写出这样的"保守"代码:
fn process_node(node: &mut Node) {  // 过度使用引用
    // 处理逻辑
}

// 而不是更高效的:
fn process_node(node: Node) -> Node {  // 移动语义,无引用开销
    // 处理逻辑,返回新节点
}

用 RefCell 绕过借用检查器

// LLM 可能会这样做来"满足"借用检查器:
use std::cell::RefCell;

struct Graph {
    nodes: Vec<RefCell<Node>>,  // 用 RefCell 绕过编译期检查
}

RefCell 在运行时检查借用,如果同一时刻有多个可变借用同时存在,会在运行时 panic。这把本应该在编译期发现的问题推迟到了运行时,对于编译器这种对正确性要求极高的系统来说,这是非常危险的。

一个更安全的写法应该是用索引而非引用来构建依赖图——这正是 Rust 版本实际采用的方案,但 LLM 不一定能自主想到这个架构层面的优化。

4.3 这是否意味着 LLM 写 Rust 不可行?

不能这么下结论。这次移植的成功说明了两个事实:

第一,架构设计仍然由人来完成。 Arena + 索引数据结构的整体架构是人类工程师设计的,LLM 是在这个框架下完成节点级别的翻译工作。这是一种"人类负责架构,AI 负责实现"的工作模式,与"全权交给 AI 从零设计"完全不同。

第二,测试覆盖率是质量保障。 React Compiler 移植后的 1,725 个测试样例全部通过,且中间状态的输出与 TypeScript 版本"几乎逐字节匹配"。如此高的测试覆盖率,是保证移植质量的关键。这对所有使用 LLM 进行代码迁移的项目都是一个重要启示:没有充分测试覆盖的 LLM 迁移,不要交付给生产环境。


五、前端工具链的 Rust 大迁徙:生态全景

React Compiler 迁移 Rust,只是 2026 年前端工具链 Rust 化浪潮中的一朵浪花。

5.1 各路选手的 Rust 化进度

工具原实现Rust 版本状态
BabelJavaScriptSWC✅ 生产可用
TypeScript 编译器TypeScript官方 Rust 编译器 (rustc-based)✅ 官方推进中
OxcTypeScriptRust (NAPI 发布)✅ 生产可用
RspackJavaScript (Webpack fork)Rust 核心✅ 2.1 版本已支持原生 React Compiler
RolldownRust (Vite 底层)Rust⚠️ 集成 React Compiler 后撤回,做"去垃圾化"处理
ViteTypeScript/JavaScript规划中
RollupJavaScript

5.2 Rolldown 的教训:集成不是简单替换

Rolldown 团队(Boshen)的经历特别值得注意。他们曾尝试将 Rust React Compiler 集成到 Rolldown 中,测试结果显示性能提升最高可达 2 倍——但最终团队主动撤回了集成,原因是"需要先做去垃圾化处理"。

"去垃圾化"(de-slopify)是 Boshen 提出的一个概念,指的是 Rust 编译器生成的代码中,存在大量"因为 LLM 翻译而产生的、不符合 Rust 惯用风格的代码模式"。这些代码在功能上是正确的,但不符合 Rust 的最佳实践,长期维护会有隐患。

这给整个社区的启示是:Rust 化不只是"翻译",还需要"重构"。 移植后的代码需要 Rust 工程师按照 Rust 的惯用模式重新审视和优化,而不是简单的一对一翻译。

5.3 为什么是现在:WASM 冷启动问题的解决

为什么 Rust 化浪潮在 2026 年真正爆发,而不是更早?一个关键因素是 WebAssembly 冷启动问题得到了显著改善。

React Compiler 的 SWC 插件路径(将 Rust 编译为 WASM 供 Node.js 使用)此前受到 WASM 冷启动的严重制约——每次构建时,WASM 模块都需要经历实例化、内存分配、JIT 编译等步骤,这部分开销在高频率的增量构建中尤为突出。

Rust 版本的直接集成方案(跳过 WASM,直接本地调用)完美解决了这个问题。这也解释了为什么 Vercel 团队特别强调"之前的 SWC 插件路径受到了较长的 WebAssembly 冷启动影响"。


六、React Compiler 迁移 Rust 的深层意义:前端工程化的范式转移

6.1 从"能用就行"到"性能即功能"

React Compiler 的 Rust 化,代表了前端社区对性能观念的根本转变。传统上,前端工具链的性能问题被视为"可以忍受的烦恼"——"反正就慢几秒,喝杯咖啡就好了"。但随着:

  • Monorepo 普及:单个仓库包含数十万行代码,构建时间从秒级膨胀到分钟级
  • AI 编程工具普及:Claude Code、Grok Build 等工具在代码库中频繁运行编译器
  • SSR/SSG 模式深化:每次代码变更都需要重新编译并预渲染页面

构建性能已经从"个人体验问题"升级为工程效率瓶颈。React Compiler 提速 3-10 倍,意味着大型项目的编译时间可以从 5 分钟压缩到 1-2 分钟,这个收益是真实的工程价值。

6.2 Rust 在前端工具链的定位:底层基础设施语言

Rust 正在前端工具链中扮演一个越来越清晰的角色:底层基础设施语言。它不是用来替代 JavaScript/TypeScript 写业务逻辑的,而是用来写那些"被频繁调用、但逻辑相对稳定"的核心组件——编译器、解析器、打包器、优化器。

这个定位类似于 C++ 在游戏引擎和数据库系统中的位置:最适合写那些"底层确定,需求稳定,但性能敏感"的组件。

对于前端工程师来说,理解 Rust 在这个生态中的位置变得越来越重要。即使你不写 Rust,了解 Rust 工具链的工作原理,也能更好地理解"为什么我的构建变快了",以及"哪些优化是编译器做的,哪些是运行时做的"。

6.3 LLM 参与工程开发的新范式

React Compiler 移植中 LLM 扮演重要角色,揭示了一个越来越普遍的现象:LLM 最擅长的,不是写创意性的新代码,而是"在给定架构下做大量机械性翻译工作"。

这对工程团队有几点启示:

  1. 架构设计能力更值钱:能设计出好的 Arena + 索引结构的人,比能写出漂亮 React 组件的人更稀缺
  2. 测试覆盖是 LLM 迁移的生命线:没有 100% 测试覆盖的 LLM 迁移,就像没有安全网的杂技
  3. 代码审查的范式变了:审 Rust 代码不仅要懂 Rust 惯用模式,还要识别"从 TypeScript 直译过来的不符合 Rust 风格"的模式

七、开发者如何应对:实操指南

7.1 如果你正在使用 React Compiler

短期内:无需任何操作。现有的 Babel 版本会继续维护,Rust 版本是"可选的实验性升级"。按照 React 官方的说法,这是一个"直接替换"的升级,不需要修改配置(只要你的代码此前已经符合 React Compiler 的约束)。

中期(Next.js 16.3 发布后):可以在测试环境中启用 Rust 版本,观察是否有新的编译器错误。如果有,通常意味着你的代码中有"依赖隐式 memoization 行为"的地方——这其实是一个潜在 bug,Rust 版本帮你提前发现了。

// 这类代码在 React Compiler 下会报错,需要显式标注
// 如果你之前依赖编译器推断隐式处理了这类情况,现在需要修正
function ProblematicComponent() {
  const obj = { a: 1 }; // 每次渲染创建新对象
  useEffect(() => {
    console.log(obj.a); // 依赖的是隐式的引用稳定性
  });
  // 编译器会报"value assigned to 'obj' escapes its scope"
}

7.2 如果你想深入理解 Rust 在前端工具链的应用

推荐的学习路径:

第一层(了解生态)

  • 学习 SWC 的基本用法:它如何解析 TypeScript,如何生成代码
  • 理解 Rspack vs Webpack 的关系:为什么 Webpack 的 fork 可以快 5-10 倍

第二层(理解原理)

  • 学习 Arena 分配:这是一个跨语言的通用技术,理解它对理解数据库引擎(如 RocksDB)、游戏引擎(如 Unity DOTS)都有帮助
  • 学习索引图结构的工程价值:它是如何在 Rust 中实现无 GC 的图遍历的

第三层(动手实践)

  • 用 Rust 写一个简单的表达式解析器(不使用任何外部 AST 库)
  • 对比 Arena + 索引 vs Box + Rc 两种实现的性能差异
// 练习:用 Arena 写一个简单的表达式计算器
#[derive(Clone, Copy)]
pub struct ExprId(u32);

pub struct ExprArena {
    // 使用 Vec 模拟 Arena
    exprs: Vec<Expr>,
}

#[derive(Clone)]
pub enum Expr {
    Number(f64),
    Add(ExprId, ExprId),
    Multiply(ExprId, ExprId),
}

impl ExprArena {
    pub fn new() -> Self {
        ExprArena { exprs: Vec::new() }
    }

    pub fn add_number(&mut self, val: f64) -> ExprId {
        self.exprs.push(Expr::Number(val));
        ExprId((self.exprs.len() - 1) as u32)
    }

    pub fn add_add(&mut self, left: ExprId, right: ExprId) -> ExprId {
        self.exprs.push(Expr::Add(left, right));
        ExprId((self.exprs.len() - 1) as u32)
    }

    // 求值函数,通过索引而非引用递归
    pub fn eval(&self, id: ExprId) -> f64 {
        match &self.exprs[id.0 as usize] {
            Expr::Number(v) => *v,
            Expr::Add(l, r) => self.eval(*l) + self.eval(*r),
            Expr::Multiply(l, r) => self.eval(*l) * self.eval(*r),
        }
    }
}

7.3 如果你对 LLM 参与工程开发有顾虑

核心建议是:把 LLM 当作一个非常高效的"翻译工人",而不是一个"架构师"。

最好的使用模式:

  • 给 LLM 清晰、具体的指令:告诉它"Arena 的使用方式",而不是"写一个高性能的 AST"
  • 严格的 Review 流程:Rust 编译通过不等于代码质量好,需要人工审查所有权模式和内存使用
  • 增量验证:每次 LLM 的改动都要有对应的测试验证,不要做大批量的 LLM 改写

总结:这不是一次简单的语言迁移

React Compiler 从 TypeScript 到 Rust 的迁移,表面上看是一次技术栈更新,实际上折射出了前端工程化正在经历的三个深层变化:

第一,性能优化的边界在持续前移。 从运行时优化(JIT、V8 优化)到编译期优化(Babel 插件),再到编译器底层优化(Arena + 索引结构),优化的战场在不断向更底层迁移。React Compiler 提速 3-10 倍的来源,有一半来自 Rust 语言本身,一半来自架构层面的重新设计(消除序列化、Arena 分配)。

第二,工具链的"平台化"趋势加速。 Rust 在前端工具链中的地位已经确立——不是"备选",而是"事实标准"。SWC、Rspack、Rolldown、Oxc 共同构成了一个活跃的 Rust 工具生态,这个生态的活跃度在 2026 年已经超过了 TypeScript 生态中对应的部分。

第三,AI 参与工程开发正在从"噱头"变为"现实"。 React Compiler 移植中 LLM 的成功参与,说明 AI 在工程化场景中确实可以发挥作用——但前提是人类工程师在架构、测试、Review 环节投入足够的精力。AI 是加速器,不是替代者。

对于前端开发者来说,这次迁移最直接的意义是:你的构建会变快,而理解为什么变快,会让你对前端工具链的理解更深一层。


参考来源

  • Meta React Compiler PR (GitHub)
  • Rust Bytes Newsletter: React Compiler Rust Port
  • Vercel Engineer Andrew Imm (@AndrewImm), Turbopack Integration Notes
  • InfoQ: Meta React Compiler Rust Migration Report (2026-07-31)
  • Rolldown by Boshen, React Compiler Integration Withdrawal Note
  • Next.js Official Changelog, Next.js 16.3 Roadmap

推荐文章

服务器购买推荐
2024-11-18 23:48:02 +0800 CST
Vue3 vue-office 插件实现 Word 预览
2024-11-19 02:19:34 +0800 CST
Vue3 组件间通信的多种方式
2024-11-19 02:57:47 +0800 CST
25个实用的JavaScript单行代码片段
2024-11-18 04:59:49 +0800 CST
gin整合go-assets进行打包模版文件
2024-11-18 09:48:51 +0800 CST
IP地址获取函数
2024-11-19 00:03:29 +0800 CST
Vue3中的虚拟滚动有哪些改进?
2024-11-18 23:58:18 +0800 CST
程序员茄子在线接单