Статьи

Понимание модулей JavaScript: объединение и перенос

Эта статья была рецензирована Дэном Принсом и Рави Кираном . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

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

Модули широко используются в Node, но мы сосредоточимся здесь на том, как мы можем использовать модули внутри браузера. Мы рассмотрим небольшую историю, пройдемся по опасному текущему ландшафту с конечной целью — получить четкий путь вперед и оценить самые важные на сегодняшний день комплекты модулей для JavaScript: Browserify, Webpack и jspm.

Наконец, мы рассмотрим, как использовать эти инструменты с транспиляторами, такими как CoffeeScript, TypeScript и Babel.

Модули сквозь века

JavaScript существует с 1995 года, и по сей день ни один браузер не поддерживает модули изначально. Node и CommonJS были созданы в 2009 году, и подавляющее большинство пакетов в npm используют модули CommonJS.

Browserify был выпущен в 2011 году и принес в браузер модули CommonJS, что позволило клиентскому JavaScript require пакеты npm. Инструмент объединяет все необходимые зависимости в один файл JavaScript.

Прошлое

Библиотека, такая как jQuery, добавляет $ в глобальную область или window .

 window.$ = function() { ... }; 

Мы включаем скрипт в библиотеку и используем предоставляемые ею глобальные объекты.

 <script src="jquery.js"></script> <script> $(function() { ... }); </script> 

Ваш собственный код приложения, как правило, размещается в пространстве имен в глобальном подобном App чтобы предотвратить загрязнение глобальной области. Без этого только так долго у вас будут конфликты имен и все развалится.

 var App = {}; App.Models = {}; App.Models.Note = function() {}; 

Будущее

Библиотеки экспортируют объекты в едином модульном формате (модули ES6).

 export default function $() { ... } 

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

 import $ from 'jquery'; $(function() { ... }); 
  • Глобальные переменные не требуются 👍
  • Независимость от источника заказа
  • Доступ к нпм
  • Нет необходимости указывать пространство для собственного кода приложения
  • Динамическая загрузка модулей в любое время по мере необходимости

Настоящее

Это действительно очень сложно. Во-первых, существуют различные форматы модулей:

Инструменты для объединения активов бывают разных форм и размеров:

Тогда есть транспортеры, которые вы можете использовать:

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

Это сокращенные списки популярных инструментов, используемых в настоящее время — это минное поле как для начинающих, так и для экспертов. Стоимость переноса также подчеркивает, что вы можете смешивать и сочетать многие из этих инструментов и получать разные результаты.

Давайте консолидировать инструменты в 2016 году

Фронтальные разработчики уже давно используют инструменты сборки, но только в последние несколько лет мы увидели, что шаг сборки стал нормой. Такие инструменты, как Sass и CoffeeScript, помогли сделать предварительную обработку популярной, но благодаря ES6 теперь все получили поддержку.

Я согласен.

Gulp и Grunt были очень популярны в последние несколько лет, эти инструменты позволяют вам написать серию преобразований для передачи ваших активов. Они были полезны и по-прежнему популярны, хотя многие люди предпочитают использовать инструменты напрямую через npm — см. Почему я оставил Gulp and Grunt для сценариев npm и Руководство по использованию npm в качестве инструмента сборки .

Лично меня больше не волнует создание конвейеров ресурсов, я ищу минимальные инструменты настройки, которые позволяют мне использовать современные инструменты по мере необходимости: такие вещи, как Sass, Autoprefixer, Babel и Coffeescript, правильная система модулей и загрузчик без необходимости беспокоиться о реализации, конфигурации и текущем обслуживании. По сути, за последние несколько лет каждый разработчик вкладывал время в создание конвейеров активов, что требует огромного переосмысления и потраченных впустую часов.

Сообщество разделено на такие инструменты, как Browserify, Webpack, jspm, Sprockets и Gulp. Это на самом деле не проблема, это просто сбивает с толку всех, кто пытается понять четкий путь вперед.

Очистить начальные точки

Есть несколько вещей, о которых мы можем договориться:

  • Модули ES2015 — это единый формат модулей будущего для JavaScript.
  • Babel сегодня является предпочтительным компилятором ES2015.
  • В родных загрузчиках все еще нет доступа к браузерам. В отчете Telerik о будущем JavaScript говорится, что полная поддержка ES2015 может занять более двух лет, учитывая трудности с загрузкой модулей .
  • Если вы хотите использовать модули сейчас, то, скорее всего, в какой-то момент будет задействован CommonJS.

