我正在为JSON/XML processing开发一个JavaScript库。我的库在浏览器和Node.js(使用xmldom
和xmlhttprequest
模块)中工作。
其中一位用户最近要求提供RequireJS支持。我已经看了一下RequireJS / AMD的事情,并认为这是一个很好的方法,所以我想提供这个。
但是我想保留可移植性:我的库必须在浏览器(有和没有RequireJS)以及Node.js中工作。在浏览器环境中,我不依赖于xmldom
或xmlhttprequest
,因为这些内容是由浏览器本身提供的。
我的问题是:我如何实现我的库,以便它可以在浏览器以及没有RequireJS的Node.js中工作?
一点历史和我目前的解决方案
我最初为浏览器编写了我的库。所以它只是创建了一个全局范围的对象并将所有内容放在其中:
var Jsonix = { ... };
后来用户要求Node.js支持。所以我补充道:
if(typeof require === 'function'){
module.exports.Jsonix = Jsonix;
}
我还必须导入上面提到的几个模块。我有条件地做了,具体取决于require
功能是否可用:
if (typeof require === 'function')
{
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
return new XMLHttpRequest();
}
现在有了这个有RequireJS的故事。如果RequireJS存在,那么也存在require
函数。但模块加载的工作方式不同,我必须使用define
函数等。我也不能require
因为require
在RequireJS中有异步API。此外,如果我的库是通过RequireJS加载的,它似乎处理源代码并检测require('something')
,即使我有条件地执行
if (typeof require === 'function' && typeof require.specified !== 'function) ...
RequireJS仍然会检测require('xmlhttprequest')
尝试加载相应的JS文件。
目前我正在采用以下解决方案。
// Module factory function, AMD style
var _jsonix = function(_jsonix_xmldom, _jsonix_xmlhttprequest, _jsonix_fs)
{
// Complete Jsonix script is included below
var Jsonix = { ... };
// Complete Jsonix script is included above
return { Jsonix: Jsonix };
};
// If require function exists ...
if (typeof require === 'function') {
// ... but define function does not exists, assume we're in the Node.js environment
// In this case, load the define function via amdefine
if (typeof define !== 'function') {
var define = require('amdefine')(module);
define(["xmldom", "xmlhttprequest", "fs"], _jsonix);
}
else {
// Otherwise assume we're in the RequireJS environment
define([], _jsonix);
}
}
// Since require function does not exists,
// assume we're neither in Node.js nor in RequireJS environment
// This is probably a browser environment
else
{
// Call the module factory directly
var Jsonix = _jsonix();
}
这就是我现在检查依赖关系的方式:
if (typeof _jsonix_xmlhttprequest !== 'undefined')
{
var XMLHttpRequest = _jsonix_xmlhttprequest.XMLHttpRequest;
return new XMLHttpRequest();
}
如果我有require
但不是define
,那么我认为这是一个Node.js环境。我使用amdefine
来定义模块并传递所需的依赖项。
如果我有require
和define
,我认为这是一个RequireJS环境,所以我只使用define
函数。目前我还假设这是一个浏览器环境,因此xmldom
和xmlhttprequest
等依赖项不可用,并且不需要它们。 (这可能也不正确。)
如果我没有require
函数,那么我认为这是一个没有RequireJS / AMD支持的浏览器环境,所以我直接调用模块工厂_jsonix
并将结果导出为全局对象。
所以,到目前为止,这是我的方法。对我来说似乎有点尴尬,作为RequireJS / AMD的新手,我正在寻求建议。这是正确的方法吗?有没有更好的方法来解决这个问题?我很感激你的帮助。
答案 0 :(得分:8)
看看underscore.js如何处理它。
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
...
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
答案 1 :(得分:4)
这就是我最终的结果:
// If the require function exists ...
if (typeof require === 'function') {
// ... but the define function does not exists
if (typeof define !== 'function') {
// Assume we're in the Node.js environment
// In this case, load the define function via amdefine
var define = require('amdefine')(module);
// Use xmldom and xmlhttprequests as dependencies
define(["xmldom", "xmlhttprequest", "fs"], _jsonix_factory);
}
else {
// Otherwise assume we're in the browser/RequireJS environment
// Load the module without xmldom and xmlhttprequests dependencies
define([], _jsonix_factory);
}
}
// If the require function does not exists, we're not in Node.js and therefore in browser environment
else
{
// Just call the factory and set Jsonix as global.
var Jsonix = _jsonix_factory().Jsonix;
}
答案 2 :(得分:-2)
以下是我目前正在使用的模板,虽然不能直接在浏览器中直接加载,但它兼具AMD和节点...
这种方法的主要优点是,对于一般情况,特定于域的代码不需要关心导入它的内容。
/**********************************************************************
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)(
function(require){ var module={} // makes module AMD/node compatible...
/*********************************************************************/
/*********************************************************************/
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })