前端的交付基于浏览器,资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,是前端发展中一直探索的难题。
模块化 这个词大家一定有所熟知。模块的产生就是为了解决前端日趋复杂,从而加载越来越多资源而产生的问题。最终目的是为了提高生产力!
前端模块发展历程:前端模块化系统
模块化发展到今天,其基本的范式为:利用 bundle 工具(如 webpack)将源码打包成浏览器可识别的 bundle。
范式从本质上讲是一种理论体系、理论框架。范式具备一定程度内的公认性,被人们普遍接受。
该范式(Bundle 模式)下,随着项目体积增大,开发阶段一次性将源代码和第三方依赖编译处理打包到一起的耗时会显著增加;成千上万个模块导致首次 dev server 启动耗时在几分钟甚至十几分钟,严重影响了开发效率与体验。
解决思路: 从减少 webpack 模块数量角度考量,剔除 node_modules 下的第三方依赖,仅对业务代码打包。
针对该方式常见的方法是将第三方库在 Webpack 构建时配置 External, HTML 中直接通过 Script 标签引入 UMD 产物。但 UMD 会带来副作用:① 不支持 UMD 的包进行改造(额外工作量);② 全局变量污染,甚至互相覆盖;③ 需要增加 UMD 额外的兼容代码。
显然,UMD 不是最佳方式。
随着 ECMAScript 2015
提出 ECMAScript Module
规范,各个浏览器都在积极地推进着浏览器模块系统的实现,前端模块化有了原生支持方式。
未来的构建范式? 两个方向:
典型的示例:Snowpack、Vite
Snowpack 是首次提出利用浏览器原生 ESM 能力的工具。开发过程中,Snowpack 为你的应用程序提供 unbundled server。每个文件只需要构建一次,就可以永久缓存。文件更改时,Snowpack 会重新构建该单个文件。在重新构建每次变更时没有任何的时间浪费,只需要在浏览器中进行 HMR 更新。
对比一下 bundle 和 ESM 两者的区别:
浏览器请求前将全部资源进行转换打包处理生成 bundle,然后浏览器加载相关 bundle。
浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
依附于 ESM import
和 export
可以单独加载依赖项。因此对于单文件构建速度、调试、缓存等优势明显。每个文件都是单独构建并无限期缓存。开发环境永远不会多次构建文件,浏览器永远不会下载文件两次(直到它发生变化)。
使用 ESM 构建的核心特点:
node_modules
完全不需要参与到构建过程,构建效率提升明显典型的示例:esm.sh、skypack
将 NPM 仓库上的包转化成支持 ESModule 的版本并通过 URL 来进行分发。
有了 ESM 分发:
CMD
或者 AMD
规范开发的众多 NPM 包;原理: 将传统的 ADM/CMD/UMD 语法,通过 AST 的解析,将其转化为 ESModule 语法。 难点: 这种转换属于语法升级,需要做向上兼容处理。