require.js读书笔记 2.usage

USAGE§ 1

Load JavaScript Files 加载javascript文件夹§ 1.1

requireJs用一种新的script加载方法,这种方法和传统<script>标签是彻底不一样的。它能够运行地更快,而且进行更好地优化,它的主要目的就是为了支持(encourage)模块化(modular)代码的加载。做为其中的一部分,它支持利用模块ID来加载script,而不是script标签里的url属性。
javascript

requireJs加载的全部代码地址都是相对于baseUrl的。页面顶层script标签有一个特殊的属性data-main,require.js用它来启动脚本加载页面,而baseUrl一般设置成这个标签所在的文件夹里。data-main attribute是一个特殊的属性,require.js会用这个属性进行加载。下面这个例子会展现了baseUrl的设置:css

<!--This sets the baseUrl to the "scripts" directory, and
    loads a script that will have a module ID of 'main'-->
<script data-main="scripts/main.js" src="scripts/require.js"></script>

或者,baseUrl能够经过RequireJS config手动(manually)地设置。若是没有明确的(explicit)config设置,或者没有使用data-main属性,那么默认的baseUrl就是包含requireJs的HTML页面所在的目录。html

requireJs默认全部依赖(dependence)资源都死scripts,因此写模块ID时不须要.js的后缀。requireJs翻译模块ID的路径时会自动加上.js尾缀的。运用paths config标签,你能够设置一组scripts脚本的位置。相对于<script>标签,这些能让你用更少的字符来加载script。java

有时候你想直接饮用一个script,而不是依照(conform)“baseUrl+paths"规则来找它。若是一个模块ID由如下之一的规则,这个ID就不会经过”baseUrl+paths"配置来加载script,而是像普通的script url属性来加载。jquery

  • 以.js结束
  • 以“/”开始
  • url协议(protocol),像 "http:" or "https:".

一般地说,最好经过baseUrl和paths来设置模块ID的路径。这样所,你能够很方便地重命名和重定位脚本(configuring the paths to different locations)。git

类似的,为了不配置凌乱,最好避免多级嵌套(deep folder hierarchies)的方式来加载代码。要么将全部的scripts放在baseUrl的目录中,否则将你的代码分置为目录(library)/第三方目录库(vendor)的结构,能够像如下所示:github

  • www/
    • index.html
    • js/
      • app/
        • sub.js
      • lib/
        • jquery.js
        • canvas.js
      • app.js

in index.html:web

<script data-main="js/app.js" src="js/require.js"></script>

and in app.js:json

requirejs.config({
    //设置默认模块ID的路径 js/lib
    baseUrl: 'js/lib',
    //另外,若是模块ID以app开始,
    //它会从js/app目录加载。paths设置时相对于baseUrl的,毫不会包括“.js”的,可是paths设置能够是相对directory的
    paths: {
        app: '../app'
    }
});

