ชื่ออิมพอร์ตตัวแปร ES6 ใน node.js?


108

เป็นไปได้หรือไม่ที่จะนำเข้าบางสิ่งบางอย่างไปยังโมดูลที่ให้ชื่อตัวแปรในขณะที่ใช้การนำเข้า ES6

คือฉันต้องการนำเข้าโมดูลบางส่วนที่รันไทม์ขึ้นอยู่กับค่าที่ระบุใน config:

import something from './utils/' + variableName;

1
@ Bigood ใช่คอมไพเลอร์พ่นและ Webstorm ก็แสดงข้อผิดพลาด
Vytautas Butkus

คำตอบ:


67

ไม่ใช่ด้วยimportคำสั่ง importและexportถูกกำหนดในลักษณะที่วิเคราะห์ได้แบบคงที่ดังนั้นจึงไม่สามารถขึ้นอยู่กับข้อมูลรันไทม์ได้

คุณกำลังมองหาloader API (polyfill)แต่ฉันไม่ชัดเจนเกี่ยวกับสถานะของข้อกำหนด:

System.import('./utils/' + variableName).then(function(m) {
  console.log(m);
});

3
ฉันต้อง "ต้องการ" ระบบหรือไม่? มันไม่ได้อยู่ในขอบเขตระดับโลก .. Ps ฉันใช้ babel js
Vytautas Butkus

ยังไม่ได้ใช้งาน AFAIK ที่ใดก็ได้ คุณต้องใช้ polyfill จากลิงค์
Felix Kling

1
เพียงแค่การตรวจสอบก็เลยทำงาน (พยายามโหลดไฟล์) แต่แล้วก็ยุ่งขึ้นเส้นทางสำหรับโมดูลอื่น ๆ ... เลวร้ายเกินไปก็ไม่ได้รับการสนับสนุนโดยกำเนิด ..
วิทอ Butkus

3
สิ่งนี้ยังคงเป็นปัญหาอยู่หรือไม่? ฉันต้องการโหลดโมดูล ES6 แบบ dinamically แต่ก็ไม่ประสบความสำเร็จ ..
calbertts

26

นอกจากคำตอบของเฟลิกซ์แล้วฉันจะสังเกตอย่างชัดเจนว่าตอนนี้ไวยากรณ์ ECMAScript 6ยังไม่ได้รับอนุญาต:

ImportDeclaration :

  • นำเข้า ImportClause FromClause;

  • นำเข้า ModuleSpecifier;

FromClause :

  • จาก ModuleSpecifier

ModuleSpecifier :

  • StringLiteral

ModuleSpecifierสามารถเป็นStringLiteralไม่ได้ชนิดอื่น ๆ ของการแสดงออกเช่นAdditiveExpression


2
น่าเสียดายที่นี่ไม่ได้รวมconst string literals ไว้ด้วย พวกมันวิเคราะห์ได้แบบคงที่ใช่หรือไม่? มันจะทำให้การนำตำแหน่งของการพึ่งพากลับมาใช้ใหม่เป็นไปได้ (เช่นนำเข้าเทมเพลตและมีทั้งเทมเพลตและตำแหน่งของเทมเพลตที่พร้อมใช้งาน)
nicodemus13

26

ในขณะที่นี่ไม่ใช่การนำเข้าแบบไดนามิก (เช่นในสถานการณ์ของฉันไฟล์ทั้งหมดที่ฉันนำเข้าด้านล่างจะถูกนำเข้าและรวมกลุ่มโดย webpack ไม่ได้เลือกในรันไทม์) รูปแบบที่ฉันใช้ซึ่งอาจช่วยได้ในบางสถานการณ์คือ :

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)หากคุณต้องการให้พวกเขาเป็นรายการอีกครั้งเพียงแค่ทำ
Andrew Koster

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 Developers

ความเข้ากันได้ของเบราว์เซอร์ (เมษายน 2020)

