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

私自身のグローバルオブジェクトを使用する代わりに、James Burkeの提案に基づいて、各クラスを独自のAMDモジュールにすることを検討しています。

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

問題は、以前は従業員と会社の間に宣言時の依存関係がなかったことです。宣言は任意の順序で配置できましたが、今ではRequireJSを使用すると、依存関係が導入されます。これは(意図的に)循環しているため、上記のコードは失敗します。もちろん、では、最初の行をaddEmployee()追加すると機能しますが、このソリューションは、開発者である私がこの新しく作成された循環依存関係を認識して何かを行う必要があるため、RequireJS/AMDを使用しない場合よりも劣ると思います。var Employee = require("Employee");

RequireJS / AMDでこの問題を解決するためのより良い方法はありますか、それとも私はRequireJS / AMDを設計されていないものに使用していますか?

4

7 に答える 7

59

これは確かにAMDフォーマットの制限です。エクスポートを使用することができ、その問題はなくなります。エクスポートは醜いですが、通常のCommonJSモジュールが問題を解決する方法です。

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

それ以外の場合は、メッセージで言及したrequire( "Employee")も機能します。

一般に、モジュールでは、AMDであるかどうかにかかわらず、循環依存関係をよりよく認識する必要があります。プレーンなJavaScriptでも、例ではGオブジェクトのようなオブジェクトを使用する必要があります。

于 2011-02-03T00:17:41.323 に答える
15

これは、(マルチレベルの)循環依存関係が検出されないまま存在する大規模なプロジェクトではかなりの欠点だと思います。ただし、madgeを使用すると、循環依存のリストを出力してそれらにアプローチできます。

madge --circular --format amd /path/src
于 2013-10-19T18:10:19.960 に答える
9

依存関係を最初にロードする必要がない場合(たとえば、クラスを拡張する場合)、これが実行できることです:(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();
            }
        };

    });

OPの例では、次のように変更されます。

    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");
    });
于 2013-10-22T23:46:13.000 に答える
7

循環依存に関するドキュメントを見ました:http://requirejs.org/docs/api.html#circular

aとbの循環依存関係がある場合は、モジュール内にrequireをモジュール内の依存関係として次のように追加するように指示されます。

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

次に、「a」が必要な場合は、次のように「a」を呼び出します。

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

これは私のために働いた

于 2015-06-25T21:17:24.200 に答える
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);

この問題を回避し、循環依存を維持しようとするのは良い考えではないと思います。一般的な悪い習慣のように感じます。この場合、エクスポートされた関数が呼び出されるときにこれらのモジュールが本当に必要になるため、機能します。しかし、モジュールが必要であり、実際の定義関数自体で使用されている場合を想像してみてください。回避策はありません。これがおそらく、require.jsが定義関数の依存関係の循環依存関係の検出で高速に失敗する理由です。

本当に回避策を追加する必要がある場合、IMOのよりクリーンな方法は、(この場合はエクスポートされた関数で)ちょうどいいタイミングで依存関係を要求することです。そうすれば、定義関数は正常に実行されます。しかし、よりクリーンなIMOは、循環依存を完全に回避することです。これは、あなたの場合は非常に簡単です。

于 2014-08-06T21:09:23.727 に答える
5

投稿されたすべての回答(https://stackoverflow.com/a/25170248/14731を除く)は間違っています。公式文書(2014年11月現在)でさえ間違っています。

私のために働いた唯一の解決策は、「ゲートキーパー」ファイルを宣言し、循環依存に依存するメソッドを定義させることです。具体的な例については、https://stackoverflow.com/a/26809254/14731を参照してください。


上記の解決策が機能しない理由はここにあります。

  1. それはいけません:
var a;
require(['A'], function( A ){
     a = new A();
});

を使用するaコードブロックの前にこのコードブロックが実行される保証はないため、後で使用しますa。(このソリューションは90%の確率で機能するため、誤解を招く可能性があります)

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

    });

これで、これらのモジュールAとBをモジュールCで使用できます。

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

    });
于 2014-11-27T20:48:31.990 に答える
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、依存関係は部外者にとってさらに明白になるはずです。

于 2018-09-05T08:34:37.803 に答える