javascript模块化之CommonJS、AMD、CMD、UMD、ES6

一、总结

一句话总结:

CommonJS是同步加载模块,用在服务端;AMD是异步加载模块,用于浏览器端

1、为什么服务器端可以同步加载,而浏览器端不能同步加载(为什么浏览器端不能用CommonJS)?

浏览器需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。
服务器端所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。

2、CommonJS是什么?

CommonJS是同步加载模块,是NodeJS采用的规范,更偏向于服务器端的规范
  1. var math = require('math');
  2. math.add(2, 3); //

3、es6如何实现模块的输入输出?

es6通过import、export实现模块的输入输出。其中import命令用于输入其他模块提供的功能,export命令用于规定模块的对外接口。

二、javascript模块化之CommonJS、AMD、CMD、UMD、ES6

转自或参考:javascript模块化之CommonJS、AMD、CMD、UMD、ES6
https://blog.csdn.net/Real_Bird/article/details/54869066

 

这是一篇关于js模块化编程的总结记录

随着网站逐渐变成”互联网应用程序”,嵌入网页的Javascript代码越来越庞大,越来越复杂。

网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等……开发者不得不使用软件工程的方法,管理网页的业务逻辑。

Javascript模块化编程,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。

Javascript社区做了很多努力,在现有的运行环境中,实现”模块”的效果。

CommonJS

这里的CommonJS规范指的是CommonJS Modules/1.0规范。

CommonJS是一个更偏向于服务器端的规范。NodeJS采用了这个规范。CommonJS的一个模块就是一个脚本文件。require命令第一次加载该脚本时就会执行整个脚本,然后在内存中生成一个对象。

  1. {
  2. id: '...',
  3. exports: { ... },
  4. loaded: true,
  5. ...
  6. }

id是模块名,exports是该模块导出的接口,loaded表示模块是否加载完毕。此外还有很多属性,这里省略了。

以后需要用到这个模块时,就会到exports属性上取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存中取值。

  1. // math.js
  2. exports.add = function(a, b) {
  3. return a + b;
  4. }
  1. var math = require('math');
  2. math.add(2, 3); // 5

由于CommonJS是同步加载模块,这对于服务器端不是一个问题,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。但是,对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。

所以在浏览器端,不适合于CommonJS规范。所以在浏览器端又出现了一个规范—-AMD。

AMD

CommonJS解决了模块化的问题,但这种同步加载方式并不适合于浏览器端。

AMD是”Asynchronous Module Definition”的缩写,即”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。
这里异步指的是不堵塞浏览器其他任务(dom构建,css渲染等),而加载内部是同步的(加载完模块后立即执行回调)。

AMD也采用require命令加载模块,但是不同于CommonJS,它要求两个参数:

  1. require([module], callback);

第一个参数[module],是一个数组,里面的成员是要加载的模块,callback是加载完成后的回调函数。如果将上述的代码改成AMD方式:

  1. require(['math'], function(math) {
  2. math.add(2, 3);
  3. })

其中,回调函数中参数对应数组中的成员(模块)。

requireJS加载模块,采用的是AMD规范。也就是说,模块必须按照AMD规定的方式来写。

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

  1. define(id?, dependencies?, factory);
  • id:模块的名字,如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字;

  • dependencies:模块的依赖,已被模块定义的模块标识的数组字面量。依赖参数是可选的,如果忽略此参数,它应该默认为 ["require", "exports", "module"]。然而,如果工厂方法的长度属性小于3,加载器会选择以函数的长度属性指定的参数个数调用工厂方法。

  • factory:模块的工厂函数,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。

假定现在有一个math.js文件,定义了一个math模块。那么,math.js书写方式如下:

  1. // math.js
  2. define(function() {
  3. var add = function(x, y) {
  4. return x + y;
  5. }
  6. return {
  7. add: add
  8. }
  9. })

加载方法如下:

  1. // main.js
  2. require(['math'], function(math) {
  3. alert(math.add(1, 1));
  4. })

如果math模块还依赖其他模块,写法如下:

  1. // math.js
  2. define(['dependenceModule'], function(dependenceModule) {
  3. // ...
  4. })

当require()函数加载math模块的时候,就会先加载dependenceModule模块。当有多个依赖时,就将所有的依赖都写在define()函数第一个参数数组中,所以说AMD是依赖前置的。这不同于CMD规范,它是依赖就近的。

CMD

CMD推崇依赖就近,延迟执行。可以把你的依赖写进代码的任意一行,如下:

  1. define(factory)

factory为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:require、exports 和 module.

  1. // CMD
  2. define(function(require, exports, module) {
  3. var a = require('./a');
  4. a.doSomething();
  5. var b = require('./b');
  6. b.doSomething();
  7. })

如果使用AMD写法,如下:

  1. // AMD
  2. define(['a', 'b'], function(a, b) {
  3. a.doSomething();
  4. b.doSomething();
  5. })

这个规范实际上是为了Seajs的推广然后搞出来的。那么看看SeaJS是怎么回事儿吧,基本就是知道这个规范了。

