搭建支持内网私有 npm 仓库的 unpkg CDN 站点
- 前端开发
- 2023-02-14
- 2171热度
- 0评论
摘要
本文对 unpkg 的服务和实现原理作了简单介绍,并提供了私有化部署的 fork 修改方案参考。希望能对有此类需求的同学提供一定的帮助。
1 unpkg 简介
1.1 什么是 unpkg
unpkg 是一个实现对 npm 上的资源提供 CDN 的服务。官方的介绍是这样的:
The CDN for everything on npm.
unpkg is a fast, global content delivery network for everything on npm.
unpkg 官方地址为: https://unpkg.com
1.2 使用基于 unpkg 的 CDN 服务
unpkg 引用资源的基本格式是这样的:
unpkg.com/:package@:version/:file
以下为具体的例子:
# 直接访问 npm 包:会跳转至最新版本,可以以目录的形式查看该包的具体资源列表
https://unpkg.com/console-log-colors/
# 以目录的形式,查看某个版本的资源列表
https://unpkg.com/browse/[email protected]/
# 查看一个文件的内容
https://unpkg.com/browse/[email protected]/src/index.js
# 查看一个文件的内容(raw 原始源码,可作为 CDN 资源的引用)
https://unpkg.com/[email protected]/src/index.js
以 CDN 方式引用 bootstrap 资源的示例:
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css" crossorigin="anonymous">
<script src="https://unpkg.com/[email protected]/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>
1.3 unpkg 的实现原理
npm 包的更新与下载是通过 http://registry.npmjs.com 提供的标准接口实现的,可以通过该 registry 标准接口服务获取一个包的最新信息。获取到的包信息中包含了其所有版本的基本信息和 tgz 下载地址。示例:
unpkg 的原理即基于 http://registry.npmjs.com 提供的标准服务接口,在访问一个包时从 npmjs 获取包信息(并缓存一定的时间),从包信息中取得查询版本或最新版本的信息,拼接出 tgz 下载地址,然后请求下载并以流的方式解压,将解压后的资源进行处理后返回。如果是目录形式或文件预览的地址请求格式,还会进行服务器端渲染(React),返回渲染后的 html 页面。
由于 npm 包具体版本的资源内容是不会变更的,unpkg 对每一个确定性的版本目录或文件都设置了长达一年的 http 长缓存 header 信息,在标准浏览器下只要成功访问过一次,后续基本都会仅从浏览器缓存中读取,不再请求服务器资源。

2 unpkg 在国内
在国内类似的资源,可以搜索到且比较稳定的有如下两个:
elemecdn。如:https://npm.elemecdn.com/jquery/zhimg。如:https://unpkg.zhimg.com/jquery/
简单分析了解一下可以得出如下初步的结论:
elemecdn是完全基于unpkg官方源码进行的私有化部署,界面、功能表现、可访问资源均基本与unpkg官方表现一致。从异常资源报错信息来看缓存资源应该存放于阿里云 CDN 上。zhimg的访问形式基本与unpkg保持一致,但在目录访问的界面上有一定的差异,可以简单推测为参考unpkg做了个性化定制。而且添加了白名单机制,并非所有的 npm 包资源都可访问到。
此外,它们均没有提供官方的对外开放服务声明,也就是说个人学习研究默认可以白嫖,企业级应用是需要避免使用的,以免带来不确定的生产级问题。
3 部署支持私有 npm 仓库的 unpkg
3.1 为什么要私有化部署
unpkg官方 CDN 服务器在境外,国内部分地区访问速度并不理想。elemecdn、zhimg等国内可用资源不能作企业级服务。- 内网私有
npm仓库上的包无法通过外部服务访问。 - 提供一个快捷浏览内网
npm私有包资源的渠道。 - more...
就个人开发来说,unpkg 服务提供的目录访问方式,可以让我们无需下载即可方便快捷的了解一个内网私有 npm 包的资源。
3.2 私有化部署存在的问题
unpkg 代码仓库是开源的,因此我们可以基于其开源仓库代码进行私有化部署:
然而,直接使用官方仓库进行部署基本是行不通的,其也没有提供明确的私有化部署说明文档。根据个人分析总结,官方仓库存在的问题:
- 以
unpkg.com的开发和部署为主,没有私有化部署的直接说明文档,私有化部署一般需作部分逻辑修改。 - 默认使用了
CLOUDFLARE上的服务做统计。私有化部署一般不会使用,可修改代码移除。 - 近两年未更新,社区有少量 PR 但无人合并。
- more...
3.3 基于官方仓库修改以适配私有化部署
前文已经做过分析说明,直接基于官方仓库代码进行私有化部署多多少少的会存在一些问题。我们可以 fork 官方仓库,修改源码以适配私有 npm 仓库的部署。
下面是本人 fork 官方仓库并做了一些修改适配的仓库,并添加了开发与私有部署的步骤指引:
该仓库的主要修改点有:
- 支持并使用
pnpm安装和管理依赖。 - 将所有外部依赖包全部升级至当前最新版本并进行了兼容(2023-02)。
- 升级并适配了 React 18。
- 包下载地址由拼接改为从包信息中读取。私有化部署的下载地址可能与标准格式有差异。
- 支持
.env[.local|.prod]的方式配置环境变量,实现简单的私有化部署配置方式。 - 支持
CLOUDFLARE统计开关,默认关闭。 - 支持
Google Analytics配置为私有 ID,不配置默认为关闭。 - 参考了部分官方仓库未合并的
PR的修改。 - More...
你也可以基于仓库提交记录进行详细了解。
可以展望的更多改进:
- 文件首次访问慢(因为需要下载
tgz并解压),可考虑对tgz包进行服务端缓存,开发包缓存策略 - 支持需要授权的私有包请求(官方已有未合并的相关 PR)
- More...
3.4 unpkg 私有化部署流程参考
拉取代码并安装依赖:
git clone https://github.com/renxia/unpkg.git
cd unpkg
pnpm i
创建并编辑 .env.prod 内容,设置相关环境变量配置:
cp .env.sample .env.prod
项目构建并打包:
set NODE_ENV=production
pnpm build
pnpm pack
服务器部署:
tar zxvf unpkg-1.0.0.tgz
cd package
npm i --omit dev
# pnpm i -P
pm2 -n unpkg start.js
也可以基于以上命令编写 dockerfile,基于 docker 镜像进行部署。

