Минимизируйте и сжимайте сетевые полезные данные с помощью gzip

Хуссейн Джирдех
Houssein Djirdeh

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

Скриншот приложения

Мера

Прежде чем приступать к оптимизации, всегда полезно проанализировать текущее состояние приложения.

  • Для предварительного просмотра сайта нажмите «Просмотреть приложение» . Затем нажмите «Полный экран». полноэкранный .

Это приложение, которое также рассматривалось в практической работе «Удаление неиспользуемого кода» , позволяет вам голосовать за вашего любимого котенка. 🐈

Теперь посмотрите, насколько велико это приложение:

  1. Нажмите `Control+Shift+J` (или `Command+Option+J` на Mac), чтобы открыть DevTools.
  2. Откройте вкладку Сеть .
  3. Установите флажок Отключить кэш .
  4. Перезагрузите приложение.

Исходный размер пакета на панели «Сеть»

Несмотря на значительный прогресс, достигнутый в лабораторной работе «Удаление неиспользуемого кода» по уменьшению размера этого пакета, 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.

Размер пакета 767 КБ

Это довольно большая разница! 😅

Прежде чем продолжить, обязательно отмените внесенные изменения.

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 .

Уменьшение размера пакета с помощью статического сжатия

Как и прежде, существенное уменьшение размера пакета!

Заключение

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