Как определить, поддерживает ли браузер загрузку модуля dynamici c ES6? - PullRequest
2 голосов
/ 20 февраля 2020

Фон

Спецификация JavaScript ES6 поддерживает импорт модулей aka ES6 модулей .

Stati c Импорт довольно очевиден использовать и уже есть довольно хорошая поддержка браузера , но динамический c импорт по-прежнему отстает .
Так что вполне возможно, что ваш код использует stati c модули (когда они не поддерживаются, код даже не будет выполняться), но браузер может пропустить поддержку dynamici c импорта. Таким образом, определение того, работает ли загрузка Dynami c (перед попыткой фактически загрузить код), может быть полезным. И , поскольку браузер не одобряет обнаружение , конечно, я хотел бы использовать функцию обнаружения.

Сценарии использования могут состоять в том, чтобы показать ошибку, откат к некоторым другим данным / стандартному / загрузочному алгоритму Предоставьте разработчикам преимущества динамической загрузки c (данных, например, в стиле ленивого режима) в модуль, в то же время позволяя отступить от прямой передачи данных и т. д. c. По сути, все обычные случаи использования, в которых может использоваться обнаружение функций.

Проблема

Теперь, когда модули Dynami c импортируются с import('/modules/my-module.js'), очевидно, что будет просто пытаться определить, есть такая функция:

// this code does NOT work
if (import) {
    console.log("dynamic import supported")
}

Полагаю, для каждой (?) другой функции это будет работать, но проблема, по-видимому, такова: как import, соответственно, было a зарезервированное ключевое слово в ECMAScript , и теперь, очевидно, также используется для указания импорта stati c, это не настоящая функция. Как говорит MDN, это «функционально».

Tries

import() приводит к синтаксической ошибке, поэтому это не очень удобно, а import("") приводит к обещанию, которое отклоняет, что может быть полезно, но выглядит действительно хакерским / как обходной путь. Кроме того, он требует наличия асинхронного c контекста (await et c.) Только для определения возможностей, что не очень хорошо.
typeeof import также не работает напрямую, вызывая синтаксическую ошибку из-за Ключевое слово («неожиданный токен: ключевое слово« импорт »»).

Итак, каков наилучший способ надежного определения функций, который браузер поддерживает dynamici c ES6 модули ?

Редактировать: Поскольку я вижу некоторые ответы, обратите внимание, что решение, конечно, должно быть максимально удобным для использования, например, CSP может помешать использование eval и в PWA вы не должны предполагать, что вы всегда онлайн, поэтому простая попытка запроса какого-либо файла с ошибкой может привести к неверным результатам.

Ответы [ 4 ]

2 голосов
/ 29 февраля 2020

Следующий код обнаруживает динамическую c поддержку импорта без ложных срабатываний. Функция фактически загружает действительный модуль из данных URI (так что он работает даже в автономном режиме).

Функция hasDynamicImport возвращает Promise, следовательно, она требует поддержки Promise как в нативном, так и в полилифицированном виде. С другой стороны, динамический импорт c возвращает обещание. Следовательно, нет смысла проверять динамическую поддержку импорта c, если Promise не поддерживается.

function hasDynamicImport() {
  try {
    return new Function("return import('data:text/javascript;base64,Cg==').then(r => true)")();
  } catch(e) {
    return Promise.resolve(false);
  }
}

hasDynamicImport()
  .then((support) => console.log('Dynamic import is ' + (support ? '' : 'not ') + 'supported'))
  • Преимущество - Нет ложных срабатываний
  • Недостаток - Использует eval

Это было проверено на последних Chrome, Chrome 62 и IE 11 (с многозаполненным Обещанием).

1 голос
/ 20 февраля 2020

На ум приходят три способа, все полагаются на синтаксическую ошибку, используя import():

  • В eval (но сталкивается с некоторыми CSP)
  • Вставка script тег
  • Использование нескольких тегов c script

Общие биты

У вас есть import() использование foo или что-то подобное. Это недопустимый спецификатор модуля, если он отсутствует в вашей карте импорта, поэтому не должен вызывать сетевой запрос. Используйте обработчик catch, чтобы перехватить ошибку загрузки, и try / catch вокруг "вызова" import(), чтобы просто перехватить любые синхронные ошибки, касающиеся спецификатора модуля, чтобы не загромождать консоль ошибок. Обратите внимание, что в браузерах, которые его не поддерживают, я не думаю, что вы можете избежать синтаксической ошибки в консоли (по крайней мере, window.onerror не для меня в Legacy Edge).

С eval

... поскольку eval не обязательно является злом; Например, если гарантировано для использования вашего собственного контента (но, опять же, CSP могут ограничить):

let supported = false;
try {
    eval("try { import('foo').catch(() => {}); } catch (e) { }");
    supported = true;
} catch (e) {
}
document.body.insertAdjacentHTML("beforeend", `Supported: ${supported}`);

Вставка script

let supported = false;
const script = document.createElement("script");
script.textContent = "try { import('foo').catch(() => { }); } catch (e) { } supported = true;";
document.body.appendChild(script);
document.body.insertAdjacentHTML("beforeend", `Supported: ${supported}`);

Использование нескольких тегов c script

<script>
let supported = false;
</script>
<script>
try {
    import("foo").catch(() => {});
} catch (e) {
}
supported = true;
</script>
<script>
document.body.insertAdjacentHTML("beforeend", `Supported: ${supported}`);
</script>

Все они молча сообщают true о Chrome, Chromium Edge, Firefox, et c .; и false в Legacy Edge (с синтаксической ошибкой).

0 голосов
/ 02 апреля 2020

Как насчет загрузки вашего JS внутри скрипта type = 'module', в противном случае загрузите следующий скрипт с атрибутом nomodule, например так:

<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>

Браузеры, понимающие type = "module", игнорируют сценарии с атрибутом nomodule.

Ссылка: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import & https://v8.dev/features/modules#browser

0 голосов
/ 20 февраля 2020

В ходе дополнительных исследований я обнаружил этот сценарий , с этим важным элементом JS для динамического обнаружения c:

function supportsDynamicImport() {
  try {
    new Function('import("")');
    return true;
  } catch (err) {
    return false;
  }
}
document.body.textContent = `supports dynamic loading: ${supportsDynamicImport()}`;

Вся заслуга в этом идет на @ ebidel от GitHub!

В любом случае, это имеет две проблемы:

  • it использует eval , , что является злом , особенно для сайтов, которые используют CSP .
  • согласно комментариям в Суть в том, что он имеет ложных срабатываний в (некоторые версии) Chrome и Edge. (т.е. он возвращает true хотя эти браузеры на самом деле его не поддерживают)
...