DevToolBox免费
博客

esbuild 完全指南:最快的 JavaScript 打包工具

22 min read作者 DevToolBox Team

esbuild 是一个用 Go 语言编写的极速 JavaScript 和 TypeScript 打包工具。它以比 Webpack、Rollup 或 Parcel 等传统工具快 10-100 倍的速度处理打包、压缩、转译和 Tree-shaking。esbuild 由 Evan Wallace(Figma 联合创始人)创建,通过利用并行性、避免不必要的数据转换以及使用编译语言而非 JavaScript 来实现其性能。无论你需要独立的打包工具还是 Vite 和 Snowpack 等工具的高速基础,esbuild 已成为现代 JavaScript 工具链中不可或缺的一部分。

TL;DR

esbuild 是基于 Go 的 JavaScript/TypeScript 打包工具,速度比 Webpack 或 Rollup 快 10-100 倍。它开箱即用支持 ESM 和 CommonJS、TypeScript 和 JSX、Tree-shaking、压缩、Source Map、CSS 打包、插件 API、监听模式和内置开发服务器。使用 CLI 进行快速任务,使用 JavaScript/Go API 进行编程构建,使用插件进行自定义转换。

Key Takeaways
  • esbuild 用 Go 编写并大量使用并行处理,比 Webpack 等基于 JavaScript 的打包工具快 10-100 倍。
  • 开箱即用支持 TypeScript、JSX、ESM、CommonJS、CSS 和 JSON,无需配置。
  • 插件 API 让你可以用自定义加载器、解析器和转换来扩展 esbuild。
  • Tree-shaking、压缩和代码分割自动工作,生成优化的生产包。
  • 监听模式和内置服务模式实现快速开发工作流和即时重建。
  • esbuild 驱动 Vite 开发服务器,在现代工具链中被广泛用作快速转译器和打包工具。

为什么 esbuild 如此快速

esbuild 通过四个关键架构决策实现了非凡的速度,使其区别于所有基于 JavaScript 的打包工具。

  • 用 Go 编写:Go 编译为原生机器码,无需垃圾回收暂停风暴。JavaScript 打包工具在 V8 中运行,有 JIT 编译开销、GC 暂停和单线程限制。:用 Go 编写:Go 编译为原生机器码,无需垃圾回收暂停风暴。JavaScript 打包工具在 V8 中运行,有 JIT 编译开销、GC 暂停和单线程限制。
  • 大规模并行:esbuild 使用所有可用 CPU 核心进行解析、链接和代码生成。JavaScript 打包工具受单线程事件循环的根本限制。:大规模并行:esbuild 使用所有可用 CPU 核心进行解析、链接和代码生成。JavaScript 打包工具受单线程事件循环的根本限制。
  • 最少数据转换:esbuild 读取源文件,将其解析为紧凑的 AST 表示一次,然后写入输出。它避免了 Webpack 和 Rollup 中插件链执行的重复字符串到 AST 到字符串转换。:最少数据转换:esbuild 读取源文件,将其解析为紧凑的 AST 表示一次,然后写入输出。它避免了 Webpack 和 Rollup 中插件链执行的重复字符串到 AST 到字符串转换。
  • 内存效率:esbuild 使用紧凑的数据结构并以流式方式处理文件,即使对于大型项目也能保持较低的内存使用。:内存效率:esbuild 使用紧凑的数据结构并以流式方式处理文件,即使对于大型项目也能保持较低的内存使用。
# Benchmark: Bundle 1,000 modules (React + TypeScript)
#
# esbuild        0.33s   (Go, parallel)
# Rollup + terser 15.4s   (JS, single-threaded)
# Webpack 5       19.8s   (JS, single-threaded)
# Parcel 2         8.7s   (Rust parser + JS plugins)
#
# esbuild is 47-60x faster than Webpack/Rollup
# for equivalent bundling tasks

安装

esbuild 可以作为本地 npm 依赖或全局安装。它提供平台特定的原生二进制文件,安装时无需编译步骤。

# Install as a local dev dependency (recommended)
npm install --save-dev esbuild

# Install globally
npm install -g esbuild

# Verify installation
npx esbuild --version
# 0.24.x

# Using with yarn
yarn add --dev esbuild

