Bun技术评估 - 16 Package Manager

概述

本文是笔者的系列博文 《Bun技术评估》 中的第十六篇。

在本文的内容中,笔者主要想要来探讨一下Bun中的包管理(Package Manager, PKM)相关的功能和特性。

在 Node.js 生态中,Package Manager(包管理器)是一个用于安装、管理、升级和卸载第三方模块(package)的工具。它简化了依赖管理,让开发者可以方便地复用他人编写的代码库。

本文的主要内容,重点参考了Bun官方技术文件中的Package Manager板块:

Bun PKM

我们应该都非常熟悉,在Nodejs中, PKM是通过一个独立的工具:npm,来完成的。Bun本身的设计诉求就是一个集成化的开发工具,所以它内置了相关包管理的功能和特性,不再需要外部的包管理工具。我们可以简单的理解未bun内置和实现了npm的相关功能。

其实npm本身也是随着Nodejs的正规安装过程一起安装的,也不需要额外的配置,但毕竟是一个外部程序,而且需要一些升级和维护的工作,使用起来并不是完全的方便。此外,nodejs社区对npm的设计也有一些不满的地方,觉得它的实现机制过于复杂,包管理的结构和不甚合理,就导致依赖关系过于复杂,依赖包的容量也非常大(甚至有人画了一张图来进行讽刺,下图)。这也是nodejs生态中一个比较突出和亟待改善的问题,因此也出现了如yarn,nkx等竞争性的技术。

即便如此,由于长时间的发展和应用,npm本身也成为了nodejs最重要的生态组成,同样是一笔非常巨大的财富。Bun作为后来者,也不可能完全脱离这个生态。所以bun试图通过内置包管理命令和精心的设计,来规避npm本身的一些问题,现在看起来,效果还是比较不错的。笔者简单的总结其一些特点如下:

  • 简洁,本身bun就内置了很多Web应用开发所需的基础功能,相对而言它比一般nodejs项目的依赖就可以少很多
  • 简单,bun的命令选项和清晰程度比npm较好
  • 快!Bun安装配置npm包的性能明显高于原生npm,根据其文档的说法,要快25倍(笔者当然觉得夸大了)

在实际使用中,bun在包管理工作方面的使用,和npm也差异不大,基本上可以平替。我们下面就可以按照类似的流程,来了解一下这些工作是如何操作的。

Init(初始化)

一般情况下,所谓的依赖包管理,都是针对开发项目而言的。所以在一切开始之前,需要进行项目创建和初始化的过程。如果要将一个普通的文件夹,转换成为nodejs应用程序的项目文件夹,我们都知道需要使用npm init指令,即所谓项目初始化。类似的,bun也可以使用init指令来初始化一个bun程序项目。这部分的详细操作和项目相关内容,笔者已经在系列文章中的前一篇:

《Bun技术评估 - 15 Project》

讨论过了,这里不再赘述.

Install(安装)

这个操作,一般在项目移植,或者部署过程中使用。也就是说,已经有了一个相对完整的package.json配置。bun install 命令的意思,就是基于当前项目的package配置,解析其中的内容,下载、安装和配置项目相关的依赖软件包,其基本目的是基于一个预配置好的信息,简化项目配置和准备的工作,简化运维。其具体过程包括:

  • 安装依赖包

默认包括的部分包括dependencies、devDependencies和optionalDependencies,还会安装peerDependencies。

  • 运行项目的{pre|post}install 和 {pre|post}prepare 脚本文件

  • 写入 bun.lock文件

有一些选项,可以控制安装操作的过程,包括:

  • --verbose 显示详细信息
  • --silent 静默安装
  • --dry-run 模拟安装
  • --production 生产模式,不按照devDependencies
  • --frozen-lockfile, 使用锁定文件安装
  • --omit dev|peer|optional,忽略依赖组
  • --filter, 过滤安装
  • --global, 安装全局包

bun有一个和npm不同的设置,就是基于安全的考虑,bun不会自动运行软件包的前后安装脚本,除非将其配置成为"可信任包(trustedDependencies)"。

除了一般使用命令参数来控制安装过程,开发者还可以使用项目配置文件bunfig.toml来配置和简化安全执行过程。关于这个配置文件的格式和使用,系列文章中有专门的篇幅讨论。

