Форматы: var, this, window, umd, commonjs, module, amd

Формат var — один из самых старых способов экспорта библиотеки в Webpack. Он создаёт глобальную переменную, доступную после подключения бандла в браузере.

Конфигурация

module.exports = {
  output: {
    filename: 'library.js',
    library: 'MyLibrary',
    libraryTarget: 'var',
  },
};

Результат сборки

После подключения файла:

<script src="library.js"></script>

в глобальной области появится переменная:

MyLibrary

Как работает

Webpack создаёт обычную переменную:

var MyLibrary = ...

Если библиотека экспортирует объект:

export default {
  sum(a, b) {
    return a + b;
  },
};

то использование выглядит так:

MyLibrary.sum(1, 2);

Особенности

  • работает только в браузере;
  • требует глобальной области видимости;
  • может конфликтовать с другими переменными;
  • подходит для старых проектов и CDN-подключений.

Ограничения

var не изолирует библиотеку. Если переменная уже существует:

var MyLibrary = {};

то новое определение может её перезаписать.

Также формат не поддерживает модульные системы:

  • CommonJS;
  • AMD;
  • ES Modules.

Формат this

Формат this экспортирует библиотеку как свойство объекта this.

Конфигурация

module.exports = {
  output: {
    filename: 'library.js',
    library: 'MyLibrary',
    libraryTarget: 'this',
  },
};

Что создаёт Webpack

this["MyLibrary"] = ...

Поведение

Значение this зависит от среды выполнения.

В браузере вне strict mode:

this === window;

Поэтому библиотека становится доступной как:

window.MyLibrary

Проблемы

В strict mode:

"use strict";

значение this может быть undefined.

В ES-модулях верхнеуровневый this также равен undefined.

Из-за этого формат считается ненадёжным для универсальных библиотек.

Когда используется

Иногда применяется:

  • в старом браузерном коде;
  • при совместимости со старыми сборками;
  • в legacy-средах.

Формат window

Формат window экспортирует библиотеку напрямую в объект window.

Конфигурация

module.exports = {
  output: {
    filename: 'library.js',
    library: 'MyLibrary',
    libraryTarget: 'window',
  },
};

Результат

Webpack генерирует:

window["MyLibrary"] = ...

Использование

<script src="library.js"></script>
window.MyLibrary

или:

MyLibrary

Отличия от var

var:

var MyLibrary = ...

window:

window.MyLibrary = ...

Главное отличие связано с поведением глобальных переменных.

Пример

window.Test = 123;

console.log(Test);

в браузере работает, потому что свойства window становятся глобальными переменными.

Но:

delete window.Test;

может удалить свойство, тогда как var ведёт себя иначе.

Ограничения

Формат работает только в браузере.

В Node.js объект window отсутствует:

ReferenceError: window is not defined

Формат global

Хотя формат global используется реже, он важен для серверной среды.

Конфигурация

module.exports = {
  output: {
    library: 'MyLibrary',
    libraryTarget: 'global',
  },
};

Поведение

Webpack создаёт:

global["MyLibrary"] = ...

Среда выполнения

Подходит для:

  • Node.js;
  • серверных приложений;
  • некоторых SSR-решений.

Ограничения

Не работает в браузере без полифиллов.


Формат commonjs

commonjs предназначен для CommonJS-модулей.

Конфигурация

module.exports = {
  output: {
    libraryTarget: 'commonjs',
  },
};

Что генерируется

exports["default"] = ...

или:

module.exports = ...

в зависимости от структуры экспорта.

Использование в Node.js

const lib = require('./library');

Особенности

CommonJS использует:

  • require;
  • module.exports;
  • exports.

Пример библиотеки

export function sum(a, b) {
  return a + b;
}

После сборки:

const lib = require('./library');

lib.sum(1, 2);

Когда используется

Формат долгое время был стандартом Node.js.

Подходит для:

  • серверных библиотек;
  • CLI-инструментов;
  • старых npm-пакетов.

Недостатки

CommonJS плохо сочетается с tree shaking.

Причина — динамический механизм импорта:

require(variable);

Webpack не всегда может статически определить зависимости.


Формат commonjs2

commonjs2 — наиболее распространённый формат для Node.js-библиотек.

Конфигурация

module.exports = {
  output: {
    libraryTarget: 'commonjs2',
  },
};

Генерируемый код

module.exports = ...

Отличие от commonjs

commonjs:

exports["name"] = value;

commonjs2:

module.exports = value;

Практическая разница

При commonjs2 импорт обычно проще:

const lib = require('./library');

без необходимости обращаться к .default.

Пример

Исходный код:

export default function () {
  return 'hello';
}

Использование:

const hello = require('./library');

hello();

Где применяется

Почти все старые Node.js-библиотеки используют:

  • commonjs2;
  • либо UMD.

Формат amd

AMD — Asynchronous Module Definition.

Формат создавался для браузеров до появления ES Modules.

Конфигурация

module.exports = {
  output: {
    library: 'MyLibrary',
    libraryTarget: 'amd',
  },
};

Что генерируется

define("MyLibrary", [], function () {
  return ...
});

Использование

Требуется AMD-загрузчик:

  • RequireJS;
  • Dojo;
  • curl.js.

Пример:

require(['MyLibrary'], function (lib) {
  lib.sum(1, 2);
});

Особенности

AMD поддерживает:

  • асинхронную загрузку;
  • разделение модулей;
  • браузерную среду.

