लोड "वेनिला" जावास्क्रिप्ट लाइब्रेरी Node.js में


108

कुछ थर्ड पार्टी जावास्क्रिप्ट लाइब्रेरी हैं जिनकी कुछ कार्यक्षमता है जिन्हें मैं Node.js सर्वर में उपयोग करना चाहूंगा। (विशेष रूप से मैं एक क्वाडट्री जावास्क्रिप्ट लाइब्रेरी का उपयोग करना चाहता हूं जो मुझे मिला।) लेकिन ये लाइब्रेरी केवल सीधी .jsफाइलें हैं और न कि "नोड.जेएस लाइब्रेरी"।

जैसे, ये लाइब्रेरी exports.var_nameसिंटैक्स का पालन नहीं करती हैं जो Node.js अपने मॉड्यूल के लिए उम्मीद करते हैं। जहां तक ​​मैं समझता हूं कि जब आप करते हैं module = require('module_name');या module = require('./path/to/file.js');आप सार्वजनिक रूप से सुलभ कार्यों के साथ मॉड्यूल के साथ समाप्त होते हैं, तो आदि।

मेरा सवाल यह है कि "मैं एक मनमाने ढंग से जावास्क्रिप्ट फ़ाइल को Node.js में कैसे लोड करूं, ताकि मैं इसकी कार्यक्षमता का उपयोग किए बिना इसे फिर से लिखने के बिना उपयोग कर सकूं ताकि यह हो exports?"

मैं Node.js के लिए बहुत नया हूं इसलिए कृपया मुझे बताएं कि मेरी समझ में कुछ आकर्षक छेद है कि यह कैसे काम करता है।


संपादित करें : चीजों में अधिक शोध करना और मैं अब देखता हूं कि मॉड्यूल लोडिंग पैटर्न जिसे Node.js उपयोग करता है, वास्तव में कॉमनजस नामक जावास्क्रिप्ट लाइब्रेरी को लोड करने के लिए हाल ही में विकसित मानक का हिस्सा है । यह Node.js के लिए मॉड्यूल डॉक्टर पृष्ठ पर यह अधिकार कहता है , लेकिन मैंने अब तक याद किया।

यह समाप्त हो सकता है कि मेरे प्रश्न का उत्तर "प्रतीक्षा करें जब तक कि आपके पुस्तकालय के लेखक कॉमनजेएस इंटरफ़ेस लिखने के लिए चारों ओर नहीं पहुंच जाते हैं या यह आपके लानत नहीं है।"


संबंधित प्रश्न: stackoverflow.com/questions/22898080/…
जोसमर

जवाबों:


75

उपयोग करने की तुलना में बहुत बेहतर विधि है 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। डिबगिंग इसलिए भी आसान है क्योंकि सिंटैक्स त्रुटियां और पसंद को सही फ़ाइल नाम के साथ रिपोर्ट किया जाएगा।


क्या runInNewContextवैश्विक संदर्भ का उपयोग करता है यदि context(अन्यथा sandboxडॉक्स में कहा गया है ) अपरिभाषित है? (यह बिंदु मेरे द्वारा पाए गए किसी भी डॉक्स द्वारा स्पष्ट नहीं किया गया था)
स्टीवन लू

ऐसा लगता है कि, नोड या कॉमन जेजेएस पैटर्न से अनजान तीसरे पक्ष के पुस्तकालय के साथ खेलने के उद्देश्य से, क्रिस्टोफर की eval विधि < stackoverflow.com/a/9823294/1450294 > अच्छा काम करती है। vmइस मामले में मॉड्यूल क्या लाभ दे सकता है ?
माइकल शेपर

2
इस पद्धति के बारे में विस्तार से बताने के लिए मेरे अपडेट देखें।
डेविड वोलेवर

1
यह पूरी तरह से चट्टानों - इसने मुझे सर्वर-साइड कार्यान्वयन के लिए अपने वेब-आधारित गैर-मॉड्यूल कोड को तुरंत उपयोग करने की अनुमति दी, जो आउटपुट को ईमेल करता है [एक अनुसूची पर] बजाय उन्हें एक वेबपेज पर प्रदर्शित करने के। सभी वेब कोड ने ढीले-वृद्धि वाले मॉड्यूल पैटर्न और स्क्रिप्ट इंजेक्शन का उपयोग किया - इसलिए यह इतना अच्छा काम करता है !!
अल जोसलिन

यदि उदाहरण.जेएस example1.js लाइब्रेरी पर निर्भर करता है, तो हम Node.js में इसका उपयोग कैसे कर सकते हैं?
सायटोक

80

यहां मैं समझता हूं कि इस स्थिति के लिए 'सबसे सही' उत्तर है।

कहते हैं कि आपके पास एक स्क्रिप्ट फ़ाइल है 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();

मुझे यह तरीका पसंद है क्योंकि आपके तीसरे पक्ष के पुस्तकालय के किसी भी स्रोत कोड को बदलने की कोई आवश्यकता नहीं है - इसलिए इसे बनाए रखना आसान है। अपग्रेड पर आपको बस इतना करना चाहिए कि वे अपने सोर्स कोड को देखें और सुनिश्चित करें कि आप अभी भी उचित वस्तुओं का निर्यात कर रहे हैं।


3
बस अपना जवाब मिला (एक मल्टीप्लेयर गेम बनाने और JigLibJS, हमारे भौतिकी इंजन, सर्वर पर और साथ ही क्लाइंट को शामिल करने की आवश्यकता है) आपने मुझे बहुत समय और परेशानी से बचाया। धन्यवाद!
स्टीवेंडेसु