ตามMDNได้รับการสนับสนุนโดยเบราว์เซอร์หลักทุกตัวในปัจจุบัน (ยกเว้น IE) และcaniuse.comแสดงการสนับสนุน 87% ในส่วนแบ่งการตลาดทั่วโลก อีกครั้งไม่รองรับ IE หรือขอบที่ไม่ใช่โครเมียม


คุณแน่ใจเกี่ยวกับการแก้ไขของคุณหรือไม่ ข้อเสนอแสดงตัวอย่างที่มีเส้นทางตัวแปร: github.com/tc39/proposal-dynamic-import#example
phil294

@Blauhirn ฉันเป็น แต่ลิงค์ของคุณแสดงให้เห็นชัดเจนว่าเป็นไปได้ แม้ว่าฉันจะไม่รู้ว่า webpack จะแก้ไขการนำเข้าเหล่านี้อย่างไร
Nicolai Schmid

แก้ไขฉันถ้าฉันผิด แต่ webpack ไม่ประมวลผลสิ่งเหล่านี้ใช่หรือไม่ ฉันคิดว่านี่เป็นจุดสำคัญของการนำเข้าแบบไดนามิกที่เรียกใช้ "ตามสภาพ" ในเบราว์เซอร์
phil294

ได้คุณสามารถเรียกใช้งานได้ในเบราว์เซอร์ตามที่เป็นอยู่ แต่ Webpack จะใช้การนำเข้าโดยอัตโนมัติเพื่อแยกแอปของคุณออกเป็นหลายกลุ่มสำหรับส่วนต่างๆของแอปเช่นสำหรับเส้นทาง ฉันใช้มันตลอดเวลาและเป็นประโยชน์จริงๆ และเท่าที่ "การประมวลผล" ดำเนินไป webpack จะส่งผ่านการนำเข้า babel ซึ่งจะเติมเต็มคุณลักษณะสำหรับเบราว์เซอร์รุ่นเก่า
Nicolai Schmid

เพื่อให้ชัดเจน: การนำเข้าแบบไดนามิก () จะทำงานกับตัวแปรและไม่จำเป็นต้องวิเคราะห์แบบคงที่ (นั่นคือจุดรวมของ 'ไดนามิก' อย่างแน่นอน?) ดูคำตอบของฉัน
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;

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 มันจะไม่เป็นการเขย่าต้นไม้อย่างถูกต้องกับอันที่สอง ยังมีความแตกต่างอื่น ๆ อีกมากมายฉันขอแนะนำให้คุณอ่านเอกสารและดูว่าข้อใดเหมาะสมกับคุณมากกว่ากัน!
Ulysse BN

@Jacob - ไม่สำคัญจริงๆ (ในกรณีส่วนใหญ่) require()เป็นวิธีการ Node.JS สำหรับการโหลดไฟล์ซึ่งเป็นเวอร์ชันแรก ๆ importคำสั่งเป็นเวอร์ชันที่ใหม่กว่าซึ่งตอนนี้เป็นส่วนหนึ่งของไวยากรณ์ภาษาทางการ อย่างไรก็ตามในหลาย ๆ กรณีเบราว์เซอร์จะใช้เบราว์เซอร์ก่อนหน้านี้ (อยู่เบื้องหลังวิทยาศาสตร์) คำสั่งที่ต้องการจะทำให้ไฟล์ของคุณเป็นเงินสดด้วยดังนั้นหากไฟล์ถูกโหลดครั้งที่ 2 ไฟล์จะถูกโหลดจากหน่วยความจำ (ประสิทธิภาพดีกว่า) วิธีการนำเข้ามีประโยชน์ในตัวเอง - หากคุณใช้ WebPack จากนั้น webpack สามารถลบการอ้างอิงที่ตายแล้ว (สคริปต์เหล่านี้จะไม่ถูกดาวน์โหลดไปยังไคลเอนต์)
Gil Epshtain


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();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.