编程 TypeScript 7.0 深度实战:Go 重写编译器如何把 10 倍提速变成现实——从 tsgo 架构原理到生产迁移全链路拆解

2026-08-16 11:44:27 +0800 CST views 7

TypeScript 7.0 深度实战:Go 重写编译器如何把 10 倍提速变成现实——从 tsgo 架构原理到生产迁移全链路拆解

2026 年 7 月 8 日,微软正式发布 TypeScript 7.0。这是 TypeScript 自 2012 年诞生以来最大的一次底层变革:编译器从 TypeScript/JavaScript 自举实现,整体迁移到 Go 语言。官方口径:完整构建(full build)场景通常带来 8~12 倍性能提升,平均约 10 倍。

对一线开发者的冲击是直接的:冷启动从"倒杯水"变成"眨个眼";CI 里动辄几分钟的 typecheck 步骤压缩到几十秒;编辑器里打开巨型 monorepo 不再转圈。

但真正值得关注的不只是"快了多少",而是"为什么能快这么多"以及"代价是什么"。这篇文章从编译器内部结构拆起:五阶段流水线怎么工作、为什么 JS 实现慢、tsgo 怎么用共享内存做并行、哪些环节并行不了、以及迁移到生产环境时你会踩到的坑。

一、背景:14 年的自举,换来一座性能债

TypeScript 2012 年发布。为了证明语言自身的完备性,编译器一直用 TypeScript 写 TypeScript——自举(self-hosted)。这在语言设计上是优雅的:编译器就是自己的第一个用户,dogfooding 拉满。但它埋下了一个结构性矛盾:

  • JS 是单线程事件循环模型,编译器跑在单个线程里,多核 CPU 白瞎;
  • AST 对象图巨大,V8 的 GC 在大堆上频繁停顿;
  • 类型检查器需要维护符号表、类型实例缓存,内存峰值动不动几个 GB。

于是出现了一个怪现象:语言越来越强(模板字面量类型、条件类型、递归类型),类型系统越强大,检查器要算的东西越多,编译就越慢。大型项目里 tsc 全量检查半小时不是段子。

社区应对方式是把 tsc 从关键路径上挪走:Vite 用 esbuild 转译、Webpack 用 babel-loader / ts-loader 的 transpileOnly,类型检查单独跑一遍 tsc --noEmit。这治标不治本——类型检查还是要跑,只是换了个没人愿意看的时间。

2025 年 3 月,Anders Hejlsberg 在 TypeScript 官方博客宣布 typescript-go 项目:把编译器移植到 Go。2026 年 4 月首个 Beta(快约 10 倍),7 月 7 日 RC,7 月 8 日 GA,编译器代号 tsgo。

二、核心概念:编译器五阶段流水线,到底慢在哪

任何 TypeScript 编译器都要走五步:

阶段输入 → 输出并行化潜力大项目耗时占比(约)
Scanner(扫描器)源码 → Token 流按文件独立,可并行10%
Parser(解析器)Token → AST按文件独立,可并行15~20%
Binder(绑定器)AST → Symbol 表需全量 AST,半串行10%
Checker(类型检查器)AST + Symbol → 类型结论受依赖图约束,部分并行40~50%
Emitter(发射器)AST → JS 输出按文件独立,可并行20%

给个直觉示例,这是我们要编译的代码:

// demo.ts
type User = {
  id: number;
  name: string;
  tags: readonly string[];
};

const users: User[] = [
  { id: 1, name: "Alice", tags: ["admin", "editor"] },
  { id: 2, name: "Bob", tags: ["viewer"] },
];

export function findByName(name: string): User | undefined {
  return users.find((u) => u.name === name);
}

编译器处理它时:

  • Scanner 产出几千个 Token;
  • Parser 建出 AST,每个节点是一个堆对象,光这 20 行代码就是几百个对象;
  • Binder 把 findByNameusersUser 挂进符号表;
  • Checker 对 findByName 做返回类型推断:find 的回调签名、User | undefined 的联合类型、readonly 数组的索引访问……每一步都在实例化类型对象;
  • Emitter 擦掉类型,输出 JS。

类型检查是最大的头,而它恰恰是最难并行化的部分。

2.1 类型检查为什么这么贵

看一个常见的类型体操:

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

