आवश्यकताएँ / AMD के साथ परिपत्र निर्भरता कैसे संभालें?


80

मेरे सिस्टम में, मेरे पास कई "कक्षाएं" हैं जो ब्राउज़र में भरी हुई हैं, प्रत्येक विकास के दौरान एक अलग फाइल, और उत्पादन के लिए एक साथ मिलाई गई हैं। जैसा कि वे लोड किए गए हैं, वे एक वैश्विक वस्तु पर एक संपत्ति को इनिशियलाइज़ करते हैं, यहाँ G, इस उदाहरण में:

var G = {};

G.Employee = function(name) {
    this.name = name;
    this.company = new G.Company(name + "'s own company");
};

G.Company = function(name) {
    this.name = name;
    this.employees = [];
};
G.Company.prototype.addEmployee = function(name) {
    var employee = new G.Employee(name);
    this.employees.push(employee);
    employee.company = this;
};

var john = new G.Employee("John");
var bigCorp = new G.Company("Big Corp");
bigCorp.addEmployee("Mary");

अपने स्वयं के वैश्विक ऑब्जेक्ट का उपयोग करने के बजाय, मैं जेम्स बर्क के सुझाव के आधार पर प्रत्येक वर्ग को अपना एएमडी मॉड्यूल बनाने पर विचार कर रहा हूं :

define("Employee", ["Company"], function(Company) {
    return function (name) {
        this.name = name;
        this.company = new Company(name + "'s own company");
    };
});
define("Company", ["Employee"], function(Employee) {
    function Company(name) {
        this.name = name;
        this.employees = [];
    };
    Company.prototype.addEmployee = function(name) {
        var employee = new Employee(name);
        this.employees.push(employee);
        employee.company = this;
    };
    return Company;
});
define("main", ["Employee", "Company"], function (Employee, Company) {
    var john = new Employee("John");
    var bigCorp = new Company("Big Corp");
    bigCorp.addEmployee("Mary");
});

मुद्दा यह है कि पहले, कर्मचारी और कंपनी के बीच कोई घोषित समय-निर्भरता नहीं थी: आप घोषणा को उस क्रम में रख सकते थे जो आप चाहते थे, लेकिन अब, आवश्यकता के अनुसार, यह एक निर्भरता का परिचय देता है, जो यहां (जानबूझकर) परिपत्र है, इसलिए उपरोक्त कोड विफल रहता है। बेशक, addEmployee()पहली पंक्ति var Employee = require("Employee");को जोड़ने से यह काम कर जाएगा , लेकिन मैं इस समाधान को आवश्यकता के रूप में नहीं देख रहा हूं आवश्यकता अनुसार / एएमडी का उपयोग नहीं करने के लिए क्योंकि मुझे इसकी आवश्यकता है, डेवलपर, इस नव निर्मित परिपत्र निर्भरता के बारे में पता होना और इसके बारे में कुछ करना।

क्या इस समस्या का समाधान करने के लिए एक बेहतर तरीका है आवश्यकता / एएमडी, या क्या मैं कुछ के लिए आवश्यकता / एज़ेडजेएस का उपयोग कर रहा हूं जो इसके लिए डिज़ाइन नहीं किया गया था?

जवाबों:


59

यह वास्तव में एएमडी प्रारूप में प्रतिबंध है। आप निर्यात का उपयोग कर सकते हैं, और यह समस्या दूर हो जाती है। मुझे लगता है कि निर्यात बदसूरत है, लेकिन यह है कि कैसे नियमित रूप से कॉमनजस मॉड्यूल समस्या का समाधान करते हैं:

define("Employee", ["exports", "Company"], function(exports, Company) {
    function Employee(name) {
        this.name = name;
        this.company = new Company.Company(name + "'s own company");
    };
    exports.Employee = Employee;
});
define("Company", ["exports", "Employee"], function(exports, Employee) {
    function Company(name) {
        this.name = name;
        this.employees = [];
    };
    Company.prototype.addEmployee = function(name) {
        var employee = new Employee.Employee(name);
        this.employees.push(employee);
        employee.company = this;
    };
    exports.Company = Company;
});

अन्यथा, आपके संदेश में आपके द्वारा उल्लेखित आवश्यकता ("कर्मचारी") भी काम करेगा।

सामान्य तौर पर मॉड्यूल के साथ आपको परिपत्र निर्भरता, एएमडी या नहीं के बारे में अधिक जागरूक होना चाहिए। सादे जावास्क्रिप्ट में भी, आपको अपने उदाहरण में G ऑब्जेक्ट जैसी किसी वस्तु का उपयोग करना सुनिश्चित करना होगा।


3
मुझे लगा कि आपको कॉलबैक की तर्क सूची, जैसे function(exports, Company)और दोनों में निर्यात की घोषणा करनी होगी function(exports, Employee)। वैसे भी, आवश्यकता के लिए धन्यवाद, यह एक महत्वपूर्ण है।
सेबेस्टियन रोक्कासेरा

