JavaScript. Импорт и экспорт

07.03.2021

Теги: exportFrontendJavaScriptWeb-разработкаМодульТеория

Экспорт

Можно пометить любое объявление переменной, функция или класса как экспортируемое, разместив перед ним ключевое слово export:

// экспорт массива
export let numbers = [1, 2, 3];

// экспорт константы
export const SOME_CONST = 2015;

// экспорт класса
export class User {
  constructor(name) {
    this.name = name;
  }
}

Также можно написать export отдельно — сначала объявить, а затем экспортировать:

/*
 * файл say.js
 */
function sayHi(user) {
  alert(`Hello, ${user}!`);
}

function sayBye(user) {
  alert(`Bye, ${user}!`);
}

// список экспортируемых переменных
export {sayHi, sayBye};

Импорт

Обычно список того, что нужно импортировать, размещается в фигурных скобках {...}, например вот так:

/*
 * файл main.js
 */
import {sayHi, sayBye} from './say.js';

sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!

Но если импортировать нужно много чего — можно импортировать всё сразу в виде объекта:

/*
 * файл main.js
 */
import * as say from './say.js';

say.sayHi('John');
say.sayBye('John');

На первый взгляд «импортировать всё» выглядит очень удобно, не надо писать лишнего, зачем нам вообще может понадобиться явно перечислять список того, что нужно импортировать? Современные инструменты сборки (webpack и другие) собирают модули вместе и оптимизируют их, ускоряя загрузку и удаляя неиспользуемый код. Предположим, мы добавили в наш проект стороннюю библиотеку say.js с множеством функций:

/*
 * файл say.js
 */
export function sayHi() { ... }
export function sayBye() { ... }
export function doSomething() { ... }

Теперь, если из этой библиотеки в проекте мы используем только одну функцию:

/*
 * файл main.js
 */
import {sayHi} from './say.js';

…Тогда оптимизатор увидит, что другие функции не используются, и удалит остальные из собранного кода.

Импорт и экспорт «как»

Можно использовать ключевое слово as, чтобы импортировать под другими именами:

/*
 * файл main.js
 */
import {sayHi as hi, sayBye as bye} from './say.js';

hi('John'); // Hello, John!
bye('John'); // Bye, John!

Аналогичный синтаксис существует и для export:

/*
 * файл say.js
 */
function sayHi() { ... }
function sayBye() { ... }

export {sayHi as hi, sayBye as bye};
/*
 * файл main.js
 */
import * as say from './say.js';

say.hi('John'); // Hello, John!
say.bye('John'); // Bye, John!

Экспорт по умолчанию

На практике модули встречаются в основном одного из двух типов:

  1. Модуль, содержащий библиотеку или набор функций, как say.js в примерах выше
  2. Модуль, который объявляет что-то одно, например модуль user.js экспортирует только класс User

По большей части, удобнее второй подход, когда каждая «вещь» находится в своём собственном модуле. Для этого случая есть специальный синтаксис «экспорт по умолчанию»:

/*
 * файл user.js
 */
export default class User { // просто добавляем default
    constructor(name) {
        this.name = name;
    }
}

И потом импортируем без фигурных скобок:

/*
 * файл main.js
 */
import User from './user.js'; // не {User}, а просто User

new User('John');

Импорты без фигурных скобок выглядят красивее. Обычная ошибка начинающих: забывать про фигурные скобки. Фигурные скобки необходимы в случае именованных экспортов, для export default они не нужны.

Технически в одном модуле может быть как экспорт по умолчанию, так и именованные экспорты, но на практике обычно их не смешивают. То есть, в модуле находятся либо именованные экспорты, либо один экспорт по умолчанию.

Так как в файле может быть максимум один export default, то экспортируемая сущность не обязана иметь имя:

/*
 * файл user.js
 */
export default class { // у класса нет имени
    constructor() {
        this.name = name;
    }
}

Это нормально, потому что может быть только один export default на файл, так что import знает, что импортировать:

/*
 * файл main.js
 */
import User from './user.js';

new User('John');

Имя «default»

В некоторых ситуациях для обозначения экспорта по умолчанию в качестве имени используется default:

/*
 * файл user.js
 */
class User {
    constructor(name) {
        this.name = name;
    }
}

// то же самое, как если добавить export default перед class
export {User as default};
/*
 * файл main.js
 */
import User from './user.js';

new User('John');

Представим еще одну ситуацию — модуль user.js экспортирует одну сущность «по умолчанию» и несколько именованных:

/*
 * файл user.js
 */
export default class User {
    constructor(name) {
        this.name = name;
    }
}

export function sayHi(user) {
  alert(`Hello, ${user}!`);
}

Вот как импортировать экспорт по умолчанию вместе с именованным экспортом:

/*
 * файл main.js
 */
import {default as User, sayHi} from './user.js';

new User('John');

Если импортировать всё как объект *, тогда свойство default — как раз и будет экспортом по умолчанию:

/*
 * файл main.js
 */
import * as user from './user.js';

let User = user.default; // экспорт по умолчанию
new User('John');

Поиск: export • import • JavaScript • Web-разработка • экспорт • импорт • Теория • Frontend

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.