除了普通的npm之外,bun支持基于github、git、本地和远程压缩包的安装,例如:

package.json 复制代码
{
  "dependencies": {
    "dayjs": "git+https://github.com/iamkun/dayjs.git",
    "lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21",
    "moment": "git@github.com:moment/moment.git",
    "zod": "github:colinhacks/zod",
    "react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
    "bun-types": "npm:@types/bun"
  }
}

Add/Remove/Update (增加/移除/更新)

一个完整的依赖包的管理生命周期,一般的应当包括依赖包的增加、删除和更新。这些操作,一般在项目开发过程中进行。

  • 增加 Add

为当前项目,增加一个依赖包,非常简单,使用add指令(npm 通常使用install xxx)。而且针对不同的场景和需要,可以有一些选项和配置:

js 复制代码
// 添加包
bun add docxtemplate

// 指定版本或后续版本
bun add zod@3.20.0 -- 特定版本 
bun add zod@^3.0.0 -- 大版本
bun add zod@latest

// 添加特定版本
bun add docxtemplater@3.37.0
bun add docxtemplater@lastest

// 确切版本
bun add react --exact
bun add react -E

// 开发依赖
bun add --dev @types/react
bun add -d @types/react

// 对等版本 peer
bun add --peer @types/react

// 全局安装
bun add --global cowsay 

bun add --global cowsay
bun add v1.2.17 (282dda62)

installed cowsay@1.6.0 with binaries:
 - cowsay
 - cowthink

33 packages installed [12.24s]

warn: To run "cowsay", add the global bin folder to $PATH:

export PATH="/home/yanjh/.bun/bin:$PATH"

简单说明一下笔者的理解。

默认情况下,bun会按照当前最新的版本进行安装。并将它们作为普通的依赖模式(dependencies),就是标准的运行时依赖。但我们知道,在开发的时候,可能会使用生产环境用不到的工具或者依赖,比如测试工具、构建工具、代码检查、转义和本地开发服务器等等,就应当将这些依赖作为"开发依赖"(devDependencies)来安装。而这些东西在生产环境部署时,其实是不需要的,所以需要区分开来。

Bun还支持一种"对等依赖"(peerDependencies),这个意思是当前的项目可能要依赖这个包,但不会安装包。这个模式通常用于开发包(而非最终应用)的时候使用,然后在引用这个包的项目中进行安装,以确保多个包之间共享同一个依赖版本,避免冲突。

和nmm类似,bun使用相同的"语义化"版本规范。一个典型的版本号格式是:MAJOR.MINOR.PATCH,即包括了主版本号,次版本号和补丁版本。一般认为主版本是比较大的变更,不兼容旧版本;次版本通常用于增加功能,会在主版本号之内向前兼容;而补丁版本用于修复buf,同样在次版本号内向前兼容。

基于这个基本设定和机制,bun基本提供了几种应用的模式。默认情况下bun会基于仓库中的最新版本来进行安装,并且将其设置为"可更新"(在package.json中使用^来进行标识,在主版本号内升级),即在部署时,可能安装更新的小版本;如果开发者希望能够安装特定的版本,可能需要使用--exact标记。

在一般情况下,基于当前安装的版本,开发者就可以展开开发工作了。但有些时候,开发者可能需要调整依赖包的使用,例如需要更新依赖包的版本,或者由于修改了技术方案,要删除某个依赖包等等。bun提供了update和remove指令来帮助完成这些工作。

  • 更新 update
js 复制代码
// 更新依赖
bun update pizzip
bun update v1.2.14 (6a363a38)

installed pizzip@3.2.0

[4.99s] done

更新后会提示到更新到的版本和所花费的时间。

  • 移除 remove

移除依赖的操作,也非常简单:

js 复制代码
// 移除依赖
bun remove jszip
bun remove v1.2.14 (6a363a38)

1 package installed [99.00ms]
Removed: 4

移除操作后,会提示移除涉及到的软件包的数量。由于Bun会使用包缓存机制,所以这个删除会将项目中的链接和信息删除,并不会真正的删除缓存内容。下次如果按照相同版本的包,是可以无需再次下载,快速恢复的。

