模块化
概念
模块化就是将一个复杂的程序按照一定的规则封装成几个块,并组合在一起。块的内部数据与实现是私有的, 只是向外部暴露一些接口与外部其它模块通信。
优势
1、避免命名冲突
2、更好的分离,按需加载
3、更高复用性
4、高可维护性
script 标签
最初模块化的雏形是使用 script 标签,将每个 js 文件看作不同的模块,加载进来。
缺点
1、污染全局作用域。
2、开发人员必须手动解决模块和代码库的依赖关系。
3、文件必须按照 script 标签的书写顺序进行加载。
4、在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱。
5、同步加载,易造成阻塞。
解决
1、defer:只支持IE,脚本不会改变文档的内容,脚本文件设置为延迟加载,当浏览器遇到带有 defer 属性的 script 标签时,会再开启一个线程去下载 js 文件,同时继续解析 HTML 文档,等 HTML 全部解析完毕、DOM 加载完成之后,再去执行加载好的js文件。
<script type="text/javascript" defer></script>
2、async:HTML5 新增的属性,仅适用于外部脚本,async 属性也是会开启一个线程去下载 js 文件,但它会在下载完成后立刻执行,而不是会等到 DOM 加载完成之后再执行,所以还是有可能会造成阻塞。
<script type="text/javascript" async></script>
规范
1、CommonJS
语法
导入:require()
导出:module.exports、exports
应用
Node.js(服务端)
特点
1、this 指向当前模块。
2、运行时同步加载,返回的是个对象。CommonJS脚本代码在require的时候,就会全部执行。只有加载完成,才能执行后面的操作,易造成阻塞。
3、加载的是整个模块,将所有接口全部加载进来,不会污染全局作用域。
4、输出的是一个值的拷贝。
5、按路径加载模块,根据实际文件名缓存,而不是 require() 提供的参数缓存的。
6、模块加载的顺序,按照其在代码中出现的顺序。
2、ES6模块化
语法
导入:
import xxx from
import {xxx} from
导出:
export { xxx, yyy };
应用
原生JS
特点
1、this指向undefined。
2、编译时输出接口,异步加载,适用于浏览器和服务端。
3、可以单独加载其中的某个接口(方法)。
4、静态分析,动态引用,不允许修改。输出的是值的引用。
5、通过 babel 将未被宿主环境直接支持的 ES6 模块 编译为 ES5 的 CommonJS。
babel 本质:将不被支持的 import/export 翻译成已被支持的 require/exports。
3、AMD(异步模块定义)
语法
定义:define 将代码定义为模块
define(['module1'], function(n){
return 模块
})
导入:require 引入需要的模块
require(['module1'], function(n){
使用 n
})
应用
RequireJS
特点
1、异步加载模块,适用于浏览器端,允许指定回调函数。
2、依赖前置,在定义模块的时候就要声明其依赖的模块。
3、不会污染全局环境,能够清楚地显示依赖关系。
4、输出的模块兼容CommonJS。
4、CMD(通用模块定义)
语法
导出:
exports.xxx = value
导入:
var module = require('./module')
//引入依赖模块(异步)
require.async('./module1', function (n) {})
应用
SeaJs
特点
1、异步加载,模块使用时才会加载执行,适用于浏览器端。
2、就近依赖,只有在用到某个模块的时候再去require。
5、UMD
AMD和CommonJS的糅合
实现
1、先判断是否支持Node.js模块(exports是否存在),存在则使用Node.js模块模式。
2、再判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。
3、否则将模块公开到全局(window或global)。
(function (window, factory) {
if (typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define(factory);
} else {
window.eventUtil = factory();
}
})(this, function () {
// module ...
});
require 和 import 区别
1、require 输入的变量是简单赋值,浅拷贝,可以修改;import 输入的变量是只读的,不允许修改,只允许修改对象的属性。
2、require 在运行时才知道加载那个模块;而 import 会将加载的模块提升到整个模块的头部,首先执行,即在编译时执行。
3、require 可以写进表达式和变量中;而 import 不能。
module.exports 和 exports 区别
1、 exports 对象 和 module.exports 对象,指同一个内存空间。
2、 module.exports 对象才是真正的暴露对象,而 exports 对象是 module.exports 对象的引用 ,不能改变指向,只能添加属性和方法,若直接改变 exports 的指向,则会返回空对象。
参考文章:https://www.jianshu.com/p/08ae4f9fa802、https://segmentfault.com/a/1190000017466120、https://developer.51cto.com/art/202103/654008.htm