[Перевод] Оптимизация времени сборки проекта

Там, где я работаю (в стартапе Spot.IM, размер которого находится где-то между малым и средним), Webpack используется для сборки различных проектов. После 4 лет работы над нашим основным продуктом, когда в его код внесло вклад столько народа, что и не сосчитать, время его первоначальной сборки достигло непомерных 90 секунд, а время пересборки — 14.

Речь идёт о 14 секундах, которые нужно ждать после каждого нажатия на кнопку «Сохранить».

Прибегнув к некоторым несложным оптимизациям, к таким, которые любой может применить в своём проекте, мы смогли сократить вышеприведённые цифры до 20 секунд на сборку и 1 секунды на пересборку проекта.

d9jbscezozngccwtdz8pkqlvmh8.png

В этом материале я хочу рассказать о некоторых простых изменениях, внеся которые в проект можно значительно улучшить время его сборки. Обратите внимание на то, что если вы используете CreateReactApp (или какой-нибудь другой модный генератор приложений), то эта статья, возможно, не окажется для вас особенно полезной. А вот если ничем таким вы не пользуетесь — тогда то, о чём здесь пойдёт речь, может вам очень пригодиться.

Измерение времени, уходящего на сборку проекта


Прежде чем заниматься какой-бы то ни было оптимизацией, давайте наладим измерение показателей, по которым сможем судить о результатах работы. Для того чтобы это сделать, воспользуемся пакетом speed-measure-webpack-plugin (SMP):

const webpackConfig = require('./webpack.config')
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')

const smp = new SpeedMeasurePlugin({
  disable: !process.env.MEASURE,
})

module.exports = smp.wrap(webpackConfig)


Мы помещаем конфигурационный файл Webpack в обёртку SMP (запуская механизм проведения измерений производительности с помощью переменной окружения), а после этого передаём конфигурационный объект Webpack. После этого в нашем распоряжении оказывается приятно выглядящий отчёт, позволяющий узнать о том, сколько времени уходит на выполнение каждого загрузчика в ходе сборки. Воспользовавшись SMP, мы получаем двойную выгоду. Во-первых — выполнив некое улучшение, мы сразу же можем узнать о том, как оно повлияло на время сборки проекта. Во-вторых — в нашем распоряжении мгновенно оказывается полный отчёт о том, сколько времени занимает выполнение каждого загрузчика (или, точнее, «цепочки загрузчиков»).

f15e088ee6791f9b4cc0d2449773cf10.png


Отчёт, генерируемый с помощью пакета speed-measure-webpack-plugin

Улучшение времени первоначальной сборки проекта


После того, как мы начали использовать SMP, в нашем распоряжении оказались сведения о том, как меняется время сборки проекта при внесении улучшений в процесс сборки. Первым, что мы начали оптимизировать, стало время первоначальной сборки (то есть — время, необходимое Webpack для того, чтобы собрать пакет после того, как он был инициализирован). Для ускорения процесса первоначальной сборки мы решили воспользоваться загрузчиком cache-loader.

Cache-loader — это загрузчик, который выполняет кэширование и сохранение на диск (или в базу данных) результатов работы идущих за ним загрузчиков. Это означает, что при следующем запуске Webpack можно увидеть значительное улучшение скорости сборки, или, по крайней мере, можно на это надеяться.

Вот пример:

{
  rules: [
    {
      test: /\.jsx?$/,
      use: [
        'cache-loader',
        'babel-loader',
      ],
    },
    {
      test: /\.scss$/,
      use: [
        'style-loader',
        'cache-loader',
        'css-loader',
        'postcss-loader',
        'sass-loader',
      ],
    },
  ]
}


Добавление cache-loader перед css-loader (для CSS) и перед babel-loader (для JS) позволило убрать примерно 75 секунд из времени, уходящего на первоначальную сборку проекта.

Теперь поработаем над временем пересборки. Улучшить это время мы попробуем, изменив свойство devtool.

Карты кода Webpack


В настройках Webpack можно найти свойство devtool, которое, в соответствии с документацией, «позволяет выбирать стиль создания карт кода, используемых для улучшения возможностей отладки. Заданные значения способны очень сильно повлиять на скорость сборки и пересборки».

Другими словами, модификация свойства devtool влияет на то, какая карта кода окажется в распоряжении разработчика после сборки проекта. А от этого, в свою очередь, зависит то, сколько времени уйдёт на создание подобной карты кода.

Эксперименты с этим свойством — это нечто из той области, что способно навсегда изменить жизнь программиста. Это оказывает огромнейшее влияние на скорость сборки проекта. А именно, мы поменяли значение devtool с source-map (вероятно, это — самый медленный режим) на eval-source-map и смогли сократить время пересборки проекта с 14 до 3.5 секунд:

{
  devtool: process.env.NODE_ENV === 'development'
    ? 'eval-source-map'
    : 'source-map'
}


Свойство devtool способно принимать 12 вариантов значений. CreateReactApp, например, использует cheap-module-source-map. Поэтому, если вы займётесь настройкой этого свойства — поэкспериментируйте и найдите то его значение, которое лучше всего подойдёт именно вам.

Надо отметить, что при использовании быстрых способов создания карт кода ухудшается качество получаемых карт. Эти ухудшения можно ощутить, приступив к отладке. К счастью, современные браузеры идут в ногу с TC39. В результате (по крайней мере — во время разработки) нет реальной необходимости в транспиляции больших объёмов кода. Если настроить Babel так, чтобы этот инструмент транспилировал бы JavaScript до уровня, понятного последней версии браузера (как сделано в CRA), то с отладкой кода всё должно быть хорошо, так как карты кода не будут слишком сильно отличаться от самого кода.

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

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: [
          'last 1 chrome version',
          'last 1 safari version',
          'last 1 firefox version',
        ].join(', '),
      },
    ],
  ],
  // ...
}


Вот и всё. Три простых шага, и время сборки нашего проекта удалось очень сильно уменьшить.

Хочется отметить, что у того, кто приступает к решению подобной задачи, может появиться желание сначала заглянуть в документацию Webpack и как следует её почитать. Однако это — не единственный источник знаний.

Я обнаружил ещё один подход к поиску ценных сведений о сборке проектов. Этот подход хорошо показал себя на практике. Он заключается в анализе файлов webpack.config существующих опенсорсных проектов. В частности — файла проекта CreateReactApp. Вдумчиво прочитав этот файл, можно узнать много полезного.

Итоги


Внимательный читатель мог заметить, что в самом начале речь шла о сокращении времени пересборки до 1 секунды. А лучшим значением этого показателя, упомянутым в тексте, было 3.5 секунды. Очевидно, при описании хода оптимизации процесса сборки что-то было опущено. Так оно и есть. Улучшить время пересборки проекта до 1 секунды, выиграв ещё 2.5 секунды, удалось благодаря тонким оптимизациям, которые очень сильно зависят от особенностей конкретного проекта, а именно — Spot.IM. Поэтому описание этих улучшений здесь не приведено.

Уважаемые читатели! Оптимизируете ли вы время сборки своих проектов?

1ba550d25e8846ce8805de564da6aa63.png

© Habrahabr.ru