Ускорение сборки HTML-страниц для Webpack
В этой небольшой статье я расскажу, как можно изначальные 3000мс/стр. сократить до 100мс/стр. и в 30 раз ускорить процесс создания HTML-страниц для Webpack-бандлов.
Автор: Вадим ФёдоровПробная статья!
Не судите строго! Путь в тысячу ли начинается с первого шага!
Есть очень популярный webpack
-плагин для сборки HTML-страниц, называется он: html-webpack-plugin. Основной мотивацией этого плагина, судя по лозунгу, является "упрощение создания HTML-страниц для бандлов". В моем случае как раз это и нужно было, так как мне требовалось создать статический сайт, состоящий только из html/css/js
файлов + различные ресурсы, типа картинок, шрифтов и т.п.
Попробовав этот плагин в работе, меня все устроило. Для шаблонизации я использовал Pug, но никаких проблем с этим не возникло. Рендер pug
-шаблонов осуществлялся с помощью pug-лоадерa для webpack
. Конфигурация плагина была достаточно простой, нужно было указать относительный путь для сгенерированного HTML-файла filename
, путь до шаблона страницы template
, и массив бандлов chunks
, которые должны быть вставлены в страницу.
Пример конфигурации:
new HtmlWebpackPlugin({ filename: 'relative/path/to/file.html', template: '/path/to/template.pug', chunks: ['chunk1', 'chunk2'] });
На выходе мы получали готовые HTML-страницы с уже прописанными страничными бандлами. Но, по непонятной мне причине, создание одной страницы занимало слишком много времени, что-то около 3000мс. Чем больше становилось страниц, тем дольше приходилось ждать сборку. Это меня никак не устраивало, так как в случае статических сайтов, количество страниц постоянно растет!
Почитав статьи по ускорению сборки, я обнаружил, что можно распараллелить этот процесс. То есть в зависимости от возможностей вашего процессора, будет запущенно несколько параллельных процессов для сборки. Таким образом, если у вас 6-ядерный процессор, то теоретически, вы можете ускорить сборку в 6 раз. Первой попыткой был thread-loader, но результата от этого лоадера я, к сожалению, не получил, уже даже не помню почему... Благо в статьях я наткнулся на плагин happypack. С помощью happypack
мне удалось сократить время сборки примерно в 3,5 раза, что не могло не радовать!
Однако, общее время сборки напрямую зависело от количества страниц, а на генерацию каждой страницы до сих пор уходило от 500 до 1000 миллисекунд. То есть 100 страниц => 100 секунд, 1000 страниц => 1000 секунд. Очевидно, что с такими цифрами возникает вопрос о целесообразности выбранного способа генерации сайта, но мне не хотелось отказываться от webpack
и я не мог смириться с тем, что на простой рендер pug
-шаблона уходит столько времени! Погружаться в исходники плагина html-webpack-plugin
не хотелось, поэтому я искал ему замену.
И тут я встретил в документации вебпака упоминание об одном совсем непопулярном плагине, назывался он htmls-webpack-plugin. Разница в названиях лишь в одной букве: htmlS-...
. Фичи плагина внушали надежду:
- Простой и гибкий, вы можете контролировать все, что угодно, не нужно настраивать множество плагинов;
- Поддержка множественной сборки HTML по-умолчанию;
- Быстрый, почти в 20 раз быстрее
html-webpack-plugin
для 20+ страниц.
Пример конфигурации:
new HtmlsWebpackPlugin({ htmls: [{ src: '/path/to/template.pug', filename: 'relative/path/to/file.html', render: (file, params) => { return pug.renderFile(file, params); } }] });
После интеграции и проверки, мои ожидание оправдались, скорость сборки сократилась еще в 5 раз! Теперь генерация одной страницы занимала всего около 170мс – в основном, это время тратилось на генерацию HTML из pug
-шаблона с помощью pug.renderFile()
. Но, к сожалению, процесс генерации выпал из loader
-флоу и happypack
не мог оптимизировать этот процесс... То есть параллельная сборка шаблонов на данный момент не работала.
Для распараллеливания в данном подходе я решил воспользоваться npm
-пакетом worker-farm. Разработчик htmls-webpack-plugin
предусмотрел асинхронный режим рендера HTML, что позволило легко организовать ферму pug
-рендеров, которые также, как с happypack
, параллельно, в несколько потоков, генерировали HTML. Прирост скорости оказался почти 2-кратным, что снизило время необходимое для сборки одной HTML-страницы до заветных 100мс.
Заключение
Таким образом, в итоге, я сократил сборку одной страницы с изначальных 3000мс, до 100мс, что очень хороший результат по-моему. Если вам и этого мало, в таком случае можно либо поискать более быстрый шаблонизатор (возможно Pug
не самый быстрый, не сравнивал), либо вообще работать с чистым html
!
Нюансы:
- Так как мы собственноручно занимаемся созданием HTML-страниц,
webpack
теперь ничего не знает о наших шаблонах. К чему это ведет:- При изменении именно файлов шаблонов, необходимо перезапустить сборку, потому что сама она не отловит факт изменения файла и
HMR
не сработает; - В режиме разработки нужно кэшировать сгенерированный HTML, чтобы при изменении файлов сборки (
js/css
и т. п.) заново не рендерить шаблоны, которые не изменились. В продакшн-билде кэш не нужен.
- При изменении именно файлов шаблонов, необходимо перезапустить сборку, потому что сама она не отловит факт изменения файла и
- Ради интереса я провел стресс-тест своей конфигурации. Сборка 1088 страниц заняла 1 минуту 27 секунд, то есть время сборки одной страницы снизилось до рекордных 80мс! Но последующее увеличение страниц прироста больше не дало. Сборка 2176 страниц заняла 3 минуты и те же 80мс на одну страницу... Видимо это все на что был способен мой процессор!
Версии:
{ "webpack": "4.41.2", "html-webpack-plugin": "3.2.0", "htmls-webpack-plugin": "1.0.9" "happypack": "5.0.1", "worker-farm": "1.7.0" }