# Using with pnpm
pnpm add -D esbuild

CLI 使用

esbuild CLI 是打包、压缩或转换文件的最快方式。它接受所有主要选项作为命令行标志,非常适合脚本、CI 管道和一次性任务。

基本打包和常用 CLI 标志:

# Bundle a single entry point
npx esbuild src/index.ts --bundle --outfile=dist/bundle.js

# Bundle with ESM output format
npx esbuild src/index.ts --bundle --format=esm --outfile=dist/bundle.mjs

# Bundle for Node.js
npx esbuild src/server.ts --bundle --platform=node --outfile=dist/server.js

# Transpile TypeScript without bundling
npx esbuild src/index.ts --outfile=dist/index.js

# Bundle multiple entry points into an output directory
npx esbuild src/app.ts src/worker.ts --bundle --outdir=dist

# Bundle with JSX for React
npx esbuild src/App.tsx --bundle --jsx=automatic --outfile=dist/app.js

用于生产构建的高级 CLI 选项:

# Production build with minification and source maps
npx esbuild src/index.ts \
  --bundle \
  --minify \
  --sourcemap \
  --target=es2020 \
  --format=esm \
  --outfile=dist/bundle.js

# With tree-shaking and external packages
npx esbuild src/index.ts \
  --bundle \
  --minify \
  --tree-shaking=true \
  --external:react \
  --external:react-dom \
  --format=esm \
  --outfile=dist/bundle.js

# With define for environment variables
npx esbuild src/index.ts \
  --bundle \
  --define:process.env.NODE_ENV=\"production\" \
  --define:__VERSION__=\"1.2.3\" \
  --outfile=dist/bundle.js

# Analyze bundle with metafile
npx esbuild src/index.ts --bundle --metafile=meta.json --outfile=dist/bundle.js
npx esbuild --analyze=verbose < meta.json

JavaScript API

JavaScript API 提供从 Node.js 脚本、构建工具和自定义开发服务器对 esbuild 的编程访问。它暴露三个主要函数:build、transform 和 context。

build()

build 函数从磁盘读取文件,打包它们并写入输出。它接受所有与 CLI 相同的选项以及额外的 JavaScript 特定选项如插件。

import * as esbuild from "esbuild";

// Basic build
const result = await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outfile: "dist/bundle.js",
  format: "esm",
  target: "es2020",
  minify: true,
  sourcemap: true,
  metafile: true,
});

// Analyze the bundle
const analysis = await esbuild.analyzeMetafile(result.metafile);
console.log(analysis);

// Library build with multiple entry points
await esbuild.build({
  entryPoints: ["src/index.ts", "src/utils.ts"],
  bundle: true,
  outdir: "dist",
  format: "esm",
  splitting: true,
  platform: "node",
  target: "node18",
  external: ["express", "pg"],
  packages: "external",
});

transform()

transform 函数在单个代码字符串上操作,不访问文件系统。适用于开发服务器和编辑器集成中的即时转译。

import * as esbuild from "esbuild";

// Transform TypeScript to JavaScript
const tsCode = "const greet = (name: string): string => `Hello \${name}`;";
const result = await esbuild.transform(tsCode, {
  loader: "ts",
  target: "es2020",
});
console.log(result.code);
// const greet = (name) => `Hello \${name}`;

// Minify JavaScript
const minified = await esbuild.transform("const x = 1 + 2;", {
  minify: true,
});
console.log(minified.code); // "const x=3;"

// Transform JSX
const jsx = await esbuild.transform(
  "const App = () => <div>Hello</div>;",
  { loader: "jsx", jsx: "automatic" }
);
console.log(jsx.code);

context()

context 函数为监听模式和服务模式创建长期构建上下文。它返回具有 rebuild、watch、serve 和 dispose 方法的对象。

import * as esbuild from "esbuild";

// Create a long-lived build context
const ctx = await esbuild.context({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outdir: "dist",
  format: "esm",
  sourcemap: true,
});

// Manual rebuild (reuses cached state)
const result = await ctx.rebuild();

// Start watching for file changes
await ctx.watch();
console.log("Watching for changes...");

