Мы поддерживаем довольно большой сайт (около 200 файлов), созданный на ES5 с использованием ранней версии AngularJS. Мы решили, что риск полной перезаписи сайта слишком велик. Учитывая, насколько сложен полный перенос сайта, мы приблизились к 6–8-месячному периоду постепенного изменения, чтобы постепенно ввести стиль кода, соответствующий стандарту ES6.

Конечным результатом является комбинация Babel, Gulp и SystemJS для «объединения» всего javascript приложения, зависимостей, css, less и html в несколько отдельных файлов. Мы также удалили bower как зависимость, чтобы можно было стандартизировать наш менеджер пакетов с помощью NPM.

Представляем стиль кода

Первый шаг - запустить наш сайт на AngularJS LTS (v1.6). Обновив как можно больше зависимостей, мы гарантируем, что наш переход на «новые» рабочие процессы JavaScript будет более плавным.

Мы представляем Руководство по стилю John Papa при разработке любой новой функции. На это потребовалось время, но по мере того, как мы продолжали рефакторинг и писать новый код, все меньше поддерживавшегося кода относилось к старому стилю.

Старый стиль:

Новый стиль:

Обратите внимание, что нам пришлось оставить переменную Application в app.js в покое до тех пор, пока весь сайт не будет готов для ES6, из-за нашей зависимости от глобального интервала между именами. В большинстве наших файлов есть Application.controller ().

Это больше соответствует тому, как мы ожидаем увидеть модуль ES6.

Модули ES6

Теперь, когда у нас есть стандарт для нашего сайта ES5, давайте приступим к формату ES6. Одно из самых больших преимуществ ES6 - это форматы модулей и глобальные интервалы между именами. Больше не нужно загрязнять глобальное пространство имен. Но стандартизация модуля ES6 все еще находится в стадии разработки и не полностью поддерживается. Вот почему требуется загрузчик модуля для «импорта» модуля javascript (например, из NPM). Этот процесс может немного сбивать с толку, но все, что вам нужно знать, это то, что существует несколько разных форматов модулей: UMD, AMD, CommonJS и SystemJS. Все они немного отличаются, но предоставляют в основном одинаковые функции.

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

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

import $ from ‘jQuery’;

Мы решили использовать SystemJS как формат модуля и загрузчик в браузере. SystemJS довольно легкий и предлагает сразу после установки идеальное количество гибкости, необходимое для переноса нашего гигантского сайта на ES6. SystemJS обеспечивает транспиляцию ES6, но нас это пока не беспокоит. За исключением синтаксиса импорта, весь наш код по-прежнему совместим с ES5. Все, что требуется, - это немного масла в шестернях.

Одним из самых серьезных препятствий в процессе переноса является настройка SystemJS. Давайте посмотрим на самую базовую конфигурацию.

Мы берем папку модулей узлов и превращаем ее в псевдоним. Каждый раз, когда в пути используется "npm:", путь к этой папке будет разрешен. Теперь свойство map в конфигурации используется для сопоставления имени модуля с определенным путем. В данном случае у нас есть приложение и angular.

Так выглядит index.html

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

Этот:

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

Становится:

System.import('Application')

Мы говорим SystemJS загрузить нашу сопоставленную конфигурацию для этого псевдонима. В этом случае мы сказали, что наша конфигурация SystemJS «Приложение» совпадает с «app / app.js». Когда страница загружается, наша конфигурация будет загружена по пути и имени файла, затем «Приложение» будет загружено по псевдониму. Единственная разница в том, что мы позволяем SystemJS загружать файл через конфигурацию вместо того, чтобы позволить браузеру загружать его.

Теперь посмотрим на app.js

Когда мы вызываем import «angular», SystemJS попытается разрешить это имя модуля. Когда мы настраивали SystemJS, мы сказали ему искать angular в папке node_modules. Когда мы загрузим наш индекс, вы заметите, что SystemJS вызовет node_modules для загрузки зависимой библиотеки. Также обратите внимание, что мы используем относительный путь для загрузки контроллера.

Давайте посмотрим на app.controller.js

Мы взяли наш «новый стиль» и просто экспортировали функцию, используя формат модуля ES6, и удалили определение модуля angular. Это позволяет нам импортировать функцию в любом контексте. Учитывая, что у нас около 50+ контроллеров, необходимо импортировать и зарегистрировать каждый из них в app.js. Да, мы коснулись каждого файла. Также необходимо импортировать все зависимости нашего модуля angular в app.js. Новый стиль кода, который мы внедрили, позволяет нам удалить несколько строк и перейти к совершенству модуля ES6.

Хорошо смотритесь!

Транспортировка на ES6 +

SystemJS просто обрабатывает модули, не более того. Кроме того, когда мы перешли к масштабированию SystemJS, SystemJS потребовалось около 30 секунд, чтобы разрешить все 200 определений модулей и пути к файлам. Не говоря уже о добавлении еще времени на перенос ES6 в ES5. Это слишком медленно для разработки и слишком медленно для производства. SystemJS имеет возможность переносить в баузер с помощью babel, но мы обнаружили, что перенос 200 файлов занимает слишком много времени. Нам нужно транспилировать весь javascript перед загрузкой браузера и нам необходимо кеширование, чтобы мы не переносили весь сайт каждый раз, когда мы вносим изменения в режим разработки.

Вставьте Gulp и Babel.

Мы можем создать задачу gulp, которая возьмет весь наш код ES6 и заблаговременно протолкнет его через babel в памяти, а не в браузере. Это должно значительно ускорить процесс. Нам нужно вывести результат babel в файл, который SystemJS может использовать для загрузки в виде модулей.

