ฉันจะนำเข้าโมดูล ES6 แบบมีเงื่อนไขได้อย่างไร


194

ฉันต้องทำสิ่งที่ชอบ:

if (condition) {
    import something from 'something';
}
// ...
if (something) {
    something.doStuff();
}

รหัสข้างต้นไม่ได้รวบรวม; SyntaxError: ... 'import' and 'export' may only appear at the top levelมันจะพ่น

ฉันพยายามใช้System.importตามที่แสดงที่นี่แต่ไม่รู้ว่าSystemมาจากไหน เป็นข้อเสนอ ES6 ที่ไม่ได้รับการยอมรับหรือไม่? เชื่อมโยงไปยัง "การเขียนโปรแกรม API" จากบทความที่ทิ้งฉันไปหน้าเอกสารเลิก


เพียงนำเข้าตามปกติ โมดูลของคุณต้องการโดยไม่คำนึงถึง
Andy

ฉันไม่เห็นเหตุผลใด ๆ ว่าทำไมคุณไม่นำเข้าโดยไม่คำนึงถึงเงื่อนไขใด ๆ มันไม่เหมือนว่ามีค่าใช้จ่ายบางอย่าง ในบางสถานการณ์คุณต้องการไฟล์ดังนั้นจึงไม่เหมือนกรณีที่สามารถข้ามได้ทั้งหมด ในกรณีนี้เพียงนำเข้าโดยไม่มีเงื่อนไข
ขอบคุณ

8
กรณีใช้งานของฉัน: ฉันต้องการทำให้ง่ายต่อการพึ่งพาตัวเลือก หากไม่จำเป็นต้องใช้ dep ผู้ใช้จะลบออกจากpackage.json; ของฉันgulpfileจากนั้นตรวจสอบว่าการพึ่งพานั้นมีอยู่ก่อนที่จะดำเนินการบางขั้นตอนการสร้าง
ericsoco

1
อีกกรณีการใช้งาน: เพื่อการทดสอบ ฉันกำลังใช้webpackและbabelเพื่อ transpile es6 เป็น es5 โครงการชอบwebpack-rewireและที่คล้ายกันไม่ได้ความช่วยเหลือที่นี่ - github.com/jhnns/rewire-webpack/issues/12 วิธีหนึ่งในการตั้งค่าการทดสอบเป็นสองเท่าหรือเพื่อลบการอ้างอิงที่มีปัญหาอาจเป็นการนำเข้าแบบมีเงื่อนไข
Amio.io

3
+1 ความสามารถในการใช้โมดูลในสภาพแวดล้อมที่หลากหลายที่การพึ่งพาอาจหรือไม่ทำงานเป็นสิ่งสำคัญโดยเฉพาะอย่างยิ่งเมื่อโมดูลอาจอ้างถึงการพึ่งพาที่จะทำงานในเบราว์เซอร์เท่านั้น (เช่นที่webpackใช้ในการแปลงสไตล์ชีทเป็นโมดูลที่ใส่สไตล์ที่เกี่ยวข้องลงใน DOM เมื่อนำเข้า) แต่โมดูลยังต้องทำงานนอกเบราว์เซอร์ (เช่นสำหรับการทดสอบหน่วย)
จูลส์

คำตอบ:


144

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

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

if (condition) {
    import('something')
    .then((something) => {
       console.log(something.something);
    });
}

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


13
ในที่สุดคำตอบ ES6 ที่แท้จริง! ขอบคุณ @thecodejack จริง ๆ แล้วในขั้นตอนที่ 3 ของการเขียนนี้อ้างอิงจากบทความตอนนี้
ericsoco

5
หรือถ้าคุณเพิ่งตั้งชื่อการส่งออกคุณสามารถทำลายโครงสร้างได้:if (condition) { import('something') .then(({ somethingExported }) => { console.log(somethingExported); }); }
ivn

