1.概述
什么是模块化?
模块化就是把系统分离成独立功能个方法,需要什么功能,就加载什么功能;
优点:
- 解决传统编码过程中的命名冲突和文件依赖的问题;
- 提高代码的可维护性,可复用性以及提高生产效率。
目前实现模块化的规范主要有:
- CommentJs
- AMD (RequireJS)
- CMD (SeaJS)
- ESModule
CommonJs
- CommentJs主要用于服务器端;
- CommentJs加载模块是同步的,也就是说,加载完成之后执行后面的操作;
- commonJs使用基于文件的模块,所以一每个文件只能定义一个模块;
Node.js主要用于服务端编程,模块都是存储在本地的硬盘中加载比较快,所以Node.js采用的是CommentJs的规范;
CommentJs规范分为三个部分:
module(模块标识):module变量在每个模块的内部,就代表当前模块require(模块引用):require()用来加载外部模块,读取并执行js文件,返回该模块的exports对象;exports(模块输出):exports是对外的接口,用于导出当前模块的变量和方法;
定义一个CommonJs的模块
// aModule.js
const $ = require("jQuery"); // 用于其他的模块
let count = 1;
const numClick = function(){
alert(++count)
}
// 使用module.exports 定义模块公共接口
module.exports = {
countClick:function(){
$(document).on("click",numClick )
}
}
CommonJs 加载模块
// 使用我们写的module
const aModule = require("aModule"); //引入模块
// 使用定义的公共接口
aModule.countClick();
我们需要注意的是:虽然我们的在
aModule.js中定义的 "全局变量"$ , count, numClick;在我们的标准js文件中会生成全局变量,但是在Commonjs中它的作用域只存在于当前的模块;
AMD(Asynchronous Module Definition)
AMD就是异步模块定义。它采用异步的方法加载模块,通过define方法去定义模块,require方法去加载模块 ;
AMD的优缺点
AMD 运行时核心思想是「Early Executing」,也就是提前执行依赖 AMD 的这个特性有好有坏:
-
首先,尽早执行依赖可以尽早发现错误。
-
另外,尽早执行依赖通常可以带来更好的用户体验,也容易产生浪费。
-
引用AMD的Javscript库:目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js
-
在浏览器环境中异步加载模块;并行加载多个模块;
-
开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅;不符合通用的模块化思维方式,是一种妥协的实现。
AMD模块定义
如果这个模块还依赖于其他模块,那么define函数的第一个参数,必须是一个数组,指明该模块的依赖;
define(["tool"],function(){
// ... 模块逻辑
})
AMD模块的加载
require(['modules'], callback);
第一个参数 ['modules'] ,是一个数组,里面的成员就是要加载的模块; 第二个参数
callback,则是加载成功之后的回调函数。
require异步加载模块,浏览器在加载过程中不会失去响应;指定的回调函数,只有在前面的模块加载成功后,才会去执行,解决了依赖性的问题。
// a.js 定义a 模块 依赖于b模块
define(["b"],function(){
console.log(b)
var hello = function(){
console.log("hello")
}
return {
hello:hello
}
})
// b.js 定义b模块
define(function(){
var name = "max";
return {
name : name
}
})
// demo.html
<script>
require.config({ // 配置路径别名
path : {
'a':'./a',
'b':'./b'
}
})
require(['a','b'],function(a,b){ // 引入a,b模块
console.log(a); // {hello:function(){}}
console.log(b); // {name:'max'}
})
</script>
CMD(Common Module Definition)
CMD 即通用模块定义,CMD规范是国内发展过来的;
Sea.js和 require.js解决的问题是一样的,只不过在模块的定义方法和加载方法不同。
CMD推崇就近依赖,延迟执行。文件是提前加载好的,只有在require的时候才去执行文件;
在CMD规范中,一个模块就是一个文件。
require是可以把其他模块导进来的一个参数;exports可以把模块内的一些属性和方法导出;module是一个对象,上面储存了与当前模块相关联的一下属性和方法;
define(function(require,exports,module){
// 模块代码
})
// a.js
define(function(require,exports,module){
export.name = "max"
})
// b.js
define(function(require,exports,module){
var a = require('./a');
export.hello = function(){
console.log("hello")
}
})
<!-- demo.html -->
<script>
seajs.config({
alias:{
a:'./a',
b:'./b'
}
})
seajs.use(['a','b'],function(a,b){
console.log(a);
console.log(b);
})
</script>
ES6模块化
ES6汲取了 Commonjs 和 AMD 的优点,语法简洁,并且基于文件。支持异步加载,可以成为浏览器和服务端通用的模块化解决方案;
ES6的主要思想是必须显式的使用标识符导出模块,才能从外部访问模块。其他的标识符,甚至在顶级的顶级作用域中定义,也只能在模块内使用;(与CommonJs一样)
ES6提供了两个标识符
import:导入模块标识符export:从模块外部指定标识符(用于把模块中的内容暴露出来)
ES6的导出模式分以下几种
- 1.使用关键字
export分被导出定义的变量和函数
export const name = 'name';
export function compare(){};
export class Car()
- 2.使用
export将所有的模块标识符全部导出
const name = 'name';
function compare(){};
class Car();
export { name, compare }
- 3.使用
expport default关键字定义模块的默认导出
export default class Car()
- 4.使用默认导出的同时还可以导出指定的名称
export const name = "name"
export default class Car();
- 5.通过关键字
as设置别名
const name = "name"
export { name as myname }
ES6的引入模式
- 1.导入默认的导出
import car from 'aModule.js'
- 2.导入命名导出
import { name, compare } from 'aModule.js'
- 3.导入模块中声明的全部导出内容
import * as aModule from 'aModule.js'
- 4.通过别名导入模块中声明的全部导出内容
import {name as myname} from 'aModule.js'
- 5.导入默认导出和命名导出
import car,{name,compare} from 'aModule.js'
export 与 export default 的区别
- 导入模块默认导出的内容,不需要使用花括号{},可以任意指定名称
- 导入已命名的导出内容必须使用花括号,且名称需要对应
ES6 模块与 CommonJS 模块的差异
它们有两个重大差异:
CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用。CommonJS模块是运行时加载,ES6模块是编译时输出接口.