Циклические зависимости в модулях с использованием requireJs

Читая документацию requireJs,
для исправления циклических зависимостей предлагается использовать exports для создания пустого объекта для модуля, который сразу же доступен для ссылки другими модулями.

Я пробую этот код, но, похоже, он не работает. Что не так?

P.S.:
прочитайте комментарии, чтобы увидеть вывод,
особенно модуль B внутри вызова setTimeout.


// A module
define([
    'b'
], function (b) {
    console.log('B:', b); // B, Object
    var A = {
        boo: 1
    };

    return A;
});

// B module
define([
    'a',
    'exports'
], function (a, exports) {
    console.log('A:', a); // A, undefined (as I was expecting)
    exports.A = function () {
        return a;
    }

    var B = {
        bar: 1
    };

    setTimeout(function () {
        console.log('exports.A', exports.A()); // exports.A undefined 
                                           // I would like to access the A object
                                           // which is defined in A module
    }, 500);

    return B;
});

// main.js

(function () {

    define([
        'a'
    ], function () {
    });
}());

person Lorraine Bernard    schedule 29.06.2012    source источник
comment
Я думаю, что это может быть полезно stackoverflow.com/questions/4771025/   -  person Jake    schedule 23.07.2012
comment
@ lorraine-bernand Ты понял, как это решить? Ссылка выше не дает мне достаточно ручек, чтобы решить эту проблему.   -  person donnut    schedule 28.09.2012
comment
Я хочу, чтобы это был ответ на вопрос. Я постоянно с этим сталкиваюсь :)   -  person SimplGy    schedule 16.07.2013
comment
возможный дубликат Как обрабатывать циклические зависимости с помощью RequireJS/AMD?   -  person tne    schedule 04.12.2013


Ответы (3)


Вы должны иметь возможность использовать синхронную версию require() в вашем модуле B для доступа к модулю "A":

// B module
define([
    'a',
    'exports'
], function (a, exports) {
    console.log('A:', a); // A, undefined (as I was expecting)
    exports.A = function () {
        return require('a');
    }
    ...
});
person Stepan Riha    schedule 24.04.2014

У меня часто возникают циклические проблемы с использованием модулей AMD для создания ядра приложения, которое поддерживает множество модулей и содержит конфигурацию или другие полезные объекты для использования этими модулями.

Я провел несколько экспериментов сегодня, и это, кажется, работает очень хорошо.

define(['exports', 'underscore', './config', './mediator'],
  function (exports, _, Backbone, config, Mediator){

    Core = /* ... */

    // Publicize a core 'singleton' so that it's dependencies can access it, and so can modules that define it as a dependency themselves.
    core = new Core()
    exports.core = core //publicize it in a way that supports circularity
    return core // And also publicize it normally
  }
)

Оба объекта '===' равны друг другу, так что это кажется очень многообещающим.

РЕДАКТИРОВАТЬ:

Вышеупомянутый метод не работает при оптимизации. Вот еще один метод, который может (не проверено): https://github.com/requirejs/example-multipage/blob/master/www/js/app/main1.js#L2

