[英]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(使用
xmldom
和xmlhttprequest
模块)中工作。
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. 在浏览器环境中,我不依赖于
xmldom
或xmlhttprequest
因为这些东西都是由浏览器本身提供的。
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. 如果我
require
并define
我认为这是一个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. 目前我还假设这是一个浏览器环境,因此
xmldom
和xmlhttprequest
等依赖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.