Модули — это способ организации и структурирования кода в приложениях JavaScript. Помимо улучшения удобства сопровождения, организации кода и инкапсуляции, они предоставляют механизм разделения кода на более мелкие повторно используемые блоки.

Раньше JavaScript не поддерживал модули. Для достижения модульности разработчики использовали множество шаблонов и библиотек. Однако с введением ES2015 в JavaScript была добавлена ​​собственная поддержка модулей, обеспечивающая стандартизированный способ работы с модулями. В JavaScript есть несколько типов модулей, каждый со своим синтаксисом и системой модулей.

Модули JavaScript решают распространенные проблемы, такие как управление зависимостями, предотвращение конфликтов имен и содействие повторному использованию кода. Они позволяют разбивать большие приложения на более мелкие, автономные части, каждая из которых отвечает за определенную функциональность.

Типы модулей JavaScript

  1. CJS (Общий JS)
  2. AMD (определение асинхронного модуля)
  3. UMD (Универсальное определение модуля)
  4. ESM (модуль ECMAScript)

CJS (Общий JS)

CommonJS — это модульная система и набор спецификаций для организации и структурирования кода JavaScript в серверных средах, особенно в экосистеме Node.js. Он был создан для решения проблемы отсутствия встроенной поддержки модулей в JavaScript до появления модулей ECMAScript 6 (ES6).

Основная цель CommonJS — предоставить стандартизированный способ определения, импорта и экспорта модулей, позволяющий разработчикам писать модульный и повторно используемый код. Модули CommonJS предназначены для синхронной работы, что хорошо согласуется с серверными средами, где доступ к файлам относительно быстр и часто блокируются операции ввода-вывода.

Характеристики ЗАО:

  1. Синхронная загрузка: модули в CommonJS загружаются синхронно. Когда модуль импортируется с помощью функции require(), загрузчик модуля блокирует выполнение до тех пор, пока требуемый модуль не будет загружен и оценен.
  2. require() и module.exports: модули CommonJS используют функцию require() для импорта других модулей и объект module.exports для экспорта значений из модуля. Функция require() принимает в качестве аргумента идентификатор модуля и возвращает экспортированные значения.
  3. Один экспорт: модуль CommonJS может иметь только один экспорт по умолчанию. Экспортируемое значение может быть объектом, функцией или любым другим допустимым объектом JavaScript. Во время импорта вы можете получить доступ к экспорту по умолчанию без указания имени.
  4. Изменяемый экспорт. Экспорт CommonJS является изменяемым, что означает, что экспортируемый объект можно изменить даже после загрузки модуля. Изменения, внесенные в экспортируемый объект в одном модуле, отразятся во всех модулях, которые его импортируют.
  5. Кэширование. Загрузчики модулей CommonJS обычно реализуют кэширование для повышения производительности. После загрузки и оценки модуля его экспорт и другая важная информация сохраняются в кэше. Последующие запросы к тому же модулю разрешаются из кеша вместо его повторной оценки.

В экосистеме Node.js, где преобладает разработка на JavaScript, широко используются модули CommonJS. По мере того, как модули ECMAScript 6 и сборщики модулей, такие как Webpack, становились все более популярными, использование модулей CommonJS постепенно сокращалось в пользу более стандартизированных и универсальных модулей ES.

// math.js
function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

module.exports = {
  add: add,
  subtract: subtract
};
// greet.js
const math = require('./math');

function welcome(name) {
  const sum = math.add(2, 3);
  const sub = math.subtract(5, 2);
  console.log(`Hello, ${name}! Sum: ${sum}, Sub: ${sub}`);
}

module.exports = {
  welcome: welcome
};
// main.js
const greet = require('./greet');

greet.welcome('Sina');
Run the following command in the terminal
> node main.js

AMD (определение асинхронного модуля)

AMD (определение асинхронного модуля) — это модульная система и спецификация для организации и загрузки модулей JavaScript в средах веб-браузера. Он был разработан для удовлетворения потребности в асинхронной загрузке и управлении зависимостями во внешних приложениях, где синхронная загрузка модулей может привести к проблемам с производительностью.

Основная цель AMD — предоставить стандартизированный способ определения, импорта и экспорта модулей, особенно в сценариях, когда модули загружаются асинхронно. Модули AMD хорошо подходят для браузерных приложений, в которых сетевые запросы и операции ввода-вывода не блокируются и могут выполняться одновременно.