Давайте посмотрим, как выглядят настройки минимальной конфигурации с использованием Browserify, Webpack и jspm. Это наиболее важные из известных на сегодняшний день пакетов JavaScript.

Новый проект

 mkdir modules-app cd modules-app npm init -y npm install --save-dev browserify webpack jspm mkdir src touch src/{entry,lib}.js index.html 

Обновите index.html в вашем любимом текстовом редакторе.

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Modules!</title> </head> <body> <script src="bundle.js"></script> </body> </html> 

Нам также понадобится сервер для запуска кода — например, live-server, который представляет собой отличный маленький HTTP-сервер с нулевой конфигурацией и возможностью перезагрузки в реальном времени. Установите его глобально с помощью npm install -g live-server и запустите live-server из корня проекта для запуска.

Browserify

Browserify позволяет вам require('modules') в браузере, объединяя все ваши зависимости.

Откройте src/lib.js и добавьте наш самый первый модуль.

 var double = function(number) { return number * 2; } module.exports = { double: double } 

Откройте src/entry.js и нам require наш модуль и мы будем его использовать.

 var lib = require('./lib.js'); console.log(lib.double(2)); 

Обновите раздел scripts в package.json

 "scripts": { "browserify": "browserify ./src/entry.js -o ./bundle.js" }, 

Запустите этот скрипт с помощью npm run browserify

Browserify создаст bundle.js в корневом bundle.js проекта, и вы должны увидеть 4 наиболее интересных вывода на консоль. Чтобы узнать больше о том, что делает Browserify и как создается этот пакет, я рекомендую посмотреть Введение в Browserify на egghead.io

Поздравляем! Теперь у нас есть модули в браузере! 🎉

Еще одним ключевым преимуществом Browserify является то, что он дает вам доступ не только к тем модулям, которые вы разрабатываете, но и к модулям npm. Давайте рассмотрим lodash.

 npm install lodash --save-dev 

Редактировать src/lib.js

 var sum = require('lodash/sum'); var double = function(number) { return number * 2; } var addFive = function(number) { return sum([number, 5]); } module.exports = { double: double, addFive: addFive } 

Отредактируйте src/entry.js и вызовите нашу новую функцию addFive

 var lib = require('./lib.js'); console.log(lib.double(2)); console.log(lib.addFive(2)); 

Снова создайте пакет с помощью npm run browserify и в браузере вы увидите 4 и 7 которые показывают, что мы успешно импортировали и использовали функцию sum lodash.

Если вы прошли этот путь, то теперь вы знаете все, что вам нужно, чтобы начать использовать модули в браузере сегодня, это дает много преимуществ, которые мы обрисовали вначале.

  • Глобальные переменные не требуются 👍
  • Независимость от источника заказа
  • Доступ к нпм
  • Нет необходимости для пространства имен вашего собственного кода приложения

Мы рассмотрим динамическую загрузку модулей во время выполнения позже.

Webpack

Webpack — это модуль-упаковщик. Webpack принимает модули с зависимостями и генерирует статические ресурсы, представляющие эти модули.

Давайте добавим новый скрипт в package.json для вызова webpack

 "webpack": "webpack ./src/entry.js bundle.js" 

Запустите его с помощью npm run webpack

В Webpack будет переписан bundle.js и вывод в браузере должен быть точно таким же.

Попробуйте запустить npm run browserify и npm run webpack и изучить различия в скомпилированном файле bundle.js . Не очень важно понимать, как эти инструменты работают внутри, важно отметить, что, хотя реализации отличаются, они по сути выполняют одну и ту же задачу по компиляции одного и того же кода с модулями CommonJS в стандартный JavaScript-код, дружественный к браузеру. Каждый модуль помещается внутри функции в bundle.js и ему присваивается идентификатор, чтобы его можно было загружать по мере необходимости.

Тем не менее, в Webpack гораздо больше, чем это! Это действительно швейцарский армейский нож из комплектов модулей. Webpack также поставляется с отличными инструментами для разработки, такими как горячая замена модулей, которая автоматически перезагружает отдельные модули в браузере по мере их изменения — аналогично LiveReload, но без обновления страницы.

