前言
最开始的模块化开发:把每个模块代码写在不同的文件中,最后在页面中分别导入
- 需要自己分析出相关的依赖,规划出导入的先后顺序(麻烦)即使基于geunt/gulp/webpack等打包工具,也需要知道依赖关系,按照依赖顺序打包'
- 如果不基于闭包把每一个模块私有化处理,最后合并在一起的时候,容易引发全局变量污染,
所有设计模式都是一种思想,这种思想解决了某些问题
单例设计模式
解决私有化: 自执行函数,产生闭包
解决模块之间的相互访问:把需要供外面访问的内容,暴露到全局上windowxxx=xxx,使用这种方式需要暴露更多的方法的时候,也可能会导致变量全局冲突
把模块中需要暴露的属性和方法,放在一个对象中管理,最后基于模块名存储这个对象即可
let xModule=(function(){
...
return{
//包含里需要暴露给外面的属性和方法
fn,
}
})()
访问:xModule.fn()
总结:这种处理方案,即保证了代码的私有化,也支持了模块间的相互访问,而且避免了全局变量的污染..我们把这种代码的设计方法称之为"单例设计模式"
AMD按需导入
AMD模块化思想 - require.js,导入;require , define:定义模块
网址:RequireJS
AMD设计思想:在单例设计模式基础上,有效的解决了模块之间的依赖问题,告别之前“手动一点点分析依赖,按照顺序依次导入”的问题了;而且可以结合gulp/grunt等,最后把各个模块代码合并压缩打包在一起... , 但是依赖的模块都需要“前置导入”
用法
入口:mian.js
//全局配置
require.config({
baseUrl: 'js/lib',
});
// 导入指定模块,然后处理相关的内容
require(['B', 'A'], function (B, A) {
console.log(A.sum(10, 20, 30, 40, 50));
console.log(B.average(10, 20, 30, 40, 50));
});
模块中
- // define:定义模块
- // AMD思想的优势:定义模块的时候,可以把依赖的模块“前置导入”
- // 回调函数中基于AModule接收导入的A模块内容(A模块中返回的对象)
define(['A'], function (AModule) {
let name = "lisa";
const average = function average(...params) {
let len = params.length,
total = AModule.sum(...params);
if (len === 0) return 0;
return (total / len).toFixed(2);
};
return {
average
};
});
CommonJS模块规范
CommonJS模块规范「模块的导入和导出」:Node自带的模块规范(浏览器端不支持)
定义模块:创建的每一个JS文件,就是定义一个单独的模块,每个模块之间的代码本身就是私有的
导入:require 导出 module.exports
导出模块中的方法:
module.exports = {
//包含需要供外部调用的属性和方法
};
导入指定的模块:
const x = require('模块地址,导入自己的模块需要加“./”');
基于x接收导出的对象,后期基于 x.xxx 即可访问!!
CommonJS模块的导入是“按需”的,随时用随时导入即可,不像AMD都需要前置处理!!,比AMD用起来跟简单,性能好一些
CMD
CommonJS不支持浏览器,所以淘宝玉伯写了一个插件 sea.js(把其定义为cmd模块规范)
本质把 CommonJS规范搬到浏览器运行
es6Module,出现以后sea代表的CMD被pass掉了
es6模块规范
在浏览器端开启ES6Module规范
- + type="module"
- + 基于标准的http/https协议的web服务预览页面
定义模块:和CommonJS类似,创建一个JS就相当于创建一个模块
导出/导入模块:
导出
- export 声明变量且赋值;
- export default 值; -> 在一个模块中只能使用一次 ,每个模块导出一个Module对象
导入: import x from '模块地址'
- -> 浏览器端直接使用,地址中模块的后缀不能省略
- -> 只能接收到基于 export default 导出的这个值
- -> 原理:找到导出Module对象中的default属性值,把属性值赋值给x变量
- -> 但是不能在这直接给x解构赋值 ,例如:import {n,m} from '模块地址'; 这样是不能给default后面的值解构赋值;需要解构赋值,则先基于x接收,然后再给x解构赋值即可;例如:const {n,m}=x;
导入: import * as x from '模块地址';
- 把当前模块导出的所有内容获取到,赋值给x变量,后期基于 x.xxx 访问即可「含: x.default 获取export default导出的值」
导入: import { num, obj } from '模块地址';
- 直接结构赋值,是把模块导出的Module中所有内容(不含default)进行解构赋值
注意:
- import需要放在模块代码的最上面编写,有点类似于前置导入
- export default 只能导出一个(Module对象),import导入时会找到导出Module对象中的default属性值,把属性值赋值给x变量
- export 可以导出多次 ,可以结构赋值拿到,或者import * as x from '模块地址';,导出全部赋值给x
总结
单例设计模式是“最早期的模块规范”,在没有CommonJS/ES6Module模块规范的时代,帮助我们实现了模块化开发!AMD(require.js)是在单例设计模式的基础上,实现了模块和模块之间的依赖管理!-----但是上述操作都是过去时了
当代前端开发,都是基于模块化进行开发,而模块化方案以 CommonJS/ES6Module 为主
- + 他们都是按照创建一个JS就是创建一个模块来管理的「每个JS文件中的代码都是私有的」
- + CommonJS:require && module.exports
- + ES6Module:export && import
我们编写的JS代码,可以运行的环境
- @1 浏览器 <script src='...'> 「和其类似的还有webview」:直接支持ES6Module,但是不支持CommonJS 全局对象 window
- @2 NODE 支持CommonJS,但是不支持ES6Module, 全局对象 global
- @3 webpack「基于node实现代码的合并压缩打包、最后把打包的结果导入到浏览器中运行」 CommonJS&ES6Module都支持,而且支持相互之间的“混用”(原理:webpack把两种模块规范都实现了一遍) 支持 window&global
- @4 vite「新的工程化打包工具」+ 不是像webpack一样编译打包的,它本质就是基于ES6Module规范,实现模块之间的相互引用