开始main app的逻辑。
requirejs(['jquery', 'canvas', 'app/sub'], function ($, canvas, sub) { //jQuery, canvas and the app/sub module are all //loaded and can be used here now. });

在这个例子中,第三方库(vendor实际上是供应商的意思)如jQuery,并无将它的版本号显示在文件名中。若是你想跟踪版本号,建议新开一个单独的文件来记录,或者你能够用一些工具,像volo,能够将package.json打上版本信息,但文件名仍是jQuery.js。这有助于你的配置最小化,避免为每一个版本的库设置paths路径。例如,将"jquery"配置成(configure)“jquery-1,7,2"canvas

理想状态下(ideally),每一个加载的脚本都是经过define()来定义的一个模块。然而,有些"浏览器全局变量注入"型传统/遗留(legacy)浏览器可能不能用define()来定义它们的依赖关系。为此(for those),你能够用shim config来解析它们的依赖关系。

若是你不想解析它们的依赖关系,你可能会获得一些加载错误,基于速度的缘由(for speed),requireJs会异步( asynchronously)、无序(out of order)地加载脚本。

 

data-main Entry Point§ 1.2

data-main属性是一个特殊属性,require.js在加载脚本的时候会检查(check)它:

<!--当require.js加载的时候,它会忽视script/main.js的其余script标签属性-->
<script data-main="scripts/main" src="scripts/require.js"></script>

你能够在data-main中设置配置选项,而后加载你的第一个应用模块(application module)。注意:require.js的标签加载的模块是异步的async attribute。这意味着,若是你在这个页面加载了其余scripts,则不能保证经过require.js加载的页面能够先于这些脚本加载完毕。

举个例子,如下的构造不能保证foo模块的require.config的路径设置会先于require()foo模块执行:

<script data-main="scripts/main" src="scripts/require.js"></script>
<script src="scripts/other.js"></script>
// contents of main.js:
require.config({
    paths: {
        foo: 'libs/foo-1.1.3'
    }
});
// contents of other.js:

//这段代码可能会在main.js 的require.config()以前执行。若是这放生了,require.js会加载'scripts/foo.js‘额不是'scripts/libs/foo-1.1.3.js

require(['foo'], function(foo) { });

若是你想在HTML页面中调用require(),最好不要用data-main。data-main只用在页面只须要一个入口的时候。若是页面想在行内调用require(),最好以下所示书写代码

<script src="scripts/require.js"></script>
<script>
require(['scripts/config']), function() {
    // Configuration loaded now, safe to do other require calls
    // that depend on that config.
    require(['foo'], function(foo) {

    });
});
</script>

Define a Module§ 1.3

一个模块不一样于传统脚本文件的地方是,它定义了一个模块范围来避免污染全局环境。它明确地列举了依赖的文件,并以函数(定义那个模块的函数)参数的形式将这些依赖注入。RequireJs的模块是Module Pattern的一个扩展,这样的好处是不须要全局地引入其余模块。

RequireJs的模块属性让它们能够尽快地被加载,就算加载是无序的,依赖也会按照争取的顺序。由于没有建立全局变量,因此在一个页面中能够建立多个版本的模块load multiple versions of a module in a page.

(若是你熟悉或者使用过commonjs,那么请看 CommonJS Notes来了解requireJs和CommonJs的映射(map to)关系)

一个磁盘文件应该只定义一个模块。optimization tool工具能够将模块分组优化(grouped into optimized bundles).

Simple Name/Value Pairs§ 1.3.1

若是一个模块没有任何依赖,仅含任何值/对(name/value),则在define()中定义这些值/对就行了。

//Inside file my/shirt.js:
define({
    color: "black",
    size: "unisize"
});

Definition Functions§ 1.3.2

若是以个模块没有依赖,可是须要一些函数来作一些setup的工做,那就在define中定义该函数:

//my/shirt.js now does setup work
//before returning its module definition.
define(function () {
    //Do setup work here

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

Definition Functions with Dependencies§ 1.3.3

若是一个模块有依赖,第一个参数(arguments)应该是一串依赖名的数组,第二个参数应该是定义的函数。一旦全部的依赖加载完毕,这个函数就会被调用来定义该模块。定义这个模块的函数应该返回一个对象。依赖会以一个参数的形式传给函数,参数列表和依赖名称列表一一对应

//my/shirt.js 有一些依赖, cart和inventory,都和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/cart.js
  • my/inventory.js
  • my/shirt.js

这个函数有两个参数“cart”和“inventory”。对应的模块以模块名"./cart" 和"./inventory"展现。

这个函数会在my/cart ,my/inventory被加载后调用,而且得到cart和inventory的函数参数。

严重不容许模块定义全局的变量,这样,多版本的模块才能存在于同一个页面。另外,函数的参数顺序应该和依赖模块的顺序同样。

返回的对象定义了"my/shirt"模块。这样定义模块,"my/shirt"就不会以全局对象的方式存在。

 

Define a Module as a Function§ 1.3.4

模块的并不必定要有返回值。任何函数的返回值(return value)都是容许的。此处有一个模块如它所定义地返回了一个函数:

/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;
        }
    }
);

 

 

Define a Module with Simplified CommonJS Wrapper§ 1.3.5

若是你想从新用一些以CommonJS module format的方式写的代码,而这些代码难以用上述的依赖数组的方式来写,你能够考虑直接将这些依赖对应到本地变量。你可使用一个简单的commonJs包装来实现simplified CommonJS wrapper

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

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

这个包装依赖于Function.prototype.toString()将函数内容赋予有意义的字符串。这PS3或者一些老的Opera手机浏览器上表现很差。用optimizer将这些依赖变成一个数组的格式,以便与在这些设备上用。
更多的信息参考 CommonJS page,  "Sugar" section in the Why AMD page.

 

Define a Module with a Name§ 1.3.6

你可能会看到一个define()中包含了模块名做为第一个参数:

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

这些一般是optimization tool生成的。你能够本身命名模块名,但这会使模块更难移植——若是你将文件夹移到另外一个目录中,你还得改它们的名字。最好避免对模块硬编码,而让optimization tool来生成模块名。这个工具须要生成模块名,以便将这些模块打成一个包(be bundled),使浏览器更快加载。

Other Module Notes§ 1.3.7

