Предисловие
Заняться этим проектом и написать эту статью вдохновил меня "молодой и шутливый человек который ускорял страницу с reactjs". Если кто-то помнит нашумевшую в своей время статью от сайта pingdom.com, о том что "Страницы в интернете прилично обросли жиром" их вывод складывался к тому, что раньше к весу страницы в основном добавляли изображения, теперь к этому "жиру" накинули и JavaScript. Страница шутливого молодого человека не дает особой практической пользы — больше разминка для его мозгов. Я же решил помочь своей девушке с продажей самого популярного продукта из ее ассортимента.
Я уже и раньше пытался делать крайне минималистичные сайты. Такие проекты были вызваны желаниям выйти за рамки фреймворков и возможно закончить проект быстрее. Такой подход достаточно популярен и среди моих друзей — популярность KISS (Keep It Simple, Stupid) дает свои плоды. Будучи в основном бэкенд программистом — я много времени провожу с оптимизацией кода на стороне сервера. На практике понял, как это важно не делать такие изменения вслепую, а подкреплять их метрикой.
Подготовка
Фронт-енд фреймворк
Я не фронтенд программист, мне приходится полагаться на фронтенд фреймворки. Чаще всего это означает bootstrap со всем "его добром". И хотя bootstrap компоненты можно использовать селективно, это все равно означает что я притащу с собой jQuery. Я видел уже достаточно native-javascript библиотек которые работают без Jquery. Размер бутстрапа и его компонент тоже меня не устраивал, мне всегда казалось что он "стилизует" чуть больше чем нужно.
Поэтому немного погуглив немного, я нашел фреймворки которые позиционируют себя как минималистычные. Вот примерный список того что я рассматривал
- Base → http://getbase.org
- Skeleton → http://getskeleton.com/
- Fluidable → http://fluidable.com/
- Purecss → http://purecss.io/
Я выбрал фрейворк base. Большинство из причин глубокого личные:
- Я уже работал с более раней версией
- Мне казалось что javascript компоненты мне не нужны, а если нужны, то я бы хотел их выбрать сам. Base — чисто css фреймворк.
- Использует gulp как билд тул. О котором я слышал много хорошего, некоторые умельцы даже пытались его интегрировать в рельсу (с которой я часто имею дело).
- Я нашел темплейт от автора фреймворка который подходил для моей цели. Я если честно не хотел много возиться с версткой, цель этого проекта была другая. Поэтому я с удовольствием отдал несколько долларов автору за это.
Поэтому я не буду рекомендовать только Base, а выдал список. Но я должен оговориться, что можно начать еще более минималистично, только с grid system — sussy grid, например.
Хостинг
Я не собирался делать динамический контент. И хоть казалось бы логичным сделать форму для покупки, большинство тайских магазинов что я видел избегали этого. Форма для покупки еще усложняется тем, что их бы пришлось интегрировать с банками, а в Тайланде нету четких лидеров в банковской сфере. Их больше 10 и все они более или менее пользуются спросом, даже среди моих друзей иностранцев\тайцев выбор банка крайне разнится. Очень много покупок делается непосредственно через интернет банки и мессенджер line. Поэтому я решил не ломать привычных паттернов, тем более это крайне упрощает мою задачу.
Мне нужен хостинг только для статического контента. Самым популярным выбором тут является github pages и AWS. Я побоялся использовать github pages из-за ограничений по трафику, а AWS мне показался не самым дешевым решением (по моим крайне грубым оценкам около 4 баксов в месяц). У меня уже было три сайта, которые крутятся на nearlyfreespeech.net — и десяти баксов хватило на них чтобы они работали больше года. Дешевле не всегда значит лучше, но в этом случае я уже имел опыт и никаких нареканий у меня нету.
Важная поправочка
_ В комментариях отметили, что мой географический выбор сервера не самый разумный для Тайского сайта. Так же, какой бы легковесный не был apache — он все равно создает новый поток для каждого соединения.
Метрика
Я изначально договорился сам с собой, что я не буду полагаться на собственные "ощущения". А буду полагаться на общественно признанный инструментарий.
gtmetrix.com — Я выбрал как основной тул для измерения "скорости" моего сайта. Он в себя включал два самых популярных инструмента google page speed и yslow. Оказалось, что оригинальный page speed все таки нашел чуть больше ошибок. Это привело меня к выводу, что полагаться на 100 процентный показатель в gtmetrix и схожие тулы — возможно не самая лучшая идея, это же скриптованая проверка на "самые популярные" ошибки. Вы всегда можете пойти дальше.
Измерять как быстро сайт будет открываться под нагрузкой я не собирался. Поэтому возможные проблемы с производительностью у Apache я игнорировал. Но вам возможно стоит задуматься.
Оптимизация
Gzip
Самая простая оптимизация которую можно было сделать — это предоставлять статические файлы в gzip формате. Это я делал во всех проектах в которых я работал до этого. Поэтому крайне быстро набросал таск в gulpfile.js, чтобы автоматизировать процесс создания .gz файлов.
var gzip = require('gulp-gzip');
gulp.task('gzip', function (){
return gulp.src('./public//**/*.+(js|css|html)')
.pipe(gzip())
.pipe(gulp.dest('./public/'))
});
И так же быстро набросал .htaccess файл, который подсовывает браузеру архивированный файл.
Header add Vary accept-encoding
RewriteEngine on
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)$ $1.gz [L]
Мне так же пришлось копировать этот файл в билд папочку, без особых изменений. Это оказалось еще проще:
gulp.task('htaccess', function () {
return gulp.src('./src/**/.htaccess')
.pipe(gulp.dest('./public/'));
});
Так же, пришлось обновить и build таск
gulp.task('build', function() {
runSequence('clean', 'sass', 'build-img', 'jsmin', 'inlinesource', 'htaccess', 'gzip');
});
Помоему это самый простой способ оптимизировать страницу, только ленивый не делает это в своих проектах. Во многих современных движках это все доводится до банальщины — добавить плагин или включить опцию.
Кэширование
Менее тривиальной оказалась задача по указыванию заголовков для кэширования. С AWS эти уже автоматизировано. Я пытался вспомнить когда я это делал последний раз, но так и не вспомнил когда. Поэтому с помощью магической силы гугла, пару попыток я все-таки сделал что-то 100% приеемлемое для gtmetrix.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/pdf "access plus 1 month"
ExpiresByType text/x-javascript "access plus 1 month"
ExpiresByType text/javascript "access plus 2 month"
ExpiresByType application/javascript "access plus 2 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType image/icon "access plus 1 year"
ExpiresByType application/ico "access plus 12 month"
ExpiresDefault "access plus 2 days"
</IfModule>
Но чтобы избежать двух попыток, я бы рекомендовал воспользоваться темплейтом для сервера из проекта html5-boilerplate. Я в следующий раз обязательно это сделаю :)
Изображения
Для человека у которого нету Photoshop'a на компьютере — работа с изображениями это большая попаболь. Половина проблем с изображениеми было решено за меня за счет того что я выбрал готовый темплейт — мне не пришлось возиться с спрайтами, векторами. Но это не решило всех моих проблем — мне нужны были другие иконки и другие изображения.
Правильный выбор формата для изображений
Я сделал самую грубую ошибку из тех ошибок что можно сделать. Я выбрал .png как формат по умолчанию для изображений, у меня было представление что png оптимизирован для веба. На самом деле для изображений насыщенных цветами (как например фото) — jpeg все-таки остается лучшим форматом, я оставил png для иконок.
Больше ликбеза на эту тему можно найти на страницах гугла (от людей которые понимают в этом больше чем я).
Компрессия без потерь
Как человек с инженерным образованием, я знаю цену специализированным инструментам. Они очень часто облегчают работу больше чем one-fits-all инструмент. Можно программировать на notepad'e, но чаще становится нашим основным рабочим инструментом — sublime text, rubymine. В данном случае imageoptim хорош, но не достаточно хорош. Так как у меня было много jpeg файлов, я нашел сравнительный анализ лослесс сжатий — выйграл jpegtran.
в gulp это оказалось очень просто:
var jpegtran = require('imagemin-jpegtran');
gulp.task('build-jpg', function () {
gulp.src('./src/img/*.+(jpg|jpeg)')
.pipe(jpegtran({ progressive: true })())
.pipe(gulp.dest('./public/img'));
});
Играемся с оптимизацией цветов в jpeg
Но выбором только правильного формата все не окончилось. Изображения все-равно были слишком большие. Мой hero background занимал больше половины мегабайта.
У меня нету photoshop'a, а что без него тут делать я не очень понимал. Но друзья подсобили и посоветовали отличный проект TinyJpg — все оказалось слишком просто.
CSS
Я все таки решил заинлайнить css. Это немного контринтуитивно, люди советуют держать все это в отдельном файле чтобы стили могли закэшироваться. Есть даже вероятность, что у юзера уже закешировано все это, особенно если используешь популярный фрэймворк.
Я скорее так бы и сделал, если бы использовал bootstrap. Но так как я использовал менее распространеный base и не очень то ожидал что пользовали будут возвращаться на мой сайт — то я решил удалить лишний http запрос.
В gulp это оказалось как обычно проще всего:
<link rel="stylesheet" href="css/styles.css" inline>
Fonts
Интересная и неожиданная ситуация для меня сложилось с Google Fonts, в темплейте было использовано два разных фонта. И вроде даже разумно оптимизированы:
- Они загружались за один http запрос
- Использовал WebFontLoader, который асинхронно загружал фонты и рендерил страницу после загрузки.
Но gtmetrix продолжал ругаться на фонты — у них небыло cache headers. Я решил пойти по пути уже предложенному в статье на которую я ссылался в начале и избавился от google fonts. во всех девайсах есть вполне приличные встроеные фонты. Поэтому я оставил вот такой вот набор:
font-family: "Helvetica Neue", "Calibri Light", Roboto, sans-serif;
Геолокация серверов
Пока в Тайланде, я набросал быстрый скрипт для проверки скорости из Тайланда:
require "uri"
SERVERS = [
{
:name => "nearlyfreespeach",
:url => "http://euphorbia.soihok.com/"
},
{
:name => "AWS servers",
:url => "https://d4s21h4msr5q2.cloudfront.net/"
}
]
SERVERS.each do |server|
uri = URI.parse(server[:url])
puts "Performing HTTP speed test for #{server[:name]}"
puts `wget -O /dev/null #{server[:url]} 2>&1 | awk '/\\/dev\\/null/ {speed=\$3 \$4} END {gsub(/\\(|\\)/,"",speed); print speed}'`
end
И получил я такие результаты:
$ rspec speed_test.rb
Performing HTTP speed test for nearlyfreespeach
85.1KB/s
Performing HTTP speed test for AWS servers
203KB/s
Performing HTTP speed test for nearlyfreespeach
92.5KB/s
Performing HTTP speed test for AWS servers
192KB/s
$ rspec speed_test.rb
Performing HTTP speed test for nearlyfreespeach
83.2KB/s
Performing HTTP speed test for AWS servers
168KB/s
Поэтому не совершайте моих ошибок. Выбирайте хостеров с серверами близкими к вашей целевой аудитории. Сайты будут открываться как минимум в два раза быстрее!
Выводы
Вот такой вот получился вебсайт — http://euphorbia.soihok.com/
А вот последнии метрики с gtmetrix — https://gtmetrix.com/reports/euphorbia.soihok.com/zhMn6OhU
Как утверждают многие СЕОшники — быстрый сайт дает бонусы в гугле. Я если честно на это надеялся, но не проверял. Поэтому здесь мне метриками крыть не получится.
Я часто путешествую и периодический приходится работать с крайне сомнительным соединением — будь то мобильное соединение, отели или просто не развитый интернет где то в Азии. И меня сильно расстраивает что нету мобильных версий у сайтов, что сайты даже для десктопов не оптимизированы.
Я очень много фокусировался чтобы делать быстрый бэкенд, которому не нужно много ресурсов. Но я никогда не фокусировался сильно на фронт-енде, хотя бы потому что считал что 100% показатели иметь не возможно. Но это оказалось, хоть и не просто, но возможно. Более того, можно пойти дальше и ускорить более "требуемого минимума". Все эти принципы и опыт построения быстрых страниц универсальны и последующие сайты ускорять будет гораздо легче и быстрее! Об этом лишь надо начать думать, где-то там на подсознании! И ваши пользователи скажут вам спасибо и будут возвращаться.
Желаю быстрых сайтов всем нам!
* "самый быстрый сайт в Таиланде" — это мой "кричащий" заголовок, я не берусь это утверждать со 100% уверенностью. Но большинство сайтов что я видел в Тайланде — не самые быстрые.