如何编写一个与Node.js,RequireJS一起工作的模块
我正在处理JSON / XML处理的JavaScript库。 我的图书馆工作在浏览器以及Node.js(与xmldom
和xmlhttprequest
模块)。
其中一名用户最近要求RequireJS支持。 我已经看了RequireJS / AMD的东西,认为这是一个很好的方法,所以我想提供这个。
不过,我想保留可移植性:我的库必须在浏览器(有和没有RequireJS)以及Node.js中工作。 而在浏览器环境中,我不依赖于xmldom
或xmlhttprequest
因为这些东西是由浏览器本身提供的。
我的问题是:如何实现我的库,以便它可以在浏览器以及Node.js中使用,而不需要RequireJS?
一点历史和我目前的解决scheme
我最初写我的浏览器库。 所以它只是创build一个全局范围的对象,并把它放在它的一切:
var Jsonix = { ... };
稍后用户请求Node.js支持。 所以我补充说:
if(typeof require === 'function'){ module.exports.Jsonix = Jsonix; }
我还不得不导入上面提到的几个模块。 我有条件地做了,取决于require
function是否可用:
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
来定义模块并传递所需的依赖关系。
如果我有require
和define
我认为这是一个RequireJS环境,所以我只是使用define
函数。 目前我也认为这是一个浏览器环境,所以像xmldom
和xmlhttprequest
这样的依赖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 })