<返回更多

Bun 的新 Bundler:比 webpack 快 220 倍?

2023-05-25  今日头条  前端圈
加入收藏

 


Bun的快速原生捆绑器现在处于测试阶段。它可以通过 bun build CLI 命令或新的 Bun.build() JAVAScript API 使用。

 


从头开始捆绑 10 份三份.js副本,带有源映射和缩小


使用捆绑器通过内置 Bun.build() 函数或 bun build CLI 命令构建前端应用。

JavaScript

CLI

Bun.build({
  entrypoints: ['./src/index.tsx'],
  outdir: './build',
  minify: true,
  // additional config
});

降低 JavaScript 中的复杂性


JavaScript 最初是表单字段的自动填充,如今它为将火箭发射到太空的仪器提供动力。
不出所料,JavaScript 生态系统的复杂性呈爆炸式增长。你如何运行打字稿文件?您如何构建/捆绑用于生产的代码?该软件包是否适用于 ESM?如何加载仅限本地的配置?是否需要安装对等依赖项?如何使源映射正常工作?


复杂性需要时间,通常花费在将工具粘合在一起或等待事情完成上。安装 npm 包需要很长时间。运行测试应该需要几秒钟(或更短)。为什么在 2023 年部署软件需要几分钟,而在 2003 年将文件上传到 FTP 服务器需要几毫秒?


多年来,我一直对JavaScript周围的一切缓慢感到沮丧。当从保存文件到测试更改的迭代周期时间变得足够长,以至于本能地检查黑客新闻时,就出了问题。


这种复杂性是有充分理由的。捆绑器和最小化器使网站加载速度更快。TypeScript 的编辑器内交互式文档使开发人员的工作效率更高。类型安全有助于在将错误交付给用户之前捕获错误。作为版本控制包的依赖项通常比复制文件更易于维护。


当“一件事”被拆分为如此多的孤立工具时,“做好一件事”的Unix哲学就崩溃了。


这就是我们构建 Bun 的原因,也是为什么今天我们很高兴推出 Bun 捆绑器。

Yes, a new bundler是的,一个新的捆绑器


使用新的捆绑器,捆绑现在是 Bun 生态系统的一流元素,包括 bun build CLI 命令、新的顶级 Bun.build 函数和稳定的插件系统。
我们决定 Bun 需要自己的捆绑器有几个原因。

Cohesiveness


捆绑器是一种元工具,它编排并支持所有其他工具,如 JSX、TypeScript、css 模块和服务器组件——所有这些都需要捆绑器集成才能工作。


今天,捆绑器是JavaScript生态系统中巨大复杂性的来源。通过将捆绑引入JavaScript运行时,我们认为我们可以使交付前端和全栈代码更简单,更快。


很快,捆绑器将与Bun的HTTP服务器API( Bun.serve )集成,从而可以用简单的声明式API表示当前复杂的构建管道。稍后会详细介绍。

Performance


这个不会让任何人感到惊讶。作为一个运行时,Bun的代码库已经包含了快速解析和转换源代码的基础(用Zig实现)。虽然可能,但很难与现有的本机捆绑器集成,并且进程间通信所涉及的开销会损害性能。
最终,结果不言自明。在我们的基准测试(源自 esbuild 的三.js基准测试)中,Bun 比 esbuild 快 1.75 倍,比 Parcel 2 快 150 倍,比 Rollup + Terserr 快 180 倍,比 Webpack 快 220 倍。

Developer experience 开发人员体验


查看现有捆绑器的 API,我们看到了很多改进的空间。没有人喜欢与捆绑器配置搏斗。Bun 的捆绑器 API 被设计为明确且不足为奇。说到这里...

The API


该 API 目前在设计上是最小的。我们在此初始版本中的目标是实现一个最小功能集,该功能集快速、稳定,并适应大多数现代用例,而不会牺牲性能。
以下是当前存在的 API:

interface Bun {
  build(options: BuildOptions): Promise<BuildOutput>;
}

