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。
在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的缺点
- 对node.js的支持度尚不完整,例如不支持next.js。
- 生态不成熟,问题缺乏社区解决方案。
- JavaScriptCore引擎与常见的V8引擎扩展插件存在兼容性问题。
- 对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上悬而未决的问题。
偶现,再次执行后正常。
4、moving "@types/node" to cache dir failed
windows的兼容性问题。
八、总结
无法断言bun能否在将来替代node.js,但毫无疑问可以推动node.js变得更好。
node22中内置了websocket,也在async API的兼容性上做了优化。