4 开源项目结合免费 CDN 服务的另类玩法
除了 unpkg,著名的针对开源项目提供免费 CDN 服务的 jsdelivr 也支持 npm 资源的 CDN 访问。此外,它还支持 GitHub 仓库、WordPress 插件的资源 CDN 访问服务。由于 GitHub 在国内访问体验极差,基于 jsdelivr 服务实现代理请求的方案也衍生了许多有意思的开源实现。
unpkg 和 jsdelivr 的基本目标是为开源社区项目提供一种免费 CDN 服务方案。但基于其基本服务能力和开源社区的开放能力,衍生了许多曲径通幽效果的应用方案。
4.1 服务请求代理加速
npm 和 github 在国内访问的速度体验不太好,基于免费 CDN 服务做功能扩展,可实现对开源社区资源的加速访问。
如著名的 hunshcn/gh-proxy 项目,基于 CDN 服务实现对 github 资源代理请求。ghproxy 的基本能力:
- 文件访问:
https://mirror.ghproxy.com/github.com/lzwme/blog-examples/blob/main/README.md - 附件下载:
wget https://mirror.ghproxy.com/https://github.com/stilleshan/ServerStatus/archive/master.zip - 仓库 clone:
git clone https://mirror.ghproxy.com/github.com/lzwme/scoop-proxy-cn
基于 ghproxy 的基本能力,则又可衍生许多针对 github 服务加速的项目。如:
- 资源类项目加速。如 lzwme/scoop-proxy-cn 即是本人尝试性创建的一个这样的仓库资源。它是一个 scoop 应用资源镜像库,基于
ghproxy的能力进行下载资源链接转换,针对国内用户使用scoop安装从github下载的应用提供加速支持。 github浏览加速。如 du33169/gh-proxy-buttons 是一个基于 gh-proxy 的油猴脚本,为 github 中的特定链接(releases、文件、项目地址)添加一个悬浮按钮,提供代理后的加速链接。此类魔改脚本非常多。- more...
github 全站代理访问。
- https://github.com/netnr/workers
https://cors.zme.ink/https://github.com/lzwme/
至于 npm 资源,则有搭建私有 npm 的完整开源解决方案(如 cnpm 等),结合 unpkg 则可实现任意 npm 资源的极速访问。
其他类似 ghproxy 服务的项目:
- (git-cloner/gitcache)[https://github.com/git-cloner/gitcache]
4.2 静态网页托管
提供静态托管服务的有许多,如 gh-pages、Vercel 等,但都会因为合规问题,容易被防火长城拦截,因 DNS 污染而无法访问。
基本需求:简单的托管在线 demo 用于演示。只需要将静态网站项目发布为 npm 包,然后即可通过 unpkg 服务进行访问。示例:
- https://unpkg.com/cocos2d/tests/public/index.html
- http://10.49.121.43:8080/@gf/[email protected]/demo/comm/include.html
4.3 附件网盘、另类图床
网站 CDN 服务大多用于 css、js 等资源的托管,但也可以上传附件、图片等。于是出现了利用 unpkg、jsdelivr 服务托管图床的方案。
- 对于
github:创建仓库并上传图片资源,然后利用jsdelivr服务引用。 - 对于
npm:将github图床仓库发布为npm包,基于unpkg服务引用图片。 - 基于
github actions创建自动发布任务,新图片提交则发布新版本至npm。
示例:
http://unpkg.com/yongruizhang-blogimg/
需注意:
- 将
npm作为图床可能会因包体积过大引发封号风险,一般单个包应不超过1G。过大的资源包可以分多个包发布。 jsdelivr对 github 仓库大小有限制,不超过50M。
这类方案如果流行起来,npm 及各种 npm 镜像都要被玩坏了。
5 总结与参考
本文对 unpkg 的服务和实现原理作了简单介绍,并提供了私有化部署的 fork 修改方案参考。希望能对有此类需求的同学提供一定的帮助。
除了 unpkg,著名的针对开源项目提供免费 CDN 服务的 jsdelivr 也支持 npm 资源的 CDN 访问。此外,它还支持 GitHub 仓库、WordPress 插件的资源 CDN 访问服务。由于 GitHub 在国内访问体验极差,基于 jsdelivr 服务实现代理请求的方案也衍生了许多有意思的开源实现,有兴趣可以进一步的进行相关探索。