// Start a dev server on port 8000
const { host, port } = await ctx.serve({
  servedir: "public",
  port: 8000,
});
console.log("Serving at http://" + host + ":" + port);

// Clean up when done
// await ctx.dispose();

内容类型和加载器

esbuild 使用加载器来确定如何处理每种文件类型。加载器根据文件扩展名选择,但可以使用 --loader 标志或 API 中的 loader 选项覆盖。

  • js / jsxJavaScript 和 JSX 文件。配置后 .js 和 .jsx 文件都支持 JSX 语法。
  • ts / tsxTypeScript 和 TSX 文件。esbuild 剥离类型注解但不执行类型检查。
  • cssCSS 文件。esbuild 打包 CSS 导入,处理 @import 解析,支持 CSS 模块。
  • jsonJSON 文件被解析并内联为 JavaScript 对象。Tree-shaking 作用于单个 JSON 属性。
  • text文件作为包含文件内容的 JavaScript 字符串导入。
  • binary文件作为文件内容的 Uint8Array 导入。
  • base64文件作为 base64 编码字符串导入。
  • file文件被复制到输出目录,导入解析为输出文件路径。
  • dataurl文件作为 data URL 字符串内联。
// Configure loaders in the JavaScript API
await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outdir: "dist",
  loader: {
    ".png": "file",
    ".svg": "dataurl",
    ".txt": "text",
    ".woff2": "file",
    ".json": "json",
    ".csv": "text",
  },
});

// CLI loader override
// npx esbuild src/index.ts --bundle --loader:.svg=dataurl --loader:.txt=text

打包和输出格式

esbuild 支持三种输出格式,决定打包代码的结构方式。选择正确的格式取决于目标环境和模块系统。

  • ESM: "module" 和库包。
  • CommonJS:
  • IIFE:
// Build a library with both ESM and CJS outputs
import * as esbuild from "esbuild";

const shared = {
  entryPoints: ["src/index.ts"],
  bundle: true,
  minify: true,
  sourcemap: true,
  target: "es2020",
  external: ["react", "react-dom"],
};

// ESM output
await esbuild.build({
  ...shared,
  format: "esm",
  outfile: "dist/index.mjs",
});

// CommonJS output
await esbuild.build({
  ...shared,
  format: "cjs",
  outfile: "dist/index.cjs",
});

// IIFE output for browsers (global variable)
await esbuild.build({
  ...shared,
  format: "iife",
  globalName: "MyLibrary",
  outfile: "dist/index.global.js",
});

代码分割

esbuild 支持 ESM 输出格式的自动代码分割。启用分割后,多个入口点之间的共享模块被提取到单独的块文件中,动态 import() 表达式创建按需加载的额外块。

// Enable code splitting (ESM format required)
await esbuild.build({
  entryPoints: ["src/home.ts", "src/about.ts"],
  bundle: true,
  splitting: true,
  format: "esm",
  outdir: "dist",
  chunkNames: "chunks/[name]-[hash]",
});

// Dynamic imports also create separate chunks
// src/home.ts
// const module = await import("./heavy-module.ts");
// module.doSomething();

// Output structure:
// dist/
//   home.js           (entry)
//   about.js          (entry)
//   chunks/shared-A1B2.js  (shared code)

Tree-Shaking

esbuild 在打包过程中自动执行 Tree-shaking(死代码消除)。它分析 import/export 图并移除从未导入或使用的代码。Tree-shaking 在语句级别工作并尊重 package.json 中的 sideEffects 字段。

Note: 为了最大化 Tree-shaking 效果,使用 ES 模块语法(import/export)而非 CommonJS(require/module.exports)。CommonJS 是动态的,阻止对未使用导出的静态分析。

// package.json — Mark the package as side-effect-free
{
  "name": "my-library",
  "sideEffects": false
}

// Or specify files with side effects
{
  "name": "my-library",
  "sideEffects": ["./src/polyfills.js", "*.css"]
}

// esbuild automatically removes unused exports
// src/utils.ts
// export function usedFunction() { ... }   <-- kept
// export function unusedFunction() { ... }  <-- removed

// Force tree-shaking even without bundle mode
// npx esbuild src/index.ts --tree-shaking=true --outfile=dist/out.js

压缩

