Как я могу условно импортировать модуль ES6?

194

Мне нужно сделать что-то вроде:

if (condition) {
    import something from 'something';
}
// ...
if (something) {
    something.doStuff();
}

Приведенный выше код не компилируется; это бросает SyntaxError: ... 'import' and 'export' may only appear at the top level.

Я попытался использовать, System.importкак показано здесь , но я не знаю, откуда System. Это предложение ES6, которое не было принято? Ссылка на «программный API» из этой статьи выводит меня на устаревшую страницу документации .

ericsoco
источник
Просто импортируйте это нормально. Ваш модуль нуждается в этом независимо.
Энди
Я не вижу никакой причины, по которой вы бы просто не импортировали независимо от условий. Это не похоже на какие-то накладные расходы. В каком-то сценарии вам нужен файл, поэтому не бывает, чтобы он был полностью пропущен. В этом случае просто импортируйте его безоговорочно.
Спасибо,
8
Мой пример использования: я хочу упростить наличие необязательной зависимости. Если dep не требуется, пользователь удаляет его из package.json; my gulpfilethen проверяет, существует ли эта зависимость, перед выполнением некоторых шагов сборки.
ericsoco
1
Другой вариант использования: для целей тестирования. Я использую webpackи babelдля транспорта ES6 в ES5. Проекты, подобные webpack-rewireи похожие, здесь не помогут - github.com/jhnns/rewire-webpack/issues/12 . Одним из способов установки теста, удваивающего ИЛИ для удаления проблемных зависимостей, может быть условный импорт.
Amio.io
3
+1. Возможность использования модуля в нескольких средах, где зависимости могут работать или не работать, имеет решающее значение, особенно когда модули могут ссылаться на зависимости, которые будут работать только в браузере (например, где webpackиспользуется для преобразования таблиц стилей в модули, которые вставляют соответствующие стили в DOM, когда они импортированы), но модуль также должен работать вне браузера (например, для модульного тестирования).
Жюль

Ответы:

146

У нас есть предложение по динамическому импорту с ECMA. Это на этапе 3. Это также доступно как предустановка babel .

Ниже приведен способ сделать условный рендеринг согласно вашему случаю.

if (condition) {
    import('something')
    .then((something) => {
       console.log(something.something);
    });
}

Это в основном возвращает обещание. Ожидается, что разрешение обещание будет иметь модуль. Предложение также имеет другие функции, такие как множественный динамический импорт, импорт по умолчанию, импорт файлов js и т. Д. Более подробную информацию о динамическом импорте можно найти здесь .

thecodejack
источник
13
Наконец, настоящий ответ ES6! Спасибо @thecodejack. На самом деле на стадии 3 на момент написания этой статьи, в соответствии с этой статьей сейчас.
ericsoco
5
или, если вы только что назвали экспорт, вы можете деструктурировать:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
IVN
4
на Firefox и во время работы npm run buildя все еще получаю ошибку:SyntaxError: ... 'import' and 'export' may only appear at the top level
Ste
1
@stackjlei: эта функция еще не является частью стандарта JavaScript, это всего лишь предложение на этапе 3! Однако это уже реализовано во многих новых браузерах, см. Caniuse.com/#feat=es6-module-dynamic-import .
Конрад
1
Эта функция условного динамического импорта не обладает мелкозернистой способностью импортировать только те элементы, которые имеет «импорт X из Y». Фактически, эта мелкозернистая способность может быть даже более важной в динамической загрузке (в отличие от связывания перед обработкой)
Крейг Хикс,
102

Если вы хотите, вы можете использовать требуют. Это способ создания условного оператора require.

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}
BaptWaels
источник
1
Я думаю, что что-то и другие переменные объявляются с использованием const, который является областью видимости блока, поэтому второе, если условие сгенерирует, что-то не определено
Мохаммед Эссехеми
Было бы лучше использовать let и объявить две переменные вне блока вместо использования 'var' и вообще избегать области видимости блока.
Воркан
Влияет ли подъем на что-либо в этом случае? Я столкнулся с некоторыми проблемами, когда поднятие означало, что я неожиданно импортировал библиотеку, следуя шаблону, близкому к этому, если память служит.
Томас
11
Следует отметить, что require()это не является частью стандартного JavaScript - это встроенная функция в Node.js, поэтому она полезна только в этой среде. ОП не указывает на работу с Node.js.
Velojet
56

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

Ты можешь сделать:

api.js

import mockAPI from './mockAPI'
import realAPI from './realAPI'

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

Я использую это для насмешек над аналитическими библиотеками, такими как mixpanel и т. Д., Потому что в настоящее время у меня не может быть несколько сборок или нашего внешнего интерфейса. Не самый элегантный, но работает. У меня просто есть несколько «если» здесь и там, в зависимости от среды, потому что в случае mixpanel, он нуждается в инициализации.

