使用节点和窗口使JS库满意

时间:2014-11-10 05:39:08

标签: javascript node.js canvas npm

我维护a JS library我希望进入Node模块,并与Node一起使用。

我的库扩展了Canvas上下文API并需要getImageData(),所以从包含所有代码的防御线开始:

if (window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype.getImageData){
  CanvasRenderingContext2D.prototype.blendOnto = /* … */;
}

使用Node,我正在使用Node Canvas。为了使我现有的代码能够工作,我需要编写这样的Node代码:

var Canvas = require('canvas');
GLOBAL.CanvasRenderingContext2D = Canvas.Context2d;
GLOBAL.window = GLOBAL;
require('context_blender');

然而,这似乎明显地与Node的模块模式作斗争。我怎样才能最好地重写我的库并将其打包为节点模块,以便它(a)继续在Web浏览器中工作,但(b)干净地使用Node Canvas,而不必通过全局变量传递数据?有没有办法将Canvas.Context2d传递给我的模块进行变异?

3 个答案:

答案 0 :(得分:5)

我建议将代码放在模块工厂函数中:

var context_blender_factory = function(CanvasRenderingContext2D)
{
    var defaultOffsets = {
        // ...
    };
    // Applies to the parameter passed to the module factory function  
    CanvasRenderingContext2D.prototype.blendOnto = // ...
    // ...
}

这基本上抽象了你得到了CanvasRenderingContext2D

然后,在声明您的功能之后,您可以编写初始化例程,检查环境并相应地调用模块工厂。

如果你只想考虑纯粹的NodeJS,那就是:

if (typeof require === 'function') {
    // Assume NodeJS environment
    context_blender_factory(require('canvas').Context2d);
}
else if (window &&
         window.CanvasRenderingContext2D &&
         window.CanvasRenderingContext2D.prototype.getImageData)
{
    // Assume browser environment
    context_blender_factory(window.CanvasRenderingContext2D);
}

此模式还允许支持AMD样式的模块环境 RequireJSamdefine


<强>背景故事

我与my JS library有类似的情况(参见下面的背景故事集)。

我最初为浏览器实现了我的库。

然后人们开始询问它是否可以在NodeJS中运行。这并不难,因为Node拥有模仿/实现功能的所有相关模块,就像在浏览器中一样。所以我为NodeJS添加了require / export构造。

然后其他一些人开始询问RequireJS / AMD - 样式模块支持。我问过周围:

  

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

最后到达following solution以支持浏览器,NodeJS,RequiredJS / AMD / amdefine。

答案 1 :(得分:1)

为节点和浏览器提供单独的构建是一种常见做法。为什么在浏览器中需要与节点相关的代码,反之亦然?

所以我建议您在

之前添加代码
var RenderingContext2d = require('Canvas').Context2d

然后使用RenderingContext2d获取您需要的所有内容,当您为浏览器创建构建时,将require('Canvas').Context2d替换为window.CanvasRenderingContext2D(可以使用sed和许多其他工具轻松实现自动化)。< / p>

结果你得到

  • 浏览器构建中没有与节点相关的代码;
  • 节点构建中没有与浏览器相关的代码;
  • 即时运行代码,以便在package.json&#34; main&#34;中引用节点。指令;
  • 一个重复的任务,聪明的JavaScript minifier将内嵌到你的代码中 - 对于浏览器来说是一件好事。

答案 2 :(得分:0)

我会像lexicore那样做,但有一些不同之处。整个事情应该是一个匿名函数(就像现在的库一样),我会以不同的方式执行测试。根据JS库中已经存在的代码,我做了:

(function () {
    var defaultOffsets = {
    // ....
    };

    function addBlendMethod(object) {
        // This should not need to change from that it is now.
    }

    function blendOnto(...
    // ... etc ...

    // At the end of the anonymous function, test the environment
    // and act accordingly.
    if (typeof require === 'function' &&
       !(typeof define === 'function' && define.amd)) {
        // Node...
        addBlendMethod(require('canvas').Context2d.prototype);
    }
    else {
        addBlendMethod(window.CanvasRenderingContext2D &&
                       window.CanvasRenderingContext2D.prototype);
    }
})();

检查!(typeof define === 'function' && define.amd)是必要的,以避免在节点检查中出现误报。如果有人试图在浏览器中加载您的代码, RequireJS(或其他兼容AMD的加载程序)已经存在,并且检查仅针对require,您的代码将尝试{ {1}}并且会失败。这是因为RequireJS也定义了require('canvas')require检查会阻止此误报。 AMD兼容的加载器应该定义define函数并在其上设置define属性。没有检查可能导致的问题不仅仅是假设。偶尔会有人发布一个关于库某某是如何无法使用RequireJS加载的RequireJS问题而我必须回答库中的环境检测代码是错误的,因为它认为它在Node中运行。

与目前库中的代码相反,我使用amd而不是window。我宁愿不依赖于将this设置为this的非严格代码的行为,否则将其指定为window。它可能会导致某些条件下的破损,并且我从未遇到过依赖此行为必需的情况。