112

有一些第三方 Javascript 库具有我想在 Node.js 服务器中使用的一些功能。(具体来说,我想使用我找到的 QuadTree javascript 库。)但是这些库只是简单的.js文件,而不是“Node.js 库”。

因此,这些库不遵循exports.var_nameNode.js 对其模块所期望的语法。据我了解,这意味着当您这样做时,module = require('module_name');或者module = require('./path/to/file.js');您最终会得到一个没有可公开访问功能的模块等。

然后我的问题是“如何将任意 javascript 文件加载到 Node.js 中,这样我就可以利用它的功能而不必重写它,这样它就可以了exports?”

我对 Node.js 很陌生,所以如果我对它的工作原理的理解有什么明显的漏洞,请告诉我。


编辑:进行更多研究,我现在看到 Node.js 使用的模块加载模式实际上是最近开发的用于加载称为CommonJS的 Javascript 库的标准的一部分。它在Node.js 的模块文档页面上说明了这一点,但直到现在我都错过了。

最终我的问题的答案可能是“等到你的库的作者开始编写 CommonJS 接口或者你自己做吧”。

4

7 回答 7

81

对于这种情况,我认为这是“最正确”的答案。

假设您有一个名为quadtree.js.

您应该构建一个node_module具有这种目录结构的自定义...

./node_modules/quadtree/quadtree-lib/
./node_modules/quadtree/quadtree-lib/quadtree.js
./node_modules/quadtree/quadtree-lib/README
./node_modules/quadtree/quadtree-lib/some-other-crap.js
./node_modules/quadtree/index.js

./node_modules/quadtree/quadtree-lib/目录中的所有内容都是来自您的 3rd 方库的文件。

然后您的./node_modules/quadtree/index.js文件将只从文件系统加载该库并完成正确导出内容的工作。

var fs = require('fs');

// Read and eval library
filedata = fs.readFileSync('./node_modules/quadtree/quadtree-lib/quadtree.js','utf8');
eval(filedata);

/* The quadtree.js file defines a class 'QuadTree' which is all we want to export */

exports.QuadTree = QuadTree

现在您可以quadtree像使用任何其他节点模块一样使用您的模块...

var qt = require('quadtree');
qt.QuadTree();

我喜欢这种方法,因为无需更改任何 3rd 方库的源代码——因此更易于维护。升级时您需要做的就是查看它们的源代码并确保您仍在导出正确的对象。

于 2011-03-07T21:53:22.443 回答
77

有一个比使用更好的方法evalvm模块。

例如,这是我的模块,它在任一或全局上下文execfile中评估脚本:pathcontext

var vm = require("vm");
var fs = require("fs");
module.exports = function(path, context) {
  context = context || {};
  var data = fs.readFileSync(path);
  vm.runInNewContext(data, context, path);
  return context;
}

它可以像这样使用:

> var execfile = require("execfile");
> // `someGlobal` will be a global variable while the script runs
> var context = execfile("example.js", { someGlobal: 42 });
> // And `getSomeGlobal` defined in the script is available on `context`:
> context.getSomeGlobal()
42
> context.someGlobal = 16
> context.getSomeGlobal()
16

其中example.js包含:

function getSomeGlobal() {
    return someGlobal;
}

这种方法的最大优点是您可以完全控制执行脚本中的全局变量:您可以传入自定义全局变量(通过context),脚本创建的所有全局变量都将添加到context. 调试也更容易,因为语法错误等将使用正确的文件名报告。

于 2012-01-10T18:05:05.353 回答
30

最简单的方法是:eval(require('fs').readFileSync('./path/to/file.js', 'utf8')); 这非常适合在交互式 shell 中进行测试。

于 2012-03-22T13:31:11.720 回答
5

AFAIK,这确实是必须加载模块的方式。exports但是,您也可以将它们附加到对象上this(否则将是全局对象),而不是将所有导出的函数附加到对象上。

所以,如果你想保持其他库兼容,你可以这样做:

this.quadTree = function () {
  // the function's code
};