esbuild 包含内置压缩器,通过移除空白、缩短标识符和应用代码转换来减小包大小。压缩器同时适用于 JavaScript 和 CSS。可以作为单个标志启用或分别为空白、标识符和语法配置。

// Enable all minification
await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  minify: true,  // Shorthand for all three below
  outfile: "dist/bundle.js",
});

// Fine-grained minification control
await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  minifyWhitespace: true,   // Remove whitespace
  minifyIdentifiers: true,  // Shorten variable names
  minifySyntax: true,       // Simplify expressions
  outfile: "dist/bundle.js",
});

// CSS minification works the same way
// npx esbuild src/styles.css --minify --outfile=dist/styles.min.css

// Minification transforms examples:
// true && x   -->  x
// if (a) b(); else c();  -->  a ? b() : c()
// "use strict" removed in ESM
// Dead code after return/throw removed

Source Maps

Source Map 允许调试器将打包/压缩的代码映射回原始源文件。esbuild 支持多种 Source Map 模式以匹配不同的部署场景。

  • linked: 生成单独的 .map 文件并在输出中添加 sourceMappingURL 注释。生产部署的标准做法。
  • inline: 将整个 Source Map 作为 base64 data URL 嵌入输出文件。适用于开发和小型脚本。
  • external: 生成单独的 .map 文件但不添加 sourceMappingURL 注释。用于上传 Source Map 到错误跟踪服务但不向用户暴露。
  • both: 生成单独的 .map 文件并嵌入内联 Source Map。很少需要。
// Source map modes
await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  minify: true,
  outfile: "dist/bundle.js",

  // Option 1: Linked (production — separate .map file)
  sourcemap: true,  // or sourcemap: "linked"

  // Option 2: Inline (development — embedded in output)
  // sourcemap: "inline",

  // Option 3: External (error tracking — no comment)
  // sourcemap: "external",

  // Include source content in the map for offline debugging
  sourcesContent: true,
});

插件 API

esbuild 插件 API 让你拦截和自定义构建过程。插件可以解析导入路径、加载文件内容和转换代码。API 设计简单高效,有两个主要钩子:onResolve 和 onLoad。

  • onResolve: 当 esbuild 遇到导入路径时调用。你的回调可以返回自定义路径、命名空间或 external 标志。
  • onLoad: 当 esbuild 需要加载文件时调用。你的回调返回文件内容和加载器类型。

以下是加载 .txt 文件为 ES 模块并添加环境变量的示例插件:

import * as esbuild from "esbuild";
import fs from "fs";

// Plugin: Load .txt files as ES modules
const txtPlugin = {
  name: "txt-loader",
  setup(build) {
    build.onLoad({ filter: /\.txt$/ }, async (args) => {
      const text = await fs.promises.readFile(args.path, "utf8");
      return {
        contents: "export default " + JSON.stringify(text) + ";",
        loader: "js",
      };
    });
  },
};

// Plugin: Replace environment variables at build time
const envPlugin = {
  name: "env-loader",
  setup(build) {
    build.onResolve({ filter: /^env$/ }, (args) => ({
      path: args.path,
      namespace: "env-ns",
    }));
    build.onLoad({ filter: /.*/, namespace: "env-ns" }, () => ({
      contents: "export default " + JSON.stringify(process.env) + ";",
      loader: "json",
    }));
  },
};

// Use plugins in build
await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outfile: "dist/bundle.js",
  plugins: [txtPlugin, envPlugin],
});

监听模式

监听模式监控源文件的变化,检测到变化时自动重建。它使用操作系统文件监视器进行即时通知而非轮询,使重建几乎是即时的。

// Watch mode using the context API
import * as esbuild from "esbuild";

const ctx = await esbuild.context({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outdir: "dist",
  sourcemap: true,
  logLevel: "info",
});

// Start watching — rebuilds on file changes
await ctx.watch();
console.log("Watching for file changes...");

// To stop watching and clean up:
// await ctx.dispose();

// CLI watch mode
// npx esbuild src/index.ts --bundle --outdir=dist --watch

服务模式

内置服务模式启动本地 HTTP 服务器来提供构建输出。它与监听模式集成,每个请求在文件发生变化时触发重建。这提供了一个最小化的开发服务器,无需额外工具。

// Serve mode with watch integration
import * as esbuild from "esbuild";

const ctx = await esbuild.context({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outdir: "public/dist",
  sourcemap: true,
});

// Start the dev server
const { host, port } = await ctx.serve({
  servedir: "public",
  port: 3000,
  fallback: "public/index.html", // SPA fallback
});

console.log("Dev server: http://" + host + ":" + port);

// Every HTTP request triggers a rebuild if files changed
// No need to call ctx.watch() separately

// CLI serve mode
// npx esbuild src/index.ts --bundle --outdir=public/dist --servedir=public --serve=3000

目标环境

target 选项告诉 esbuild 输出哪个 JavaScript 版本。esbuild 自动将现代语法向下转换到指定的目标。支持的目标包括 ESNext、ES2015-ES2022、Chrome、Firefox、Safari、Edge、Node 版本号。

// Target a JavaScript version
await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  target: "es2020",
  outfile: "dist/bundle.js",
});

// Target specific browsers
// target: ["chrome90", "firefox88", "safari14", "edge90"]
// Target Node.js: target: "node18"
// Target latest: target: "esnext"

// Syntax transforms based on target:
// Optional chaining (?.)   -> target < es2020
// Nullish coalescing (??)  -> target < es2020
// Class fields             -> target < es2022
// Async/await              -> target < es2017

平台配置

platform 选项告诉 esbuild 代码将在浏览器还是 Node.js 中运行。这影响主字段、条件和内置模块的默认设置。

// Browser platform (default) — resolves "browser" field in package.json
await esbuild.build({
  entryPoints: ["src/app.ts"],
  bundle: true,
  platform: "browser",
  outfile: "dist/app.js",
});

// Node.js platform — marks fs, path, crypto as external
await esbuild.build({
  entryPoints: ["src/server.ts"],
  bundle: true,
  platform: "node",
  target: "node18",
  outfile: "dist/server.js",
});

// Neutral platform — no defaults, full control
// platform: "neutral"

Define 和环境变量

define 选项让你在构建时用常量表达式替换全局标识符。常用于环境变量、功能标志和条件编译。替换在 AST 级别进行,可与 Tree-shaking 配合消除死代码分支。

await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outfile: "dist/bundle.js",
  define: {
    "process.env.NODE_ENV": '"production"',
    "__DEV__": "false",
    "__VERSION__": '"1.2.3"',
    "process.env.API_URL": '"https://api.example.com"',
  },
});

// In source code, this enables dead code elimination:
//
// if (process.env.NODE_ENV === "development") {
//   // This entire block is removed in production
//   enableDevTools();
// }
//
// if (__DEV__) {
//   console.log("debug info"); // Removed
// }

// CLI equivalent:
// npx esbuild src/index.ts --bundle \
//   --define:process.env.NODE_ENV=\"production\" \
//   --define:__DEV__=false

esbuild vs Webpack vs Rollup vs Vite

了解 esbuild 与其他打包工具的比较有助于为项目选择正确的工具。每个工具都有不同的优势和权衡。

特性esbuildWebpackRollupVite
编写语言GoJavaScriptJavaScriptJS(使用 esbuild + Rollup)
速度(1K 模块)~0.3秒~20秒~15秒~0.5秒(开发),~8秒(构建)
代码分割仅 ESM完全支持完全支持完全支持
插件生态最大增长中(Rollup 兼容)
HMR无内置通过插件是(即时)
TypeScript仅剥离通过 ts-loader通过插件通过 esbuild
CSS 打包内置通过加载器通过插件内置
配置复杂度最小中等

esbuild 擅长原始构建速度、库打包和作为其他工具内的快速转译器。Webpack 最适合需要完整插件生态的复杂应用。Rollup 适合具有高级 Tree-shaking 的库发布。Vite 将 esbuild 的开发速度与 Rollup 的生产质量结合。

