模块化是一种处理复杂系统分解为更好的可管理模块的方式。简单来说就是解耦,简化开发,一个模块就是实现特定功能的文件,可以更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发需要遵循一定的规范。
常见规范有
- CommonJs
- AMD
- UMD
- CMD
-
CommonJS规范
用在服务器端的node的模块规范,前端的webpack也是对CommonJS原生支持的。
特点:
模块输出的是一个值的拷贝, 模块是运行时加载,同步加载
-
有两个API :
require
: 加载所要依赖的其他模块module.exports
或者exports
: 对外暴露的接口注意
exports 与module.exports 的区别:exports 是对 module.exports 的引用,不能直接给exports 赋值,直接赋值无效,结果是一个空对象,module.exports 可以直接赋值
- . 一个文件不能写多个module.exports ,如果写多个,对外暴露的接口是最后一个module.exports
. 模块如果没有指定使用module.exports 或者exports 对外暴露接口时,在其他文件就引用该模块,得到的是一个空对象{}
AMD
AMD 即 Asynchronous Module Definition,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,所以AMD规范的实现,就是的require.js了
特点
异步加载,不阻塞页面的加载,能并行加载多个模块,但是不能按需加载,必须提前加载所需依赖
两个重要的api
define(id?,[]?,callbakc)
- require([module], callback)
还有一个配置属性API:
- require.config({
注意
- paths 的设置加载优化与shim 中配置(AMD模式优先非AMD模式)
加载非AMD 模式的模块,依赖非AMD模块 ,第三方插件(如jquery .lazyload 插件)这里用到了shim属性配置(加载非AMD 模式)
UMD
其实是amd和commonjs的统一规范,支持两种规范,即写一套代码,可用于多种场景。
毋庸置疑的是,其写法也是最复杂的前后端均通用
- 与CJS或AMD不同,UMD更像是一种配置多个模块系统的模式。
UMD在使用诸如Rollup/ Webpack之类的bundler时通常用作备用模块
(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); } else if (typeof exports === 'object') { // Node, CommonJS之类的 module.exports = factory(require('jquery')); } else { // 浏览器全局变量(root 即 window) root.returnExports = factory(root.jQuery); } }(this, function ($) { // 方法 function myFunc(){}; // 暴露公共方法 return myFunc; }));
CMD
CMD规范是阿里的玉伯提出来的,实现js库为sea.js。 它和requirejs非常类似,即一个js文件就是一个模块,但是CMD的加载方式更加优秀,是通过按需加载的方式,而不是必须在模块开始就加载所有的依赖。
ES6的Module (ESM)
ES6 在语言标准的层面上,实现了模块功能,而且非常简单,ES6到来,完全可以取代 CommonJS 和 AMD规范,成为浏览器和服务器通用的模块解决方案。由vue,Angular React这些mvvm 模式的框架发展,让前端的编程变得模块化,组件化。
特点
ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。
- 自动采用严格模式”use strict”。须遵循严格模式的要求
- ES6 模块的设计思想是尽量的静态化,编译时加载”或者静态加载,编译时输出接口
- ES6 模块export、import命令可以出现在模块的任何位置,但是必须处于模块顶层。如果处于块级作用域内,就会报错
- ES6 模块输出的是值的引用
- 很多浏览器开始支持
- 拥有类似commonjs的写法和同、异步加载机制
- 能通过设置type=module,用于html中
-
两个主要API
export:用于规定模块的对外接口,
- import:用于输入其他模块提供的功能。
注意
- export命令除了输出变量,还可以输出函数或类(class),不同的数据类型
- 使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
- 本质上,export default就是输出一个叫做default的变量或方法 ```javascript // modules.js function add(x, y) { return x * y; } export {add as default}; // 等同于 // export default add;
// app.js import { default as foo } from ‘modules’; // 等同于 // import foo from ‘modules’;
```javascript
// modules.js
function add(x, y) {
return x * y;
}
export {add as default};
// 等同于
// export default add;
// app.js
import { default as foo } from 'modules';
// 等同于
// import foo from 'modules';
CommonJS vs ES6 Module:
- CommonJS支持动态导入,也就是 require(${path}/xx.js),ES6目前不支持
- CommonJS是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。而ES6是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响。
- ES6模块中的值属于【动态只读引用】。只读:import的变量是只读的,不论是基本数据类型还是复杂数据类型,在不能在导入的文件修改,复杂数据类型可以修改属性值不会报错,但是不建议这么做。动态:原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
CommonJS导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,当导出的是复杂数据类型时,属于浅拷贝,由于引用相同,所以修改会互相影响。使用require命令加载某个模块时,就会运行整个模块的代码。当再次使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值,除非手动清除系统缓存。 - ES6编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,在运行时才能确定。
浏览器加载规则:
1、浏览器加载 ES6 模块,也使用