Auto-Install 自动安装

前面我们看到的是bun应用PKM的一般过程,但实际上,bun能够提供更加灵活方便的应用方式,就是自动安装。在bun项目中,可以在没有做任何项目配置和依赖包的安装操作之前,直接执行需要依赖的代码,例如下面这个示例代码:

js 复制代码
import { foo } from "foo"; 

// install `latest` version 导入 { foo } ; // 安装 `latest` 版本

// 执行方法
foo();


// 指定确切版本
import { z } from "zod@3.0.0"; // specific version
import { z } from "zod@next"; // npm tag
import { z } from "zod@^3.20.0"; // semver range

熟悉nodejs的开发者会知道,如果直接执行这段代码,大概率会报一个"foo模块未找到"的错误。但在bun环境中,如果在工作目录或更高位置找不到node_modules目录和软件包,Bun将放弃Node.js风格的模块解析, 转而使用Bun模块解析算法。然后,bun会尝试将这个依赖包安装到全局缓存模块中,并且自动安装到当前项目当中。所以,这个代码使用bun其实是可以直接执行的(可能稍有延迟而已)。

它的简单工作过程和原理如下:

  • 正常的依赖包,就使用当前的package.json配置和node_modules文件夹
  • 如果都没有找到,则在全局缓存中进行查找,如果匹配,则使用这个版本
  • 如果仍然没有找到,bun会自动安装包的lastest版本,到全局缓存,并使用这个版本工作

关于要安装和使用那个版本,遵循以下规则:

  • 检查bun.lock的设置
  • 检查packag.json和依赖包相关的版本
  • 使用lastest版本

笔者觉得,这个特性应该就很接近deno提出来的在线依赖包引用和管理模式了(当然Bun还不支持从URL直接引用),它可以大大简化特别是在开发阶段的环境管理工作。这方面有很多优势:

  • 空间效率:使用全局缓存,只实际存储一份代码
  • 可移植性:项目原始代码是自描述的,方便移植和执行
  • 方便: 无需额外的配置和准备工作
  • 向后兼容: 可以总是使用比较新的版本

PM包管理

bun提供了pm命令集合,来进行相关的包管理, 可以使用bun pm来显示完整的帮助信息如下:

js 复制代码
bun pm 
Usage: bun pm [flags] [<command>]
  Run package manager utilities

Examples:

  bun pm pack               create a tarball of the current workspace
  ├ --dry-run               do everything except for writing the tarball to disk
  ├ --destination           the directory the tarball will be saved in
  ├ --filename              the name of the tarball
  ├ --ignore-scripts        don't run pre/postpack and prepare scripts
  └ --gzip-level            specify a custom compression level for gzip (0-9, default is 9)
  bun pm bin                print the path to bin folder
  └ -g                      print the global path to bin folder
  bun pm ls                 list the dependency tree according to the current lockfile
  └ --all                   list the entire dependency tree according to the current lockfile
  bun pm whoami             print the current npm username
  bun pm hash               generate & print the hash of the current lockfile
  bun pm hash-string        print the string used to hash the lockfile
  bun pm hash-print         print the hash stored in the current lockfile
  bun pm cache              print the path to the cache folder
  bun pm cache rm           clear the cache
  bun pm migrate            migrate another package manager's lockfile without installing anything
  bun pm untrusted          print current untrusted dependencies with scripts
  bun pm trust names ...    run scripts for untrusted dependencies and add to `trustedDependencies`
  └  --all                  trust all untrusted dependencies
  bun pm default-trusted    print the default trusted dependencies list

Learn more about these at https://bun.sh/docs/cli/pm

笔者对于npm包管理的工作接触的不多。所以相关的很多操作和功能也不是特别了解。但是从帮助信息的内容我们可以看到,bun提供的包管理功能,特别是npm服务相关的包发布和使用相关的内容还是比较丰富和完善的。有兴趣的读者可以自行了解。

这里只是从日常开发和运维的角度讨论经常使用的一些包管理的工作和操作指令如下:

  • ls

列表当前项目的依赖包。--all可以显示更深度的依赖包。

