"Vanilla" Javascript Kitaplıklarını Node.js'ye yükleyin


108

Bir Node.js sunucusunda kullanmak istediğim bazı işlevlere sahip bazı üçüncü taraf Javascript kitaplıkları var. (Özellikle bulduğum bir QuadTree javascript kitaplığını kullanmak istiyorum.) Ancak bu kitaplıklar, .js"Node.js kitaplıkları" değil , yalnızca basit dosyalardır.

Bu nedenle, bu kitaplıklar exports.var_nameNode.js'nin modülleri için beklediği sözdizimini izlemez. Anladığım kadarıyla bu, bunu yaptığınızda module = require('module_name');veya module = require('./path/to/file.js');halka açık işlevleri olmayan bir modülle sonuçlanacağınız anlamına gelir .

Öyleyse sorum şu: "Node.js'ye rastgele bir javascript dosyasını nasıl yüklerim, böylece işlevini yeniden yazmak zorunda kalmadan kullanabilirim, böylece işe yarayabilir exports?"

Node.js'de çok yeniyim, bu yüzden nasıl çalıştığına dair anlayışımda göze batan bir boşluk olup olmadığını lütfen bana bildirin.


DÜZENLEME : Daha fazla şey araştırmak ve şimdi Node.js'nin kullandığı modül yükleme modelinin aslında CommonJS adlı Javascript kitaplıklarını yüklemek için yeni geliştirilen bir standardın parçası olduğunu görüyorum . Node.js için modül dokümanı sayfasında bu yazıyor , ancak şimdiye kadar bunu kaçırdım.

Sorumun cevabının "kütüphanenizin yazarlarının bir CommonJS arayüzü yazmaya gelmesini bekleyin veya bunu kendi kendinize yapın" olabilir.


Yanıtlar:


75

Kullanmaktan çok daha iyi bir yöntem var eval: vmmodül.

Örneğin, burada execfilekomut dosyasını pathya contextda küresel bağlamda değerlendiren benim modülüm:

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

Ve şu şekilde kullanılabilir:

> 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

Nerede example.jsbulunur:

function getSomeGlobal() {
    return someGlobal;
}

Bu yöntemin en büyük avantajı, yürütülen komut dosyasındaki global değişkenler üzerinde tam kontrole sahip olmanızdır: özel globalleri (aracılığıyla context) geçirebilirsiniz ve komut dosyası tarafından oluşturulan tüm globaller eklenir context. Sözdizimi hataları ve benzerleri doğru dosya adıyla rapor edileceği için hata ayıklama da daha kolaydır.


( Dokümanlarda aksi belirtilmedikçe ) tanımsız runInNewContextise genel bağlamı kullanıyor mu ? (bulduğum hiçbir belge bu noktayı netleştirmedi)contextsandbox
Steven Lu

Görünüşe göre, Düğüm veya CommonJS kalıbından habersiz bir üçüncü taraf kitaplığı ile oynamak amacıyla, Christopher eval yöntemi < stackoverflow.com/a/9823294/1450294 > iyi çalışıyor. vmModül bu durumda ne gibi faydalar sağlayabilir ?
Michael Scheper

2
Bu yöntemin neden eval'dan daha iyi olduğuna ilişkin bir açıklama için güncellemelerime bakın.
David Wolever

1
bu tamamen harika - çıktıları bir web sayfasında görüntülemek yerine [bir zamanlamaya göre] e-postayla gönderen bir sunucu tarafı uygulaması için web tabanlı modül olmayan kodumu anında yeniden kullanmama izin verdi. Tüm web kodu gevşek artırma modülü kalıbını ve komut dosyası yerleştirmeyi kullandı - bu yüzden bu çok iyi çalışıyor !!
Al Joslin

Example.js, example1.js kitaplığına bağlıysa, bunu Node.js'de nasıl kullanabiliriz?
sytolk

80

İşte bu durum için 'en doğru' cevap olduğunu düşünüyorum.

Adında bir komut dosyanız olduğunu varsayalım quadtree.js.

node_moduleBu tür bir dizin yapısına sahip bir özel oluşturmalısınız ...

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

Dizininizdeki her şey ./node_modules/quadtree/quadtree-lib/3. taraf kitaplığınızdaki dosyalardır.

Ardından ./node_modules/quadtree/index.jsdosyanız bu kitaplığı dosya sisteminden yükleyecek ve her şeyi düzgün bir şekilde dışa aktarma işini yapacaktır.

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

Artık quadtreemodülünüzü diğer herhangi bir düğüm modülü gibi kullanabilirsiniz ...

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

Bu yöntemi seviyorum çünkü 3. taraf kitaplığınızın herhangi bir kaynak kodunu değiştirmeye gerek yok - bu nedenle bakımı daha kolay. Yükseltme sırasında yapmanız gereken tek şey, kaynak kodlarına bakmak ve uygun nesneleri dışa aktarmaya devam ettiğinizden emin olmaktır.


3
Cevabınızı yeni buldum (çok oyunculu bir oyun yapmak ve JigLibJS, fizik motorumuzun sunucuya ve istemciye dahil edilmesi gerekiyordu) bana ÇOK zaman ve güçlük kazandırdınız. Teşekkür ederim!
stevendesu

8
Bunu tam olarak takip ederseniz, NPM kullanarak node_modules klasörünüzü yanlışlıkla silmenin oldukça kolay olduğunu unutmayın, özellikle de SCM'ye iade etmezseniz. QuadTree kitaplığınızı ayrı bir depoya koyup ardından npm linkuygulamanıza eklemeyi kesinlikle düşünün . Ardından, yerel bir Node.js paketi gibi işlenir.
btown

@btown, benim gibi yeni başlayanlar için SCM ve npm bağlantısının tam olarak ne yaptığını ve bahsettiğiniz olası sorunu önleyen biraz genişletebilir misiniz?
Flion

Sadece bir senaryo eklemek istersem bu gerçekten gerekli mi?
quantumpotato

1
@flion, başkaları için eski yorumu yanıtlarken, eminim ki şimdiye kadar cevapladığınızı bileceksiniz. SCM - Kaynak Kontrol Yönetimi (örn. GIT) ve npm bağlantısının
delp

30

En basit yol şudur: eval(require('fs').readFileSync('./path/to/file.js', 'utf8')); Bu, etkileşimli kabukta test etmek için harika çalışıyor.


1
Şerefe arkadaşlar! Çok yardımcı oldu
Schoening

Bu aynı zamanda en hızlı yoldur ve bazen hızlı ve kirli ihtiyacınız olan şeydir. Bununla David'in cevabı arasında, bu SO sayfası mükemmel bir kaynaktır.
Michael Scheper

5

AFAIK, modüllerin nasıl yüklenmesi gerektiği gerçekten de budur. Ancak, dışa aktarılan tüm işlevleri exportsnesneye yapıştırmak yerine, bunları this(aksi takdirde küresel nesne olacak) üzerine de tutturabilirsiniz .

Yani, diğer kitaplıkları uyumlu tutmak istiyorsanız, bunu yapabilirsiniz:

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

veya harici kütüphane zaten kendi ad, örneğin sahip olduğunda jQuery(değil kendinizin kullanabileceği bu bir sunucu tarafı ortamında):

this.jQuery = jQuery;

Düğüm olmayan bir ortamda, thisgenel nesneye çözümlenir, böylece onu zaten olduğu gibi küresel bir değişken haline getirir. Yani hiçbir şeyi kırmamalı.

Düzenleme : James Herdman'ın yeni başlayanlar için node.js hakkında güzel bir yazısı var, bu da bundan bahsediyor.


'Bu' hilesi, işleri daha taşınabilir hale getirmenin iyi bir yolu gibi görünüyor, böylece Node.js kitaplıkları Node.js dışında kullanılabilir, ancak yine de javascript kitaplıklarımı Node.js sözdizimi gerektirecek şekilde manuel olarak değiştirmem gerektiği anlamına gelir .
Chris W.

@ChrisW .: evet, kitaplıklarınızı manuel olarak değiştirmeniz gerekecek. Şahsen ben de harici dosyaları dahil etmek için ikinci bir mekanizma isterdim, bir tane dahil edilen dosyanın genel ad alanını otomatik olarak içe aktarılan ad alanına dönüştürür. Belki Node geliştiricilerine bir RFE gönderebilirsiniz?
Martijn

3

Bunu gerçekten kullanıp bitiremeyeceğimden emin değilim çünkü bu oldukça karmaşık bir çözüm, ancak bunun bir yolu, bunun gibi küçük bir mini modül ithalatçısı oluşturmaktır ...

Dosyada ./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;
}

Ardından, kitaplığınızın işlevini kullanmak istediğinizde, hangi adları dışa aktaracağınızı manuel olarak seçmeniz gerekir.

Yani dosya gibi bir kitaplık için ./lib/mylibrary.js...

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

İşlevselliğini Node.js kodunuzda kullanmak istediğinizde ...

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)

Tüm bunların pratikte ne kadar işe yarayacağını bilmiyorum.


Hey, vay canına: aynı soru için aynı kullanıcı tarafından aşağı oylanmış (benim tarafımdan değil) ve yukarı oyla! Bunun için bir rozet olmalı! ;-)
Michael Scheper

2

Senaryolarını çok kolay bir şekilde güncelleyerek, module.exports =uygun olan yere basitçe ekleyerek çalışmasını sağladım ...

Örneğin, onların dosyasını aldım ve './libs/apprise.js' dosyasına kopyaladım. O zaman nerede başlar

function apprise(string, args, callback){

İşlevi şu module.exports =şekilde atadım:

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

Böylece kitaplığı şu şekilde koduma aktarabiliyorum :

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

Ve gitmek için iyiydim. YMMV, bu webpack ile oldu .


0

include(filename)Hata evaldurumunda aşağıdakiler için daha iyi hata mesajı (yığın, dosya adı vb.) İçeren basit bir işlev :

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

Ancak nodejs ile daha da kirlenir: bunu belirtmeniz gerekir:

export NODE_MODULE_CONTEXTS=1
nodejs tmp.js

Aksi takdirde, ile birlikte verilen dosyalarda global değişkenleri kullanamazsınız include(...).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.