题目
谈谈模块化的发展历程以及各自的特点。
解析
Javascript不是一种模块化编程语言,但是随着前端的发展,对模块需求越来越大。
如今最常见的模块规范就是CommonJS、AMD、CMD
模块化的作用
- 抽离公共代码,提高代码复用性
- 隔离作用域,避免污染全局作用域
- 避免变量冲突
立即执行函数(IIFE)
这是最早期的模块化手段。
特点:在一个单独的函数作用域中执行代码,避免变量冲突,污染全局作用域
(function(globalVariable) {
globalVariable.test = function() {}
// ... 声明各种变量、函数都不会污染全局变量
}) (globalVariable)
CommonJS
Node中自带的模块化,采用CommonJS模块规范。
- 一个文件就可以是一个模块,使用require来引入、加载第三方模块;
- 每个模块都有一个module对象,代表当前模块。其属性如下:
- module.exports属性是当前模块对外输出的接口,其他文件加载该模块,就是读取module.exports变量。(下面会有例子详解)
CommonJS实例
1、引入第三方模块并调用其方法:这里引用的是http模块。
const http = require('http');
const hostname = '127.0.0.1';
const port = 8080;
// 创建一个服务器对象
const server = http.createServer((req, res) => {
...
})
server.listen(console.log(`运行http://${hostname}:${port}`))
2、CommonJS模块例子
-
创建一个myModule.js文件
//模块定义 myModule.js var name = 'Byron'; function printName(){ console.log(name); } function printFullName(firstName){ console.log(firstName + name); } module.exports = { printName: printName, printFullName: printFullName } console.log(module);
执行命令node myModule.js,可以看到模块自带的module对象:
-
再创建一个mk.js
//加载模块 var myModule = require('./myModule.js'); myModule.printName();
这里的myModule就是对myModule.js文件中的module.exports对象的引用。
-
执行命令 node mk.js,输出结果:Byron
CommonJS特点
- 主要用作服务端(node应用主要用于服务端)
- 模块加载时同步的,即资源加载完在执行。(模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用)
- 导出的值都是拷贝。就算导出的值变了,导入的值也不变。
AMD
AMD诞生原因:
因CommonJS是同步加载模块,对于服务端是友好的,但对于浏览器端是不能使用“同步加载”的。以免出现浏览器“假死”现象,所有有了AMD的诞生。
AMD规范(require.js使用的规范):采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
-
AMD要想定义一个模块,必须使用define方法定义模块。
如果该模块不依赖其他模块:
// 无依赖时,定义模块 define(function(){ ... } return { ... } )
如果该模块依赖其他模块:
// 依赖其他模块 // 第一个参数为数组,要指明其依赖 define(['module1','module2'], function(m1, m2) { ... } return { ... } )
上面的例子要想定义myModule.js模块就要像下面这样:
// 定义模块myModule.js define( function() { var name = 'Byron'; function printName(){ console.log(name); } return { printName: printName } })
-
AMD也采用require加载模块,但与CommonJS不同的是,require有两个参数:
require([module], callback);
第一个参数[module]是一个数组:它表示要加载的模块
第二个参数callback:是一个回调函数,当模块加载完之后,所有依赖加载模块的语句就会在这个回调函数中执行。
要想加载上面的myModule.js模块就像下面一样:
// 加载myModule.js 模块 require(['myModule'], function(myModule) { myModule.printName(); })
AMD特点:
- 是Require.js的规范
- 主要用于浏览器端
- 必须使用define方法定义模块
- 先定义所有的依赖,再加载完成后的回调函数中执行
- 模块加载是异步的
CMD
CMD规范(Sea.js使用的规范)。同样用于浏览器端,也是异步加载模块。
与AMD不同的是,CDM是就近依赖,想用哪个模块时就加载哪个模块;而AMD 在定义模块的时候就要声明其依赖的模块。
-
CMD模块的定义:使用define方法定义模块
define(function(require, exports, module){ // ... // 可以在函数体内部任意地方使用require加载其所依赖的模块 })
第一个参数require: 可用来加载该模块所依赖的模块
第二个参数exports与第三个参数module:都是作为暴露接口实例: 使用sea.js创建一个模块:myModule.js(关于sea.js这里不详说)
define(function(require, exports) { // 对外提供name属性 exports.name = 'myModule'; // 对外提供hello方法 exports.hello = function() { console.log('Hello myModule'); }; });
-
CMD模块的加载:
创建一个html页面,其内部加载并调用myModule.js这个模块<!DOCTYPE html> <html> <head> <script type="text/javascript" src="sea.js"></script> <script type="text/javascript"> //加载一个模块,在加载完成时,执行回调 seajs.use('myModule', function(a) { console.log(a.name); a.hello(); }); </script> </head> <body> </body> </html>
CMD的特点
从上面的例子中,我们可以看到CMD整合了AMD与CommonJS的特点 ,与AMD的模块定义和加载机制有些不同,但两者都用于浏览器。下面说说它的特点。
- 是Sea.js的规范
- 异步加载模块
- 用于浏览器
- 与AMD的区别:模块定义方式与加载模块机制不同。
- CMD整合了AMD与CommonJS的特点
ES6模块
这个我们应该不陌生把,ES6实现了模块功能,完全可以取代CommonJS和AMD的规范,是浏览器与服务器通用的模块解决方式。
-
采用import 导入模块
-
采用export default 或者 export 导出模块
// 引入模块 API import XXX from './a.js' import { XXX } from './a.js' // 导出模块 API export function a() {} export default function() {}
ES6模块特点
- 浏览器与服务器通用
- 异步导入
- es6导出的是一个值的引用。导入值会跟随导出值变化
- 该模块所依赖的模块都要先用import声明在顶部
最后附上一张超级经典的模块化发展的图,看完上面的讲解,下面的图就很清晰了。
详图点击 ->模块化
参考链接: