首页 > 其他分享 >区分CommonJs/ES6 Module/AMD/CMD

区分CommonJs/ES6 Module/AMD/CMD

时间:2023-01-07 18:35:45浏览次数:69  
标签:ES6 CommonJs exports CMD AMD js CommonJS 模块 加载

模块加载方式


1.CommonJS

CommonJS 是一个项目,其目标是为 JavaScript 在网页浏览器之外创建模块约定。创建这个项目的主要原因是当时缺乏普遍可接受形式的 JavaScript 脚本模块单元,模块在与运行JavaScript 脚本的常规网页浏览器所提供的不同的环境下可以重复使用。

CommonJS的特点:

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

浏览器不兼容CommonJS的根本原因,在于缺少四个Nodejs环境的变量:

  • module
  • exports
  • require
  • global

每个文件都是一个单独的模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

另外,为了方便,Node为每个模块提供了一个exports变量,指向module.exports。等同在每个模块头部,有一行这样的命令:

var exports = module.exports;

需要注意的是,不能直接将exports变量指向一个值,这种行为相当于切断了exportsmodule.exports的关系。

let a = {
  num: 5,
}
exports = a; // 错误
exports.a = a; // 正确

建议使用module.exports


2.ES6 Module

ES6模块的设计是尽可能的静态化,使得编辑时就能确定模块之间的依赖关系,以及输入和输出变量。(而CommonJs和AMD则是在运行时才能实现以上结果)

例如CommonJs模块就是一个对象,输入时必须查找对象属性,而ES6模块则可以暴露出任何变量、函数等。

ES6模块的加载方式是“编译时”加载或者是静态加载

ES6模块功能主要由两个命令构成:exportimportexport用来规定模块的对外接口,import用来输入其他模块提供的功能。

需要注意的是:ES6模块的import/export目前不支持在node环境中直接使用,可以使用webpack打包之后在浏览器中查看效果。

var firstName = 'w';
var lastName = 'bin';
var year = 1993;
function add(a, b){
  return a + b;
}
function reduce(a, b){
  return a - b;
}
export {firstName, lastName ,year}; // import * as circle from './circle';
export function mul(a, b){ return a * b; }
export {
  add as sum,
  reduce as mius,
  reduce as jian
} // 使用as进行重命名
export let name = 'wbin'; // import {name as wbin, age} from './e2';
export let age = 26; // import {name, age} from './e2';
export default function(){ return '123' } // import name from './e3';

3.AMD和require.js

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。

它采用异步方式加载模块,模块的加载不影响它后面语句的运行。

所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

AMD也采用require()语句加载模块,它要求两个参数:

require([module], callback);

require.js是实现AMD规范的模块化:用require.config指定引用路径,用define定义模块,用require加载模块。

/** 网页中引入require.js及main.js **/
<script src="js/require.js" data-main="js/main"></script>

/** main.js 入口文件/主模块 **/
// 首先用config()指定各模块路径和引用名
require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",  //实际路径为js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
});
// 执行基本操作
require(["jquery","underscore"],function($,_){
  // some code here
});

/** 定义和引用其他模块 **/
// 定义math.js模块
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});

// 定义一个依赖underscore.js的模块
define(['underscore'],function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young';
    })
  };
  return {
    classify :classify
  };
})

// 引用模块,将模块放在[]内
require(['jquery', 'math'],function($, math){
  var sum = math.add(10,20);
  $("#sum").html(sum);
});

 


4.CMD和sea.js

CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。此规范其实是在sea.js推广过程中产生的。

/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等于在最前面声明并初始化了要用到的所有模块
    a.doSomething();
    if (false) {
        // 即便没用到某个模块 b,但 b 还是提前执行了
        b.doSomething()
    } 
});

/** CMD写法 **/
define(function(require, exports, module) {
    var a = require('./a'); //在需要时申明
    a.doSomething();
    if (false) {
        var b = require('./b');
        b.doSomething();
    }
});

