背景

这段时间一个性能号称能够吊打Node和Deno的JavaScript运行时Bun(包子)横空出世,立刻在JavaScript社区闹得满城风雨。那么到底这个号称快到飞起的包子运行时到底几斤几两呢?这篇文章就让咱们通过一些理论的例子来理解一下Bun提供的性能以及它比Node到底快了多少,最初再探讨一下Bun是不是能够代替Node。

什么是Bun

咱们先来看官网形容:

Bun is a fast all-in-one JavaScript runtime

Bundle, transpile, install and run JavaScript & TypeScript projects — all in Bun. Bun is a new JavaScript runtime with a native bundler, transpiler, task runner and npm client built-in.

简略来说Bun是一个大而全的JavaScript运行时

它不止做了Node和Deno的工作(JavaScript运行时),而且还原生反对上面这些性能:

  • bundler: Bun能够像Webpack等工具一样对咱们的我的项目进行打包
  • transpiler: 反对TypeScript/JavaScript/JSX的Transpiling,换句话来说就是你能够在我的项目外面间接写TypeScript和JSX语法而不必放心配错环境了
  • task runner: 能够跑package.json外面的script脚本,相似于npm run xxx
  • 内置的和npm一样的包管理工具: 实现了和npm,yarn和pnpm等包管理工具一样的性能,不过更快

有同学看到这里能够会问:这些性能不是当初前端和node生态都反对了吗?有什么好学的?其实不然,Bun的特色其实不在这些性能,而是在原生这两个方面。原生就意味着这是Bun自带的,咱们不须要写任何配置文件或者装置任何插件就能够用,这就升高了咱们编写代码的老本以及编写效率。而Bun另外一个特点是快,那它有如许快呢?看一些官网的形容:

从下面的形容能够看出这些数据:

  • Bun的服务端渲染速度大略是Node的3倍
  • Bun跑脚本的速度大略是npm run的30倍
  • Bun的下载包的速度是yarn的20倍

听起来的确有点快,到底实际效果如何呢?接着就让咱们理论看一下。

Bun性能介绍

这部分内容将会介绍Bun和Node或者Node生态其余工具如npm的比照,而不会波及到Deno,因为Deno其实和Node的性能没有太大的区别。

Bundler和Transpiler

作为一个前端工程师,咱们常常须要应用Webpack等工具来对咱们的代码进行transpile和bundle。和Node不同的是,Bun原生就反对了Bundler和Transipler这些能力,而不必咱们额定装置其它依赖。除了这些,Bun还内置了React脚手架的性能,你输出一个命令即可创立一个React利用:

bun create react ./react-app

下面的命令跑完后会生成一个这样目录构造的我的项目代码:

react-app├── node_modules├── README.md├── bun.lockb├── node_modules.bun├── package.json├── public│   ├── favicon.ico│   ├── index.html│   ├── logo192.png│   ├── logo512.png│   ├── manifest.json│   └── robots.txt└── src    ├── App.css    ├── App.jsx    ├── images.d.ts    ├── index.css    ├── index.jsx    ├── logo.svg    └── reportWebVitals.js

从下面的目录构造来看,应用bun创立的React利用非常清新,根本没有额定的配置文件,能够间接进行代码的编写。最重要的是,在笔者的渣渣电脑上跑这个命令只须要6.55s!。而相比之下应用npx create-react-app跑了足足四分钟才创立出一个React我的项目来。换句话来说Bun创立一个React我的项目的速度是用npm通过create-react-app创立速度的40倍!单纯从这个速度上看,Bun说本人快如闪电真的有余不为过。

下面生成的代码尽管只有JS文件,不过bun是原生反对TypeScript Transpiler的,所以你间接写TypeScript的代码也是能够运行的。

代码生成后,咱们能够通过bun dev命令来启动一个监听在3000端口的带热更新的dev server。这个命令在我的电脑的执行工夫是0.3s!,还没反馈过去服务就起来的。相同应用create-react-app创立的我的项目用npm start耗时是25s。单纯看这个case的话,Bun task runner是npm task runner的83倍

在你开发完代码之后运行bun bun这个命令就能够对我的项目打包了,这个命令会打包出一个带有.bun后缀的二进制文件,这个命令大略执行了0.1s,打包完的文件你间接扔到一个动态服务器就能够拜访了。而绝对之下,create-react-app创立的利用运行npm run build打包耗时28s,所以这个状况下,Bun的速度是npm的280倍

