Имя импорта переменной ES6 в node.js?

108

Можно ли импортировать что-то в модуль, указав имя переменной при использовании импорта ES6?

Т.е. я хочу импортировать какой-то модуль во время выполнения в зависимости от значений, указанных в конфигурации:

import something from './utils/' + variableName;
Витаутас Буткус
источник
1
@Bigood да, компилятор выкидывает, и Webstorm также показывает ошибку
Витаутас Буткус

Ответы:

67

Не с importзаявлением. importиexport определены таким образом, чтобы их можно было статически анализировать, поэтому они не могут зависеть от информации времени выполнения.

Вы ищете API загрузчика (polyfill) , но мне немного неясно состояние спецификации:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});
Феликс Клинг
источник
3
мне нужно "требовать" Систему? Это не в глобальном масштабе .. ps Я использую babel js
Витаутас Буткус
Это еще нигде не реализовано, AFAIK. Вы должны использовать полифилл по ссылке.
Феликс Клинг,
1
Только что проверил, вроде работает (пытается загрузить файл), но потом путает пути для других модулей ... Жаль, что изначально он не поддерживается ..
Витаутас Буткус
3
Это все еще проблема? Мне нужно динамически загружать модули ES6, но у меня ничего не получилось ..
Calbertts
26

В дополнение к ответу Феликса я прямо отмечу, что в настоящее время это не разрешено грамматикой ECMAScript 6 :

Декларация об импорте :

  • import ImportClause FromClause;

  • Импортировать ModuleSpecifier;

FromClause :

  • из ModuleSpecifier

ModuleSpecifier :

  • СтроковойЛитерал

ModuleSpecifier может быть только СтроковойЛитерал , а не какой - либо другой вид выражения , как АддитивноеВыражение .

apsillers
источник
2
Жаль, что это не было расширено, чтобы включить const string literals. Их можно статически анализировать, не так ли? Это сделало бы возможным повторное использование местоположения зависимости. (например, импортируйте шаблон и получите как шаблон, так и его расположение).
nicodemus13 08
26

Хотя на самом деле это не динамический импорт (например, в моих обстоятельствах все файлы, которые я импортирую ниже, будут импортированы и объединены веб-пакетом, а не выбраны во время выполнения), я использовал шаблон, который может помочь в некоторых обстоятельствах: :

import Template1 from './Template1.js';
import Template2 from './Template2.js';

const templates = {
  Template1,
  Template2
};

export function getTemplate (name) {
  return templates[name];
}

или альтернативно:

// index.js
export { default as Template1 } from './Template1';
export { default as Template2 } from './Template2';


// OtherComponent.js
import * as templates from './index.js'
...
// handy to be able to fall back to a default!
return templates[name] || templates.Template1;

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

Хорошие примеры и сравнения между require и import можно найти здесь: http://www.2ality.com/2014/09/es6-modules-final.html

Отличная документация по реэкспорту из @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Мне интересно услышать отзывы об этом подходе :)

птим
источник
Голосовать за. Я использовал подход «или альтернативно». Сработал как шарм для моего индивидуального решения по локализации.
Groundh0g
1
Я должен был подумать об этом. Отличное решение, независимо от того, как вы импортируете вещи (и даже если вы ничего не импортируете). У вас есть список предметов, которые вы хотите узнать позже или получить по имени? Поместите их в литерал объекта вместо литерала массива, и пусть синтаксис объекта позаботится о присвоении им имен на основе их локальных имен констант / переменных. Если они вам снова понадобятся в виде списка, просто сделайте это Object.values(templates).
Эндрю Костер,
15

Появилась новая спецификация, которая называется динамическим импортом модулей ES. В общем, вы просто звоните, import('./path/file.js')и все готово. Функция возвращает обещание, которое разрешается модулем, если импорт был успешным.

async function importModule() {
   try {
      const module = await import('./path/module.js');
   } catch (error) {
      console.error('import failed');
   }
}

Случаи использования

Сценарии использования включают импорт компонентов на основе маршрута для React, Vue и т. Д., А также возможность отложенной загрузки модулей. , когда они требуются во время выполнения.

Дальнейшая информация

Вот объяснение от разработчиков Google .

Совместимость браузера (апрель 2020 г.)

