摘要
本文对 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 包资源都可访问到。
此外,它们均没有提供官方的对外开放服务声明,也就是说个人学习研究默认可以白嫖,企业级应用是需要避免使用的,以免带来不确定的生产级问题。
4 部署支持私有 npm 仓库的 unpkg
4.1 为什么要私有化部署
unpkg
官方 CDN 服务器在境外,国内部分地区访问速度并不理想。elemecdn
、zhimg
等国内可用资源不能作企业级服务。- 内网私有
npm
仓库上的包无法通过外部服务访问。 - 提供一个快捷浏览内网
npm
私有包资源的渠道。 - more…
就个人开发来说,unpkg
服务提供的目录访问方式,可以让我们无需下载即可方便快捷的了解一个内网私有 npm
包的资源。
4.2 私有化部署存在的问题
unpkg
代码仓库是开源的,因此我们可以基于其开源仓库代码进行私有化部署:
然而,直接使用官方仓库进行部署基本是行不通的,其也没有提供明确的私有化部署说明文档。根据个人分析总结,官方仓库存在的问题:
- 以
unpkg.com
的开发和部署为主,没有私有化部署的直接说明文档,私有化部署一般需作部分逻辑修改。 - 默认使用了
CLOUDFLARE
上的服务做统计。私有化部署一般不会使用,可修改代码移除。 - 近两年未更新,社区有少量 PR 但无人合并。
- more…
4.3 基于官方仓库修改以适配私有化部署
前文已经做过分析说明,直接基于官方仓库代码进行私有化部署多多少少的会存在一些问题。我们可以 fork
官方仓库,修改源码以适配私有 npm 仓库的部署。
下面是本人 fork
官方仓库并做了一些修改适配的仓库,并添加了开发与私有部署的步骤指引:
该仓库的主要修改点有:
- 支持并使用
pnpm
安装和管理依赖。 - 将所有外部依赖包全部升级至当前最新版本并进行了兼容(2023-02)。
- 升级并适配了 React 18。
- 包下载地址由拼接改为从包信息中读取。私有化部署的下载地址可能与标准格式有差异。
- 支持
.env[.local|.prod]
的方式配置环境变量,实现简单的私有化部署配置方式。 - 支持
CLOUDFLARE
统计开关,默认关闭。 - 支持
Google Analytics
配置为私有 ID,不配置默认为关闭。 - 参考了部分官方仓库未合并的
PR
的修改。 - More…
你也可以基于仓库提交记录进行详细了解。
可以展望的更多改进:
- 文件首次访问慢(因为需要下载
tgz
并解压),可考虑对tgz
包进行服务端缓存,开发包缓存策略 - 支持需要授权的私有包请求(官方已有未合并的相关 PR)
- More…
4.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 镜像进行部署。
5 总结与参考
本文对 unpkg
的服务和实现原理作了简单介绍,并提供了私有化部署的 fork
修改方案参考。希望能对有此类需求的同学提供一定的帮助。
除了 unpkg
,著名的针对开源项目提供免费 CDN 服务的 jsdelivr 也支持 npm
资源的 CDN 访问。此外,它还支持 GitHub
仓库、WordPress
插件的资源 CDN 访问服务。由于 GitHub
在国内访问体验极差,基于 jsdelivr
服务实现代理请求的方案也衍生了许多有意思的开源实现,有兴趣可以进一步的进行相关探索。