前端工程师必备:前端的模块化

42次阅读

共计 14361 个字符,预计需要花费 36 分钟才能阅读完成。

JS 模块化
模块化的理解

什么是模块?

将一个复杂的程序依据一定的规则 (规范) 封装成几个块(文件), 并进行组合在一起;
块的内部数据 / 实现是私有的, 只是向外部暴露一些接口 (方法) 与外部其它模块通信;

一个模块的组成

数据 —> 内部的属性;
操作数据的行为 —> 内部的函数;

模块化是指解决一个复杂的问题时自顶向下把系统划分成若干模块的过程,有多种属性,分别反映其内部特性;
模块化编码:编码时是按照模块一个一个编码的, 整个项目就是一个模块化的项目;

非模块化的问题
页面加载多个 js 的问题:
<script type=”text/javascript” src=”module1.js”></script>
<script type=”text/javascript” src=”module2.js”></script>
<script type=”text/javascript” src=”module3.js”></script>
<script type=”text/javascript” src=”module4.js”></script>

发生问题:

难以维护;
依赖模糊;
请求过多;

所以,这些问题可以通过现代模块化编码和项目构建来解决;

模块化的优点

更好地分离:避免一个页面中放置多个 script 标签,而只需加载一个需要的整体模块即可,这样对于 HTML 和 JavaScript 分离很有好处;
更好的代码组织方式:有利于后期更好的维护代码;
按需加载:提高使用性能,和下载速度,按需求加载需要的模块
避免命名冲突:JavaScript 本身是没有命名空间,经常会有命名冲突,模块化就能使模块内的任何形式的命名都不会再和其他模块有冲突。
更好的依赖处理:使用模块化,只需要在模块内部申明好依赖的就行,增加删除都直接修改模块即可,在调用的时候也不用管该模块依赖了哪些其他模块。

模块化的发展历程
原始写法
只是把不同的函数简单地放在一起,就算一个模块;
function fun1(){
//…
}
function fun2(){
//…
}
// 上面的函数 fun1,fun2 组成了一个模块,使用的时候直接调用某个函数就行了。

缺点:

“ 污染 ” 了全局变量,无法保证不与其他模块发生变量名冲突;
模块成员之间看不出直接关系。

对象写法
为了解决污染全局变量的问题,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。
var module1 = new Object({
count : 0,
fun1 : function (){
//…
},
fun2 : function (){
//…
}
});
// 这个里面的 fun1 和 fun2 都封装在一个赌侠宁里,可以通过对象. 方法的形式进行调用;
module1.fun1();

优点:
减少了全局上的变量数目;

缺点:
本质是对象,而这个对象会暴露所有模块成员,内部状态可以被外部改写。

立即执行函数(IIFE 模式)

避免暴露私有成员,所以使用立即执行函数(自调函数,IIFE);
作用: 数据是私有的, 外部只能通过暴露的方法操作

var module1 = (function(){
var count = 0;
var fun1 = function(){
//…
}
var fun2 = function(){
//…
}
// 将想要暴露的内容放置到一个对象中,通过 return 返回到全局作用域。
return{
fun1:fun1,
fun2:fun2
}
})()
// 这样的话只能在全局作用域中读到 fun1 和 fun2,但是读不到变量 count,也修改不了了。
// 问题:当前这个模块依赖另一个模块怎么办?
IIFE 的增强(引入依赖)

如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用 ” 增强模式 ”;
IIFE 模式增强:引入依赖;
这就是现代模块实现的基石;

var module1 = (function (mod){
mod.fun3 = function () {
//…
};
return mod;
})(module1);
// 为 module1 模块添加了一个新方法 fun3(),然后返回新的 module1 模块。

// 引入 jquery 到项目中;
var Module = (function($){
var _$body = $(“body”); // we can use jQuery now!
var foo = function(){
console.log(_$body); // 特权方法
}
// Revelation Pattern
return {
foo: foo
}
})(jQuery)
Module.foo();
js 模块化需要解决那些问题:

