• AMD、CMD规范


    本文原链接:https://cloud.tencent.com/developer/article/1177217

    AMD && CMD

    前言

    JavaScript初衷:实现简单的页面交互逻辑,寥寥数语即可;

    随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀

    问题:

    这时候JavaScript作为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,甚至没有类的概念,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码

    一、模块

    模块化:是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。

    在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。

    无模块

    <script src="jquery.js"></script>   
    <script src="jquery_scroller.js"></script>   
    <script src="main.js"></script>   
    <script src="other1.js"></script>   
    <script src="other2.js"></script>   
    <script src="other3.js"></script>

    优点:

    相比于使用一个js文件,这种多个js文件实现最简单的模块化的思想是进步的。 

    缺点:

    污染全局作用域。 因为每一个模块都是暴露在全局的,简单的使用,会导致全局变量命名冲突,当然,我们也可以使用命名空间的方式来解决。 对于大型项目,各种js很多,开发人员必须手动解决模块和代码库的依赖关系,后期维护成本较高。 依赖关系不明显,不利于维护。 比如main.js需要使用jquery,但是,从上面的文件中,我们是看不出来的,如果jquery忘记了,那么就会报错。

    二、CommonJS

    CommonJs 是服务器端模块的规范,Node.js采用了这个规范。

    根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。

    var math = require('math');
    math.add(2, 3);

    第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。

    CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD  CMD 解决方案。

    三、AMD

    AMD 即 Asynchronous Module Definition,中文名是异步模块定义的意思。它是一个在浏览器端模块化开发的规范

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

    require([module], callback);

    第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

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

    math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js

    与 RequireJS

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

    AMD异步加载模块。它的模块支持对象 函数 构造器 字符串 JSON等各种类型的模块。

    //通过数组引入依赖 ,回调函数通过形参传入依赖
    define(['Module1', ‘Module2’], function (Module1, Module2) {
    
        function foo () {
            /// someing
            Module1.test();
        }
    
        return {foo: foo}
    });
    • 第一个参数 id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。如果存在,那么模块标识必须为顶层的或者一个绝对的标识。
    • 第二个参数,dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。
    • 第三个参数,factory,是一个需要进行实例化的函数或者一个对象。

        创建模块标识为 Module1 的模块,依赖于 require, export,和标识为 beta 的模块  

    AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:

    define(function (require, exports, module) {
        
        var reqModule = require("./someModule");
        requModule.test();
        
        exports.asplode = function () {
            //someing
        }
    });

    优点:

    适合在浏览器环境中异步加载模块。可以并行加载多个模块。 缺点:

    提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖。

    四、CMD

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

    CMD和AMD的区别有以下几点:

    1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。

    2.AMD推崇依赖前置(在定义模块的时候就要声明其依赖的模块),CMD推崇依赖就近(只有在用到某个模块的时候再去require——按需加载)。

    //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();
        }
    });

    3.AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。

    AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,CMD是SeaJS 在推广过程中被广泛认知。RequireJs出自dojo加载器的作者James Burke,SeaJs出自国内前端大师玉伯。二者的区别,玉伯在12年如是说

    RequireJS 和 SeaJS 都是很不错的模块加载器,两者区别如下:

    1. 两者定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 服务器端
    
    2. 两者遵循的标准有差异。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不同,导致了两者API 的不同。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
    
    3. 两者社区理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
    
    4. 两者代码质量有差异。RequireJS 是没有明显的 bug,SeaJS 是明显没有 bug。
    
    5. 两者对调试等的支持有差异。SeaJS 通过插件,可以实现 Fiddler 中自动映射的功能,还可以实现自动 combo 等功能,非常方便便捷。RequireJS无这方面的支持。
    
    6. 两者的插件机制有差异。RequireJS 采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采取的插件机制则与 Node 的方式一致开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。

    优点: 同样实现了浏览器端的模块化加载。 可以按需加载,依赖就近。

    缺点: 依赖SPM打包,模块的加载逻辑偏重。

     五、 ES6 模块

    CommonJS

    • 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
    • 对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
    • 当使用require命令加载某个模块时,就会运行整个模块的代码。
    • 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
    • 循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。

    ES6模块

    • ES6模块中的值属于【动态只读引用】。
    • 对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
    • 对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
    • 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。
  • 相关阅读:
    Google官方教程之Selling In-app Products
    In-app Billing 概述
    Android SDK和ADT无法更新的解决办法
    在NGUI中高效优化UIScrollView之UIWrapContent的简介以及使用
    cocos2d-x 3.1 编译脚本android-build.py
    Storm---DirectGroup(直接分组)
    Lucene Spatial构建地理空间索引
    Log4j2日志配置
    Guava缓存使用
    Maven 多套环境配置
  • 原文地址:https://www.cnblogs.com/leftJS/p/11073481.html
Copyright © 2020-2023  润新知