前端模块化(CommonJs、AMD、CMD、ES6)

一、总述

前端模块化:CommonJS、AMD、CMD、ES6模块化

二、COMMONJS

  1. 定义:根据CommonJS规范,一个单独的文件就是一个模块,每一个模块就是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其模块读取,除非是定义为global对象的属性。
  2. 输出:模块只有一个输出,module.exports对象,一个模块输出的内容会被放在这个对象中。
  3. 加载:加载模块使用require.js方法,该方法读取一个文件并执行,返回文件内部的module.exports对象。

注:require是同步的,在服务器端实现很简单,但是在浏览器端,加载JS最佳方法是在document中插入script标签,但是脚本标签是异步的,故传统的commonJS模块在浏览器环境中无法正常加载。

三、AMD

  1. AMD(Asynchronous Module Definition),异步模块定义,实现在浏览器端模块化开发的规范。使用AMD规范进行页面开发需要用到对应的库函数,即RequireJs,实际上AMD是RequireJs在推广过程中对模块定义的规范化的产出。
  2. requireJs主要解决两个问题:
    (1)多个js文件kennel有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器;
    (2)js加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长。
  3. 语法

(1)define:define(id?, dependences?, factory);

  • id:可选参数,用来定义模块的标识,若没有提供,则使用脚本文件名;
  • dependences:当前模块依赖的模块名称数组;
  • factory:工厂方法,模块初始化要执行的函数或对象。若是函数,应该只被执行一次;若是对象,此对象应该为模块的输出值。

(2)require:require([dependencies], function(){});

  • dependencies:依赖的模块;
  • function:回调函数,所有模块加载成功后被调用,加载的模块会以参数的形式传入。
  1. require()函数在加载依赖函数的时候是异步加载的,这样浏览器不会失去响应;在模块都加载成功后,指定的回调函数才会运行,解决了依赖性的问题。
  2. RequireJS 的基本思想为:通过一个函数来将所有所需要的或者说所依赖的模块实现装载进来,然后返回一个新的函数(模块)。

四、CMD

  1. CMD(Common Module Definition),通用模块定义,CMD浏览器实现是SeaJs。SeaJS与RequireJS类似,只不过在模块定义方法和模块加载时机上有所不同。
  2. 语法

(1)define:define(id?, dependence?, factory);

  • CMD推崇一个文件一个模块,故经常用文件名作为模块ID;
  • CMD推崇依赖就近,所以一般在factory中写依赖。

(2)define中的factory:function(require, exports, module);

  • require:方法,接受模块标识作为唯一参数,用来获取其他模块提供的接口:require(id);
  • export:对象,对外提供模块接口;
  • module:对象,储存了与当前模块相关联的一些属性和方法。

五、AMD与CMD区别

  1. AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块;
  2. CMD推崇就近依赖,只有在用到某个模块的时候在去require;
  3. 两者最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不对。
  • 同样都是异步加载模块,AMD在加载模块完成过会执行该模块,所有模块都加载执行后会进入require的回调函数,执行主逻辑。这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,哪个先下载下来哪个先执行,但是主逻辑一定在所有依赖加载完成后才会执行;
  • CMD加载完某个依赖模块后并不执行,只是下载下来而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的;
/** requireJS和SeaJS可以互相实现**/
/** 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);
});

六、ES6 Module

  1. ES6模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入与输出的变量。CommonJS与AMD模块只能在运行时确定模块的依赖关系。
  2. 例如,CommonJ就是一个对象,输入时必须查找对象属性。
// CommonJS模块
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

以上代码实质上先整体加载fs模块生成一个对象_fs,然后再从_fs对象中读取用到的3个方法。这种加载叫做运行时加载,没有办法在编译时做到静态优化。
3. ES6模块不是对象,是通过export命令显式指定输出的代码,再通过import命令输入。

// ES6模块
import { stat, exists, readFile } from 'fs';

以上代码实质上是从fs模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即ES6模块在编译时就完成模块加载,效率要比commonJS高。

  1. ES6与CommonJS区别

(1)CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用;

  • CommonJs输出的是一个值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值;
  • JS引擎在遇到import的时候,会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用,到被加载的那个模块里面去取值。原始值变化会导致import加载的值变化。ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

(2)CommonJS模块是运行时加载,ES6是编译时输出接口

  • CommonJS时运行时加载,ES6是编译时加载。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值