关于 esbuild-bundler
面向 JavaScript 和 TypeScript 开发者的 esbuild 配置指南,介绍打包、输出格式、加载器、插件和开发流程,并说明类型检查需单独处理。
主要功能
- 01命令行与 JavaScript API 构建配置
- 02浏览器与 Node.js 配置
- 03输出格式与加载器
- 04代码拆分与插件
- 05监听模式与开发服务器
- 06单独进行 TypeScript 类型检查
适用场景
- 01打包 TypeScript 或 JSX 项目
- 02构建浏览器或 Node.js 产物
- 03配置监听与增量构建
- 04生成库和应用程序包
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill esbuild-bundler安装后在 Claude Code 中输入/esbuild-bundler确认已加载。
详细介绍
esbuild 打包器
你是 esbuild 方面的专家。esbuild 是一款使用 Go 编写、速度极快的 JavaScript 和 TypeScript 打包器。使用 esbuild 配置时,请遵循以下指南。
核心原则
- esbuild 的速度比传统打包器快 10–100 倍
- 大多数使用场景无需任何配置
- 原生支持 TypeScript 和 JSX,无需额外设置
- 在保持代码质量的同时注重速度
- 使用 Go 编写,实现原生性能
项目结构
project/
├── src/
│ ├── index.ts # Main entry point
│ ├── components/ # UI components
│ └── utils/ # Utility functions
├── dist/ # Build output
├── esbuild.config.mjs # Build script (optional)
├── tsconfig.json # TypeScript config
└── package.json
基本用法
命令行
# Basic bundle
esbuild src/index.ts --bundle --outfile=dist/bundle.js
# Production build
esbuild src/index.ts --bundle --minify --sourcemap --outfile=dist/bundle.js
# Watch mode
esbuild src/index.ts --bundle --watch --outfile=dist/bundle.js
JavaScript API
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
minify: true,
sourcemap: true,
outfile: 'dist/bundle.js'
});
TypeScript 配置
tsconfig.json 最佳实践
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"esModuleInterop": true,
"isolatedModules": true,
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true
},
"include": ["src/**/*"]
}
重要的 TypeScript 设置
isolatedModules: true- esbuild 兼容性所必需esModuleInterop: true- 提高 ESM 兼容性noEmit: true- 让 esbuild 处理输出,tsc 仅用于类型检查
构建配置
浏览器构建
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
minify: true,
sourcemap: true,
target: ['chrome90', 'firefox88', 'safari14', 'edge90'],
outfile: 'dist/bundle.js'
});
Node.js 构建
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'node',
target: 'node18',
format: 'esm',
outfile: 'dist/index.js'
});
多个入口点
await esbuild.build({
entryPoints: ['src/index.ts', 'src/worker.ts'],
bundle: true,
outdir: 'dist',
splitting: true,
format: 'esm'
});
输出格式
ESM(ES 模块)
await esbuild.build({
format: 'esm',
// Outputs: import/export syntax
});
CommonJS
await esbuild.build({
format: 'cjs',
// Outputs: require/module.exports
});
IIFE(浏览器脚本)
await esbuild.build({
format: 'iife',
globalName: 'MyApp',
// Outputs: self-executing function
});
加载器
内置加载器
await esbuild.build({
loader: {
'.png': 'file',
'.svg': 'text',
'.json': 'json',
'.woff': 'dataurl'
}
});
加载器类型
js- JavaScriptts- TypeScriptjsx- 带 JSX 的 JavaScripttsx- 带 JSX 的 TypeScriptjson- JSON 数据text- 纯文本file- 复制文件并返回路径dataurl- 内联为数据 URLbinary- 内联为 Uint8Arraybase64- 内联为 base64copy- 复制文件,不生成引用
外部依赖
标记为外部依赖
await esbuild.build({
external: ['react', 'react-dom', 'lodash']
});
外部匹配模式
await esbuild.build({
external: ['*.png', '@aws-sdk/*']
});
代码拆分
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'dist'
});
注意:代码拆分需要 format: 'esm' 和 outdir(而非 outfile)。
插件
插件结构
const myPlugin = {
name: 'my-plugin',
setup(build) {
// On resolve - intercept import paths
build.onResolve({ filter: /^env$/ }, args => ({
path: args.path,
namespace: 'env-ns'
}));
// On load - provide module contents
build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
contents: JSON.stringify(process.env),
loader: 'json'
}));
}
};
await esbuild.build({
plugins: [myPlugin]
});
常见插件
import esbuildPluginTsc from 'esbuild-plugin-tsc';
await esbuild.build({
plugins: [
esbuildPluginTsc({
force: true
})
]
});
开发服务器
Serve API
const ctx = await esbuild.context({
entryPoints: ['src/index.ts'],
bundle: true,
outdir: 'dist'
});
await ctx.serve({
servedir: 'dist',
port: 3000
});
监视模式
const ctx = await esbuild.context({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js'
});
await ctx.watch();
console.log('Watching for changes...');
环境变量
Define API
await esbuild.build({
define: {
'process.env.NODE_ENV': '"production"',
'process.env.API_URL': JSON.stringify(process.env.API_URL)
}
});
优化
压缩
await esbuild.build({
minify: true,
// Or granular control:
minifyWhitespace: true,
minifyIdentifiers: true,
minifySyntax: true
});
Tree Shaking
await esbuild.build({
treeShaking: true,
// Mark files as side-effect free
ignoreAnnotations: false
});
移除 Console 和 Debugger
await esbuild.build({
drop: ['console', 'debugger']
});
类型检查
esbuild 不执行类型检查。请单独运行 TypeScript:
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "npm run typecheck && node esbuild.config.mjs",
"dev": "concurrently \"tsc --noEmit --watch\" \"node esbuild.config.mjs --watch\""
}
}
构建脚本示例
// esbuild.config.mjs
import * as esbuild from 'esbuild';
const isProduction = process.env.NODE_ENV === 'production';
const isWatch = process.argv.includes('--watch');
const config = {
entryPoints: ['src/index.ts'],
bundle: true,
platform: 'browser',
target: ['es2020'],
format: 'esm',
outdir: 'dist',
sourcemap: !isProduction,
minify: isProduction,
splitting: true,
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}
};
if (isWatch) {
const ctx = await esbuild.context(config);
await ctx.watch();
console.log('Watching for changes...');
} else {
await esbuild.build(config);
console.log('Build complete!');
}
最佳实践
建议
- 在 TypeScript 配置中使用
isolatedModules: true - 使用
tsc --noEmit单独运行类型检查 - 在监视模式下使用 context API
- 利用原生的 TypeScript 和 JSX 支持
- 在库中将同级依赖设为 external
避免
- 依赖 esbuild 进行类型检查
- 使用需要类型信息的功能(例如带元数据的装饰器)
- 忽略
isolatedModules要求 - 默认配置已满足需求时,避免过度配置
常见模式
库构建
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
external: ['react', 'react-dom'],
format: 'esm',
outfile: 'dist/index.js',
sourcemap: true
});
应用构建
await esbuild.build({
entryPoints: ['src/index.tsx'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'dist',
minify: true,
sourcemap: true,
target: ['chrome90', 'firefox88', 'safari14']
});
性能提示
- esbuild 会自动并行处理任务
- 内置文件系统缓存
- 在开发过程中使用增量构建
- 避免使用会拖慢构建速度的不必要插件
评论
支持 Markdown