/** sea.js **/
// 定义模块 math.js
define(function(require, exports, module) {
    var $ = require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});

// 加载模块
seajs.use(['math.js'], function(math){
    var sum = math.add(1+2);
});

5.UMD(Universal Module Definition - 通用模块定义)

UMD是AMD和CommonJS的一个糅合。AMD是浏览器优先,异步加载;CommonJS是服务器优先,同步加载。

既然要通用,怎么办呢?

那就先判断是否支持node.js的模块,存在就使用node.js;再判断是否支持AMD(define是否存在),存在则使用AMD的方式加载。这就是所谓的UMD。

((root, factory) => {
  if (typeof define === 'function' && define.amd) {
    //AMD
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    //CommonJS
    var $ = requie('jquery');
    module.exports = factory($);
  } else {
    //都不是,浏览器全局定义
    root.testModule = factory(root.jQuery);
  }
})(this, ($) => {
  //do something...  这里是真正的函数体
});

6.ES6 模块与 CommonJS 模块的差异

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
    • CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
    • ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
    • 运行时加载: CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
    • 编译时加载: ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,import时采用静态命令的形式。即在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”。模块内部引用的变化,会反应在外部。

CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

废话不多说,直接看代码:

首先看个CommonJS输出拷贝的例子:

// a.js
let a = 1;
let b = { num: 1 }
setTimeout(() => {
    a = 2;
    b = { num: 2 };
}, 200);
module.exports = {
    a,
    b,
};

// main.js
// node main.js
let {a, b} = require('./a');
console.log(a);  // 1
console.log(b);  // { num: 1 }
setTimeout(() => {
    console.log(a);  // 1
    console.log(b);  // { num: 1 }
}, 500);

所谓输出拷贝,如果了解过 NodeJS 或者 webpack 对 CommonJS 的实现(不了解可以看这篇文章),就会知道:exports对象是模块内外的唯一关联, CommonJS 输出的内容,就是exports对象的属性,模块运行结束,属性就确定了。

再看ES6 Module输出的例子:

// a.mjs
let a = 1;
let b = { num: 1 }
setTimeout(() => {
    a = 2;
    b = { num: 2 };
}, 200);
export {
    a,
    b,
};

// main.mjs
// node --experimental-modules main.mjs
import {a, b} from './a';
console.log(a);  // 1
console.log(b);  // { num: 1 }
setTimeout(() => {
    console.log(a);  // 2
    console.log(b);  // { num: 2 }
}, 500);

以上就是 ES6 Module 输出引用和 CommonJS 输出值的区别,模块内部引用的变化,会反应在外部,这是 ES6 Module 的规范。


总结

  1. AMD/CMD/CommonJs 是js模块化开发的规范,对应的实现是require.js/sea.js/Node.js
  2. CommonJs 主要针对服务端,AMD/CMD/ES Module主要针对浏览器端,容易混淆的是AMD/CMD。(顺便提一下,针对服务器端和针对浏览器端有什么本质的区别呢?服务器端一般采用同步加载文件,也就是说需要某个模块,服务器端便停下来,等待它加载再执行。这里如果有其他后端语言,如java。而浏览器端要保证效率,需要采用异步加载,这就需要一个预处理,提前将所需要的模块文件并行加载好。)
  3. AMD/CMD区别,虽然都是并行加载js文件,但还是有所区别,AMD是预加载,在并行加载js文件同时,还会解析执行该模块(因为还需要执行,所以在加载某个模块前,这个模块的依赖模块需要先加载完成);而CMD是懒加载,虽然会一开始就并行加载js文件,但是不会执行,而是在需要的时候才执行。
  4. AMD/CMD的优缺点.一个的优点就是另一个的缺点, 可以对照浏览。
    AMD优点:加载快速,尤其遇到多个大文件,因为并行解析,所以同一时间可以解析多个文件。
    AMD缺点:并行加载,异步处理,加载顺序不一定,可能会造成一些困扰,甚至为程序埋下大坑。
    CMD优点:因为只有在使用的时候才会解析执行js文件,因此,每个JS文件的执行顺序在代码中是有体现的,是可控的。
    CMD缺点:执行等待时间会叠加。因为每个文件执行时是同步执行(串行执行),因此时间是所有文件解析执行时间之和,尤其在文件较多较大时,这种缺点尤为明显。(PS:重新看这篇文章,发现这里写的不是很准确。确切来说,JS是单线程,所有JS文件执行时间叠加在AMD和CMD中是一样的。但是CMD是使用时执行,没法利用空闲时间,而AMD是文件加载好就执行,往往可以利用一些空闲时间。这么来看,CMD比AMD的优点还是很明显的,毕竟AMD加载好的时候也未必就是JS引擎的空闲时间!)
  5. CommonJS 和 ES Module 区别:CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
  6. 如何使用?CommonJs 的话,因为 NodeJS 就是它的实现,所以使用 node 就行,也不用引入其他包。AMD则是通过<script>标签引入require.js,CMD则是引入sea.js
  7. UMD是AMD和CommonJS的糅合

 