这里值得一提的是,尽管bun打包文件绝对于应用npm来打包文件快的不止一个数量级,不过bun还不反对minify和tree-shaking等常见的动态代码优化伎俩,这些都在它的RoadMap下面,前面会反对的。到时候加上了这些性能后还有没有这么快,就只能刮目相待了。

用Bun开发一个Web服务器

Bun对HTTP服务的反对是很好的,你能够编写极其精简的代码来启动一个Web服务:

// http.jsexport default {  port: 3000,  fetch(request) {    return new Response("Hello World!")  }}

下面的代码会在3000端口启动一个HTTP服务,这个服务会返回一个Hello World!字符串。在笔者的电脑(Macbook Pro 2.9GHz 双核 Intel Core i5 8GB内存)上应用wrk工具来测试一下这个服务的性能:

wrk -t 10 -c256 -d30s http://localhost:3000/

下面的命令会启动10个线程256个链接来间断测试咱们的接口30s,最初失去的数据是:

Running 30s test @ http://localhost:3000/  10 threads and 256 connections  Thread Stats   Avg      Stdev     Max   +/- Stdev    Latency    17.42ms   40.52ms 515.51ms   96.32%    Req/Sec     2.33k   822.60     4.26k    65.55%  660630 requests in 30.08s, 57.33MB read  Socket errors: connect 0, read 3, write 0, timeout 0Requests/sec:  21963.67Transfer/sec:      1.91MB

咱们能够看到Bun原生的HTTP服务在我的电脑上的QPS能够达到21963.67,那么咱们再来看一下Node的原生HTTP服务的性能能够到多少,代码如下:

const http = require('http')const server = http.createServer((req, res) => {  res.end('Hello World!')})server.listen(3000, () => {  console.log('server is up')})

下面的代码实现了和Bun的HTTP服务器一样的逻辑,只不过编写的代码多了一丢丢。在同样的电脑应用同样的参数来测试一下Node原生HTTP服务的性能失去的后果是:

Running 30s test @ http://localhost:3000/  10 threads and 256 connections  Thread Stats   Avg      Stdev     Max   +/- Stdev    Latency    23.80ms   34.24ms 460.40ms   96.19%    Req/Sec     1.31k   341.72     2.02k    82.15%  376228 requests in 30.07s, 46.64MB read  Socket errors: connect 0, read 58, write 1, timeout 0Requests/sec:  12513.65Transfer/sec:      1.55MB

从下面的后果能够看到,Node原生的HTTP服务的QPS是12513。单纯从这个测试后果来看,Bun的性能大略是Node的1.75倍。这么一看尽管Bun是快点,不过也没有很夸大。不过这可能也和机器无关,因为我看了一下一些国外博主的测试后果,他们电脑的性能比拟好,一样的测试代码,Bun的QPS能够达到200k,而Node只有20k。从他们的后果来看Bun的性能一下子比Node好了10倍,这真是质的飞跃了。所以在你的电脑上用Bun和Node实现一个简略的HTTP服务的性能差距又有多大呢?你还不放松试一下?

Bun用作包管理器

下面提到了,Bun内置了一个相似于npm的包管理器。应用Bun装置一个包(moment.js)的命令是:

bun install moment

下面的命令和npm install moment是一样的,会装置一个依赖到node_modules文件夹外面,这个命令在笔者电脑跑了2.6s,而后咱们再来跑一下npm:

npm install moment

一样的电脑,npm跑了差不多12s,单纯从装置moment这个包的速度来看,bun的速度是npm的4.6倍。而国外博主个别测出的范畴是4到80倍,这应该是因为不同依赖安装时间不同导致的。总的来说,Bun作为包管理工具来说真的比npm快了不少,至于和yarn以及pnpm比怎么样的话,有待读者本人验证并将后果留在评论区了。

Bun作为React服务端渲染的解决方案

Bun原生反对React的server-side rendering,应用Bun实现一个最简略的SSR是这样的:

import { renderToReadableStream } from "react-dom/server"const dt = new Intl.DateTimeFormat()export default {  port: 3000,  async fetch(request: Request) {    return new Response(      await renderToReadableStream(        <html>          <head>            <title>Hello World</title>          </head>          <body>            <h1>Hello from React!</h1>            <p>The date is {dt.format(new Date())}</p>          </body>        </html>      )    )  },}

拜访localhost:3000能够失去:

不过个别人应该不会这么裸写SSR而是用NextJS这种现代化的框架。Bun也思考到这点,所以它也原生反对Next脚手架。应用上面的命令能够创立一个Bun的Next利用:

bun create next next-app

