如何编写一个可与 Node.js、RequireJS 一起使用,也可以在没有它们的情况下使用的模块?

9
我正在开发一个JavaScript库,用于JSON/XML处理。我的库可以在浏览器和Node.js中使用(使用xmldomxmlhttprequest模块)。
最近,有一位用户要求添加RequireJS支持。我查看了RequireJS/AMD的相关内容,并认为这是一个很好的方法,因此我想提供此功能。
然而,我希望保留可移植性:我的库必须在浏览器中工作(有或没有RequireJS),以及在Node.js中工作。在浏览器环境中,我不依赖于xmldomxmlhttprequest,因为这些功能由浏览器本身提供。 我的问题是:如何实现我的库,使其可以在浏览器和Node.js中使用,无论是否使用RequireJS? 一点历史和我的当前解决方案
我最初为浏览器编写了我的库。所以它只是创建了一个全局对象,并将所有内容放在其中:
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来定义模块并传递所需的依赖项。
如果我有requiredefine,那么我假设这是一个RequireJS环境,因此我只需使用define函数。目前,我还假设这是一个浏览器环境,因此像xmldomxmlhttprequest这样的依赖项不可用,也不需要它们。(这可能是不正确的。)
如果我没有require函数,则我假设这是一个不支持RequireJS / AMD的浏览器环境,因此直接调用模块工厂_jsonix并将结果导出为全局对象。
所以,这是我的方法。对我来说似乎有点笨拙,作为RequireJS / AMD的新手,我正在寻求建议。这是正确的方法吗?有更好的方法来解决问题吗?感谢您的帮助。

1
这可能会有用:https://github.com/umdjs/umd - go-oleg
3个回答

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

5
这是我最终得到的结果:
// 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
这是我目前正在使用的模板,它既支持 AMD 又支持 node,尽管不能在浏览器中直接加载。
这种方法的主要优点是,特定于域的代码不需要考虑导入它的方式,适用于一般情况。
/**********************************************************************
* 
*
*
**********************************************************************/
((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 })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接