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

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

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" из этой статьи выводит меня на устаревшую страницу документации.

17 ответов

Решение

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

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

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

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

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

let something = null;
let other = null;

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

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

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

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, он нуждается в инициализации.

Обновление 2020

Теперь вы можете позвонить в import ключевое слово как функция (т.е. import()) для загрузки модуля во время выполнения.

Пример:

      const mymodule = await import(modulename);

или же:

      import(modulename)
    .then(mymodule => /* ... */);

См. Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports.

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

http://exploringjs.com/es6/ch_modules.html

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

Важное отличие, если вы используете режим динамического импорта 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(...);
}

Условный импорт и экспорт в JS

      const value = (
    await import(`${condtion ? `./file1.js` : `./file2.js`}`)
).default

export default value

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]);

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

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

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

Утаивание его в eval работало для меня, скрывая его от статического анализатора...

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

У меня была похожая ситуация. Структура моего проекта была такой:

  • библиотеки /
    • макетApi.js
    • RealApi.js
  • index.js

Мне было необходимо, чтобы в продакшен -режиме в комплект не попадали моки. Еще мне было важно не писать условия в каждом месте использования и не работать с промисами.

Для меня решением было создать объединяющий файл api.js с кодом:

      // solution 1
export const api = (
    await import(`${process.env.NODE_ENV === 'development' ? './mockAPI.js' : './realAPI.js'}`)
).default

export default api;

При таком подходе в продакшен-режиме в бандл попадает только обработанный realAPI.js , а использование решения не требует отдельных условий или работы с промисами, например:

      import api from './libs/api';
api.getUser();

Также можно использовать аналогичное решение:

      // solution 2
let api = (await import('./realAPI')).default;

if (process.env.NODE_ENV === 'development') {
    api = (await import('./mockAPI')).default;
}

export default api;

Оба решения позволяют не включать в комплект «моки» в рабочем режиме. Это делается путем удаления недостижимого кода во время процесса сборки, важно не перемещать условиеprocess.env.NODE_ENV === 'development' в переменную.

Можно перейти по ссылке ниже, чтобы узнать больше о динамическом импорте.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports

Нет, нельзя!

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

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

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

Посмотрите на этот пример, чтобы понять, как работает динамический импорт.

Пример импорта динамического модуля

Иметь базовое представление об импорте и экспорте модулей.

Модули JavaScript Github

Модули Javascript MDN

Я знаю, что это не то, о чем задается вопрос, но вот мой подход к использованию моков при использовании vite . Я уверен, что мы можем сделать то же самое с webpack и другими.

Предположим, у нас есть две библиотеки с одинаковым интерфейсом: link.jsа также link-mock.js, тогда:

В моем vite.config.js

      export default defineConfig(({ command, mode }) => {
    
    const cfg = {/* ... */}

    if (process.env.VITE_MOCK == 1) {
        cfg.resolve.alias["./link"] = "./link-mock"; // magic is here!
    }

    return cfg;
}

код:

      import { link } from "./link";

в консоли вызываем:

      # to use the real link.js
npm run vite

# to use the mock link-mock.js
VITE_MOCK=1 npm run vite

или же

Скрипт package.json

      {
    ....
    "scripts": {
        "dev": "vite",        
        "dev-mock": "VITE_MOCK=1 vite"
    }
}

Как говорит Эриксоко , вместо:

      import {ExampleClass} from "./ExampleClass.js";
new ExampleClass();

ты можешь написать

      if(condition) {
  import("./ExampleClass.js").then((module) => {
    new module.ExampleClass();
  });
}

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

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

if(something) {
  something.doStuff();
}
Другие вопросы по тегам