RequireJS in Node
Node 本身不是已经有模块加载器了吗? § 1
是的。Node它确实有。该加载器使用的是 CommonJS 模块格式。CommonJS 模块格式在浏览器环境中效果不佳,并且我也不认同CommonJS 模块格式中一些取舍的做法。通过在服务器端使用 RequireJS,你可以对所有模块使用一种格式,无论它们是在服务器端还是浏览器中运行。这样可以保留 RequireJS 在浏览器中提供的快速加载和易于调试的优势,并且无需担心在两种格式之间转换所带来的额外成本。
如果你希望为你的模块使用 define() 方法,但又希望在不使用 RequireJS 的情况下在 Node 中运行它们,请参阅下面的章节关于amdefine.
我能否使用已经以 CommonJS 模块格式编写好的 Node 模块? § 2
当然可以!RequireJS 的 Node 适配器 r.js 将会使用 Node 的 require 实现以及 Node 的搜索路径,如果该模块无法通过 RequireJS 的配置找到的话。因此你可以继续使用现有的 Node 模块而无需对其做出任何修改。
RequireJS 将首先使用其配置选项来查找模块。如果 RequireJS 无法使用其配置找到模块,则假定该模块是使用 Node 类型的模块和配置的模块。因此,只有当模块使用的是 RequireJS API 时才需要使用 RequireJS 配置模块位置。对于期望使用 Node 的 API 和配置/路径的模块,只需使用 Node 包管理器如npm来安装这些模块,不要使用 RequireJS 对这些模块的位置进行配置。
最佳实践:将仅用于 Node 的包/模块通过 npm 安装到项目中的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
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+ 中 will 调用函数回调是异步的(早期版本则是同步的)。然而,在 Node 中运行时,模块加载将使用同步 IO 调用完成,加载插件应该同步解析它们的 load 方法调用。这使得在 Node 中可以通过 requirejs('stringValue') 这样的方式同步使用 requirejs 模块:
//Retrieves the module value for 'a' synchronously
var a = requirejs('a')
使用 AMD 或 RequireJS 构建 Node 模块
如果你想编写一个模块,使其可以在 RequireJS 和 Node 中都能正常工作,而又不需要你的库在 Node 中的用户必须使用 RequireJS,那么你可以使用amdefineamdefine
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 () {};
});
包来实现这一点:只需确保按照上面所示的方式精确使用 'amdefine' 的 if 测试及其内容即可。空格和换行的不同是可以接受的。请参阅amdefine 项目了解更多详情。
如果你希望直接使用 RequireJS 编写模块,然后向 Node 导出模块值,使得其他 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 () {};
将优化器作为 Node 模块使用
Node 模块也以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
});
这允许你构建其它的优化工作流,比如一个网页构建工具如果你更倾向于始终使用“在</body>标签之前包含一个脚本文件”的方式开发,就可以使用它。在Node中运行的优化器速度相当快,但对于那些不想每次浏览器请求都重新生成构建的大项目来说,仅当你修改了属于构建一部分的脚本时才重新生成构建即可。你可以使用Node的fs.watchFile()来监听文件,并在文件更改时触发构建。
反馈
如果你遇到问题并希望报告,请使用r.js GitHub Issues页面.