zoukankan      html  css  js  c++  java
  • JavaScript中的模块化之AMD和CMD

    前言: 
    为什么我们需要模块化开发,模块化开发的好处有哪些? 首先我们先说一下非模块化的开发方式带来的弊端。 非模块化开发中会导致一些问题的出现,变量和函数命名可能相同,会造成变量污染和冲突,并且出错时候很难排查。耦合程度高,不符合软件开发中的高内聚和低耦合的原则,所以我们就可以总结一下模块化开发的好处了: 
    ① 解决项目中的变量污染问题。 
    ② 开发效率高,有利于多人协同开发。 
    ③ 职责单一,方便代码重用和维护 。 
    ③ 解决文件依赖问题,无需关注引包顺序 。

    模块化开发的演变过程

    • 普通的函数封装
    • 封装成对象
    • 私有公有成员分离 (使用自执行函数,避免变量污染)
    • 模块的维护和扩展(用多个自执行函数把模块分离开来,使用开闭原则—去增添功能,而尽量不要修改原来的代码,)
    • 可以添加模块的第三方依赖(比如添加jQuery的$,$作为一个编程的接口,降低程序之间的耦合度)

    模块化的规范

    服务端规范 — CommonJs

    以为作为服务器端的开发,不会经过网络传输,所以包的加载几乎都是瞬间完成的,只有加载完成了,才能执行操作,所以commonjs是同步的,nodejs就是实现了这种规范。

    有关规范的具体详情,找了两篇文章: 
    http://www.open-open.com/doc/view/f7df10bb81c347f79b436faa85dcfd81 
    http://blog.jobbole.com/49290/

    浏览器端规范 — AMD 和 CMD

    浏览器端的规范,因为所有的文件请求都要经过网络传输,很多文件的加载会有不确定因素存在为了解决这个问题,浏览器端出来了这两个规范。

    AMD规范:

    异步模块定义规范(Asynchronous Module Definition)制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)。具体规范链接在这里:中文版   英文版 ,requireJs就是AMD规范的实现。

    • requirejs的基本使用: 
      ① 引入requirejs包,并且设置入口文件

      <script data-main='js/main' src='http://apps.bdimg.com/libs/require.js/2.1.9/require.min.js'></script>
      
      • 1
      • 2
      • 1
      • 2

      ② 定义主模块 在main.js文件中

      define(['module1','module2',function(m1,m2) {
          // 说明:上面两个module是依赖模块,通过m1,m2的方式来使用
          // 在这里我们写自己的业务逻辑 todo
          // 如果我们需要暴露出去,需要 return ,不是用exports或者module.exports
          // return {};
      }]);
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      后续的使用,直接看文档吧,以后有机会再来补充。 
      requirejs官网 
      requirejs中文官网

    CMD规范:

    通用模块定义 (Common Module Definition) 是 SeaJS 在推广过程中对模块定义的规范化产出。类似的还有 CommonJS Modules/2.0 规范。 
    这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。 
    目前这些规范的实现都能达成浏览器端模块化开发的目的。有两篇文档相关文档: 文档1   文档2 , 下面简要说一下seajs, 详细资料还得戳旁边的资料文档。

    • seajs 的基本使用

      • 引入包文件

        <script src='http://apps.bdimg.com/libs/seajs/2.3.0/sea.js'></script>
        
        • 1
        • 2
        • 1
        • 2
      • 定义一个模块,比如在这个文件 student.js 中

        define(function(require,exports,module){
            function Student(){
                this.name = '张三';
            }
            // 对外暴露该模块的接口:
            module.exports = new Student();
        })
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
      • 使用模块:通过 seajs.use() 来实现,第一个参数是使用模块的路径,第二个回调函数中的参数是所要使用模块暴露出来的一个接口。

        seajs.use('./student.js',function(stu){
            console.log(stu.name); // 张三
        });
        
        • 1
        • 2
        • 3
        • 4
        • 1
        • 2
        • 3
        • 4
    • seajs 暴露出接口的方式

      • 通过 module.exports 方式导出对象
      • 通过 exports.属性或方法 方式导出
      • 两者的区别和联系:module.exports 和exports 指向的是同一个对象。exports是函数内的一个形式参数,而module.exports是一个实实在在的对象,一般在导出的时候用 module.exports 指向一个新的对象,而最终模块都是用module.exports来导出的。
    • seajs 内部的异步加载简单小demo — 写个小demo来阐述一下这个异步加载机制,在demo中,有以下几个文件 index.html ,loadJs.js ,module1.js:

      1. index.html文件中的关键代码

        <!--引入cdn上的seajs文件-->
        <script src='http://apps.bdimg.com/libs/seajs/2.3.0/sea.js'></script>
        <script src='loadJs.js'></script>
        <script>
            loadJs('module1.js',function(){
                console.log('回调内的函数最后执行');
            });
        </script>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
      2. loadJs.js文件中的代码

        function loadJs(path,callback) {
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            script.setAttribute('src',path);
            head.appendChild(script);
            if(!/*@cc_on!*/false) {
                // 非IE浏览器
                script.onload = function() {
                    console.log('非IE浏览器');
                    callback();
                }
            }else{
                script.onreadystatechange = function(){
                    if(script.readyState === 'loaded' || script.readyState === 'complete') {
                        console.log('IE');
                        callback();
                    }
                }
            }
        }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
      3. module1.js文件中的代码:

        console.log('我是module1文件内的');
        
        • 1
        • 2
        • 1
        • 2
      4. 在Chrome下测试,最后控制台的输出结果:

        我是module1文件内的
        非IE浏览器
        回调内的函数最后执行
        • 1
        • 2
        • 3
        • 1
        • 2
        • 3
      5. 总结:在模块加载的过程中,先执行依赖模块内的逻辑,最后再去执行回调函数内的逻辑

        • seajs 异步加载 require 中的async方法:

          require.async() 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。 require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。

        • seajs 的第三方依赖库的引入:比如 jQuery,我们需要使用jquery,加入到我们的项目中,首先我们需要对jquery 进行改造,在高版本的jquery文件比如2.2中支持amd规范但不支持cmd规范,在文件中的最后找到如下代码:

          if ( typeof define === "function" && define.amd ) {
              define( "jquery", [], function() {
                  return jQuery;
              } );
          }
          • 1
          • 2
          • 3
          • 4
          • 5
          • 1
          • 2
          • 3
          • 4
          • 5

          我们把它改造为:

          if ( typeof define === "function" && (define.amd || define.cmd) ) {
              define( "jquery", [], function() {
                  return jQuery;
              } );
          }
          • 1
          • 2
          • 3
          • 4
          • 5
          • 1
          • 2
          • 3
          • 4
          • 5

          然后我们可以通过加载模块的方式,把jQuery加载进去了。

    • seajs的配置调试:

      • https://github.com/seajs/seajs/issues/262 
        API上讲的很详细。
      • 简单的写一写:

        seajs.config({
          // 别名配置
          alias: {
            'es5-safe': 'gallery/es5-safe/0.9.3/es5-safe',
            'json': 'gallery/json/1.0.2/json',
            'jquery': 'jquery/jquery/1.10.1/jquery'
          },
          // 路径配置
          paths: {
            'gallery': 'https://a.alipayobjects.com/gallery'
          }
        })
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
      • 通过对 sea.js 进行配置,让模块编写、开发调试更方便。

    • seajs 中的module

      • 看 API 中的module Object

    Seajs和RequireJS的区别

    — 引自 玉伯

    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. 两者对调试等的支持有差异。SeaJS 通过插件,可以实现 Fiddler 中自动映射的功能,还可以实现自动 combo 等功能,非常方便便捷。RequireJS 无这方面的支持。
    5. 两者的插件机制有差异。RequireJS 采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采取的插件机制则与 Node 的方式一致:开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。
    6. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
    7. CMD 推崇依赖就近,AMD 推崇依赖前置。
  • 相关阅读:
    命令拷屏之网络工具
    PHP 设计模式 笔记与总结(1)命名空间 与 类的自动载入
    Java实现 计蒜客 1251 仙岛求药
    Java实现 计蒜客 1251 仙岛求药
    Java实现 计蒜客 1251 仙岛求药
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 LeetCode 143 重排链表
    Java实现 LeetCode 143 重排链表
  • 原文地址:https://www.cnblogs.com/libin-1/p/5938605.html
Copyright © 2011-2022 走看看