JS模块化加载方式 ,AMD、CMD、CommonJS、ES6四者间的异同

发布时间:2024-04-13更新时间:2024-04-13阅读量:148原创
  • AMD----- 依赖前置 (require.js)

引入模块-----require([ ],callback)
定义模块----- define([],callback)
不论定义模块,还是引入模块时,都会先行引入依赖,再定义/引入模块

  • CMD ----- 依赖就近引入原则(seaJs)

引入模块 (遵循就近原则)

复制代码
define(function (requie, exports, module) {
    //依赖可以就近书写
    var a = require('./a');
    a.test();
});

定义模块----- define([],callback)

  • CommonJS

引入模块-------require('模块路径')
定义模块 ------ exports.模块名= function(){ //some code ... }

ES6

引入模块 ----- import
定义模块 ----- export、export default

2、四种模块化加载方式的详说

2.1 AMD
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
步骤如下:
1、引入require.js,并用data-main指定入口的js文件。
2、在入口的js文件内用require.config配制各个模块的路径。
3、define定义js模块文件
4、require引入js模块文件

注意:

require([ ],callback)首先异步加载数组内的模块,随后才会去执行callback;

index.html
html 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
	</head>
	<body>
		
	</body>
	<!--data-main属性的作用是,指定网页程序的主模块。在下面例中,
	就是js目录下面的main.js,这个文件会第一个被require.js加载。
	由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。-->
    
    <script src="https://cdn.bootcss.com/require.js/2.3.5/require.js" defer async="true" data-main="js/main"></script>
</html>
js/main.js (使用require引入模块)
js 复制代码
//require配制各个模块加载路径
require.config({
//	   baseUrl: "./",
    paths: {
      "jquery": "jquery.min",
          "math":"math",
          "getStyle":"getStyle"

    }

});

//真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。

//require()函数接受两个参数。
//第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;
//第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。
//加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。


//require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;
//它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。
require(['jquery','math','getStyle'], function ($,math,getStyle){

     let total = math.add(109,23);
     console.log(total);
     
     getStyle.setRedBg('body','#dddddd');
   
     // some code ......
});
2.2 CMD

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

CMD与AMD的区别:

对于依赖的模块,AMD是提前执行,CMD是延迟执行。
(不过RequireJS从2.0开始,也改成可以延迟执行)

AMD推崇依赖前置,CMD推崇依赖就近。

js 复制代码
//AMD
define(['./a','./b'], function (a, b) {
    //依赖一开始就写好
    a.test();
    b.test();
});
 
//CMD
define(function (requie, exports, module) {
    //依赖可以就近书写
    var a = require('./a');
    a.test();
     
    ...
    //软依赖
    if (status) {
        var b = requie('./b');
        b.test();
    }
});

2.3 CommonJs规范
在模块中,通过require()方法来引入外部的模块。
上下文提供了exports 对象导出当前模块的方法和变量, 并且它是唯一导出的出口。
在模块中还存在一个module对象,它代表模块自身,而exports是module的属性。

案例如下:

math.js 定义导出模块

复制代码
exports.math = function(){
   //some  code ...
}

另一个文件 引入模块

复制代码
var math = require('math')1
2.4 ES6的import、export、export default

注意:

同一个文件中,可以有多个export,但只能有一个export default;
更多请参考:https://blog.csdn.net/yiyueqinghui/article/details/84382229

3、ES6 Module 和 CommonJS 模块的区别:

1、语法上
CommonJS 使用的是 module.exports = {} 导出一个模块对象,require(‘file_path’) 引入模块对象;
ES6使用的是 export 导出指定数据, import 引入具体数据。

2、CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

ES6 Modules 的运行机制与 CommonJS 不一样。JS遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。因此,原始值变了,import加载的值也会跟着变。

3、CommonJS 全量加载整个模块,ES6可以选择性加载模块的部分内容

名称 AMD CMD CommonJs ES6
全称 Asynchronous Module Definition Common Module Definition CommonJs ECMAScript6.0
同步异步 异步 异步 同步 同步/异步 均支持,取决于采用什么loader API
实现实例 RequireJS SeaJS(淘宝) nodeJS JavaScript
函数定义与引入 见代码块-01 见代码块-02 见代码块-03 见代码块-04
运行环境 浏览器 浏览器 服务端 前后端
代码块
  • 代码块-01:AMD函数定义方式
复制代码
// 声明
define(['moduel'], () => {
  'use strict';
  const name = 'jsong';

  const sayHello = function() {
    console.log(`hello ${name}`);
  };

  return { name, sayHello };
});

// 引入
require(['module'], module => {
  module.sayHello();
});
  • 代码块-02:CMD函数定义方式
复制代码
// 声明
define(function(require, exports, module) {
  const name = 'jsong';
  const age = 11;
  const sayHello = () => {
    console.log(`hello ${name}`);
  };
  module.exports = { name, sayHello };
  console.log(module.exports);
});

// 引入
define(function(require, exports, module) {
  const m = require('./cmddefined');
  m.sayHello();
});
  • 代码块-03:CommonJs函数定义方式
复制代码
// 声明
// 方式1
const name = 'jsong';
const age = 1;

const sayHelloName = function() {
  console.log(name);
};

module.exports = { name, age, sayHelloName };
// { name: 'jsong', age: 1, sayHelloName: [Function: sayHelloName] }
console.log(module.exports);

// 方式2
exports.name = 'jsong';
exports.age = 11;

// {name:'jsong',age:11}
console.log(module.exports);


// 引入
const commonjs = require('./common.js');
const commonjs2 = require('./common2.js');
// jsong
commonjs.sayHelloName();
// jsong
console.log(commonjs2.name);
  • 代码块-04:ES6函数定义方式
复制代码
// 导出变量
export const name = 'jsong';
// 等价于
// const name = 'jsong';
// export { name };

export const sayHello = () => console.log(`hello ${name}`);
// 等价于
// const sayHello = () => console.log(`hello ${name}`);
// export { sayHello };

// 导出函数
export function eat() {
  console.log(`${name} eat`);
}
// 等价于
// function eat() {
//   console.log(`${name} eat`);
// }
// export { eat };

const age = 11;
// 导出对象
export const people = {
  name,
  age,
  hello: () => console.log(`hello ${name}`)
};
// 等价于
// const people = {
//   name,
//   age,
//   hello: () => console.log(`hello ${name}`)
// };
// export { people };

// 导出类
export class jsong{
    sayHello() {
        console.log(`hello ${name}`);
      }  
}
// 等价于
// class jsong {
//   sayHello() {
//     console.log(`hello ${name}`);
//   }
// }
// export { jsong };

// 引入
// 例1
import * as people from './es6.js';

// jsong
console.log(people.name);
// hello jsong
people.sayHello();
// jsong eat
people.eat();
console.log(people.people);
// hello jsong
people.people.hello();

// hello jsong
new people.jsong().sayHello();

// 例2
import { name, sayHello } from './es6.js';

// jsong
console.log(name);
// hello jsong
sayHello();

详细参考 AMD-CMD-CommonJS三者间的异同