Bun指北

695 阅读7分钟

bun指北(bun v1.1)

一丶什么是bun?

基本印象:bun ≈ node.js+webpack+内置部分常用插件

bun是一个为提高速度而设计的多功能JavaScript运行时和工具包,配有打包工具、测试运行器和node.js兼容的包管理器。

常见的JavaScript运行时:浏览器、node.js。

常见的打包工具:webpack、vite

常见的测试运行器:jest、vitest

Bun由Zig编写,采用JavaScriptCore引擎。

二、关于zig

了解zig,有助于了解bun。

image.png

Stack Overflow提供数据中,可以看到bun是2023年收入最高的编程语言。

zig是一门追求极致性能和兼顾语法简洁的语言。

它有着近乎python的语法简洁性,从而使得开发者可以将精力集中于性能优化。

同时,它有着良好的跨语言兼容性,能够在C中使用zig,也可以在zig中使用C。

这样一来,zig就有着近乎C的底层操作能力和极致性能的优化空间。

三、bun存在的意义

bun志在整合目前复杂的前端工具链(node.js + webpack + 各种插件),打造一体化前端基建工具。

倘若bun的初衷仅仅是成为node.js的替代品,绝对不会成为近两年最受关注的技术。

无非继yarn、pnpm,前端在包管理工具上又多了一个选择。

简而言之,node.js能做的,bun也要做,node.js做不到的,bun更要做。

这才是它独具魅力的地方。

四、Node.js的设计缺陷(重点)

设计缺陷一般是指项目框架底层的问题,牵一发而动全身,影响范围广,修改周期长。甚至,永远无法在原项目上解决,想要解决的话,只能彻底重构,通过类似vue2到vue3的路线,彻底抛弃历史代码。

1、非标js规范

node.js采用CommonJS规范,随着es6的普及,两种截然不同的async API对代码可读性和兼容性都造成了巨大的困扰。

例如:vite复杂的预加载机制、tree shaking失效等问题。

2、老旧的GYP构建

老版(6.5以前)V8采用 GYP 构建,新版(6.5以后)V8采用 GN 构建。

GN的速度是GYP的二十多倍。

node.js与老版v8同处于一个时代,跟随老版v8采用了GYP构建,但却在后来的更新中脱节。

v8在es6推出后,第一时间完成了版本升级,在编译上,对新的语法类型进行支持,在性能上,也完成了从GYP到GN的升级。

GN的FFI,即跨语言兼容性要优于GYP。

node.js不得不花更多的精力在语言包的兼容上。

3、vendored-by-default 机制

npm的vendored-by-default机制存在根本缺陷。

npm早期的依赖安装是十分简单粗暴的,先安装项目依赖,再安装子依赖的依赖.....

由于不同层级存在相同的依赖,大量的重复安装,导致node_module体积十分庞大。

随后参考了yarn的扁平化,将所有依赖安装到一个层级,但依旧存在一些隐患。

开发者无法确定最终安装的是不是自己想要的版本。

需要借助双版本重命名或者peerDependencies进行优化。

4、过于宽松的引用形式

node.js并不强制在引用时添加文件后缀,也不需要添加文件路径。

require("lodash");
// require("lodash.js");
// require("esm/lodash.js");
Plain Text

node.js会自动为模块添加.js后缀,从最外层向内层遍历寻找相应的模块,倘若找不到,改为添加.json后缀,重复循环遍历的操作。

这种宽松的机制,毫无疑问大大拖累了node.js的性能。

这种机制还导致node.js的文件类型支持的拓展性受限,只能支持.js、.json两种类型,需要通过第三方插件来支持.ts/.jsx/.tsx/.vue。

通过第三方插件实现的类型拓展,本质是一种伪拓展,ts/jsx/tsx/vue最后还是要被转换成js交给node.js处理的。

相较于能够直接解析ts/jsx/tsx的bun,在非纯js项目中,两者存在更明显的性能差距。

五、Bun的优点

1、bun的(启动)速度更快

bun采用追求极致性能zig语言,性能上要略优于采用c++编写的node.js。

(zig与c++都是可以转换成机器码的简单语言,也没有占用性能的垃圾回收极致,性能上并没有本质区别,但zig作为一门新语言,采用了和js/python一样简洁的语法,使得开发者能够将更多的精力集中在性能优化上。在底层系统的调用上,zig拥有良好的跨语言兼容性,无论是在C语言项目中使用zig,还是在zig中使用C语言。简而言之,zig拥有像python一样简洁的语法和C一样强大的底层调用能力。)

bun采用JavaScriptCore引擎,相较于v8引擎,JavaScriptCore的特点是启动快,处理慢。

谁优谁劣,不同的开发者给出的答案或许不尽相同。

从通常的开发体验来看,JavaScriptCore是要更优的。

项目在启动的时候,工作量是最大的,启动时间从10秒优化至5秒,感知是十分明显的。

而在运行时,工作量要小很多,往往都是一些毫秒级的任务,性能上的优化是难以感知的。

2、前端工具一体化

  • 内置ts/jsx解析器,无需配置外的文件类型拓展和安装babel(文件类型转换插件),在非纯js的项目中,性能优势明显。
  • 对esm和CommonJS的兼容性支持。

tips:node.js为支持esm,由.js衍生出两种文件后缀.mjs和.cjs用于区分,esm还需要在package.json中配置type: module。对开发者而言,存在较大的心智负担。

(node22中可以在.cjs中用require引入.mjs文件)

  • 内置web API,对fetch、websocket等常用API进行支持,无需额外安装依赖。
  • 通过bun指令便可以进行打包和启动。内置热更和测试运行器。
  • 一体化工具链,内置集成拥有更好的性能表现。

3、依赖管理优化

  • 持久化缓存,所有依赖会安装到本地的.bun/install/cache文件夹下,安装时,优先从本地获取依赖。
  • 跳过校验机制,直接从持久化缓存中获取依赖。

node.js在从缓存中获取依赖之前,会先从github上校验包是否有更新。

  • bun.lockb采用二进制编写,性能上优于JavaScript类型的其他.lock文件。
  • 内置workspace(用于monorepo形式的仓库管理)

4、良好的兼容度

bun的兼容性主要体现在两个地方:

  • 对node.js的完全兼容,两者之间可以来回切换。
  • 若不想采用一体化工具链,也可以将bun视为纯粹的包管理工具,与webpack\jest\babel\polyfill等插件结合使用。

六、Bun的缺点

  1. 对node.js的支持度尚不完整,例如不支持next.js。
  2. 生态不成熟,问题缺乏社区解决方案。
  3. JavaScriptCore引擎与常见的V8引擎扩展插件存在兼容性问题。
  4. 对windows开发环境极其不友好,具体参考踩坑经历。

七、踩坑经历

1、bun init在C盘外初始化报错

windows的兼容性问题。

2、bun init包含bun install操作

bun在初始化项目的时候会顺带安装初始依赖。

node.js初始化时不会执行依赖安装,对初始化的刻板印象是一个很快的指令,bun init的时候还以为电脑卡了。

3、bun install/create fails with error: ConnectionRefused downloading package manifest

github上悬而未决的问题。

github.com/oven-sh/bun…

偶现,再次执行后正常。

4、moving "@types/node" to cache dir failed

windows的兼容性问题。

github.com/oven-sh/bun…

github.com/oven-sh/bun…

八、总结

无法断言bun能否在将来替代node.js,但毫无疑问可以推动node.js变得更好。

node22中内置了websocket,也在async API的兼容性上做了优化。