Кев
источник
40
Это решение приводит к загрузке нежелательных модулей, поэтому я думаю, что это не оптимальное решение.
ismailarilik
5
Как указано в ответе, это обходной путь. В то время решения просто не было. Импорт ES6 не является динамическим, это предусмотрено проектом. Предложение по функции динамического импорта ES6, которое описано в принятом в настоящее время ответе, может сделать это. JS развивается :)
Kev
9

Похоже, ответ в том, что на данный момент вы не можете.

http://exploringjs.com/es6/ch_modules.html#sec_module-loader-api

Я думаю, что цель состоит в том, чтобы включить статический анализ в максимально возможной степени, и условно импортированные модули ломают это. Также стоит упомянуть - я использую Babel , и я предполагаю, что Systemэто не поддерживается Babel, потому что API загрузчика модулей не стал стандартом ES6.

ericsoco
источник
@FelixKling сделай свой ответ, и я с радостью приму его!
ericsoco
4

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

const defaultOne = require('path/to/component').default;
const NamedOne = require('path/to/component').theName;

Для динамического разрешения модулей с полной поддержкой статического анализа, сначала индексируйте модули в индексаторе (index.js) и импортируйте индексатор в хост-модуле.

// index.js
export { default as ModuleOne } from 'path/to/module/one';
export { default as ModuleTwo } from 'path/to/module/two';
export { SomeNamedModule } from 'path/to/named/module';

// host.js
import * as indexer from 'index';
const moduleName = 'ModuleOne';
const Module = require(indexer[moduleName]);
Шоаиб Наваз
источник
7
Следует отметить, что require()это не является частью стандартного JavaScript - это встроенная функция в Node.js, поэтому она полезна только в этой среде. ОП не указывает на работу с Node.js.
Velojet
2

Важное отличие, если вы используете режим динамического импорта Webpack eager:

if (normalCondition) {
  // this will be included to bundle, whether you use it or not
  import(...);
}

if (process.env.SOMETHING === 'true') {
  // this will not be included to bundle, if SOMETHING is not 'true'
  import(...);
}
Соло
источник
Но importвозвращает обещание.
Новый
0

скрывая это в eval у меня получалось, скрывая это от статического анализатора ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}
Крис Марсталл
источник
3
Кто-нибудь может объяснить, почему этот ответ был отклонен? Есть ли какие-то реальные недостатки или это была просто автоматическая негативная реакция на злое ключевое слово «eval»?
Юрий Гор
3
Автоматическое понижение для использования уродливого ключевого слова eval. Держись подальше.
Тормод Хаугене
1
Можете ли вы объяснить, что на самом деле не так с использованием evalздесь, @TormodHaugene?
Адам Барнс
MDN резюмирует немало причин, по которым evalих не следует использовать . В целом: если вы обнаружите необходимость использования eval, вы, вероятно, делаете это неправильно и должны сделать шаг назад, чтобы рассмотреть свои альтернативы. Вероятно, есть некоторые сценарии, в которых использование evalявляется правильным, но вы, скорее всего, не сталкивались ни с одной из этих ситуаций.
Тормод Хаугене
5
Следует отметить, что require()это не является частью стандартного JavaScript - это встроенная функция в Node.js, поэтому она полезна только в этой среде. ОП не указывает на работу с Node.js.
Velojet
0

Я смог добиться этого, используя функцию, которая была вызвана немедленно и требовал утверждения.

const something = (() => (
  condition ? require('something') : null
))();

if(something) {
  something.doStuff();
}
bradley2w1dl
источник
5
Следует отметить, что require()это не является частью стандартного JavaScript - это встроенная функция в Node.js, поэтому она полезна только в этой среде. ОП не указывает на работу с Node.js.
Velojet
0

Условный импорт также может быть достигнут с тройной и require()с:

const logger = DEBUG ? require('dev-logger') : require('logger');

Этот пример взят из глобальных документов ES Lint: https://eslint.org/docs/rules/global-require

Эллиот Леджер
источник
5
Следует отметить, что require()это не является частью стандартного JavaScript - это встроенная функция в Node.js, поэтому она полезна только в этой среде. ОП не указывает на работу с Node.js.
Velojet
0

Нет, ты не можешь!

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

До модулей ES6 у нас были модули CommonJS, которые использовали синтаксис require (). Эти модули были «динамическими», то есть мы могли импортировать новые модули на основе условий в нашем коде. - источник: https://bitsofco.de/what-is-tree-shaking/

Я полагаю, что одной из причин, по которой они отказались от поддержки ES6 и далее, является тот факт, что компиляция будет очень трудной или невозможной.

Aldee
источник