前端过去如何实现模块化
函数作用域的尝试
在 es6 规范推出 let 和 const 能够使用的 block 作用域之前,var 能够声明的只有全局变量和函数中的函数作用域变量
因此直接利用这一局部作用域讲特定操作/功能组织到一起已经是一种模块化的雏形
至于说这种写法下,封装函数功能和模块化相互基本无法区分,那只能说是没法强求的时代问题
scirpt 文件模式
其核心就是 script 引入的文件即模块
完全以约定为规范\
// module-A.js文件
let data = "data";
// module-B.js文件
function method() {
console.log("execute method");
}
// index.html文件
...
<body>
<script src="./module-A.js"></script>
<script src="./module-B.js"></script>
<script>
console.log(data)
method()
</script>
</body>
缺点
- 所有模块都使用全局作用域,污染全局
- 也容易产生命名冲突
- 无法管理模块依赖
命名空间时期
模块只暴露一个全局对象,模块内容挂载到对象之下
自然也就是将模块内容整个包裹起来,就形成了上面的全局对象
缺点
- 私有空间是假的
- 依赖关系仍然不清楚
LIFE 时期
用立即执行函数的方式包裹模块,来提供私有空间
在函数内部就用.语法挂载到外部对象
这些都算 script 引入时期
缺点可能就是麻烦
CommonJS 规范走了第一步
一个文件就是一个模块
每个模块都有单独作用域,不会污染全局作用域
模块可以被多次加载和引用,第一次加载时就会被缓
通过 module.exports 导出成员,通过 require 函数载入模块
输出的值是值的复制,因此不同其他文件引入模块后的修改不会影响模块自身
模块按照引入顺序进行加载
缺点
- 同步执行
- 浏览器不能用
AMD 异步模块定义
CJS 的主要问题是它本身没有考虑浏览器中常见的异步加载
而响应使用浏览器要求的模块化规范是 requireJS
requireJS 仍然是通过 life 实现的,当然只要不自己写就都算方便
通过 define 函数定义模块,模块依赖和模块私有空间的内容
通过 require 函数加载模块,会自动创建 script 标签请求实现
加载完成后,requireJS 会自动删除引入时的 script 标签 缺点
- 写起来相对麻烦
- 模块划分一多就无数请求
整合性的 CMD 与 UMD
Common Module Definition
CMD 主要是整合了 cjs 和 AMD,整体和 require.js 大差不差,主要实现是 sea.js
CMD 和 AMD 的区别主要是\
- AMD 纯粹是异步加载,CMD 异步同步都可以
- AMD 是依赖前置,需要提前声明依赖;CMD 是就近依赖,使用前引入即可
Universal Module Definition
同时允许 AMD 和 CJS 规范
核心实现是实时判断具体用什么模块规范,然后调用相应的 CJS 和 AMD 方法\
原生的 ESM
使用直接在 script 标签中指定 type=module 即可 esmodule 的特点
- 自动严格模式,忽略 use strict
- module 都运行在独立的私有作用域
- 必须在文件顶部引入依赖
- 导出的变量类型收到严格限制
- 变量不允许重新绑定,且引入的模块名本身也需要是不可更改的字符串 常量
- esmodule 是通过 cors 的方式请求外部 js 模块
- 需要服务端支持 cors
- 不支持文件形式,只支持 httpServer
- esmodule script 采取延迟执行
- 一般 script 是立即执行的
esm 和 cjs 等其他规范 相互之间
- cjs 是动态运行,只能在运行时确定,
- esm 是静态编译,编译时即确定输入输出的内容,这俩是核心区别
- esm 传值是传引用
- cjs 传值是通过复制值
- esmodule 可以导入 cjs
- cjs 不能导入 esmodule
- cjs 始终只会导出一个默认成员
说法很多的 Tree Shaking 和 DCE(先占个坑)
Tree Shaking 概念和实现的提出者 Rich Harris (同时也是 Svelte 和 RollUp 的发明者)对两个概念做过形象的对比
假象一个打鸡蛋和面做蛋糕的场景。因为鸡蛋壳确实没啥用,所以在蛋糕制作中我们总是要想办法把鸡蛋壳清理出来。 Dead Code Elimination(DCE 死代码清除)就好像先把鸡蛋整个和到面里打碎,等到蛋糕做完了才把碎鸡蛋壳挑出来 Tree Shaking(摇树,其实挺形象的)就好像提前把鸡蛋敲开,把蛋白蛋清蛋液直接准备好和面用