js 复制代码
bun pm ls
/path/to/project node_modules (135)
├── eslint@8.38.0
├── react@18.2.0
├── react-dom@18.2.0
├── typescript@5.0.4
└── zod@3.21.4
  • pack 打包

此命令用于将当前项目打包成为压缩文件,方便移植和部署,这个特性在很多离线不方便在线安装的场景中,非常实用。

js 复制代码
bun pm pack
# Creates my-package-1.0.0.tgz in current directory

这个命令还有一些选项,可以指定目标文件名,压缩级别,输出信息,模拟执行等等。

  • bin

显示当前项目bin路径。-g 显示全局bin路径。

  • whoami

显示PM发布所使用的用户名。

  • hash

打印当前锁定文件的hash(标识)。

  • cache

显示当前全局cache位置。

  • migrate

迁移锁文件。

  • trushed / untrusted / default-trusted

设置信任。显示当前未信任包。打印默认信任包名。

  • pkg

管理package.json操作。

shell 复制代码
# set
bun pm pkg get name                               # single property
bun pm pkg get name version                       # multiple properties
bun pm pkg get                                    # entire package.json
bun pm pkg get scripts.build                      # nested property

# set
bun pm pkg set name="my-package"                  # simple property
bun pm pkg set scripts.test="jest" version=2.0.0  # multiple properties
bun pm pkg set {"private":"true"} --json          # JSON values with --json flag

# delete
bun pm pkg delete description                     # single property
bun pm pkg delete scripts.test contributors[0]    # multiple/nested

# fix
bun pm pkg fix                                    # auto-fix common issues

在一些本地开发的场景中,经常可能需要共享使用一些自己开发的库或者软件包。如果开发者不想将其发布出去,又需要在多个项目中共享,可以考虑使用"链接 Link"的操作方式。

这些共享包,其实也是普通的bun程序项目。当开发完成或者过程中,开发者可以选择将其设置成为"可链接"的,这个操作就会将当前的项目,注册成为一个可以引用的包,然后在其他的项目中进行引用。示例操作如下:

js 复制代码
// 库包项目
cd liba

// package内容
bun init -y 
cat package.json
{
  "name": "liba",
  "version": "1.0.0"
}

// link 操作
PS C:\Work\Dev\liba> bun link
bun link v1.2.18 (0d4089ea)
Success! Registered "liba"

To use liba in a project, run:
  bun link liba

Or add it in dependencies in your package.json file:
  "liba": "link:liba"


// 在另一个项目中进行连接
cd bproject
PS C:\Work\Dev\bproject> bun link liba --save
bun link v1.2.18 (0d4089ea)

installed liba@link:liba

1 package installed [82.00ms]

// 项目package文件内容
Or add it in dependencies in your package.json file:
"liba": "link:liba", 

// 引用方式类似
const { func1, func2 } = require("liba");

如果在项目中,链接已经注册的项目,将对在当前项目中产生两个影响:

  • 将会在node_modules文件夹中,会增加一个指向链接项目的项目(应该是快捷或者链接方式)
  • 如果使用--继续ve标签,将会在当前项目package.json中增加相关链接条目,类似于:"cool-pkg": "link:cool-pkg"

Publish 发布

前面已经提过,bun本质也是npm生态的一部分,所以它也具有将开发者的项目,发布到私有或者公共npm仓库中的选项。所以,开发者可以使用publish指令,将自己当前的bun项目,发布到指定的npm仓库当中:

js 复制代码
## Publishing the package from the current working directory
bun publish

## Output
bun publish v1.2.18 (ca7428e9)

packed 203B package.json
packed 224B README.md
packed 30B index.ts
packed 0.64KB tsconfig.json

Total files: 4
Shasum: 79e2b4377b63f4de38dc7ea6e5e9dbee08311a69
Integrity: sha512-6QSNlDdSwyG/+[...]X6wXHriDWr6fA==
Unpacked size: 1.1KB
Packed size: 0.76KB
Tag: latest
Access: default
Registry: http://localhost:4873/

 + publish-1@1.0.0
 

由于笔者在日常工作中,相关软件发布的操作涉及的非常少,相信很多行业类应用开发的技术团队也是类似的情况,软件的分发和部署,主要是通过git的方式,这里对于发布操作的细节就不再展开讨论了,读者有兴趣或者需求,可以自己查阅相关技术文档。

