Примечание. Этот пост вдохновлен этим превосходным 30-минутным видео Тайлера МакГинниса и представляет собой его резюме, и я рекомендую вам его посмотреть!
Понимание модулей Javascript - это ахиллесова пята для многих великих разработчиков. Это понятно из-за довольно нервной истории управления модулями в Javascript, а также из-за его многочисленных причуд. В этой статье мы кратко рассмотрим модули в Javascript, а также их историю и работу.
Модули и зачем они нужны
В модуль входят 3 основных компонента:
- Импорт: зависимости, необходимые для кода в модуле.
- Код: собственный код модуля.
- Экспорт: какие части себя модуль делает видимыми и используемыми для других модулей.
Есть 5 основных причин, по которым мы разбиваем код на модули:
- Возможность повторного использования. Один модуль можно использовать в нескольких местах кода.
- Возможность комбинирования. Модули можно соединять вместе несколькими способами для достижения более продвинутой функциональности.
- Использование: модули можно легко установить из репозиториев, таких как NPM, что позволяет быстро добавлять функциональные возможности в свой код.
- Изоляция: модуль содержит определенные, изолированные, что упрощает тестирование. Кроме того, это позволяет нескольким людям работать над разными модулями одновременно.
- Организация: с большими базами кода легче управлять, если код разделен на логические модули.
Мы собираемся использовать игрушечный проект и продемонстрировать на нем основные подходы, которые были приняты для управления модулями в Javascript в хронологическом порядке. «Проект» устроен следующим образом: файл data.js импортирует массив чисел. m1.js и m2.js содержат функции, которые полагаются на эти данные (мы просто распечатаем их, чтобы понять суть). Все это хранится в файле index.html.
Подход 0: импорт файлов без модулей
В традиционном Javascript мы использовали для импорта внешних файлов Javascript без каких-либо попыток создания модулей. Это показано ниже на трех файлах.
Во-первых, у нас есть наш основной файл index.html с функцией, определенной внутри, которая называется indexHTMLFunction.
Далее наши файлы javascript выглядят следующим образом:
m1Function и m2Functi on теперь доступны для использования в файле index.html, а также имеют доступ к данным. Нам удалось разбить наш код на несколько файлов, что кажется правильным шагом в направлении разделения нашего кода на модули. Однако у этого элементарного подхода есть одна ключевая проблема: на самом деле мы не создаем модули. Это как если бы мы просили компилятор просто скопировать / вставить код из файлов Javascript в index.html, что в большинстве случаев является смертным грехом. Это можно быстро проверить, открыв index.html в браузере и взглянув на объект окна.
Любая функция, определенная в импортированных файлах, напрямую встраивается в наш глобальный объект окна. Это быстро становится невозможным, поскольку наша кодовая база становится больше и конфликты имен становятся неизбежными.
Подход 1: выражения немедленного вызова функций (IIFE):
Следующим шагом на пути к нашему элементарному подходу является использование немедленно вызываемых функциональных выражений для экспорта материалов в объект, который мы определяем. Затем этот объект будет содержать все наши экспортные данные, тем самым предотвращая затопление оконного объекта. Чтобы перейти на этот подход, мы должны сделать две вещи:
- Создайте объект для хранения всех экспортируемых вещей. Мы назовем это APP.
- Оберните весь код импортированных модулей в IIFE.
Отредактированный код будет следующим:
Давайте посмотрим на результаты:
Кажется, после небольшого рефакторинга мы решили нашу проблему. Однако в этом методе все еще есть некоторые ключевые проблемы:
- Весь наш импорт помещается в один объект. Существует высокий риск конфликта имен.
- Мы должны быть осторожны с порядком. Сначала необходимо объявить объект APP.
- Мы должны завернуть все, что мы импортируем, в IIFE, что утомительно.
Подход 2: CommonJS
«То, что я здесь описываю, не является технической проблемой. Это вопрос того, чтобы люди собрались вместе и приняли решение сделать шаг вперед и вместе начать создавать что-то большее и крутое ». - Кевин Дангур, создатель CommonJS
CommonJS был впервые предложен в 2009 году для решения проблемы модуля Javascript. Это был набор спецификаций, которые развивались в течение нескольких лет и стали чрезвычайно популярными благодаря Node, который принял его парадигму как часть Node Package Manager. CommonJS решил все три проблемы IIFE, предоставив основу для использования модулей:
- Модули можно легко импортировать с помощью ключевого слова require.
- У каждого модуля есть объект module.exports, в котором он может указать свой экспорт, который, в свою очередь, может быть требоваться ’d другими модулями.
- Для этого подхода не требуется глобального пространства имен или объекта, что решает проблему конфликтов имен.
А как насчет браузеров?
Хотя кажется, что CommonJS мог бы решить все проблемы с зависимостями модулей Javascript, у него есть одна неприятная загвоздка: он полностью синхронен.
Что это обозначает? Все приостанавливается при выполнении инструкции require.
Каковы его последствия? CommonJS нельзя использовать в браузерах. Загрузка удаленного модуля приостановит поток браузера, заморозив пользовательский интерфейс.
Однако есть несколько решений, которые позволяют нам использовать спецификацию CommonJS в браузерах, таких как Webpack и RequireJS. Эти решения выходят за рамки данной статьи.
Подход 3: модули ES6
Спецификация ES6 официально представила новую систему управления модулями в Javascript, которая работает аналогично CommonJS, но работает «из коробки» в браузерах и не блокируется при импорте. Модули ES6 позволяют использовать ключевые слова с подходящими названиями import и export для определения именно этих вещей в каждом модуле. Вот последняя версия нашего игрушечного примера, теперь в ES6:
Следует отметить два важных момента:
- Вы должны указать тип как модуль в теге скрипта, чтобы это работало в браузерах.
- Вам нужно будет обслуживать index.html с сервера (например, http-сервера python), иначе вы получите CORS при использовании этого подхода в браузере.
Теперь у нас есть простая в использовании и надежная реализация модуля на Javascript, которая работает везде. Вы заметите очень четкое соответствие между кодом и тремя компонентами модуля, которые мы описали в начале. Модули ES6 также предлагают некоторые другие полезные функции, такие как экспорт по умолчанию и псевдонимы, которые вам обязательно стоит проверить, если вы с ними не знакомы.
Последние тенденции и подведение итогов
Очень редко можно увидеть простой импорт скриптов и IIFE в современном коде Javascript. CommonJS по-прежнему довольно популярен среди сообщества NodeJS. Последние версии Node упростили использование модулей ES6, и возможно, что сообщество Node перейдет на модули ES6 в будущем. С другой стороны, экосистема React использует исключительно модули ES6.
Подводя итог, можно сказать, что модули являются неотъемлемой частью хороших практик разработки программного обеспечения. С современным Javascript вы должны стремиться использовать модули CommonJS или ES6 в зависимости от среды и сообщества инструментов, с которыми вы работаете.