type Config = {
  server: { host: string; port: number; tls: { cert: string; key: string } };
  database: { url: string; pool: { min: number; max: number } };
  logging: { level: "debug" | "info" | "error"; file?: string };
};

type PartialConfig = DeepPartial<Config>;

映射类型 + 递归条件类型:每层递归都要实例化新的类型对象。编译器做的不是简单"展开",而是维持延迟求值——只有真正用到 PartialConfig.server.tls.cert 时才会展开对应分支。这种惰性求值机制(deferred type reference)是 TS 检查器的核心设计,也是它慢的来源之一:每个 deferred type 都要在检查队列里排队、缓存、失效,环环相扣。

控制流分析(narrowing)也不便宜:if (x !== null) x.foo() 这种收窄在每个分支都会更新类型流状态,函数体越大,状态拷贝越多;strictNullChecks 开启后成本再翻一倍。

这些微观成本乘以数万文件,就是"全量检查半小时"的真相。

2.2 JS 实现的三个硬伤

  1. 单线程。解析 1 万个文件只能用 1 个核。V8 的 worker 线程共享内存困难(SharedArrayBuffer 有大量限制),TypeScript 团队在 JS 里做并行的尝试基本没戏。
  2. GC 停顿。上百万 AST 节点 + 类型实例,V8 的增量标记再努力,Major GC 的停顿依然肉眼可见。堆越大,停顿越痛。
  3. 内存膨胀。JS 对象有 header 开销,一个 AST 节点几十字节起步,类型系统还会缓存大量中间类型实例。源码 1GB 的项目,堆内存 4~6GB 不奇怪。

这三条是结构性限制——在 JS 里再怎么优化都是修修补补。换语言,是唯一的解。

三、架构分析:tsgo 的 Go 重写——材料替换而非推倒重来

3.1 逐行移植:语义一致性的保险丝

tsgo 最反直觉的设计决策是:不是重写,是移植。团队把现有 JS 实现逐行翻译成 Go,保留原有的结构、命名、算法,连变量名都尽量一致。

为什么这么干?

  • 类型检查的语义极其微妙。TS 类型系统有大量 edge case(条件类型分发、逆变协变、结构化类型……),重写 = 重新踩一遍 14 年的坑。
  • 一致性是可验证的。移植完跑十年积累的测试套件(百万级测试),语义对等是硬指标,不是嘴上的"行为兼容"。
  • 后续维护简单。两个代码库结构对应,上游修 bug 可以同步移植。

这就是"材料替换":逻辑没变,运行它的"材料"从解释执行的 JS 变成了编译成机器码的 Go。收益来自材料本身的性质,而不是重新设计。

3.2 共享内存并行:解析、检查、发射各干各的

Go 给编译器的第一份礼物是 goroutine 和真正的共享内存(对比 JS 的 SharedArrayBuffer 各种限制,Go 的 goroutine 可以随便读写同一块堆)。

并行化的核心思路是按文件划分的流水线并行

  • 解析(Parse):每个文件独立成 AST,天然并行——10 个核就 10 个文件一起扫;
  • 发射(Emit):类型擦除和降级转译对每个文件独立,也可以并行;
  • 检查(Check):部分并行,受文件间依赖图约束。

示意(Go 伪代码,展示调度思路,非 tsgo 真实源码):

func build(program *Program) {
    files := program.SourceFiles()

    // 阶段一:并行解析
    asts := make([]*SourceFile, len(files))
    sem := make(chan struct{}, runtime.NumCPU())
    var wg sync.WaitGroup
    for i, f := range files {
        wg.Add(1)
        sem <- struct{}{}
        go func(i int, path string) {
            defer wg.Done()
            defer func() { <-sem }()
            asts[i] = parseSourceFile(path)
        }(i, f)
    }
    wg.Wait()

    // 阶段二:绑定(建立符号表)
    symbols := NewBinder().Bind(asts)

    // 阶段三:依赖感知的并行检查
    checker := NewChecker(symbols, asts)
    checker.CheckParallel() // 内部用任务队列:文件依赖就绪后才开始检查
}

真正的 tsgo 检查器并行化要精巧得多:文件之间有依赖图(A import B,A 的类型检查依赖 B 的导出符号),所以它用任务队列 + 依赖就绪触发的方式调度——每个文件是一个任务,其依赖文件检查完才能开始。这有点像构建系统的拓扑排序,不过是运行时动态计算的。

