Як я можу умовно імпортувати модуль 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" із цієї статті переносить мене на застарілу сторінку документів .


Просто імпортуйте його нормально. Ваш модуль потребує цього незалежно.
Енді

Я не бачу жодної причини, чому ви не просто імпортували, незалежно від умови. Це не так, як там якісь накладні. У деяких сценаріях вам потрібен файл, так що це не так, як коли-небудь випадок, коли його можна повністю пропустити. У такому випадку просто імпортуйте його беззастережно.
Дякую

8
Мій випадок використання: я хочу полегшити наявність факультативної залежності. Якщо dep не потрібен, користувач видаляє його з package.json; мій gulpfileпотім перевіряє, чи існує така залежність, перш ніж виконувати деякі етапи побудови.
ericsoco

1
Інший випадок використання: для тестування. Я використовую webpackта babelтранспілюю es6 в es5. Такі проекти, як webpack-rewireі подібні, тут не допомагають - github.com/jhnns/rewire-webpack/isissue/12 . Одним із способів встановити тестові подвійні АБО для усунення проблемних залежностей може бути умовний імпорт.
Amio.io

3
+1. Можливість використання модуля в декількох середовищах, де залежності можуть працювати або не працювати, особливо важливо, коли модулі можуть посилатися на залежності, які працюватимуть лише в браузері (наприклад, де webpackвикористовується для перетворення таблиць стилів у модулі, які вставляють відповідні стилі в DOM, коли вони імпортовані), але модуль також повинен працювати поза браузером (наприклад, для тестування одиниць).
Жуль

Відповіді:


144

У нас є динамічна пропозиція про імпорт зараз з ECMA. Це на етапі 3. Це також доступно у вигляді попередньо встановленої ватаги .

Далі - спосіб зробити умовне відображення відповідно до вашого випадку.

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

Це в основному повертає обіцянку. Очікується, що модуль буде мати дозвіл. Пропозиція також має інші функції, такі як багаторазовий динамічний імпорт, імпорт за замовчуванням, імпорт файлів js тощо. Ви можете знайти більше інформації про динамічний імпорт тут .


13
Нарешті, справжня відповідь ES6! Дякуємо @thecodejack. Насправді на етапі 3 стадії написання цієї статті, відповідно до цієї статті.
ericsoco

5
або якщо ви щойно назвали експорт, ви можете знищити:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
18:56

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

Якщо ви хочете, ви можете використовувати вимагати. Це спосіб мати умовне твердження про вимогу.

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}

1
Я думаю, що щось та інші змінні декларуються за допомогою const, який блокується, тому другий, якщо умова кине, що щось не визначено
Mohammed Essehemy

Було б краще використовувати дозволити і оголосити дві змінні за межами блоку, а не використовувати "var" і взагалі уникати області блоку.
Воркан

Чи впливає підіймання на що-небудь у цьому випадку? Я зіткнувся з деякими проблемами, коли підйоми означали, що я ненавмисно імпортував бібліотеку, коли дотримувався шаблону, близького до цього, якщо пам'ять слугує.
Томас

11
Слід зазначити, що require()це не є частиною стандартного JavaScript - це вбудована функція в Node.js, тому корисна лише в цьому середовищі. ОП не вказує на роботу з Node.js.
Велоджет

55

Ви не можете імпортувати умовно, але ви можете зробити навпаки: експортувати щось умовно. Це залежить від вашого випадку використання, тому ця робота навколо може бути не для вас.

Ви можете зробити:

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, і т. Д. ... тому що в даний час я не можу мати декілька збірок або наш інтерфейс. Не найелегантніший, але працює. У мене просто кілька "if" тут і там, залежно від середовища, оскільки у випадку mixpanel вона потребує ініціалізації.


40
Це рішення спричинює завантаження небажаних модулів, тому не оптимальне рішення, я думаю.
ismailarilik

5
Як зазначено у відповіді, це обробка справи. На той час рішення просто не було. Імпорт ES6 не динамічний, це за задумом. Пропозиція ES6 щодо функції динамічного імпорту, описана в прийнятій відповіді, може це зробити. JS розвивається :)
Кев

9

Схоже, відповідь полягає в тому, що, як і зараз, ви не можете.

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

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


@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.
Велоджет

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повертає обіцянку.
newguy

0

затемнення його в eval працювало на мене, ховаючи його від статичного аналізатора ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}

3
Хтось може пояснити, чому цю відповідь було скасовано? Чи є якісь реальні недоліки чи це була лише автоматична негативна реакція на зло ключове слово 'eval'?
Юрій Гор

3
Автоматичне голосування для використання огидного ключового слова eval. Тримайся подалі.
Tormod Haugene

1
Чи можете ви пояснити, що насправді не так у використанні evalтут, @TormodHaugene?
Адам Барнс

MDN підсумовує досить багато причин, чому evalйого не слід використовувати . Загалом: якщо ви виявите потребу в використанні eval, ви, ймовірно, робите це неправильно і вам слід зробити крок назад, щоб розглянути свої альтернативи. Напевно, є певні сценарії, коли використання evalправильне, але ви, швидше за все, не стикалися з жодною з цих ситуацій.
Tormod Haugene

5
Слід зазначити, що require()це не є частиною стандартного JavaScript - це вбудована функція в Node.js, тому корисна лише в цьому середовищі. ОП не вказує на роботу з Node.js.
Велоджет

0

Мені вдалося досягти цього, використовуючи функцію негайного виклику та вимагати заяви.

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

if(something) {
  something.doStuff();
}

5
Слід зазначити, що require()це не є частиною стандартного JavaScript - це вбудована функція в Node.js, тому корисна лише в цьому середовищі. ОП не вказує на роботу з Node.js.
Велоджет

0

Умовний імпорт також може бути досягнутий за допомогою трирічного та require()s:

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

Цей приклад узятий із документів ES Lint global-requ: https://eslint.org/docs/rules/global-require


5
Слід зазначити, що require()це не є частиною стандартного JavaScript - це вбудована функція в Node.js, тому корисна лише в цьому середовищі. ОП не вказує на роботу з Node.js.
Велоджет


0

Ні, ви не можете!

Однак, наткнувшись на цю проблему, ви повинні змусити переосмислити, як ви впорядковуєте свій код.

Перед модулями ES6 у нас були модулі CommonJS, які використовували синтаксис requ (). Ці модулі були "динамічними", це означає, що ми могли імпортувати нові модулі на основі умов у нашому коді. - джерело: https://bitsofco.de/what-is-tree-shaking/

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.