112

Node.js サーバーで使用したい機能を備えたサードパーティの Javascript ライブラリがいくつかあります。(具体的には、私が見つけた QuadTree JavaScript ライブラリを使用したいと考えています。)しかし、これらのライブラリは単純な.jsファイルであり、「Node.js ライブラリ」ではありません。

exports.var_nameそのため、これらのライブラリは、Node.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/サードパーティ ライブラリのファイルです。

次に、./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();

サードパーティ ライブラリのソース コードを変更する必要がないため、この方法が気に入っています。メンテナンスが容易です。アップグレード時に行う必要があるのは、それらのソース コードを調べて、適切なオブジェクトを引き続きエクスポートしていることを確認することだけです。

于 2011-03-07T21:53:22.443 に答える
77

evalモジュールを使用するよりもはるかに優れた方法がありvmます。

たとえば、次のモジュールは、グローバル コンテキスト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;
}

そして、次のように使用できます。

> 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')); これは、インタラクティブシェルでのテストに最適です。

于 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

これはかなりハックなソリューションであるため、実際にこれを使用することになるかどうかはわかりませんが、これを回避する1つの方法は、このような小さなミニモジュールインポーターを構築することです...

ファイル内./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 に答える