3.3 并行化的边界:类型检查器为什么不能无限并行

官方明确说过:不是所有步骤都能轻松并行

原因:

  • 类型检查有全局状态:符号表、类型缓存、declare global 的合并、模块解析结果;
  • 跨文件泛型实例化会互相触发(A 文件实例化 B 文件的泛型,B 又在等 A 的结果——循环等待);
  • 错误报告需要确定性的顺序,并行检查的报错顺序要做归并。

所以 tsgo 的策略是务实的:解析和发射全并行;检查器在"安全边界"内并行(比如无依赖关系的兄弟文件并行检查),有依赖关系的保持顺序。这也是官方说的是"通常 8~12 倍"而不是"40 倍"的原因——Amdahl 定律在那儿摆着:串行部分再小也是天花板。

不过检查器内部的并行度远高于"文件级"直觉。检查 1 万个文件时,文件依赖图是很稀疏的(大多数文件只依赖少量公共模块),所以大多数文件可以同时进入检查队列。10 倍加速意味着有效并行度约 10 倍,在 16 核机器上完全合理。

3.4 为什么是 Go:goroutine、低停顿 GC、单二进制

选 Go 而不是 Rust/C++ 的理由:

  1. 并发模型:goroutine 轻量(2KB 栈起步),百万 goroutine 不虚;channel + sync 包让并行的编译器代码写起来像串行;
  2. GC 与内存:Go 的 GC 是并发三色标记,停顿毫秒级,对 AST 这种大量短生命周期对象的场景远好于 V8;内存布局紧凑,没有 JS 对象 header 膨胀;
  3. 交叉编译:GOOS/GOARCH 一键出 Linux/macOS/Windows 二进制,发布工具链成本几乎为零;
  4. 部署形态:单二进制,无 node_modules 依赖,装进 CI 镜像就是一个文件。

TypeScript 团队评估过 Rust(性能更强但开发效率低、借用检查器在编译器这种"到处都是共享可变状态"的代码里是灾难)、C++(同理)。Go 是"够快 + 开发效率高 + 内存安全"的甜点区。而且 tsgo 的定位不是最快的编译器,是"和 tsc 语义完全一致的最快编译器"。

3.5 tsgo 与 esbuild/SWC 的定位差异

很多人问:esbuild 不是已经很快了吗?为什么还要重写 tsc?

因为 esbuild/SWC 根本不检查类型。它们做的是语法级转换:strip 类型注解、降级语法。它们的 AST 是"一次性"的——转完就丢,不需要符号表,不需要跨文件信息。这就像 OCR 只负责把图片转成文字,不负责校对内容。

而类型检查本质上是一个全程序分析:文件 A 的类型错误可能源于文件 B 的声明。它需要:

  • 跨文件的模块解析(node_modules 里几百个 .d.ts);
  • 全局符号合并(declare global、接口合并、命名空间合并);
  • 泛型的跨文件实例化。

所以 esbuild 能 50ms 转完的项目,tsc 要 50 秒——不是 tsc 笨,是它干的活重一个数量级。tsgo 的价值在于:把"全程序分析"这种重活也干进秒级。至此,前端构建链路上不再有"慢到离谱"的环节:转译归 esbuild/SWC,类型检查归 tsgo,打包归 Rolldown,各管一段,每一段都在原生速度上。

这也解释了为什么 TS 7 不把 esbuild 的活也揽过来:职责分离才是健康的架构。tsgo 专注"语义正确",把"速度极致"留给更窄的领域。

四、代码实战:从 6.0 迁移到 7.0 的全过程

4.1 安装与版本矩阵

# 项目安装 TypeScript 7
npm install -D typescript@7

# 确认版本
npx tsc --version
# Version 7.0.0

# 新二进制:tsgo
npx tsgo --version

几个关键点:

  • TS 7 的 npm 包重新导出了 TS 6.0 的 API,tsc 命令依然可用,第三方工具链(vite-plugin 们、ts-node、ESLint 的 typescript-estree)可以继续工作;
  • 官方同时维护 6.x 分支,给生态迁移留窗口;
  • tsgo 是新编译器的二进制入口,功能与 tsc 对等(编译、--noEmit--watch-b 等),但老的编译器插件 API 不支持——这是最大的兼容性缺口。

