持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前端模块化
首先理解前端工程化中一个很重要的概念:模块化。一起聊聊模块化是何物以及JS模块化包括哪些解决方案。当然CJS与ESM这两种模块规范是重点回顾对象。
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler) 。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph) ,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
模块化的演变
js最初的设计是实现一些简单的浏览器交互效果,随着技术发展,网络和用户设备的不断升级,前端项目不断膨胀,早期的js非模块管理的方式已经不适应了,基于此,js引入了模块化的概念,它包括CJS、AMD、CMD、UMD以及ESmodel的ESM。
使用模块化能给前端工程带来什么样的好处?
解决命名冲突:隔离作用域
提供复用性:可以抽离公共代码
提高可维护性:使代码更加高内聚
模块化方案
模块化方案有很多种
最常用的两种模块化方案的对比
CJS与ESM有何细微不同。
| - | ESM | cjs |
|---|---|---|
| 语法类型 | 静态 | 动态 |
| 关键声明 | export、import | require、module.export |
| 加载方式 | 编译时加载 | 运行时加载 |
| 加载行为 | 异步加载 | 同步加载 |
| 书写位置 | 顶部位置 | 任何位置 |
| 指针指向 | this指向underfind | this指向当前模块 |
| 执行顺序 | 引用时生成只读引用执行是才 正式取值 | 首次引用时加载模块再次引用时 读取缓存 |
| 属性应用 | 所有类型属于动态只读引用 | 基本类型属于复制不共享引用类型属于 浅拷贝且共享 |
| 属性修改 | 工作空间不可修改引用的值 但可以通过引用的方法修改 | 工作空间可修改引用的值 |
| node变量 | 不支持 | 支持 例如:__filename |
接下来我们深入分析
静态 || 动态
我们都知道javascript是一门JIT语言,v8引擎拿到js代码后会边编译边执行,在编译的时候v8就给import导入的模块建立静态的引用,并且不能在运行时不能更改。所以import都放在文件开头,不能放在条件语句里。
而require导入模块是在运行时才对值进行拷贝,所以require的路径可以使用变量,并且require可以放在代码的任何位置。
基于这个差异,ESM比CJS好做tree-shaking。
异步 || 同步
ESM是顶层await的设计,而require是同步加载,所以require无法导入ESM模块,但是可以通过import()导入。
引用 || 拷贝
CJS模块require导入的是值的拷贝,而ESM导入的是值的引用。
让我们回到打包工具
Webpack:Webpack 作为一个模块打包工具,本身就可以解决模块化代码打包的问题,将零散的 JavaScript 代码打包到一个或多个 JS 文件中。
对于有环境兼容问题的代码,Webpack 可以在打包过程中通过 Loader 机制对其实现编译转换,然后再进行打包。
对于不同类型的前端模块类型,Webpack 支持在 JavaScript 中以模块化的方式载入任意类型的资源文件,例如,我们可以通过 Webpack 实现在 JavaScript 中加载 CSS 文件,被加载的 CSS 文件将会通过 style 标签的方式工作。
除此之外,Webpack 还具备代码拆分的能力,它能够将应用中所有的模块按照我们的需要分块打包。这样一来,就不用担心全部代码打包到一起,产生单个文件过大,导致加载慢的问题。我们可以把应用初次加载所必需的模块打包到一起,其他的模块再单独打包,等到应用工作过程中实际需要用到某个模块,再异步加载该模块,实现增量加载,或者叫作渐进式加载,非常适合现代化的大型 Web 应用。
当然,除了 Webpack,其他的打包工具也都类似,总之,所有的打包工具都是以实现模块化为目标,让我们可以在开发阶段更好的享受模块化带来的优势,同时又不必担心模块化在生产环境中产生新的问题。
Vite:Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。
小结
最后,让我们一起加油吧!
都看到这了,不如顺手点个赞再走 ( *ˇωˇ* )