Скорость загрузки сайта крайне важный параметр. Тут дело не только в удобстве в работе с ресурсом, но и важный показатель для SEO оптимизации.

В этой статье расскажу про ускорения лендинга для одного интернет магазина женской одежды работающего на 1с-Битрикс.
Чаше всего есть несколько путей реализовать поставленную задачу. К примеру настроить отдачу картинки в формате webp в место jpg или png можно так:
- тег html <picture>
- .htaccess
- nginx
- CDN сервис
- и есть еще.
Я не буду освещать все возможные варианты, а опешу только то что применял сам в конкретном примере. Так же не буду подробно описывать процессы работы. В интернете уже есть куча материалов по всем этим фичам, заново проектировать велосипед не будем.
Архитектура сайта.
Лендинги находятся на поддоменах основного сайта и работаю на одном ядре 1с-Битрикс. Внедрялись посадочные страницы еще до появления «Сайтов24» и были разработаны на стороннем сервисе «Хамелеон». Использовали одно ядро с магазином, что бы подтягивать нужные товары из каталога для демонстрации, и проверять юзеров при потытки зарегистрироваться.
Было решено отказаться от функционала 1с-Битрикс, сайт был полностью переделан в самописную схему MVC.
Структура фронтэнда остался почти без изменений. На бекэнде была подключена новая база MySQL в которую с основного сайта загружались товары.
Для загрузки товаров был добавлен отдельный инфоблок. По событию сохранение инфоблока была реализована отправка данных инструментом cURL, на стороне лендинг данные записываются в ною базу.
Второй момент требующий данных с основного сайта это регистрация нового пользователя. Тут все еще проще. Ajax’ом отправляем данные пользователя на сайт, регистрируем стандартным компонентом bitrix:register. Если регистрация не прошла отправляем лиду ошибку регистрации, если регистрация успешна, то перенаправляем пользователя на основной сайт. При перенаправлении передаем get пораметром хук с уникальным кодом, по этому хуку сразу авторизуем нового клиента.
Стили и скрипты.
- Отключили все внешние подключения статических данных. В header’е несколько тегов line подгружали шрифты с google fonts. рифты были загружены и подключены в style.css
- Асинхронная загрузка. Для скиптов добавили атрибут «async»
<script async src="example.js"></script>
. Тут важно понять что можно загружать асинхронно, а что необходимо отдавать сразу. Скрипты завязанные с jquery и участвующие в формировании DOM должны подгружаться сразу. Карту с Яндекс.Карты было решено совсем не отображать при загрузке страницы, а отдавать ее ajax’ом по запросу пользователя. - Сжатие и удаление не использующегося кода. Так как сайт старый в статических файлах накопилось много устаревшего кода. Очистку и сжатия реализовали с помощью инструмента на node.js Grunt. Установили Grunt и нужные нам плагины, в корне проекта создаем файл Gruntfile.js в нем подключаем нужные плагины функцией
grunt.loadNpmTasks()
, при помощи методаregisterMultiTask()
объявляем нужные нам задачи. Теперь из консоли вызываем нужную задачу и Grunt преобразует наши файлы.
Изображения.
Основная часть веса страницы это конечно изображения. На данным момент формат .webp поддерживается большинством браузеров, в этот формат мы и будем конвертировать с помощью плагина cwebp на том же node.js
Устанавливаем и настраиваем cwebp, под node пишем функцию которая будет выдергивать картинки из нужной нам папки и создавать ее копию в webp рядышком. Приведем код тут, благо он не большой:
var path = require('path'); var fs = require('fs'); var async = require('async'); var webp = require('webp-converter'); function getFiles (dirPath, callback) { fs.readdir(dirPath, function (err, files) { if (err) return callback(err); var filePaths = []; async.eachSeries(files, function (fileName, eachCallback) { var filePath = path.join(dirPath, fileName); fs.stat(filePath, function (err, stat) { if (err) return eachCallback(err); if (stat.isDirectory()) { getFiles(filePath, function (err, subDirFiles) { if (err) return eachCallback(err); filePaths = filePaths.concat(subDirFiles); eachCallback(null); }); } else { if (stat.isFile() && /\S+(?:jpg|jpeg|png)$/.test(filePath)) { filePaths.push(filePath); } eachCallback(null); } }); }, function (err) { callback(err, filePaths); }); }); }
getFiles('/var/www/data/template/img', function (err, files) { for(let i=0; i<files.length; i++){ function ChangePhoto(foto){ fs.access(foto + ".webp", function (error) { if (error != null) { webp.cwebp(error.path.slice(0, -5), error.path, "-q 70", function (status, error) { // if conversion successful status will be '100' // if conversion fails status will be '101' }); } }); } setTimeout(() => { ChangePhoto(files[i]) }, i * 300); } });
Теперь нам важно настроить отдачу webp вместо jpg, в тех браузерах которые поддерживают новый формат изображений. Я посчитал наиболее подходящим настроить настроить подмену на nginx. Конфиг будет выглядеть примерно так:
location ~* ^(.*)(/static/.*)(jpg|jpeg|png)$ {
set $webp $1$2webp;
set $rootFile "${document_root}${webp}";
if ($http_accept ~* "webp"){set $test A;} if (-f $rootFile)
{set $test "${test}B";}
if ($test = AB) { add_header Vary Accept; rewrite (.*) $webp break; }
}
Картинки которые еще не попали в поле зрения клиента можно скрыть, и загрузить их поле того как они попадут в область экрана. Я выбрал плагин B-Lazy. Он достаточно легкий, быстро подключается и работает стабильно. Так же есть возможность добавить gif на время загрузки картинки.
Кеширование.
Крайне важный параметр для современных сайтов. Большое наличие стилей, скриптов и медиа нагружает наш сервер, кеширование существенно снизит эту нагрузку.
Что бы объяснить браузеру что данные станицы необходимо занести в кеш мы выставим HTTP-заголовок Expires, и добавим в файл .htaccess, следующие строки:
<IfModule mod_expires.c>
Header append Cache-Control "public"
FileETag MTime Size
ExpiresActive On
ExpiresDefault "access plus 0 minutes"
ExpiresByType image/ico "access plus 1 years"
ExpiresByType text/css "access plus 1 years"
ExpiresByType text/javascript "access plus 1 years"
ExpiresByType image/gif "access plus 1 years"
ExpiresByType image/jpg "access plus 1 years"
ExpiresByType image/jpeg "access plus 1 years"
ExpiresByType image/bmp "access plus 1 years"
ExpiresByType image/png "access plus 1 years"
</IfModule>
Данная конфигурация Apache проверяет наличие модуля mod_expires и, если модуль mod_expires доступен, включает отдачу HTTP-заголовков Expires, в котором мы установили срок жизни кеша.
На этом все. Учитывая то, что на лендинге и так мало данных мы еще максимально облегчили его отказавшись от CMS. В нашем примере таблицы БД не превышали 50 полей. На более тяжелых проектах конечно не получится ограничится только оптимизацией фронтэнда. Там уже необходимо оптимизировать запросы к MySQL, добавлять memcache в бэкенд-архитектуру, рассмотреть связку Nginx + php-fpm, использовать CND и прочее.