4.2 五分钟迁移清单

对绝大多数项目,迁移就是换版本:

  1. 升级依赖:typescript@^7.0.0
  2. 跑一遍 npx tsgo --noEmit,确认没有语义差异导致的报错(理论上零差异,实践上偶尔有边界 case 的报错顺序变化);
  3. 检查项目里是否用了 ts.createProgram / 自定义 Transformer 等编译器 API——这些是 JS 时代的老接口,TS 7 原生包虽然 re-export 了 API,但 Transformer 生态需要走新的路径;
  4. CI 里把 tsc --noEmit 换成 tsgo --noEmit
  5. 编辑器:VS Code 用 workspace 版本指向 7.0。

tsconfig.json 完全不用动:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

4.3 基准测试:用数据说话

官方口径:完整构建通常 8~12 倍,平均约 10 倍。社区实测(WebStorm 2026.2 发布博客)给出的例子:Kibana 代码库项目加载从约 12 秒降到约 3 秒。

自己项目怎么测?用 hyperfine 做对照:

# 记录 6.0 基线(在临时目录/分支里)
npx -p typescript@6 tsc --noEmit -p tsconfig.json

# 再测 7.0
hyperfine --warmup 1 --runs 5 'npx tsgo --noEmit -p tsconfig.json'

典型输出(示意):

Benchmark 1: npx tsgo --noEmit -p tsconfig.json
  Time (mean ± σ):      3.42 s ± 0.21 s    [User: 18.9 s, System: 2.1 s]
  Range (min … max):    3.10 s … 3.71 s    5 runs

注意 User 时间远大于 Real 时间——这就是并行的直接证据:多核同时工作,墙钟时间被压下来。对比 6.0 的单线程实现,User ≈ Real。

增量构建(--watch / --incremental)同样受益:增量本来就快,但大仓第一次全量构建从 10 分钟降到 1 分钟,之后每次增量从 30 秒降到 3~5 秒,体感是"编辑器保存即反馈"。

4.4 与工具链的协作:Vite、esbuild、Rolldown

现在的常见架构是"转译与类型检查分离":

  • 转译:esbuild / SWC / Rolldown(Rust),只做语法降级和类型擦除,快但不做类型检查
  • 类型检查:tsc / tsgo,--noEmit 单独跑。

TS 7 不改变这个架构,但把短板补齐了:以前类型检查是整个链路上最慢的一环(比转译慢一个数量级),现在 tsgo 全量检查和 esbuild 转译的量级拉近了,"CI 里 typecheck 拖后腿"成为历史。

Vite 用户注意:Vite 的依赖预构建和转译走 esbuild,不依赖 tsc 性能,dev 无感;但对 vue-tsctsc -b 这类做全量检查的流程,收益巨大。

WebStorm / IntelliJ 系:2026.2 起原生支持 TS 7 语言服务。VS Code:把 typescript.tsdk 指向项目里的 TS 7 即可:

{
  "typescript.tsdk": "node_modules/typescript/lib"
}

4.5 编辑器语言服务

编辑器体验(补全、跳转、重构、悬停信息)由语言服务提供。TS 7 的原生语言服务(Go 实现的 LSP 部分)在 2026 年 1 月预览时就"基本稳定,可日常使用",GA 后成为默认。

大项目里最直观的变化:

  • 打开项目到"可补全"的等待时间大幅缩短;
  • 全仓 rename symbol 不再卡死;
  • 内存占用下降(Go 的紧凑内存布局 + 无 JS 堆膨胀)。

4.6 一个真实项目的迁移记录

以一个 800+ 文件的中台业务项目为例(React + Vite + TS,CI 与本地同规格 16 核机器):

环节TS 6.0TS 7.0 (tsgo)提升
冷启动全量检查(--noEmit)212s21s10.1x
增量检查(--incremental)8.5s1.9s4.5x
-b 多 project 构建96s14s6.9x
编辑器项目加载~11s~2s5.5x

(数据为典型场景示意,请以自己项目实测为准)