4
ใน Firefox และในขณะที่ทำงานnpm run buildผมยังคงได้รับข้อผิดพลาด:SyntaxError: ... 'import' and 'export' may only appear at the top level
วาง

1
@stackjlei: คุณลักษณะนี้ยังไม่ได้เป็นส่วนหนึ่งของมาตรฐาน JavaScript มันเป็นเพียงขั้นตอน 3 ข้อเสนอ! แต่จะดำเนินการแล้วในเบราว์เซอร์รุ่นใหม่จำนวนมากเห็นcaniuse.com/#feat=es6-module-dynamic-import
Konrad Höffner

1
ฟังก์ชั่นการนำเข้าแบบไดนามิกที่มีเงื่อนไขนั้นไม่มีความสามารถที่ละเอียดยิ่งขึ้นในการนำเข้าองค์ประกอบเฉพาะที่ "นำเข้า X จาก Y" มี ในความเป็นจริงว่ามีความสามารถปรับเม็ดเล็กอาจจะสำคัญยิ่งขึ้นในการโหลดแบบไดนามิก (เมื่อเทียบกับ bundling preprocess)
เคร็กฮิกส์

102

หากคุณต้องการคุณสามารถใช้ต้องการ นี่คือวิธีที่จะมีคำสั่งแบบมีเงื่อนไข

let something = null;
let other = null;

if (condition) {
    something = require('something');
    other = require('something').other;
}
if (something && other) {
    something.doStuff();
    other.doOtherStuff();
}

1
ฉันคิดว่ามีบางสิ่งและตัวแปรอื่น ๆ กำลังประกาศโดยใช้ const ซึ่งเป็นบล็อกที่กำหนดขอบเขตดังนั้นข้อที่สองหากเงื่อนไขจะโยนสิ่งที่ไม่ได้กำหนดไว้
Mohammed Essehemy

น่าจะดีกว่าถ้าใช้ให้และประกาศตัวแปรสองตัวนอกบล็อกแทนที่จะใช้ 'var' และหลีกเลี่ยงขอบเขตบล็อกทั้งหมด
Vorcan

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

11
จำเป็นต้องชี้ให้เห็นว่าrequire()ไม่ใช่ส่วนหนึ่งของ JavaScript มาตรฐาน - เป็นฟังก์ชันในตัวใน Node.js ดังนั้นจึงมีประโยชน์เฉพาะในสภาพแวดล้อมนั้น OP ไม่มีข้อบ่งชี้ในการทำงานกับ Node.js
Velojet

55

คุณไม่สามารถนำเข้าแบบมีเงื่อนไข แต่คุณสามารถทำสิ่งตรงข้ามได้: ส่งออกบางสิ่งแบบมีเงื่อนไข มันขึ้นอยู่กับกรณีการใช้งานของคุณดังนั้นสิ่งนี้อาจไม่เหมาะกับคุณ

คุณทำได้:

api.js

import mockAPI from './mockAPI'
import realAPI from './realAPI'

const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI

apiConsumer.js

import API from './api'
...

ฉันใช้สิ่งนั้นเพื่อเยาะเย้ยการวิเคราะห์ libs เช่นมิกซ์พาเนล ฯลฯ ... เนื่องจากฉันไม่สามารถมีหลายบิลด์หรือส่วนหน้าของเราในขณะนี้ ไม่ใช่แบบหรูหราที่สุด แต่ใช้ได้ผล ฉันมีไม่กี่ 'ถ้า' ที่นี่และที่นั่นขึ้นอยู่กับสภาพแวดล้อมเพราะในกรณีของ mixpanel ก็ต้องเริ่มต้น


40
ฉันคิดว่าวิธีแก้ปัญหานี้ทำให้โมดูลที่ไม่ต้องการโหลดดังนั้นจึงไม่ใช่ทางออกที่ดีที่สุด
ismailarilik

