Это вторая из двух частей серии о том, почему и как мы переключили нашу систему связывания JavaScript с специальной системы задач Grunt и PHP на декларативную конфигурацию webpack. Щелкните здесь, чтобы перейти к Почему мы перешли на веб-пакет, чтобы узнать, почему мы перешли на веб-пакет.

Эта статья предназначена для вас, если в системе сборки JavaScript вашей кодовой базы есть проблемы, аналогичные нашим в Почему мы перешли на веб-пакет, и вы хотите перейти на веб-пакет инкрементным способом. разделив его на более мелкие шаги. Некоторые части этой статьи относятся к AngularJS 1.x, но остальное можно применить к любому фреймворку.

Чтобы сделать миграцию максимально безболезненной, разбейте ее на следующие шаги:

  1. Представьте webpack вместе с существующей системой сборки без изменения кода приложения.
  2. Используйте webpack во время разработки в течение определенного периода времени и решайте проблемы по мере их возникновения. Не рекомендуется использовать старую систему сборки, но продолжать использовать ее в производственной среде.
  3. Удалите устаревшую систему сборки, оставив только webpack.
  4. Обновите кодовую базу улучшениями, которые теперь возможны с помощью webpack.

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

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

1. Представьте webpack вместе с существующей системой сборки.

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

  • Создание пакета JavaScript
  • Создание пакета CSS
  • Отображение путей к ресурсам JS / CSS в ваш HTML-шаблон

Обратите внимание, что вы можете исключить из этого списка более сложные требования, такие как минификация, объединение для тестов кармы или объединение строк переведенного языка. С ними можно справиться на шаге №2.

Создание пакета JavaScript

Ваша существующая система сборки, вероятно, включает в себя этап конкатенации для объединения нескольких скриптов в один, например, с помощью grunt-contrib-concat:

grunt.initConfig({
  concat: {
    js: {
      files: [{
        src: [
          'vendor/lodash.js',
          'vendor/jquery.js',
          'vendor/angular.js',
          'vendor/angular-cookies.js',
          'app/scripts/**/*.js'
        ],
        dest: 'build/js/'
      }]
    }
  }
});

К счастью, webpack достаточно гибок, чтобы воспроизвести это поведение с помощью import-loader, exports-loader и context:

Чтобы ваш пакет JS webpack вел себя так же, как ваш старый пакет конкатенации скриптов, создайте новый файл .js, который будет служить точкой входа в веб-пакет, и используйте import-loader и exports-loader для импорта зависимостей и экспортировать значения для скриптов вашего поставщика, например

// app/app.js
// Import legacy vendor scripts in the correct order
window._ = require(
  '../vendor/lodash'
);
window.$ = window.jQuery = require(
  '../vendor/jquery'
);
window.angular = require(
  'exports?window.angular!../vendor/angular'
);
require(
  'imports?angular=>window.angular!' +
  '../vendor/angular-cookies'
);
// ... application scripts

Поскольку каждый сценарий поставщика имеет разные зависимости и экспортируемые значения, каждый из них необходимо проверить вручную, чтобы определить, следует ли и как использовать import-loader и exports-loader. Обязательно назначьте глобальные переменные для `window`, чтобы убедиться, что они доступны в коде вашего приложения.

Если код вашего приложения также должен выполняться в определенном порядке, вы можете просто `требовать` каждый файл последовательно, например

// app/app.js
// ... vendor scripts
require('./scripts/moduleA');
require('./scripts/moduleB');
require('./scripts/moduleC');
// ...
require('./scripts/main');

Или, если код вашего приложения может выполняться в любом порядке (например, если вы используете модули AngularJS), вы можете использовать контекст веб-пакета, чтобы требовать все эти файлы сразу:

// app/app.js
// ... vendor scripts
/**
 * `require` all modules in the given webpack context
 */
function requireAll(context) {
  context.keys().forEach(context);
}
// Collect all angular modules
requireAll(require.context(
  './scripts',
  /* use subdirectories: */ true,
));

Вот и все, что нужно для обработки ваших JS-файлов! Не нужно вносить никаких изменений в скрипты поставщика или код приложения; вам нужно только изменить точку входа в веб-пакет. Важно не изменять сам код приложения, чтобы вы могли продолжить сборку с использованием предыдущей системы сборки.

Создание пакета CSS

Ваш этап сборки CSS, вероятно, включает этап препроцессора, например, с Stylus через grunt-contrib-stylus:

grunt.initConfig({
  stylus: {
    compile: {
      files: {
        src: 'app/css/main.styl',
        dest: 'build/css/main.css'
      }
    }
  }
});

webpack предлагает набор загрузчиков для обработки предварительной обработки и вывода CSS:

  • Stylus-loader / sass-loader / less-loader: запускает данный препроцессор в вашем файле (файлах) CSS, возвращая простой CSS
  • Css-loader: разрешает `@ import` /` url (…) `и возвращает результирующий CSS
  • Url-loader / file-loader: встраивает или выводит содержимое файла в вывод webpack, например шрифт или изображение `url (…)` s в CSS.
  • Extract-text-webpack-plugin: перемещает ваш CSS в отдельный выходной файл, а не встраивает его в HTML.

Настройка веб-пакета с этими загрузчиками оставлена ​​в качестве упражнения для читателя, но после его настройки добавить вашу таблицу стилей в сборку веб-пакета так же просто, как добавить `require (…)` в вашу точку входа:

// app/app.js
require('./css/main.styl');
// ... vendor scripts
// ... application scripts