Характеристики AMD

  1. Асинхронная загрузка: модули AMD поддерживают асинхронную загрузку, что позволяет загружать и оценивать модули без блокировки. Это особенно полезно для браузерных приложений, где синхронная загрузка модулей может вызвать проблемы с производительностью.
  2. define() и require(): модули AMD используют функцию define() для определения и объявления модулей. Функция define() принимает в качестве аргументов зависимости и фабричную функцию. Фабричная функция определяет функциональность модуля и возвращает экспортированные значения. Функция require() используется для импорта и использования модулей в других модулях.
  3. Объявление зависимостей: модули AMD явно объявляют свои зависимости в виде массива идентификаторов модулей в функции define(). Перед выполнением модуля эти зависимости разрешаются и загружаются асинхронно. Затем фабричная функция получает разрешенные зависимости в качестве аргументов.
  4. Именованные экспорты: модули AMD могут иметь несколько именованных экспортов. Экспортируемые значения определяются в фабричной функции и возвращаются как объект, содержащий именованные экспорты.
  5. Универсальная совместимость. Модули AMD предназначены для работы в различных средах JavaScript, включая как браузеры, так и серверные среды, такие как Node.js. Загрузчики AMD могут обнаруживать наличие поддержки AMD и соответствующим образом корректировать свое поведение. Если поддержка AMD недоступна, для обеспечения совместимости можно использовать резервные механизмы.
  6. Загрузчики AMD: модулям AMD требуется загрузчик AMD для обработки загрузки модуля и процесса разрешения зависимостей. К популярным загрузчикам AMD относятся RequireJS и SystemJS, которые предоставляют необходимые функции для загрузки и выполнения модулей AMD в браузере.

Модули AMD широко используются в сообществе разработчиков интерфейсов, особенно при работе с крупномасштабными приложениями JavaScript, требующими динамической загрузки и управления зависимостями. Однако с введением собственных модулей ES и поддержкой современных сборщиков использование модулей AMD несколько уменьшилось.

// math.js
define('math', [] , function () {
    function add(a, b) {
        return a + b;
    }

    function subtract(a, b) {
        return a - b;
    }

    return {
        add: add,
        subtract: subtract
    };
});
// greet.js
define('greet', ['math'], function (math) {
    function welcome(name) {
        var sum = math.add(2, 3);
        var sub = math.subtract(5, 2);
        console.log(`Hello, ${name}! Sum: ${sum}, Sub: ${sub}`);
    }

    return {
        welcome: welcome
    };
});
// main.js
require(['greet'], function (greet) {
    greet.welcome('Sina');
});
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AMD Sample</title>
</head>
<body>
    <h1>AMD Sample</h1>
    
    <!-- AMD loader must be loaded before any script -->
    <script src="https://requirejs.org/docs/release/2.3.6/minified/require.js"></script>
    <script src="./main.js"></script>
</body>
</html>
Open the index.html file in the browser

UMD (Универсальное определение модуля)

UMD (универсальное определение модуля) — это не модульная система, а шаблон или подход к разработке модулей JavaScript, которые работают в разных модульных системах и средах. Модули UMD предназначены для поддержки сред CommonJS, AMD и глобальных переменных. Это позволяет использовать модули в различных средах JavaScript, не полагаясь на определенную модульную систему.

Основная цель UMD — создавать модули, которые могут без проблем работать в разных средах JavaScript, и разработчикам не нужно переписывать код специально для каждой среды. Это позволяет повторно использовать код и обеспечивать совместимость между многомодульными системами.

Характеристики УМД:

  1. Универсальная совместимость. Модули UMD предназначены для работы в различных средах JavaScript, включая как браузеры, так и серверные среды, такие как Node.js. Они могут адаптироваться к наличию различных модульных систем и соответствующим образом корректировать свое поведение.
  2. Проверить доступность модульной системы. Модули UMD обычно проверяют доступность различных модульных систем (например, CommonJS, AMD), проверяя глобальные объекты или определенные функции. Делая это, модули UMD могут определять, какую модульную систему использовать для загрузки и экспорта модулей.
  3. Поддержка CommonJS: модули UMD часто обеспечивают поддержку сред CommonJS, проверяя, доступны ли объекты module и exports. Если они существуют, экспорт модуля назначается объекту module.exports.
  4. Поддержка AMD: модули UMD также могут поддерживать среды AMD, проверяя, доступна ли функция define. Если это так, модуль определяется с использованием шаблона AMD с указанием зависимостей и фабричной функции.
  5. Возврат к глобальной переменной. В случае, если ни среда CommonJS, ни среда AMD не обнаружены, модули UMD возвращаются к предоставлению своего экспорта через глобальный объект (window в браузерах или global в Node.js). Это позволяет получить доступ к модулю через глобальную переменную.

Используя модули UMD, вы можете писать модули, которые можно использовать в разных средах JavaScript, обеспечивая совместимость между несколькими модульными системами. Однако современная разработка JavaScript отошла от использования модулей UMD из-за широкого распространения собственных модулей ES и доступности сборщиков модулей.

