RequireJS - не может получить доступ к функции внешнего модуля

У меня проблема с RequireJS. По сути, я не могу получить доступ к функции, определенной внутри другого файла, из другого.

Мне нужно сделать это, потому что я хочу экспортировать данный поднабор функций, например

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

    let myFunction1 = function(){ return "Hello"; }
    let myFunction2 = function(){ return " From"; }
    let myFunction3 = function(){ return " Submodule!"; }

    return {
             myFunction1 : myFunction1,
             myFunction2 : myFunction2,
             myFunction3 : myFunction3,
    };

});

И доступ к ним из другого файла

define('main', ['config', 'sub1', 'sub2', 'submodule'],   
        function(config, sub1, sub2, submodule) {

  //Config
  alert(config.conf);

  //Submodule
  let callSubmodule = function() {
    alert(submodule.myFunction1() + 
          submodule.myFunction2() + 
          submodule.myFunction3());
  }

  //sub1
  let callSub1 = function() {
    alert(sub1.myFunction1());
  }

  //sub2
  let callSub2 = function() {
    alert(sub2.myFunction1());
  }

});

Дело в том, что обычно я могу сделать это с помощью sub1 и sub2, но с подмодулем я просто не могу. Я думаю, что это почему-то вызвано зависимостями в require.config.js.

Мой require.config.js:

require(['common'], function () { //contains vendors
    require(['config'], function () { //contains a js config file
        require(['main'], function () { //main file
            require(['sub1', 'sub2'], function () { //some subfiles
                require(['submodule']);
            });
        });
    });
});

Для submodule.myFunction1() и двух связанных функций, которые я получаю:

Неподготовлено (в обещании) TypeError: Невозможно прочитать свойство myFunction1 из undefined

Это странно, так как я могу это сделать в других ситуациях, и я действительно не могу понять, почему это происходит. Например, я могу вызвать функции sub1 и sub2 из основного и других файлов, но не в подмодуль в частности.

Index.html

//Taken from Plunker
. . .
<script data-main="common"  data-require="[email protected]" data-semver="2.1.20" src="http://requirejs.org/docs/release/2.1.20/minified/require.js"></script>
<script src="require.config.js"></script>

. . .
<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>

common.js содержит поставщиков, вот только пример

requirejs.config({
   baseUrl : "",
    paths : {
             "jquery" : "http://code.jquery.com/jquery-latest.min.js"
    }
});

sub1.js

define('sub1', ['submodule'], function(submodule) {

    let myFunction1 = function(){ return "called sub1"; }

    return {
             myFunction1 : myFunction1
    };

});

sub2.js

define('sub2', ['submodule'], function(submodule) {

    let myFunction1 = function(){ return "called sub2"; }

    return {
             myFunction1 : myFunction1
    };

});

Я установил Plunker с помощью @SergGr help, который пытается реплицировать структуру приложения, но все модули получают undefined при щелчке. На реальном приложении этого не происходит.

Как я могу это решить?

Ответ 1

Я решил решить эту проблему. По сути, это было вызвано циклической зависимостью между модулями. Таким образом, необходимые b и b потребовали, чтобы один из них был undefined по разрешению зависимостей.

Я нашел решение для ответа, предоставленного @jgillich в requirejs модуле, undefined.

Итак, мне удалось решить, используя в основном

define('main', ['config', 'sub1', 'sub2', 'require'],   
    function(config, sub1, sub2, submodule, require) {

  //Config
  alert(config.conf);

  //Submodule
  let callSubmodule = function() {
    alert(require('submodule').myFunction1() + 
          require('submodule').myFunction2() + 
          require('submodule').myFunction3());
  }

});

Как сказал @jgillich:

Если вы определяете круговую зависимость ( "a" требует "b" и "b", то "a" ), тогда в этом случае, когда вызывается функция модуля "b", она получит значение undefined для "а". "b" может получить "a" позже после того, как модули были определены с помощью метода require() (обязательно укажите require как зависимость, чтобы правильный контекст использовался для поиска "a" ):

//Inside b.js:
define(["require", "a"],
    function(require, a) {
        //"a" in this case will be null if "a" also asked for "b",
        //a circular dependency.
        return function(title) {
            return require("a").doSomething();
        }
    }
);

http://requirejs.org/docs/api.html#circular

Ответ 2

Это ваш код:

define('main', ['submodule'], function(submod) {
   console.log(submodule.myFunction());
});

У вас есть submod в списке параметров. Но затем вы пытаетесь получить доступ к submodule. Обратите внимание, что вы возвращаете функцию прямо из своего модуля (return myFunction), поэтому ваш модуль имеет значение функции myFunction, и, таким образом, модуль - это то, что вы должны вызвать. Код должен быть:

define('main', ['submodule'], function(submod) {
   console.log(submod());
});

Ответ 3

Как вы назвали свои модули, я бы ожидал, что все они пришли из требуемого конфигурационного файла. Я бы не ожидал, что requirejs будет знать, как загрузить эти файлы без какого-либо явного процесса компиляции. Я также подозреваю, что ваш сервер что-то возвращает из-за 404, который JS почти способен интерпретировать без взрыва.

Ваша настройка кажется, и схема именования кажется довольно странной. Если у вас есть возможность начать с нуля, мои рекомендации.

Рекомендация:

  • Я замечаю, что вы используете абсолютные пути. Я настоятельно рекомендую использовать относительные пути для всего. Этому есть много причин.
  • Ваша основная информация должна быть тем, что вы называете "require.config.js". Ваш common.js на самом деле является require.config.js.
  • Вы загружаете require.config.js(который является вашим основным) отдельно с помощью тега script. Вы можете сделать это, но это странно.
  • Вы можете использовать синтаксис стиля "commonjs", чтобы требовать файлы без использования массива для определения всех ваших зависимостей. Я рекомендую это.

Это моя рекомендация по настройке:

index.html

<script src="/js/config.js" />
<script src="http://requirejs.org/docs/release/2.1.20/minified/require.js" />
<script>
      require('/js/main', function(main) {
          main({});
      });
</script>

/js/config.js

// setting requirejs to an object before its loaded will cause requirejs to use it as the config
window.requirejs = {
   baseUrl : "/",
    paths : {
        "jquery" : "http://code.jquery.com/jquery-latest.min.js"
    }
};

/js/main.js

define(function(require) {
    const sum = require('./sum');
    return (a, b) => sum(a, b);
});

/js/sum.js

define(function(require) {
    return (a, b) => a + b;
});

Ответ 4

Обновление (02 марта 2017 года)

Ваш плункер, очевидно, не будет работать, потому что у вас есть прямые вызовы от HTML к вашим функциям модуля.

<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>

RequireJS не работает. Одна из ключевых целей RequireJS - обеспечить изоляцию модулей, и поэтому она просто не может работать так: представьте себе, если несколько различных модулей имели функции callSubmodule.

Насколько я знаю, нет способа привязать вызовы от HTML к коду модуля RequireJS, это должно быть наоборот: модуль привязывается к HTML. И если вы исправите эти проблемы, все будет хорошо для меня, как вы можете видеть в этой вилке вашего плунжера.


Старый ответ

Ошибка находится в subModule.js

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

    let myFunction = function(){ return "Hello"; }

    //return myFunction;     // old, wrong
    return { myFunction: myFunction }; 
});

Даже если вы хотите вернуть только одну функцию, вы не должны возвращать ее как есть, вы должны перенести ее в объект и дать ему явное имя.

P.S. если это не ваш реальный опыт, предоставьте нам реальный минимальный, полный и проверенный пример