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

IT技术 javascript node.js commonjs
2021-01-28 05:05:11

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

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

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

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


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

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

6个回答

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

假设您有一个名为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使用任何其他节点module一样使用您的module...

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

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

@btown,您能否为像我这样的新手扩展一下 SCM 和 npm 链接究竟是做什么的,以防止您提到的潜在问题?
2021-03-18 05:05:11
刚刚找到您的答案(制作多人游戏并且需要在服务器和客户端上包含 JigLibJS,我们的物理引擎),您为我节省了很多时间和麻烦。谢谢!
2021-03-28 05:05:11
如果您完全遵循这一点,请记住,使用 NPM 很容易意外删除您的 node_modules 文件夹,特别是如果您没有将其签入 SCM。绝对考虑将您的 QuadTree 库放在一个单独的存储库中,然后将npm link其放入您的应用程序中。然后将其作为本机 Node.js 包进行处理。
2021-04-01 05:05:11
如果我只想包含一个脚本,这真的有必要吗?
2021-04-05 05:05:11
@flion 回复其他人的旧评论 ref 因为我相信你现在知道你已经回答了。SCM - 源代码控制管理(例如 GIT)和一个指向npm 链接的快速但很好的演示的链接
2021-04-10 05:05:11

有一个比使用更好的方法evalvmmodule。

例如,这是我的execfilemodule,它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;
}

它可以像这样使用:

> 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. 调试也更容易,因为语法错误等会以正确的文件名报告。

看来,为了玩弄不了解 Node 或 CommonJS 模式的第三方库,Christopher 的 eval 方法 < stackoverflow.com/a/9823294/1450294 > 效果很好。vm在这种情况下module可以提供什么好处
2021-03-28 05:05:11
有关为什么此方法比 eval 更好的说明,请参阅我的更新。
2021-03-31 05:05:11
完全令人震惊——它让我能够立即将基于 Web 的非module代码重新用于服务器端实现,该实现通过电子邮件 [按计划] 发送输出,而不是将它们显示在网页上。所有的网络代码都使用了松散扩展module模式和脚本注入——所以这很好用!!
2021-04-03 05:05:11
如果 example.js 依赖于 example1.js 库,我们如何在 Node.js 中使用它?
2021-04-10 05:05:11
runInNewContext如果contextsandbox在文档中也称为, )未定义,是否使用全局上下文(我发现的任何文档都没有明确说明这一点)
2021-04-11 05:05:11

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

队友的欢呼声!帮助了很多
2021-04-02 05:05:11
这也是最快的方式,有时又快又脏就是你所需要的。在这个和大卫的回答之间,这个 SO 页面是一个很好的资源。
2021-04-11 05:05:11

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

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

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

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

this.jQuery = jQuery;

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

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

@ChrisW。:是的,您必须手动更改库。就我个人而言,我还希望采用第二种机制来包含外部文件,即自动将包含文件的全局命名空间转换为导入命名空间的机制。也许您可以向 Node 开发人员提交 RFE?
2021-03-14 05:05:11
“这个”技巧听起来是一个让事情更便携的好方法,这样 Node.js 库就可以在 Node.js 之外使用,但这仍然意味着我需要手动更改我的 javascript 库以支持 Node.js require 语法.
2021-04-04 05:05:11

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

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

不知道这在实践中的效果如何。

嘿,哇:同一个用户对同一问题的反对票(不是我)和赞成票的答案!应该有一个徽章!;-)
2021-04-13 05:05:11