Передати варіанти імпорту модуля ES6


144

Чи можливо передавати варіанти на імпорт ES6?

Як ви перекладете це:

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

до ES6?


Не впевнений, що можна, є API завантажувача модулів, або, принаймні, був якийсь час, який використовував щось на кшталт System.import(module), не впевнений, чи дозволяє це аргументи чи ні, мабуть, хтось, хто знає більше про ES6?
— adeneo

Для цього є запропоноване рішення, для якого вже є реалізація в node.js (за допомогою плагіна) та webpack: 2ality.com/2017/01/import-operator.html
— Метт Браун

Відповіді:


104

Неможливо зробити це за допомогою одного importзаяви, це не дозволяє викликати.

Таким чином, ви б не називали це безпосередньо, але ви можете в основному робити те саме, що і звичайний експорт за замовчуванням:

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

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

Крім того, якщо ви використовуєте завантажувач модулів, який підтримує монадійні обіцянки, ви можете зробити щось на кшталт

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 Я повністю з тобою згоден. Я перетворював старий експрес-webapp, і мені траплялося var session = require('express-session'); var RedisStore = require('connect-redis')(session);мені просто цікаво, чи є рішення в одній лінії. Я можу повністю вижити, розділивши завдання RedisStore на 2 рядки :)
— Fabrizio Giordano,

@FabrizioGiordano: Я міг би уявити щось подібне import {default(someoptions) as x} from 'module'в ES7, якщо в цьому дійсно є потреба.
— Бергі

2
Для session/ connect-redisнаприклад, я собі синтаксис на зразок цього: import session from 'express-session'); import RedisStore(session) from 'connect-redis'.
— Джефф Хендлі

24

Концепція

Ось моє рішення за допомогою ES6

Дуже сильно відповідає реакції @ Бергі, це "шаблон", який я використовую під час створення імпорту, для якого потрібні параметри, передані для classдекларацій. Це використовується на ізоморфних рамках , яку я пишу, так що буде працювати з 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в консолі

EDIT

Приклад реального світу

Для прикладу реального світу я використовую це для передачі в просторі імен для доступу до інших класів і екземплярів в рамках. Оскільки ми просто створюємо функцію і передаємо об’єкт в якості аргументу, ми можемо використовувати його з нашим класовим декларацією 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 */})... але це не дуже зрозуміло.
— Штійн де Вітт

2
Нічого собі не можу повірити, що в Е6 немає елегантного способу зробити це. Саме так я в основному пишу модулі.
— Роберт Москаль

3

Вам просто потрібно додати ці 2 рядки.

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

1
Це просто передача параметрів функції, яку ви імпортували та викликаєте. Це модуль не передає жодних параметрів модулю, з якого ви імпортуєте його . xModuleтут вводиться в оману. Те, що ви насправді маєте, так і є import func from 'module'; func('someOptions');.
— Дан Даскалеску

1

Я приземлився на цю нитку, шукаючи дещо подібне і хотів би запропонувати якесь рішення, принаймні для деяких випадків (але див. Зауваження нижче).

Використовуйте футляр

У мене є модуль, який виконує певну логіку екземплярів відразу після завантаження. Мені не подобається називати цю логіку init поза модулем (це те саме, що дзвінок new SomeClass(p1, p2)або new ((p1, p2) => class SomeClass { ... p1 ... p2 ... })і так).

Мені подобається, що ця init-логіка буде працювати один раз, на зразок сингулярного потоку інстанцій, але один раз у якомусь конкретному параметризованому контексті.

Приклад

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

модуль буде повторно імпортований, буде запущена основна логіка init, і ми побачимо в консолі:

initialized in context special

Зауваження: Я б радив НЕ дуже практикувати цей підхід, а залишити його як крайній захід. Чому? Модуль, що імпортується не один раз, є скоріше винятком, ніж правилом, тому це дещо несподівана поведінка, і як таке може збити з пантелику споживачів або навіть порушити власні парадигми "одиночки", якщо такі є.


0

Ось мій погляд на це питання, використовуючи модуль налагодження як приклад;

На npm-сторінці цього модуля у вас є таке:

var debug = requ ('налагодження') ('http')

У верхньому рядку рядок передається модулю, який імпортується, для побудови. Ось як би ви зробили те саме в ES6


import {debug as Debug} з 'debug' const debug = Debug ('http');


Сподіваюся, це допомагає комусь там.


Навіщо публікувати відповідь, що дублює вже опубліковану ?
— Дан Даскалеску

1
Моє ліжко. Ніколи не бачив згаданого поста. Тільки подивився на питання і взяв на нього удар. Дякуємо, що повідомили про це.
— Akinwale Folorunsho Habib

Ласкаво просимо. Ви також можете видалити дублюючу відповідь, якщо хочете.
— Дан Даскалеску
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.