内容类型

下面列出了所有内置的内容类型。每种内容类型都有一个关联的"加载器", 它告诉esbuild如何解释文件内容。某些文件扩展名默认已经配置了加载器, 尽管这些默认值可以被覆盖。

JavaScript

加载器: js

这个加载器默认启用于.js.cjs.mjs文件。 .cjs扩展名被node用于CommonJS模块,而.mjs 扩展名被node用于ECMAScript模块。

请注意,默认情况下,esbuild的输出会利用所有现代JS特性。例如,当启用压缩时, a !== void 0 && a !== null ? a : b 将变成a ?? b,这使用了来自ES2020 版本的JavaScript语法。如果不想要这种行为,你必须指定esbuild的 target设置,以说明你需要输出在哪些浏览器中正确工作。 然后esbuild将避免使用对这些浏览器来说过于现代的JavaScript特性。

esbuild支持所有现代JavaScript语法。然而,较新的语法可能不被旧浏览器支持, 因此你可能想要配置target选项,告诉esbuild适当地将较新的 语法转换为较旧的语法。

以下语法特性总是会为旧浏览器进行转换:

Syntax transform Language version Example
Trailing commas in function parameter lists and calls es2017 foo(a, b, )
Numeric separators esnext 1_000_000

These syntax features are conditionally transformed for older browsers depending on the configured language target:

Syntax transform Transformed when --target is below Example
Exponentiation operator es2016 a ** b
Async functions es2017 async () => {}
Asynchronous iteration es2018 for await (let x of y) {}
Async generators es2018 async function* foo() {}
Spread properties es2018 let x = {...y}
Rest properties es2018 let {...x} = y
Optional catch binding es2019 try {} catch {}
BigInt es2020 123n
Optional chaining es2020 a?.b
Nullish coalescing es2020 a ?? b
import.meta es2020 import.meta
Logical assignment operators es2021 a ??= b
Class instance fields es2022 class { x }
Static class fields es2022 class { static x }
Private instance methods es2022 class { #x() {} }
Private instance fields es2022 class { #x }
Private static methods es2022 class { static #x() {} }
Private static fields es2022 class { static #x }
Ergonomic brand checks es2022 #x in y
Class static blocks es2022 class { static {} }
Import assertions esnext import "x" assert {}1
Import attributes esnext import "x" with {}
Auto-accessors esnext class { accessor x }
using declarations esnext using x = y
Decorators esnext @foo class Bar {}

These syntax features are currently always passed through un-transformed:

Syntax transform Unsupported when --target is below Example
RegExp dotAll flag es2018 /./s1
RegExp lookbehind assertions es2018 /(?<=x)y/1
RegExp named capture groups es2018 /(?<foo>\d+)/1
RegExp unicode property escapes es2018 /\p{ASCII}/u1
Top-level await es2022 await import(x)
Arbitrary module namespace identifiers es2022 export {foo as 'f o o'}
RegExp match indices es2022 /x(.+)y/d1
RegExp set notation es2024 /[\w--\d]/v1
Hashbang grammar esnext #!/usr/bin/env node

See also the list of finished ECMAScript proposals and the list of active ECMAScript proposals. Note that while transforming code containing top-level await is supported, bundling code containing top-level await is only supported when the output format is set to esm.

JavaScript caveats

You should keep the following things in mind when using JavaScript with esbuild:

ES5支持不完善

目前还不支持将ES6+语法转换为ES5。但是,如果你正在使用esbuild转换ES5代码, 你仍然应该将target设置为es5。这可以防止esbuild在你的 ES5代码中引入ES6语法。例如,如果没有这个标志,对象字面量{x: x}会变成 {x},字符串"a\nb"在压缩时会变成多行模板字面量。这些替换都是因为 生成的代码更短,但如果targetes5,就不会执行这些替换。

私有成员性能

私有成员转换(用于#name语法)使用WeakMapWeakSet来保持此特性的私有性。 这与Babel和TypeScript编译器中的相应转换类似。大多数现代JavaScript引擎 (V8、JavaScriptCore和SpiderMonkey,但不包括ChakraCore)对于大型WeakMapWeakSet对象可能没有良好的性能特征。

使用此语法转换创建许多带有私有字段或私有方法的类实例可能会给垃圾收集器 带来大量开销。这是因为现代引擎(除了ChakraCore)将弱值存储在实际的map对象中, 而不是作为键本身的隐藏属性,而大型map对象可能会导致垃圾收集的性能问题。 更多信息请参见此参考

导入遵循ECMAScript模块行为

你可能会尝试在导入需要全局状态的模块之前修改全局状态,并期望它能工作。 然而,JavaScript(因此也包括esbuild)实际上会将所有import语句 "提升"到文件顶部,所以这样做是行不通的:

window.foo = {}
import './something-that-needs-foo'

有一些ECMAScript模块的实现(例如TypeScript编译器)在这方面没有遵循 JavaScript规范。用这些工具编译的代码可能会"工作",因为import被替换为 内联的require()调用,这忽略了提升要求。但是这样的代码在真正的 ECMAScript模块实现(如node、浏览器或esbuild)中将无法工作, 所以不建议编写这样的代码,因为它不具有可移植性。

正确的做法是将全局状态修改移到它自己的导入中。这样它_会_在其他导入之前运行:

import './assign-to-foo-on-window'
import './something-that-needs-foo'

打包时避免直接使用eval

虽然表达式eval(x)看起来像一个普通的函数调用,但它在JavaScript中实际上 具有特殊的行为。以这种方式使用eval意味着存储在x中的被评估代码可以通过 名称引用任何包含作用域中的任何变量。例如,代码 let y = 123; return eval('y')将返回123

这被称为"直接eval",在打包代码时会因为多种原因产生问题:

幸运的是,通常很容易避免直接使用eval。有两种常用的替代方法可以避免上述 所有缺点:

函数(和类)上的toString()值不会被保留

在JavaScript函数对象上调用toString()然后将该字符串传递给某种形式的eval 以获取新的函数对象是比较常见的做法。这实际上将函数从包含的文件中"抽离"出来, 并破坏了与该文件中所有变量的链接。在esbuild中这样做不受支持,可能无法正常工作。 特别是,esbuild经常使用辅助方法来实现某些功能,并假设JavaScript作用域规则 没有被篡改。例如:

let pow = (a, b) => a ** b;
let pow2 = (0, eval)(pow.toString());
console.log(pow2(2, 3));

当这段代码被编译为ES6时,由于**运算符在ES6中不可用,**运算符会被替换为 对__pow辅助函数的调用:

let __pow = Math.pow;
let pow = (a, b) => __pow(a, b);
let pow2 = (0, eval)(pow.toString());
console.log(pow2(2, 3));

如果你尝试运行这段代码,你会得到类似 ReferenceError: __pow is not defined 的错误,因为函数(a, b) => __pow(a, b) 依赖于局部作用域中的符号__pow,而这个符号在全局作用域中不可用。 许多JavaScript语言特性都是这种情况,包括async函数,以及一些esbuild特有的 功能,如keep names设置。

当人们使用.toString()获取函数的源代码,然后尝试将其用作 Web Worker 的主体时,这个问题最常出现。如果你正在这样做并且想要使用esbuild, 你应该在单独的构建步骤中为Web Worker构建源代码,然后将Web Worker源代码 作为字符串插入到创建Web Worker的代码中。define功能是 在构建时插入字符串的一种方法。

从模块命名空间对象调用的函数中的this值不会被保留

在JavaScript中,函数中的this值会根据函数的调用方式自动填充。例如,如果使用 obj.fn()调用函数,那么在函数调用期间this的值将是obj。esbuild尊重这种行为, 但有一个例外:如果你从模块命名空间对象调用函数,this的值可能不正确。 例如,考虑这段从模块命名空间对象ns调用foo的代码:

import * as ns from './foo.js'
ns.foo()

如果foo.js尝试使用this引用模块命名空间对象,那么在代码被esbuild打包后, 这可能不会正常工作:

// foo.js
export function foo() {
  this.bar()
}
export function bar() {
  console.log('bar')
}

原因是esbuild自动将大多数使用模块命名空间对象的代码重写为直接导入内容的代码。 这意味着上面的示例代码将被转换为以下内容,这会移除函数调用的this上下文:

import { foo } from './foo.js'
foo()

这种转换极大地改善了树摇(tree shaking) (也称为死代码消除),因为它使esbuild能够理解哪些导出的符号未被使用。 它的缺点是这会改变使用this访问模块导出的代码的行为,但这不是问题, 因为首先就不应该编写这种奇怪的代码。如果你需要从同一个文件中访问导出的函数, 只需直接调用它(即在上面的例子中使用bar()而不是this.bar())。

default导出可能容易出错

ES模块格式(即ESM)有一个特殊的名为default的导出,它有时的行为与所有其他 导出名称不同。当具有default导出的ESM格式的代码被转换为CommonJS格式, 然后该CommonJS代码被导入到另一个ESM格式的模块中时,对于应该发生什么有两种 不同的解释,这两种都被广泛使用(Babel方式和 Node方式)。这非常不幸,因为它导致了无尽的兼容性问题, 尤其是因为JavaScript库通常以ESM编写但以CommonJS发布。

当esbuild打包执行这样操作的代码时,它必须决定使用哪种解释, 但没有完美的答案。esbuild使用的启发式方法与Webpack 使用的相同(详见下文)。由于Webpack是最广泛使用的打包工具,这意味着esbuild 在关于这个兼容性问题方面尽可能与现有生态系统兼容。因此,好消息是如果你可以让 有这个问题的代码在esbuild中工作,它也应该能在Webpack中工作。

这里有一个例子演示了这个问题:

// index.js
import foo from './somelib.js'
console.log(foo)
// somelib.js
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = 'foo';

以下是两种解释,两者都被广泛使用:

如果你是库作者: 在编写新代码时,你应该强烈考虑完全避免使用default导出。 不幸的是,它已经被兼容性问题所污染,使用它可能会在某些时候给你的用户带来问题。

如果你是库用户: 默认情况下,esbuild将使用Babel的解释。如果你想让esbuild 使用Node的解释,你需要将代码放在以.mts.mjs结尾的文件中,或者需要在 package.json文件中添加"type": "module"。 理由是Node的原生ESM支持只能在文件扩展名为.mjs或存在 "type": "module"时运行ESM代码,所以这样做是一个很好的 信号,表明代码打算在Node中运行,因此应该使用Node对default导入的解释。 这与Webpack使用的启发式方法相同。

TypeScript

加载器: tstsx

此加载器默认为.ts.tsx.mts.cts文件启用,这意味着esbuild 内置了对解析TypeScript语法和丢弃类型注释的支持。然而,esbuild 不会 进行任何类型检查,所以你仍然需要与esbuild并行运行tsc -noEmit来检查类型。 这不是esbuild自身所做的事情。

像这样的TypeScript类型声明会被解析并忽略(非详尽列表):

Syntax feature Example
Interface declarations interface Foo {}
Type declarations type Foo = number
Function declarations function foo(): void;
Ambient declarations declare module 'foo' {}
Type-only imports import type {Type} from 'foo'
Type-only exports export type {Type} from 'foo'
Type-only import specifiers import {type Type} from 'foo'
Type-only export specifiers export {type Type} from 'foo'

支持TypeScript特有的语法扩展,并且始终转换为JavaScript(非详尽列表):

Syntax feature Example Notes
Namespaces namespace Foo {}
Enums enum Foo { A, B }
Const enums const enum Foo { A, B }
Generic type parameters <T>(a: T): T => a Must write <T,>(... with the tsx loader
JSX with types <Element<T>/>
Type casts a as B and <B>a
Type imports import {Type} from 'foo' Handled by removing all unused imports
Type exports export {Type} from 'foo' Handled by ignoring missing exports in TypeScript files
Experimental decorators @sealed class Foo {} Requires experimentalDecorators,
does not support emitDecoratorMetadata
Instantiation expressions Array<number> TypeScript 4.7+
extends on infer infer A extends B TypeScript 4.7+
Variance annotations type A<out B> = () => B TypeScript 4.7+
The satisfies operator a satisfies T TypeScript 4.9+
const type parameters class Foo<const T> {} TypeScript 5.0+

TypeScript caveats - TypeScript使用注意事项

在使用esbuild处理TypeScript时,除了JavaScript注意事项外, 你还需要注意以下几点:

Files are compiled independently - 文件独立编译

即使在转译单个模块时,TypeScript编译器实际上仍会解析导入的文件,以便判断导入的名称是类型还是值。 然而,像esbuild和Babel这样的工具(以及TypeScript编译器的transpileModule API)会独立编译每个文件, 因此无法判断导入的名称是类型还是值。

因此,如果你在esbuild中使用TypeScript,应该启用isolatedModules TypeScript配置选项。此选项可以防止你使用那些在像esbuild这样独立编译每个文件(不跨文件追踪类型引用)的环境中 可能导致错误编译的功能。例如,它会阻止你使用export {T} from './types' 从另一个模块重新导出类型(你需要使用export type {T} from './types'代替)。

Imports follow ECMAScript module behavior - 导入遵循ECMAScript模块行为

由于历史原因,TypeScript编译器默认将ESM(ECMAScript模块)语法编译为CommonJS语法。例如, import * as foo from 'foo'会被编译为 const foo = require('foo')。这可能是因为当TypeScript采用这种语法时, ECMAScript模块还只是一个提案。然而,这是一个遗留行为,与这些语法在node等实际平台上的行为不符。 例如,require函数可以返回任何JavaScript值(包括字符串),但import * as语法总是会得到一个对象, 不可能是字符串。

为了避免这个遗留特性带来的问题,如果你在esbuild中使用TypeScript,应该启用 esModuleInterop TypeScript配置选项。启用它会禁用这个遗留行为,并使TypeScript的类型系统与ESM兼容。 这个选项默认不启用是因为它会对现有的TypeScript项目造成破坏性更改,但Microsoft 强烈建议在新项目和现有项目中都应用它 (然后更新你的代码),以便更好地与生态系统的其他部分兼容。

具体来说,这意味着如果要使用ESM导入语法从CommonJS模块导入非对象值,必须使用默认导入而不是使用import * as。 所以如果一个CommonJS模块通过module.exports = fn导出一个函数, 你需要使用import fn from 'path'而不是 import * as fn from 'path'

Features that need a type system are not supported - 不支持需要类型系统的功能

esbuild将TypeScript类型视为注释并忽略它们,因此TypeScript被视为"经过类型检查的JavaScript"。 类型注解的解释由TypeScript类型检查器负责,如果你使用TypeScript,你应该在esbuild之外运行类型检查器。 这与Babel的TypeScript实现使用的是相同的编译策略。然而,这意味着一些需要类型解释才能工作的TypeScript 编译功能在esbuild中不起作用。

具体来说:

Only certain tsconfig.json fields are respected - 仅支持特定的tsconfig.json字段

在打包过程中,esbuild的路径解析算法会考虑最近的父目录中的tsconfig.json文件内容,并相应地修改其行为。 也可以使用esbuild的tsconfig设置在构建API中显式设置tsconfig.json路径, 或使用esbuild的tsconfigRaw设置在转换API中显式传入tsconfig.json文件的内容。 但是,esbuild目前只检查tsconfig.json文件中的以下字段:

所有其他tsconfig.json字段(即不在上述列表中的字段)都将被忽略。

You cannot use the tsx loader for *.ts files - 不能对*.ts文件使用tsx加载器

tsx加载器不是ts加载器的超集。它们是两种不同的、部分不兼容的语法。例如,字符序列 <a>1</a>/gts加载器中解析为<a>(1 < (/a>/g)),而在tsx加载器中解析为 (<a>1</a>) / g

这导致的最常见问题是在使用tsx加载器时无法在箭头函数表达式上使用泛型类型参数, 比如<T>() => {}。这是有意为之的,与官方TypeScript编译器的行为一致。在tsx语法中, 这个语法空间是为JSX元素保留的。

CSS

加载器: css

这个加载器默认启用于.css文件。当CSS文件被导入到JavaScript中时, CSS代码会被打包,并在运行时注入到文档的<head>中。

import './styles.css'

JSX

加载器:jsxtsx

JSX是为React创建的 JavaScript的XML类语法扩展。它旨在被你的构建工具转换为普通的JavaScript。每个XML元素都会变成 一个普通的JavaScript函数调用。例如,以下JSX代码:

import Button from './button'
let button = <Button>Click me</Button>
render(button)

将被转换为以下JavaScript代码:

import Button from "./button";
let button = React.createElement(Button, null, "Click me");
render(button);

这个加载器默认对.jsx.tsx文件启用。注意,JSX语法在.js文件中默认是不启用的。 如果你想启用它,你需要进行配置:

CLI JS Go
esbuild app.js --bundle --loader:.js=jsx
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.js': 'jsx' },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".js": api.LoaderJSX,
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

Auto-import for JSX - JSX的自动导入

使用JSX语法通常需要你手动导入你正在使用的JSX库。例如,如果你使用React,默认情况下 你需要在每个JSX文件中像这样导入React:

import * as React from 'react'
render(<div/>)

这是因为JSX转换会将JSX语法转换为对React.createElement的调用, 但它本身不会导入任何内容,所以React变量不会自动存在。

如果你想避免在每个文件中手动import你的JSX库,你可以通过将esbuild的JSX 转换设置为automatic来实现,它会为你生成导入语句。请记住,这也会完全改变JSX转换的 工作方式,所以如果你使用的不是React的JSX库,它可能会破坏你的代码。配置方式如下:

CLI JS Go
esbuild app.jsx --jsx=automatic
require('esbuild').buildSync({
  entryPoints: ['app.jsx'],
  jsx: 'automatic',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.jsx"},
    JSX:         api.JSXAutomatic,
    Outfile:     "out.js",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

Using JSX without React - 不使用React的JSX

如果你使用React以外的库(如Preact)的JSX,你可能需要配置 JSX factoryJSX fragment设置, 因为它们分别默认为React.createElementReact.Fragment

CLI JS Go
esbuild app.jsx --jsx-factory=h --jsx-fragment=Fragment
require('esbuild').buildSync({
  entryPoints: ['app.jsx'],
  jsxFactory: 'h',
  jsxFragment: 'Fragment',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.jsx"},
    JSXFactory:  "h",
    JSXFragment: "Fragment",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

另外,如果你使用TypeScript,你可以通过在tsconfig.json文件中添加以下内容来配置JSX, esbuild应该会自动识别这些配置,而不需要额外设置:

{
  "compilerOptions": {
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"
  }
}

Text - 文本

加载器:text

此加载器默认对.txt文件启用。它在构建时将文件作为字符串加载,并将该字符串作为默认导出。 使用它看起来像这样:

import string from './example.txt'
console.log(string)

注意,如果文件中存在UTF-8 BOM, 此加载器会自动将其删除。BOM是一个特殊的字节序列,某些程序(如Windows上的记事本)在 保存文件时有时会插入。

Binary - 二进制

加载器:binary

此加载器会在构建时将文件作为二进制缓冲区加载,并使用Base64编码将其嵌入到包中。 文件的原始字节在运行时从Base64解码,并使用默认导出作为Uint8Array导出。 使用它看起来像这样:

import uint8array from './example.data'
console.log(uint8array)

如果你需要ArrayBuffer,你可以直接访问uint8array.buffer。 注意,此加载器默认不启用。你需要为相应的文件扩展名配置它,像这样:

CLI JS Go
esbuild app.js --bundle --loader:.data=binary
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.data': 'binary' },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".data": api.LoaderBinary,
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

Base64

加载器:base64

此加载器会在构建时将文件作为二进制缓冲区加载,并使用Base64编码将其作为字符串嵌入到包中。 这个字符串使用默认导出导出。使用它看起来像这样:

import base64string from './example.data'
console.log(base64string)

注意,此加载器默认不启用。你需要为相应的文件扩展名配置它,像这样:

CLI JS Go
esbuild app.js --bundle --loader:.data=base64
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.data': 'base64' },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".data": api.LoaderBase64,
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

如果你打算将其转换为Uint8ArrayArrayBuffer,你应该使用binary加载器。 它使用优化的Base64到二进制转换器,比通常的atob转换过程更快。

Data URL - 数据URL

加载器:dataurl

此加载器会在构建时将文件作为二进制缓冲区加载,并将其作为Base64编码的数据URL嵌入到包中。 这个字符串使用默认导出导出。使用它看起来像这样:

import url from './example.png'
let image = new Image
image.src = url
document.body.appendChild(image)

数据URL包含基于文件扩展名和/或文件内容的最佳MIME类型猜测,对于二进制数据看起来像这样:

data:image/png;base64,iVBORw0KGgo=

...或者对于文本数据看起来像这样:

data:image/svg+xml,<svg></svg>%0A

注意,此加载器默认不启用。你需要为相应的文件扩展名配置它,像这样:

CLI JS Go
esbuild app.js --bundle --loader:.png=dataurl
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.png': 'dataurl' },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".png": api.LoaderDataURL,
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

External file - 外部文件

根据你需要的行为,有两种不同的加载器可以用于外部文件。两种加载器都在下面描述:

The file loader - file加载器

加载器:file

此加载器会将文件复制到输出目录,并将文件名作为字符串嵌入到包中。这个字符串使用默认导出导出。 使用它看起来像这样:

import url from './example.png'
let image = new Image
image.src = url
document.body.appendChild(image)

这种行为有意类似于Webpack的file-loader 包。注意,此加载器默认不启用。你需要为相应的文件扩展名配置它,像这样:

CLI JS Go
esbuild app.js --bundle --loader:.png=file --outdir=out
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.png': 'file' },
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".png": api.LoaderFile,
    },
    Outdir: "out",
    Write:  true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

默认情况下,导出的字符串只是文件名。如果你想在导出的字符串前添加基本路径,可以使用 public path API选项来实现。

The copy loader - copy加载器

加载器:copy

此加载器会将文件复制到输出目录,并重写导入路径以指向复制的文件。这意味着导入语句仍会 存在于最终的包中,并且最终的包仍会引用该文件而不是将文件包含在包内。如果你在esbuild的 输出上运行额外的打包工具,或者你想从包中省略一个很少使用的数据文件以提高启动性能, 或者你想依赖运行时的特定行为(由导入触发),这可能会很有用。例如:

import json from './example.json' assert { type: 'json' }
console.log(json)

如果你使用以下命令打包上述代码:

CLI JS Go
esbuild app.js --bundle --loader:.json=copy --outdir=out --format=esm
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.json': 'copy' },
  outdir: 'out',
  format: 'esm',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".json": api.LoaderCopy,
    },
    Outdir: "out",
    Write:  true,
    Format: api.FormatESModule,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

生成的out/app.js文件可能看起来像这样:

// app.js
import json from "./example-PVCBWCM4.json" assert { type: "json" };
console.log(json);

注意导入路径已被重写以指向复制的文件out/example-PVCBWCM4.json(由于asset names 设置的默认值,添加了内容哈希),并且保留了JSON的import assertion, 这样运行时就能加载JSON文件。

Empty file - 空文件

加载器:empty

此加载器告诉esbuild假装一个文件是空的。在某些情况下,这可以帮助从你的包中移除内容。 例如,你可以将.css文件配置为使用empty加载器,以防止esbuild打包导入到JavaScript 文件中的CSS文件:

CLI JS Go
esbuild app.js --bundle --loader:.css=empty
require('esbuild').buildSync({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.css': 'empty' },
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".css": api.LoaderEmpty,
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

此加载器还允许你从CSS文件中移除导入的资源。例如,你可以将.png文件配置为使用empty 加载器,这样CSS代码中对.png文件的引用(如url(image.png))会被替换为url()