胖蔡叨叨叨
你听我说

RequireJs API基本使用

加载JavaScript文件

与传统的<script>标记相比,RequireJS采用了不同的脚本加载方式。尽管它还可以快速运行并优化得很好,但主要目标是鼓励使用模块化代码。作为其一部分,它鼓励使用模块ID代替脚本标记的URL。

RequireJS加载相对于baseUrl的所有代码。通常,将baseUrl设置为与data-main属性中使用的脚本相同的目录,以使顶级脚本加载页面。该数据主要属性是一个特殊的属性,require.js将检查启动脚本加载。本示例将以脚本的baseUrl结尾:

<!--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>

或者,可以通过RequireJS config手动设置baseUrl。如果没有显式配置且未使用data-main,则默认的baseUrl是包含运行RequireJS的HTML页面的目录。

默认情况下,RequireJS还假定所有依赖项都是脚本,因此它不希望在模块ID上看到尾随的“ .js”后缀。在将模块ID转换为路径时,RequireJS将自动添加它。使用path config,可以设置一组脚本的位置。与传统的<script>标记相比,所有这些功能都允许您为脚本使用较小的字符串。

有时您可能确实想直接引用脚本,而又不遵循“ baseUrl +路径”规则来查找脚本。如果模块ID具有以下特征之一,则该ID将不会通过“ baseUrl +路径”配置传递,而只会被视为与文档相关的常规URL:

  • 以“ .js”结尾。
  • 以“ /”开头。
  • 包含URL协议,例如“ http:”或“ https:”。

通常,最好使用baseUrl和“ paths”配置来设置模块ID的路径。这样,它为您重命名和配置指向不同位置的路径以提供更大的灵活性,以进行优化构建。

同样,为避免进行大量配置,最好避免对脚本使用较深的文件夹层次结构,而是将所有脚本都保留在baseUrl中,或者如果要将库/供应商提供的代码与应用程序代码分开,请使用像这样的目录布局:

  • 万维网/
    • index.html
    • js /
      • 应用程序/
        • sub.js
      • lib /
        • jquery.js
        • canvas.js
      • app.js
      • require.js

在index.html中:

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

并在app.js中:

requirejs.config({
    //By default load any module IDs from js/lib
    baseUrl: 'js/lib',
    //except, if the module ID starts with "app",
    //load it from the js/app directory. paths
    //config is relative to the baseUrl, and
    //never includes a ".js" extension since
    //the paths config could be for a directory.
    paths: {
        app: '../app'
    }
});

// Start the main app logic.
requirejs(['jquery', 'canvas', 'app/sub'],
function   ($,        canvas,   sub) {
    //jQuery, canvas and the app/sub module are all
    //loaded and can be used here now.
});

请注意,作为该示例的一部分,像jQuery这样的供应商库在文件名中没有其版本号。如果要跟踪版本信息,建议将其存储在单独的文本文件中,或者如果使用诸如volo之类的工具,它将在package.json中标记版本信息,但将文件保留在磁盘上为“ jquery”。 js”。这使您可以进行非常小的配置,而不必在每个库的“路径”配置中放置一个条目。例如,将“ jquery”配置为“ jquery-1.7.2”。

理想情况下,您加载的脚本将是通过调用define()定义的模块。但是,您可能需要使用某些传统/旧版的“浏览器全局变量”脚本,这些脚本无法通过define()表达它们的依赖性。对于那些,您可以使用shim config。正确表达其依赖性。

如果不表达依赖关系,则由于RequireJS异步加载脚本且速度不佳,可能会导致加载错误。

数据主入口点

data-main属性是一个特殊属性,require.js将检查该属性以开始脚本加载:

<!--when require.js loads it will inject another script tag
    (with async attribute) for scripts/main.js-->
<script data-main="scripts/main" src="scripts/require.js"></script>

通常,您将使用数据主脚本来设置配置选项,然后加载第一个应用程序模块。注意:为您的数据主模块生成的脚本标签require.js包含async属性。这意味着您不能假定数据主脚本的加载和执行将在同一页面稍后引用的其他脚本之前完成。

例如,当在稍后需要require()之前未设置’foo’模块的require.config路径时,这种安排将随机失败:

<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:

// This code might be called before the require.config() in main.js
// has executed. When that happens, require.js will attempt to
// load 'scripts/foo.js' instead of 'scripts/libs/foo-1.1.3.js'
require(['foo'], function(foo) {

});