@jrburke मुझे लगता है कि यह एक-प्रत्यक्ष रूप से सही हो सकता है, एक मध्यस्थ या कोर या अन्य टॉप-डाउन घटक के लिए? क्या यह एक भयानक विचार है, दोनों तरीकों का उपयोग करके इसे सुलभ बनाने के लिए? stackoverflow.com/questions/11264827/…
SimpleGy

1
मुझे यकीन नहीं है कि मैं समझता हूं कि यह समस्या को कैसे हल करता है। मेरी समझ यह है कि सभी निर्भरता को परिभाषित रन से पहले लोड किया जाना चाहिए। क्या ऐसा नहीं है यदि "निर्यात" को पहली निर्भरता के रूप में पारित किया जाता है?
बीटी

1
क्या आप फ़ंक्शन में परम के रूप में निर्यात को याद नहीं करते हैं?
शबूनक

1
अनुपलब्ध निर्यात परम के बारे में @ शबनम की बात पर अमल करने के लिए, यह प्रश्न देखें: stackoverflow.com/questions/28193382/…
Michael.Lumley

15

मुझे लगता है कि यह बड़ी परियोजनाओं में काफी कमी है जहां (बहु-स्तरीय) परिपत्र निर्भरताएं अनिर्धारित रहती हैं। हालांकि, मैडेज के साथ आप उनसे संपर्क करने के लिए परिपत्र निर्भरता की सूची प्रिंट कर सकते हैं।

madge --circular --format amd /path/src

CACSVML-13295: sc-admin-ui-express amills001c $ madge --circular --format amd ./ कोई परिपत्र निर्भरता नहीं मिली!
अलेक्जेंडर मिल्स

8

