如何编写一个与Node.js,RequireJS一起工作的模块

我正在处理JSON / XML处理的JavaScript库。 我的图书馆工作在浏览器以及Node.js(与xmldomxmlhttprequest模块)。

其中一名用户最近要求RequireJS支持。 我已经看了RequireJS / AMD的东西,认为这是一个很好的方法,所以我想提供这个。

不过,我想保留可移植性:我的库必须在浏览器(有和没有RequireJS)以及Node.js中工作。 而在浏览器环境中,我不依赖于xmldomxmlhttprequest因为这些东西是由浏览器本身提供的。

我的问题是:如何实现我的库,以便它可以在浏览器以及Node.js中使用,而不需要RequireJS?

一点历史和我目前的解决scheme

我最初写我的浏览器库。 所以它只是创build一个全局范围的对象,并把它放在它的一切:

 var Jsonix = { ... }; 

稍后用户请求Node.js支持。 所以我补充说:

 if(typeof require === 'function'){ module.exports.Jsonix = Jsonix; } 

我还不得不导入上面提到的几个模块。 我有条件地做了,取决于requirefunction是否可用:

 if (typeof require === 'function') { var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; return new XMLHttpRequest(); } 

现在有了RequireJS这个故事。 如果RequireJS存在,那么require函数也存在。 但是,模块加载工作方式不同,我必须使用define函数等。我也不能只require东西,因为require在RequireJS有一个asynchronousAPI。 此外,如果我的库通过RequireJS加载,它似乎处理源代码,并检测require('something')即使我有条件地

 if (typeof require === 'function' && typeof require.specified !== 'function) ... 

RequireJS仍然检测到require('xmlhttprequest')试图加载相应的JS文件。

目前我来到以下解决scheme。

 // 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来定义模块并传递所需的依赖关系。

如果我有requiredefine我认为这是一个RequireJS环境,所以我只是使用define函数。 目前我也认为这是一个浏览器环境,所以像xmldomxmlhttprequest这样的依赖xmlhttprequest不可用,也不需要它们。 (这可能不正确。)

如果我没有require函数,那么我认为这是一个没有RequireJS / AMD支持的浏览器环境,所以我直接调用模块工厂_jsonix并将结果导出为全局对象。

所以,这是我迄今的做法。 看起来有点尴尬,作为RequireJS / AMD的新手,我正在寻求build议。 这是正确的做法吗? 有更好的方法来解决这个问题吗? 我会很感激你的帮助。

看看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 _; }); } 

这是我结束了:

 // 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; } 

这里是我目前使用的一个模板,它既是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 })