Node 不是已经有模块加载器了吗? § 1

是的,Node 确实有。该加载器使用 CommonJS 模块格式。CommonJS 模块格式对于浏览器来说并非最佳选择,而且我不同意CommonJS 模块格式中做出的一些权衡。通过在服务器上使用 RequireJS,您可以对所有模块使用一种格式,无论它们是在服务器端运行还是在浏览器中运行。这样,您就可以保留在浏览器中使用 RequireJS 获得的速度优势和易于调试的优势,而不必担心在两种格式之间移动的额外转换成本。

如果您想为您的模块使用 define() 但仍然希望在 Node 中运行它们而无需在服务器上运行 RequireJS,请参阅下面的部分,了解如何使用 amdefine

我可以使用已经用 CommonJS 模块格式编写的 Node 模块吗? § 2

是的!RequireJS 的 Node 适配器(称为 r.js)如果在 RequireJS 使用的配置中找不到模块,它将使用 Node 的 require 实现和 Node 的搜索路径,因此您可以继续使用现有的基于 Node 的模块,而无需对其进行更改。

RequireJS 将首先使用其配置选项来查找模块。如果 RequireJS 无法使用其配置找到模块,则假定该模块是使用 Node 类型的模块和配置的模块。因此,只有在模块使用 RequireJS API 时才使用 RequireJS 配置模块位置。对于需要 Node API 和配置/路径的模块,只需使用 Node 包管理器(如 npm)安装它们,而不要使用 RequireJS 配置它们的位置。

**最佳实践**:使用 npm 将仅限 Node 的包/模块安装到项目的 **node_modules** 目录中,但不要将 RequireJS 配置为在 node_modules 目录中查找。还要避免使用相对模块 ID 来引用仅限 Node 的模块。因此,**不要**执行类似 **require("./node_modules/foo/foo")** 的操作。

其他注意事项

  • Node 中的 RequireJS 只能加载本地磁盘上的模块——例如,目前不支持通过 http 获取模块。
  • RequireJS 配置选项(如 map、packages、paths)仅在 RequireJS 加载模块时才适用。如果 RequireJS 需要询问 node 模块系统,则原始 ID 将传递给 Node。如果您需要 node 模块来使用 map 配置,则可以使用内联 define() 调用,如此电子邮件列表线程所示。

我该如何使用它? § 3

有两种方法可以获取 Node 适配器

npm

使用 npm 安装它

npm install requirejs

此选项将安装最新版本。

下载 r.js

如果您不想使用 npm,可以直接获取 r.js

  • 下载页面下载 r.js 并将其放在您的项目中。
  • r.js 仓库获取源代码,并通过“node dist.js”生成 r.js,或者从 **dist** 目录中获取快照。

用法

这些说明假定您已使用 npm 安装了“requirejs”。如果您直接使用 r.js 文件,请将 require('requirejs') 替换为 require('./path/to/r.js')。基本用法是

  • require('requirejs')
  • 在配置中将主 js 文件的“require”函数传递给 requirejs。

示例

var requirejs = require('requirejs');

requirejs.config({
    //Pass the top-level main.js/index.js require
    //function to requirejs so that node modules
    //are loaded relative to the top-level JS file.
    nodeRequire: require
});

requirejs(['foo', 'bar'],
function   (foo,   bar) {
    //foo and bar are loaded according to requirejs
    //config, but if not found, then node's require
    //is used to load the module.
});

请务必阅读第 2 节中的注意事项,了解如何配置 RequireJS 以便它可以加载通过 npm 安装的仅限 node 的模块。

要查看一个更完整的示例,该示例通过 RequireJS 加载模块,但将 Node 原生模块用于其他用途,请参阅 r.js 仓库中的嵌入式测试

**注意:**`requirejs([], function() {})` 将在 RequireJS 2.1+ 中异步调用函数回调(对于早期版本,它是同步调用的)。但是,在 Node 中运行时,模块加载将使用同步 IO 调用加载,并且加载器插件应同步解析对其加载方法的调用。这允许通过 requirejs('stringValue') 调用在节点中同步使用 requirejs 模块

//Retrieves the module value for 'a' synchronously
var a = requirejs('a')

使用 AMD 或 RequireJS 构建节点模块

如果您想编写一个模块,使其可以与 RequireJS 和 Node 一起使用,而无需 Node 中的库用户使用 RequireJS,则可以使用 amdefine 包来做到这一点

if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(function(require) {
    var dep = require('dependency');

    //The value returned from the function is
    //used as the module export visible to Node.
    return function () {};
});

从 1.0.3 版本开始,RequireJS 优化器将删除上面对“amdefine”的使用,因此也可以安全地将此模块用于您的基于 Web 的项目。只需确保**使用与上面显示的完全相同的“amdefine”if() 测试和内容**。允许空格/换行符的差异。有关更多信息,请参阅 amdefine 项目

如果您想直接使用 RequireJS 对模块进行编码,然后将模块值导出到节点,以便它可以在其他 Node 程序中使用,而无需该应用程序使用 RequireJS,则可以使用下一个示例中列出的方法。

最好将 baseUrl 特别设置为包含模块的目录,以便它在嵌套在 node_modules 层次结构中时可以正常工作。使用同步的 `requirejs('moduleId')` 使用 requirejs 中的配置和规则获取模块,然后使用 Node 的 module.exports 导出您的模块值

var requirejs = require('requirejs');

requirejs.config({
    //Use node's special variable __dirname to
    //get the directory containing this file.
    //Useful if building a library that will
    //be used in node but does not require the
    //use of node outside
    baseUrl: __dirname,

    //Pass the top-level main.js/index.js require
    //function to requirejs so that node modules
    //are loaded relative to the top-level JS file.
    nodeRequire: require
});

//foo and bar are loaded according to requirejs
//config, and if found, assumed to be an AMD module.
//If they are not found via the requirejs config,
//then node's require is used to load the module,
//and if found, the module is assumed to be a
//node-formatted module. Note: this synchronous
//style of loading a module only works in Node.
var foo = requirejs('foo');
var bar = requirejs('bar');

//Now export a value visible to Node.
module.exports = function () {};

将优化器用作节点模块

节点模块还将 RequireJS 优化器公开为 **optimize** 方法,用于通过函数调用而不是命令行工具使用RequireJS 优化器

var requirejs = require('requirejs');

var config = {
    baseUrl: '../appDir/scripts',
    name: 'main',
    out: '../build/main-built.js'
};

requirejs.optimize(config, function (buildResponse) {
    //buildResponse is just a text output of the modules
    //included. Load the built file for the contents.
    //Use config.out to get the optimized file contents.
    var contents = fs.readFileSync(config.out, 'utf8');
}, function(err) {
    //optimization err callback
});

这允许您构建其他优化工作流,例如Web 构建器,如果您希望始终使用“在 </body> 标记之前包含一个脚本文件”方法进行开发,则可以使用该工作流。在 Node 中运行的优化器相当快,但对于不想为每个浏览器请求都重新生成构建的大型项目,而只是在您修改了构建中包含的脚本时才需要重新生成。您可以使用 Node 的 fs.watchFile() 来监视文件,然后在文件更改时触发构建。

反馈

如果您发现问题并想报告,请使用r.js GitHub 问题页面