यदि आपको शुरू में लोड होने के लिए अपनी निर्भरता की आवश्यकता नहीं है (जैसे, जब आप एक वर्ग का विस्तार कर रहे हैं), तो यह वही है जो आप कर सकते हैं: ( http://requirejs.org/docs/api.html# से लिया गया) परिपत्र )

फ़ाइल में a.js:

    define( [ 'B' ], function( B ){

        // Just an example
        return B.extend({
            // ...
        })

    });

और दूसरी फाइल में b.js:

    define( [ ], function( ){ // Note that A is not listed

        var a;
        require(['A'], function( A ){
            a = new A();
        });

        return function(){
            functionThatDependsOnA: function(){
                // Note that 'a' is not used until here
                a.doStuff();
            }
        };

    });

ओपी के उदाहरण में, यह इस प्रकार होगा:

    define("Employee", [], function() {

        var Company;
        require(["Company"], function( C ){
            // Delayed loading
            Company = C;
        });

        return function (name) {
            this.name = name;
            this.company = new Company(name + "'s own company");
        };
    });

    define("Company", ["Employee"], function(Employee) {
        function Company(name) {
            this.name = name;
            this.employees = [];
        };
        Company.prototype.addEmployee = function(name) {
            var employee = new Employee(name);
            this.employees.push(employee);
            employee.company = this;
        };
        return Company;
    });

    define("main", ["Employee", "Company"], function (Employee, Company) {
        var john = new Employee("John");
        var bigCorp = new Company("Big Corp");
        bigCorp.addEmployee("Mary");
    });

2
जैसा कि गिल्ली ने अपनी टिप्पणी में कहा, यह समाधान गलत है और हमेशा काम नहीं करेगा। एक दौड़ की स्थिति है जिस पर पहले कोड ब्लॉक निष्पादित किया जाएगा।
लुइस एमलिन

6

मैंने परिपत्र निर्भरताओं पर डॉक्स को देखा: http://requirejs.org/docs/api.html#circular

यदि a और b के साथ एक परिपत्र निर्भरता है, तो यह आपके मॉड्यूल में इस तरह से निर्भरता के रूप में आवश्यकता को जोड़ने के लिए कहता है:

define(["require", "a"],function(require, a) { ....

तब जब आपको "a just just" "a" जैसी आवश्यकता हो:

return function(title) {
        return require("a").doSomething();
    }

इसने मेरे लिए काम किया


5

मैं सिर्फ परिपत्र निर्भरता से बचना होगा। शायद कुछ इस तरह:

G.Company.prototype.addEmployee = function(employee) {
    this.employees.push(employee);
    employee.company = this;
};

var mary = new G.Employee("Mary");
var bigCorp = new G.Company("Big Corp");
bigCorp.addEmployee(mary);

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

यदि आपको वास्तव में एक काम जोड़ना है, तो क्लीनर एक आईएमओ को केवल समय (इस मामले में आपके निर्यात कार्यों में) पर निर्भरता की आवश्यकता है, फिर परिभाषा फ़ंक्शन ठीक चलेंगे। लेकिन यहां तक ​​कि क्लीनर आईएमओ केवल परिपत्र निर्भरता से पूरी तरह से बचने के लिए है, जो आपके मामले में वास्तव में करना आसान लगता है।


2
आप एक डोमेन मॉडल को सरल बनाने और इसे कम उपयोग करने योग्य बनाने का सुझाव दे रहे हैं, क्योंकि अपेक्षित उपकरण का समर्थन नहीं करता है। उपकरण डेवलपर के जीवन को आसान बनाने के लिए हैं। डोमेन मॉडल बहुत सरल है - कर्मचारी और कंपनी। कर्मचारी वस्तु को पता होना चाहिए कि वह किस कंपनी के लिए काम करता है, कंपनियों के पास कर्मचारियों की एक सूची होनी चाहिए। डोमेन मॉडल सही है, यह वह उपकरण है जो यहां विफल रहता है
डेट्रायेल

5

सभी पोस्ट किए गए उत्तर ( https://stackoverflow.com/a/25170248/14731 को छोड़कर ) गलत हैं। यहां तक ​​कि आधिकारिक दस्तावेज (नवंबर 2014 तक) गलत है।

एकमात्र समाधान जो मेरे लिए काम करता है, वह है "गेटकीपर" फ़ाइल की घोषणा करना, और यह किसी भी विधि को परिभाषित करता है जो परिपत्र निर्भरता पर निर्भर करता है। एक ठोस उदाहरण के लिए https://stackoverflow.com/a/26809254/14731 देखें ।


यहाँ क्यों उपरोक्त समाधान काम नहीं करेगा।

  1. आप नहीं कर सकते:
var a;
require(['A'], function( A ){
     a = new A();
});

और फिर aबाद में उपयोग करें , क्योंकि इस बात की कोई गारंटी नहीं है कि यह कोड ब्लॉक उपयोग करने वाले कोड ब्लॉक से पहले निष्पादित हो जाएगा a। (यह समाधान भ्रामक है क्योंकि यह समय का ९ ०% काम करता है)

  1. मैं यह मानने का कोई कारण नहीं देखता कि exportsयह एक ही जाति की स्थिति के लिए असुरक्षित नहीं है।

इसका समाधान यह है:

//module A

    define(['B'], function(b){

       function A(b){ console.log(b)}

       return new A(b); //OK as is

    });


//module B

    define(['A'], function(a){

         function B(a){}

         return new B(a);  //wait...we can't do this! RequireJS will throw an error if we do this.

    });


//module B, new and improved
    define(function(){

         function B(a){}

       return function(a){   //return a function which won't immediately execute
              return new B(a);
        }

    });

अब हम मॉड्यूल C में इन मॉड्यूल A और B का उपयोग कर सकते हैं

//module C
    define(['A','B'], function(a,b){

        var c = b(a);  //executes synchronously (no race conditions) in other words, a is definitely defined before being passed to b

    });

btw, अगर आप अभी भी इस से परेशान हैं, तो @ yeahdixon का उत्तर सही होना चाहिए, और मुझे लगता है कि दस्तावेज़ स्वयं ही सही है।
अलेक्जेंडर मिल्स

मैं मानता हूं कि आपकी कार्यप्रणाली काम करती है लेकिन मुझे लगता है कि प्रलेखन सही है, और "तुल्यकालिक" के करीब एक कदम हो सकता है।
अलेक्जेंडर मिल्स

आप क्योंकि सभी चर लोड पर सेट किए गए हैं। जब तक आपके उपयोगकर्ता समय के यात्री नहीं होते हैं और मौजूद होने से पहले बटन पर क्लिक करते हैं। यह कार्य-कारण को तोड़ देगा और फिर एक दौड़ की स्थिति संभव है।
एडी

0

मेरे मामले में मैंने "सरल" ऑब्जेक्ट के कोड को अधिक जटिल में स्थानांतरित करके परिपत्र निर्भरता को हल किया। मेरे लिए वह एक संग्रह और एक मॉडल वर्ग था। मुझे लगता है कि आपके मामले में मैं कंपनी के कर्मचारी-विशिष्ट भागों को कर्मचारी वर्ग में जोड़ दूंगा।

define("Employee", ["Company"], function(Company) {
    function Employee (name) {
        this.name = name;
        this.company = new Company(name + "'s own company");
    };
    Company.prototype.addEmployee = function(name) {
        var employee = new Employee(name);
        this.employees.push(employee);
        employee.company = this;
    };

    return Employee;
});
define("Company", [], function() {
    function Company(name) {
        this.name = name;
        this.employees = [];
    };
    return Company;
});
define("main", ["Employee", "Company"], function (Employee, Company) {
    var john = new Employee("John");
    var bigCorp = new Company("Big Corp");
    bigCorp.addEmployee("Mary");
});

थोड़ा हैकी, लेकिन यह सरल मामलों के लिए काम करना चाहिए। और यदि आप addEmployeeएक कर्मचारी को पैरामीटर के रूप में लेने के लिए रिफ्लेक्टर करते हैं, तो बाहरी लोगों के लिए निर्भरता और भी स्पष्ट होनी चाहिए।

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