一个软件包可以在维护者宣布弃用之后,每月继续被下载数千万次。另一个软件包没有停止维护,却可能已经不是新项目完成同一件事所必需的选择。如果只看 npm 的下载数字,这两种变化都不容易辨认。

哪些 Library/Utility 正在退场,哪些值得留下?这个问题值得调查,但“过时软件包排行榜”回答不了它。真正要追问的是:当初让我们安装这个包的理由,现在还存在吗?如果存在,它正在替谁承担什么?

我的判断是,最容易失去必要性的,是替平台补缺口、却没有持续承担额外复杂度的工具。这不等于小型软件包都该删掉,也不等于原生 API 永远较好。以下以截至 2026 年 10 月 8 日的官方文档、维护声明与 npm 数据,调查五种替代压力,并把已证实的状态和我的推论分开。

下载量告诉我们流量,没有直接告诉我们选择

Request 的 README 明示自 2020 年 2 月 11 日起弃用;inflight 的包元数据则明示不再支持,并警告内存泄漏。Moment 的情况不同:它仍在维护,只是不以扩张新功能为目标。这三者不能统称为“死掉的软件包”。Request 声明、inflight 包元数据、Moment 最新政策

我查询 npm 官方 Downloads API,以 2026 年 9 月整个日历月作为共同期间。这是特意挑选来检验“维护状态是否等于下载消失”的三个案例,不是随机抽样,也不是生态占有率。

弃用或维护模式,仍能与大量下载并存

npm Downloads API · 2026-09-01–2026-09-30

三个案例在 2026 年 9 月仍有大量下载;长条不能用来判定新项目的采用意愿。

百万次下载(约)/2026 年 9 月
  • request(已弃用)59.699
  • moment(维护模式)149.03
  • inflight(已弃用)392.617
查看 npm 下载量数据表
弃用或维护模式,仍能与大量下载并存 · 百万次下载(约)/2026 年 9 月
包与维护状态百万次下载(约)/2026 年 9 月
request(已弃用)59.699
moment(维护模式)149.03
inflight(已弃用)392.617

npm 官方 API,2026-09-01 至 2026-09-30,UTC 日期、首尾皆含。数值以百万次为单位,四舍五入至小数点后三位;精确整数见来源。这是单月快照,不能证明增长或衰退。下载次数不是用户或项目数,无法区分新增采用、间接依赖与自动化安装,也无法量化各自占比。

数据源

API 返回的是指定期间的下载总数,没有提供“这次是否由工程师主动选用”的字段。假设某个旧工具仍间接依赖 Request,构建环境在没有缓存时重新下载它,就不需要有人再次认可 Request 的设计。这是可能产生下载的机制示例;本次数据无法判定有多少下载来自这种情况。npm 统计接口与日期定义

因此,调查退场至少要拆开四件事:功能是否还稀缺、维护者还承诺什么、新项目是否选它、既有系统是否搬得走。官方弃用声明能回答第二件事,API 对照能帮助回答第一件事;单月下载总数无法替后两件事作答。

这也是本文的界线:有充分证据谈某些用途正在失去必要性,却没有足够数据宣布这五类工具的新增采用率都在下降。

第一种:替运行环境补上标准能力的兼容层

node-fetch 的原始价值很清楚:把 Fetch API 带进 Node.js。当运行环境本身提供稳定的 fetch,只为发出基本 HTTP 请求而额外安装它,理由就变弱了。Node.js 文档记录 fetch 自 v21 起不再是实验功能;本文以 Node 24 的文档为能力基准。Node.js Fetch

这是可以确认的平台吸收,而不是 node-fetch 使用量下降的证明。更不是把 import 删掉就一定完成迁移。node-fetch 的 response body 使用 Node stream,原生 Fetch 使用 Web Stream;包的 agent 设置与 Node 原生 Fetch 的 dispatcher 也不是同一个接口。流的读取方式、代理连接与取消行为,都要按实际用法比对。node-fetch 文档

HTTP client 的价值还可能在更上层。应用需要统一的错误分类、重试策略、认证信息或可观测性记录时,有共用封装仍很合理。原生 fetch 不会把所有 HTTP 错误状态都自动变成被拒绝的 Promise;你的应用仍须决定如何处理响应。重试写入请求,也不能只加一个循环而不考虑重复运行。Fetch 标准

还有一个容易忽略的转变:Node 的 Fetch 实现以 Undici 为基础。你少安装一个软件包,底层维护工作未必消失;它可能只是被运行环境接手。衰弱的是一个独立安装的理由,不一定是原来那份工程知识的价值。

第二种:把日常语法包得比较方便的工具箱

