Node还没有模块加载器吗?§ 1

是的,Node可以。该加载器使用CommonJS模块格式。对于浏览器来说,CommonJS模块格式不是最佳的,我不同意使用CommonJS模块格式进行的一些折衷。通过在服务器上使用RequireJS,您可以对所有模块使用一种格式,无论它们是在服务器端还是在浏览器中运行。这样,您可以保留在浏览器中使用RequireJS所获得的速度优势和简便的调试函数,而不必担心在两种格式之间移动会产生额外的解析成本。

如果要对模块使用define(),但仍在Node中运行它们而不需要在服务器上运行RequireJS,请参阅下面有关使用amdefine的部分。

我可以使用以CommonJS模块格式编写的服务器模块吗?§ 2

如果未通过RequireJS使用的配置找到模块,则RequireJS的Node适配器r.js将使用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,包,路径之类的RequireJS配置选项。如果RequireJS需要询问Node模块系统,则将原始ID传递给Node。如果您需要Node模块来处理map config,则内联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({
    //将顶级main.js/index.js require函数传递给requirejs,
    //以便相对于顶级js文件加载Node模块。
    nodeRequire: require
});
requirejs(['foo', 'bar'],
function   (foo,   bar) {
    //foo和bar是根据requirejs config加载的,
    //但如果找不到,则使用node的require加载模块。
});

确保阅读第2节中有关配置RequireJS 的注释,以便它可以加载通过npm安装的仅Node模块。

要查看通过RequireJS加载模块但将Node-native模块用于其他用途的更完整的示例,请参阅r.js存储库中的嵌入式测试。

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

//同步检索“a"的模块值
var a = requirejs('a')

使用AMD或RequireJS构建Node模块

如果要对模块进行编码以使其可与RequireJS和Node一起使用,而无需Node中库的用户使用RequireJS,则可以使用amdefine包来执行此操作:

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

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

    //从函数返回的值作用Node可见的模块导出。
    return function () {};
});

从1.0.3版开始,RequireJS优化器将取消上面的"amdefine"的使用,因此也可以将该模块用于基于Web的项目。只要确保使用精确的'amdefine'if()测试和内容即可,如上所示。允许空格/换行符之间存在差异。有关更多信息,请参见amdefine项目。

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

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

var requirejs = require('requirejs');

requirejs.config({
    //使用Node的特殊变量dirname获取包含此文件的目录。
    //如果构建将在Node中使用但不需要在外部使用Node的库,则很有用
    baseUrl: __dirname,

    //将顶级main.js/index.js require函数传递给requirejs,
    //以便相对于顶级js文件加载Node模块。
    nodeRequire: require
});

//foo和bar根据requirejs config加载,如果找到,则假设是AMD模块;
//如果通过requirejs config找不到,则使用node的require加载模块;
//如果找到,则假设模块是Node格式化的模块。
//注意:这种加载模块的同步样式只在Node中有效
var foo = requirejs('foo');
var bar = requirejs('bar');

//现在导出Node可见的值。
module.exports = function () {};

如何使用?

Node模块也暴露了RequireJS优化为优化用于使用该方法RequireJS 优化器通过一个函数调用,而不是一个命令行工具:

var requirejs = require('requirejs');

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

requirejs.optimize(config, function (buildResponse) {
    //buildResponse只是模块的文本输出包括。加载内容的生成文件。
    //使用config.out获取优化的文件内容。
    var contents = fs.readFileSync(config.out, 'utf8');
}, function(err) {
    //优化错误回调
});

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