Overrides/Resulutions 覆盖/解析

其实两个是同一个概念和操作。只不过Overrides是npm的说法,Resulutions是Yarn的说法,但两者在Bun中都是支持的(迁移方便)。我们后面的讨论只使用Overrides。

Overrides是一种依赖和其依赖管理的一种处理机制,它可以为一个软件包指定其依赖关系的版本范围,其实就是"元依赖"。以下面的代码为例:

package.json 复制代码
{
  "name": "my-app",
  "dependencies": {
    "foo": "^2.0.0"
  },
  "overrides": {
    "bar": "~4.4.0"
  }
}

默认情况下(如果没有overrides),项目安装foo包时,它会安装最新的版本进行安装,并且递归安装其依赖,比如bar@4.5.6。这个信息,正常情况下,是不会体现的package.json文件中的。但是,如果开发中由于一些安全性或者脆弱性的考虑,需要配置foo的依赖固定为bar@4.4.0,就可以使用overides配置信息来达成这个目的。

resulutions的情况是类似的:

package.json 复制代码
"resolutions": {
    "bar": "~4.4.0"
}

Patch 补丁

开发经常会遇到需要临时对库代码进行一些小的修复(通常是Bug修复,或者增加一个小功能)的情况,这时没有条件和时间来走一个完整的修改-发布-更新的流程。这种修补操作也常被称为Patch(补丁)。补丁无需向代码提供整个包,并且可以在多个安装和项目上重用,提供了很大的灵活性。

在bun包管理体系中,补丁的功能通过patch子命令来提供,它允许开发者以可维护的、类似于Git工作方式的形式,持续修补node_modules。但是,笔者在工作流程中,很少使用这一特性,确实没有太大的经验,就只能结合自己的理解简单说明它的基础原理和过程:

  • bun可以基于现有安装的模块和依赖,生成.patch文件
  • .patch文件,可以提交到依赖库,并在多个安装、项目和主机上重用
  • package.json文件中的"patchedDependencies"项目,可以跟踪修补的软件包
  • bun补丁可以修补 node_modules/ 中的依赖,同时保证Bun全局缓存完整性
  • 应当在bun补丁提交之前进行测试,并使用 --commit 进行提交
  • 补丁可以提交到本地全局缓存

它的基本工作流程如下:

  • 1 为依赖包准备补丁

可以执行patch子命令,来为特定的软件包来准备补丁:

js 复制代码
# you can supply the package name

bun patch react


# ...and a precise version in case multiple versions are installed

bun patch react@17.0.2


# or the path to the package

bun patch node_modules/react

这一步非常必要,因为它会确保在项目node_modules文件夹中包含原始新鲜的依赖软件包代码。而且相关的修改都在本地副本中执行,不会影响到全局缓存。

  • 2 本地修改和测试

然后,开发者就可以在本地的依赖包原代码中进行修改,并进行测试了。

  • 3 提交修改

测试完成之后,就可以使用 --commit 选项,应用并且提交补丁:

js 复制代码
# you can supply the path to the patched package

bun patch --commit node_modules/react


# ... or the package name and optionally the version

bun patch --commit react@17.0.2


# choose the directory to store the patch files

bun patch --commit react --patches-dir=mypatches


# `patch-commit` is available for compatibility with pnpm

bun patch-commit react

提交后,Bun会在patches/ 文件夹中生成补丁文件,同时更新package.json和锁文件。并且使用修补后的代码进行工作。

Audit 审计

随着npm生态的逐渐庞大和复杂,软件包的安全问题也越来越突显出来了。为此npm引入了软件包的审计机制。Bun也支持这一机制。它的使用非常简单,就是在当前项目中,使用audit指令:

shell 复制代码
// 开始审计
bun audit

// 审计通过,显示未发现漏洞
No vulnerabilities found

// 发现漏洞,显示相关信息
3 vulnerabilities (1 high, 2 moderate)
To update all dependencies to the latest compatible versions:
  bun update
To update all dependencies to the latest versions (including breaking changes):
  bun update --latest