5
ตามที่ระบุไว้ในคำตอบนี้เป็นการแก้ไข ในเวลานั้นก็ไม่มีวิธีแก้ปัญหา การนำเข้า ES6 ไม่ใช่แบบไดนามิกนี่คือจากการออกแบบ ข้อเสนอฟังก์ชั่นการนำเข้าแบบไดนามิก ES6 ซึ่งอธิบายไว้ในคำตอบที่ยอมรับในปัจจุบันสามารถทำได้ JS กำลังพัฒนา :)
Kev

9

ดูเหมือนว่าคำตอบคือ ณ ตอนนี้คุณทำไม่ได้

http://exploringjs.com/es6/ch_modules.html#sec_module-loader-api

ฉันคิดว่าเจตนาคือการเปิดใช้งานการวิเคราะห์แบบคงที่ให้มากที่สุดและโมดูลที่นำเข้าแบบมีเงื่อนไขจะทำเช่นนั้น ควรพูดถึงอีกด้วย - ฉันใช้Babelและฉันเดาว่าSystemไม่รองรับ Babel เพราะ API ตัวโหลดโมดูลไม่ได้กลายเป็นมาตรฐาน ES6


@ FelixKling ทำให้คำตอบนี้เป็นของตัวเองและฉันจะยอมรับมันอย่างมีความสุข!
ericsoco

4

require()เป็นวิธีที่จะนำเข้าโมดูลบางอย่างในเวลาทำงานและมันก็มีคุณสมบัติอย่างเท่าเทียมกันสำหรับการวิเคราะห์แบบคงที่เช่นimportถ้าใช้กับเส้นทางตัวอักษรสตริง สิ่งนี้เป็นสิ่งจำเป็นโดยผู้รวบรวมเพื่อเลือกการพึ่งพาสำหรับกลุ่ม

const defaultOne = require('path/to/component').default;
const NamedOne = require('path/to/component').theName;

สำหรับการแก้ปัญหาโมดูลแบบไดนามิกด้วยการสนับสนุนการวิเคราะห์แบบสแตติกที่สมบูรณ์ให้ทำดัชนีโมดูลแรกในตัวทำดัชนี (index.js) และอิมพอร์ตตัวทำดัชนีในโมดูลโฮสต์

// index.js
export { default as ModuleOne } from 'path/to/module/one';
export { default as ModuleTwo } from 'path/to/module/two';
export { SomeNamedModule } from 'path/to/named/module';

// host.js
import * as indexer from 'index';
const moduleName = 'ModuleOne';
const Module = require(indexer[moduleName]);

7
จำเป็นต้องชี้ให้เห็นว่าrequire()ไม่ใช่ส่วนหนึ่งของ JavaScript มาตรฐาน - เป็นฟังก์ชันในตัวใน Node.js ดังนั้นจึงมีประโยชน์เฉพาะในสภาพแวดล้อมนั้น OP ไม่มีข้อบ่งชี้ในการทำงานกับ Node.js
Velojet

2

ข้อแตกต่างที่สำคัญถ้าคุณใช้โหมด Webpack นำเข้าแบบไดนามิกeager:

if (normalCondition) {
  // this will be included to bundle, whether you use it or not
  import(...);
}

if (process.env.SOMETHING === 'true') {
  // this will not be included to bundle, if SOMETHING is not 'true'
  import(...);
}

แต่importคืนสัญญา
newguy

0

การปิดบังใน eval ใช้งานได้สำหรับฉันซ่อนจากตัววิเคราะห์แบบคงที่ ...

if (typeof __CLI__ !== 'undefined') {
  eval("require('fs');")
}

3
ใครช่วยอธิบายว่าทำไมคำตอบนี้จึงถูกลดระดับลงบ้าง มีข้อเสียจริงหรือมันเป็นเพียงปฏิกิริยาเชิงลบโดยอัตโนมัติต่อคำหลักชั่วร้าย 'eval'?
Yuri Gor