Примечание: этап сборки CSS не обязательно должен быть частью сборки веб-пакета; например, мы могли бы переключиться на простой сценарий npm, вызывающий команду `stylus`. Но мы получаем некоторые преимущества, используя webpack:

  • Для сборки как JS, так и CSS необходимо запустить только webpack; нет необходимости запускать отдельную команду для построения CSS.
  • Ресурсы изображений / шрифтов, на которые ссылается CSS, автоматически включаются в вывод webpack; нет необходимости копировать их в папку вывода и управлять путями к ресурсам каким-либо другим способом.
  • file-loader автоматически сгенерирует хешированные имена файлов для долгосрочного кэширования файла CSS и включенных шрифтов / изображений.

Отображение путей к ресурсам JS / CSS в ваш HTML-шаблон

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

Вы можете фиксировать пути ко всем выходным ресурсам webpack, используя объект Stats, который возвращается после завершения сборки ». Простой способ передать эти данные в ваш HTML-шаблон - использовать stats-webpack-plugin:

// webpack.config.js
const StatsPlugin = require('stats-webpack-plugin');
module.exports = {
  // ...
  plugins: [
    // ...
    new StatsPlugin('webpack-stats.json', {
      chunks: false,
      modules: false,
      children: false,
      cached: false,
      reasons: false,
      source: false,
      errorDetails: false,
      chunkOrigins: false,
    })
  ]
};

Чтобы легко переключаться между webpack и вашей старой системой сборки, вы можете просто отобразить пути ресурсов webpack, если webpack-stats.json существует в вашей выходной папке, иначе вернуться к путям ресурсов вашей старой системы сборки .

Например, используя PHP:

Это все, что необходимо для того, чтобы минимальная сборка веб-пакетов работала! На данный момент ваш проект должен быть в этом состоянии:

  • Запустите свой старый этап сборки (например, `grunt build`), который создаст ресурсы JS / CSS в папке сборки, в результате чего HTML-шаблон вашего сайта будет отображать пути к ресурсам по-старому.
  • Запустите `webpack`, который создаст ресурсы JS / CSS и webpack-stats.json в вашей папке сборки, что заставит HTML-шаблон вашего сайта отображать пути к ресурсам с использованием webpack-stats.json .

2. Используйте webpack во время разработки и не рекомендуйте использовать старую систему сборки.

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

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

Если вы используете средство запуска задач, такое как Grunt или Gulp, то сценарии npm часто являются более простой альтернативой задачам Grunt / Gulp.

Вот несколько альтернативных задач Grunt, которые мы используем:

По окончании этого этапа у вас должно быть:

  • Сценарий npm для замены каждой задачи сборки, например `npm run bundle: watch` для объединения и просмотра во время разработки,` npm run bundle: production` для объединения и минимизации с языковыми переводами, `npm run karma` для запуска тестов Karma
  • Тщательно протестируйте все свое приложение, чтобы убедиться, что оно работает правильно: следите за отсутствием скриптов или таблиц стилей, поскольку они могут привести к поломке вашего приложения.

3. Удалите устаревшую систему сборки, оставив только веб-пакет.

Эта часть проста: просто настройте свой сценарий сборки CI для запуска новых сценариев сборки npm (например, `npm test && npm run bundle: production`), удалите свою старую конфигурацию запуска задач (Gruntfile.js / gulpfile.js / и т. Д.) и удалите неиспользуемые в настоящее время зависимости из package.json.

Вы также захотите удалить код в шаблоне HTML, который возвращается к вашей старой системе сборки, если webpack-stats.json не существует.

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

4. Обновите кодовую базу, добавив улучшения, доступные с помощью webpack.

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

Разрешение зависимостей

Помните, как вы настраивали точку входа веб-пакета для глобального импорта скриптов в порядке «сначала зависимости»?

// app/app.js
require('./css/main.styl');
// Import legacy vendor scripts in the correct order
window._ = require(
  '../vendor/lodash'
);
// ...
require('./scripts/moduleA');
require('./scripts/moduleB');
require('./scripts/moduleC');
// ...
require('./scripts/main');

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

Вместо этого теперь вы можете импортировать зависимости в модуль, в котором они используются, например если main.js зависит от lodash.js, moduleA.js и moduleC.js:

// app/scripts/main.js
var _ = require('../../vendor/lodash');
var A = require('./moduleA');
var C = require('./moduleC');
// ...

И если moduleB.js зависит только от moduleA.js:

// app/scripts/moduleB.js
var A = require('./moduleA');
// ...

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

// app/app.js
require('./css/main.styl');
require('./scripts/main');

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

Теперь вы можете начать использовать пакеты npm вместо копирования модулей сторонних поставщиков в репозиторий: обновление пакетов становится проще, общие транзитивные зависимости сокращают дублирование кода, и вам больше не нужно создавать собственные пакеты UMD.

Это должно быть так же просто, как выполнить `npm install ‹package›`, а затем обновить ссылки на модуль, например измените это:

var _ = require('../../vendor/lodash');

К этому:

var _ = require('lodash');

Теперь, когда зависимости явны и мы используем пакеты npm, мы достигли (по крайней мере, отчасти) цели облегчения понимания базы кода. И мы также улучшили паритет между разработками и продуктами, поскольку теперь webpack обрабатывает зависимости наших модулей одинаково как при разработке, так и при производстве: с той лишь разницей, что производственная сборка минимизирована.

Конечно, мы также подготовили кодовую базу к будущему, где будет проще начать использовать ES2015 +, включив babel-loader, а использование React и Redux станет проще благодаря возможности использовать JSX и npm. пакеты.

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

[Если вам понравилась эта статья, щелкните маленькую ❤️ слева, чтобы об этом узнали другие люди. Если вы хотите больше подобных сообщений, следите за публикацией EventMobi с помощью кнопки (Подписаться) справа!

Наконец, если решение подобных проблем кажется вам интересным, мы приглашаем вас! Ознакомьтесь с нашими текущими открытыми вакансиями на http://www.eventmobi.com/careers/]