Імпорт імпорту змінної 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 :

ІмпортДекларація :

  • імпортувати ImportClause FromClause;

  • імпорт ModuleSpecifier;

ВідКауза :

  • від ModuleSpecifier

ModuleSpecifier :

  • StringLiteral

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


2
Шкода, що це не було розширено, щоб включити const string literals. Вони статистично проаналізовані, чи не так? Це дозволить повторно використовувати розташування залежності. (наприклад, імпортувати шаблон і мати доступний і шаблон, і розташування шаблону).
nicodemus13

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(), що призводить до помилки, якщо я спробую імпортувати побудований шлях шаблону, який не існує.

Хороші приклади та порівняння між потребою та імпортом можна знайти тут: 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/proposed-dynamic-import#example
phil294

@Blauhirn Я був, але ваше посилання чітко показує, що це можливість. Хоча я навіть не маю уявлення, як веб-пакет, наприклад, вирішив би цей імпорт
Ніколай Шмід

виправте мене, якщо я помиляюся, але webpack не обробляє їх, чи не так? Я подумав, що це сенс динамічного імпорту, який вони виконують «як є» у браузері.
phil294

Так, ви можете запустити їх у веб-переглядачі як є. Але webpack автоматично використовує імпорт, щоб розділити ваш додаток на кілька пакетів для різних частин вашої програми, наприклад для маршрутів. Я їх постійно використовую, і вони дуже корисні. І наскільки йде "обробка"; webpack передасть імпорт бабеля, що поліфікує функцію для старих браузерів.
Микола Шмід

Щоб було зрозуміло: динамічний імпорт () буде працювати зі змінними, і не потрібно його статичного аналізу (це, втім, вся суть "динамічного", безумовно?). Дивіться мою відповідь.
Велоджет

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;

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 вони насправді абсолютно різні, так що так, це може мати значення залежно від того, що ти робиш. Перший синтаксис оцінюється статично, тоді як другий динамічно оцінюється. Так, наприклад, якщо ви використовуєте webpack, це не буде правильно виконувати струшування дерева з другим. Є багато інших відмінностей, я б запропонував вам прочитати документ і побачити, який з них підходить вам більше!
Уліссе БН

@Jacob - насправді не має значення (у більшості випадків). require()це метод Node.JS для завантаження файлів, який є ранньою версією. importзаява - це нова версія, яка зараз є частиною синтаксису офіційної мови. Однак у багатьох випадках браузер використовуватиме попередній (за наукою). Виписка вимагає також грошових коштів ваших файлів, тому якщо файл завантажується вдруге, він буде завантажений з пам'яті (краща продуктивність). Спосіб імпорту має власні переваги - якщо ви використовуєте WebPack. тоді веб-пакет може видалити мертві посилання (ці сценарії не завантажуватимуться на клієнт).
Гіл Епштейн


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