资讯专栏INFORMATION COLUMN

RequireJS进阶:模块的定义与加载

legendmohe / 1020人阅读

摘要:将模块定义为一个函数对模块的返回值类型并没有强制为一定是个,任何函数的返回值都是允许的。此处是一个返回了函数的模块定义点评加载该模块后,返回值是一个闭包。仅支持返回值类型为的服务,其他返回类型如数组字串数字等都不能支持。

概述

模块不同于传统的脚本文件,它良好地定义了一个作用域来避免全局名称空间污染。它可以显式地列出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入,而无需引用全局变量。RequireJS的模块是模块模式的一个扩展,其好处是无需全局地引用其他模块。

RequireJS的模块语法允许它尽快地加载多个模块,虽然加载的顺序不定,但依赖的顺序最终是正确的。同时因为无需创建全局变量,甚至可以做到在同一个页面上同时加载同一模块的不同版本。

(如果你熟悉ConmmonJS,可参看CommonJS的注释信息以了解RequireJS模块到CommonJS模块的映射关系)。
一个磁盘文件应该只定义 1 个模块。多个模块可以使用内置优化工具将其组织打包。

模块的定义 简单的键值对

如果一个模块仅含值对,没有任何依赖,则在define()中定义这些值对就好了:

define({
    name: "john",
    age : 20,
    sex : "femal"
});

点评:这种情况用途不是很大,一般用于简单的数据模拟。加载该模块后,回调函数中注入的参数是该键值对Object对象,即:

 {
    name: "john",
    age : 20,
    sex : "femal"
 }
函数式定义

如果一个模块没有任何依赖,但需要一个做setup工作的函数,则在define()中定义该函数,并将其传给define():

define(function () {
    //Do setup work here

    return {
        color: "black",
        size: "unisize"
    }
});

点评:这种适合无依赖模块的编写,且回调函数中注入的参数是根据函数中return的返回值确定的,如果没有return语句,则默认返回undefined

存在依赖的函数式定义

如果模块存在依赖:则第一个参数是依赖的名称数组;第二个参数是函数,在模块的所有依赖加载完毕后,该函数会被调用来定义该模块,因此该模块应该返回一个定义了本模块的object。依赖关系会以参数的形式注入到该函数上,参数列表与依赖名称列表一一对应。

//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
        //return an object to define the "my/shirt" module.
        return {
            color: "blue",
            size: "large",
            addToCart: function() {
                inventory.decrement(this);
                cart.add(this);
            }
        }
    }
);

本示例创建了一个my/shirt模块,它依赖于my/cart及my/inventory。磁盘上各文件分布如下:

my/cart.js

my/inventory.js

my/shirt.js

模块函数以参数"cart"及"inventory"使用这两个以"./cart"及"./inventory"名称指定的模块。在这两个模块加载完毕之前,模块函数不会被调用

严重不鼓励模块定义全局变量。遵循此处的定义模式,可以使得同一模块的不同版本并存于同一个页面上(参见 高级用法 )。另外,函参的顺序应与依赖顺序保存一致

返回的object定义了"my/shirt"模块。这种定义模式下,"my/shirt"不作为一个全局变量而存在。

点评:这算是模块中最常见的用法了。注意上面强调的部分。

将模块定义为一个函数

对模块的返回值类型并没有强制为一定是个object,任何函数的返回值都是允许的。此处是一个返回了函数的模块定义:

//A module definition inside foo/title.js. It uses
//my/cart and my/inventory modules from before,
//but since foo/title.js is in a different directory than
//the "my" modules, it uses the "my" in the module dependency
//name to find them. The "my" part of the name can be mapped
//to any directory, but by default, it is assumed to be a
//sibling to the "foo" directory.
define(["my/cart", "my/inventory"],
    function(cart, inventory) {
        //return a function to define "foo/title".
        //It gets or sets the window title.
        return function(title) {
            return title ? (window.title = title) :
                   inventory.storeName + " " + cart.name;
        }
    }
);

点评:加载该模块后,返回值是一个闭包。

简单包装CommonJS来定义模块

如果你现有一些以CommonJS模块格式编写的代码,而这些代码难于使用上述依赖名称数组参数的形式来重构,你可以考虑直接将这些依赖对应到一些本地变量中进行使用。你可以使用一个CommonJS的简单包装来实现:

define(function(require, exports, module) {
        var a = require("a"),
            b = require("b");

        //Return the module value
        return function () {};
    }
);

该包装方法依靠Function.prototype.toString()将函数内容赋予一个有意义的字串值,但在一些设备如PS3及一些老的Opera手机浏览器中不起作用。考虑在这些设备上使用优化器将依赖导出为数组形式。

更多的信息可参看CommonJS Notes页面,以及"Why AMD"页面的"Sugar"段落。

点评:这种用法跟上面说的一样,首先回调用Function.prototype.toString()将函数转换成字符串,然后在进行词法分析,这样依赖,就比较低耗了,原则上不考虑使用这种方法进行模块封装。

定义一个命名模块

你可能会看到一些define()中包含了一个模块名称作为首个参数:

    //Explicitly defines the "foo/title" module:
    define("foo/title",
        ["my/cart", "my/inventory"],
        function(cart, inventory) {
            //Define foo/title object in here.
       }
    );

这些常由优化工具生成。你也可以自己显式指定模块名称,但这使模块更不具备移植性——就是说若你将文件移动到其他目录下,你就得重命名。一般最好避免对模块硬编码,而是交给优化工具去生成。优化工具需要生成模块名以将多个模块打成一个包,加快到浏览器的载人速度。

点评:define 函数有三个参数,第一个 id 即模块名称,这个名称的格式是相对于 baseUrl 的路径除去文件格式,比如 baseUrl 为 js 目录,一个模块放在 js/libs/hi.js 里,则如果名称是这样定义的:

define("libs/hi", ["jquery"], function($){......});

这样的定义形式的好处是,模块不可能冲突,因为同一目录下不允许同名文件。但也因此 require.js 建议我们不要设置模块名称,因为设置了 ‘libs/hi’ 的模块名称后,模块就必须放在 js/libs 目录下的 hi.js 文件中,要移动位置的话,模块名称要跟着改变。至于后期利用 r.js 优化时生成了模块名称,那已经是另外一回事。所以定义模块时,第一参数常常会忽略。

JSONP服务依赖

JSONP是在javascript中服务调用的一种方式。它仅需简单地通过一个script标签发起HTTP GET请求,是实现跨域服务调用一种公认手段。

为了在RequireJS中使用JSON服务,须要将callback参数的值指定为"define"。这意味着你可将获取到的JSONP URL的值看成是一个模块定义。

下面是一个调用JSONP API端点的示例。该示例中,JSONP的callback参数为"callback",因此"callback=define"告诉API将JSON响应包裹到一个"define()"中:

require(["http://example.com/api/data.json?callback=define"],
    function (data) {
        //The data object will be the API response for the
        //JSONP data call.
        console.log(data);
    }
);

JSONP的这种用法应仅限于应用的初始化中。一旦JSONP服务超时,其他通过define()定义了的模块也可能得不得执行,错误处理不是十分健壮。

仅支持返回值类型为JSON object的JSONP服务,其他返回类型如数组、字串、数字等都不能支持。

这种功能不该用于long-polling类的JSONP连接——那些用来处理实时流的API。这些API在接收响应后一般会做script的清理,而RequireJS则只能获取该JSONP URL一次——后继使用require()或define()发起的的对同一URL的依赖(请求)只会得到一个缓存过的值。

JSONP调用错误一般以服务超时的形式出现,因为简单加载一个script标签一般不会得到很 详细的网络错误信息。你可以override requirejs.onError()来过去错误。更多的信息请参看错误处理部分。

点评:跨域脚本调用。

模块的加载

常见模块加载调用的形式如下:

require(["jquery"],function($){

    //todo

});

第一个参数指定要加载的模块名,第二个是相对应的注入参数。

帮助文档

官网API文档:http://requirejs.org/docs/api.html#define
中文API文档:http://requirejs.cn/docs/api.html#define
中文API文档:http://makingmobile.org/docs/tools/requirejs-api-zh/#define
中文文档:http://www.zfanw.com/blog/require-js.html
中文文档:http://www.fanli7.net/a/bianchengyuyan/ASP/20130419/300243.html

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85410.html

相关文章

  • RequireJS进阶:配置文件学习

    摘要:概述强大灵活的运用是通过配置文件决定的。下面通过示例来进行深度的探讨配置文件的使用。配置文件的位置配置文件的位置和声明用法是相对于这个脚本文件来决定的。配置文件参数的介绍所有模块的查找根路径。 概述 Requires强大灵活的运用是通过配置文件决定的。通过配置文件我们可以给模块取别名、给模块加上版本标识、设置模块依赖、包装非模块等强大功能。同时RequireJS的优化器也大量使用了配...

    lemon 评论0 收藏0
  • JS进阶篇--RequireJS模块化编程详解

    摘要:所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 1.模块的写法 模块化编程一般都有这么几个过渡过程,如下描述。 原始方法 function m1(){   //... } function m2(){   //... } 上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。 这种做法的缺点很明显:污染了全局变量,无法保证不与...

    妤锋シ 评论0 收藏0
  • 前端资源系列(4)-前端学习资源分享&前端面试资源汇总

    摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...

    princekin 评论0 收藏0
  • 前端基础进阶(十三):透彻掌握Promise使用,读这篇就够了

    摘要:在对象的构造函数中,将一个函数作为第一个参数。二对象中的方法,可以接收构造函数中处理的状态变化,并分别对应执行。 showImg(https://segmentfault.com/img/remote/1460000008932857); Promise的重要性我认为我没有必要多讲,概括起来说就是必须得掌握,而且还要掌握透彻。这篇文章的开头,主要跟大家分析一下,为什么会有Promise...

    yy736044583 评论0 收藏0
  • RequireJS学习笔记

    摘要:如果有疑惑的地方,欢迎讨论,我是初学,希望能切磋和得到指点加载会阻塞页面加载默认异步加载文件方法一把放到页面底部加载方法二支持定义全局相对路径方法一自定义属性指定网页程序的主模块文件定义整个网页代码的入口文件的相对位置,以后此文件 如果有疑惑的地方,欢迎讨论,我是初学,希望能切磋和得到指点; js加载会阻塞页面加载: //requirejs默认异步加载js文件; 方法一...

    hersion 评论0 收藏0

发表评论

0条评论

legendmohe

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<