Require.ensure() неблокирующий
Если у нас есть разные пакеты, созданные webpack, и мы require.ensure
что-то динамически передаем + eval в более поздний момент времени, это происходит через jsonPadding и некоторую магию webpack js. Если мы имеем
require.ensure([ ], ( require ) => {
console.log('before...');
var data = require( './myModule.js' );
console.log('after...');
}, 'myModule')
"after..."
встретится, когда этот модуль будет полностью перенесен и оценен. Если это случается так, что этот кусок/модуль довольно большой, он содержит изображения, css и еще что-то еще, загрузка в значительной степени блокирует браузер, в то время как код javascript webpack распаковывает пакет со всеми его компонентами.
Вопрос: Есть ли способ "подключиться" к этой магии require
? Например, было бы сценарием мечты иметь обратные вызовы для:
- весь файл/фрагмент передан
- Изображение [1] было оценено
- css [1] была оценена/добавлен тег стиля
- javascript был оценен
и т.д., предполагая, что наш переносимый пакет содержит много данных. В общем, мне просто очень сложно иметь хороший вариант для асинхронной передачи всех пакетов динамически, но все равно придется загружать этот самый пакет в режиме полной синхронизации/блокировки.
Ответы
Ответ 1
Наверное, я сам был смущен этой темой, поэтому мой вопрос был, вероятно, недостаточно точным, чтобы получить правильный ответ. Тем не менее, мое недоразумение в целом "загрузку динамического модуля" commonJS "заключалось в том, что require.ensure()
просто передаст код модуля (соответственно, кусок, который создал веб-пакет) по проводу. После этого переданный Chunk, который в основном представляет собой только один большой файл ECMAscript, просто находится в браузере, кэшируется, но пока не оценивается. Оценка всего фрагмента происходит только при фактическом вызове require()
.
Сказав это, полностью в ваших руках, как вы отделяете и оцениваете отдельные части модуля/куска. Если, например, как в моем первоначальном вопросе, модуль requires()
в некоторых файлах CSS, некоторые изображения и некоторые HTML, которые все асинхронно передаются по вызову require.ensure()
. Каким образом вы require()
(и, следовательно, оцениваете), эти части полностью зависят от вас, и вы можете отделить их, если необходимо, самостоятельно.
Например, модуль выглядит следующим образом:
Module1.js
"use strict";
import { io } from 'socket.io-client';
document.getElementById( 'foo' ).addEventListener('click', ( event ) => {
let partCSS = require( 'style/usable!./someCoolCSS.css' ),
moarCSS = require( 'style/usable!./moarCoolCSS.css' ),
tmpl = require( './myTemplate.html' ),
image1 = require( './foo.jpg' ),
image2 = require( './bar.png' );
}, false);
Конечно, все эти файлы уже содержатся в Chunk, который передается клиенту, когда какой-то другой модуль вызывает:
require.ensure([ 'Module1.js' ], ( require ) => {
}, 'Module1');
Это была моя путаница. Итак, теперь мы можем просто играть с вызовами require()
внутри module1.js
. Если нам действительно требуется много файлов таким образом, мы могли бы даже использовать таймер setTimeout
/setImmediate
для развязки синхронной оценки между каждым вызовом require()
, если это необходимо или требуется.
На самом деле длинный ответ для довольно простой истории.
TL; ДР:
"require.ensure
передает весь фрагмент по проводу. Этот фрагмент содержит все файлы, которые являются частью вызова require()
в пределах гарантированного модуля. Но эти файлы автоматически оценивают не. Это происходит только когда фактический вызов require()
сопоставляется во время выполнения (который представлен вызовом webpackJSONP в этой точке)"
Ответ 2
Позвольте мне предисловие, сказав, что я знаю, что это может быть "раздражающим" ответом, потому что он не отвечает на ваш вопрос напрямую, но предлагает альтернативное, прагматичное решение проблемы висячего браузера. я сам использовал этот шаблон для управления загрузкой активов в контексте тяжелая 3D-игра в Интернете.
Я пишу это как ответ, а не как комментарий, чтобы он мог служить другие, которые сталкиваются с одной и той же проблемой. Если это отвечает на ваши случай, я буду рад предоставить реальный код для реализации и создания эти типы модулей.
Если я правильно понимаю, по сути, вы хотите разбить MyModule
на дискретные компоненты, которые могут быть атомарно загружены и оценены в контексте одного require.ensure
, но обрабатывают оценку, чтобы не все оценивалось в один идет в результате зависания браузера.
Другой способ взглянуть на это - использовать сами методы require
и ensure
как механизмы загрузки/оценки. Рассмотрим MyModule.js
, который представляет собой модуль с огромной загрузкой с зависимостями Css1, Css2, ... CssN
, а также JS1, JS2, ... JSN
и изображениями.
Мое предложение состоит в том, чтобы разбить его на SuperMyModule.js
, для которого требуется MyModuleLogic.js
, а также все CSS, изображения и JS.
Node, в SuperMyModule.js
вы можете сделать:
let myModuleLogic = require("myModuleLogic");
console.log('JS was evaluated');
require.ensure(['image1.png'], ( require ) => {
let data = require( './images1.png' );
console.log('image[1] was evaluated');
// register that resource was evaluated/fire event
})
require.ensure(['style1.css'], ( require ) => {
let data = require( './style1.css' );
console.log('css[1] was evaluated');
// register that resource was evaluated/fire event
})
//after all resources evaluated/fire callback or event
Затем в исходном файле, как вы просили:
require.ensure([ ], ( require ) => {
console.log('before...');
let myModule = require( './superMyModule.js' );
console.log('after...');
})
И если вы настроите экземпляр вашего модуля как эмитента события, возможно, подключите к загрузке таких ресурсов:
require.ensure([ ], ( require ) => {
let myModule = require( './superMyModule.js' );
myModule.on("loadResource", myCallback)
})
Ответ 3
Вы можете разгрузить основной поток и избежать блокировки с помощью рабочего загрузчика.
Нижняя сторона: есть дополнительное сообщение, проходящее между главным окном и рабочим.
https://github.com/webpack/worker-loader
Вы также можете попробовать emmitting load events в большом модуле, чтобы отслеживать более подробный прогресс.
Дальнейшая ссылка:
Ответ 4
Если вы хотите начать загрузку асинхронного пакета JavaScript через require.ensure
, а также другие Promises, вот как вы можете это сделать:
const requireEnsurePromise = new Promise((resolve) => {
require.ensure(['./modulePath'], function (requireEnsure) {
console.log('The module is fetched but not evaluated yet');
resolve(requireEnsure.bind(null, require.resolve('./modulePath')));
});
});
Promise.all([
fetch('/api/relevant/stuff').then(response => response.json()),
requireEnsurePromise,
]).then((values) => {
if (values[0]) {
// DO STUFF
}
console.log('right before module is evaluated');
const evaluatedModule = values[1]();
});
Webpack статически определил, что путь к модулю соответствует внутреннему представлению Webpack (может быть целым числом или строкой). Всякий раз, когда Webpack распознает паттен require.ensure([], fn)
, он смотрит на тело функции обратного вызова fn
и делает это. Чтобы отложить время оценки после извлечения пакета JavaScript в режиме Promise, require('./modulePath')
не может присутствовать внутри обратного вызова успеха require.ensure
, поскольку он будет оценивать модуль. Webpack переводит require('./modulePath')
на что-то вроде __webpack_require__(2343423)
, поэтому вы бы не хотели использовать его в этом сценарии.