什么是模块化呢?
事实上模块化开发最终的目的是将程序划分成一个个小的结构;
这个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的结构;
这个结构可以将自己希望暴露的变量、函数、对象等导出给其结构使用;
也可以通过某种方式,导入另外结构中的变量、函数、对象等;
上面说提到的结构,就是模块;按照这种结构划分开发程序的过程,就是模块化开发的过程
无论你多么喜欢JavaScript,以及它现在发展的有多好,我们都需要承认在Brendan Eich用了10天写出JavaScript的时候,
它都有很多的缺陷:
比如var定义的变量作用域问题;
比如JavaScript的面向对象并不能像常规面向对象语言一样使用class;
比如JavaScript没有模块化的问题;
所以 js 并不是一个完美的语言,他需要被优化,需要模块化来帮助开发者解决问题
于是利用函数调用表达式实现简单的模块化,即:
IIFE (Immediately Invoked Function Expression)
// aaa.js
var aaa = (function(){
var name = "123"
return {
name
}
})
// bbb.js
console.log(aaa.name) // 123
通过这种方式实现了简单的模块化,但是依然存在着诸多问题,比如模块变量名冲突,比如需要在入口文件将所有的js文件全部引入,不能做到按需引入,所以就需要一个规范,JavaScript社区为了解决上面的问题,涌现出一系列好用的规范,如 commonJs
commonJs
我们需要知道CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来为了体现它的广泛性,修改为CommonJS,平时我们也会简称为CJS。
- Node是CommonJS在服务器端一个具有代表性的实现;
- Browserify是CommonJS在浏览器中的一种实现;
- webpack打包工具具备对CommonJS的支持和转换;
所以,Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以方便的进行模块化开发
- 在Node中每一个js文件都是一个单独的模块;
- 这个模块中包括CommonJS规范的核心变量:exports、module.exports、require;
- CommonJS仅支持在服务端使用;
require("") 是同步加载,代码执行中需要先加载require中的包,再去执行后面的代码,因为服务端读取的是本地的资源,所以同步加载的影响并不明显,如果在前端使用的话,加载异步模块就会耗费大量加载时间,造成后面代码无法执行,进而系统卡死;所以为了解能在前端也使用这个规范,AMD 应运而生,
AMD
CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
AMD也采用require()语句加载模块,但是不同于CommonJS,这里加载是异步的,不会阻塞后面的代码执行,加载成功之后的回调函数中执行相应的逻辑,这样,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范,require.js和curl.js。
- 实现js文件的异步加载,避免网页失去响应;
- 管理模块之间的依赖性,便于代码的编写和维护。
define(['aaa','bbb'],function(aaa,bbb){...});
AMD 特点
对于依赖的模块,AMD推崇依赖前置,提前执行。也就是说,在define方法里传入的依赖模块(数组),会在一开始就下载并执行,也称之为运行时执行;
CMD
CMD规范,全称”Common Module Definition”,称为 通用模块加载规范 。一般也是用在浏览器端。浏览器端异步加载库 Sea.js 实现的就是CMD规范。
AMD/CMD区别,虽然都是并行加载js文件,但还是有所区别,AMD是预加载,在并行加载js文件同时,还会解析执行该模块(因为还需要执行,所以在加载某个模块前,这个模块的依赖模块需要先加载完成);而CMD是懒加载,虽然会一开始就并行加载js文件,但是不会执行,而是在需要的时候才执行。
CMD 特点
对于依赖的模块,CMD推崇依赖就近,延迟执行。也就是说,只有到require时依赖模块才执行。
ES Module
JavaScript没有模块化一直是它的痛点,所以才会产生我们前面学习的社区规范:CommonJS、AMD、CMD等,所以在ES推出了自己的模块化系统ES Module。
ES Module和CommonJS的模块化有一些不同之处:一方面它使用了import和export关键字;另一方面它采用编译期的静态分析,并且也加入了动态引用的方式;
- ES Module模块采用export和import关键字来实现模块化:
- export负责将模块内的内容导出;
- import负责从其他模块导入内容;
- 采用ES Module将自动采用严格模式:use strict;
如果你不熟悉严格模式可以简单看一下MDN上的解析;
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
ES Module 与 CommonJS特点比较
CommonJS模块是运行时加载,ES6 Module是编译时输出接口;
CommonJS加载的是整个模块,将所有的接口全部加载进来,ES6 Module可以单独加载其中的某个接口;
CommonJS输出是值的拷贝,ES6 Module输出的是值的引用,被输出模块的内部的改变会影响引用的改变;
CommonJS this指向当前模块,ES6 Module this指向undefined;