Nama impor variabel ES6 di node.js?

108

apakah mungkin untuk mengimpor sesuatu ke modul yang menyediakan nama variabel saat menggunakan impor ES6?

Yaitu saya ingin mengimpor beberapa modul saat runtime tergantung pada nilai yang diberikan dalam konfigurasi:

import something from './utils/' + variableName;
Vytautas Butkus
sumber
1
@Bagus ya, kompiler muntah dan Webstorm juga menunjukkan kesalahan
Vytautas Butkus

Jawaban:

67

Tidak dengan importpernyataannya. importdan exportdidefinisikan sedemikian rupa sehingga dapat dianalisis secara statis, sehingga tidak dapat bergantung pada informasi waktu proses.

Anda mencari API pemuat (polyfill) , tetapi saya agak tidak jelas tentang status spesifikasinya:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});
Felix Kling
sumber
3
apakah saya perlu "memerlukan" Sistem? Ini bukan dalam lingkup global .. Ps Saya menggunakan babel js
Vytautas Butkus
Ini belum diterapkan di mana pun. AFAIK. Anda harus menggunakan polyfill dari tautan.
Felix Kling
1
Baru saja dicentang, ini agak berfungsi (mencoba memuat file) tetapi kemudian mengacaukan jalur untuk modul lain ... Sayang sekali itu tidak didukung secara native ..
Vytautas Butkus
3
Apakah ini masih menjadi masalah? Saya perlu memuat modul ES6 secara dinamis tetapi saya belum berhasil ..
calbertts
26

Selain jawaban Felix , saya akan mencatat secara eksplisit bahwa ini saat ini tidak diizinkan oleh tata bahasa ECMAScript 6 :

ImportDeclaration :

  • import ImportClause FromClause;

  • import ModuleSpecifier;

FromClause :

  • dari ModuleSpecifier

ModuleSpecifier :

  • StringLiteral

Sebuah ModuleSpecifier hanya bisa menjadi StringLiteral , tidak ada jenis lain dari ekspresi seperti AdditiveExpression .

apsillers
sumber
2
Sayang sekali ini tidak diperpanjang untuk menyertakan const string literals. Mereka dapat dianalisis secara statis, bukan? Itu akan membuat kemungkinan menggunakan kembali lokasi ketergantungan. (mis. mengimpor template dan memiliki template serta lokasi template yang tersedia).
nicodemus13
26

Meskipun ini sebenarnya bukan impor dinamis (misalnya dalam keadaan saya, semua file yang saya impor di bawah ini akan diimpor dan digabungkan oleh webpack, tidak dipilih saat runtime), pola yang telah saya gunakan yang mungkin membantu dalam beberapa keadaan adalah :

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

const templates = {
  Template1,
  Template2
};

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

atau sebagai alternatif:

// 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;

Saya tidak berpikir saya bisa kembali ke default semudah dengan require(), yang menimbulkan kesalahan jika saya mencoba mengimpor jalur template yang dibangun yang tidak ada.

Contoh dan perbandingan yang baik antara Requirement dan Import dapat ditemukan di sini: http://www.2ality.com/2014/09/es6-modules-final.html

Dokumentasi yang sangat baik tentang mengekspor ulang dari @iainastacio: http://exploringjs.com/es6/ch_modules.html#sec_all-exporting-styles

Saya tertarik untuk mendengar umpan balik tentang pendekatan ini :)

ptim
sumber
Suara positif. Saya menggunakan pendekatan "atau alternatif". Bekerja seperti pesona untuk solusi lokalisasi kustom saya.
groundh0g
1
Saya seharusnya memikirkan ini. Solusi hebat, tidak peduli bagaimana Anda mengimpor sesuatu (dan bahkan jika Anda tidak mengimpor apa pun). Punya daftar item yang ingin Anda dapatkan namanya, atau dapatkan namanya, nanti? Tempatkan mereka dalam literal objek alih-alih literal array, dan biarkan sintaks objek mengurus penamaannya berdasarkan nama variabel / konstanta lokalnya. Jika Anda membutuhkannya sebagai daftar lagi, lakukan saja Object.values(templates).
Andrew Koster
15

Ada spesifikasi baru yang disebut impor dinamis untuk modul ES. Pada dasarnya, Anda hanya menelepon import('./path/file.js')dan Anda siap berangkat. Fungsi mengembalikan janji, yang diselesaikan dengan modul jika impor berhasil.

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

Kasus penggunaan

Kasus penggunaan termasuk pengimporan komponen berbasis rute untuk React, Vue, dll. Dan kemampuan untuk memuat modul secara malas , setelah modul tersebut diperlukan selama runtime.

Informasi lebih lanjut

Berikut penjelasan tentang Google Developers .