或者,当外部库已经有自己的命名空间时,例如jQuery(不是你可以在服务器端环境中使用它):

this.jQuery = jQuery;

在非节点环境中,this将解析为全局对象,从而使其成为全局变量......它已经是。所以它不应该破坏任何东西。

编辑:James Herdman 为初学者写了一篇关于 node.js 的不错的文章,其中也提到了这一点。

于 2011-03-02T18:15:38.023 回答
4

我不确定我是否真的会最终使用它,因为它是一个相当老套的解决方案,但解决这个问题的一种方法是构建一个像这样的小型迷你模块导入器......

在文件中./node_modules/vanilla.js

var fs = require('fs');

exports.require = function(path,names_to_export) {
    filedata = fs.readFileSync(path,'utf8');
    eval(filedata);
    exported_obj = {};
    for (i in names_to_export) {
        to_eval = 'exported_obj[names_to_export[i]] = ' 
            + names_to_export[i] + ';'
        eval(to_eval); 
    }
    return exported_obj;
}

然后,当您想使用库的功能时,您需要手动选择要导出的名称。

所以对于像文件这样的库./lib/mylibrary.js......

function Foo() { //Do something... }
biz = "Blah blah";
var bar = {'baz':'filler'};

当你想在你的 Node.js 代码中使用它的功能时......

var vanilla = require('vanilla');
var mylibrary = vanilla.require('./lib/mylibrary.js',['biz','Foo'])
mylibrary.Foo // <-- this is Foo()
mylibrary.biz // <-- this is "Blah blah"
mylibrary.bar // <-- this is undefined (because we didn't export it)

不知道这在实践中会有多好。

于 2011-03-02T18:59:10.007 回答
2

我能够通过更新他们的脚本来使其工作,非常容易,只需module.exports =在适当的地方添加......

例如,我把他们的文件复制到“./libs/apprise.js”。然后从哪里开始

function apprise(string, args, callback){

我将功能分配给module.exports =

module.exports = function(string, args, callback){

因此,我可以像这样将库导入到我的代码中:

window.apprise = require('./libs/apprise.js');

我很高兴去。YMMV,这是使用webpack 的

于 2016-11-15T01:03:22.337 回答
0

一个简单的include(filename)函数,具有更好的错误消息(堆栈、文件名等)eval,以防出现错误:

var fs = require('fs');
// circumvent nodejs/v8 "bug":
// https://github.com/PythonJS/PythonJS/issues/111
// http://perfectionkills.com/global-eval-what-are-the-options/
// e.g. a "function test() {}" will be undefined, but "test = function() {}" will exist
var globalEval = (function() {
    var isIndirectEvalGlobal = (function(original, Object) {
        try {
            // Does `Object` resolve to a local variable, or to a global, built-in `Object`,
            // reference to which we passed as a first argument?
            return (1, eval)('Object') === original;
        } catch (err) {
            // if indirect eval errors out (as allowed per ES3), then just bail out with `false`
            return false;
        }
    })(Object, 123);
    if (isIndirectEvalGlobal) {
        // if indirect eval executes code globally, use it
        return function(expression) {
            return (1, eval)(expression);
        };
    } else if (typeof window.execScript !== 'undefined') {
        // if `window.execScript exists`, use it
        return function(expression) {
            return window.execScript(expression);
        };
    }
    // otherwise, globalEval is `undefined` since nothing is returned
})();

function include(filename) {
    file_contents = fs.readFileSync(filename, "utf8");
    try {
        //console.log(file_contents);
        globalEval(file_contents);
    } catch (e) {
        e.fileName = filename;
        keys = ["columnNumber", "fileName", "lineNumber", "message", "name", "stack"]
        for (key in keys) {
            k = keys[key];
            console.log(k, " = ", e[k])
        }
        fo = e;
        //throw new Error("include failed");
    }
}

但是使用 nodejs 甚至会变得更脏:您需要指定以下内容:

export NODE_MODULE_CONTEXTS=1
nodejs tmp.js

否则,您不能在包含的文件中使用全局变量include(...).

于 2015-06-30T21:37:52.203 回答