AMD and CMD and cjs and esm

258 阅读5分钟

[TOC]

AMD (Asynchronous Module Definition ):AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块

AMD在加载模块完成后就会执行改模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行。

define(id?, dependencies?, factory); if module "a/b/c" asks for "../d", that resolves to "a/d" if module "a/b/c" asks for "./e", that resolves to "a/b/e"

defined("alpha", ["require", "exports", "beta"],function(require, exports, beta){
    return beta.verb();
})

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出的 // 文件目录

  • www/
    • index.html
    • js/
      • app/
        • sub.js
      • lib/
        • jquery.js
        • canvas.js
      • app.js
      • require.js

使用方式:

//index.html 中导入script标签,data-main 单文件入口app.js src是requirejs包文件
<script data-main="js/app.js" src="js/require.js"></script>

//app.js:
requirejs.config({
    baseUrl: 'js/lib',
    paths: {
        app: '../app',
        jquery:'jquery'
    }
});

// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],function($, canvas,sub) {
    //jQuery, canvas and the app/sub module are all
    //loaded and can be used here now.
});

//sub.js
defined([jquery],function($){
    return{
        body:$('#id')
    }
})
//或者
define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});
CMD (Common Module Definition):CMD推崇就近依赖,只有在用到某个模块的时候再去移入

CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的

define(function(require, exports, module) {

  // 通过 require 引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通过 exports 对外提供接口
  exports.doSomething = ...

  // 或者通过 module.exports 提供整个接口
  module.exports = ...

});

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

值得注意的是:AMD和CMD都是异步加载模块。

// index.html
<script src="seajs"></scrpt>
<script>
// seajs 的简单配置 可以忽略
seajs.config({
  base: "../sea-modules/",
  alias: {
    "jquery": "jquery/jquery/1.10.1/jquery.js"
  }
})

// 加载入口模块文件
seajs.use("../static/hello/src/main.js")
</script>
// main.js
// 所有模块都通过 define 来定义
define(function(require, exports, module) {

  // 通过 require 引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通过 exports 对外提供接口
  exports.doSomething = ...

  // 或者通过 module.exports 提供整个接口
  module.exports = ...

});
commonjs Node 应用由模块组成,采用 CommonJS 模块规范。

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

// 导出
var addX = function (value) {
  return value + x;
};
// module.exports.addX = addX;
module.exports = addX;
exports.addX = addX;


//导入
var example = require('./example.js');

module.exports 赋值之前 exports == module.exports 赋值之后 exports != module.exports

commonjs规范

commonjs 规范

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作

commonjs 和 commonjs2 区别以及影响 exports是module.exports的引用,module.exports是exports的具体实现 打印 module

Module {
  id: '.',
  exports: {},
  parent: null,
  filename: '/home/jim/Desktop/index.js',
  loaded: false,
  children: [],
  paths:
   [ '/home/jim/Desktop/node_modules',
     '/home/jim/node_modules',
     '/home/node_modules',
     '/node_modules' ] }

commonjs规定了exports才是正统,module.exports最多只能算'小妾'。所以commonjs2是规定用module.exports输出的。

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

es6 模块静态方式导入
// a.js
export let count = 1;
export default let key = 'degal'

// b.js
import keys {count} from './b.js'
// keys == key , count == count

每个模块只有一个 export default 通过标签引入es6模块需要加module

<script type="module">
    import {f2} from "./index2.js"
    f2(1);
</script>

es6 与 commonjs区别

es6模块是引用 commonjs模块是拷贝
静态化,必须在顶部,不能使用条件语句,自动采用严格模式, 当使用require命令加载某个模块时,就会运行整个模块的代码,并缓存下来。
import只会导入一次,无论你引入多少次 require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。
import的变量是只读的,不论是基本数据类型还是复杂数据类型。 require导入缓存中,基本类型可以直接赋值,但是对缓存中没影响,引用类型属于浅拷贝,数据改变会影响另一个模块。
可以通过一些方法调用改变内部值,import加载的值也会发生变化 可以通过一些方法调用改变内部值,import加载的值不会发生变化
外部可以拿到实时值,而非缓存值(是引用而不是copy) 外部拿到的是缓存值(是copy而不是引用)
treeshaking和编译优化,以及webpack3中的作用域提升 当使用require命令加载某个模块时,就会运行整个模块的代码。
循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。 循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。
export default 不能重复导出,会报错 module.exports 可以重复定义,后面的会覆盖前面的

自己理解循环引用: 对于commonjs对于已经全部加载完成的模块,无须加载直接,对于未加载的模块,首次需要正常加载,记载一部分然后内部加载其他模块的部分,再次引用无需加载直接读取加载部分缓存,(如缓存部分没有导出,接下来使用的是undefined,有导出正常导出),未加载的部分无缓存也无需加载。 对于es6模块,无须关注加载,正常执行,(可能导致栈溢出)

关于循环加载示例

amd 扩展

对于一些文件不遵循amd规范可以通过shim方式加载

require.config({
        baseUrl:'js/utily',//指定js文件的基路径
        paths: {
            framework: "framework"
        },
        shim:{
            'framework':{//这个键名为要载入的目标文件的文件名,不能随便命名否则加载framework.js文件后是拿不到改文件对外提供的接口的。因为这个坑了哥一下午!!!
                exports:'framework'//exports的值为framework.js提供的 对外接口的名称
            }
        }
    });
require(['framework'],function(frame){
     console.log(frame);//此处就会打印framework.js中对外提供的接口对象啦哈啊哈
}

入门视频教程