В этой лаборатории кода показано, как минимизация и сжатие пакета 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 B действительно поначалу было немного, но все же сокращение размеров было на 50%!
В этом приложении в качестве сборщика модулей используется веб-пакет версии 4. Конкретную версию можно увидеть в package.json
.
"devDependencies": {
//...
"webpack": "^4.16.4",
//...
}
Версия 4 уже минимизирует пакет по умолчанию в рабочем режиме. Он использует плагин TerserWebpackPlugin
для Terser . Terser — популярный инструмент, используемый для сжатия кода JavaScript.
Чтобы получить представление о том, как выглядит минимизированный код, нажмите main.bundle.js
, оставаясь на панели DevTools Network . Теперь перейдите на вкладку «Ответ» .
Код в его окончательной форме, уменьшенный и искаженный, отображается в теле ответа. Чтобы узнать, насколько большим мог бы быть пакет, если бы он не был минимизирован, откройте webpack.config.js
и обновите конфигурацию mode
.
module.exports = {
mode: 'production',
mode: 'none',
//...
Перезагрузите приложение и еще раз посмотрите на размер пакета через панель DevTools Network .
Это довольно большая разница! 😅
Прежде чем продолжить, обязательно отмените изменения здесь.
module.exports = {
mode: 'production',
mode: 'none',
//...
Включение процесса минимизации кода в ваше приложение зависит от используемых вами инструментов:
- Если используется веб-пакет v4 или более поздней версии, никаких дополнительных действий выполнять не нужно, поскольку код по умолчанию минимизируется в производственном режиме. 👍
- Если используется более старая версия веб-пакета, установите и включите
TerserWebpackPlugin
в процесс сборки веб-пакета. В документации это подробно объясняется. - Вместо них также существуют и другие плагины минификации, например 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/
(и эти файлы создаются веб-пакетом при каждой сборке).
Чтобы гарантировать, что все ресурсы сжимаются каждый раз, когда они запрашиваются, можно использовать библиотеку промежуточного программного обеспечения сжатия . Начните с добавления его как 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 и веб-пакета
Поскольку статическое сжатие предполагает предварительное сжатие файлов, настройки веб-пакета можно изменить для сжатия ресурсов на этапе сборки. Для этого можно использовать CompressionPlugin
.
Начните с добавления его как devDependency
в package.json
:
"devDependencies": {
//...
"compression-webpack-plugin": "^1.1.11"
},
Как и любой другой плагин веб-пакета, импортируйте его в файл конфигурации 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
.
Следующее, что необходимо сделать, — это указать серверу отправлять эти файлы в формате gzip всякий раз, когда запрашиваются их исходные версии 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
.
Как и раньше, значительное уменьшение размера пакета!
Заключение
В этой лаборатории кода рассмотрен процесс минимизации и сжатия исходного кода. Оба эти метода становятся стандартными во многих доступных сегодня инструментах, поэтому важно выяснить, поддерживает ли их уже ваша цепочка инструментов или вам следует начать применять оба процесса самостоятельно.