Существует также растущий список загрузчиков для различных типов ресурсов, даже CSS с css-loader и style-loader которые могут компилировать CSS в пакет JavaScript и вставлять его на страницу во время выполнения. Это выходит за рамки данной статьи, но об этом можно узнать при начале работы с Webpack .

JavaScript Transpilers

Это три самых популярных транспортера, используемых сегодня, вы также можете использовать другой из очень длинного списка языков, которые компилируются в JS .

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

 npm install --save-dev coffee-script typescript babel-cli babel-preset-es2015 touch src/{coffee-lib.coffee,ts-lib.ts,es6-lib.js} 

CoffeeScript

Редактировать coffee-lib.coffee

 sum = require 'lodash/sum' double = (number)-> number * 2 addFive = (number)-> sum([number, 5]) module.exports = double: double addFive: addFive 

Примечание . CoffeeScript использует синтаксис CommonJS для модулей.

Добавьте скрипт в package.json для запуска исполняемого файла coffee

 "coffee": "coffee --output ./dist ./src/coffee-lib.coffee" 

Запустить его с npm run coffee

Машинопись

Редактировать ts-lib.ts

 /// <reference path="lodash.d.ts" /> import * as _ from 'lodash'; const double = (value: number)=> value * 2 const addFive = (value: number)=> _.sum([value, 5]) export = { double, addFive } 

Примечание : TypeScript имеет собственный синтаксис для модулей, которые выглядят как сочетание синтаксиса модулей ES2015 и CommonJS.

Добавьте скрипт в package.json для запуска исполняемого файла tsc

 "tsc": "tsc --outDir ./dist ./src/ts-lib.ts" 

Запустите его с помощью npm run tsc

Компилятор будет жаловаться на невозможность найти lodash, так как он требует определения типа, чтобы знать, как работать с внешними модулями, которые не являются файлами TypeScript. Вы можете получить файл определения с помощью:

 cd src curl -O https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/lodash/lodash.d.ts cd .. npm run tsc 

галдеж

Редактировать es6-lib.js

 import sum from 'lodash/sum'; const double = (number)=> number * 2 const addFive = (number)=> sum([number, 5]) export { double, addFive } 

Примечание : Babel понимает прекрасный новый синтаксис модуля ES2015.

Babel требует файл конфигурации для указания, какие пресеты использовать

 echo '{ "presets": ["es2015"] }' > .babelrc 

Добавьте скрипт в package.json для запуска babel cli

 "babel": "babel ./src/es6-lib.js -o ./dist/es6-lib.js" 

Запустите его с помощью npm run babel

Файлы в /dist теперь содержат код ES5 в формате модуля CommonJS, который будет отлично работать с Browserify или Webpack, как мы использовали ранее. Вы можете либо перейти к ES5 сначала с CommonJS, а затем связать, либо вы можете использовать другие пакеты для выполнения обоих действий за один шаг.

Для Browserify есть плагины coffeeify , tsify и babelify для переноса и комплектации.

Для Webpack существуют загрузчики кофе-загрузчик , ts-загрузчик и babel-загрузчик, для которых требуются модули на разных языках.

JSPM

jspm — менеджер пакетов для универсального загрузчика модулей SystemJS , построенный поверх динамического загрузчика модулей ES6

jspm использует другой подход и начинается с загрузчика модулей System.js . System.js — это проект, который будет следовать спецификации загрузчика при его разработке.

Установите и инициализируйте проект jspm

 npm install -g jspm jspm init 

Примите все значения по умолчанию и убедитесь, что Babel используется в качестве транспилятора, который настроит System.js для использования Babel при работе с модулями стиля ES6.

Обновите index.html для загрузки и настройки System.js

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Modules!</title> </head> <body> <script src="jspm_packages/system.js"></script> <script src="config.js"></script> <!--<script src="bundle.js"></script>--> <script> System.import('src/entry.js'); </script> </body> </html> 

В браузере вы увидите несколько запросов и 404 для lodash, потому что jspm_packages по умолчанию загружает пакеты из каталога jspm_packages .

