Node 中的 RequireJS
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
用法
这些说明假定您已使用 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 问题页面。