Как определить, когда динамический модуль и все его зависимости были загружены?

Webpack поддерживает синтаксис import(), который соответствует предложение ECMAScript для динамического импорта. Этот синтаксис использует обещания для асинхронной загрузки модулей.

Проблема в том, что обещание выполняется, как только загружается конкретный модуль, не дожидаясь загрузки зависимостей модуля (это может быть любой тип ресурса, включая JS и CSS).

Пример кода:

import('./myModule.js').then(myModule => {
    myModule.sayHello(); // This will be called before someCSS.css has been loaded
});

myModule.js

import './someCSS.css'; // <-- I need to know when this is loaded (there can be more than one asset)

export default class myModule {
    sayHello() {
        alert('Hello!');
    }
}

Как я могу определить, когда модуль и все связанные с ним активы были загружены? Что-то вроде события onload для асинхронных активов?


person Yoav Kadosh    schedule 29.08.2017    source источник


Ответы (2)


Метод возвращает Promise, что позволяет определить, был ли загружен скрипт или произошла ошибка при загрузке (например):

// utils.js
function insertJs({src, isModule, async, defer}) {
    const script = document.createElement('script');

    if(isModule){
      script.type = 'module';
    } else{
      script.type = 'application/javascript';
    }
    if(async){
      script.setAttribute('async', '');
    }
    if(defer){
      script.setAttribute('defer', '');
    }

    document.head.appendChild(script);

    return new Promise((success, error) => {
        script.onload = success;
        script.onerror = error;
        script.src = src;// start loading the script
    });
}

export {insertJs};

//An example of its use:

import {insertJs} from './utils.js'

// The inserted node will be:
// <script type="module" src="js/module-to-be-inserted.js"></script>
const src = './module-to-be-inserted.js';

insertJs({
  src,
  isModule: true,
  async: true
})
    .then(
        () => {
            alert(`Script "${src}" is successfully executed`);
        },
        (err) => {
            alert(`An error occured during the script "${src}" loading: ${err}`);
        }
    );
// module-to-be-inserted.js
alert('I\'m executed');
person Aliaksandr Pitkevich    schedule 29.08.2017
comment
Промис разрешается при загрузке скрипта, независимо от его зависимостей. Смотрите мое обновление для получения дополнительной информации. - person Yoav Kadosh; 29.08.2017
comment
@YoavKadosh Тогда у вас проблемы с дизайном. Это зависимости дочернего модуля. Родительский модуль не должен полагаться на них. Если ему нужны эти зависимости, родительский модуль также должен их импортировать. - person Estus Flask; 29.08.2017
comment
В родительском модуле они не нужны. Мне нужно знать, когда они будут доступны, чтобы скрыть счетчик. - person Yoav Kadosh; 29.08.2017

Можно использовать document.styleSheets для проверки загрузки всех таблиц стилей. Объект CSSStyleSheet будет содержать свойство cssRules только после загрузки таблицы стилей, поэтому вы можете создать обещание, которое проверяет это:

export function awaitStylesheets() {
    let interval;
    return new Promise(resolve => {
        interval = setInterval(() => {
            for (let i = 0; i < document.styleSheets.length; i++) {
                // A stylesheet is loaded when its object has a 'cssRules' property
                if (typeof document.styleSheets[i].cssRules === 'undefined') {
                    return;
                }
            }

            // Only reached when all stylesheets have been loaded
            clearInterval(interval);
            resolve();
        }, 10);
    });
}
person Yoav Kadosh    schedule 30.08.2017