admin / 26.01.2018
С ростом скорости интернет соединения и увеличении мощности не только десктопных, но и мобильных устройств веб страницы стают более «тяжелыми». Количество и размер подключаемых файлов растет: JavaScript файлы, css файлы, изображения, виджеты сторонних сайтов, iframe. На данный момент специфика работы браузеров такая, что при загрузке js файла блокируется отривсовка до того момента, пока скрипт не выполниться. Современные браузеры в фоновом режиме будут парсить документ и скачивать скрипты и стили, но отрисовка будет заблокирована. Сравнение сетевых параметров для различных браузеров можно посмотреть на browserscope.org. Мы не можем устранить блокировку полностью, но можем оптимизировать серверную и клиентскую часть приложения, что бы блокировка отрисовки занимала наименьший промежуток времени.
Решения для серверной части:
— Уменьшить размер передаваемых файлов
— Использовать CDN
— Вынести статические файлы на отдельный домен или под домен, таким образом увеличить количество одновременных соединений браузера.
— Включить сжатие передаваемых файлов(gzip)
Решения для клиентской части:
— Уменьшить количество запросов.
— Кэшировать файлы на стороне клиента с помощью заголовков Expires и Etags.
— Использовать общедоступные CDN(Google CDN, Yandex CDN). Таким образом, существует вероятность, что файл с общедоступного CDN уже будет храниться в кеше браузера.
— Асинхронная загрузка подключаемых файлов.
Одним из способов оптимизации скорости загрузки сайта является асинхронная загрузка файлов, которая не блокирует отрисовку.
Скрипт асинхронной загрузки JavaScript:
<script type=»text/javascript»> (function() { var s = document.createElement(‘script’); s.type = ‘text/javascript’; s.async = true; s.src = ‘URL файла’; document.getElementsByTagName(‘head’)[0].appendChild(script); })(); </script>
Если JavaScript нужно выполнить после загрузки всей страницы, включая содержание, изображения, стилевые файлы и внешние скрипты, то к загрузчику нужно добавить отслеживания события onload.
if (window.addEventListener) { window.addEventListener(‘load’, async_load, false); } else if (window.attachEvent) { window.attachEvent(‘onload’, async_load); }
Содержание
<script type=»text/javascript»> (function() { function async_load(){ var s = document.createElement(‘script’); s.type = ‘text/javascript’; s.async = true; s.src = ‘URL файла’; document.getElementsByTagName(‘head’)[0].appendChild(script); } if (window.addEventListener) { window.addEventListener(‘load’, async_load, false); } else if (window.attachEvent) { window.attachEvent(‘onload’, async_load); } })(); </script>
Но это единичный случай, когда требуется загрузка одного файла. Часто на практике подключается множество файлов.
<script type=»text/javascript»> (function() { function async_load(){ [ ‘URL_файла_1.js’, ‘URL_файла_2.js’, ‘URL_файла_3.js’ ].forEach(function(src) { var s = document.createElement(‘script’); s.type = ‘text/javascript’; s.async = true; s.src = src; document.getElementsByTagName(‘head’)[0].appendChild(script); }); } if (window.addEventListener) { window.addEventListener(‘load’, async_load, false); } else if (window.attachEvent) { window.attachEvent(‘onload’, async_load); } })(); </script>
Но в такой реализации есть минус — скрипты будут загружаться в произвольном порядке и соответсвенно выполнятся они будут произвольно во времени. Данный скрипт асинхронной загрузки идеально подходит, если выполнение JavaScript файлов не зависят один от другого и не зависит от DOM. В обратном случае его использование может привести к ошибкам на странице или непредвиденному результату выполнения. Для последовательного выполнения, но асинхронной загрузки, нужно указать async=false, тогда файлы будут скачиваться в произвольном порядке, но выполняться по очереди.
Стандарт HTML 5 поддерживает асинхронную загрузку JavaScript. Это можно сделать путем добавления ключевого слова async или defer. Например:
<script src=»https://steptosleep.ru/wp-content/uploads/2018/06/98516.jpg» type=»text/javascript» async></script> <script src=»https://steptosleep.ru/wp-content/uploads/2018/06/98516.jpg» type=»text/javascript» defer></script>
В обеих вариантах загрузка javascript будет асинхронной. Разница состоит только в начале времени выполнения скрипта.
Скрипт, который подключен с атрибутом defer выполнится не нарушая порядок выполнения по отношению к остальным скриптам и его выполнение произойдет после полной загрузки и парсинга страницы, но перед тем, как вызовется DOMContentLoaded.
Скрипт, который подключен с атрибутом async выполнится при первой возможности после полной загрузки, но при этом не ожидает окончания парсинга документа и до загрузки объекта window. Браузеры не гарантируют выполнение скриптов в том же порядке в котором они подключены.
RequireJS — модуль загрузки JavaScript. Оптимизирован под браузеры, но он может использоваться в других средах, таких как Node, Rhino.
require([«script»], function(script) { console.log(«start after load script.js»); });
extsrc.js — библиотека, которая запускает скрипты на выполнение после того, как страница загрузится и отобразится пользователю. Работает корректно с document.write.
<script src=»https://steptosleep.ru/wp-content/uploads/2018/06/99075.jpg»></script> <script extsrc=»https://steptosleep.ru/wp-content/uploads/2018/06/41537.jpg»></script> <script asyncsrc=»https://steptosleep.ru/wp-content/uploads/2018/06/41537.jpg»></script>
yepnope.js — позволяет совершать асинхронную загрузку JavaScript и CSS файлов.
yepnope([ ‘script.js’, ‘style.css’ ]);
Оказывается, что на практике добиться оптимальной кросс браузерной загрузки JavaScript скриптов, которые не блокируют отображение сложно, а порой невозможно. Наиболее оптимальный способом является добавление <script type=»text/javascript» src=»https://steptosleep.ru/wp-content/uploads/2018/06/24321.jpg»></script> в конец документа перед закрывающимся тегом body. Из за ограничения разных браузеров и самого HTML такой вариант загрузки, который не блокирует отображение, можно считать наиболее простой.
В этом посту будет подразумеваться, что вы знакомы с инструментом Google по оптимизации скорости загрузки страниц сайта — PageSpeed Insights. Слушайте, да прямо сейчас вбейте туда свой сайт нажмите кнопку «Analize».
Окей, а теперь — о чём этот пост?
Вполне возможно, что в результатах проверки вашего сайта есть пункт «Eliminate render-blocking JavaScript and CSS in above-the-fold content».
Я заметил, что этот пункт один из самых трудноразрешимых (трудоёмких) и практически на всех сайтах, даже на очень быстрых, он присутствует.
Как его исправить в теории:
Как же обстоит дело на практике, и в данном конкретном случае — для сайтов на WordPress?
В корректно состряпанной теме WordPress все CSS и JS файлы подключаются через wp_head() и wp_footer() — то есть в и в конце соответственно.
Также у файлов есть зависимости, то есть например плагин должен подключаться после , а это значит, что если библиотека jQuery находится в wp_footer(), то FancyBox ну никак не может попасть в wp_head().
Делается это очень просто — при помощи функций wp_deregister_script(), wp_register_script(), wp_enqueue_script() и хука (иногда используют хук в связке с is_admin()). Всё, что требуется от вас, это вставить код следующего содержания в файл вашего сайта.
add_action(’wp_enqueue_scripts’, ‘true_peremeshhaem_jquery_v_futer’); function true_peremeshhaem_jquery_v_futer(){// снимаем стандартную регистрацию jQuery wp_deregister_script(’jquery’); // регистрируем для подключения в футере, описание параметров — в документации функции (ссылка чуть выше) wp_register_script(’jquery’, includes_url(’/js/jquery/jquery.js’), false, null, true); // подключаем wp_enqueue_script(’jquery’); } |
Хочу обратить ваше внимание на то, что это автоматизированное решение, и хотя оно работает практически в 100% случаев, бывает такое, что некоторые скрипты не хотят переноситься в футер сайта. Тогда уже потребуется более внимательный к каждому вашему файлу JavaScript.
На этом наша работа с JS заканчивается, конечно прирост в скорости даст ещё и объединение скриптов (то есть снимаете их все с регистрации и потом просто подключаете свою объединенную версию) — но Google сейчас это уже не требует.
Если объединение всех JavaScript в один файл — не всегда хорошая идея, то CSS-ки я бы рекомендовал объединять по возможности всегда.
Помните скриншот в самом начале статьи (10 blocking CSS resources)? Откуда берется такое количество файлов стилей, ведь разработчик темы наверное понимал, что делает?
Ответ — из плагинов.
Например плагин «Contact Form 7» подключает свою собственную таблицу стилей, и хотя сама по себе она невелика, то лучше всё же избежать лишних HTTP-запросов.
Давайте пошагово разберем как.
add_action(’wp_print_styles’, ‘true_otkljuchaem_stili_contact_form’, 100); // по идее вы можете использовать и хук wp_enqueue_scripts, хотя конкретно его я не тестировал function true_otkljuchaem_stili_contact_form(){ wp_deregister_style(’contact-form-7’); // в параметрах — ID подключаемого файла} |
Также иногда при помощи условных тегов файлы плагинов (как CSS, так и JS) отключают только с тех страниц, на которых они не используется.
Ок, с «Contact Form 7» разобрались, а как узнать ID файлов CSS других плагинов?
Да легко, открываем исходный код страницы и видим там подобную картину:
Также есть плагин, который позволит выполнить объединение CSS и JavaScript автоматически — JS & CSS Script Optimizer.
Если у вас остались вопросы, либо я забыл упомянуть о чем-либо в этой статье, пожалуйста, оставьте свой комментарий.
Браузер загружает страницу последовательно. Особенно это актуально для внешних ссылках на файлы — css и javascript. Возьмем для примера блок для сайта lesnoy.name.
<head> <title> Блог Лесного Владислава </title> <link href=»/assets/css/main.css?ver=1.0″ type=»text/css» rel=»stylesheet» /> <link href=»/assets/css/prettify.css» type=»text/css» rel=»stylesheet» /> <script src=»https://steptosleep.ru/wp-content/uploads/2018/06/50418.jpg»></script> <meta charset=»utf-8″ /> <meta name=»viewport» content=»initial-scale=1.0, user-scalable=no» /> </head>
На данном сайте подгружается всего три внешних файла.
2 из них — css стили, и один — js-файл. Но это пример простого сайта, зачастую подгружаются десятки внешних файлов и это существенно влияет на скорость загрузки страницы.
Все бы ничего, но главная проблема в том, что браузер работает следующим образом: при встрече ссылки на внешний файл он загружает и обрабатывает его, приостанавливая отрисовку (рендер) всей остальной страницы.
Т.е., из примера выше видно, что браузер загрузит заголовок сайта (title), затем встретит ссылку на внешний css файл main.css и пойдет загружать его. После загрузки он его обрабатывает и идет дальше — встречает второй css-файл, опять откладывает обработку страницы на потом и работает с prettify.css. Так же и с prettify.js. И лишь потом он принимается за отображения остальной страницы, уже применяя все обработанные ранее css-правила из css файлов и js-кол из javascript файлов.
При медленном интернете либо большом количестве внешних файлов время между переходом на страницу и её отрисовкой может достигать десятков секунд, а то и более минуты. Но ведь разве нельзя, пока грузятся внешние файлы, отображать текст со страницы, за которым, собственно, и пришел посетитель?
Можно, конечно. Самый банальный, но от этого не менее действенный метод — перенос всех не приоритетных внешних файлов с хедера сайта в футер. Т.е. с head-блока как можно ближе к тегу.
Под не приоритетными файлами я подразумеваю те, которые не критичны для функциональности или внешнего вида сайта. Хороший способ разделить большой css или js файл на два — первый маленький хранит в себе то, что должно загрузиться как можно быстрее и размещается в head-секции, а второй и объемный содержит все остальное и располагается как можно ниже в html-коде страницы, не влияя на скорость отображения контента страницы.
Но с приходом html5 можно сделать это проще и красивее. У тега script добавлен параметр async и defer.
<script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/50418.jpg»></script>
Атрибут тега script async делает загрузку js-файлов асинхронным. Те, кто умеют программировать на JS точно знают, как это — асинхронно. Тем же, кто не умеет — расскажу: браузер встречает ссылку на внешний javascript файл (тег script с параметром src), начинает выполнять его загрузку и обработку, но не останавливает при этом загрузку и обработку основной страницы.
Т.е. делает это параллельно. Как раз то, что нам и нужно! И при этом не требуется переносить этот тег в футер (тем более не во всех CMS-системах это просто сделать).
У асинхронной загрузки js-файла асинхронность является как плюсом так и минусом. Ведь размеры файлов скорее всего разные, скорость загрузки и обработки файлов так же не является детерминированной. А значит при асинхронной загрузки нескольких файлов нет никакой гарантии, что файл, который стал загружаться позже не загрузиться в итоге раньше (из-за размера в основном).
<head> … <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/6393.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/21229.jpg»></script> <script src=»https://steptosleep.ru/wp-content/uploads/2018/06/26312.jpg»></script> <script src=»https://steptosleep.ru/wp-content/uploads/2018/06/80419.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/36331.jpg»></script> </head>
В данном примере я не могу точно сказать, какая последовательность выполнения этих js-файлов будет. Я могу точно сказать лишь то, что script4 загрузиться после script3 из-за отсутствия у них атрибута async. Но какой файл из script1.js, script2.js и script5.js загрузиться раньше я не знаю, т.к. они загружаются асинхронно.
«Да и какая нам разница?» — спросите вы. Но она появляется в том случае, если выполнения одного js-скрипта зависит от другого. Такое сейчас сплошь и рядом и самый простой пример такой зависимости — jQuery.
<head> … <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/26695.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/62453.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/47547.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/22007.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/19941.jpg»></script> </head>
В данном случае очень велика вероятность получения ошибки JavaScript из-за того, что какой-либо из jQuery плагинов начнет выполнятся раньше, чем загрузиться сам jQuery.
Что же делать?
Тут нас и выручает другой атрибут script-тега — defer.
<head> … <script defer src=»https://steptosleep.ru/wp-content/uploads/2018/06/26695.jpg»></script> <script defer src=»https://steptosleep.ru/wp-content/uploads/2018/06/62453.jpg»></script> <script defer src=»https://steptosleep.ru/wp-content/uploads/2018/06/47547.jpg»></script> <script defer src=»https://steptosleep.ru/wp-content/uploads/2018/06/22007.jpg»></script> <script async src=»https://steptosleep.ru/wp-content/uploads/2018/06/19941.jpg»></script> </head>
Deferred переводиться с английского языка как «отложенный».
Соответственно deferred javascript load — отложенная загрузка javascript. Если у ссылки на внешний js-файл браузер встречает атрибут defer, то он откладывает загрузку и выполнения этих файлов до тех пор, когда вся страница не будет загружена и отображена. При этом он гарантирует такой же порядок выполнения скриптов, который изначально и был установлен в html-коде.
Соответственно, в нашем примере с jQuery и его плагинами defer нас выручает, выполняя две задачи: время отображения страницы существенно уменьшается (браузер не блокирует отрисовку страницы для загрузки js-файлов, он откладывает загрузку на потом) и при этом мы избавляется от возможных ошибках, связанных с асинхронной загрузкой зависимых друг от друга js файлов.
В примере выше скрипт other_script.js загрузиться асинхронно, т.к. он не зависит от какого-либо другого файла, а jQuery и его плагины загрузятся сразу после отображения страницы в следующем порядке: сначала jquery.min.js, затем по порядку plugin1.jquery.js, plugin2.jquery.js, plugin3.jquery.js.
В итоге с приходом html5 и атрибутов defer и async мы можем намного гибче управлять порядком загрузки внешних js-файлов, тем самым повышая скорость отрисовки веб-страницы. А, главное, для пользователя будет создаваться впечатления значительного повышения скорости загрузки всего веб-сайта в целом, ведь уже через несколько секунд он увидит то, зачем пришел на сайт — информацию.
sergeiss
8.12.2016 — 09:02
Владимир55, погугли на тему 'lazy loading'. Эта штуковина позволяет подгружать необходимые файлы по мере необходимости, а не все сразу. Можно применить к любому файлу, будь то JS или CSS. При этом, естественно, осуществляется контроль, был ли уже загружен этот файл.
Затем, после загрузки, когда она заведомо завершена, можно вызвать callback, который уже будет использовать данные из этого файла.
Lazy loading в чем-то похож на require_once в ПХП. И это название lazy loading не относится к какому-то определенному скрипту, это скорее название алгоритма/паттерна.
Цитата (FatCat @ 8.12.2016 — 02:30) |
1. Чем это лучше чем write()? 2. Асинхронная и отложенная загрузкаА нафига вообще? |
В итоге получаем lazy loading, описанный мной в предыдущем абзаце
2. Чтобы увеличить скорость начальной загрузки страницы. И чтобы уменьшить трафик: если что-то не потребуется, то это вообще не будет загружено.
_____________
* Хэлп по PHP
* Описалово по JavaScript
* Хэлп и СУБД для PostgreSQL
* Обучаю PHP, JS, вёрстке. Интерактивно и качественно. За разумные деньги.
* "накапливаю умение телепатии" (С) и "гуглю за ваш счет" (С)
Владимир55
8.12.2016 — 09:52
Коды не очень сложны, но в организационном плане её реализовать крайне сложно, поскольку она требует участия программиста при наполнении сайта, а это нереально.
У операторов есть норма выработки за день и девочки не будут заниматься никакими кодами. Им нужно вносить инфу с помощью редактора — быстро и много.
Но, в принципе, надо эту идею иметь в виду.
А если сейчас, применительно к поставленному вопросу, подкачку двух файлов сделать так:
function loadCss(href){
varlink=document.createElement("link");
link.rel ="stylesheet";
link.href=href;
document.head.appendChild(link);
}
loadCss(‘file1.css’);
loadCss(‘file2.css’);
Цитата (sergeiss @ 8.12.2016 — 08:02) |
Потому что надо осуществлять контроль, не был ли уже загружен этот файл. |
С контролем через DOM? Месье знает толк в извращениях.
_____________
Бесплатному сыру в дырки не заглядывают…
sergeiss
8.12.2016 — 16:25
Цитата (FatCat @ 8.12.2016 — 12:40) |
Цитата (sergeiss @ 8.12.2016 — 08:02) Потому что надо осуществлять контроль, не был ли уже загружен этот файл. С контролем через DOM? Месье знает толк в извращениях. |
$(‘html’).context.styleSheets
JS? Аналогично:
$(‘html’).context.scripts
Анализируем и, если нету в списке, то грузим.
PS. Можно и без jQuery обойтись
document.getElementsByTagName(‘head’)[0].childNodes
_____________
* Хэлп по PHP
* Описалово по JavaScript
* Хэлп и СУБД для PostgreSQL
* Обучаю PHP, JS, вёрстке. Интерактивно и качественно. За разумные деньги.
* "накапливаю умение телепатии" (С) и "гуглю за ваш счет" (С)
Быстрый ответ:
Powered by dgreen
FILED UNDER : IT