// math.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define('math', [], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory();
    } else {
        root.math = factory();
    }
})(this, function () {
    function add(a, b) {
        return a + b;
    }

    function subtract(a, b) {
        return a - b;
    }

    return {
        add: add,
        subtract: subtract
    };
});
// greet.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define('greet', ['math'], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory(require('./math'));
    } else {
        root.greet = factory(root.math);
    }
})(this, function (math) {
    function welcome(name) {
        var sum = math.add(2, 3);
        var sub = math.subtract(5, 2);
        console.log(`Hello, ${name}! Sum: ${sum}, Sub: ${sub}`);
    }

    return {
        welcome: welcome
    };
});
// main.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        require(['greet'], factory);
    } else if (typeof module === 'object' && module.exports) {
        factory(require('./greet'));
    } else {
        root.main = factory(root.greet);
    }
})(this, function (greet) {
    greet.welcome('Sina');
});
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>UMD Sample</title>
</head>
<body>
    <h1>UMD Sample</h1>

    <!-- AMD loader must be loaded before any script -->
    <script src="https://requirejs.org/docs/release/2.3.6/minified/require.js"></script>
    <script src="./main.js"></script>
</body>
</html>
1. Run the following command in the terminal
> node main.js

2. Open the index.html file in the browser

ESM (модуль ECMAScript)

ESM означает модули ECMAScript, которые являются официальной системой модулей, представленной в спецификации ECMAScript 6 (ES6) (также известной как ECMAScript 2015). ESM предоставляет стандартизированный и собственный способ определения, импорта и экспорта модулей в JavaScript.

Характеристики ЭСМ:

  1. Встроенная поддержка. ESM изначально поддерживается в современных средах JavaScript, включая браузеры, Node.js и другие среды выполнения JavaScript. Эта встроенная поддержка означает, что для использования ESM не требуются дополнительные библиотеки или инструменты, что упрощает работу с ним.
  2. Область на уровне файлов: ESM работает на уровне файлов, что означает, что каждый модуль имеет свою собственную область. Переменные, функции и классы, определенные в модуле, по умолчанию являются локальными для этого модуля и автоматически не доступны за его пределами. Это помогает предотвратить загрязнение глобальной области видимости и улучшает инкапсуляцию.
  3. Явные операторы import и export: ESM использует операторы import и export для управления зависимостями модулей и экспортом. Оператор import используется для переноса функций из других модулей в текущий модуль, а оператор export используется для того, чтобы сделать значения или сущности доступными для использования в других модулях.
  4. Разрешение статического модуля: ESM использует разрешение статического модуля, что означает, что импорт и экспорт разрешаются во время синтаксического анализа модуля и до выполнения кода. В процессе сборки это обеспечивает лучшую поддержку инструментов, обнаружение ошибок и оптимизацию.
  5. Именованные экспорты и экспорты по умолчанию: ESM поддерживает как именованные экспорты, так и экспорты по умолчанию. Именованный экспорт позволяет модулям экспортировать определенные значения или объекты из модуля, чтобы использовать их соответствующие имена. Экспорт по умолчанию позволяет модулям экспортировать одно значение или объект в качестве значения по умолчанию. Благодаря такой гибкости модули можно структурировать и использовать по-разному.
  6. Асинхронная загрузка с помощью динамического импорта: ESM поддерживает асинхронную загрузку модулей посредством динамического импорта. Динамический импорт позволяет загружать и оценивать модули во время выполнения, обеспечивая отложенную загрузку и динамическое выполнение кода в зависимости от условий. Эта возможность особенно полезна для разделения кода и сокращения времени начальной загрузки приложения.
  7. Встряхивание дерева и устранение мертвого кода: ESM позволяет встряхивать дерево и устранять мертвый код в процессе сборки. Встряхивание дерева относится к процессу удаления неиспользуемых экспортов из модулей, что приводит к уменьшению размеров пакетов. В дополнение к уменьшению размера пакета и повышению производительности устранение мертвого кода устраняет части кода, которые недоступны или не используются в приложении.

Встроенная поддержка ESM, область действия на уровне файлов, явный импорт и экспорт, разрешение статического модуля и другие характеристики делают его мощной и стандартизированной модульной системой для современной разработки на JavaScript. Он способствует модульности кода, инкапсуляции и улучшенной поддержке инструментов, что позволяет разработчикам создавать масштабируемые и удобные в сопровождении приложения.

// math.mjs
export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}
// greet.mjs
import { add, subtract } from './math.mjs';

export function welcome(name) {
    const sum = add(2, 3);
    const sub = subtract(5, 2);
    console.log(`Hello, ${name}! Sum: ${sum}, Sub: ${sub}`);
}
// main.mjs
import { welcome } from './greet.mjs';

welcome('Sina');
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ESM Sample</title>
</head>
<body>
    <h1>ESM Sample</h1>

    <script type="module" src="./main.mjs"></script>
</body>
</html>
1. Run the following command in the terminal for node greater than 12.0
> node main.mjs

2. Open the index.html file in the browser

Рекомендации

  1. Что такое CJS, AMD, UMD и ESM в Javascript?
  2. Использование модулей ECMAScript (ESM) с Node.js
  3. RequireJS — определение функции
  4. Несоответствие анонимного модуля define()
  5. ПОЧЕМУ АМД?
  6. Модули ECMAScript в браузерах