interface BuildOptions {
  entrypoints: string[]; // required
  outdir?: string; // default: no write (in-memory only)
  target?: "browser" | "bun" | "node"; // "browser"
  format?: "esm"; // later: "cjs" | "iife"
  splitting?: boolean; // default false
  plugins?: BunPlugin[]; // [] // see https://bun.sh/docs/bundler/plugins
  loader?: { [k in string]: string }; // see https://bun.sh/docs/bundler/loaders
  external?: string[]; // default []
  sourcemap?: "none" | "inline" | "external"; // default "none"
  root?: string; // default: computed from entrypoints
  publicPath?: string; // e.g. http://mydomAIn.com/
  naming?:
    | string // equivalent to naming.entry
    | { entry?: string; chunk?: string; asset?: string };
  minify?:
    | boolean // default false
    | { identifiers?: boolean; whitespace?: boolean; syntax?: boolean };
}


其他捆绑器在追求功能完整性时做出了糟糕的架构决策,最终导致性能下降;这是我们小心翼翼地试图避免的错误。

Module systems


目前仅支持 format: "esm" 。我们计划添加对其他模块系统和目标的支持,如 iife 。如果有足够多的人问,我们也会添加 cjs otuput 支持(支持 CommonJS 输入,但不支持输出)。

Targets


支持三个“目标”: "browser" (默认值)、 "bun" 和 "node" 。

browser

bun

node


目前,这与 target: "bun" 相同。将来,我们计划自动填充 Bun API,例如 Bun 全局模块和 bun:* 内置模块。

File types


捆绑器支持以下文件类型:


其他一切都被视为资产。资产按原样复制到 outdir 中,导入将替换为文件的相对路径或 URL,例如 /images/logo.png .

Input

Output

import logo from "./images/logo.png";

console.log(logo);

Plugins


与运行时本身一样,捆绑器被设计为可通过插件进行扩展。事实上,运行时插件和捆绑器插件之间根本没有区别。

import YamlPlugin from "bun-plugin-yaml";

const plugin = YamlPlugin();

// register a runtime plugin
Bun.plugin(plugin);

// register a bundler plugin
Bun.build({
  entrypoints: ["./src/index.ts"],
  plugins: [plugin],
});

Build outputs


Bun.build 函数返回一个 Promise<BuildOutput> ,定义为:

interface BuildOutput {
  outputs: BuildArtifact[];
  success: boolean;
  logs: Array<object>; // see docs for details
}

interface BuildArtifact extends Blob {
  kind: "entry-point" | "chunk" | "asset" | "sourcemap";
  path: string;
  loader: Loader;
  hash: string | null;
  sourcemap: BuildArtifact | null;
}


outputs 数组包含生成生成的所有文件。每个项目都实现 Blob 接口。

const build = await Bun.build({
  /* */
});

for (const output of build.outputs) {
  output.size; // file size in bytes
  output.type; // MIME type of file

  await output.arrayBuffer(); // => ArrayBuffer
  await output.text(); // string
}


项目还包含以下附加属性:

kind

此文件是哪种类型的生成输出。构建会生成捆绑的入口点、代码拆分的“块”、源映射和复制的资产(如图像)。

path

磁盘上文件的绝对路径或输出路径(如果文件未写入磁盘)。

loader

加载程序用于解释文件。请参阅 捆绑程序 > 加载程序 以了解 Bun 如何将文件扩展名映射到相应的内置加载程序。

hash

文件内容的哈希。始终为资产定义。

sourcemap

与此文件对应的源映射的另一个 BuildArtifact (如果生成)。仅为入口点和区块定义。

与 BunFile 类似, BuildArtifact 对象可以直接传递到 new Response() 中。

const build = Bun.build({
  /* */
});

const artifact = build.outputs[0];

// Content-Type is set automatically
return new Response(artifact);


Bun 运行时在记录 BuildArtifact 对象时实现了特殊的漂亮打印,以便更轻松地进行调试。

Build script

Shell output

// build.ts
const build = Bun.build({/* */});

const artifact = build.outputs[0];
console.log(artifact);

Server components

Bun 的捆绑器通过 --server-components 标志对 React Server Components 提供了实验性支持。我们将在本周晚些时候发布其他文档和示例项目。

Tree shaking

Bun 的捆绑器支持对未使用的代码进行树摇晃。捆绑时始终启用此功能。

package.json"sideEffects"field package.json"sideEffects"field


Bun 在 package.json 中支持 "sideEffects": false 。这是对捆绑器的提示,即该包没有副作用,并且可以更积极地消除死代码。

