Подтвердить что ты не робот

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 был оценен

и т.д., предполагая, что наш переносимый пакет содержит много данных. В общем, мне просто очень сложно иметь хороший вариант для асинхронной передачи всех пакетов динамически, но все равно придется загружать этот самый пакет в режиме полной синхронизации/блокировки.

4b9b3361

Ответ 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), поэтому вы бы не хотели использовать его в этом сценарии.