Bun将当前项目中已安装的软件包和版本列表发送到NPM,并打印发现的任何漏洞的报告(npm社区维护各个软件包可能的漏洞信息)。当然,这个审计操作,只支持从默认仓库安装的项目。使用--json可以将审计结果输出成为json文件。

一般情况下的后续操作,就是升级软件包。

Bun.lock

和nodejs类似,Bun也使用锁文件,来管理和确保项目依赖。当然,它不使用npm常用的package-lock.json文件,而是使用自己的锁文件(.lock或者.lockb)。

使用锁文件的基本原理是,虽然package.json 里声明了依赖,但它可能写的是范围(如 "^1.2.3"),这样可能导致不同时间安装时得到不同的具体版本。而锁文件则精确记录了具体的依赖版本、来源、校验哈希、以及依赖的依赖(transitive dependencies)。这个机制,可以确保团队或项目中的每个人在安装依赖时,都使用相同的依赖版本,以避免"在我机器上没问题"的问题。

开始的时候,Bun使用的是一个二进制格式的锁文件:bun.lockb。这个设定的好处就是性能,bun install的速度比npm快很多。但Bun开发团队却发现,使用二进制锁文件会带来了很多"细节上的烦恼",特别是开发者无法在GitHub或其他平台上查看锁文件的内容,这样就很难信任一个来自外部的设置修改,还有就是二进制文件几乎无法处理版本管理中常见的内容合并操作,这样就无法使用现实中常见的分布式开发和协助流程。

为此,在版本1.2中,开发社区决定引入基于文本的锁文件:bun.lock,并且将此作为默认格式。下面是一个简单的示例(在笔者环境中默认自动生成的内容):