如果要require()在HTML页面中进行调用,则最好不要使用data-main。data-main仅在页面只有一个主要入口点即data-main脚本时使用。对于要进行内联require()调用的页面,最好将那些页面嵌套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>

定义模块

模块与传统脚本文件的不同之处在于,它定义了一个范围广泛的对象,可避免污染全局名称空间。它可以显式列出其依赖关系,并在无需引用全局对象的情况下获取这些依赖关系的句柄,而是将依赖关系作为定义模块的函数的参数来接收。RequireJS中的模块Module Pattern的扩展,其优点是不需要全局变量来引用其他模块。

模块的RequireJS语法允许它们以尽可能快的速度加载,即使顺序混乱也可以,但是以正确的依赖关系顺序进行评估,并且由于未创建全局变量,因此可以在页面中加载模块的多个版本

(如果您熟悉或正在使用CommonJS模块,那么也请参阅CommonJS Notes以获取有关RequireJS模块格式如何映射到CommonJS模块的信息)。

磁盘上每个文件应该只有一个模块定义。可以通过优化工具将模块分组为优化的捆绑包。

简单名称/值对

如果模块没有任何依赖关系,而只是名称/值对的集合,则只需将对象文字传递给define():

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

定义功能

如果模块没有依赖项,但是需要使用函数来完成一些设置工作,则定义自己,然后将函数传递给define():

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

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

具有依赖关系的定义函数

如果模块具有依赖项,则第一个参数应为依赖项名称数组,第二个参数应为定义函数。加载所有依赖项后,将调用该函数以定义模块。该函数应返回定义模块的对象。依赖项将作为函数参数传递给定义函数,并以与依赖项数组中的顺序相同的顺序列出:

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

在此示例中,创建了我/衬衫模块。这取决于我/购物车和我/库存。在磁盘上,文件的结构如下:

  • 我/cart.js
  • 我/inventory.js
  • 我的/shirt.js

上面的函数调用指定了两个参数:“购物车”和“库存”。这些是由“ ./cart”和“ ./inventory”模块名称表示的模块。

在加载了my / cart和my / inventory模块之后,该函数才被调用,并且该函数将这些模块作为“ cart”和“ inventory”自变量接收。

明确建议不要使用定义全局变量的模块,以便一次在一个页面中可以存在一个模块的多个版本(请参阅高级用法)。此外,函数参数的顺序应与依赖项的顺序匹配。

函数调用的返回对象定义“我/衬衫”模块。通过以这种方式定义模块,“ my / shirt”就不会作为全局对象存在。

将模块定义为函数

模块不必返回对象。允许来自函数的任何有效返回值。这是一个返回函数作为其模块定义的模块:

//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页面,以及“为什么选择AMD”页面中的“糖”部分

用名称定义模块

您可能会遇到一些define()调用,其中包含模块的名称作为define()的第一个参数:

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

这些通常是由优化工具生成的。您可以自己明确命名模块,但这会使模块的可移植性降低-如果将文件移动到另一个目录,则需要更改名称。通常最好避免为模块的名称编码,而让优化工具以模块名称进行刻录。优化工具需要添加名称,以便一个文件中可以捆绑多个模块,以允许在浏览器中更快地加载。

其他模块说明

每个文件一个模块。注意:鉴于模块名称到文件路径查找算法的性质,每个JavaScript文件只能定义一个模块。您仅应使用优化工具将多个模块分组为优化文件。

define()中的相对模块名称:对于在define()函数调用中可能发生的require(“ ../ relative / name”)调用,请务必要求“ require”作为依赖项,以便解析相对名称正确地:

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

更妙的是,使用可用于翻译CommonJS模块的缩短的语法:

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

这种形式将使用Function.prototype.toString()查找require()调用,并将它们与“ require”一起添加到依赖项数组中,因此代码将在相对路径下正常工作。

如果要在目录中创建一些模块,则相对路径非常有用,这样您就可以与其他人或其他项目共享目录,并且希望能够获得该目录中同级模块的句柄,而不必知道目录的名称。

相对模块名称相对于其他名称,而不是路径:加载程序按模块名称而不是内部路径存储模块。因此,对于相对名称引用,将相对于作为引用的模块名称进行解析,然后,如果需要加载,则将该模块名称或ID转换为路径。其中具有“ main”和“ extras”模块的“ compute”包的示例代码:

* lib/
    * compute/
        * main.js
        * extras.js

main.js模块如下所示:

define(["./extras"], function(extras) {
    //Uses extras in here.
});

如果这是路径配置:

require.config({
    baseUrl: 'lib',
    paths: {
      'compute': 'compute/main'
    }
});

并且require(['compute'])做了,那么LIB /计算/ main.js将有“计算”的模块名称。当它要求“ ./extras”时,相对于“ compute”可以解决,因此“ compute /./ extras”将其标准化为“ extras”。由于没有用于该模块名称的路径配置,因此生成的路径将是针对“ lib / extras.js”的,这是不正确的。

在这种情况下,packages config是一个更好的选择,因为它允许将主模块设置为“计算”,但是在内部加载程序将存储ID为“ compute / main”的模块,以便“ ./演员的作品。

另一个选择是在lib / compute.js处构建一个模块just define(['./compute/main'], function(m) { return m; });,那么就不需要路径或程序包配置了。

或者,不要设置路径或程序包的配置,并且不要在顶层要求调用as require(['compute/main'])

生成相对于模块的URL:您可能需要生成相对于模块的URL。为此,要求“ require”作为依赖项,然后使用require.toUrl()生成URL:

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

控制台调试:如果需要使用已经通过require(["module/name"], function(){})JavaScript控制台中的调用加载的模块,则可以使用require()形式,该形式仅使用模块的字符串名称来获取它:

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

请注意,这仅在先前通过require:的异步版本加载“模块/名称”时有效require(["module/name"])。如果使用相对路径,例如“ ./module/name”,则这些仅在内部定义

循环依赖

如果定义循环依赖关系(“ a”需要“ b”,“ b”需要“ a”),则在这种情况下,当调用“ b”的模块函数时,它将获得“ a”的未定义值。在使用require()方法定义模块之后,“ b”可以稍后再获取“ a”(请确保将require指定为依赖项,以便使用正确的上下文来查找“ a”):

//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()来获取模块,而是依靠将模块作为参数传递给函数。循环依赖关系很少见,通常是您可能需要重新考虑设计的信号。但是,有时需要它们,在这种情况下,请使用上面指定的require()。

如果您熟悉CommonJS模块,则可以使用导出为该模块创建一个空对象,该空对象可立即供其他模块引用。通过在循环依赖关系的两边执行此操作,然后可以安全地保留另一个模块。仅当每个模块都为模块值而不是函数导出对象时,此方法才有效:

//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”依赖项:

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

指定JSONP服务依赖关系

JSONP是使用JavaScript调用某些服务的一种方式。它跨域工作,并且是一种仅通过脚本标签仅需要HTTP GET即可调用服务的既定方法。

要在RequireJS中使用JSONP服务,请指定“ define”作为回调参数的值。这意味着您可以获取JSONP URL的值,就好像它是模块定义一样。

这是一个调用JSONP API端点的示例。在此示例中,JSONP回调参数称为“ 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服务。如果JSONP服务超时,则意味着可能无法执行通过define()定义的其他模块,因此错误处理不可靠。

仅支持作为JSON对象的JSONP返回值。数组,字符串或数字的JSONP响应将不起作用。

此功能不应用于长轮询JSONP连接-处理实时流的API。这类API在收到每个响应后应该执行更多的脚本清理工作,而RequireJS将只获取一次JSONP URL-随后将相同的URL作为require()或define()调用中的依赖项使用,将获得一个缓存的值。

加载JSONP服务时,错误通常会通过服务超时显示出来,因为脚本标记加载不会为网络问题提供太多详细信息。要检测错误,可以重写requirejs.onError()以获取错误。在“处理错误”部分中有更多信息。

取消定义模块

有一个全局函数requirejs.undef(),它允许取消定义模块。它将重置加载程序的内部状态,以忽略模块的先前定义。

但是,它不会从其他已定义的模块中删除该模块,并且在执行时会将该模块作为依赖项获得该模块的句柄。因此,只有在没有其他模块无法处理该模块值的错误情况下使用它,或者作为将来可能使用该模块的模块加载的一部分,它才真正有用。有关示例,请参见errback部分

如果要进行更复杂的依赖关系图分析以进行未定义的工作,则半私有的 onResourceLoad API可能会有所帮助。

赞(0) 打赏
转载请附上原文出处链接:胖蔡叨叨叨 » RequireJs API基本使用
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