摘要
本文对 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/console-log-colors@0.3.5/ # 查看一个文件的内容 https://unpkg.com/browse/console-log-colors@0.3.5/src/index.js # 查看一个文件的内容(raw 原始源码,可作为 CDN 资源的引用) https://unpkg.com/console-log-colors@0.3.5/src/index.js
以 CDN 方式引用 bootstrap 资源的示例:
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.2.3/dist/css/bootstrap.min.css" crossorigin="anonymous"> <script src="https://unpkg.com/bootstrap@5.2.3/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/html-print@0.0.1/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
服务实现代理请求的方案也衍生了许多有意思的开源实现,有兴趣可以进一步的进行相关探索。