ส่งตัวเลือกไปยังการนำเข้าโมดูล ES6


144

เป็นไปได้ไหมที่จะส่งตัวเลือกต่าง ๆ ไปยังการนำเข้า ES6

คุณแปลสิ่งนี้ได้อย่างไร:

var x = require('module')(someoptions);

ถึง ES6?


ไม่แน่ใจว่าคุณทำได้หรือไม่มีโมดูลตัวโหลดเดอร์ API หรืออย่างน้อยก็มีบางครั้งที่ใช้บางอย่างเช่นSystem.import(module)ไม่แน่ใจว่าอนุญาตให้มีการขัดแย้งหรือไม่ใครที่รู้เกี่ยวกับ ES6 อาจทำอะไรบ้าง
adeneo

มีวิธีแก้ไขปัญหาที่เสนอสำหรับสิ่งนี้ซึ่งมีการนำไปใช้งานแล้วใน node.js (ผ่านทางปลั๊กอิน) และwebpack
Matt Browne

คำตอบ:


104

ไม่มีวิธีในการทำเช่นนี้โดยใช้importคำสั่งเดียวไม่อนุญาตให้ทำการเรียกใช้

ดังนั้นคุณจะไม่เรียกมันว่าโดยตรง แต่โดยทั่วไปคุณสามารถทำสิ่งเดียวกันกับสิ่งที่ commonjs ทำกับการส่งออกเริ่มต้น:

// module.js
export default function(options) {
    return {
        // actual module
    }
}

// main.js
import m from 'module';
var x = m(someoptions);

อีกทางหนึ่งถ้าคุณใช้ตัวโหลดโมดูลที่รองรับสัญญาแบบ monadicคุณอาจทำสิ่งที่ต้องการได้

System.import('module').ap(someoptions).then(function(x) {
    
});

กับimportผู้ประกอบการใหม่มันอาจกลายเป็น

const promise = import('module').then(m => m(someoptions));

หรือ

const x = (await import('module'))(someoptions)

อย่างไรก็ตามคุณอาจไม่ต้องการนำเข้าแบบไดนามิก แต่เป็นแบบคงที่


7
ขอบคุณฉันหวังว่าจะมีบางอย่างเหมือนimport x from 'module' use someoptions;ไวยากรณ์ของมัน
Fabrizio Giordano

1
@ Fabrizio: ถ้าคุณคิดเกี่ยวกับมันต่อไปมันจะไม่เป็นประโยชน์จริงๆ มันจะทำงานถ้าโมดูลส่งออกฟังก์ชั่นและอาจจะไม่ได้รับอนุญาตถ้าเราได้ตั้งชื่อการนำเข้า (เช่นimport {x, y} from 'module') แล้วไวยากรณ์ควรเป็นอย่างไรถ้าฉันต้องการผ่านอาร์กิวเมนต์หลาย ๆ หรือกระจายอาเรย์ของข้อโต้แย้ง? เป็นกรณีการใช้งานที่แคบและโดยทั่วไปคุณกำลังพยายามเพิ่มไวยากรณ์ที่แตกต่างกันสำหรับการเรียกใช้ฟังก์ชัน แต่เรามีการเรียกใช้ฟังก์ชันที่ทำให้เราสามารถจัดการกับกรณีอื่นทั้งหมดได้แล้ว
เฟลิกซ์คลิง

3
@ FelixKling ฉันเห็นด้วยกับคุณอย่างสมบูรณ์ ฉันกำลังแปลงเว็บแอปด่วนแบบเก่าและฉันพบว่าvar session = require('express-session'); var RedisStore = require('connect-redis')(session);ฉันแค่สงสัยว่ามีวิธีแก้ปัญหาหนึ่งบรรทัดหรือไม่ ฉันสามารถอยู่รอดได้อย่างสมบูรณ์ด้วยการแบ่งการมอบหมาย RedisStore เป็น 2 บรรทัด :)
Fabrizio Giordano

@ FabrizioGiordano: ฉันสามารถจินตนาการบางอย่างเช่นimport {default(someoptions) as x} from 'module'ใน ES7 หากมีความต้องการจริงๆ
Bergi

2
สำหรับsession/ ตัวอย่างเช่นผมได้รับการจินตนาการไวยากรณ์เช่นนี้connect-redis import session from 'express-session'); import RedisStore(session) from 'connect-redis'
Jeff Handley

