后端

官方 npm 统计不够用?我用 Nuxt3 从零搭建完整 NPM 下载量可视化站点

2026/07/24·阅读约 6 分钟·掘金标注 5分钟
官方 npm 统计不够用?我用 Nuxt3 从零搭建完整 NPM 下载量可视化站点

本文记录一个第三方 npm 下载量可视化工具的诞生过程与技术选型。


一、为什么要自己写?

起因很简单:想查自己发的 npm 包下载量。

官方提供的下载统计能力偏「单点」——适合看某个包、某一段时间的基础数字,但要做多包对比、看多年趋势,或者换一种更直观的曲线图体验,就会觉得不够用。

社区里大家熟知的第三方站点 npm-stat 曾经很好用,但后来服务不稳定、经常打不开。工具链一断,日常想看一眼下载曲线就变成「打开一堆文档、自己拼 API」。

于是干脆自己写一个:

  • 技术栈选自己熟悉的 Nuxt 3 + Vue 3 + TypeScript
  • 部署到 Netlify,免运维、带 HTTPS
  • 功能上补齐官方与老站缺口:中英切换、深色模式、多包对比、跨年长区间查询

项目名仍叫 npm-stat,定位很明确:个人可用的第三方统计小工具,不是官方替代品。


二、做成了什么?

screencapture-npm-stat-netlify-app-2026-07-24-14_59_27.png

当前能力一览:

能力说明
多包查询与对比英文逗号分隔,如 vue, react, @babel/core,折线图多色叠加
跨年长区间自定义起止日期,最长约 近 10 年;服务端自动分段请求再合并
快捷时间近 7 / 30 / 90 / 365 天(结束日取「昨天」,避开当日未汇总的 0)
中英文切换界面文案、页脚免责声明随语言切换,默认英文
深色模式右上角一键切换,偏好写入 localStorage
统计卡片周期总下载、日均、单日最高 / 最低
SEO / 部署SSR + @nuxtjs/seo,Netlify 构建发布

数据流很直接:浏览器 → 本站 Nitro API → npm 官方下载接口,不落库,实时(带短时内存缓存)。


三、技术栈与目录

Nuxt 3(SSR)
├── Vue 3 + TypeScript          # 前端
├── ECharts(按需引入)         # 折线趋势图
├── Tailwind CSS                # 样式
├── Nitro server/api            # 代理 npm、分段拉取、缓存
├── @nuxtjs/seo                 # sitemap / robots / canonical
└── Netlify(nitro preset)     # HTTPS + Serverless

核心目录大致如下:

npm-stat/
├── pages/index.vue                 # 首页(查询 + 图表)
├── components/                     # 表单、图表、统计卡、主题/语言切换、页脚
├── composables/                    # 日期、查询状态、主题、i18n
├── server/api/npm/download.get.ts  # 下载量代理 + 分段合并 + 缓存
├── locales/messages.ts             # 中英文案
├── utils/date.ts                   # 日期规范化、分段切分
├── nuxt.config.ts
├── netlify.toml
└── docs/                           # 技术分享(本文)

四、几个值得讲的技术点

1. 为什么必须做服务端代理?

浏览器直连 https://api.npmjs.org/... 会遇到 CORS。把请求收到 server/api,由 Nitro 转发,既能跨域,又能统一错误码、加缓存、做分段逻辑。

官方接口形态:

GET https://api.npmjs.org/downloads/range/:start::end/:package

2. 跨年查询:官方「一次拉太长会被截断」

踩过一个坑:把 2021-01-01 到「现在」塞进一次 range 请求,npm 往往只返回大约近 18 个月的数据,起点会被悄悄改掉。

但按年(或按 ≤365 天一段)去请求,历史年份是有数据的。因此服务端做了:

  1. splitDateRangeChunks(start, end, 365) 切段
  2. 逐段请求官方接口
  3. day 合并去重排序后返回前端

这样「查 21 年到现在」才能真正画出完整曲线。区间上限约 10 年,避免 Netlify Function 被拖到超时。

3. 为什么默认查不到「今天」?

即便官方返回了「今天」「昨天」的点,近期经常是 downloads: 0——日维度汇总有延迟。产品上直接把可选结束日上限定为昨天,快捷「近 N 天」也以昨天为终点,减少「图上突然掉零」的误解。

4. 中英文:别和 @nuxtjs/seouseI18n 这个名字

接入 @nuxtjs/seo 后,线上曾出现标题、表单标签全空、只剩色块的情况。根因是 nuxt-seo-utils 注入了同名 useI18n polyfill,把项目自己的 composable 盖掉了;polyfill 的 t() 签名不同,单参数调用会得到空串。

解决办法:业务 i18n 改名为 useAppI18n,与 SEO 工具链彻底避开命名冲突。

5. 深色模式与 SSR

  • html.dark class 策略 + Tailwind darkMode: 'class'
  • app.html 内联脚本读 localStorage,减轻首屏闪白
  • 图表坐标轴颜色跟随 isDark 重绘

页面默认英文 SSR,有利于 Google 抓取固定文案;语言切换在客户端进行。

6. Netlify 上的超时与缓存

Serverless 默认超时偏紧。对策包括:

  • 上游请求超时设在约 8s 内
  • /api/npm/download 5 分钟进程内内存缓存(温实例可复用)
  • 分段多时包级串行,降低并发打满超时的概率

部署要点:nuxt build,发布目录 distNUXT_SITE_URL 指向规范域名。

7. 合规与免责

页脚提供中英文免责声明(随语言切换),强调:

  • 第三方独立工具,非 npm 官方站
  • 与 npm、GitHub 无合作 / 隶属关系
  • 数据来自公共 API,仅供参考,不保证准确

站点内不使用 npm 官方 Logo,文案避免「官方」「正版合作」等误导表述。


五、本地跑起来

npm install
cp .env.example .env   # 按需设置 NUXT_SITE_URL
npm run dev

浏览器打开本地地址即可查询。生产构建:

npm run build
npm run preview

六、回顾与还可以做什么

已经解决的问题:

  • 官方能力偏单一 → 多包对比 + 曲线 + 统计卡
  • 第三方老站不可用 → 自建可控的小工具
  • 长区间被截断 → 分段拉取合并
  • SEO / 部署 → SSR + Netlify + 基础 SEO 模块

若继续迭代,可以考虑:

  • 包名搜索建议(registry 补全)
  • 导出 CSV / 图片
  • 预渲染若干「热门包对比」内容页,加强 SEO
  • 更细的缓存层(KV / Edge)以适配更多并发

七、小结

这个项目不是从「要做一个大而全的平台」开始的,而是从查自己的下载量不方便开始的。官方够基础、第三方挂了,就用 Nuxt 3 把代理、图表、体验和部署串成一条最小可用链路。

如果你也有类似「小工具挂了、官方又不够用」的场景,与其干等,不如花一个周末把核心链路自己搭起来——往往比想象中更可控。


image.png