数组查找、迭代、去除重复值,曾经是通用工具箱的重要入口。今天,Array.prototype.find、map 与 Set 已是语言的一部分。若项目只需要对简单字符串数组去重,为它引入额外函数库的必要性自然降低。ECMAScript 集合与数组规范

但“有近似写法”与“语义等价”之间,经常隔着生产环境才会遇到的数据。以下是刻意设计的反例:

const settings = { retries: null };
 
// _.get 的默认值只在结果是 undefined 时使用。
_.get(settings, 'retries', 3); // null
 
// ?? 同时把 null 和 undefined 视为需要默认值。
settings?.retries ?? 3; // 3

若 null 在系统里表示“明确停用”,机械替换就改变了业务规则。Lodash 的 get 文档界定了前者的契约;检查工具函数的第一步,应是找出调用方依赖的行为,而不是比较字符数。Lodash get

同样地,structuredClone 支持许多数据类型与循环引用,却不是任意 JavaScript 对象的透明复制:函数、DOM 节点以及原型、属性描述符的处理都有边界。把所有 cloneDeep 一律换掉,不是完整的迁移策略。结构化克隆算法

防抖也不只是“晚一点运行”。Lodash 的 debounce 包含 leading、trailing、maxWait、cancel 与 flush 等选择;如果项目依赖其中的交互语义,十行自制版本未必更便宜。Lodash debounce

所以正在承受压力的是工具箱里与原生能力重叠的使用面,不是“整个 Lodash 已无价值”。同一个 library 可能同时包含容易替代的便利函数与值得保留的行为契约。替换单位应该是用途,不必一开始就追求整包清空。

第三种:补足 Node.js 基础文件操作的工具

创建多层目录、复制目录、依模式寻找文件,过去常需要额外工具。Node 24 已提供递归 mkdir、fs.cp 与 fs.glob;其中 cp 自 v22.3 不再是实验 API,glob 在 v24.0 标为稳定。对运行环境固定的新脚本,这些能力值得先检查。Node.js 文件系统文档

以下是假设的构建脚本片段,只示范 Node 24 可以直接完成哪些工作,不宣称与所有第三方工具完全等价:

import { mkdir, cp, glob } from 'node:fs/promises';
 
await mkdir('dist/assets', { recursive: true });
await cp('assets', 'dist/assets', { recursive: true });
 
for await (const file of glob('content/**/*.mdx')) {
  console.log(file);
}

应用程序能指定 Node 版本,公开发布的软件包却要照顾用户的最低版本;这两者的决策条件不同。即使版本符合,也要比对符号链接、覆盖、排除规则、隐藏文件与平台路径。第三方 glob 工具提供的选项,不能只因为内置 API 名称相同就视为一致。

这类工具退场有三道门槛:平台先提供能力,项目再能要求兼容版本,最后迁移成本才低于保留成本。第一道门槛跨过,不代表后两道也已完成。许多所谓“过时依赖”,实际卡在支持范围与风险分配。

第四种:为上一代架构保留契约的封装

Request 与 Moment 容易被并列,却代表不同的维护选择。Request 已弃用;Moment 持续维护,并保留既有可变对象的 API。将它们简化成“都有新的替代品”,会漏掉旧系统真正难以搬动的部分。

Moment 在 2026 年 8 月 17 日修订政策:仍不新增面向用户的功能,但技术维护可以继续,也不再一概排除需要破坏性变更的 3.0。这提醒我们,不应继续引用旧公告的“不会有 v3”当成目前承诺。Moment 政策更新

真正的迁移成本藏在契约里。假设一段日期流程依赖操作后原对象会被修改,换成返回新对象的工具,画面可能仍显示合理日期,另一段流程却读到旧值。替换前要盘点可变性、解析、时区与格式输出,不只是对照方法名称。

原生 Intl.DateTimeFormat 可以承担许多显示工作,但格式化一个时间点,不等于解析所有人类输入,也不等于定义“下个月同一天”或跨夏令时的预约规则。ECMA-402 日期格式化

Request 的用户也可能依赖 cookie、代理或流设置。迁移要从应用真正使用的契约开始,再选择新 client 或薄封装。成熟系统暂时保留某个依赖,可以是有期限、有责任人的工程决策;“不要再新增使用”与“今天全面移除”是两种不同指令。

第五种:替浏览器做它现在已经会做的事

jQuery 的部分入门用途,现在可以由 querySelectorAll、classList 与 addEventListener 完成。检测元素进入视口,也有 Intersection Observer 可作为基础。若一个软件包的全部价值只在这些工作,原生能力确实压缩了它的必要性。DOM 标准、Intersection Observer 规范

但 jQuery 在 2026 年 1 月 17 日发布了 4.0。这直接否定“新项目可能不需要,所以项目已停止维护”的推论。既有插件、生态集成与团队熟悉度,仍可能构成保留理由。jQuery 4.0 公告

