本文最后更新于:2024年4月25日 上午
Esbuild
是一个前端打包工具,使用Go
编写拥有极快的打包于压缩能力
安装
Esbuild
1 2 pnpm i esbuild -D pnpm i typescript -D
ts-node
由于使用了TS
,后续用ts-node
来直接执行TS
文件,所以要先安装ts-node
tsc --init
初始化tsconfig.json
,并添加ts-node
配置与module
配置
tsconfig.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "ts-node" : { "esm" : true } , "compilerOptions" : { "target" : "es2017" , "module" : "ESNext" , "moduleResolution" : "node" , "esModuleInterop" : true , "forceConsistentCasingInFileNames" : true , "strict" : true , "skipLibCheck" : true } }
CLI 编译
1 2 3 4 5 6 7 8 9 10 11 12 { "type" : "module" , "scripts" : { "build" : "esbuild src/main.ts --outdir=dist" } , "devDependencies" : { "@types/node" : "^18.11.18" , "esbuild" : "^0.17.5" , "typescript" : "^4.9.5" } }
esbuild
自带一些loader
,可以直接打包ts
, jsx
, tsx
, css
等文件
src/main.ts
1 2 3 4 5 6 7 8 9 10 11 12 interface Person { name : string age : number }const person : Person = { name : '清山' , age : 20 }console .log (person)
transform
API接受一个待编译的字符串 和一个TransformOptions
对象, 用Promise
返回编译结果
1 2 3 4 5 6 7 8 9 10 11 import { transform, TransformOptions } from 'esbuild' async function runTransform ( ) { const options : TransformOptions = {} const res = await transform ('typeof x == null' , options) console .log (res) }runTransform ()
API Build
build
API接受一个BuildOptions
对象, 用Promise
返回构建结果
src/cli/build.ts
1 2 3 4 5 6 7 8 9 10 11 import { build, BuildOptions } from 'esbuild' const options : BuildOptions = {}runBuild (options)async function runBuild (options: BuildOptions ) { const res = await build (options) console .log (res) }
absWorkingDir
absWorkingDir
属性设置项目的根目录,设置后输入输出的地址可以依此来用相对路径,若用绝对路径设置输入输出也可不设置该属性
1 2 3 4 const options : BuildOptions = { absWorkingDir : process.cwd (), }
outfile与outdir
outfile
选项适用于单入口项目,打包输出单文件的情况.
outdir
设置结果输出目录
1 2 3 4 5 6 7 8 9 const options1 : BuildOptions = { absWorkingDir : process.cwd (), outfile : './dist/index.js' }const options2 : BuildOptions = { absWorkingDir : process.cwd (), outdir : './dist' }
entryPoints
声明入口文件路径数组,有多个独立模块可以声明多个路径入口,默认输出项目入口名与入口相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const options : BuildOptions = { absWorkingDir : process.cwd (), outdir : './dist' , entryPoints : [ './src/main.ts' , ] }const options : BuildOptions = { absWorkingDir : process.cwd (), outdir : './dist' , entryPoints : [ {out : 'utils/log.js' , in : './src/utils/say.ts' } ] }
先写两个待编译的文件
src/main.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { say } from './utils/say' interface Person { name : string age : number }const person : Person = { name : '清山' , age : 20 , }say (person)
src/utils/say.ts
1 2 3 4 export function say (words: unknown ): void { console .log (words) }
src/cli/build.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { build, BuildOptions } from 'esbuild' const options : BuildOptions = { absWorkingDir : process.cwd (), entryPoints : ['./src/main.ts' ], outdir : './dist/esm' , format : 'esm' , }async function runBuild ( ) { const res = await build (options) console .log (res) }runBuild ()
使用命令ts-node ./src/cli/build.ts
即可打包, 但打包结果没有打包say.ts
1 2 3 4 5 6 7 8 import { say } from "./utils/say" ;const a = 1 ;const person = { name : "\u6E05\u5C71" , age : 20 };say (person);
bundle
默认build
只会打包入口文件,不会将导入的文件与包一起打包,需要显示传递bundle: true
才能打包所有入口文件即子文件导入的文件
1 2 3 4 5 const options : BuildOptions = { bundle : true , }
1 2 3 4 5 6 7 8 9 10 11 12 function say (words ) { console .log (words); }var person = { name : "\u6E05\u5C71" , age : 20 };say (person);
banner
与footer
分别在相应类型的文件开头与结尾插入代码
1 2 3 4 5 6 7 8 9 const options : BuildOptions = { banner : { js : '#!/usr/bin/env node' }, footer : { js : '// @link https://esbuild.github.io/api/#footer' } }
编译结果如下
dist/esm/main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/usr/bin/env node function say (words ) { console .log (words); }var person = { name : "\u6E05\u5C71" , age : 20 };say (person);
打包结果设置
format
: 设置产物格式"iife" | "cjs" | "esm"
charset
: 设置产物字符编码
minify
: 压缩产物
spitting
: 自动拆包
treeShaking
: 是否使用树摇
sourcemap
: 是否使用源映射文件
plugins
使用插件以拓展esbuild
打包能力或优化打包效果esbuild - Plugins ,下面使用插件打包Vue3
1 2 pnpm i vue pnpm i esbuild-plugin-vue-next -D
1 2 3 4 5 6 7 8 9 10 11 import pluginVue from 'esbuild-plugin-vue-next' const options : BuildOptions = { absWorkingDir : process.cwd (), entryPoints : ['./src/main.ts' ], plugins : [pluginVue ()], outdir : './dist/esm' , format : 'esm' , bundle : true , minify : true , }
src/main.ts
1 2 3 4 5 import { createApp } from 'vue' import App from './App.vue' const app = createApp (App ) app.mount ('#app' )
src/App.vue
1 2 3 <template > <h1 > Hello World!</h1 > </template >
define
该配置可以对代码中匹配的字符串进行替换,如Vue3
打包后浏览器控制台会提示建议替换__VUE_OPTIONS_API__
,__VUE_PROD_DEVTOOLS__
以提供这两个常量
Feature flags __VUE_OPTIONS_API__
, __VUE_PROD_DEVTOOLS__
are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle.
For more details, see https://link.vuejs.org/feature-flags.
1 2 3 4 5 6 7 const options : BuildOptions = { define : { __VUE_OPTIONS_API__ : 'false' , __VUE_PROD_DEVTOOLS__ : 'false' , }, }
API Context
context
API接受同build
API相同的参数
1 2 3 4 import { context, BuildOptions } from 'esbuild' const options : BuildOptions = {}const ctx = await context (options)
自定义插件
esbuild
插件为一个对象拥有name
与setup
属性
1 2 3 4 5 6 7 8 9 10 11 const pluginTmp = { name : 'tmp' , setup (build: PluginBuild ) { console .log (build) } }const options : BuildOptions : { plugins : [pluginTmp] }
可以将插件用函数返回这样就可以对插件传入一些设置
1 2 3 4 5 6 7 function pluginTmp (lang: 'en-US' | 'zh-CN' ) { return { name : 'tmp' setup (build: PluginBuild ) {} } }