教程集 www.jiaochengji.com
教程集 >  脚本编程  >  javascript  >  正文 使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript

使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript

发布时间:2016-09-05   编辑:jiaochengji.com
教程集为您提供使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript等资源,欢迎您收藏本站,我们将为您提供最新的使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript资源

AMD(异步模块定义)和CMD(通用模块定义)越来越受更多人接受,了解一下这些模块定义知识演变和使用方法能够帮助你快速上手相关的javascript框架使用。

Canada Goose makes jackets that will advice you break balmy
、存放在不同模块中的独特功能构成。你可能已经知道,松散耦合通过尽可能地去除依赖性来让可维护性更加简单易得。当这一点被有效实现的时候,系统中某一部分的变化将如何影响其它部分就会变得显而易见。

然而,与一些更传统的编程语言不同的是,JavaScript 的当前版本(ECMA-262) )并没有为开发者们提供以一种简洁、有条理地的方式来引入模块的方法。规范的一大问题,就是未曾在这方面投入足够多的考量。直到近年来,人们对更为有序组织的 JavaScript 应用的需求变得越来越显著,这一情况才有所改观。

作为代替,当前的开发者们只能被迫降级使用模块模式或是对象字面量模式的各种变体。通过很多这样的方法,各模块的脚本被串在一起注入到 DOM 中(译注:感谢 @lkaihua 的确认,指的是作为 script 标签注入到 DOM 中),其命名空间是由单一的全局对象来描述的。你的整个体系架构在这种模式下,仍然有可能发生命名冲突。想要简洁地管理依赖关系,不通过一些手工处理或借助第三方库往往是不可能的。

尽管这些问题的原生解决方案在 ES Harmony 中才会被引入,但好消息是,编写模块化的 JavaScript 目前已经变得极为简单,Canada Goose Jacket cheap sale
甚至今天就可以开始动手。

在本文中,我们将会考察三种编写模块化 JavaScript 的格式:AMDCommonJS 以及 JavaScript 下一个版本——Harmony 中的提案。

RequireJS 和 curl.js。这些工具完整的入门教程已经在本文的讨论范围之外,但我还是推荐大家读一读 John Hann 关于 curl.js 的文章以及 James Burke 的 RequireJS 的 API 文档来了解更多相关内容。

从生产的角度,在处理这样的模块时,使用优化工具(例如 RequireJS Optimizer,即 RequireJS 优化器)来拼合脚本来进行部署是值得推荐的。有趣的是,只要有了 Almond AMD shim,RequireJS 甚至无须加入到部署完的站点之中,你当做脚本加载器的那个东西可以被轻松地从开发过程中移除出去。

尽管如此,James Burke 还是很可能会说,在页面加载完毕以后还能动态加载脚本的使用场景依然存在,并且 RequireJS 在这些场景下也有用武之地。带着上面的这些注解,让我们开始吧。

异步地进行加载。它有很多独特的优势,包括天生的异步及高度灵活等特性,这些特性能够解除常见的代码与模块标识间的那种紧密耦合。它被许多开发者所青睐,可以认为它是迈向 ES Harmony 中提出的模块系统的一块可靠基石。

AMD 最初是一份 CommonJS 列表中模块格式的规范草案,但因为没能达成完整的共识,格式的后续开发就被移到了 amdjs 讨论组中。

目前它已经被包括 Dojo (1.7)、MooTools (2.0)、Firebug (1.8) 甚至 jquery (1.7) 这样的项目所接纳。尽管我们时不时还是会遇到 CommonJS AMD 格式 这个说法,但由于不是所有 CJS 列表上的参与者都愿意支持它,我们最好还是称它为 AMD 或是异步模块支持。

注:曾经有一段时间,这个提案还被称为 Modules Transport/C,但是这个规范并不适合用来传输现有的 CJS 模块,而是适合用来定义模块,所以选择 AMD 这个命名约定就显得更为合理了。

r.js 这样的 CommonJS 环境下运行的 AMD 优化器,开发者就可以在多个不同环境中运行相同的代码。

回到定义的方法签名,dependencies 参数代表了一组对所定义的模块来说必须的依赖项。第三个参数(’definition function’)是一个用来为你的模块执行初始化的函数。一个最简单的模块可以以如下方式定义:

CSS 依赖时已经包含了 css!,但我们要牢记这个方法需要注意的地方:无法真正确认何时 CSS 会被加载完毕。根据你构建代码方式的不同,可能会导致 CSS 作为一个依赖项加入到优化完的文件中,所以在这种情况下将 CSS 作为依赖项来加载需要倍加小心。

JS 加载 AMD 模块

require(['app/myModule'], 
    function( myModule ){
        // 启动主模块,用来轮流加载其它模块
        var module = new myModule();
        module.doStuff();
});

js 加载 AMD 模块

curl(['app/myModule.js'], 
    function( myModule ){
        // 启动主模块,用来轮流加载其它模块
        var module = new myModule();
        module.doStuff();
});

The RequireJS Guide To AMD

What’s the fastest way to load AMD modules?

AMD vs. CJS, what’s the better format?

AMD Is Better For The Web Than CommonJS Modules

The Future Is Modules Not Frameworks

AMD No Longer A CommonJS Specification

On Inventing JavaScript Module Formats And Script Loaders

The AMD Mailing List

dojo.require() 等你惯用的模块加载器所使用。

如果想了解模块引用的话,有一些有趣的小陷阱,在这里了解一下会很有帮助。虽然 AMD 主张的引用模块的方式是将它们声明在依赖列表中,与一组参数相对应,但 Dojo 1.6 的构建系统却不支持这种方式——这只能在完全遵循 AMD 的加载器中正常工作。比如:

define(["dojo/cookie", "dijit/Tooltip"], function( cookie, Tooltip ){
    var cookieValue = cookie("cookieName"); 
    new Tree(...); 
});

这相对嵌套的命名空间而言有很多优势,因为模块不再需要每次都直接用完整的命名空间来引用——我们须要的只是在依赖项中的 ‘dojo/cookie’ 这种路径,一旦通过参数生成了别名,就可以用那个变量来引用了,这样也就无须在应用程序中反复地敲打出 ‘dojo.’ 了。

注:虽然 Dojo 1.6 对基于用户的 AMD 模块并没有提供正式的支持(也不支持异步加载),但是用一些其它的脚本加载器也可以让它们在 Dojo 下正常工作。目前,所有 Dojo 核心与 Dijit 模块都被转换成了 AMD 语法,并且很可能会在 1.7 到 2.0 版本之间从整体上提高对 AMD 的支持。

最后需要注意的小陷阱是,如果你希望继续使用 Dojo 的构建系统或是希望把旧的模块移植到新的 AMD 风格下,那么下面这种更为详细的版本会更易于移植。请注意 dojo 和 dijit 也都被作为依赖项引用:

define(["dojo", "dijit", "dojo/cookie", "dijit/Tooltip"], function(dojo, dijit){
    var cookieValue = dojo.cookie("cookieName");
    new dijit.Tooltip(...);
});

John Hann 最近作了一个关于 AMD 模块设计模式的报告,内容涵盖了单例模式(Singleton)、装饰者模式(Decorator)、中介模式(Mediator)等等。我强烈推荐如果有机会的话去看看他的幻灯片。

下面有几个这些模式的实例:

装饰者(Decorator)模式:

// mylib/UpdatableObservable:dojo/store/Observable 的一个装饰者
define(['dojo', 'dojo/store/Observable'], function ( dojo, Observable ) {
    return function UpdatableObservable ( store ) {

        var observable = dojo.isFunction(store.notify) ? store :
                new Observable(store);

        observable.updated = function( object ) {
            dojo.when(object, function ( itemOrArray ) {
                dojo.forEach( [].concat(itemOrArray), this.notify, this );
            };
        };

        return observable; // 让 `new` 成为可选的
    };
});


// 装饰者使用者
// mylib/UpdatableObservable 的一个使用者

define(['mylib/UpdatableObservable'], function ( makeUpdatable ) {
    var observable, updatable, someItem;
    // ... 获取或得到 `observable` 的代码

    // ... 让 observable store 也变得 updatable
    updatable = makeUpdatable(observable); // `new` is optional!

    // ... 之后,当一条 cometd 消息带着新的数据项到达时
    updatable.updated(updatedItem);
});

适配器(Adapter)模式

// 'mylib/Array' 将 `each` 函数适配为仿 jQuery 的接口:
define(['dojo/_base/lang', 'dojo/_base/array'], function (lang, array) {
    return lang.delegate(array, {
        each: function (arr, lambda) {
            array.forEach(arr, function (item, i) {
                lambda.call(item, i, item); // 就像 jQuery 的 each
            })
        }
    });
});

// 适配器使用者
// 'myapp/my-module':
define(['mylib/Array'], function ( array ) {
    array.each(['uno', 'dos', 'tres'], function (i, esp) {
        // 这里 `this` == item
    });
});

这里讨论了一些关于如何用通用模块定义(UMD,Universal Module Definition)来编写 jQuery 的思路和例子。UMD 定义那些既能在客户端又能在服务器端工作的模块,这样的模块同时也能和目前可用的主流脚本加载器一同工作。虽然这仍然是一个许多概念都还没最终确定的新领域,还是不妨看看标题为 AMD 与 CommonJS 的章节中的代码示例。如果你觉得我们哪里还可以改进,请告诉我。

http://requirejs.org
  • curl.js http://github.com/unscriptable/curl
  • bdLoad http://bdframework.com/bdLoad
  • Yabble http://github.com/jbrantly/yabble
  • PINF http://github.com/pinf/loader-js
  • (还有更多)
  • http://requirejs.org
  • PINF http://github.com/pinf/loader-js
  • IBM 与 BBC iPlayer,这明显体现了企业级开发者们目前有多么真看待这种格式。

    想了解更多那么多开发者选择在应用中使用 AMD 模块的理由,你可能会想看一看 James Burke 的 这篇 文章。

    CommonJS是一个志愿性质的工作组,它致力于设计、规划并标准化 JavaScript API。至今为止他们已经尝试着认可了模块标准以及程序包标准。CommonJS 的模块提案为在服务器端声明模块指定了一个简单的 API。不像 AMD,它试图覆盖更宽泛的方面比如 IO、文件系统、promise 模式等等。

    http://github.com/unscriptable/curl
  • SproutCore 1.1 http://sproutcore.com
  • PINF http://github.com/pinf/loader-js
  • (还有更多)
  • http://nodejs.org
  • Narwhal https://github.com/tlrobinson/narwhal
  • Perseverehttp://www.persvr.org/
  • Wakandahttp://www.wakandasoft.com/
  • Demystifying CommonJS Modules

    JavaScript Growing Up

    The RequireJS Notes On CommonJS

    Taking Baby Steps With Node.js And CommonJS – Creating Custom Modules

    Asynchronous CommonJS Modules for the Browser

    The CommonJS Mailing List

    不需要)、并且着眼于适应未来的场景(在服务器上)。这么说的意思是,CJS 支持未包裹的模块,感觉上更接近 ES.next/Harmony 规范,把你从 AMD 要求的 define() 包裹函数中解放了出来。但 CJS 模块只支持将对象作为模块。

    尽管可能还会有另一个模块格式的想法令人有些气馁,但是你还是可能会有兴趣看一些 AMD/CJS 混合模块和通用 AMD/CJS 模块相关工作的例子。

    UMDjs)

    /**
     * 如果你需要做一些循环依赖或是需要兼容非 Node 的类
     * commonjs 环境,导出基于对象的版本。
     */
    (function (define) {
        //'id' 是可选的,但如果这是一个流行的 web 类库而且
        //通常被用于非 AMD/Node 的环境下,还是推荐给出。然
        //而如果希望生成匿名模块,去掉下面的 'id',并且去掉
        //define 兼容方案中使用的 id。
        define('id', function (require, exports) {
            //如果有兼容项,把它们加载进来
            var a = require('a');
    
            //给 exports 绑定属性。
            exports.name = value;
        });
    }(typeof define === 'function' && define.amd ? define : function (id, factory) {
        if (typeof exports !== 'undefined') {
            //commonjs
            factory(require, exports);
        } else {
            //创建一个全局函数。仅在代码没有依赖项、或
            //是依赖项符合下面的调用模式时可用。
            factory(function(value) {
                return window[value];
            }, (window[id] = {}));
        }
    }));
    

    TC39——负责制定 ECMAScript 语法和语义以及其未来迭代的标准团体,是由一部分非常聪明的开发者组成的。其中的一些人(比如 Alex Russell)在近几年一直在密切关注 JavaScript 在大规模开发中的使用情况的演进,而且也敏感地意识到了需要有更好的语言特性来编写更加模块化的 JS。

    基于这个原因,目前有提案已经提出了一系列令人振奋的对语言的补充,包括灵活的、可以同时在客户端与服务器端使用的 模块、一个模块加载器以及其它。在这个章节中,我将向你展示一些 ES.next 中语法的代码样例,让你能对即将到来的东西一睹为快。(译注:关于 ES.next 与 ES Harmony 的关系,可以参考这篇文章。)

    注:尽管 Harmony 仍然在提案阶段中,多亏了 Google 的 Traceur 编译器,你已经可以(部分地)尝试 ES.next 引入的对编写模块化 JavaScript 进行原生支持的特性。想要立刻获取并运行 Traceur,请阅读这篇入门指南。如果你有兴趣对这个项目了解更多的话,还有一个 JSConf 上相关的演讲也值得一看。

    李松峰老师的这篇文章)以后同样是原型行为的框架或抽象。

    在 Harmony 中,类与构造器一同作为语言的一部分出现,(终于)有了一些真正的私有性。在下面的例子中,我引入了一些行内的注释来帮助你理解类是如何组织的,但是你可能会注意到这里缺少了“函数”这个词汇。这并非一个排印错误:TC39 一直以来都在有意识地努力减少我们对 function 关键字的到处滥用,希望这有助于帮助我们简化代码的编写。

    class Cake{
    
        // 我们可以用 'constructor' 关键字后面紧跟一个公有及私有声明
        // 的参数列表来定义一个类的构造器主体。
        constructor( name, toppings, price, cakeSize ){
            public name = name;
            public cakeSize = cakeSize;
            public toppings = toppings;
            private price = price;
    
        }
    
        // 作为 ES.next 对于减少不必要的到处使用 function 的努力的一部
        // 分,你会看到它在如同下面那样的使用场景中被抛弃了。在这里一个标
        // 识符后面紧跟一个参数列表和一个定义了新方法的主体。
    
        addTopping( topping ){
            public(this).toppings.push(topping);
        }
    
        // Getter 可以通过在标识符、方法名以及花括号主体前
        // 声明一个 get 来定义。
        get allToppings(){
            return public(this).toppings;
        }
    
        get qualifiesForDiscount(){
            return private(this).price > 5;
        }
    
        // 与 getter 类似,setter 也能通过在标识符前使用 'set'
        // 关键字来定义。
        set cakeSize( cSize ){
            if( cSize < 0 ){
                throw new Error('Cake must be a valid size - 
                either small, medium or large');
            }
            public(this).cakeSize = cSize;
        }
    
    
    }
    

    A First Look At The Upcoming JavaScript Modules

    David Herman On JavaScript/ES.Next (Video)

    ES Harmony Module Proposals

    ES Harmony Module Semantics/Structure Rationale

    ES Harmony Class Proposals

    twitter 骚扰我,我会尽力帮忙!

    本文的技术审阅是通过 Diigo(Google Chrome 版)进行的。Diigo 是一个让你能够在任意在线文档中加入评论与高亮的免费工具,如果你发现什么错误或是想建议什么扩展,请用 Diigo(或在 GitHub 上建个 gist),我会亲自做测试来处理任何你发过来的点子。

    原文:http://justineo.github.io/singles/writing-modular-js/

    您可能感兴趣的文章:
    使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript
    浅谈模块化的JavaScript
    浅谈 JavaScript 模块化编程
    javaScript 依赖管理
    JavaScript笔记:CMD模块定义规范
    ECMAScript 6新特性印象之二:面对对象和模块化
    Sublime Text内运行javascript(ES6)
    前端模块化杂谈
    Javascript自动化文档工具:YUI Doc, JSDoc 3, JSDuck等比较
    javascript模块化之require.js实例教程

    [关闭]

    ~ ~