Ускорение сборки 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"
}