• AMD规范学习笔记


    背景

    NodeJS的一套比较简洁 Moudles 规范, 使得在服务器端的模块化变得更加简单。很长一段时间,很多公司或者项目都有自己的一套模块化机制, 却未能形成一套统一的标准, NodeJS的Moudles规范如果运用在浏览器端会存在一些问题,如

    1. 服务器端JS模块文件就在本地,浏览器端则需要通过网络请求
    2. 服务器端可以很容易的实现同步或异步请求模块,浏览器端代价会比较大

    采用XHR的方式实现同步请求模块,存在明显的跨域缺陷,而使用script的方式,默认是异步的。 在这样的背景下, CommonJS的Modules/Wrappings、AMD、CMD等规范应时而生。

    Modules/Wrappings 规范的约定如下,更多请参考:http://wiki.commonjs.org/wiki/Modules/Wrappings

    1. 使用modules.declare定义模块
    2. declare 只接受一个参数,类型可以是函数也可以是object,参数又称为module factory
    3. 如果factory为function,三个参数分别为require、exports、module,factory使用返回值或exports导出模块API
    4. factory如果是对象类型,则将该对象作为模块输出

    A basic wrapped module:

    module.declare(function(require, exports, module){
        exports.foo = "bar";
    });

    AMD 规范

    AMD:全称为异步模块定义, 是专门为浏览器中JavaScript环境设计的规范. 规范本身非常简单, 概括如下:

    define(id?, dependencies?, factory);

      id: 模块名

      dependencies: 依赖的模块,如果缺省,默认为 ["require", "exports", "module"]

      factory: 模块工厂,通过排列组合这种模块定义能满足很多场景的需求

    AMD在定义自己的Module规范的同时,也简单兼容了CommonJS的Modules/Wrappings。

    匿名模块与具名模块

    define(["beta"], function (beta) {
        exports.verb = function() {
               return beta.verb();
           }
    });
     
    define('alpha', ["beta"], function (beta) {
        exports.verb = function() {
               return beta.verb();
           }
    });

    匿名模块也带来一些好处,如减少维护成本,使得模块的源代码与它的标识分离。从而实现在不改变模块代码的情况下移动源码文件的位置等。

    Simplified CommonJS wrapping

    define(function(require, exports, module){
        var query = require("query");
        var on = require("on");
        ...
    });

    AMD模块加载器将会扫描该工厂函数的require调用,并自动的在运行该工厂方法之前加载他们, 也是和CMD规范的重要区别, 很多人在讨论到AMD、CMD上都会探讨这个问题,有许多争议, 不过这点也正是体现了AMD规范的核心:模块依赖必须在真正执行具体的factory方法前解决。

    RequireJS是目前最流行的AMD加载器,从代码中可以看出,为了支持CommonJS Wrapping, define会解析工厂函数的FunctionBody,去掉注释,并将require的模块匹配出来, 加到dependencies数组中。

    var commentRegExp = /(/*([sS]*?)*/|([^:]|^)//(.*)$)/mg,
        cjsRequireRegExp = /[^.]s*requires*(s*["']([^'"s]+)["']s*)/g;
     
    if (!deps && isFunction(callback)) {
        deps = [];   
        if (callback.length) {       
            callback           
                .toString()           
                .replace(commentRegExp, '')
                .replace(cjsRequireRegExp, function (match, dep) {               
                    deps.push(dep);
                });
     
            deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
     
        }
    }

    由于实际项目中的FunctionBody情况比较复杂,cjsRequireRegExp,这个匹配规则不是很强大,如果在实际项目中发现require有问题的地方, 可以将FunctionBody手动匹配一下这个正则表达式, 看是否有问题。

    exports有什么好处?

    1、相对使用return输出对象, 由于js语言特性决定,代码依次执行, 当模板存在循环依赖关系时,这种export导出就会特别有用。

    2、相对于命名空间导出API,它的好处在于它不会影响全局空间, 而且导出的API所属的对象名称不会和模块代码强耦合。 这也是YUI的模块API导出方式采用命名空间的一点局限。

    数据或者独立API模块

    对于一些仅仅提供数据或者独立方法的模块,factory格式变为obj就可以了, 如:

    define({
        camelCase: function(x) {
          return string.camelCase('abc ABC');
        }
    });

    AMD Plugin Dependency

    一个插件依赖应该被描述为如下格式:

    [Plugin Module ID]![resource ID]

    目前流行的RequireJS、curl、Dojo等支持AMD的加载器都支持插件, 如加载各种格式的数据,在domReady 完成后在执行操作等。由于“!”被插件语法占用,所以在一般资源请求中,请不要使用带有“!”的URL。 完整的插件清单可以参考http://requirejs.org/docs/download.html#plugins

    下面是两个RequireJS使用插入的例子:

    define([
        'backbone',
        'text!templates.html'
    ], function( Backbone, template ){
       // ...
    });
     
    require(['domReady!'], function (doc) {
        //This function is called once the DOM is ready,
        //notice the value for 'domReady!' is the current
        //document.
    });

    如果让自己的模块支持AMD规范

    jquery是如何做的?

    if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
        define("jquery", [], function () { return jQuery; } );
    }

    需要在加载中设置:

    define.amd = {
        jQuery: true
    }; 

    小结

    AMD仅仅提供了模块定义的规则,在实际项目使用中还需要考虑一些模块合并、打包方案等。

    目前,实现AMD规范的库有RequireJS 、curl 、Dojo 、bdLoad 、JSLocalnet 、Nodules等,也有很多库支持AMD规范,即将自己作为一个模块存在,如MooTools 、jQuery 、qwery 、bonzo、firebug等。 在前端模块化发展如此快的今天,未来应该会有很多库或者模块会支持作为满足AMD、CMD or CommonJS Module规范的模块存在。

    随着模块化的发展,高质量的模块会越来越多,为了让大家有统一的规则来使用模块, 模块规范化就显得格外重要。 而不管是哪一种模块规范,未来在浏览器端前端是否也可以像Nodejs一样灵活的去使用满足相同规范,甚至各种不同规范的优秀模块, 这样就不用将代码局限同一种框架中, 或许只是自己瞎想。

     

    参考:

    http://wiki.commonjs.org/wiki/Modules/1.1.1#Module_Identifiers

    https://github.com/amdjs/amdjs-api

    https://github.com/requirejs/example-multipage

    http://requirejs.org/docs/whyamd.html

  • 相关阅读:
    Java之旅_高级教程_数据结构
    Java之旅_高级教程_多线程编程
    Java之旅_高级教程_URL处理
    Java之旅_高级教程_网络编程
    Java问题汇总
    java之旅_高级教程_java泛型
    Chrome中安装Firebug插件
    Selenium+Python环境搭建
    批处理DOS基础命令
    Appium-两个小报错
  • 原文地址:https://www.cnblogs.com/mininice/p/3875792.html
Copyright © 2020-2023  润新知