3
downvote อัตโนมัติสำหรับการใช้คำสำคัญ eval น่าเกลียด อยู่ห่าง ๆ.
Tormod Haugene

1
คุณช่วยอธิบายสิ่งที่ผิดปกติกับการใช้evalที่นี่ @TormodHaugene ได้หรือไม่?
Adam Barnes

MDN สรุปเหตุผลบางประการว่าทำไม evalไม่ควรใช้ โดยทั่วไป: หากคุณพบว่าจำเป็นต้องใช้evalคุณอาจทำผิดและควรใช้เวลาสักครู่เพื่อพิจารณาทางเลือกของคุณ อาจมีบางสถานการณ์ที่ใช้งานevalถูกต้อง แต่คุณมักจะไม่พบกับหนึ่งในสถานการณ์เหล่านั้น
Tormod Haugene

5
จำเป็นต้องชี้ให้เห็นว่าrequire()ไม่ใช่ส่วนหนึ่งของ JavaScript มาตรฐาน - เป็นฟังก์ชันในตัวใน Node.js ดังนั้นจึงมีประโยชน์เฉพาะในสภาพแวดล้อมนั้น OP ไม่มีข้อบ่งชี้ในการทำงานกับ Node.js
Velojet

0

ฉันสามารถทำได้โดยใช้ฟังก์ชันที่เรียกใช้ทันทีและต้องการคำสั่ง

const something = (() => (
  condition ? require('something') : null
))();

if(something) {
  something.doStuff();
}

5
จำเป็นต้องชี้ให้เห็นว่าrequire()ไม่ใช่ส่วนหนึ่งของ JavaScript มาตรฐาน - เป็นฟังก์ชันในตัวใน Node.js ดังนั้นจึงมีประโยชน์เฉพาะในสภาพแวดล้อมนั้น OP ไม่มีข้อบ่งชี้ในการทำงานกับ Node.js
Velojet

0

การนำเข้าแบบมีเงื่อนไขสามารถทำได้ด้วยการประกอบไปด้วยและ require() s:

const logger = DEBUG ? require('dev-logger') : require('logger');

ตัวอย่างนี้นำมาจากเอกสารที่ต้องใช้ทั่วโลกของ ES Lint: https://eslint.org/docs/rules/global-require


5
จำเป็นต้องชี้ให้เห็นว่าrequire()ไม่ใช่ส่วนหนึ่งของ JavaScript มาตรฐาน - เป็นฟังก์ชันในตัวใน Node.js ดังนั้นจึงมีประโยชน์เฉพาะในสภาพแวดล้อมนั้น OP ไม่มีข้อบ่งชี้ในการทำงานกับ Node.js
Velojet

0

ดูตัวอย่างนี้เพื่อความเข้าใจที่ชัดเจนว่าการนำเข้าแบบไดนามิกทำงานอย่างไร

ตัวอย่างการนำเข้าโมดูลแบบไดนามิก

มีความเข้าใจพื้นฐานของการนำเข้าและส่งออกโมดูล

โมดูล JavaScript Github

Javascript Modules MDN


0

ไม่ได้!

อย่างไรก็ตามการชนกับปัญหานั้นจะทำให้คุณคิดใหม่เกี่ยวกับวิธีการจัดระเบียบรหัสของคุณ

ก่อนโมดูล ES6 เรามีโมดูล CommonJS ซึ่งใช้ไวยากรณ์ () โมดูลเหล่านี้เป็น "ไดนามิก" ซึ่งหมายความว่าเราสามารถนำเข้าโมดูลใหม่ตามเงื่อนไขในรหัสของเรา - แหล่งที่มา: https://bitsofco.de/what-is-tree-shaking/

ฉันเดาว่าสาเหตุหนึ่งที่พวกเขาทิ้งการสนับสนุน ES6 เป็นต้นไปนั้นเป็นความจริงที่ว่าการรวบรวมมันจะยากหรือเป็นไปไม่ได้

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