什么是模块化
- 事实上模块化开发最终目的是将程序划分成一个个小的结构
- 在这个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的结构
- 这个结构可以将自己希望暴露的变量、函数、对象等导出给其他结构使用。
- 可以通过某种方式,导入另外结构中的变量、函数、对象等;
组件化和模块化的特点
- 低耦合
- 热插拔,需要的时候使用,不需要的时候去掉
模块化特点
- 独立性
可以针对一个模块单独进行设计、研发,相对工作量和难度变小。 - 复用性
一些通用模块(例如登录或注册)可以被重复使用,而不用每次重新开发。 - 解偶性
模块与模块之间,将相互影响降到最低,使得更换、升级或添加某个模块,不影响其他模块的工作。 - 灵活性
通过选择和组合不同的模块,可以快速构建一个 新的产品。
目前流行的模块化规范
AMD
- AMD主要是应用于浏览器的一种代码规范。
- 它采用的是异步加载模块。
- 事实上AMD的规范还要早于CommonJS,但是CommonJS目前依然在被使用,而AMD使用的较少了;
- AMD实现的比较常用的库是require.js和curl.js
CMD
- CMD规范也是应用于浏览器的一种模块化规范:
- 它也采用了异步加载模块,但是目前CMD使用也非常少了;
- CMD也有自己比较优秀的实现方案: SeaJS
CommonJS
介绍
CommonJS是一个规范,最初提出来是在浏览器以外的地方使用
- Node是CommonJS在服务器端一个具有代表性的实现,Node中对CommonJS进行了支持和实现,Node中每一个js文件都是一个单独的模块。
- Browserify是CommonJS在浏览器中的一种实现。
- webpack打包工具具备对CommonJS的支持和转换。
使用
- exports和module.exports负责对模块中的内容进行导出
- require函数可以帮助我们导入其他模块中的内容
require 加载标识符原则
exports和module.exports
- CommonJS是没有module.exports的概念的
- 但是为了实现模块导出,Node中使用的是Module类,每一个模块都是Module类的一个实例,也就是module
- 在Node中真正用于导出的根本不是exports,而是module.exports;
- exports是module.exports对象地址的一个引用
模块的加载过程
- 模块在被第一次引用时,模块中的js代码会被运行一次
- 模块被多次引用时,会缓存,最终只加载一次,因为每个模块对象module都有一个属性:loaded,为false表示还没有加载,为true表示已经加载
- 如果有循环引用,Node采用的是深度优先算法。
缺点
- CommonJS加载模块是同步的
同步意味着只有等到对应的模块加载完毕,当前模块中的内容才能被运行。
这个在服务器不会有什么问题,因为服务器加载的js文件都是本地文件,加载速度非常快 - 如果他应用于浏览器
浏览器加载js文件需要先从服务器下载下来,之后再加载运行
那么采用同步就意味着后续的js代码都无法正常运行,即使是一些简单的DOM操作 - 所以在浏览器中,通常不使用CommonJS规范
当然我们可以用webpack将其转化为浏览器可以直接执行的代码。
ES Module
从 ES6 开始, JavaScript 才真正意义上有自己的模块化规范。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
使用
export负责将模块内的内容导出;
import负责从其他模块导入内容;
- export { }导出的成员并不是对象字面量,import { } 导入的时候并不是使用解构语法,都只是固定的语法。
- export导出的成员并不是导出成员的值,而是成员的引用关系,所以外部拿到的值会受模块内部值的变化所影响。
- 外部导入一个模块中的成员,导入的是一个只读的成员,并不能去修改
- 动态导入模块返回的是promise
- Node v12 之后的版本,可以通过package.json 中添加type字段为module, 将默认模块系统修改为 ES Module 此时就不需要修改文件扩展名为 .mjs 了
特性
-
通过给script标签上一个type=‘module’的属性,就可以在ES Module的标准执行其中的js代码了
-
ES Module自动采用严格模式,忽略 'use strict’
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
- eval不会在它的外层作用域引入变量
- eval和arguments不能被重新赋值
- arguments不会自动反映函数参数的变化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局对象
- 不能使用fn.caller和fn.arguments获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
-
每个ES Module都是运行在单独的私有作用域中
-
ES Module是通过 CORS 的方式请求外部JS模块的
请求的服务端需要支持CORS,否则会报一个跨域的错误,而且在Nerwork中可以看到这样一个请求被浏览器终止了 -
ES Module会延迟执行脚本,类似于defer的效果
ES Module的解析流程
- 阶段一:构建(Construction),根据地址查找js文件,并且下载,将其解析成模块记录(Module Record);
- 阶段二:实例化(Instantiation),对模块记录进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向 对应的内存地址。
- 阶段三:运行(Evaluation),运行代码,计算值,并且将值填充到内存地址中
ES Modules 和 CommonJS的一些区别
- 使用语法层面,CommonJs是通过module.exports,exports导出,require导入;ESModule则是export导出,import导入
- CommonJs是运行时加载模块,ESModule是在静态编译期间就确定模块的依赖
- ESModule在编译期间会将所有import提升到顶部,CommonJs不会提升require
- CommonJs导出的是一个值拷贝,会对加载结果进行缓存,一旦内部再修改这个值,则不会同步到外部。ESModule是导出的一个引用,内部修改可以同步到外部
- CommonJs中顶层的this指向这个模块本身,而ESModule中顶层this指向undefined
- CommonJS加载的是整个模块,将所有的接口全部加载进来,ESModule可以单独加载其中的某个接口
参考文档
https://blog.csdn.net/Lyb__/article/details/109382975?ops_request_misc=&request_id=&biz_id=102&utm_term=web%E5%89%8D%E6%AE%B5%E6%A8%A1%E5%9D%97%E5%8C%96&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-109382975.142v73control_1,201v4add_ask,239v2insert_chatgpt&spm=1018.2226.3001.4187
https://blog.csdn.net/qq_39221436/article/details/119531017?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167836248416800184119552%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=167836248416800184119552&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-3-119531017-null-null.142v73control_1,201v4add_ask,239v2insert_chatgpt&utm_term=ESMoudle&spm=1018.2226.3001.4187
https://juejin.cn/post/7193887403570888765
https://juejin.cn/post/7053823393539293191
https://blog.csdn.net/weixin_45047039/article/details/109985949