Добро пожаловать на второе препятствие. К счастью, есть плагин babel, который делает именно это. Модуль ES2015 для плагина SystemJS для babel сотворит серьезные чудеса с нашими файлами. Это не только перенесет наш код, но и возьмет любой файл, который мы транспилируем, и зарегистрирует его как модуль SystemJS. Это означает, что SystemJS не нужно будет загружать и переносить файл при загрузке страницы. Он уже будет загружен и зарегистрирован в браузере к тому моменту, когда SystemJS начнет его импортировать. Это важно, потому что это означает, что мы можем объединить все наши JS в один связанный файл, загруженный заранее, а затем позволить SystemJS загружать модули по мере необходимости.

Пара замечаний.

  • Используйте gulp cache, чтобы кэшировать все файлы и передавать на следующий шаг только те файлы, которые были изменены. Это означает, что когда мы добавляем оператор watch, более поздние последовательные сборки будут переносить только те файлы, которые мы обновили. Значительно ускоряет работу.
  • Наши настройки babel отключают соглашение об экспорте модулей es2015, потому что вместо этого мы экспортируем все наши файлы как системные модули.
  • Плагин transform-es2015-modules-systemjs зарегистрирует каждый файл как модуль systemJS. Это означает каждый файл. Например, app / app.js становится зарегистрированным системным модулем и не будет запрашиваться через XHR при загрузке браузера (ускорение загрузки в 50 раз).
  • Gulp Remember вернет в поток все кешированные файлы, которые не были изменены.
  • Эта задача возьмет ВСЕ javascript из папки приложения и объединит его в один файл.

Теперь, когда у нас есть bundle-app.js, что нам с ним делать? Что ж, единственное, что нам нужно сделать, это сообщить SystemJS о зарегистрированных модулях (чтобы он не пытался загрузить их с помощью XHR).

Наш обновленный index.html

Обратите внимание, что мы все еще импортируем «Приложение». Разница в том, что все js-модули и файлы уже зарегистрированы в «bundle-app».

System.import('bundle-app.js')

HTML шаблоны

Помимо css / less, другие файлы, которые мы поддерживаем в нашем приложении, - это шаблоны HTML. Поскольку все наши JS будут объединены в один файл, это означает, что единственная причина, по которой нам нужна структура папок в нашем приложении, - это запрос HTML-шаблонов из соответствующей папки.

Глоток и бабел спешат на помощь! (снова)

Модуль gulp-angular-templatecache - отличный инструмент, который мы можем использовать, чтобы получить все наши шаблоны и зарегистрировать их как угловые шаблоны в кеше. Затем мы можем транспилировать этот файл как модуль ES6 и зарегистрировать его как SystemJS. модуль. Это дает огромное преимущество, потому что это означает, что мы можем объединить весь наш html в файл javascript, который загружается при загрузке страницы и кешируется в angular. Он также создает действительный файл ES6.

Мы создаем новую задачу, которая захватывает весь наш HTML, преобразует их в шаблоны angular, строит модуль es6 и транспилирует этот модуль в пакет systemjs es5.

Затем мы должны загрузить наши зарегистрированные модули SystemJS перед загрузкой приложения. Это может потребовать небольшого изменения некоторых путей к шаблонам во всем приложении (в нашем случае нам пришлось удалить папку верхнего уровня).

Теперь наш индекс выглядит так:

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

Управление зависимостями с помощью NPM

До этого момента мы управляем и транспилируем все файлы наших приложений, которые мы поддерживаем. Если не считать CSS, который охватывает почти весь сайт. Последняя оставшаяся часть - это связывание зависимостей. Помните наш синтаксис «import angular»? Хорошо, потому что мы зарегистрировали это как псевдоним NPM, и этот путь не является модулем SystemJS, зарегистрированным в нашем «bundle-app.js». SystemJS будет использовать XHR, чтобы попытаться загрузить любую зависимость из папки node_module. Немного медленно, но настоящий драйвер здесь мы не хотим копировать всю нашу папку node_modules. Нам нужны только зависимости приложения, а node_modules довольно большой, и мы не знаем, от каких файлов действительно зависит наше приложение.

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

Создаем еще одну связку. За исключением этого раза, мы позволяем системе решать, какие модули включать. Мы явно говорим системе НЕ включать файлы в наше приложение (потому что мы хотим обрабатывать их сами, используя более быстрые средства). Затем сборщик SystemJS выведет все наши зависимости (с встряхиванием дерева) в другой набор модулей в формате SystemJS.

В производственных рабочих процессах: не забудьте скопировать одну папку node_module в dist, которая требуется без пакета - SystemJs!

Зависимости CSS

Нет реального изящного способа автоматической обработки зависимостей css. Мы создали ручной шаг в gulp, чтобы скопировать все зависимости в один файл, который мы включаем в наш индекс.

Вывод

Существует множество различных вариантов рабочего процесса. Мы обнаружили, что многие инструменты и рабочие процессы расходятся из-за масштабируемости. Не заставляйте пользователей ждать секунды, пока загружается ваше приложение. При добавлении пары сотен файлов локальная разработка может быть медленной и болезненной. Кроме того, помимо другого диспетчера пакетов (JSPM, bower) нет четкого ответа для управления зависимостями веб-приложений с помощью npm. Использование правильных инструментов может избавить вас от многих проблем, связанных с выдергиванием волос и разочарованием. Кроме того, полезно вносить свой вклад в развитие экосистемы javascript.