Согласно MDN, он поддерживается всеми текущими основными браузерами (кроме IE), а caniuse.com показывает 87% поддержки на мировом рынке. Снова нет поддержки в IE или Edge без хрома.

Николай Шмид
источник
ты уверен в своей правке? в предложении показан пример с переменным путем: github.com/tc39/proposal-dynamic-import#example
phil294
@Blauhirn Я был, но ваша ссылка ясно показывает, что это возможно. Хотя я понятия не имею, как, например, webpack разрешит этот импорт
Николай Шмид
поправьте меня, если я ошибаюсь, но webpack их не обрабатывает, не так ли? Я думал, что дело в том, что динамический импорт запускается в браузере «как есть».
phil294
Да, вы можете запускать их в браузере как есть. Но webpack автоматически использует импорт для разделения вашего приложения на несколько пакетов для разных частей вашего приложения, например, для маршрутов. Я использую их все время, и они действительно полезны. А что касается «обработки»; webpack передаст импорт babel, который заполнит эту функцию для старых браузеров.
Николай Шмид
Чтобы прояснить: динамический import () будет работать с переменными и не требует статического анализа (ведь в этом весь смысл «динамического»?). Смотрите мой ответ.
Velojet
6

Я понимаю вопрос, специально заданный для ES6 importв Node.js, но следующее может помочь другим, ищущим более общее решение:

let variableName = "es5.js";
const something = require(`./utils/${variableName}`);

Обратите внимание, если вы импортируете модуль ES6 и вам нужно получить доступ к defaultэкспорту, вам нужно будет использовать одно из следующих действий:

let variableName = "es6.js";

// Assigning
const defaultMethod = require(`./utils/${variableName}`).default;

// Accessing
const something = require(`./utils/${variableName}`);
something.default();

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

// Destructuring 
const { someMethod } = require(`./utils/${variableName}`);    
someMethod();

К сожалению, если вы хотите получить доступ, defaultа также деструктурировать, вам нужно будет выполнить это в несколько этапов:

// ES6 Syntax
Import defaultMethod, { someMethod } from "const-path.js";

// Destructuring + default assignment
const something = require(`./utils/${variableName}`);

const defaultMethod = something.default;    
const { someMethod, someOtherMethod } = something;
MCTaylor17
источник
4

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

let myModule = null;
if (needsToLoadModule) {
  myModule = require('my-module').default;
}
млеванон
источник
3

Мне этот синтаксис меньше нравится, но он работает:
вместо того, чтобы писать

import memberName from "path" + "fileName"; 
// this will not work!, since "path" + "fileName" need to be string literal

используйте этот синтаксис:

let memberName = require("path" + "fileName");
Гил Эпштейн
источник
1
@UlysseBN В чем разница? Или способ, который на самом деле не имеет значения?
Сэм
@Jacob, они действительно совершенно разные, так что да, это может иметь значение в зависимости от того, что вы делаете. Первый синтаксис оценивается статически, а второй - динамически. Так, например, если вы используете веб-пакет, не удастся правильно выполнить встряхивание дерева со вторым. Есть много других отличий, я бы посоветовал вам прочитать документ и посмотреть, какой из них вам больше подходит!
Ulysse BN
@Jacob - Неважно (в большинстве случаев). require()- это ранняя версия метода Node.JS для загрузки файлов. importstatement - это более новая версия, которая теперь является частью синтаксиса официального языка. Однако во многих случаях браузер будет использовать предыдущий (за наукой). Оператор require также кэширует ваши файлы, поэтому, если файл загружается второй раз, он будет загружен из памяти (лучшая производительность). У способа импорта есть свои преимущества - если вы используете WebPack. тогда webpack может удалить мертвые ссылки (эти скрипты не будут загружены на клиент).
Гил Эпштейн
1

Динамический импорт () (доступен в Chrome 63+) сделает вашу работу. Вот как:

let variableName = 'test.js';
let utilsPath = './utils/' + variableName;
import(utilsPath).then((module) => { module.something(); });
Велоджет
источник
0

Я бы сделал это так

function load(filePath) {
     return () => System.import(`${filePath}.js`); 
     // Note: Change .js to your file extension
}

let A = load('./utils/' + variableName)

// Now you can use A in your module
апрель
источник
0

./utils/test.js

export default () => {
  doSomething...
}

звонок из файла

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
Андрес Муньос
источник