83、JS模块化:AMD、CMD、CommonJS、ES6 Module的区别

模块化原因:

随着网站逐渐变成”互联网应用程序”,嵌入网页的Javascript代码越来越庞大,越来越复杂。

如果你没有使用模块化系统,那么你只能用这种方式来处理你的模块化代码了。

<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>

每个模块向外暴露一个接口给全局对象,即window对象。模块就可以通过全局对象访问依赖项向外暴露的接口。通常存在的问题:

  • 全局对象中的变量冲突
  • 按需加载的问题。
  • 开发者需要手动解析模块或者库的依赖项。
  • 在特别大的项目中,嵌入网页的Javascript代码越来越庞大,越来越复杂。这个现象会变得越来越严重,越来越难以管理。

 解决方法:

Javascript模块化编程,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。 

1、CommonJS

是一个更偏向于服务器端的规范。

同步加载模块。这对于服务器端不是一个问题,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间(很小)。但是对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态

(1)基本用法

  • 通过require加载模块;
  • 通过exportsmodul.exports暴露模块中的内容;
//a.js  导出
module.exports = function () {
  console.log("hello world")
}

//b.js 导入
var a = require('./a');

a();//"hello world"

或者 

//a2.js  导出
exports.num = 1;
exports.obj = {xx: 2};

//b2.js  导入
var a2 = require('./a2');

console.log(a2);//{ num: 1, obj: { xx: 2 } }

(2)特点

  •  一个文件就是一个模块,拥有单独的作用域;
  • 所有代码都运行在模块作用域,不会污染全局作用域;
  • 模块可以多次加载,但只在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果;
  • 模块是同步加载的,即只有加载完成,才能执行后面的操作
  • CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

2、AMD

CommonJS解决了模块化的问题,但这种同步加载方式并不适合于浏览器端。

AMD是”Asynchronous Module Definition”的缩写,即”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。这里异步指的是不堵塞浏览器其他任务(dom构建,css渲染等),而加载内部是同步的(加载完模块后立即执行回调)。

一般说AMD也是指RequireJS

(1)基本用法

  • 通过define来定义一个模块,并通过return向外暴露
  • 使用require可以导入定义的模块
//a.js 暴露 (参数1:表示a模块还依赖c模块;若无依赖模块,也可省略)
//define可以传入三个参数,分别是字符串-模块名、数组-依赖模块、函数-回调函数
define(['c'],function(){ 
    return 1;
})

// b.js 导入
//数组中声明需要加载的模块,可以是模块名、js文件路径
require(['a'], function(a){
  console.log(a);// 1
});

当require()函数加载a模块的时候,就会先加载c模块。当a模块有多个依赖时,就将所有的依赖都写在define()函数第一个参数数组中,所以说AMD是依赖前置的。这不同于CMD规范,它是依赖就近的。 

(2)特点

依赖前置,提前执行。即define方法里传入的依赖模块(数组),会在一开始就下载并执行

       

更多介绍,可参考https://blog.csdn.net/qq_44701189/article/details/107473506

2、CMD

(1)基本用法

  • 通过define来定义一个模块,并通过return向外暴露
  • 使用seajs.use(导入的模块名,回调函数) 导入定义的模块

1)向外暴露

CMD方式: 在使用过程中提出依赖,就是不管代码写到哪突然发现需要依赖另一个模块,那就在当前代码用require引入就可以了

// CMD 需要引入模块a、b
// c.js
define(function(require, exports, module) {
  var a = require('./a');
  a.doSomething();
  var b = require('./b');
  b.doSomething();
})

 AMD方式:必须提前在头部依赖参数部分写好

// AMD
define(['a', 'b'], function(a, b) {
  a.doSomething();
  b.doSomething();
})

2)通过seajs.use()导入模块

//数组中声明需要加载的模块,可以是模块名、js文件路径
seajs.use(['c'], function(c) {
  $('#el').click(c.setColor);
});

(2)特点

推崇依赖就近,延迟执行。也就是说,只有到require时依赖模块才执行

4、ES6 Module

(1)基本用法

  • 通过importexport实现模块的输入输出
  • import命令用于输入其他模块提供的功能
  • export命令用于规定模块的对外接口

1)方式一:使用export导出 (export和import后需要加{})

//a.js
var name = 'lin';
var age = 13;
var job = 'ninja';

export { name, age, job};

//b.js
import { name, age, job} from './a.js';

console.log(name, age, job);// lin 13 ninja

2)方式二:使用export default导出 (不需要{})

//a2.js
export default function () {
  console.log('default ');
}

//b2.js
import customName from './a2.js';
customName(); // 'default'

 3)注意:

脚本加载了变量a,对其重新赋值就会报错,因为a是一个只读的接口。但是,如果a是一个对象,改写a的属性是允许的。

import {a} from './xxx.js'

a.foo = 'hello'; // 合法操作
a = {}; // Syntax Error : 'a' is read-only;

(2)特点

  1. ES6 模块输出的是值的引用(原始值变了,import加载的值也会跟着变);
  2. ES6 模块是编译时输出接口;
  3. ES6 模块的import命令是异步加载;
  4. ES6 Module可以单独加载其中的某个接口
  5. ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

(3)ES6 Module 与 CommonJS 的差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用(原始值变了,import加载的值也会跟着变)。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
  • CommonJS加载的是整个模块,将所有的接口全部加载进来,ES6 Module可以单独加载其中的某个接口
  • CommonJS this指向当前模块,ES6 Module this指向undefined;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值