# modularization
**Repository Path**: lay_zhou/modularization
## Basic Information
- **Project Name**: modularization
- **Description**: CMD、AMD、CommonJS、Es6模块 模块化详细解答
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2020-05-20
- **Last Updated**: 2020-12-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 模块化
**模块化就是把系统分离成独立功能的方法,这样我们需要什么功能,就加载什么功能**
- 每个模块都是独立的,良好设计的模块会尽量与外部代码撇清关系,以便于独立对其改进和维护
- 可以重复利用,而不用经常复制自己之前写的代码
## 模块化主要解决两个问题,“命名冲突”、“文件依赖”
**命名冲突**
```javascript
// a.js
var a = 1;
// b.js
var a = 2;
```
**文件依赖**
```javascript
// b.js依赖a.js,标签的书写顺序必须是:
```
这样在很多人开发的时候很难协调啊,令人头疼的问题
### 结局冲突、依赖(按需加载)
**使用命名空间**
这样的写法会暴露所有模块内的成员,内部状态可以被外部改写
```javascript
let module = {
name: 'likang xie',
sayName() {
console.log(this.name);
}
}
```
**立即执行函数+闭包**
函数内部有自己独立的作用域,外部只能访问自己暴露的成员而不能读取内部的私有成员
```javascript
let module = (function () {
let privateName = 'private'; // 私有变量
let privateFn = function () {}; // 私有函数
// 对外暴露的成员
return {
name: 'likang xie', // 公有属性
sayName() { // 公有方法
console.log(this.name);
}
}
})();
// 外部调用
module.sayName(); // likang xie
```
**使用立即执行函数+类**
同上
```javascript
const People = (function () {
let privateName = 'private'; // 私有变量
let fn = function () {}; // 私有方法
return class People {
constructor () {
this.name = 'likang xie'; // 公有变量
}
// 公有方法
sayName() {
console.log(this.name);
}
}
})()
```
## CommonJs(用于Node环境)
根据CommonJs规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见,**CommonJS规范加载模块是同步的,也就是说,加载完成才可以执行后面的操作**,Node.js主要用于服务器编程,模块一般都是存在本地硬盘中,加载比较快,**所以Node.js采用CommonJS规范。**
### 定义模块
```javascript
// module.js
let name = 'liakng xie';
let sayName = function () {
console.log(name);
};
module.exports = { name, sayName }
// 或者
exports.sayName = sayName;
```
### 加载模块
```javascript
// 通过 require 引入依赖
let module = require('./module.js');
module.sayName(); // likang xie
```
### 注意:module.export和exports的区别
1.module.exports方法还可以单独返回一个数据类型(String,Number,Object...),exports**只能返回一个Object对象**
2.所有的exports对象**最终都是通过module.exports传递执行**,因此可以更确切的说,exports是module.exports添加属性和方法
```javascript
exports.name = 'likang xie';
exports.age = 21;
console.log(module.exports); // 运行结果:{ name: 'likang xie', age: 21 }
```
3.同时用到module.exports和exports的时候
```javascript
// 情况1
module.exports = { a: 1 }
exports.b = 2;
// { a:1 }
console.log(module.exports);
// 情况2
module.exports.a =1;
exports.b = 2;
// { a:1, b:2 }
console.log(module.exports);
```
## AMD(用于浏览器环境RequireJs)
AMD是"Asynchronous Module Definition"的简写,也就是异步模块定义。**它采用异步方式加载模块**。通过define方法去定义模块,require方法去加载模块。
**定义模块**
```javascript
define(['module'], function() {
let name = 'likang xie';
function sayName() {
console.log(name);
}
return { sayName }
})
```
**使用模块**
```javascript
// 通过 require 引入依赖
require(['module'], function(mod) {
mod.sayName(); // likang xie
})
```
## CMD(用于浏览器环境)(SeaJs)
CMD与AMD用法很相似
**定义模块、使用模块**
```javascript
define(function(require, exports, module) {
// 通过 require 引入依赖
var otherModule = require('./otherModule');
// 通过 exports 对外提供接口
exports.myModule = function () {};
// 或者通过 module.exports 提供整个接口
module.exports = function () {};
})
```
### 注意:CMD和AMD
#### 不同点
1. **定位有差异**。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
2. **遵循的规范不同**。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
3. **推广理念有差异**。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
4. **对开发调试的支持有差异**。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
5. **插件机制不同**。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。
6. 对于依赖的模块,AMD 是**提前执行**,CMD 是**延迟执行**。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible。
7. CMD 推崇**依赖就近**,AMD 推崇**依赖前置**。看代码:
```javascript
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ...
})
// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
...
})
```
虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。
8. AMD 的 API 默认是**一个当多个用**,CMD 的 API 严格区分,推崇**职责单一**。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都**简单纯粹**。
9. 等等。
#### 相同点
RequireJS 和 Sea.js 都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。
## ES6模块(用于浏览器环境)
模块功能主要由两个命令构成:**export和import**。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
**定义模块、输出变量**
a.js
```javascript
export default { name: 'likang xie' }
```
b.js
```javascript
// 输出多个变量
export let name = 'likang xie';
export let sayName = () => console.log(name);
```
**使用模块**
```javascript
import people from 'a.js';
console.log(people); // { name: 'likang xie' }
```
```javascript
// 将所有获取到的变量存到people里
import * as people from 'b.js';
console.log(people); // 一个module对象 { name: 'likang xie', sayName: ....}
// 或者
import { name, sayName } from 'b.js';
```
### 引用:[ES6在浏览器中使用](https://segmentfault.com/a/1190000014342718)