簡體   English   中英

將“Vanilla”Javascript庫加載到Node.js中

[英]Load “Vanilla” Javascript Libraries into Node.js

有一些第三方Javascript庫具有我想在Node.js服務器中使用的一些功能。 (具體來說,我想使用我發現的QuadTree javascript庫。)但這些庫只是簡單的.js文件,而不是“Node.js庫”。

因此,這些庫不遵循Node.js對其模塊所期望的exports.var_name語法。 據我所知,這意味着當你做module = require('module_name'); module = require('./path/to/file.js'); 你最終會得到一個沒有可公開訪問功能的模塊等。

我的問題是“如何將任意javascript文件加載到Node.js中,以便我可以利用其功能而無需重寫它以便它確實可以exports ?”

我對Node.js很新,所以如果我對它的工作原理有一些明顯的漏洞,請告訴我。


編輯 :研究更多東西,我現在看到Node.js使用的模塊加載模式實際上是最近開發的加載Javascript庫的標准的一部分,稱為CommonJS 它在Node.js模塊文檔頁面上說明了這一點,但直到現在我才錯過了。

可能最終我的問題的答案是“等到你的圖書館的作者開始編寫一個CommonJS界面或者做你自己該死的自己。”

以下是我認為這種情況的“最正確”答案。

假設您有一個名為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/目錄中的所有內容都是來自第三方庫的文件。

然后你的./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();

我喜歡這種方法,因為不需要更改第三方庫的任何源代碼 - 所以它更容易維護。 升級所需要做的就是查看源代碼並確保仍在導出正確的對象。

有一個比使用eval更好的方法: vm模塊。

例如,這是我的execfile模塊,它在context或全局上下文中評估path中的腳本:

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 調試也更容易,因為將使用正確的文件名報告語法錯誤等。

最簡單的方法是: eval(require('fs').readFileSync('./path/to/file.js', 'utf8')); 這非常適合在交互式shell中進行測試。

AFAIK,確實是必須加載模塊的。 但是,不是將所有導出的函數都添加到exports對象上,而是可以將它們添加this (否則將是全局對象)。

因此,如果您想保持其他庫兼容,您可以這樣做:

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

或者,當外部庫已經有自己的命名空間時,例如jQuery (不是你可以在服務器端環境中使用 ):

this.jQuery = jQuery;

在非Node環境中, this將解析為全局對象,從而使其成為全局變量......它已經存在。 所以它不應該破壞任何東西。

編輯 :James Herdman為初學者提供了關於node.js的精彩文章,其中也提到了這一點。

我不確定我是否真的會最終使用它,因為它是一個相當hacky的解決方案,但是解決這個問題的一個方法是構建一個像這樣的小型模塊導入器...

./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)

不知道這在實踐中會有多好。

我能夠通過更新腳本來實現它的工作,非常簡單,只需在適當的時候添加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

一個簡單的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(...)附帶的文件中使用全局變量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM