大家好,我卡颂。
已经有越来越多前端开发者放弃webpack
,改用vite
作为项目打包工具。
其中最主要的原因是 —— vite
在开发环境基于ESM
规范实现的Nobundle
模式,节省了代码打包的时间(当然,也有ESBuild
的功劳)。
而在生产环境,当前仍有打包的需求。
随着浏览器的迭代,ESM
规范兼容性越来越好,终有一天会进入生产环境大面积可用的状态。
届时生产环境打包将不再是刚需。
另一方面,从HTTP
协议的角度看,在HTTP/1.1
时代,多个模块被打包成一个文件能减少浏览器并发请求数,达到优化目的。
但在HTTP/2
多路复用普及后,这么做的意义就不大了。
可以说,当这些基建成熟后,生产环境使用ESM
模块是水到渠成的事情。
很多团队预感到这点,很早就开始布局相关产品。今天要介绍的Skypack
就是这样一款产品。
欢迎加入人类高质量前端框架群,带飞
不一样的CDN
Skypack
首次发布于19年6月(曾用名Pika CDN
),是一款基于ESM规范的CDN服务。
在浏览器中,常见的CDN
服务通常以script
标签的形式引入UMD
规范的代码,以ReactDOM
举例:
<script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.development.js"></script>
代码执行后会在全局暴露对象window.ReactDOM
。
一些情况下,一个包还会依赖其他包,比如ReactDOM
还会依赖如下3个包:
React
scheduler
object-assign
为了应对这种情况,在生产环境开发者通常会将第三方依赖统一打包。
而Skypack
以ESM
规范引入代码:
// 在业务代码中引入如下语句
import ReactDOM from 'https://cdn.skypack.dev/react-dom';
浏览器会依次发起对包及其依赖的请求:
配合上浏览器的Module Preload特性,可以让这些资源统一预加载。
这就解决了第三方依赖需要打包的问题。
按需polyfill
如果你访问上述CDN
链接(https://cdn.skypack.dev/react...),会发现返回的结果并不是ReactDOM
的代码,而是下面两句export
语句:
export * from '/-/react-dom@v17.0.1-oZ1BXZ5opQ1DbTh7nu9r/dist=es2019,mode=imports/optimized/react-dom.js';
export {default} from '/-/react-dom@v17.0.1-oZ1BXZ5opQ1DbTh7nu9r/dist=es2019,mode=imports/optimized/react-dom.js';
语句的背后才是ESM
规范的ReactDOM
代码。
之所以这么做是因为:Skypack
会根据目标浏览器的UA为浏览器提供适合的包。
在高版本Chrome
中的代码不需要polyfill
,而在低版本IE
中的代码需要polyfill
,所以不同目标浏览器拿到的是不同的ReactDOM
代码。
上述export
语句中哈希(oZ1BXZ5opQ1DbTh7nu9r)的不同就对应同一个版本的ReactDOM经过不同程度polyfill后的不同结果。
此外,在url
后加min
能得到压缩后的代码:
import ReactDOM from 'https://cdn.skypack.dev/react-dom?min';
接下来让我们看看Skypack
是如何处理请求的。
处理请求的流程
并不是所有包都有ESM
规范的产物(React
就没有),当以如下url
格式访问任意包时:
// xxx替换为任意包名
import React from 'https://cdn.skypack.dev/xxx';
如果之前从未有人访问过这个包,则会构建包及其依赖的ESM产物并返回。
比如ReactDOM
本身只提供UMD
规范的产物,第一个访问他的Skypack CDN
链接的用户会经历如下步骤:
- 收集
ReactDOM
及其依赖 - 将
ReactDOM
及其依赖变为ESM
规范 - 构建不同
polyfill
程度的ESM
产物 - 根据目标浏览器
UA
返回对应的ReactDOM
在ReactDOM
的产物代码中可以看到,他依赖的三个包已经转为ESM
规范:
总结
除了Skypack
外,esm.sh也是类似功能的ESM CDN
服务。
等到前端基建成熟的那天,相信这些ESM CDN
服务一定能大放异彩。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。