yandex / mapsapi-modules

Async modular system
132 stars 29 forks source link

Динамическая загрузка модуля. #54

Closed lilliputten closed 8 years ago

lilliputten commented 8 years ago

Использую ym в составе bem/enb/i-bem для клиентского кода блоков. При попытке загружать файлы модулей динамически (через js document.createElement('script')... вместо статического <script src="..."></script>) происходит сбой и до вызова decl.fn.apply дело не доходит.

Насколько сейчас понимаю, дело в том, что modules.define определяется после отработки jQuery.ready (где происходит всё это шаманство с nextTick, requireDeps, startDeclResolving и пр.

Сейчас удалось добиться запуска модуля вызовом modules.require(...) после загрузки файла с кодом.

Насколько это нормально? Как лучше решать вопрос?

Описание проблемы на форуме bem.info

dfilatov commented 8 years ago

modules.define ничего не знает и никак не связан с jquery.ready.

Можно собрать минимальный пример, иллюстрирующий проблему?

tadatuta commented 8 years ago

Ответил в https://github.com/bem/bem-forum-content-ru/issues/1094#issuecomment-238626391

lilliputten commented 8 years ago

@dfilatov, да, define только сохраняет переданные ему данные в modulesStorage, а вот инициализируется всё уже, наск. понял, по пинку от jQuery.complete.

Попробовал собрать пример, насколько возможно, более близко повторяющий реальную ситуацию:

Архив ~55К: test.zip

Если раскомментировать строчку <script id="test1-browser-script" src="test.browser.js"></script>, то "динамический" скрипт не будет загружаться. Этот же будет загружен и кто-то (видимо, всё же i-bem?) выполнит инициализацию блока: т.е., второй фрагмент безо всяких предварительных вызовов require (оно тоже не будет выполняться, т.к. динамическая загрузка не производилась) покажет блок, который будет проинициализирован.

ФАЙЛЫ

ОПИСАНИЕ

Код в test1.js пытается создать что-то похожее на то, что происходит в приложении:

Перед тем, как создавать блок, смотрим в конфиг, где нам говорят, что надо загрузить пакеты с шаблонами и клиентским кодом. Грузим, по коллбеку вызваем код, создающий блок.

В жизни всё немножко не так, но в целом вроде картина правильная. Самое главное, что эти куски находятся совсем в разных местах и не очень знают друг о друге -- специально не стал завязывать второй фрагмент на завершение загрузки для наглядности (таймаут гарантирует выполнение после загрузки).

Т.е., если не делать modules.require(['test1'], ... после загрузки модуля, то ничего не сработает. Сейчас это кажется уже более логичным.

ИТОГ

Видимо, придётся делать в пакете с модулями (их там, по идее много -- всё, что нужно для отрисовываемого элемента интерфейса) "оглавление" и так или иначе по завершении загрузки вызывать для них require.

Наверное, это естественное поведение и вариантов нету?

qfox commented 8 years ago

@dfilatov Дим, а что в итоге с Promise.resolve? Хотелось бы, чтобы YM сама это делала.

dfilatov commented 8 years ago

@zxqfox про Promise.resolve это вообще про что? В ym нет никаких промисов.

dfilatov commented 8 years ago

@lilliputten я правильно понимаю что от test1 никакой другой модуль в подгружаемом бандле не зависит?

qfox commented 8 years ago

@dfilatov Про export default new Promise(); и чтоб на том конце в import не надо было всё это заворачивать в import x from 'x'; Promise.resolve(x).then((x) => { ... });

dfilatov commented 8 years ago

@zxqfox Я чего-то совсем перестал понимать, как модули из es2015 связаны с этим issue?

qfox commented 8 years ago

@dfilatov Так. Еще раз с начала. Человек пришел с проблемой: у него модуль грузит другие модули асинхронно. Реализация, которую ему предложил @tadatuta, на промисах. Я вспомнил, что были мысли поддержать это на уровне ym.

modules.define('x', function(provide) {
  provide(Promise.resolve(42));
});
modules.require(['x'], function(x) {
  console.log(x); // 42
});

И решил спросить).

Модули из es2015 можно без проблем перегонять в любой модульный формат, и мне так показалось короче. Зря показалось).

dfilatov commented 8 years ago

@zxqfox Непонятно, зачем это делать (поддерживать промисы в модульной системе).

Вместо:

provide(Promise.resolve(42));

Напиши:

Promise.resolve(42).then(provide);

и никакой специальной поддержки от модульной системы не нужно.

qfox commented 8 years ago

@dfilatov потому что это логично возвращать значение, модульная система этим и занимается.

dfilatov commented 8 years ago

@zxqfox Не вижу ничего логичного в том, чтобы вводить поддержку дополнительной сущности, когда и без нее можно обойтись, причем с точностью до синтаксиса. Мне как раз импонирует математический подход, когда ты сводишь решение новой задачи к решению предыдущей, а не придумываешь каждый раз новый способ.

qfox commented 8 years ago

@dfilatov Ну подожди, тогда почему не вернуться на шаг назад и не сделать более совместимую с AMD модульную систему. Ведь можно делать return new Promise() и не надо будет прокидывать provide аргументом в тело. Это, конечно, не решит вопросов доопределения через аргумент.

Впрочем, это совсем оффтоп. Давай в другой раз.

dfilatov commented 8 years ago

@zxqfox Ну так оно же не сводится )

lilliputten commented 8 years ago

@dfilatov

я правильно понимаю что от test1 никакой другой модуль в подгружаемом бандле не зависит?

В общем случае неизвестно и скорее всего там будут и внутренние зависимости. Это может как-то повлиять?

Если я правильно понял, для моей ситуации решением получается реквайринг 'i-bem__dom_init' после загрузки пакета и определения модулей в нём?

Сейчас буду пробовать экспериментировать уже в рабочем проекте. Хотя в целом вроде бы это уже к самому YM отношения не имеет, -- т.е., слегка оффтоп получился.