ES6: งบการนำเข้าตามเงื่อนไขและแบบไดนามิก


86

เงื่อนไข

เป็นไปได้ไหมที่จะมีข้อความนำเข้าแบบมีเงื่อนไขเช่นด้านล่างนี้

if (foo === bar) {
    import Baz from './Baz';
}

ฉันได้ลองข้างต้นแล้ว แต่ได้รับข้อผิดพลาดต่อไปนี้ (จาก Babel) เมื่อทำการคอมไพล์

'import' and 'export' may only appear at the top level

ไดนามิก

เป็นไปได้ไหมที่จะมีคำสั่งการนำเข้าแบบไดนามิกเช่นด้านล่าง

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        import Baz from `./${foo}`;
    }
}

ข้างต้นได้รับข้อผิดพลาดเดียวกันจาก Babel ในขณะที่รวบรวม

เป็นไปได้หรือไม่หรือมีบางอย่างที่ฉันขาดหายไป?

การใช้เหตุผล

เหตุผลที่ฉันพยายามทำเช่นนี้คือฉันมีการนำเข้าจำนวนมากสำหรับ "เพจ" จำนวนมากและเป็นไปตามรูปแบบที่คล้ายกัน ฉันต้องการล้างฐานรหัสของฉันโดยการนำเข้าไฟล์เหล่านี้ด้วยไดนามิกสำหรับลูป

หากไม่สามารถทำได้มีวิธีที่ดีกว่าในการจัดการการนำเข้าจำนวนมากใน ES6 หรือไม่?


1
ไม่สามารถใช้มรดกในกรณีนี้ได้หรือไม่? ใช้superโทรเฉพาะ
ใจ

ฉันใช้การสืบทอดอยู่แล้ว แต่ "เพจ" เหล่านี้มีตรรกะเฉพาะ "เพจ" อยู่ในนั้น ฉันมีคลาส "เพจ" พื้นฐานที่ทั้งหมดขยายออกไป แต่นี่ไม่เพียงพอที่จะล้างการนำเข้าจำนวนมากที่ฉันมี
Enijar

1
@zerkms: พวกเขาไม่ได้ถูกยกออกจากบล็อก - เป็นข้อผิดพลาดทางไวยากรณ์
Bergi


ที่เกี่ยวข้อง: การสร้างการส่งออกโมดูล
es6

คำตอบ:


54

ขณะนี้เรามีข้อเสนอการนำเข้าแบบไดนามิกด้วย ECMA นี้อยู่ในขั้นตอนที่ 2 นี้ยังมีอยู่เป็นBabel-ที่ตั้งไว้

ต่อไปนี้เป็นวิธีการแสดงผลตามเงื่อนไขตามกรณีของคุณ

if (foo === bar) {
    import('./Baz')
    .then((Baz) => {
       console.log(Baz.Baz);
    });
}

สิ่งนี้ส่งคืนคำสัญญาโดยทั่วไป ความละเอียดของสัญญาคาดว่าจะมีโมดูล ข้อเสนอนี้ยังมีสิ่งที่ต้องการนำเข้าแบบไดนามิกหลายการนำเข้าค่าเริ่มต้น JS นำเข้าไฟล์ ฯลฯ คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับการนำเข้าแบบไดนามิกที่นี่


3
นี้. การนำเข้าแบบไดนามิกเป็นวิธีที่จะไป พวกเขาทำงานเหมือนกับต้องใช้ () ยกเว้นว่าพวกเขาให้สัญญากับคุณมากกว่าโมดูล
superluminary

25

คุณไม่สามารถแก้ไขการอ้างอิงของคุณแบบไดนามิกได้ตามที่importsมีไว้สำหรับการวิเคราะห์แบบคงที่ อย่างไรก็ตามคุณสามารถใช้บางส่วนได้requireที่นี่เช่น:

for (let foo in bar) {
    if (bar.hasOwnProperty(foo)) {
        const Baz = require(foo).Baz;
    }
}

8
"เนื่องจากการนำเข้ามีไว้สำหรับการวิเคราะห์แบบคงที่" --- คำสั่งนี้คลุมเครือ imports ถูกออกแบบมาเพื่อนำเข้าไม่ใช่เพื่อการวิเคราะห์
zerkms

13
@zerkms - ฉันคิดว่าสิ่งที่พวกเขาหมายถึงคือimportข้อความนั้นมีไว้เพื่อให้เหมาะสำหรับการวิเคราะห์แบบคงที่ - เนื่องจากไม่เคยมีเงื่อนไขเครื่องมือจึงสามารถวิเคราะห์โครงสร้างการพึ่งพาได้ง่ายขึ้น
Joe Clay

4
ยากที่จะเข้าใจกับ "foo" "baz" และ "bar" - ตัวอย่างชีวิตจริงเป็นอย่างไร?
TetraDev

