简体   繁体   English

如何编写一个适用于Node.js,RequireJS以及没有它们的模块

[英]How to write a module that works with Node.js, RequireJS as well as without them

I am working on a JavaScript library for JSON/XML processing . 我正在开发一个用于JSON / XML处理的JavaScript库。 My library works in browser as well as Node.js (with xmldom and xmlhttprequest modules). 我的库在浏览器和Node.js(使用xmldomxmlhttprequest模块)中工作。

One of the users recently asked for RequireJS support. 其中一位用户最近要求提供RequireJS支持。 I have taken a look at the RequireJS/AMD thing and think it is a good approach so I'd like to provide this. 我已经看了一下RequireJS / AMD的东西,认为这是一个很好的方法,所以我想提供这个。

However I'd like to retain the portability: my library must work in browsers (with and without RequireJS) as well as Node.js. 但是我想保留可移植性:我的库必须在浏览器(有和没有RequireJS)以及Node.js中工作。 And in the browser environment I don't depend on xmldom or xmlhttprequest since these things are provided by the browser itself. 在浏览器环境中,我不依赖于xmldomxmlhttprequest因为这些东西都是由浏览器本身提供的。

My question is: how can I implement my library so that it works in browsers as well as in Node.js with an without RequireJS? 我的问题是:我如何实现我的库,以便它在浏览器以及没有RequireJS的Node.js中工作?

A bit of historyand my current solution 一点历史和我目前的解决方案

I initially wrote my library for browsers. 我最初为浏览器编写了我的库。 So it just created a global-scope object and put everything inside it: 所以它只是创建了一个全局范围的对象并将所有内容放入其中:

var Jsonix = { ... };

Later on users asked for Node.js support. 后来用户要求Node.js支持。 So I added: 所以我补充说:

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

I also had to import few modules mentioned above. 我还必须导入上面提到的几个模块。 I did it conditionally, depending on whether the require function is available or not: 我有条件地做了,这取决于require函数是否可用:

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

Now there's this story with RequireJS. 现在有了RequireJS这个故事。 If RequireJS is present then the require function is present as well. 如果RequireJS存在,那么也存在require函数。 But module loading works differently, I have to use the define function etc. I also can't just require things since require has an async API in RequireJS. 但模块加载的工作方式不同,我必须使用define函数等。我也不能只require东西,因为require在RequireJS中有异步API。 Moreover, if my library is loaded via RequireJS, it seems to process the source code and detects require('something') even if I do it conditionally like 此外,如果我的库是通过RequireJS加载的,它似乎处理源代码并检测require('something')即使我有条件地执行它

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

RequireJS still detects require('xmlhttprequest') an tries to load the corresponding JS file. RequireJS仍然检测到require('xmlhttprequest')尝试加载相应的JS文件。

Currently I'm coming to the following solution. 目前我正在寻求以下解决方案。

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

And this is how I check for dependencies now: 这就是我现在检查依赖关系的方法:

if (typeof _jsonix_xmlhttprequest !== 'undefined')
{
    var XMLHttpRequest = _jsonix_xmlhttprequest.XMLHttpRequest;
    return new XMLHttpRequest();
}

If I have require but not define then I assume this is a Node.js environment. 如果我有require但没有define那么我认为这是一个Node.js环境。 I use amdefine to define the module and pass the required dependencies. 我使用amdefine来定义模块并传递所需的依赖项。

If I have require and define thet I assume this is a RequireJS environment, so I just use the define function. 如果我requiredefine我认为这是一个RequireJS环境,所以我只使用define函数。 Currently I also assume this is a browser environment so dependencies like xmldom and xmlhttprequest are not available and don't require them. 目前我还假设这是一个浏览器环境,因此xmldomxmlhttprequest等依赖xmlhttprequest不可用,并且不需要它们。 (This is probably nor correct.) (这可能也不正确。)

If I don't have the require function then I assume this is a browser environment without RequireJS/AMD support so I invoke the module factory _jsonix directly and export the result as a global object. 如果我没有require函数,那么我认为这是一个没有RequireJS / AMD支持的浏览器环境,所以我直接调用模块工厂_jsonix并将结果导出为全局对象。

So, this is my approach so far. 所以,到目前为止,这是我的方法。 Seems a little bit awkward to me, and as a newbie to RequireJS/AMD I'm seeking advise. 对我来说似乎有点尴尬,作为RequireJS / AMD的新手,我正在寻求建议。 Is it the right approach? 这是正确的方法吗? Are there better ways to address the problem? 有没有更好的方法来解决这个问题? I'd be grateful for your help. 我很感激你的帮助。

Take a look at how underscore.js handles it. 看一下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 _;
  });
}

This is what I ended up with: 这就是我最终得到的结果:

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

Here is a template I'm currently using, it's both AMD and node compatible though not directly loadable stand-alone in the browser... 这是我目前正在使用的模板,虽然不能直接在浏览器中单独加载,但它兼具AMD和节点...

The main advantage to this approach is that the domain-specific code does not need to care about what imported it, for the general case. 这种方法的主要优点是,对于一般情况,特定于域的代码不需要关心导入它的内容。

/**********************************************************************
* 
*
*
**********************************************************************/
((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 })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM