将“Vanilla”Javascript 库加载到 Node.js 中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5171213/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:55:41  来源:igfitidea点击:

Load "Vanilla" Javascript Libraries into Node.js

javascriptnode.jscommonjs

提问by Chris W.

There are some third party Javascript libraries that have some functionality I would like to use in a Node.js server. (Specifically I want to use a QuadTree javascript library that I found.) But these libraries are just straightforward .jsfiles and not "Node.js libraries".

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

As such, these libraries don't follow the exports.var_namesyntax that Node.js expects for its modules. As far as I understand that means when you do module = require('module_name');or module = require('./path/to/file.js');you'll end up with a module with no publicly accessible functions, etc.

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

My question then is "How do I load an arbitrary javascript file into Node.js such that I can utilize its functionality without having to rewrite it so that it does do exports?"

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

I'm very new to Node.js so please let me know if there is some glaring hole in my understanding of how it works.

我对 Node.js 很陌生,所以如果我对 Node.js 的工作方式的理解有一些明显的漏洞,请告诉我。



EDIT: Researching into things more and I now see that the module loading pattern that Node.js uses is actually part of a recently developed standard for loading Javascript libraries called CommonJS. It says this right on the module doc page for Node.js, but I missed that until now.

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

It may end up being that the answer to my question is "wait until your library's authors get around to writing a CommonJS interface or do it your damn self."

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

采纳答案by David Wolever

There is a much better method than using eval: the vmmodule.

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

For example, here is my execfilemodule, which evaluates the script at pathin either contextor the global context:

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

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

And it can be used like this:

它可以像这样使用:

> 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

Where example.jscontains:

其中example.js包含:

function getSomeGlobal() {
    return someGlobal;
}

The big advantage of this method is that you've got complete control over the global variables in the executed script: you can pass in custom globals (via context), and all the globals created by the script will be added to context. Debugging is also easier because syntax errors and the like will be reported with the correct file name.

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

回答by Chris W.

Here's what I think is the 'rightest' answer for this situation.

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

Say you have a script file called quadtree.js.

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

You should build a custom node_modulethat has this sort of directory structure...

您应该构建一个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

Everything in your ./node_modules/quadtree/quadtree-lib/directory are files from your 3rd party library.

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

Then your ./node_modules/quadtree/index.jsfile will just load that library from the filesystem and do the work of exporting things properly.

然后您的./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

Now you can use your quadtreemodule like any other node module...

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

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


I like this method because there's no need to go changing any of the source code of your 3rd party library--so it's easier to maintain. All you need to do on upgrade is look at their source code and ensure that you are still exporting the proper objects.

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

回答by Christopher Weiss

The simplest way is: eval(require('fs').readFileSync('./path/to/file.js', 'utf8'));This works great for testing in the interactive shell.

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

回答by Martijn

AFAIK, that is indeed how modules must be loaded. However, instead of tacking all exported functions onto the exportsobject, you can also tack them onto this(what would otherwise be the global object).

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

So, if you want to keep the other libraries compatible, you can do this:

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

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

or, when the external library already has its own namespace, e.g. jQuery(not that you can use thatin a server-side environment):

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

this.jQuery = jQuery;

In a non-Node environment, thiswould resolve to the global object, thus making it a global variable... which it already was. So it shouldn't break anything.

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

Edit: James Herdman has a nice writeupabout node.js for beginners, which also mentions this.

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

回答by Chris W.

I'm not sure if I'll actually end up using this because it's a rather hacky solution, but one way around this is to build a little mini-module importer like this...

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

In the file ./node_modules/vanilla.js:

在文件中./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;
}

Then when you want to use your library's functionality you'll need to manually choose which names to export.

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

So for a library like the file ./lib/mylibrary.js...

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

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

When you want to use its functionality in your Node.js code...

当你想在你的 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)

Don't know how well this would all work in practice though.

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

回答by John Mee

I was able to make it work by updating their script, very easily, simply adding module.exports =where appropriate...

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

For example, I took theirfile and I copied to './libs/apprise.js'. Then where it starts with

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

function apprise(string, args, callback){

I assigned the function to module.exports =thus:

我将功能分配给module.exports =这样的:

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

Thus I'm able to import the library into mycode like this:

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

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

And I was good to go. YMMV, this was with webpack.

我很高兴去。YMMV,这是与webpack

回答by lama12345

A simple include(filename)function with better error messaging (stack, filename etc.) for eval, in case of errors:

一个简单的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");
    }
}

But it even gets dirtier with nodejs: you need to specify this:

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

export NODE_MODULE_CONTEXTS=1
nodejs tmp.js

Otherwise you cannot use global variables in files included with include(...).

否则你不能在包含在include(...).