最佳实践

  • 使用 esbuild 进行库打包,速度和简单配置最重要。对于复杂 Web 应用,考虑使用底层采用 esbuild 的 Vite。
  • 始终单独运行 tsc --noEmit 进行类型检查。esbuild 剥离类型但不验证它们。
  • 使用 context API 的监听模式进行开发,而不是重复调用 build。上下文重用内部状态以加快增量重建。
  • 设置 target 选项以匹配最低支持环境。
  • 使用 metafile 选项分析包组成。
  • 使用 ES 模块语法并设置 sideEffects 字段来启用 Tree-shaking。
  • 使用 define 进行编译时常量和环境变量,支持死代码消除。
  • 生产构建启用压缩、Source Map(linked 模式)并设置适当的 target 和 platform 选项。
// Complete production build script (build.mjs)
import * as esbuild from "esbuild";

const isProduction = process.env.NODE_ENV === "production";

const result = await esbuild.build({
  entryPoints: ["src/index.ts"],
  bundle: true,
  outdir: "dist",
  format: "esm",
  splitting: true,
  target: "es2020",
  platform: "browser",
  minify: isProduction,
  sourcemap: isProduction ? "linked" : "inline",
  metafile: true,
  define: {
    "process.env.NODE_ENV": isProduction ? '"production"' : '"development"',
  },
  loader: {
    ".png": "file",
    ".svg": "dataurl",
    ".woff2": "file",
  },
  chunkNames: "chunks/[name]-[hash]",
  assetNames: "assets/[name]-[hash]",
});

if (isProduction) {
  const analysis = await esbuild.analyzeMetafile(result.metafile);
  console.log(analysis);
}

常见问题

为什么 esbuild 比 Webpack 快这么多?

esbuild 用 Go 编写,编译为原生机器码并在所有 CPU 核心上完全并行运行。Webpack 在 Node.js 中使用 JavaScript 运行,是单线程的,有 JIT 编译开销和垃圾回收暂停。esbuild 还通过每个文件只解析一次为紧凑表示来最小化数据转换。

esbuild 支持 TypeScript 类型检查吗?

不支持。esbuild 在转译时剥离 TypeScript 类型注解但不执行类型检查。这是为了速度而设计的。你应该在 CI 管道中单独运行 tsc --noEmit。

esbuild 能替代 Webpack 用于 React 应用吗?

对于简单到中等的 React 应用,可以。esbuild 处理 JSX、TypeScript、CSS、代码分割和压缩。但对于需要 HMR、模块联邦或广泛插件生态的复杂设置,Webpack 或 Vite 是更好的选择。

esbuild 如何处理 CSS?

esbuild 有内置 CSS 打包器,解析 @import 语句,处理 CSS 模块,执行压缩并生成 Source Map。

esbuild 和 Vite 有什么区别?

esbuild 是专注于原始速度的底层打包器和转译器。Vite 是更高级的构建工具,开发时使用 esbuild 进行依赖预打包和转译,生产时使用 Rollup。Vite 在 esbuild 之上添加了 HMR、开发服务器、框架插件和更丰富的配置系统。

esbuild 支持代码分割吗?

是的,但仅限 ESM 输出格式。启用分割后,共享模块被提取到单独的块文件中。IIFE 和 CommonJS 格式不支持代码分割。

如何使用 esbuild 插件?

插件是具有 name 和 setup 函数的 JavaScript 对象。setup 函数接收具有 onResolve 和 onLoad 钩子的构建对象。插件通过 plugins 选项传递给 build 或 context 函数。

esbuild 适合生产环境吗?

是的。esbuild 自 2021 年以来一直稳定且广泛用于生产环境。它驱动 Vite 的开发体验,被 Vue、React、Svelte 和 SolidJS 框架使用。其 API 在次版本之间保持稳定且无破坏性变更。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON FormatterJSON Validator

相关文章

Rollup.js 完全指南:现代 JavaScript 模块打包

掌握 Rollup.js 库打包,包括 tree-shaking、插件、代码分割、输出格式与高级配置。

Prettier 完全指南:统一代码格式化的最佳实践

掌握 Prettier 代码格式化工具,包括配置、ESLint 集成、编辑器设置、预提交钩子与 CI/CD 流水线。

ESLint 9 完全指南:Flat Config、TypeScript 和现代 Linting (2026)

完整的 ESLint 指南,涵盖 Flat Config、规则、TypeScript-ESLint、React/Vue 插件、自定义规则、共享配置、IDE 集成、自动修复和从 .eslintrc 迁移。