8
यदि आप इसका ठीक-ठीक पालन करते हैं, तो ध्यान रखें कि गलती से NPM का उपयोग करके अपने node_modules फ़ोल्डर को अनियंत्रित करना बहुत आसान है, खासकर यदि आप इसे SCM में चेक नहीं करते हैं। निश्चित रूप से अपनी QuadTree लाइब्रेरी को एक अलग रिपॉजिटरी में रखने पर विचार करें, फिर npm linkइसे अपने आवेदन में शामिल करें। फिर इसे ऐसे संभाला जाता है मानो यह एक देशी Node.js पैकेज हो।
btown

@ शहर, क्या आप मेरे जैसे एससीएम और एनपीएम लिंक के लिए न्यूबाइट्स के लिए थोड़ा विस्तार कर सकते हैं जो आपके द्वारा उल्लिखित संभावित मुद्दे को रोकता है?
फ्लिअन

अगर मैं सिर्फ एक स्क्रिप्ट को शामिल करना चाहता हूं, तो क्या यह वास्तव में आवश्यक है?
क्वांटम्पोटेटो सेप

1
@flion दूसरों के लिए पुरानी टिप्पणी का उत्तर देने के लिए रेफरी के रूप में मुझे यकीन है कि आप जानते हैं कि आप अब तक जवाब देंगे। SCM - सोर्स कंट्रोल मैनेजमेंट (जैसे GIT) और npm लिंक
delp

30

सबसे सरल तरीका है: eval(require('fs').readFileSync('./path/to/file.js', 'utf8')); यह इंटरैक्टिव शेल में परीक्षण के लिए बहुत अच्छा काम करता है।


1
चीयर्स दोस्त! बहुत मदद की
Schoening

यह सबसे तेज़ तरीका भी है, और कभी-कभी जल्दी-जल्दी गंदे भी होते हैं। इस और डेविड के उत्तर के बीच, यह एसओ पृष्ठ एक उत्कृष्ट संसाधन है।
माइकल स्काइपर

5

AFAIK, यह वास्तव में है कि कैसे मॉड्यूल लोड किया जाना चाहिए। हालाँकि, exportsऑब्जेक्ट पर सभी निर्यात किए गए फ़ंक्शंस से निपटने के बजाय , आप उन पर भी व्यवहार कर सकते हैं this(जो अन्यथा वैश्विक ऑब्जेक्ट होगा)।

इसलिए, यदि आप अन्य पुस्तकालयों को संगत रखना चाहते हैं, तो आप ऐसा कर सकते हैं:

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

या, बाहरी पुस्तकालय लिए पहले से कोई नाम स्थान, जैसे है जब jQuery(नहीं आप उपयोग कर सकते हैं कि कि सर्वर की ओर से वातावरण में):

this.jQuery = jQuery;

एक गैर-नोड वातावरण में, thisवैश्विक ऑब्जेक्ट को हल करेगा, इस प्रकार यह एक वैश्विक चर बनाता है ... जो कि यह पहले से ही था। इसलिए इसे कुछ भी तोड़ना नहीं चाहिए।

संपादित करें : जेम्स हेरडमैन ने शुरुआती लोगों के लिए नोड.जेएस के बारे में एक अच्छा राइटअप किया है, जिसमें यह भी उल्लेख किया गया है।


'यह' ट्रिक चीजों को अधिक पोर्टेबल बनाने के लिए एक अच्छा तरीका लगता है ताकि Node.js पुस्तकालयों का उपयोग Node.js के बाहर किया जा सके, लेकिन इसका मतलब यह है कि मुझे नोड्स का समर्थन करने के लिए मैन्युअल रूप से अपनी जावास्क्रिप्ट लाइब्रेरी को बदलने की आवश्यकता है। ।
क्रिस डब्ल्यू।

@ क्रिस:। हां, आपको अपने पुस्तकालयों को मैन्युअल रूप से बदलना होगा। व्यक्तिगत रूप से, मुझे बाहरी फ़ाइलों को शामिल करने के लिए एक दूसरा तंत्र भी पसंद आया होगा, एक जो स्वचालित रूप से शामिल फ़ाइल के वैश्विक नाम स्थान को आयातित नामस्थान में परिवर्तित कर देता है। शायद आप नोड डेवलपर्स के लिए एक RFE फाइल कर सकते हैं?
मार्टिअन

3

मुझे यकीन नहीं है कि अगर मैं वास्तव में इसका उपयोग कर रहा हूं क्योंकि यह एक घिनौना समाधान है, लेकिन इसके चारों ओर एक तरह से इस तरह के एक छोटे से मॉड्यूल का निर्माण करना है ...

फ़ाइल में ./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)

न जाने कितनी अच्छी तरह से यह सब अभ्यास में काम करेगा।


अरे, वाह: एक डाउन-वोटेड (मेरे द्वारा नहीं) और एक ही प्रश्न के लिए एक ही उपयोगकर्ता द्वारा अप-वोट किए गए उत्तर! उसके लिए एक बैज होना चाहिए! ;-)
माइकल शीपर

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, यह वेबपैक के साथ था ।


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

लेकिन यह भी नोडज के साथ गंदा हो जाता है: आपको इसे निर्दिष्ट करने की आवश्यकता है:

export NODE_MODULE_CONTEXTS=1
nodejs tmp.js

अन्यथा आप शामिल फ़ाइलों में वैश्विक चर का उपयोग नहीं कर सकते हैं include(...)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.