One module per file.: 每一个js文件只能定义一个模块,这是模块寻找机制的天然要求。多个模块会被 optimization tool打包成一个模块,但你须要用将多个模块放到一个文件夹里.

Relative module names inside define(): 对于define()中("./relative/name")的模块调用,确保将require自己做为一个依赖注入到模块中, 这样,相对路径的名称才能被正确地解析:

define(["require", "./relative/name"], function(require) {
    var mod = require("./relative/name");
});

更好的方式是,像commomjs同样使用一个更短的解析:

define(function(require) {
    var mod = require("./relative/name");
});

这个模块利用Function.prototype.toString()去寻找require()的调用,并把它们加到依赖的数组中。在require()的工做路径下,代码会被正确的解析。

若是你在一个目录中建立了不少模块,相对路径一般是颇有用的。你分享这个目录给其余人,你也能够对这个目录中的其余模块作一些改动,而没必要知道模块的名称。

Generate URLs relative to module: 你可能须要生成一个相对于模块的URL. 你能够将require做为一个依赖注入,而后用require.toUrl()来生成一个url:

define(["require"], function(require) {
    var cssUrl = require.toUrl("./style.css");
});

Console debugging: 若是你想调用一个已经经过 require(["module/name"] , function(){}) 方式加载的模块,你能够用字符串做为模块名的参数来调用它:

require("module/name").callSomeFunction()

注意,这个只对于已经用"module/name"方式异步加载的模块有效。若是用一个相对路径,像'./module/name',那只会在define内部工做有效。

Circular Dependencies§ 1.3.8

若是你定义了一个循环依赖("a" needs "b" and "b" needs "a"), 那么当"b"模块被调用的时候, 它会获得一个未定义的a值。“b”能够在已经被require()定义好后来获取“a”(确保将require做为一个依赖注入):

//Inside b.js:
define(["require", "a"],
    function(require, a) {
        //"a" in this case will be null if "a" also asked for "b",
        //a circular dependency.
        return function(title) {
            return require("a").doSomething();
        }
    }
);

一般,你不须要用require()来获取模块, 只要将依赖的模块做为参数传给函数就能够了.循环依赖是不多出现的,若是出现了,你就想从新考虑设计问题了。然而,有时候也是须要循环依赖的,若是这样的话,能够考虑上述的代码组织。

若是你很是熟悉commonJs,你也能够用exports的来制造一个空模块,这也能够以依赖的形式被其余模块获取。这样作的话,你能够在循环依赖的任意一方安全地使用其余模块。.这只在每一个模块都以object做为模块值输出的时候有效,函数形式无效:

//Inside b.js:
define(function(require, exports, module) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.
    var a = require("a");

    exports.foo = function () {
        return a.bar();
    };
});

或者,若是你再用数组做为依赖,能够调用特殊的exports模块 'exports' dependency:

//Inside b.js:
define(['a', 'exports'], function(a, exports) {
    //If "a" has used exports, then we have a real
    //object reference here. However, we cannot use
    //any of "a"'s properties until after "b" returns a value.

    exports.foo = function () {
        return a.bar();
    };
});

Specify a JSONP Service Dependency§ 1.3.9

JSONP 是javascript中服务调用的一种方式。它经过script标签发起HTTP GET请求,是实现跨域的一种公认手段。

要在requireJs使用JSONP,要在回调中将参数属性设置为”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服务的初始化initial application setup. 一旦JSONP服务超时,意味着其余经过define()的模块不会执行,因此这种错误处理是不强健的。

Only JSONP return values that are JSON objects are supported.一个JSONP的返回是一个数组,若是是一个字符串或者数字式不会工做的。

这个机制不能被用在long-polling 类的JSONP链接-- 那些用来处理实时流的APIs. 这种类型的api在收到每一个返回的时候通常会作script清理,RequireJS只会获取一次JSONP URL—— 后继使用require()或者define()发起的URL会获得一个缓存值。

加载JSONP服务的错误一般以服务超时的形式出现。由于简单加载一个script标签不会获得不少网络错误信息。你能够用override requirejs.onError() 来获取错误。更多信息参见 Handling Errors section.

Undefining a Module§ 1.3.10

有一个全局的函数requirejs.undef(), 用来undefining一个模块.它会重设加载器的内部(internal)状态来消除前一个模块的定义。

然而,若是一个模块已经被定义了,而且在其余模块中做为一个依赖被调用执行了,全局函数是不会清除这个模块的。 因此它仅在无其余模块持有该模块错误的时候有用,或者当将来须要加载这模块时有点用。 See the errback section for an example.

若是想知道更多关于undefined的复杂语法,the semi-private onResourceLoad API may be helpful.

相关文章
相关标签/搜索