1
นี่ไม่เป็นความจริงอีกต่อไป การนำเข้าแบบไดนามิกเป็นสิ่งสำคัญ ดูที่นี่: stackoverflow.com/a/46543949/687677
superluminary

7

เนื่องจากคำถามนี้ได้รับการจัดอันดับสูงโดย Google จึงควรชี้ให้เห็นว่ามีการเปลี่ยนแปลงตั้งแต่มีการโพสต์คำตอบเก่า ๆ

MDN มีรายการนี้ภายใต้การนำเข้าแบบไดนามิก :

คีย์เวิร์ดอิมพอร์ตอาจเรียกว่าเป็นฟังก์ชันเพื่ออิมพอร์ตโมดูลแบบไดนามิก เมื่อใช้วิธีนี้จะส่งคืนคำสัญญา

import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });

// This form also supports the await keyword.
let module = await import('/modules/my-module.js');

บทความที่มีประโยชน์เกี่ยวกับเรื่องนี้สามารถพบได้บนกลาง


2

ตั้งแต่ปี 2016 ได้ผ่านไปมากมายในโลก JavaScript ดังนั้นฉันเชื่อว่าถึงเวลาที่จะนำเสนอข้อมูลล่าสุดในหัวข้อนี้ ปัจจุบันการนำเข้าแบบไดนามิกเป็นจริงทั้งบนโหนดและบนเบราว์เซอร์ (โดยปกติถ้าคุณไม่สนใจ IE หรือด้วย@ babel / plugin-syntax-dynamic-importหากคุณสนใจ)

ดังนั้นให้พิจารณาโมดูลตัวอย่างที่something.jsมีการส่งออกที่มีชื่อสองรายการและการส่งออกเริ่มต้นหนึ่งรายการ:

export const hi = (name) => console.log(`Hi, ${name}!`)
export const bye = (name) => console.log(`Bye, ${name}!`)
export default () => console.log('Hello World!')

เราสามารถใช้import()ไวยากรณ์เพื่อโหลดตามเงื่อนไขได้อย่างง่ายดายและหมดจด:

if (somethingIsTrue) {
  import('./something.js').then((module) => {
    // Use the module the way you want, as:
    module.hi('Erick') // Named export
    module.bye('Erick') // Named export
    module.default() // Default export
  })
}

แต่เนื่องจากผลตอบแทนเป็นPromiseที่async/ awaitน้ำตาลประโยคยังเป็นไปได้:

async imAsyncFunction () {
  if (somethingIsTrue) {
    const module = await import('./something.js')
    module.hi('Erick')
  }
}

ลองคิดถึงความเป็นไปได้พร้อมกับObject Destructuring Assignment ! ตัวอย่างเช่นเราสามารถใส่การส่งออกที่มีชื่อเพียงรายการเดียวในหน่วยความจำเพื่อใช้ในภายหลังได้อย่างง่ายดาย:

const { bye } = await import('./something.js')
bye('Erick')

หรืออาจคว้าหนึ่งในการส่งออกที่มีชื่อและเปลี่ยนชื่อเป็นสิ่งอื่นที่เราต้องการ:

const { hi: hello } = await import('./something.js')
hello('Erick')

หรือแม้กระทั่งเปลี่ยนชื่อฟังก์ชันที่ส่งออกเริ่มต้นเป็นสิ่งที่เหมาะสมกว่า:

const { default: helloWorld } = await import('./something.js')
helloWorld()

หมายเหตุสุดท้าย (แต่ไม่ท้ายสุด): import()อาจดูเหมือนการเรียกใช้ฟังก์ชัน แต่ไม่ใช่ไฟล์Function. เป็นไวยากรณ์พิเศษที่ใช้วงเล็บ (คล้ายกับสิ่งที่เกิดขึ้นกับsuper()) ดังนั้นจึงเป็นไปไม่ได้ที่จะกำหนดimportให้กับตัวแปรหรือการใช้สิ่งที่ของFunctionต้นแบบเช่น/callapply


1

Require จะไม่ช่วยแก้ปัญหาของคุณเนื่องจากเป็นการโทรแบบซิงโครนัส มีหลายทางเลือกและเกี่ยวข้องทั้งหมด

  1. ขอโมดูลที่คุณต้องการ
  2. รอสัญญาว่าจะคืนโมดูล

ใน ECMA Script มีการรองรับโมดูลการโหลดแบบขี้เกียจโดยใช้ SystemJS แน่นอนว่าสิ่งนี้ไม่ได้รับการสนับสนุนในทุกเบราว์เซอร์ดังนั้นในระหว่างนี้คุณสามารถใช้ JSPM หรือ SystemJS shim

https://github.com/ModuleLoader/es6-module-loader

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