В этой лабораторной работе рассматривается, как минимизация и сжатие пакета JavaScript для следующего приложения повышают производительность страницы за счет уменьшения размера запроса приложения.
Мера
Прежде чем приступать к оптимизации, всегда полезно проанализировать текущее состояние приложения.
- Для предварительного просмотра сайта нажмите «Просмотреть приложение» . Затем нажмите «Полный экран».
.
Это приложение, которое также рассматривалось в практической работе «Удаление неиспользуемого кода» , позволяет вам голосовать за вашего любимого котенка. 🐈
Теперь посмотрите, насколько велико это приложение:
- Нажмите `Control+Shift+J` (или `Command+Option+J` на Mac), чтобы открыть DevTools.
- Откройте вкладку Сеть .
- Установите флажок Отключить кэш .
- Перезагрузите приложение.
Несмотря на значительный прогресс, достигнутый в лабораторной работе «Удаление неиспользуемого кода» по уменьшению размера этого пакета, 225 КБ все еще довольно много.
Минификация
Рассмотрим следующий блок кода.
function soNice() {
let counter = 0;
while (counter < 100) {
console.log('nice');
counter++;
}
}
Если эту функцию сохранить в отдельном файле, размер файла составит около 112 Б (байт).
Если удалить все пробелы, то код будет выглядеть так:
function soNice(){let counter=0;while(counter<100){console.log("nice");counter++;}}
Размер файла теперь составит около 83 байт. Если его еще больше исказить, уменьшив длину имени переменной и изменив некоторые выражения, окончательный код может выглядеть следующим образом:
function soNice(){for(let i=0;i<100;)console.log("nice"),i++}
Размер файла теперь достигает 62 Б.
С каждым шагом код становится всё сложнее читать. Однако JavaScript-движок браузера интерпретирует каждый из них одинаково. Преимущество такого обфускирования кода может помочь уменьшить размер файла. 112 байт — это, конечно, немного, но всё же размер уменьшился на 50%!
В этом приложении для сборки модулей используется Webpack версии 4. Конкретную версию можно посмотреть в package.json
.
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
Версия 4 уже по умолчанию минимизирует пакет в режиме производства. Она использует плагин TerserWebpackPlugin
для Terser . Terser — популярный инструмент для сжатия JavaScript-кода.
Чтобы увидеть, как выглядит минифицированный код, нажмите на main.bundle.js
, оставаясь на панели «Сеть» в DevTools. Затем перейдите на вкладку «Ответ» .
Код в его окончательном виде, минифицированный и искажённый, показан в теле ответа. Чтобы узнать, насколько большим мог быть пакет без минификации, откройте webpack.config.js
и обновите конфигурацию mode
.
module.exports = {
mode: 'production',
mode: 'none',
//...
Перезагрузите приложение и еще раз проверьте размер пакета через панель «Сеть» DevTools.
Это довольно большая разница! 😅
Прежде чем продолжить, обязательно отмените внесенные изменения.
module.exports = {
mode: 'production',
mode: 'none',
//...
Включение процесса минимизации кода в ваше приложение зависит от инструментов, которые вы используете:
- Если используется Webpack v4 или выше, никаких дополнительных действий выполнять не нужно, поскольку код в производственном режиме по умолчанию минимизирован. 👍
- Если используется более старая версия Webpack, установите и включите
TerserWebpackPlugin
в процесс сборки Webpack. Подробное описание см. в документации . - Вместо этого также можно использовать другие плагины минификации, например BabelMinifyWebpackPlugin и ClosureCompilerPlugin .
- Если сборщик модулей вообще не используется, используйте Terser как инструмент CLI или включите его напрямую как зависимость.
Сжатие
Хотя термин «сжатие» иногда вольно используется для объяснения того, как сокращается код в процессе минификации, на самом деле он не сжимается в буквальном смысле.
Сжатие обычно относится к коду, изменённому с помощью алгоритма сжатия данных. В отличие от минификации, которая в итоге даёт абсолютно корректный код, сжатый код необходимо распаковать перед использованием.
Браузеры и веб-серверы могут добавлять заголовки к каждому HTTP-запросу и ответу, чтобы включить дополнительную информацию о загружаемом или получаемом ресурсе. Это можно увидеть на вкладке Headers
на панели «Сеть» в DevTools, где отображаются три типа заголовков:
- General представляет собой общие заголовки, относящиеся ко всему взаимодействию запрос-ответ.
- Заголовки ответа показывают список заголовков, относящихся к фактическому ответу сервера.
- Заголовки запроса показывают список заголовков, прикрепленных к запросу клиентом.
Взгляните на заголовок accept-encoding
в Request Headers
.
accept-encoding
используется браузером для указания поддерживаемых им форматов кодирования контента (алгоритмов сжатия). Существует множество алгоритмов сжатия текста, но для сжатия (и распаковки) сетевых HTTP-запросов здесь поддерживаются только три:
- Gzip (
gzip
): наиболее распространённый формат сжатия для взаимодействия сервера и клиента. Он основан на алгоритме Deflate и поддерживается всеми современными браузерами. - Сдувать (
deflate
): Обычно не используется. - Brotli (
br
): новый алгоритм сжатия, направленный на дальнейшее повышение степени сжатия, что может привести к ещё большей скорости загрузки страниц. Поддерживается в последних версиях большинства браузеров .
Пример приложения в этом руководстве идентичен приложению, созданному в рамках практической работы «Удаление неиспользуемого кода», за исключением того, что Express теперь используется в качестве серверного фреймворка. В следующих нескольких разделах мы рассмотрим как статическое, так и динамическое сжатие.
Динамическое сжатие
Динамическое сжатие подразумевает сжатие ресурсов «на лету» по мере их поступления в браузер.
Плюсы
- Создавать и обновлять сохраненные сжатые версии ресурсов не требуется.
- Сжатие «на лету» особенно эффективно для динамически генерируемых веб-страниц.
Минусы
- Сжатие файлов на более высоких уровнях для достижения лучшей степени сжатия занимает больше времени. Это может привести к снижению производительности, поскольку пользователю приходится ждать, пока данные будут сжаты, прежде чем сервер отправит их.
Динамическое сжатие с помощью Node/Express
Файл server.js
отвечает за настройку сервера Node, на котором размещается приложение.
const express = require('express');
const app = express();
app.use(express.static('public'));
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});
В настоящее время все, что делает эта функция, — импортирует express
и использует промежуточное ПО express.static
для загрузки всех статических файлов HTML, JS и CSS в каталоге public/
(и эти файлы создаются Webpack при каждой сборке).
Чтобы гарантировать сжатие всех ресурсов при каждом запросе, можно использовать библиотеку промежуточного программного обеспечения для сжатия . Для начала добавьте её как devDependency
в package.json
:
"devDependencies": {
//...
"compression": "^1.7.3"
},
И импортируем его в файл сервера server.js
:
const express = require('express');
const compression = require('compression');
И добавьте его как промежуточное ПО перед монтированием express.static
:
//...
const app = express();
app.use(compression());
app.use(express.static('public'));
//...
Теперь перезагрузите приложение и посмотрите размер пакета на панели «Сеть» .
С 225 КБ до 61,6 КБ! В Response Headers
теперь заголовок content-encoding
показывает, что сервер отправляет этот файл, закодированный с помощью gzip
.
Статическое сжатие
Идея статического сжатия заключается в предварительном сжатии и сохранении ресурсов.
Плюсы
- Задержка, вызванная высокой степенью сжатия, больше не проблема. Сжатие файлов не требует никаких действий «на лету», поскольку теперь их можно загрузить напрямую.
Минусы
- Ресурсы необходимо сжимать при каждой сборке. Время сборки может значительно увеличиться при использовании высоких уровней сжатия.
Статическое сжатие с помощью Node/Express и Webpack
Поскольку статическое сжатие подразумевает предварительное сжатие файлов, настройки Webpack можно изменить для сжатия ресурсов на этапе сборки. Для этого можно использовать CompressionPlugin
.
Начните с добавления его как devDependency
в package.json
:
"devDependencies": {
//...
"compression-webpack-plugin": "^1.1.11"
},
Как и любой другой плагин Webpack, импортируйте его в файл конфигурации webpack.config.js:
const path = require("path");
//...
const CompressionPlugin = require("compression-webpack-plugin");
И включаем его в массив plugins
:
module.exports = {
//...
plugins: [
//...
new CompressionPlugin()
]
}
По умолчанию плагин сжимает файлы сборки с помощью gzip
. Ознакомьтесь с документацией , чтобы узнать, как добавить опции для использования другого алгоритма или включения/исключения определённых файлов.
При перезагрузке и сборке приложения создаётся сжатая версия основного пакета. Откройте консоль Glitch, чтобы посмотреть содержимое итогового каталога public/
обслуживаемого сервером Node.
- Нажмите кнопку «Инструменты» .
- Нажмите кнопку «Консоль» .
- В консоли выполните следующие команды, чтобы перейти в
public
каталог и увидеть все его файлы:
cd public
ls
Сжатая версия пакета main.bundle.js.gz
теперь также сохраняется здесь. CompressionPlugin
также по умолчанию сжимает index.html
.
Следующее, что нужно сделать, — указать серверу отправлять эти сжатые файлы при каждом запросе их исходных JS-версий. Это можно сделать, определив новый маршрут в server.js
до того, как файлы будут переданы с помощью express.static
.
const express = require('express'); const app = express(); app.get('*.js', (req, res, next) => { req.url = req.url + '.gz'; res.set('Content-Encoding', 'gzip'); next(); }); app.use(express.static('public')); //...
app.get
используется, чтобы сообщить серверу, как отвечать на GET-запрос к конкретной конечной точке. Затем используется функция обратного вызова, определяющая, как обрабатывать этот запрос. Маршрут работает следующим образом:
- Указание
'*.js'
в качестве первого аргумента означает, что это будет работать для каждой конечной точки, которая активируется для извлечения JS-файла. - В обратном вызове
.gz
прикрепляется к URL-адресу запроса, а заголовок ответаContent-Encoding
устанавливается наgzip
. - Наконец,
next()
гарантирует, что последовательность продолжится до любого обратного вызова, который может быть следующим.
После перезагрузки приложения еще раз взгляните на панель Network
.
Как и прежде, существенное уменьшение размера пакета!
Заключение
В этой лабораторной работе был рассмотрен процесс минимизации и сжатия исходного кода. Оба эти метода становятся стандартными во многих современных инструментах, поэтому важно выяснить, поддерживает ли их ваш набор инструментов или вам следует начать применять оба процесса самостоятельно.