bun.lock 复制代码
{
  "lockfileVersion": 1,
  "workspaces": {
    "": {
      "name": "btest2",
      "devDependencies": {
        "@types/bun": "latest",
      },
      "peerDependencies": {
        "typescript": "^5",
      },
    },
  },
  "packages": {
    "@types/bun": ["@types/bun@1.2.18", "https://registry.npmmirror.com/@types/bun/-/bun-1.2.18.tgz", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],

    "@types/node": ["@types/node@24.0.12", "https://registry.npmmirror.com/@types/node/-/node-24.0.12.tgz", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="],

    "@types/react": ["@types/react@19.1.8", "https://registry.npmmirror.com/@types/react/-/react-19.1.8.tgz", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="],

    "bun-types": ["bun-types@1.2.18", "https://registry.npmmirror.com/bun-types/-/bun-types-1.2.18.tgz", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],

    "csstype": ["csstype@3.1.3", "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],

    "typescript": ["typescript@5.8.3", "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],

    "undici-types": ["undici-types@7.8.0", "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
  }
}

而且,为了提升实用性和兼容性,bun.lock使用改进后的JSONC格式,它基于JSON格式,但可以带有注释信息和尾逗号支持。

为了和原有的lockb文件保持延续性,开发者也可以选择持续使用原有的lockb文件,也可以使用--save-text-lockfile 参数来迁移到新的锁文件格式:

bun install --save-text-lockfile

Registries 代码仓库

Bun默认使用npm官方的代码仓库,即 registry.npmjs.org。可以通过配置全局配置bunfig.toml来进行修改:

js 复制代码
[install]
# set default registry as a string
registry = "https://registry.npmjs.org"
# set a token
registry = { url = "https://registry.npmjs.org", token = "123456" }
# set a username/password
registry = "https://username:password@registry.npmjs.org"

甚至可以为特别的组织,来指定特别的仓库,这个特性通常用于企业私有仓库,还可以使用密码保护:

js 复制代码
[install.scopes]
# registry as string
"@myorg1" = "https://username:password@registry.myorg.com/"

# registry with username/password
# you can reference environment variables
"@myorg2" = { username = "myusername", password = "$NPM_PASS", url = "https://registry.myorg.com/" }

# registry with token
"@myorg3" = { token = "$npm_token", url = "https://registry.myorg.com/" }

Bun在1.2版本之后,还支持了.npmrc这种npm配置文件的读取。这些文件用于配置开发者自己的npm仓库,类似私有仓库:

.npmrc 复制代码
@my-company:registry=https://packages.my-company.com
@my-org:registry=https://packages.my-company.com/my-org

Bun将会在用户主文件夹和项目主文件夹中,查找并处理.npmrc文件。

bunx

在nodejs生态中,有一个命令行工具,可以用来临时运行任意依赖包中的CLI工具或脚本命令,就是npx。这个特性在很多场景中非常有用,因为很多依赖包并不是简单的代码库,还有很多工具的功能,npx就提供了一致化的,使用这些工具的方式。

bun也提供了类似的功能,在其中这一特性被称为Package Runner(包执行器),提供的命令行为bunx。等等? 前面不是已经提过,bun是一个单体程序啊。如果要直接执行bunx命令,可能会找不到可以执行的命令的。确实是这样的bunx其实可以使用 bun x 指令执行,当然完整的安装包可以创建一个bunx的快捷方式,来更好的模拟npx的行为,实际上是一样的。

js 复制代码
// bunx 执行
bunx cowsay "Welcome"
bunx: The term 'bunx' is not recognized as a name of a cmdlet, function, 
script file, or executable program.Check the spelling of the name, 
or if a path was included, verify that the path is correct and try again.

// bun x 执行
bun x cowsay "Welcome"
 _________
< Welcome >
 ---------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

实际上,bunx的运行原理,并没有看上去的那么简单。一般需要在npm依赖包中进行相关的配置,来声明这个npm是可以被执行的。下面是一个简单的示例:

js 复制代码
// npm package.json 文件
{
  // ... other fields
  "name": "my-cli",
  "bin": {
    "my-cli": "dist/index.js"
  }
}

// dist/index.ts 内容,需要指定执行环境
#!/usr/bin/env node

console.log("Hello world!");


// 执行npm, 指定包名
bun x my-cli

性能

如果开发者有使用npm install的经验,当使用bun install时,确实可以明显感觉到安装配置性能的提升。

理论上,bun也使用和npm一样的软件仓库,所以也使用相同的仓库设置。所以可能也需要使用相同的镜像设置,来加速软件包的下载:

~/.npmrc 复制代码
registry=https://registry.npmmirror.com

但笔者觉得,bun install性能更好的原因,应该是和其使用全局缓存(Global Cache)的关系更大。当进行下载安装的时候,下载的软件包,首先存储在一个预设好的文件夹下(一般为 ~/.bun/install/cache)。然后有安装检查软件包是否已经下载,并将其"快速复制"到项目文件夹中。注意,这里的快速复制,可能是系统级的,特别是在Windows、Linux或者MacOS系统上,它可能会使用链接的方式。这样的好处是,可以共享下载的缓存内容,安装配置速度更快,同时大幅度节省磁盘空间。当然,这个选项,也是可以配置的,如果开发者遇到有缓存产生的问题,也可以通过禁用缓存来简化环境配置。

小结

本文讨论了在bun开发体系中,有关于依赖包管理(Package Manager)方面的内容。包括为项目添加、更新和删除包,按照配置信息来安装包,自动安装,链接,发布,包锁定和PM命令等。

相关推荐
paopaokaka_luck1 小时前
基于SpringBoot+Uniapp的健身饮食小程序(协同过滤算法、地图组件)
前端·javascript·vue.js·spring boot·后端·小程序·uni-app
Villiam_AY1 小时前
Redis 缓存机制详解:原理、问题与最佳实践
开发语言·redis·后端
魔尔助理顾问5 小时前
系统整理Python的循环语句和常用方法
开发语言·后端·python
Ares-Wang5 小时前
JavaScript》》JS》 Var、Let、Const 大总结
开发语言·前端·javascript
程序视点5 小时前
Java BigDecimal详解:小数精确计算、使用方法与常见问题解决方案
java·后端
你的人类朋友5 小时前
❤️‍🔥微服务的拆分策略
后端·微服务·架构
SY_FC6 小时前
uniapp input 聚焦时键盘弹起滚动到对应的部分
javascript·vue.js·elementui
AI小智7 小时前
后端变全栈,终于可以给大家推出我的LangChain学习小站了!
后端
lkf197117 小时前
商品中心—1.B端建品和C端缓存
开发语言·后端·缓存
渣渣盟8 小时前
JavaScript核心概念全解析
开发语言·javascript·es6