下面的命令跑了在我电脑跑了20s,而应用npm运行create-next-app跑了10分钟。单纯从这个速度上看Bun是npm的30倍

下面的命令会创立一个监听在3000端口的next利用:

咱们同样尝试应用wrk来测试一下这个Next利用的并发性能:

wrk -t 10 -c256 -d30s http://localhost:3000/

测试过程中能够看到Bun利用在规范输入疯狂输入而后就奔溃了!没错是真的解体了

[603.38ms] / - 3 transpiled, 15 imports[604.97ms] / - 3 transpiled, 15 imports[613.77ms] / - 3 transpiled, 15 imports[618.38ms] / - 3 transpiled, 15 imports[621.86ms] / - 3 transpiled, 15 imports[623.29ms] / - 3 transpiled, 15 imports[648.52ms] / - 3 transpiled, 15 importsSegmentationFault at 0x0000000000000000----- bun meta -----Bun v0.1.10 (fe7180bc) macOS x64 21.6.0DevCommand: fast_refresh hot_module_reloading tsconfig filesystem_router framework public_folder bunfig Elapsed: 87754ms | User: 3341ms | Sys: 662msRSS: 127.34MB | Peak: 127.34MB | Commit: 67.15MB | Faults: 4894----- bun meta -----Ask for #help in https://bun.sh/discord or go to https://bun.sh/issues

具体起因不晓得,不过官网文档也说了当初Bun对Next只是Partial Support,也就是局部反对,咱们也就不深究起因了。接着咱们来看一下一个应用Node跑的Next利用:

而应用create-next-app创立的运行在Node的Next利用是能够通过wrk的测试的,后果如下:

Running 30s test @ http://localhost:3000/  10 threads and 256 connections  Thread Stats   Avg      Stdev     Max   +/- Stdev    Latency   922.74ms  200.38ms   2.00s    78.20%    Req/Sec    30.48     22.34   160.00     80.72%  7635 requests in 30.09s, 31.90MB read  Socket errors: connect 0, read 0, write 0, timeout 97Requests/sec:    253.71Transfer/sec:      1.06MB

Node的Next利用QPS能够达到253。这也得出一个论断就是当初Bun还没有齐全反对Next的性能,并且很不稳固

Bun一些其余的性能

因为文章篇幅无限,这里就不全副列出Bun所有的性能了,总的来说Bun还有上面这些性能值得读者去自行摸索:

  • bun:sqlite: Bun内置的对Sqlite数据库操作,性能同样很好
  • bun:ffi: Bun能够通过ffi调用任何反对C ABI的语言,例如Zig, Rust和C/C++等
  • napi: Bun反对Node 90%的napi

Bun为什么这么快

从下面笔者的理论应用成果来看,Bun确实比Node快了不少,接着就让咱们摸索一下Bun比Node快的一些可能起因。

JavaScriptCore代替V8

这个是Bun官网给出的起因。Bun应用了Apple的JavaScriptCore引擎而不是Google的V8引擎,JavaScriptCore是Safari底层的JS引擎,和V8比起来嵌入老本高一点。至于JavaScriptCore是不是真的比V8性能好的话,这个真的很难判断,我在网上没找到一些不同JS引擎Benchmark的比照数据,反而是一些国外博主理论测试了一下Hermes(Facebook出品),JavaScriptCore和V8,发现性能上来说Hermes 略好于 JavaScriptCore 略好于 v8。不过也就差了一点点,这也和测试代码无关,如果大家能有一些理论的比拟权威的测试数据欢送在留言区评论。

应用Zig语言

这个也是Bun官网给出的起因,咱们先来看一下原话怎么说的:

Zig's low-level control over memory and lack of hidden control flow makes it much simpler to write fast software.

它的意思是Bun快的一个起因是应用了Zig这门对内存有更低层次的管制和暗藏控制流的语言。诚实说要不是因为Bun我都不晓得有这个语言的存在。我Google了一下这门语言,它大略借鉴了Rust的一些思维,目标是想改良C语言,而且公布工夫十分短,最早公布于2016年,当初在TIOBE的语言受欢迎榜单连前50都没有。所以我对这门语言放弃了狐疑的态度

我认为的起因

诚实说官网给出的起因我感觉说服力是不够的。依我看来,可能是上面这些起因导致Bun目前性能好于Node

  • Bun还不成熟,很多工业级的性能都没有,等加上这些性能后是不是还比Node快真不知道
  • Bun的目的性很强,一开始就晓得本人须要提供什么工具解决什么问题,所以它在设计的时候自然而然就会依据具体场景设计一些优化的计划,所以在某些场景下性能会长处,例如包治理等