标签:ES6,CommonJs,exports,CMD,AMD,js,CommonJS,模块,加载
From: https://www.cnblogs.com/ligd2022/p/17033023.html

相关文章

  • win终端利器-Cmder
    cmder官网:https://cmder.app/安装直接选择full版本下载,完成后解压即可启动直接双击Cmder.exe如果每次都进入到Cmder解压目录双击Cmder.exe打开的方式很麻烦,可以......
  • bat cmd 无效参数/选项 - deleting
    ​​Windowbatexpdp数据库定时任务逻辑备份数据库定时删除N天前的旧文件​​Linuxshellcrontabexpdp定时任务逻辑备份数据库定时删除旧文件错误配置forfiles命令,......
  • Java调用CMD命令
    ProcessBuilderbuilder= newProcessBuilder(commands);1、在使用ProcessBuilder时,如果commands使用{"java-version"}这种带空格的命令,可能会报文件找不到的错误,最......
  • ES6语法糖,超甜!
    ES6语法糖1.......表示取出可遍历数组中的内容。constarr=newArray()constnumbers=[1,2,3,4,5]arr.push(...numbers)//arr内容:12345,即将numbers的内......
  • bat cmd 无效参数/选项 - deleting
    Windowbatexpdp数据库定时任务逻辑备份数据库定时删除N天前的旧文件Linuxshellcrontabexpdp定时任务逻辑备份数据库定时删除旧文件错误配置forfiles命令,目录......
  • ES6笔记
    1、参考​​ECMAScript6简介​​2、…展开运算符/收集运算符​​ES6…展开&收集运算符​​​ES6之展开运算符(…)​​Es6(…)3、解构赋值解构赋值需要满足的条件:1、左......
  • ES6 arrow function and normal function difference
      watch(()=>data,()=>{drawChart()})//Uncaught(inpromise)ReferenceError:Cannotaccess'drawChart'beforeinitializationconstdrawChart=(......
  • 运行Cmd
    ​​QT杀程序进程_歌昔的博客​​Qt实现的根据进程名来结束进程-hbcdr​​Qt如何在程序中启动第三方程序?_Mr.Sugarcane​​QT学习QCommandLineParser与QCommandLineOptio......
  • 微软次时代新版cmd——windows terminal
    微软次时代新版cmdWindowsTerminal-MicrosoftStore应用程序microsoft/terminal:ThenewWindowsTerminalandtheoriginalWindowsconsolehost,allinthesa......
  • day01-ES6新特性
    ES6新特性1.ES6是什么?DCMAScript6.0(以下简称ES6)是JavaScript语言的下一代标准,2015年6月发布ES6设计目标:达到JavaScript语言可以用来编写复杂的大型程序,成为企业级......