同样Seajs也是预加载依赖js跟AMD的规范在预加载这一点上是相同的,明显不同的地方是调用,和声明依赖的地方。AMD和CMD都是用difine和require,但是CMD标准倾向于在使用过程中提出依赖,就是不管代码写到哪突然发现需要依赖另一个模块,那就在当前代码用require引入就可以了,规范会帮你搞定预加载,你随便写就可以了。但是AMD标准让你必须提前在头部依赖参数部分写好(没有写好? 倒回去写好咯)。这就是最明显的区别。

sea.js通过sea.use()来加载模块。

  1. seajs.use(id, callback?)

UMD

由于CommonJS是服务器端的规范,跟AMD、CMD两个标准实际不冲突。

当我们写一个文件需要兼容不同的加载规范的时候怎么办呢,看看下面的代码。

  1. (function (root, factory) {
  2. if (typeof define === 'function' && define.amd) {
  3. // AMD
  4. define(['jquery', 'underscore'], factory);
  5. } else if (typeof exports === 'object') {
  6. // Node, CommonJS之类的
  7. module.exports = factory(require('jquery'), require('underscore'));
  8. } else {
  9. // 浏览器全局变量(root 即 window)
  10. root.returnExports = factory(root.jQuery, root._);
  11. }
  12. }(this, function ($, _) {
  13. // 方法
  14. function a(){}; // 私有方法,因为它没被返回 (见下面)
  15. function b(){}; // 公共方法,因为被返回了
  16. function c(){}; // 公共方法,因为被返回了
  17. // 暴露公共方法
  18. return {
  19. b: b,
  20. c: c
  21. }
  22. }));

这个代码可以兼容各种加载规范了。

ES6

es6通过importexport实现模块的输入输出。其中import命令用于输入其他模块提供的功能,export命令用于规定模块的对外接口。

export

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果希望外部文件能够读取该模块的变量,就需要在这个模块内使用export关键字导出变量。如:

  1. // profile.js
  2. export var a = 1;
  3. export var b = 2;
  4. export var c = 3;

下面的写法是等价的,这种方式更加清晰(在底部一眼能看出导出了哪些变量):

  1. var a = 1;
  2. var b = 2;
  3. var c = 3;
  4. export {a, b, c}

export命令除了输出变量,还可以导出函数或类。

  • 导出函数
  1. export function foo(){}
  1. function foo(){}
  2. function bar(){}
  3. export {foo, bar as bar2}

其中上面的as表示给导出的变量重命名。

要注意的是,export导出的变量只能位于文件的顶层,如果处于块级作用域内,会报错。如:

  1. function foo() {
  2. export 'bar'; // SyntaxError
  3. }
  • 导出类
  1. export default class {} // 关于default下面会说

export语句输出的值是动态绑定,绑定其所在的模块。

  1. // foo.js
  2. export var foo = 'foo';
  3. setTimeout(function() {
  4. foo = 'foo2';
  5. }, 500);
  6. // main.js
  7. import * as m from './foo';
  8. console.log(m.foo); // foo
  9. setTimeout(() => console.log(m.foo), 500); // foo2

import

import命令可以导入其他模块通过export导出的部分。

  1. // abc.js
  2. var a = 1;
  3. var b = 2;
  4. var c = 3;
  5. export {a, b, c}
  6. //main.js
  7. import {a, b, c} from './abc';
  8. console.log(a, b, c);

如果想为导入的变量重新取一个名字,使用as关键字(也可以在导出中使用)。

  1. import {a as aa, b, c};
  2. console.log(aa, b, c)

如果想在一个模块中先输入后输出一个模块,import语句可以和export语句写在一起。

  1. import {a, b, c} form './abc';
  2. export {a, b, c}
  3. // 使用连写, 可读性不好,不建议
  4. export {a, b, c} from './abc';

模块的整体加载

使用*关键字。

  1. // abc.js
  2. export var a = 1;
  3. export var b = 2;
  4. export var c = 3;
  1. // main.js
  2. import * from as abc form './abc';
  3. console.log(abc.a, abc.b, abc.c);

export default

在export输出内容时,如果同时输出多个变量,需要使用大括号{},同时导入也需要大括号。使用export defalut输出时,不需要大括号,而输入(import)export default输出的变量时,不需要大括号。

  1. // abc.js
  2. var a = 1, b = 2, c = 3;
  3. export {a, b};
  4. export default c;
  1. import {a, b} from './abc';
  2. import c from './abc'; // 不需要大括号
  3. console.log(a, b, c) // 1 2 3

本质上,export default输出的是一个叫做default的变量或方法,输入这个default变量时不需要大括号。

  1. // abc.js
  2. export {a as default};
  3. // main.js
  4. import a from './abc'; // 这样也是可以的
  5. // 这样也是可以的
  6. import {default as aa} from './abc';
  7. console.log(aa);

就到这里了吧。关于循环加载(模块相互依赖)没写,CommonJS和ES6处理方式不一样。

参考