Запустите jspm install lodash чтобы установить lodash в этот каталог, и вы должны увидеть ожидаемый результат в консоли, 4 и 7 , вот что происходит:

  • Наш файл entry.js динамически загружается с помощью System.import('src/entry.js'); ,
  • System.js загружает entry.js , видит, что для этого требуется наш модуль lib поэтому выбирает его во время выполнения.
  • System.js загружает lib.js , видит, что ему требуется lodash/sum и извлекает его тоже.

System.js также знает, как работать непосредственно с ES6, обновлять entry.js чтобы динамически запрашивать наш модуль ES6, и компилировать на лету.

 import lib from './es6-lib'; // import lib from '../dist/coffee-lib'; // import lib from '../dist/ts-lib'; console.log(lib.double(2)); console.log(lib.addFive(2)); 

Вы также можете попробовать загрузить скомпилированные версии ES5 наших модулей CoffeeScript или TypeScript, раскомментировав эти строки по одной за раз. Другой вариант — использовать плагины System.js для переноса кода, а не требовать предварительно скомпилированного кода ES5.

Добавьте последний скрипт в package.json для создания пакета с помощью jspm

 "jspm": "jspm bundle src/entry bundle.js" 

Запустите его с помощью npm run jspm

Наконец, раскомментируйте тег script для bundle.js в index.html и браузер должен загрузить готовый к работе пакет без каких-либо дополнительных http-запросов.

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

Пересматривая Webpack

Наш пример с Webpack ранее был самым простым примером с использованием параметров по умолчанию, он скомпилировал entry.js с модулями CommonJS в один пакет. Когда вы делаете более модные вещи с Webpack, вы захотите создать пользовательский конфигурационный файл для всей конфигурации загрузчика.

Создайте webpack.config.js в корне проекта

 module.exports = { context: __dirname + "/src", entry: "./entry", output: { path: __dirname, filename: "bundle.js" }, module: { loaders: [{ test: /\.js$/, loader: 'babel-loader', query: { presets: ['es2015'] } },{ test: /\.coffee$/, loader: 'coffee-loader' },{ test: /\.ts$/, loader: 'ts-loader' }] } } 

Обновите файл index.html чтобы снова загрузить только связанный файл.

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Modules!</title> </head> <body> <script src="bundle.js"></script> </body> </html> 

Установите загрузчики для переноса с Babel, CoffeeScript и TypeScript

 npm install --save-dev babel-loader coffee-loader ts-loader 

Установите webpack глобально и запустите без аргументов, чтобы создать пакет из нашего конфигурационного файла.

 npm install -g webpack webpack 

Теперь, когда веб-пакет знает, как использовать эти загрузчики для этих расширений файлов, мы можем свободно использовать ES6, CoffeeScript или TypeScript из entry.js , попробуйте, раскомментировав их по одному.

 import lib from './es6-lib.js'; // import lib from './coffee-lib.coffee'; // import lib from './ts-lib.ts'; 

В Webpack есть гораздо больше, чем я описал здесь, но эти простые настройки — отличная отправная точка.

Туда и обратно

И поэтому мы заканчиваем исследование модулей, они решают множество проблем и могут реально снизить сложность наших приложений, если инструменты не мешают нам. Если вы еще не используете модули, сейчас самое время. Не нужно тратить лишние часы на создание конвейеров активов, вместо этого используйте простые инструменты Just Work ™.

В настоящее время Webpack является мощным игроком, и вы сможете настроить его для выполнения практически любых задач. jspm — это отличный инструмент для всех ваших потребностей в комплектации, он работает с различными форматами и имеет хороший опыт для разработчиков. Browserify по-прежнему является надежным вариантом, прадедом современных сборщиков модулей — его экосистема выросла и теперь включает в себя некоторые из любимых функций Webpack (например, разбиение пакетов и горячая перезагрузка). Наконец, System.js идеально подходит для случаев, когда вам необходимо загружать дополнительные модули во время выполнения.

Вы не захотите использовать все вышеперечисленные инструменты в одном проекте, но важно иметь представление об этих трех популярных вариантах, а также о том, как вы можете использовать транспортеры, когда возникает необходимость. Если вы просто хотите использовать модули, то Browserify, jspm или Webpack с параметрами по умолчанию сделают эту работу.

Сохраняйте простоту инструментов и легкость настройки. Счастливое Объединение.