迁移中实际遇到的问题:

  1. 两个老项目用了自定义 Transformer(给 styled-components 生成 displayName),tsgo 不支持 → 改用 babel-plugin 方案,类型检查交给 tsgo;
  2. 一个文件长期靠 // @ts-ignore 压着的错误,tsgo 报错顺序变化导致 CI 里一个 grep 脚本失效——教训:不要在 CI 里依赖报错顺序;
  3. 内存:CI runner 2GB 限制下 tsgo 峰值约 1.2GB,而 6.0 需要 4GB+,低配 runner 也能跑了。

五、性能优化:大仓与 CI 的进一步压榨

升级到 TS 7 只是第一步。想要极致体验,还得配合架构层面的优化。

5.1 project references:让并行更彻底

monorepo 里,project references 让每个子包独立编译、独立缓存。TS 7 对 references 是重点优化项:多个 project 可以并行构建(tsgo -b),project 间的 .d.ts 生成与消费天然形成依赖 DAG,正好匹配并行的调度模型。

// tsconfig.json(根)
{
  "files": [],
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" },
    { "path": "./apps/web" }
  ]
}

// 构建:-b 模式自动按依赖拓扑并行
npx tsgo -b

以前在 JS 实现里,references 的序列化/反序列化(.tsbuildinfo)是性能开销;Go 实现里二进制格式的读写快得多,多 project 构建的边际成本显著下降。

5.2 CI 类型检查提速

CI 是另一个大头。GitHub Actions 示例:

name: typecheck
on:
  push:
    branches: [main]
jobs:
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx tsgo --noEmit -p tsconfig.json

实测效果:一个 2000+ 文件的 monorepo,tsc 全量检查约 8 分钟 → tsgo 约 45 秒。CI 排队时间肉眼可见地缩短。

进阶技巧:

  • 增量缓存:把 tsconfig.tsbuildinfo 上传到 CI 缓存(actions/cache),配合 --incremental,PR 检查只编译变更波及的文件;
  • 分片并行:超大仓按 project 分片到多个 runner 并行跑 tsgo -b
  • 只检查变更--incremental + git diff 驱动,进一步缩小范围。

5.3 常见坑与避坑指南

  1. 编译器插件(Transformer)不兼容:老的自定义 Transformer(比如给装饰器做代码生成的)依赖 JS 时代的 Plugin API,TS 7 不支持。替代方案:转译交给 esbuild/SWC(它们支持插件),类型检查交给 tsgo;
  2. 报错顺序变化:并行检查后错误列表顺序可能不同于 6.0,别在 CI 里 grep 固定行号的错误输出;
  3. --watch 行为差异:Go 实现的文件监听更高效,但首次启动的缓存预热方式不同,个别项目需要微调参数;
  4. 内存参数:Go 编译器默认内存配置合理,但超大仓(10 万+文件)遇到 OOM 时,用 GOMEMLIMIT 而不是 NODE_OPTIONS=--max-old-space-size
  5. skipLibCheck 依然是第一性能开关:.d.ts 检查开销占比高,大仓里保持开启;
  6. 类型层面别做病态体操:tsgo 再快也救不了 O(2^n) 的条件类型递归。检查器并行化解决的是"吞吐",不是"单点复杂度"。遇到编译极慢的单个文件,还是要回头审视类型设计。

5.4 类型性能反模式:tsgo 救不了的类型体操

// 反模式 1:指数级条件类型递归
// 递归深度失控时,编译器直接报 "Type instantiation is excessively deep"
type Bad<T> = T extends string
  ? number
  : T extends number
    ? string
    : Bad<T[]>;

// 反模式 2:映射类型的笛卡尔积展开
// 联合类型有 n 个成员时,这里会生成 n² 个类型对象
type Union = "a" | "b" | "c" | "d" | "e";
type AllPairs = { [K in Union]: { [P in Union]: [K, P] } };

// 反模式 3:在类型层做"编译期计算"
// 模板字面量类型很强大,但它是真实计算,复杂解析器=检查期噩梦
type ParseUrl<S extends string> = S extends `${infer Proto}://${infer Rest}`
  ? { proto: Proto; rest: Rest }
  : never;

修复思路:把"表达力"留在类型层,把"计算量"压到最小——

  • 用接口组合代替深层递归(递归深度控制在小常数);
  • 需要展开的联合类型提前拍平(precompute),别在检查热点里现场算;
  • .d.ts 里复杂的工具类型,考虑局部豁免而不是全局 any;
  • 实在要写类型体操,把它隔离到单一文件,避免成为"全仓类型热点"。