原生基础能力也不等于完整产品。能观察交集,不代表已经处理图片加载失败、虚拟列表、焦点顺序或屏幕阅读器交互。当框架或较高层组件接手这些责任,应用不再直接安装某个小 utility,它的工作仍可能留在集成层里。

类型与原用途接手的能力仍需检查的剩余需求
Fetch 兼容层:让 Node 能用 Fetch运行环境内置 Fetch流、连接设置、最低版本
通用工具箱:简化数据操作数组方法、Set、可选链等边界语义、深层数据、防抖契约
文件工具:补 mkdir、复制与搜索Node 的 mkdir、cp、glob路径、链接、排除规则、支持版本
旧 API 封装:统一 HTTP 或日期操作新 client、Intl 与其他日期工具可变性、解析、时区、兼容承诺
浏览器补丁:选取、事件、可见性DOM 与观察者 API插件、生态集成、完整交互行为

这是一张替代边界表,不是衰退程度排名。每一行都可能包含活跃维护、持续有需求的产品。

AI 可能拆掉薄封装,也可能替旧依赖续命

以下是我的机制推论:如果一个工具只包装几个稳定 API,需求清楚、输入受控、容易验证,AI 降低撰写局部实现的成本后,维持一个外部依赖的吸引力可能下降。团队可以选择持有少量代码,而不接受整个软件包的接口与升级节奏。

但产生代码不等于消除责任。原本由软件包作者处理的边界条件、测试、漏洞修补与版本兼容,会转移到你的代码仓库。生成得越容易,越需要问:这个实现出错时,谁有能力判断它错了?

反方向也已出现在维护者的观察里。Moment 的 2026 公告认为,agent 对既有程序与文档的熟悉,可能带动新的使用。这是维护团队对增长来源的判断,不能当成已证实的 AI 因果效应;本次下载数据也无法验证它。Moment 对 agent 使用的观察

因此,“AI 会让 utility 消失”还不足以成为结论。它可能让简单程序更容易自建,也可能通过示例与旧知识反复引入同一套依赖。包的历史曝光度,甚至可能比它是否最适合目前环境,更容易影响自动选择。这是需要管理的风险,而不是本文已量化的市场趋势。

对工具作者而言,我认为更值得累积的是可验证的正确性、明确的支持边界、可靠文档与专业问题的测试案例。薄薄一层方便写法容易被重做;多年整理出的失败情境,却没有同样低的成本。

哪些值得留下?看它持续承担的复杂度

时区数据会随政治决策更新;IANA 明确说明数据库会因 UTC offset、时区边界及夏令时规则的变化而修订。这种工作不会因为第一次实现只用了几个函数就结束。IANA 时区数据库

解析器、密码学实现、复杂协议与无障碍组件也提供一个判断方向:真正的成本可能在持续对照规范、处理恶意或意外输入、跨环境测试和修正错误。这是选择依赖时应检查的责任,不是对任何特定包的安全保证。

一个只有几十行的工具,可能封装了你不想自行负责的微妙语义;一个大型工具箱,也可能在你的项目里只被用来完成一行原生操作。大小与价值没有固定对应。关键是:移除之后,剩下的责任由平台承担,还是其实落回团队?

我会把判断收敛成以下行动,而不以“零依赖”当目标:

当前情况建议行动验收重点
原生能力已足够,版本固定,用途单纯逐步替换直接依赖以真实输入验证成功与失败行为
原生能力接近,但边界语义不同先保留或加薄兼容层null、错误、取消、时区等契约
包弃用且应用直接使用限制新增使用,安排迁移找出负责人、替代方案与回退点
弃用包由上游间接带入追查并升级或替换上游不把手改 lockfile 当成根治
依赖承担仍在变动的专业问题保留并持续评估维护承诺、测试、支持范围与更新能力

实际操作时,先用包管理器的依赖追踪功能确认来源,再搜索程序真正调用的功能。列出最低运行版本与重要语义,选一条小路径替换,以既有行为测试比较成功、失败与边界输入,最后确认生产构建及依赖树。单纯删掉直接依赖声明,未必能移除上游带进来的副本;删除 lockfile 也不会让上游突然改用新 API。

回到那个仍在大量下载的包。它可能是重要基础设施,也可能只是迁移尚未完成的痕迹。数字本身没有替你作出判断。

真正值得追踪的退场信号,是包原来替你承担的工作,已经有更合适、可验证的归属。 当平台接手基础能力,工具就要靠更深的专业继续证明价值;当团队选择自行持有代码,也要同时接下原本外包的责任。能少装一个软件包是结果,知道谁负责剩下的复杂度,才是决策。