分享:upm:320 KB 的 npm 亲兄弟,凭什么和 Rust 系包管理器掰手腕?

最近在折腾项目的时候碰到了这个知识点,查了不少资料,索性整理出来分享给大家。

这里写目录标题

五、安全设计:两条默认防线六、横向对比与选型建议七、当前限制(预发布阶段,务必知晓)八、结语参考与延伸阅读
摘要:2026 年 9 月底,unjs 团队发布了包管理器 upm——一个纯 TypeScript 实现的 npm 客户端,磁盘体积仅约 320 KB(pnpm 12 为 59.8 MB),安装速度却宣称与 Rust 系包管理器相当。它默认启用 1 天"最低发布年龄"防供应链攻击、跳过依赖生命周期脚本、兼容 npm/pnpm/bun 三家锁文件,还提供了一套可嵌入任意工具链的 JavaScript API。本文从设计理念、工作原理、上手实战、横向对比到当前限制,带你全面认识这位 npm 家族的新成员。

一、为什么 2026 年还得"又一个"包管理器?

前端圈对包管理器的讨论早已饱和:npm 慢但全能,pnpm 快但功能持续膨胀,Yarn Berry 概念繁琐,Bun/Deno 快却绑定自家运行时。此时再推出一个新工具,听起来像行为艺术。

但 upm 给出的回答相当清醒,它瞄准了两个长期被忽视的痛点:

痛点一:原生二进制太重了。 Rust/Go 编写的包管理器每个版本都要分发完整二进制。在 CI 环境,即便有缓存,仅拉取二进制的开销可能就抵消了安装提速的收益。upm 官方给的对比数字:

工具磁盘占用(工具本身)pnpm 1259.8 MBaube44.5 MBupm约 320 KB(打包后 107 KB)

差距接近 190 倍。而 upm 的核心论点是:Node.js 本身已经足够强大——worker threads(并行)、zlib(解压 tarball)、fetch(网络请求)都是内置能力,原生二进制里的大部分字节,做的事情 Node 早就自带了。

痛点二:包管理器无法被"编程"。 目前所有主流工具的唯一调用方法都是 spawn 子进程,没有任何一个轻量到可以被 bundle 进另一个库。upm 的每个命令同时是一个可导入的 JS 函数,这意味着脚手架、构建工具、云端 IDE 等可以直接以库的形式嵌入安装能力,而不必再包装一层命令行调用。

💡 upm 最初只是一个实验:"纯 TypeScript 的 npm 客户端,把 Node.js 的内置能力用到极致,能做到多快多小?"答案是:相当快,也相当小。

二、upm 是什么:一句话与五张牌

一句话:upm 是 unjs 出品、面向 npm registry 的快速微型包管理器,纯 TypeScript 编写,MIT 协议,目前最新版本 v1.4.0(2026-10-01),仍处于预发布阶段。

它亮出的五张牌:

1. 🪶 小:约 320 KB,可打包嵌入你自己的工具;
2. 🚀 快:安装速度对标 Rust 实现(靠共享东西寻址 store + 硬链接 + worker 线程,而非原生编译);
3. 🎯 零迁移成本:不发明新配置文件,现有 .npmrc 直接生效,命令行高度兼容 npm;
4. 🔒 安全默认值:默认只选发布满 1 天的版本,默认跳过依赖的生命周期脚本;
5. 📦 锁文件兼容:package-lock.json、pnpm-lock.yaml、bun.lock 开箱即读。

三、工作原理:快从哪里来?

upm 的速度不是魔法,而是三个经典思路的组合,整体架构如下:

max-age 内

过期

硬链接

upm install

依赖解析器
worker 线程池

查询 registry 元数据

store/metadata
裁剪后的元数据缓存

registry + ETag/304

~/.upm/store
全局东西寻址存储

项目 node_modules/.upm

依赖可见性
严格隔离 + hoist 回退

1. 全局东西寻址 store + 硬链接。 这条路是 pnpm 验证过的:文件按内容哈希存进 ~/.upm/store(可用 UPM_STORE 改),再硬链接进各项目的 node_modules/.upm。相同文件跨项目只占一份磁盘,重复安装几乎零成本。硬链接不可用时(如跨文件系统)自动回退为复制。

2. 精细的元数据缓存。 registry 文档下载后按"upm 实际读取的字段"裁剪存入 store,配合 ETag/304 和 max-age(npmjs 为 5 分钟)减少网络往返;版本选择时只解析它查看的那些版本,而非整个 packument。

3. worker 线程并行。 解析和链接工作分给 Node 内置的 worker threads(线程池可通过 UPM_RESOLVE_POOL、UPM_LINK_POOL 调节),线程启动失败时优雅降级为单线程。由于 worker 从包内代码启动而非独立文件,upm 才能做到"可被整个 bundle 进应用"。

另外有个贴心设计:安装时会在 node_modules/.upm.lock 留一份锁文件副本,下次 upm install 若发现副本与 package.json 一致,无需 store 和网络即可确认"树已是最新",直接跳过。

四、上手实战:五分钟从 npm 切到 upm

4.1 安装(要求 Node.js 22.3+)

# Windows(PowerShell)
irm https://upm.sh/install.ps1 | iex

# macOS / Linux
curl -fsSL https://upm.sh/install.sh | sh

# 或者直接用 npm 装
npm i -g upm

4.2 迁移与日常命令

# 进入已有项目,先删旧 node_modules(官方强烈建议)
rm -rf node_modules

# 直接安装:upm 能读懂现有 package-lock.json / pnpm-lock.yaml / bun.lock
upm install

# 生成 upm.lock 并提交到仓库,从此由 upm 接管
upm lock

日常操作和 npm 几乎无差异,肌肉记忆可以原样保留:

upm add vue@^3 nanoid        # 添加依赖(等价 npm install xxx -S)
upm add --dev vitest         # 等价 -D
upm add --exact nanoid       # 等价 -E,保存精确版本
upm remove nanoid            # 移除依赖
upm run build                # 跑脚本
upx eslint .                 # 类似 npx:临时安装并执行包命令
upm install --frozen-lockfile  # CI 场景:锁文件缺失/过期直接失败
upm ci --omit=dev            # npm 说法也兼容,等价上一行的生产版

其中 upx(即 upm exec)值得一提:upx cowsay@1.6.0 hello 会把包装进共享 store 再链接执行,每组版本只装一次——可以理解为带缓存的 npx。

4.3 最小可跑示例:用 JS API 把安装能力嵌进自己的工具

这是 upm 区别于所有同行的能力,一段真实可跑的代码:

// 需要在 Node.js 22.3+ 环境运行,先执行 npm i upm
import { add, install, resolve } from "upm";

// 1. 以库的形式安装依赖(等价命令行 upm install --frozen-lockfile)
const result = await install({
  dir: "./my-project",
  frozen: true,
  log: (message, level) => console.error(`[${level}] ${message}`),
});
console.log(`装了 ${result.packages} 个包`);

// 2. 添加依赖到 package.json 并安装
await add(["vue@^3"], { dir: "./my-project", group: "dependencies" });

// 3. 只做版本解析,不安装——甚至有实验性 resolver 可以跑在浏览器里
const [vue] = await resolve(["vue@^3"]);
console.log(vue.version); // 例如 "3.5.13"

更进一步,upm 还暴露了 storeBackend 接口,可以把共享存储接到团队缓存、KV 数据库甚至浏览器 OPFS 上:

import { install } from "upm";

const cache = new Map();
await install({
  storeBackend: {
    get: async (key) => cache.get(key),
    set: async (key, value) => void cache.set(key, value),
  },
});

对做脚手架、云 IDE、依赖分析工具的团队来说,这是"零子进程开销"的安装方案。

五、安全设计:两条默认防线

防线一:最低发布年龄(minimum release age),默认 1 天。 新版本发布后 24 小时内不会被 upm 选中——给社区留出发现投毒包并撤下的时间窗口。配置走标准 .npmrc:

min-release-age=7                      # 提高到 7 天
min-release-age-exclude[]=@acme/*     # 内部包豁免

细节上比较克制:已锁定的版本和精确版本号不受影响;指向过新版本的 latest 标签会自动回退到"不高于它且年龄足够"的最高版本。

防线二:默认跳过依赖的生命周期脚本。 postinstall 脚本是供应链攻击的经典入口(参考历史上多起 npm 事件),upm 安装时一律不执行依赖的 install/postinstall 脚本。代价是依赖构建产物的包(如 node-sass 时代的二进制包)得额外处理——这是安全性与便利性的明确取舍,后文选型部分会再谈。

六、横向对比与选型建议

结合官方特性和社区资料,把 upm 放进现有格局看:

维度npmpnpmYarn BerryBunupm实现语言JavaScriptTypeScript/GoTypeScriptZigTypeScript(纯 JS)工具自身体积随 Node 分发59.8 MB较大大(含运行时)约 320 KB安装速度基准线快快最快之一宣称对标 Rust 系依赖隔离弱(幽灵依赖)严格(默认无回退)严格(PnP)严格严格 + hoist 回退可开关锁文件package-lock.jsonpnpm-lock.yamlyarn.lockbun.lockupm.lock(可读前三者)供应链防护audit(被动)minimumReleaseAge 可配可配可配默认 1 天年龄 + 默认跳过脚本可编程 API无无无无完整 JS API + 浏览器 resolver成熟度极成熟成熟成熟较成熟预发布(v1.4.0)

我的选型建议(中立视角,推荐混合策略):

  • 个人项目 / 尝鲜场景:可以直接试 upm。迁移成本几乎为零(读现有锁文件 + 命令兼容),且安全默认值更符合当前供应链形势。
  • 公司生产项目:建议观望 3–6 个月。upm 发布仅一周余,git 来源受限(仅 GitHub/GitLab/Bitbucket 归档)、无过滤安装、peer 处理有限、无 update 命令,这些对繁琐项目是实打实的坑。
  • 混合方案(我认为最务实的路径):CI 和工具链用 upm,主开发流程维持 pnpm/npm。upm 只有 320 KB,放进 CI 镜像毫无负担;--frozen-lockfile 保证与主流程锁文件语义一致(同一份 upm.lock 或上游锁文件)。等它离开预发布、生态适配跟上,再全面切换不迟。
  • 需要嵌入安装能力的工具作者:upm 目前几乎是唯一解,选它不需要犹豫。

七、当前限制(预发布阶段,务必知晓)

  • 依赖来源仅支持:registry、workspace、tarball、link: 目录、GitHub/GitLab/Bitbucket 归档(不运行 git,无 prepare 脚本,私有仓库不可用);
  • monorepo 全树安装,没有过滤安装和 catalogs;
  • peer dependency 处理有限:一个包在树中只有一份拷贝;
  • 没有 update 命令(用 add 指定新版本代替)、没有 upm login/upm config set、不支持代理等其余 npm 配置;
  • 锁文件为所有平台保留可选构建,安装器在部分平台尚未完全测试。

八、结语

upm 最有价值的地方也许不是"又快又小"本身,而是它证明了一个被长期忽略的事实:在 2026 年,用 Node.js 自己的内置能力,写一个纯 TS 的包管理器,性能就能追上原生实现。它像 pnpm 一样共享 store,像 npm 一样零配置兼容,又独有可编程 API 和激进的安全默认值——这位 npm 的"亲兄弟"值得放进观察名单。

对普通开发者,我的建议是:把它装进 CI 试试,把主项目留给时间验证。等 upm 从预发布走向稳定,这个 320 KB 的小家伙,可能会成为 Node 工具链里被引用最多的包之一。


参考与延伸阅读

1. upm 官方仓库(unjs/upm):https://github.com/unjs/upm
2. upm 官网:https://upm.sh
3. npm 包页:https://www.npmjs.com/package/upm
4. unjs 生态:https://unjs.io

AI 辅助创作声明:本文在 AI 辅助下做好资料整理与初稿撰写,技术细节基于 upm 官方 README(v1.4.0)核实。文中体积、性能数据为官方基准测试结果(经验值),未含博主独立实测,请以实际环境验证为准。


以上就是这次整理的全部内容,希望对你有所启发。如果有不同见解,欢迎在评论区交流讨论。

评论 (0)

暂无评论