define(function (require) {
  var $ = require('jquery'),
      lib = require('./lib'),
      Core;

   Core = /* ... */

   return new Core()
});
person SimplGy    schedule 16.07.2013
comment
На самом деле, я бы не стал использовать этот метод. Он работает для неоптимизированного кода, но если вы запустите на нем оптимизатор, ваш экспорт будет недоступен, похоже. - person SimplGy; 17.07.2013
comment
Я сам сейчас занимаюсь этой проблемой циклических зависимостей, поэтому мне интересно, есть ли у вас предложенная альтернатива - person Dexygen; 18.07.2013
comment
К сожалению нет. Это была проблема в течение года или около того, я использовал RequireJS и CurlJS для управления модулями приложений. Мое «решение» состоит в том, чтобы передать ссылку на объект посредника на все, от чего зависит посредник, и начать новую цепочку зависимостей на уровне «страницы». Это выглядит примерно так: github.com/SimpleAsCouldBe/ appCore/blob/master/shared/appCore/ Это не очень хорошо. Я хочу иметь возможность всегда получать свои зависимости через объявление :( - person SimplGy; 18.07.2013
comment
посмотрю спасибо. Сегодня утром, ожидая выхода на работу, я написал в блог обо всем, что, по моему мнению, не так с require.js — если бы не контракт с магазином, который уже использует его, я бы никогда не сделал этого добровольно: codrspace.com/dexygen/ - person Dexygen; 18.07.2013
comment
Я действительно люблю модули AMD. Они намного лучше любой альтернативы, которую я нашел. Мой текущий проект управляет 135 модулями .coffee и 72 шаблонами .hbs с использованием requireJS. Круговая проблема реальна и влияет на организацию, но проблема DRY, о которой упоминается в вашем посте, кажется небольшой и своего рода особенностью. Приятно иметь контроль над переименованием зависимости между именем файла и именем переменной. - person SimplGy; 18.07.2013

Одним из вариантов может быть возврат не самого модуля, а функции, которая создает экземпляр модуля (в этом примере это будет конструктор, как определено в машинописном тексте, внизу находится сгенерированный код js — обратите внимание, что интерфейсы не генерировать код .js)

  • Файл IA.ts

    /// <reference path="IB.ts" />
    interface IA{
        funcA();
        _classB : IB;
    }
    
  • Файл IB.ts

    /// <reference path="IA.ts" />
    interface IB{
        funcB();
        _classA : IA;
    }
    
  • Файл ClassA.ts

    /// <reference path="IA.ts" />
    /// <reference path="IB.ts" />
    
    export class ClassA implements IA
    {
        _classB : IB = null;
    
        constructor(classB : IB)
        {
            this._classB = classB;
            if (classB){
                this._classB._classA = this;
            }
            return this;
        }
    
        funcA(){
            console.log('I am ClassA');
        }
    }
    
  • Файл ClassB.ts

    /// <reference path="IA.ts" />
    /// <reference path="IB.ts" />
    export class ClassB implements IB
    {
        _classA : IA = null;
        constructor(classA : IA)
        {
            this._classA = classA;
            if (classA){
                this._classA._classB = this;
            }
            return this;
        }
        funcB(){
            console.log('I am ClassB');
        }
    }
    
  • Файл MainTest.ts

    /// <reference path="../../def/require.d.ts" />
    /// <reference path="IA.ts" />
    /// <reference path="IB.ts" />
    define(['ClassA', 'ClassB'],
        function (classA, classB)
        {
            var aa : IA = new classA.ClassA();
            var bb : IB = new classB.ClassB(aa);
    
            bb.funcB();
            aa._classB.funcB();
            bb._classA.funcA();
            aa.funcA();
        });
    

И сгенерированный код js:

  • Файл ClassA.js

    define(["require", "exports"], function(require, exports) {
        var ClassA = (function () {
            function ClassA(classB) {
                this._classB = null;
                this._classB = classB;
                if (classB) {
                    this._classB._classA = this;
                }
                return this;
            }
            ClassA.prototype.funcA = function () {
                console.log('I am ClassA');
            };
            return ClassA;
        })();
        exports.ClassA = ClassA;
    });
    
  • Файл ClassB.js

    define(["require", "exports"], function(require, exports) {
        var ClassB = (function () {
            function ClassB(classA) {
                this._classA = null;
                this._classA = classA;
                if (classA) {
                    this._classA._classB = this;
                }
                return this;
            }
            ClassB.prototype.funcB = function () {
                console.log('I am ClassB');
            };
            return ClassB;
        })();
        exports.ClassB = ClassB;
    });
    
  • Файл MainTest.js

    define(['ClassA', 'ClassB'], function (classA, classB) {
    
        var aa = new classA.ClassA();
        var bb = new classB.ClassB(aa);
    
        bb.funcB();
        aa._classB.funcB();
        bb._classA.funcA();
        aa.funcA();
    
    });
    

наконец, вывод будет:

я класс B

я класс B

я класс А

я класс А

person Leo    schedule 21.03.2014