该如何理解AMD ,CMD,CommonJS规范–javascript模块化加载学习总结

AMD/CMD与前端规范

前端模块化之旅(二):CommonJS、AMD和CMD

研究一下javascript的模块规范(CommonJs/AMD/CMD)

Javascript模块化编程(一):模块的写法

Javascript模块化编程(二):AMD规范

Javascript模块化编程(三):require.js的用法

Module

 

javascript模块化之CommonJS、AMD、CMD、UMD、ES6的更多相关文章

  1. JavaScript模块化演变 CommonJs,AMD, CMD, UMD(一)

    原文链接:https://www.jianshu.com/p/33d53cce8237 原文系列2链接:https://www.jianshu.com/p/ad427d8879cb 前端完全手册: h ...

  2. JavaScript模块化CommonJS/AMD/CMD/UMD/ES6Module的区别

    目录 JS-模块化进程 原始的开发方式 CommonJS && node.js AMD && Require.js CMD && Sea.js UMD ...

  3. Javascript模块化编程之CommonJS,AMD,CMD,UMD模块加载规范详解

    JavaSript模块化 在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?     模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问 题进行系 ...

  4. (转) 前端模块化:CommonJS,AMD,CMD,ES6

    模块化的开发方式可以提高代码复用率,方便进行代码的管理.通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数.目前流行的js模块化规范有CommonJS.AMD.CMD以及ES6的模块 ...

  5. CommonJS/AMD/CMD/UMD

    为什么会有这几种模式? 起源:Javascript模块化 模块化就是把复杂问题分解成不同模块,这样可维护性高,从而达到高复用,低耦合. 1.Commonjs CommonJS是服务器端模块的规范,No ...

  6. 关于 CommonJS AMD CMD UMD 规范的差异总结

    一.CommonJS 主要是用于服务器端的规范,比如目前的nodeJS. 根据CommonJS规范,一个单独的文件就是一个模块.每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函 ...

  7. JavaScript模块化编程 - CommonJS, AMD 和 RequireJS之间的关系

    这几天在学习CommonJS的时候突然在StackOverflow上搜索到一个非常好的一个帖子,是关于CommonJS, AMD和RequireJS之间的关系的问答贴.我感觉写的非常好,鉴于没有找到相 ...

  8. 关于 CommonJS AMD CMD UMD 规范的差异总结(转)

    根据CommonJS规范,一个单独的文件就是一个模块.每一个模块都是一个单独的作用域,也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其他文件是不可见的. // foo.js var ...

  9. 关于 CommonJS AMD CMD UMD

    1. CommonJS CommonJS 原来叫 ServerJS, 是服务器端模块的规范,Node.js采用了这个规范. 根据CommonJS规范,一个单独的文件就是一个模块.加载模块使用requi ...

  10. CommonJS/AMD/CMD/UMD概念初探

    1.CommonJS是一种规范,NodeJS是这种规范的实现. 1.1.CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作. 参考: http://www.commonjs.org ...

随机推荐

  1. Git复习(十)之常见报错和疑问

    报错 情况一:git pull报错 There is no tracking information for the current branch. Please specify which bran ...

  2. 一、redis学习(基础)

    redis  持久化 rdb aof 

  3. centos配置vsftp,ftp服务

    1.安装vsftp 1.1.安装vsftp,测试安装的vsftpd的版本是:vsftpd.x86_64 0:3.0.2-11.el7_2 yum -y install vsftpd 1.2.修改配置文 ...

  4. SSD源码解读——损失函数的构建

    之前,对SSD的论文进行了解读,可以回顾之前的博客:https://www.cnblogs.com/dengshunge/p/11665929.html. 为了加深对SSD的理解,因此对SSD的源码进 ...

  5. Linux运维课程体系大纲

    Linux入门:    Linux系统管理:    Linux服务及安全管理:        httpd,lamp,lnmp        Cache:memcached,varnish(缓存系统)  ...

  6. JetBrains IDEA Web开发简单配置

    很早前因为使用了一年的MyEclipse,不想更换其他的IDE工具,是因为各项配置,以及快捷键等.前段时间更换了IDEA工具,初步了解了一些功能,包括快捷,调试,配置,都很优于MyEclipse.但是 ...

  7. 【2019.3.20】NOI模拟赛

    题目 这里必须标记一下那个傻逼问题,再不解决我人就没了! 先放一个 $T3$ $20$ 分暴力 #include<bits/stdc++.h> #define rep(i,x,y) for ...

  8. java代码调用exe(cmd命令)

    public class ShellCommand{    public static void execCmd(String cmd, boolean wait)    {        execC ...

  9. Mysql 查看连接数,状态及最大并发数(转载)

    -- show variables like '%max_connections%'; 查看最大连接数 set global max_connections=1000 重新设置   mysql> ...

  10. 前端之HTML初识

    目录 手写服务端,启用浏览器(客户端连接服务端) Web服务的本质: HTTP协议(HyperText Transfer Protocol) HMTL(HyperText Mark Language) ...