1. 如何安全的包装一个模块的代码?(不污染模块外的任何代码)
2. 如何唯一标识一个模块?
3. 如何优雅的把模块的 API 暴漏出去?(不能增加全局变量)
4. 如何方便的使用所依赖的模块?

模块化规范

Node: 服务器端
Browserify : 浏览器端

CommonJS:服务器端

概述

Node 应用由模块组成,采用 CommonJS 模块规范。
CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。

特点

所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
模块加载的顺序,按照其在代码中出现的顺序。

基本语法:
定义暴露模块 : exports
exports.xxx = value
// 通过 module.exports 指定暴露的对象 value
module.exports = value
引入模块 : require
var module = require(‘ 模块相对路径 ’)

引入模块发生在什么时候?

Node:运行时, 动态同步引入;
Browserify:在运行前对模块进行编译 / 转译 / 打包的处理(已经将依赖的模块包含进来了), 运行的是打包生成的 js, 运行时不需要再从远程引入依赖模块;

CommonJS 通用的模块规范(同步)

Node 内部提供一个 Module 构建函数。所有模块都是 Module 的实例。
每个模块内部,都有一个 module 对象,代表当前模块。
module.exports 属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取 module.exports 变量。
Node 为每个模块提供一个 exports 变量,指向 module.exports。
如果一个模块的对外接口,就是一个单一的值,不能使用 exports 输出,只能使用 module.exports 输出。

Modules/1.0 规范包含内容:

模块的标识应遵循的规则(书写规范)
定义全局函数 require,通过传入模块标识来引入其他模块,执行的结果即为模块暴露出来的 API;
如果被 require 函数引入的模块中也包含依赖,那么依次加载这些依赖;
如果引入模块失败,那么 require 函数应该报一个异常;
模块通过变量 exports 来向外暴露 API,exports 赋值暴露的只能是一个对象 exports = {Obj},暴露的 API 须作为此对象的属性。exports 本质是引入了 module.exports 的对象。不能直接将 exports 变量指向一个值,因为这样等于切断了 exports 与 module.exports 的联系。
如果暴露的不是变量 exports,而是 module.exports。module 变量代表当前模块,这个变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。exports=module.exports={Obj}

node 中的 commonJS 教程

1. 安装 node.js;
2. 创建项目结构

// 结构如下
|-modules
|-module1.js// 待引入模块 1
|-module2.js// 待引入模块 2
|-module3.js// 待引入模块 3
|-app.js// 主模块
|-package.json
{
“name”: “commonjsnode”,
“version”: “1.0.0”
}

3. 下载第三方模块:举例 express
npm i express –save

4. 模块化编码

// module1
// 使用 module.exports = value 向外暴露一个对象
module.exports = {
name: ‘this is module1’,
foo(){
console.log(‘module1 foo()’);
}
}
// module2
// 使用 module.exports = value 向外暴露一个函数
module.exports = function () {
console.log(‘module2()’);
}
// module3
// 使用 exports.xxx = value 向外暴露一个对象
exports.foo = function () {
console.log(‘module3 foo()’);
};
exports.bar = function () {
console.log(‘module3 bar()’);
};
exports.name = ‘this is module3’

//app.js 文件
var uniq = require(‘uniq’);
// 引用模块
let module1 = require(‘./modules/module1’);
let module2 = require(‘./modules/module2’);
let module3 = require(‘./modules/module3’);
// 使用模块
module1.foo();
module2();
module3.foo();
module3.bar();
module3.name;

5. 通过 node 运行 app.js

命令:node.app.js
工具:右键 –> 运行

浏览器中的 commonJS 教程

借助 Browserify

步骤
创建项目结构
|-js
|-dist // 打包生成文件的目录
|-src // 源码所在的目录
|-module1.js
|-module2.js
|-module3.js
|-app.js // 应用主源文件
|-index.html // 浏览器上的页面
|-package.json
{
“name”: “browserify-test”,
“version”: “1.0.0”
}

下载 browserify

全局: npm install browserify -g
局部: npm install browserify –save-dev