Недостатки

Система стала устаревшей после появления:

  • Webpack;
  • Rollup;
  • ES Modules.

Сегодня AMD встречается в legacy-проектах.


Формат umd

UMD — Universal Module Definition.

Один из самых важных форматов библиотек.

Идея UMD

UMD пытается работать одновременно:

  • в браузере;
  • в CommonJS;
  • в AMD;
  • как глобальная переменная.

Конфигурация

module.exports = {
  output: {
    filename: 'library.js',
    library: 'MyLibrary',
    libraryTarget: 'umd',
  },
};

Что создаёт Webpack

Webpack генерирует сложную обёртку:

(function (root, factory) {
  if (typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if (typeof define === 'function' && define.amd)
    define([], factory);
  else if (typeof exports === 'object')
    exports["MyLibrary"] = factory();
  else
    root["MyLibrary"] = factory();
})(self, function () {
  return ...
});

Преимущества

UMD поддерживает почти все среды:

Среда Поддержка
Browser globals Да
CommonJS Да
AMD Да
Node.js Да

Использование

В браузере:

<script src="library.js"></script>

В Node.js:

const lib = require('./library');

В AMD:

require(['MyLibrary'], function (lib) {

});

umdNamedDefine

Дополнительная настройка:

output: {
  library: 'MyLibrary',
  libraryTarget: 'umd',
  umdNamedDefine: true,
}

Позволяет задавать имя AMD-модуля:

define('MyLibrary', [], factory);

Без неё модуль может быть анонимным.

Минусы UMD

UMD создаёт:

  • более крупный бандл;
  • сложную обёртку;
  • legacy-совместимость.

В современных проектах всё чаще заменяется ES Modules.


Формат module

Формат module генерирует настоящий ES Module.

Конфигурация

module.exports = {
  experiments: {
    outputModule: true,
  },

  output: {
    libraryTarget: 'module',
  },
};

Важная особенность

Без:

experiments.outputModule = true

Webpack выдаст ошибку.

Генерируемый код

Webpack создаёт:

export
import

вместо:

module.exports

Использование

import lib from './library.js';

Преимущества ES Modules

Tree shaking

Webpack может удалять неиспользуемый код:

import { sum } from 'lib';

если:

multiply()

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

Статический анализ

ESM имеет фиксированную структуру:

import ...
export ...

Это улучшает:

  • оптимизацию;
  • анализ зависимостей;
  • lazy loading.

Современные браузеры

Поддержка:

<script type="module">

Ограничения

Нельзя использовать как обычный <script>

Требуется:

<script type="module" src="library.js"></script>

Старые браузеры не поддерживаются

Например:

  • Internet Explorer;
  • старые Android WebView.

Особенности Node.js

Node.js требует:

  • .mjs;
  • либо "type": "module" в package.json.

Формат assign

assign экспортирует библиотеку присваиванием.

Конфигурация

output: {
  library: 'MyLibrary',
  libraryTarget: 'assign',
}

Результат

MyLibrary = ...

Особенность

Webpack не использует:

  • var;
  • window;
  • this.

Просто выполняется присваивание.

Риски

Если переменная не существует:

ReferenceError

или создание неявной глобальной переменной в нестрогом режиме.


Сравнение форматов

Формат Browser Node.js AMD ESM Глобальная переменная
var Да Нет Нет Нет Да
this Да Частично Нет Нет Да
window Да Нет Нет Нет Да
global Нет Да Нет Нет Да
commonjs Нет Да Нет Нет Нет
commonjs2 Нет Да Нет Нет Нет
amd Да Нет Да Нет Нет
umd Да Да Да Нет Да
module Да Да Нет Да Нет

output.library

Параметр library задаёт имя экспортируемой библиотеки.

Пример

output: {
  library: 'Utils',
  libraryTarget: 'window',
}

Результат:

window.Utils

Без library

Некоторые форматы могут работать без имени:

libraryTarget: 'commonjs2'

потому что экспорт идёт через:

module.exports

Современный синтаксис output.library.type

Начиная с Webpack 5 рекомендуется использовать новый формат.

Старый синтаксис

output: {
  library: 'MyLibrary',
  libraryTarget: 'umd',
}

Новый синтаксис

output: {
  library: {
    name: 'MyLibrary',
    type: 'umd',
  },
}

Преимущества

Новый API:

  • более расширяемый;
  • лучше типизирован;
  • поддерживает дополнительные настройки.

Форматы и npm-пакеты

Современные библиотеки часто публикуют несколько сборок одновременно.

Пример структуры

{
  "main": "./dist/index.cjs.js",
  "module": "./dist/index.esm.js",
  "browser": "./dist/index.browser.js"
}

Назначение

Поле Формат
main CommonJS
module ES Modules
browser Browser build

Комбинирование нескольких сборок

Webpack позволяет экспортировать массив конфигураций.

Пример

module.exports = [
  {
    output: {
      filename: 'library.umd.js',
      library: {
        name: 'MyLibrary',
        type: 'umd',
      },
    },
  },

  {
    experiments: {
      outputModule: true,
    },

    output: {
      filename: 'library.esm.js',
      library: {
        type: 'module',
      },
    },
  },
];

Практический результат

Получаются:

  • универсальная UMD-сборка;
  • современная ESM-сборка.

Именно такой подход используется во многих крупных библиотеках.