一句话:tsgo 把检查器吞吐提高了 10 倍,但病态类型是"每文件线性成本",该优化还是要优化。

六、FAQ:升级前你最关心的几个问题

Q1:TS 7 会和 ESLint / Prettier 冲突吗?

不会。ESLint 走 typescript-estree 解析(它依赖 typescript 包的 API,TS 7 re-export 了 6.0 API),Prettier 是纯语法层。两者都不依赖编译器的性能。唯一要确认的是 typescript-eslint 版本是否声明支持 TS 7(v8.2x 之后已适配)。

Q2:我还在用 CommonJS + ts-node,能升吗?

ts-node 用自己的编译器宿主运行,不走 tsgo 二进制,但它在内部调用 typescript API——升到 7 后 ts-node 老版本可能报 API 不兼容,建议同时升级 ts-node 到支持 TS 7 的版本,或者直接切 tsx(esbuild 驱动,更快)。

Q3:.d.ts 文件会被检查得更快吗?

会,而且明显。skipLibCheck 关闭时 .d.ts 的检查是重头戏;即使开着 skipLibCheck,模块解析和声明合并依然要走。tsgo 对 .d.ts 的解析同样并行化,大依赖树(antd、lodash 这种)的加载速度提升肉眼可见。

Q4:会不会有语义不一致的 bug?

理论上不会,实际上有边界 case。官方承诺语义一致,但 14 年的 edge case 太多,个别冷门组合(如装饰器 + 条件类型 + 模块合并)可能出现行为差异。建议升级后跑全量类型检查 + 测试套件,遇到差异用官方 issue 追踪。目前已知差异集中在报错顺序和个别错误消息措辞,不影响正确性。

Q5:团队不升,我一个人升了会怎样?

类型检查结果一致,所以混用没问题——你本地用 tsgo,CI 用 6.0,两边结论一样。但增量缓存(.tsbuildinfo)格式可能不同版本不互通,混用时把缓存文件加进 .gitignore 就好。

七、总结与展望

TypeScript 7.0 的意义不只是"快 10 倍"。它证明了三件事:

  1. 语义冻结是重写的护城河。tsgo 没有引入任何破坏性语义变化(除了插件 API),开发者升级零成本、收益全拿走;
  2. 并行化是编译器性能的下一个主战场。JS 单线程时代的编译器已经到天花板,Go 重写只是开始——后续版本会在检查器并行度上继续加码;
  3. 工具链分层进一步固化:转译(esbuild/SWC/Rolldown)+ 类型检查(tsgo)+ 打包(Rolldown)各司其职,谁快谁上。

对普通开发者的行动清单:

  • 本周就升:npm i -D typescript@7,跑一遍 npx tsgo --noEmit
  • CI 里替换 typecheck 命令,顺手把 .tsbuildinfo 缓存加上;
  • 编辑器指向 TS 7,享受秒开的大仓;
  • 别急着删 ts-node / tsx——它们走自己的转译路径,不受影响,但注意它们依赖的 typescript API 版本兼容。

展望 TS 8:类型系统的演进(更精确的泛型、模式匹配类型的强化)会继续,而编译器性能的冗余(现在检查快 10 倍)意味着语言可以承担更重的类型分析——比如更严格的默认检查、更深的推断。性能冗余就是语言进化的空间。

对生态的另一层影响在工具链侧:tsgo 的并行架构为"分布式编译"留了接口——大仓可以按 project 分发到多机编译,本地只收结果。这会在未来一两年改变 monorepo 的 CI 形态:类型检查不再是串行瓶颈,而是可以横向扩展的普通任务。

最后说句实在话:TypeScript 7.0 是那种"升级完就忘了它存在"的版本——你感知不到它,只感知到世界变快了。这才是编译器最好的样子。

推荐文章

基于Webman + Vue3中后台框架SaiAdmin
2024-11-19 09:47:53 +0800 CST
五个有趣且实用的Python实例
2024-11-19 07:32:35 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
Vue3中如何实现插件?
2024-11-18 04:27:04 +0800 CST
程序员茄子在线接单