24

แนวคิด

นี่คือทางออกของฉันโดยใช้ ES6

สอดคล้องมากกับการตอบสนองของ @ Bergi นี่คือ "เทมเพลต" ที่ฉันใช้เมื่อสร้างการนำเข้าที่ต้องผ่านพารามิเตอร์เพื่อclassการประกาศ สิ่งนี้ถูกใช้ในเฟรมเวิร์ก isomorphic ที่ฉันกำลังเขียนดังนั้นจะทำงานกับ transpiler ในเบราว์เซอร์และใน node.js (ฉันใช้BabelกับWebpack):

./MyClass.js

export default (Param1, Param2) => class MyClass {
    constructor(){
        console.log( Param1 );
    }
}

./main.js

import MyClassFactory from './MyClass.js';

let MyClass = MyClassFactory('foo', 'bar');

let myInstance = new MyClass();

ข้างต้นจะส่งออกfooในคอนโซล

แก้ไข

ตัวอย่างโลกแห่งความจริง

สำหรับตัวอย่างโลกแห่งความจริงฉันใช้สิ่งนี้เพื่อส่งผ่านในเนมสเปซสำหรับการเข้าถึงคลาสและอินสแตนซ์อื่น ๆ ภายในกรอบงาน เพราะเราเพียงแค่สร้างฟังก์ชั่นและส่งผ่านวัตถุเป็นอาร์กิวเมนต์เราสามารถใช้กับการประกาศคลาสของเรา likeo:

export default (UIFramework) => class MyView extends UIFramework.Type.View {
    getModels() {
        // ...
        UIFramework.Models.getModelsForView( this._models );
        // ...
    }
}

การนำเข้าค่อนข้างซับซ้อนและautomagicalในกรณีของฉันเนื่องจากเป็นกรอบทั้งหมด แต่โดยพื้นฐานแล้วนี่คือสิ่งที่เกิดขึ้น:

// ...
getView( viewName ){
    //...
    const ViewFactory = require(viewFileLoc);
    const View = ViewFactory(this);
    return new View();
}
// ...

ฉันหวังว่านี่จะช่วยได้!


เนื่องจากโมดูลที่อิมพอร์ตทั้งหมดของคุณเป็นคลาสทำไมไม่ผ่านพารามิเตอร์เมื่อสร้างอินสแตนซ์ของคลาส?
jasonszhao

1
@jasonszhao สิ่งที่สำคัญที่สุดที่ควรทราบที่นี่คือคลาสจะMyViewขยายรายการบางรายการที่มีอยู่ในเนมสเปซของกรอบงาน ในขณะที่เป็นไปได้อย่างแน่นอนที่จะส่งผ่านมันเป็นพารามิเตอร์ให้กับคลาส แต่ก็ขึ้นอยู่กับเวลาและสถานที่ที่อินสแตนซ์ถูกสร้างขึ้น พกพาได้รับผลกระทบแล้ว ในทางปฏิบัติชั้นเรียนเหล่านี้อาจถูกส่งมอบให้กับกรอบงานอื่น ๆ ที่อาจยกตัวอย่างพวกเขาที่แตกต่างกัน (เช่นองค์ประกอบ React ที่กำหนดเอง) เมื่อคลาสพบว่าตัวเองอยู่นอกขอบเขตของเฟรมเวิร์กมันยังคงสามารถเข้าถึงเฟรมเวิร์กได้เมื่ออินสแตนซ์เนื่องจากวิธีการนี้
หมุน

@Swivel โปรดช่วยฉันต้องการความช่วยเหลือเกี่ยวกับปัญหาที่คล้ายกัน: stackoverflow.com/questions/55214957/…
TSR


4

ฉันเชื่อว่าคุณสามารถใช้โมดูลตัก ES6 http://babeljs.io/docs/learn-es6/

System.import("lib/math").then(function(m) {
  m(youroptionshere);
});

3
แต่ผลลัพธ์ของการm(youroptionshere)สิ้นสุดอยู่ที่ไหน ฉันคิดว่าคุณสามารถเขียนSystem.import('lib/math').then(m => m(options)).then(module => { /* code using module here */})... แต่มันไม่ชัดเจน
Stijn de Witt

2
ว้าวฉันไม่อยากจะเชื่อเลยว่ามันไม่มีวิธีที่ยอดเยี่ยมในการทำเช่นนี้ใน E6 นั่นคือวิธีที่ฉันเขียนโมดุลส่วนใหญ่
Robert Moskal

3

คุณต้องเพิ่ม 2 บรรทัดนี้

import xModule from 'module';
const x = xModule('someOptions');

1
นั่นเป็นเพียงการส่งพารามิเตอร์ไปยังฟังก์ชันที่คุณนำเข้าและกำลังโทร มันไม่ได้ผ่านตัวเลือกใด ๆไปยังโมดูลที่คุณนำเข้าได้จาก xModuleกำลังทำให้เข้าใจผิดที่นี่ import func from 'module'; func('someOptions');สิ่งที่คุณจะมีคือ
Dan Dascalescu

1

ฉันได้อ่านบทความนี้แล้วดูคล้ายกันบ้างและต้องการเสนอวิธีการแก้ปัญหาอย่างน้อยในบางกรณี (แต่ดูหมายเหตุด้านล่าง)

ใช้กรณี

ฉันมีโมดูลที่ใช้ตรรกะการสร้างอินสแตนซ์บางอย่างทันทีที่โหลด ฉันไม่ชอบเรียกตรรกะเริ่มต้นนี้นอกโมดูล (ซึ่งเหมือนกับการโทรnew SomeClass(p1, p2)หรือnew ((p1, p2) => class SomeClass { ... p1 ... p2 ... })เหมือนกัน)

ฉันชอบที่ตรรกะเริ่มต้นนี้จะทำงานครั้งเดียวชนิดของการสร้างอินสแตนซ์เอกพจน์แต่หนึ่งครั้งต่อบริบทที่ จำกัด เฉพาะบางอย่าง

ตัวอย่าง

service.js มีขอบเขตพื้นฐานมาก:

let context = null;                  // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));

โมดูล A ทำ:

import * as S from 'service.js';     // console has now "initialized in context root"

โมดูล B ทำ:

import * as S from 'service.js';     // console stays unchanged! module's script runs only once

จนถึงตอนนี้ดีมาก: บริการพร้อมใช้งานสำหรับโมดูลทั้งสอง แต่เริ่มต้นได้เพียงครั้งเดียว

ปัญหา

จะทำให้มันทำงานเป็นอินสแตนซ์อื่นและเริ่มต้นอีกครั้งในบริบทอื่นได้อย่างไรพูดในโมดูล C?

สารละลาย?

นี่คือสิ่งที่ฉันคิด: ใช้พารามิเตอร์การสืบค้น ในบริการเราจะเพิ่มสิ่งต่อไปนี้:

let context = new URL(import.meta.url).searchParams.get('context');

โมดูล C จะทำ:

import * as S from 'service.js?context=special';

โมดูลจะถูกนำเข้าอีกครั้งมันเป็นตรรกะเริ่มต้นพื้นฐานที่จะทำงานและเราจะเห็นในคอนโซล:

initialized in context special

หมายเหตุ: ฉันขอแนะนำตัวเองว่าอย่าฝึกวิธีนี้มากนัก แต่ให้เป็นทางเลือกสุดท้าย ทำไม? โมดูลที่นำเข้ามากกว่าหนึ่งครั้งเป็นข้อยกเว้นมากกว่ากฎดังนั้นจึงเป็นพฤติกรรมที่ไม่คาดคิดและอาจทำให้ผู้บริโภคเกิดความสับสนหรือทำให้แตกเป็นกระบวน


0

นี่คือคำถามของฉันในการใช้โมดูล debug เป็นตัวอย่าง;

ในหน้า npm ของโมดูลนี้คุณมี:

var debug = ต้องการ ('debug') ('http')

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


นำเข้า {debug เป็น Debug} จาก 'debug' const debug = Debug ('http');


หวังว่านี่จะช่วยให้ใครบางคนที่นั่น


ทำไมโพสต์คำตอบที่ซ้ำกับที่โพสต์ไปแล้ว ?
Dan Dascalescu

1
ความผิดฉันเอง. ไม่เคยเห็นโพสต์ดังกล่าว เพียงแค่ดูที่คำถามและแทงมัน ขอขอบคุณที่แจ้งให้ทราบ
Akinwale Folorunsho Habib

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