Kompatibilitas browser (April 2020)

Menurut MDN , ini didukung oleh setiap browser utama saat ini (kecuali IE) dan caniuse.com menunjukkan 87% dukungan di seluruh pangsa pasar global. Sekali lagi tidak ada dukungan di IE atau non-chromium Edge.

Nicolai Schmid
sumber
apakah Anda yakin dengan hasil edit Anda? proposal menunjukkan contoh dengan jalur variabel: github.com/tc39/proposal-dynamic-import#example
phil294
@Blauhirn Saya dulu, tetapi tautan Anda dengan jelas menunjukkan bahwa itu adalah kemungkinan. Meskipun saya tidak tahu bagaimana webpack, misalnya akan menyelesaikan impor ini
Nicolai Schmid
koreksi saya jika saya salah, tetapi webpack tidak memproses ini, bukan? Saya pikir itu adalah tujuan dari impor dinamis yang dijalankan "sebagaimana adanya" di browser.
phil294
Ya, Anda dapat menjalankannya di browser apa adanya. Tetapi webpack secara otomatis menggunakan impor untuk membagi aplikasi Anda menjadi beberapa bundel untuk bagian yang berbeda dari aplikasi Anda, misalnya untuk rute. Saya menggunakannya sepanjang waktu dan sangat membantu. Dan sejauh "pemrosesan" berjalan; webpack akan meneruskan impor babel, yang akan mem-polyfill fitur untuk browser lama.
Nicolai Schmid
Untuk memperjelas: dynamic import () akan bekerja dengan variabel dan tidak perlu dianalisis secara statis (itulah inti dari 'dinamis', tentunya?). Lihat jawaban saya.
Velojet
6

Saya memahami pertanyaan yang secara khusus ditanyakan untuk ES6 importdi Node.js, tetapi yang berikut ini mungkin membantu orang lain mencari solusi yang lebih umum:

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

Perhatikan jika Anda mengimpor modul ES6 dan perlu mengakses defaultekspor, Anda perlu menggunakan salah satu dari berikut ini:

let variableName = "es6.js";

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

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

Anda juga dapat menggunakan destructuring dengan pendekatan ini yang dapat menambahkan lebih banyak sintaks yang familiar dengan impor Anda yang lain:

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

Sayangnya, jika Anda ingin mengakses defaultserta merusak, Anda perlu melakukan ini dalam beberapa langkah:

// 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
sumber
4

Anda dapat menggunakan notasi non-ES6 untuk melakukannya. inilah yang berhasil untuk saya:

let myModule = null;
if (needsToLoadModule) {
  myModule = require('my-module').default;
}
mlevanon.dll
sumber
3

Saya kurang suka sintaks ini, tetapi berfungsi:
alih-alih menulis

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

gunakan sintaks ini:

let memberName = require("path" + "fileName");
Gil Epshtain
sumber
1
@UlysseBN Berbeda dengan cara yang buruk? Atau cara yang tidak terlalu penting?
Sam
@Jacob mereka benar-benar berbeda, jadi ya itu bisa jadi masalah tergantung pada apa yang Anda lakukan. Sintaks pertama dievaluasi secara statis, sedangkan sintaks kedua dievaluasi secara dinamis. Jadi misalnya, jika Anda menggunakan webpack, itu tidak akan menjadi untuk melakukan getar pohon dengan benar dengan yang kedua. Ada banyak perbedaan lainnya, saya sarankan Anda membaca dokumen dan melihat mana yang lebih sesuai untuk Anda!
Ulysse BN
@Jacob - Tidak terlalu penting (dalam banyak kasus). require()adalah metode Node.JS untuk memuat file, yang merupakan versi awal. importpernyataan adalah versi yang lebih baru, yang sekarang menjadi bagian dari sintaks bahasa resmi. Namun dalam banyak kasus, browser akan menggunakan versi sebelumnya (di belakang sains). Pernyataan Requirement juga akan menguangkan file Anda, jadi jika file dimuat untuk kedua kalinya, file tersebut akan dimuat dari memori (performa lebih baik). Cara impor memiliki keuntungannya sendiri - jika Anda menggunakan WebPack. kemudian webpack dapat menghapus referensi mati (skrip ini tidak akan diunduh ke klien).
Gil Epshtain
1

Impor dinamis () (tersedia di Chrome 63+) akan melakukan tugas Anda. Begini caranya:

let variableName = 'test.js';
let utilsPath = './utils/' + variableName;
import(utilsPath).then((module) => { module.something(); });
Velojet
sumber
0

Saya akan melakukannya seperti ini

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
April
sumber
0

./utils/test.js

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

panggilan dari file

const variableName = 'test';
const package = require(`./utils/${variableName}`);
package.default();
Andres Munoz
sumber