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 推崇依赖前置。
  • 相关阅读:
    python lambda函数的用法
    python 中is 和 ==的区别
    Mongo 聚合函数 $group方法的使用
    mongo聚合
    当mongo数据库报错关于 Failed global initialization:
    python 中字符串的拼接
    python eval()用法报错 SyntaxError: unexpected EOF while parsing
    高性能MySQL(六):选择合适的存储引擎
    高性能MySQL(五):存储引擎
    高性能MySQL(四):多版本并发控制
  • 原文地址:https://www.cnblogs.com/libin-1/p/5938605.html
Copyright © 2011-2022 走看看