Bun相比Deno哪里做对了

和之前另外一个Node替代者Deno比起来,我感觉Bun做得对的中央有两点。一个是兼容Node生态而不是新陈代谢。这一点其实很重要,因为从Node 2009年公布而来曾经在很多大公司应用实际了,领有微小的生态系统。Deno一开始压根就没有想着兼容Node,所以它的很多轮子都要从新造,并且现有Node利用迁徙起来十分困难,所以你看看Deno v1也公布了两年多了,该用Node的还在用Node。不过侥幸的是,Deno团队也意识到了这些问题,在1.15后容许你应用std/node来运行局部Node利用。和Deno不同,Bun一开始就将兼容Node放在了第一位,尽管当初还没有做到齐全兼容Node的API,不过这是它始终奋斗的指标,你想一下,如果有一天Bun做到Node利用无缝迁徙,还有Deno什么事吗?

Bun做对的另外一个事是,它解决了一个Node生态的痛点,就是慢,特地是装置依赖和启动我的项目慢。这些是很重要也是很广泛的问题,所以它能够成为一个短缺的理由让Node开发者违心迁徙到成熟的Bun。而相同Deno并没有解决这个问题,而是解决了一些我集体感觉不是那么广泛的问题,所以原来的Node开发者迁徙到Deno能源也就有余了。

Bun可不可以代替Node

Bun和Deno比起来好那么多,那么是不是意味着它能够真正代替Node呢?先说论断,我感觉Bun不会代替Node,最起码将来几年不会

在我看来,Bun存在上面这些问题。

  • Zig语言的局限性
  • All-in-one的架构设计
  • 远不齐备的性能

Zig语言的局限性

下面在说Bun为什么这么快的起因时有提到Zig语言是一门很年老而且并不受欢迎的语言。很年老就意味着这门语言可能还会存在很多没通过实际测验的bug,不那么受欢迎就意味着这门语言的开发者群体少,也就意味着Bun的contributor的潜在开发人员少。这些起因都会妨碍Bun的倒退。

All-in-one的架构设计

在日常工作中,我发现如果一个人什么都想做好的话,往往会导致他最初什么都没做好,究其原因是因为一个人的精力是无限的。

尽管我的项目不同于人,不过也能够进行类比。Bun的指标很远大什么都想做,并且什么都想做好(你不做好他人也不会用)。这里有两个问题,一个是你真的是什么都能够做吗?依照官网的介绍来说Bun当初反对JSX,可是Vue和Angular的Transpile形式是和React不一样的,除了这两个框架还有Svelte等等其余框架而且将来可能还有更多新的框架,你真的能够每一个框架都反对吗?另外一个问题是什么都想做好,后面提到Zig语言的时候就说了,这是一门不那么受欢迎的语言,也就是说很少这方面优良的编程专家,因而Bun前面开发起来预计会很吃力,总不能始终靠这几个人吧?既然开源进来了社区参加才是王道,而Zig语言的局限性就意味着能参加的优良开发会很少,品质自然而然就比拟难保障了。

相同我感觉Bun能够学习Unix的那种方法having small independent tools that do one thing well and still can play together的思维。换句话来说,就是不必什么都要做,而是提供一个相似于bun-core的外围,这个外围是一个JS运行时+包管理器,它保障了Bun飞快的JS运行速度,高效的包治理以及兼容所有的NodeAPI,因为这些才是它的外围。而后bun-core对外提供API反对外界扩大它的性能,这部分扩大的性能由对应的社区开发者开发,这样Bun才不会最初变成一个什么都做不好的硕大无朋。

远不齐备的性能

我感觉Bun临时代替不了Node的最初一个起因是它目前还欠缺了很多外围的性能,所以远远达不到能够在生产环境应用的成果。在介绍Bun的Transpiler性能的时候,你也发现Bun还不反对Minify和Tree-Shaking等罕用的性能,其实不止这样,如果你看一下Bun的RoadMap,你会发现它连百分之20的性能都没开发完,也就是说还须要很长很长的一段时间咱们能力在理论中应用。

总结

这篇文章我为大家介绍了一个全新的JS运行时Bun,它最大的特点就是快,不过我感觉大家当初大略理解这个技术并放弃肯定的关注水平即可,不必那么焦急投入到学习中去,因为它将来的路线不确定性还是很大的,目前来说还代替不了Node

集体技术动静

关注我的公众号 - 进击的大葱获取我的最新技术推送