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. если это не ваш реальный опыт, предоставьте нам реальный минимальный, полный и проверенный пример