PURE__评论


Bun 支持 __PURE__ 注释:

file.js

function foo() {
  return 123;
}

/** #__PURE__ */ foo();


由于 foo 没有副作用,这会导致一个空文件:

output.js


 


在 Webpack 的文档 中了解更多信息。

process.env.NODE_ENVand--define


Bun 支持 NODE_ENV 环境变量和 --define CLI 标志。这些通常用于有条件地在生产版本中包含代码。


如果 process.env.NODE_ENV 设置为 "production" ,Bun 将自动删除包装在 if (process.env.NODE_ENV !== "production") { ... } 中的代码。

node-env.js

if (process.env.NODE_ENV !== "production") {
  module.exports = require("./cjs/react.development.js");
} else {
  module.exports = require("./cjs/react.production.min.js");
}

ES Module tree-shaking ES 模块摇树


ESM 树摇动适用于 ESM 和 CommonJS 输入文件。Bun 的捆绑器会在安全的情况下自动从 ESM 文件中删除未使用的导出。

entry.js

foo.js

import { foo } from "./foo.js";
console.log(foo);


未使用的 bar 导出将被消除,从而导致:

output.js

// foo.js
var $foo = 456;
console.log($foo);

CommonJS tree-shaking


在有限的情况下,Bun 的捆绑器会自动将 CommonJS 转换为 ESM,运行时开销为零。考虑这个微不足道的例子:

index.ts

foo.js

import { foo } from "./foo.js";
console.log(foo);


Bun 会自动将 foo.js 转换为 ESM,并对未使用的 exports 对象进行树摇晃。

Bundled

// foo.js
var $foo = 123;

// entry.js
console.log($foo);


请注意,在许多情况下,CommonJS的动态特性使这变得非常困难。例如,考虑以下三个文件:

// entry.js
export default require("./foo");


Bun 无法在不执行 foo.js 的情况下静态确定它的导出。( Object.assign 也可以被覆盖,这使得静态分析在一般情况下是不可能的。在这种情况下,Bun 不会摇晃 exports 对象;相反,它会注入一些 CommonJS 运行时代码以使其按预期工作。

 

Source maps


捆绑器支持内联和外部源映射。

const build = await Bun.build({
  entrypoints: ["./src/index.ts"],

  // generates a *.js.map file alongside each output
  sourcemap: "external",

  // adds a base64-encoded `sourceMAppingURL` to the end of each output file
  sourcemap: "inline",
});

console.log(await build.outputs[0].sourcemap.json()); // => { version: 3, ... }

Minifier


没有缩小器的JavaScript捆绑器是不完整的。此版本还引入了内置于 Bung 中的全新 JavaScript 缩减器。使用 minify: true 启用缩小,或使用以下选项更精细地配置缩小行为:

{
  minify?: boolean | {
    identifiers?: boolean; // default: false
    whitespace?: boolean; // default: false
    syntax?: boolean; // default: false
  }
}


缩小器能够删除死代码,重命名标识符,删除空格,并智能地压缩和内联常量值。

Input

Minified

// This comment will be removed!
console.log("this" + " " + "text" + " will" + " be " + "merged");

Jump in


我们更新了以下 React bun create 模板,以便在后台使用 Bun.build 。运行以下命令来搭建由 Bun 捆绑器提供支持的简单 React 项目的基架。

# a React single-page app
bun create react ./myapp

# a Next.js-like app with a /pages directory
# with SSR and client-side hydration
bun create react-ssr ./myapp

Sneak peek:Bun.App


捆绑器只是为更雄心勃勃的努力奠定了基础。在接下来的几个月里,我们将宣布 Bun.App :一个“超级 API”,它将 Bung 的原生速度捆绑器、HTTP 服务器和文件系统路由器拼接成一个有凝聚力的整体。


目标是让使用Bun轻松表达任何类型的应用程序,只需几行代码:

Static file server

API server

Next.js-style framework

new Bun.App({
 bundlers: [
   {
     name: "static-server",
     outdir: "./out",
   },
 ],
 routers: [
   {
     mode: "static",
     dir: "./public",
     build: "static-server",
   },
 ],
});

app.serve();
app.build();


此 API 仍在积极讨论中,可能会发生变化。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>