定义模块代码:index.html 文件要运行在浏览器上,需要借助 browserify 将 app.js 文件打包编译,如果直接在 index.html 引入 app.js 就会报错。
打包处理 js:根目录下运行 browserify js/src/app.js -o js/dist/bundle.js

页面使用引入:

<script type=”text/javascript” src=”js/dist/bundle.js”></script>

AMD : 浏览器端

CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
AMD 规范则是非同步加载模块,允许指定回调函数,可以实现异步加载依赖模块,并且会提前加载;
由于 Node.js 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以 CommonJS 规范比较适用。
如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用 AMD 规范。

语法
AMD 规范基本语法

定义暴露模块: define([依赖模块名], function(){return 模块对象})

引入模块: require([‘ 模块 1 ’, ‘ 模块 2 ’, ‘ 模块 3 ’], function(m1, m2){// 使用模块对象})

兼容 CommonJS 规范的输出模块
define(function (require, exports, module) {
var reqModule = require(“./someModule”);
requModule.test();
exports.asplode = function () {
//someing
}
});
AMD:异步模块定义规范(预加载)

AMD 规范:https://github.com/amdjs/amdj…

AMD 是 ”Asynchronous Module Definition” 的缩写,意思就是 ” 异步模块定义 ”。
它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

AMD 也采用 require()语句加载模块,但是不同于 CommonJS,它要求两个参数:
require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;
第二个参数 callback,则是加载成功之后的回调函数。

目前,主要有两个 Javascript 库实现了 AMD 规范:RequireJS 和 curl.js。

RequireJS

优点

实现 js 文件的异步加载,避免网页失去响应;
管理模块之间的依赖性,便于代码的编写和维护。

require.js 使用教程

下载 require.js, 并引入

官网: https://requirejs.org/

中文:https://blog.csdn.net/sanxian…

github : https://github.com/requirejs/…

将 require.js 导入项目: js/libs/require.js

创建项目结构

|-js
|-libs
|-require.js // 引入的 require.js
|-modules
|-alerter.js
|-dataService.js
|-main.js
|-index.html

定义 require.js 的模块代码

require.js 加载的模块,采用 AMD 规范。也就是说,模块必须按照 AMD 的规定来写。

具体来说,就是模块必须采用特定的 define()函数来定义;
如果一个模块不依赖其他模块,那么可以直接定义在 define()函数之中。

define([‘myLib’], function(myLib){
function foo(){
myLib.doSomething();
}
// 暴露模块
return {foo : foo};
});
// 当 require()函数加载上面这个模块的时候,就会先加载 myLib.js 文件。
– 如果这个模块还依赖其他模块,那么 define()函数的第一个参数,必须是一个数组,指明该模块的依赖性;

“`
// dataService.js
define(function () {
let msg = ‘this is dataService’
function getMsg() {
return msg.toUpperCase()
}
return {getMsg}
})

// alerter.js
define([‘dataService’, ‘jquery’], function (dataService, $) {
let name = ‘Tom2’
function showMsg() {
$(‘body’).css(‘background’, ‘gray’)
alert(dataService.getMsg() + ‘, ‘ + name)
}
return {showMsg}
})
“`

应用主(入口)js: main.js
使用 require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块 main.js 的头部,参数就是一个对象,这个对象的 paths 属性指定各个模块的加载路径。
(function () {
// 配置
require.config({
// 基本路径
baseUrl: “js/”,
// 模块标识名与模块路径映射
paths: {
“alerter”: “modules/alerter”,// 此处不能写成 alerter.js, 会报错
“dataService”: “modules/dataService”,
}
})

// 引入使用模块
require([‘alerter’], function(alerter) {
alerter.showMsg()
})
})()

页面使用模块:

<script data-main=”js/main” src=”js/libs/require.js”></script>
定义模块

require.config()接受一个配置对象,这个对象除了有前面说过的 paths 属性之外,还有一个 shim 属性,专门用来配置不兼容的模块。

具体来说,每个模块要定义:

1、exports 值(输出的变量名),表明这个模块外部调用时的名称;
2、deps 数组,表明该模块的依赖性。

支持的配置项:

baseUrl:所有模块的查找根路径。

当加载纯.js 文件(依赖字串以 / 开头,或者以.js 结尾,或者含有协议),不会使用 baseUrl。
如未显式设置 baseUrl,则默认值是加载 require.js 的 HTML 所处的位置。如果用了 data-main 属性,则该路径就变成 baseUrl。
baseUrl 可跟 require.js 页面处于不同的域下,RequireJS 脚本的加载是跨域的。唯一的限制是使用 text! plugins 加载文本内容时,这些路径应跟页面同域,至少在开发时应这样。优化工具会将 text! plugin 资源内联,因此在使用优化工具之后你可以使用跨域引用 text! plugin 资源的那些资源。

paths:path 映射那些不直接放置于 baseUrl 下的模块名。

设置 path 时起始位置是相对于 baseUrl 的,除非该 path 设置以 ”/” 开头或含有 URL 协议(如 http:)。
用于模块名的 path 不应含有.js 后缀,因为一个 path 有可能映射到一个目录。路径解析机制会自动在映射模块名到 path 时添加上.js 后缀。在文本模版之类的场景中使用 require.toUrl()时它也会添加合适的后缀。
在浏览器中运行时,可指定路径的备选(fallbacks),以实现诸如首先指定了从 CDN 中加载,一旦 CDN 加载失败则从本地位置中加载这类的机制;

shim: 为那些没有使用 define()来声明依赖关系、设置模块的 ” 浏览器全局变量注入 ” 型脚本做依赖和导出配置。

使用第三方基于 require.js 的框架(jquery)

将 jquery 的库文件导入到项目: js/libs/jquery-1.10.1.js

在 main.js 中配置 jquery 路径

paths: {
‘jquery’: ‘libs/jquery-1.10.1’
}
在 alerter.js 中使用 jquery
define([‘dataService’, ‘jquery’], function (dataService, \$) {
var name = ‘xfzhang’
function showMsg() {
$(‘body’).css({background : ‘red’})
alert(name + ‘ ‘+dataService.getMsg())
}
return {showMsg}
})
使用第三方不基于 require.js 的框架(angular)

将 angular.js 导入项目:js/libs/angular.js
流行的函数库(比如 jQuery)符合 AMD 规范,更多的库并不符合。这样的模块在用 require()加载之前,要先用 require.config()方法,定义它们的一些特征。

// main.js 中配置
(function () {
// 配置
require.config({
// 基本路径
baseUrl: “js/”,
// 模块标识名与模块路径映射
paths: {
// 第三方库作为模块
‘jquery’ : ‘./libs/jquery-1.10.1’,
‘angular’ : ‘./libs/angular’,
// 自定义模块
“alerter”: “./modules/alerter”,
“dataService”: “./modules/dataService”
},
/*
配置不兼容 AMD 的模块
exports : 指定与相对应的模块名对应的模块对象
*/
shim: {
‘angular’ : {
exports : ‘angular’
}
}
})
// 引入使用模块
require([‘alerter’, ‘angular’], function(alerter, angular) {
alerter.showMsg()
console.log(angular);
})
})()
CMD : 浏览器端

CMD 规范:https://github.com/seajs/seaj…

CMD 规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。
CMD 规范整合了 CommonJS 和 AMD 规范的特点。
在 Sea.js 中,所有 JavaScript 模块都遵循 CMD 模块定义规范

基本语法

定义暴露模块:
// 没有依赖的模块
define(function(require, module, exports){
let value = ‘xxx’;
// 通过 require 引入依赖模块
// 通过 module.exports/exports 来暴露模块
exports.xxx = value
module.exports = value
})
// 有依赖的模块
define(function(require, exports, module){
// 引入依赖模块(同步)
var module2 = require(‘./module2’)
// 引入依赖模块(异步)
require.async(‘./module3’, function (m3) {
……
})
// 暴露模块
exports.xxx = value
})

使用模块 seajs.use([‘ 模块 1 ’, ‘ 模块 2 ’])

sea.js 简单使用教程

下载 sea.js, 并引入

官网: http://seajs.org/

github : https://github.com/seajs/seajs

将 sea.js 导入项目: js/libs/sea.js

如何定义导出模块 :

define()
exports
module.exports

如何依赖模块:require()

如何使用模块:seajs.use()

创建项目结构

|-js
|-libs
|-sea.js
|-modules
|-module1.js
|-module2.js
|-module3.js
|-module4.js
|-main.js
|-index.html

定义 sea.js 的模块代码

module1.js
define(function (require, exports, module) {
// 内部变量数据
var data = ‘this is module1’
// 内部函数
function show() {
console.log(‘module1 show() ‘ + data)
}
// 向外暴露
exports.show = show
})

module2.js
define(function (require, exports, module) {
module.exports = {
msg: ‘I Will Back’
}
})

module3.js
define(function (require, exports, module) {
const API_KEY = ‘abc123’
exports.API_KEY = API_KEY
})

module4.js
define(function (require, exports, module) {
// 引入依赖模块(同步)
var module2 = require(‘./module2’);
function show() {
console.log(‘module4 show() ‘ + module2.msg)
}
exports.show = show
// 引入依赖模块(异步)
require.async(‘./module3’, function (m3) {
console.log(‘ 异步引入依赖模块 3 ‘ + m3.API_KEY)
})
})

main.js : 主 (入口) 模块

define(function (require) {
var m1 = require(‘./module1’)
var m4 = require(‘./module4’)
m1.show()
m4.show()
})
index.html:
<script type=”text/javascript” src=”js/libs/sea.js”></script>
<script type=”text/javascript”>
seajs.use(‘./js/modules/main’)
</script>
ES6 模块化

模块化的规范:CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。
而 ES6 中提供了简单的模块系统,完全可以取代现有的 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

基本用法

es6 中新增了两个命令 export 和 import;

export 命令用于规定模块的对外接口;
import 命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。
如果你希望外部能够读取模块内部的某个变量,就必须使用 export 关键字输出该变量。
下面是一个 JS 文件,里面使用 export 命令输出变量。

// math.js
export const add = function (a, b) {
return a + b
}
export const subtract = function (a, b) {
return a – b
}
使用 export 命令定义了模块的对外接口以后,其他 JS 文件就可以通过 import 命令加载这个模块(文件)。
// main.js
import {add, subtract} from ‘./math.js’
add(1, 2)
substract(3, 2)

定义暴露模块 : export

暴露一个对象:
默认暴露,暴露任意数据类型,暴露什么数据类型,接收什么数据类型
export default 对象

暴露多个:
常规暴露,暴露的本质是对象,接收的时候只能以对象的解构赋值的方式来接收值
export var xxx = value1
export let yyy = value2
// 暴露一个对象
var xxx = value1
let yyy = value2
export {xxx, yyy}

引入使用模块 : import
default 模块:
import xxx from ‘ 模块路径 / 模块名 ’
其它模块
import {xxx, yyy} from ‘ 模块路径 / 模块名 ’
import * as module1 from ‘ 模块路径 / 模块名 ’

export 详细用法

export 不止可以导出函数,还可以导出,对象、类、字符串等等;

暴露多个:
分别暴露
export const obj = {test1: ”}
export const test = ”
export class Test {
constuctor() {
}
}
// 或者,直接在暴露的地方定义导出函数或者变量
export let foo = ()=>{console.log(‘fnFoo’);return “foo”},bar=”stringBar”
一起暴露,推荐使用这种写法,这样可以写在脚本尾部,一眼就看清楚输出了哪些变量。
let a=1
let b=2
let c=3
export {a,b,c}
还可以通过 as 改变输出名称
// test.js
let a = 1
let b = 2
let c = 3
export {
a as test,
b,
c
};
import {test, b, c} from ‘./test.js’ // 改变命名后只能写 as 后的命名
通过通配符暴露其他引入的模块
// test.js
let a = 1
let b = 2
let c = 3
export {
a as test,
b,
c
};
// lib.js 引入 test.js 的内容
export * from ‘./test.js’
// 引入
import {test,b,c} from ‘./lib.js’

暴露一个对象,默认暴露
export default 指定默认输出,import 无需知道变量名就可以直接使用
// test.js
export default function () {
console.log(‘hello world’)
}
// 引入
import say from ‘./test.js’ // 这里可以指定任意变量名
say() // hello world
常用的模块
import $ from ‘jQuery’ // 加载 jQuery 库
import _ from ‘lodash’ // 加载 lodash
import moment from ‘moment’ // 加载 moment

import 详细用法
import 为加载模块的命令,基础使用方式和之前一样
// main.js
import {add, subtract} from ‘./test’

// 对于 export default 导出的
import say from ‘./test’
通过 as 命令修改导入的变量名
import {add as sum, subtract} from ‘./test’
sum (1, 2)
加载模块的全部,除了指定输出变量名或者 export.default 定义的导入,还可以通过 * 号加载模块的全部。
// math.js
export const add = function (a, b) {
return a + b
}
export const subtract = function (a, b) {
return a – b
}

// 引入
import * as math from ‘./test.js’
math.add(1, 2)
math.subtract(1, 2)
ES6-Babel-Browserify 使用教程

问题: 所有浏览器还不能直接识别 ES6 模块化的语法

解决:

使用 Babel 将 ES6—>ES5(使用了 CommonJS) —- 浏览器还不能直接执行;
使用 Browserify—> 打包处理 js—- 浏览器可以运行

定义 package.json 文件
{
“name” : “es6-babel-browserify”,
“version” : “1.0.0”
}
安装 babel-cli, babel-preset-es2015 和 browserify
npm install babel-cli browserify -g
npm install babel-preset-es2015 –save-dev
定义.babelrc 文件,这是一个 babel 的设置文件
{
“presets”: [“es2015″]
}
编码
// js/src/module1.js
export function foo() {
console.log(‘module1 foo()’);
};
export let bar = function () {
console.log(‘module1 bar()’);
};
export const DATA_ARR = [1, 3, 5, 1];

// js/src/module2.js
let data = ‘module2 data’;
function fun1() {
console.log(‘module2 fun1() ‘ + data);
};
function fun2() {
console.log(‘module2 fun2() ‘ + data);
};
export {fun1, fun2};

// js/src/module3.js
export default {
name: ‘Tom’,
setName: function (name) {
this.name = name
}
}

// js/src/app.js
import {foo, bar} from ‘./module1’
import {DATA_ARR} from ‘./module1’
import {fun1, fun2} from ‘./module2’
import person from ‘./module3’
import $ from ‘jquery’
// 引入完毕
$(‘body’).css(‘background’, ‘red’)
foo()
bar()
console.log(DATA_ARR);
fun1()
fun2()
person.setName(‘JACK’)
console.log(person.name);

编译

使用 Babel 将 ES6 编译为 ES5 代码(但包含 CommonJS 语法) : babel js/src -d js/lib
使用 Browserify 编译 js : browserify js/lib/app.js -o js/lib/bundle.js

页面中引入测试

<script type=”text/javascript” src=”js/lib/bundle.js”></script>

引入第三方模块(jQuery)
1). 下载 jQuery 模块:
npm install jquery@1 –save

– 2). 在 app.js 中引入并使用

“`
import $ from ‘jquery’
$(‘body’).css(‘background’, ‘red’)
“`

总结

模块化方案
优点
缺点

commonJS
复用性强; 使用简单; 实现简单;
有不少可以拿来即用的模块,生态不错;同步加载不适合浏览器, 浏览器的请求都是异步加载;不能并行加载多个模块。

AMD
异步加载适合浏览器
可并行加载多个模块;模块定义方式不优雅,不符合标准模块化

ES6
可静态分析,提前编译
面向未来的标准;浏览器原生兼容性差,所以一般都编译成 ES5;目前可以拿来即用的模块少,生态差

AMD 和 CMD 区别:

权威参考:https://github.com/seajs/seaj…

对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。
不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.

CMD 推崇依赖就近,AMD 推崇依赖前置。
// 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 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。

AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。
还有一些细节差异,具体看这个规范的定义就好,就不多说了。

参考:使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript

正文完
 0