Ускорение работы сайта. Боремся с Google PageSpeed
Rus
Eng
Ускорение работы сайта. Боремся с Google Page Speed
Решаю сложные технические задачи для Bitrix и 1С: от аудита чужого кода до разработки сайтов и настройки сложных интеграций с гарантией результата
Рекомендую

Хостинг для сайта

Если выбираете хостинг под проект — советую Timeweb. Современная инфраструктура, широкий выбор тарифов от стартовых до VPS под нагрузку, отзывчивая техподдержка, которая отвечает по существу, а не отписками. Я сам держу на нём часть своих проектов и рекомендую клиентам.

Перейти Партнёрская ссылка. Для вас цена не меняется.

Данный материал является агрегативным и написан более для себя. По мере работы и накопления опыта обновляю его.

Используйте описанные методы на свой страх и риск.

Прелоад файлов и классов на php 7.4

В php 7.4 появилась возможность использовать предзагрузку скриптов и файлов проекта для постоянного хранения их в памяти. Воспользуемся этим, подробно здесь

.htaccess для Битрикс

Настраиваем сжатие и устанавливаем время кеширования для ресурсов. Подробно здесь

Использование preload для ускорения

Указание спецификации preload позволяет браузеру установить правильный приоритет и способ загрузки в соответствии с типом загружаемого контента. Плюс, это позволяет пройти один тест Google Page Speed

<link rel="preload" href="/js/script.js" as="script">
<link rel="preload" href="/fonts/font.woff2" as="font" type="font/woff2" crossorigin>

Асинхронная загрузка скриптов и стилей

<link rel="preload" as="style" href="async.css" onload="this.rel='stylesheet'"> //асинхронно загружаем css

<script async src="async.js"></script> //асинхронно загружаем js

Отложенная загрузка скриптов

Выполняем загрузку скрипта после инициализации страницы

function loadScript(src) {
  let script = document.createElement('script');
  script.src = src;
  script.async = false; //Можем поставить async = true
  document.body.append(script);
}

loadScript("lazy.js");

Предварительная загрузка скрипта с отсроченным выполнением

<link href="async.js" rel="preload" as="script" onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">

Более подробно

Отложенная загрузка GTM

Помещаем скрипт GTM в данную конструкцию

function goodbyeGTM() {
	(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
			new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
		j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
		'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
	})(window,document,'script','dataLayer','YOUR_WIDJET_ID');
}

setTimeout(goodbyeGTM, 1000);

Еще один вариант откладывания загрузки - подписка на событие скролла и вызов скрипта, только после его движения. За способ спасибо Игорю Воротнёву

var fired = false;

window.addEventListener('scroll', () => {
    if (fired === false) {
        fired = true;
        
        setTimeout(() => {
            // Здесь все эти тормознутые трекеры, чаты и прочая ересь,
            // без которой жить не может отдел маркетинга, и которые
            // дико бесят разработчиков, когда тот же маркетинг приходит
            // с вопросом "почему сайт медленно грузится, нам гугл сказал"
        }, 1000)
    }
});

Отложенная загрузка jivosite

(function(){ document.jivositeloaded=0;var widget_id = 'ВАШ_ВИДЖЕТ_ID';var d=document;var w=window;function l(){var s = d.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = '//code.jivosite.com/script/widget/'+widget_id; var ss = document.getElementsByTagName('script')[0]; ss.parentNode.insertBefore(s, ss);}//эта строка обычная для кода JivoSite
	function zy(){
		//удаляем EventListeners
		if(w.detachEvent){//поддержка IE8
			w.detachEvent('onscroll',zy);
			w.detachEvent('onmousemove',zy);
			w.detachEvent('ontouchmove',zy);
			w.detachEvent('onresize',zy);
		}else {
			w.removeEventListener("scroll", zy, false);
			w.removeEventListener("mousemove", zy, false);
			w.removeEventListener("touchmove", zy, false);
			w.removeEventListener("resize", zy, false);
		}
		//запускаем функцию загрузки JivoSite
		if(d.readyState=='complete'){l();}else{if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}
		//Устанавливаем куку по которой отличаем первый и второй хит
		var cookie_date = new Date ( );
		cookie_date.setTime ( cookie_date.getTime()+60*60*28*1000); //24 часа для Москвы
		d.cookie = "JivoSiteLoaded=1;path=/;expires=" + cookie_date.toGMTString();
	}
	if (d.cookie.search ( 'JivoSiteLoaded' )<0){//проверяем, первый ли это визит на наш сайт, если да, то назначаем EventListeners на события прокрутки, изменения размера окна браузера и скроллинга на ПК и мобильных устройствах, для отложенной загрузке JivoSite.
		if(w.attachEvent){// поддержка IE8
			w.attachEvent('onscroll',zy);
			w.attachEvent('onmousemove',zy);
			w.attachEvent('ontouchmove',zy);
			w.attachEvent('onresize',zy);
		}else {
			w.addEventListener("scroll", zy, {capture: false, passive: true});
			w.addEventListener("mousemove", zy, {capture: false, passive: true});
			w.addEventListener("touchmove", zy, {capture: false, passive: true});
			w.addEventListener("resize", zy, {capture: false, passive: true});
		}
	}else {zy();}
})();

Отложенная загрузка vk.com

Достаточно добавить атрибут async

<script type="text/javascript" async>!function(){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src="https://vk.com/js/api/openapi.js?162",t.onload=function(){VK.Retargeting.Init("ВАШ_ВИДЖЕТ_ID"),VK.Retargeting.Hit()},document.head.appendChild(t)}();</script><noscript><img src="https://vk.com/rtrg?p=ВАШ_ВИДЖЕТ_ID" style="position:fixed; left:-999px;" alt=""/></noscript>

Отложенная загрузка изображений

document.addEventListener('DOMContentLoaded', function(){
    //Блок замены src изображений для товаров в галерее
    if(document.querySelector( 'js-zoom__pic' )){
        let zoomPic = document.querySelectorAll('js-zoom__pic');

        for (let i = 0; i < zoomPic.length; i++){
            if(zoomPic[i].getAttribute('data-src')){
                zoomPic[i].src = zoomPic[i].getAttribute('data-src');
            }
        }
    }

}, false);

Отдельный более сложный вариант javascript lazy-load

Отложенная загрузка iframe

document.addEventListener('DOMContentLoaded', function(){
    if(document.querySelector( 'js-lazy__iframe' )){
        let lazyIframe = document.querySelectorAll('js-lazy__iframe');

        for (let i = 0; i < lazyIframe.length; i++){
            if(lazyIframe[i].getAttribute('data-src')){
                lazyIframe[i].src = lazyIframe[i].getAttribute('js-lazy__iframe');
            }
        }
    }
}, false);

Отложенная загрузка Яндекс карт

function loadScript(src) {
	let script = document.createElement('script');
	script.src = src;
	script.async = false; //Можем поставить async = true
	document.body.append(script);
}

function mapLoad() {
	if (ymaps) {
		let spbMap;
		let moscowMap;
		function init() {
			spbMap = new ymaps.Map('map_spb', {
				center: [59, 30],
				zoom: 15,
			}, {
				searchControlProvider: 'yandex#search',
			});
			moscowMap = new ymaps.Map('map_moscow', {
				center: [55, 37],
				zoom: 15,
			}, {
				searchControlProvider: 'yandex#search',
			});

			spbMap.geoObjects.add(new ymaps.Placemark([59.924812, 30.303085]));
			moscowMap.geoObjects.add(new ymaps.Placemark([55.675352, 37.470662]));
		}
		window.addEventListener('resizeYandexMap', function (e) {
			moscowMap.container.fitToViewport();
			spbMap.container.fitToViewport();
		});
		ymaps.ready(init);
	}
}

document.addEventListener("DOMContentLoaded", () => {
	loadScript('https://api-maps.yandex.ru/2.1/?lang=ru_RU&amp;amp;apikey=API_KEY');
	setTimeout(mapLoad, 2000);
});

Отложенная загрузка youtube

Вместо iframe используем свой div

<div class="youtube" id="8XLWGW109876" style="width:100%;height:100%;"></div>

js

document.addEventListener('DOMContentLoaded', function(){
    /* Блок замены youtube video */
    var videos = document.getElementsByClassName("youtube");
    var nb_videos = videos.length;

    for (var i=0; i<nb_videos; i++) {
        // Based on the YouTube ID, we can easily find the thumbnail image
        videos[i].style.backgroundImage = 'url(http://i.ytimg.com/vi/' + videos[i].id + '/maxresdefault.jpg)';

        // Overlay the Play icon to make it look like a video player
        var play = document.createElement("div");
        play.setAttribute("class","play");
        videos[i].appendChild(play);

        videos[i].onclick = function() {
            // Create an iFrame with autoplay set to true
            var iframe = document.createElement("iframe");
            var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1&enablejsapi=1";
            if (this.getAttribute("data-params")) iframe_url+='&'+this.getAttribute("data-params");
            iframe.setAttribute("src",iframe_url);
            iframe.setAttribute("frameborder",'0');

            // The height and width of the iFrame should be the same as parent
            iframe.style.width  = this.style.width;
            iframe.style.height = this.style.height;

            // Replace the YouTube thumbnail with YouTube Player
            this.parentNode.replaceChild(iframe, this);
        }
    }
}, false);

css

.youtube {background-position: center;background-repeat: no-repeat;position: relative;display: block;overflow: hidden;transition: all 200ms ease-out;cursor: pointer;}
.youtube .play {background-image: url('/upload/youtube.svg');background-position: center center;background-repeat: no-repeat;background-size: 64px 64px;position: absolute;height: 100%;width: 100%;opacity: .8;filter: alpha(opacity=80);-webkit-transition: all 0.2s ease-out;-moz-transition: all 0.2s ease-out;-o-transition: all 0.2s ease-out;transition: all 0.2s ease-out;}
.youtube .play:hover {opacity: 1;filter: alpha(opacity=100);}

Оптимизируем изображения

Режем излишне большие изображения с помощью CFile::ResizeImageGet. Подробно здесь

Переводим все изображения на сайте в формат webP. Подробно здесь

Исправляем Настройте показ всего текста во время загрузки веб-шрифтов

Во все вызовы font-face добавьте свойство font-display, например так:

@font-face {
    font-family: Roboto;
    font-style: normal;
    font-weight: 700;
    src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v18/KFOlCnqEu92Fr1MmWUlfCRc9.ttf) format('truetype');
    font-display: swap;
}

Более быстрый способ получения SECTION_PAGE_URL

// этой функцией будем стоить урл
function makeUrl($parentId = 0){
 
    global $sections;
 
    $sefFolder = '/katalog/';       
 
    $codes = array();
 
    while (isset($parentId)) {
        $codes[] = $sections[$parentId]['CODE'];
        if(isset($sections[$parentId])){
            $parentId = $sections[$parentId]['IBLOCK_SECTION_ID'];
        }else{
            $parentId = null;
        }
    }
 
    if (sizeof($codes)) {
        return $sefFolder . implode('/', array_reverse($codes));
    } else {
        return null;
    }   
}
 
$sections = array();
 
$resCatalog = CIBlockSection::GetList(
    array(
        'LEFT_MARGIN' => 'ASC'
    ), 
    array(
        'IBLOCK_ID' => $arParams['IBLOCK_ID'], 
        'ACTIVE' => 'Y', 
        'GLOBAL_ACTIVE' => 'Y',
    ), 
    false, 
    array(
        'IBLOCK_ID', 
        'IBLOCK_SECTION_ID', 
        'NAME', 
        'SECTION_PAGE_URL', 
        'UF_*'
    )
);
 
while($arCatalog = $resCatalog->Fetch()){
    $sections[$arCatalog['ID']] = $arCatalog;
}
 
foreach($sections as &$section){
    $section['SECTION_PAGE_URL'] = makeUrl($section['ID']);
}

За подсказку спасибо Olegpro

Оптимизация запросов

Во всех запросах следует отказаться от GetNext() и GetNextElement() в пользу Fetch(). Использование GetNext() оправдано только если нужно получить DETAIL_PAGE_URL или SECTION_PAGE_URL

При фильтрации по свойству типа список следует отказаться от конструкции PROPERTY_VALUE в пользу фильтрации по ID свойства

При фильтрации HL блоков по точному соответствию - следует явно указывать = в свойстве

Быстрое получение значения свойства элемента инфоблока по id свойства

$el = \ElementPropertyTable::getList([
	'filter'=>['IBLOCK_ELEMENT_ID'=>$arParams['ELEMENT_ID'], 'IBLOCK_PROPERTY_ID'=>PROPERTY_ID], //где PROPERTY_ID это id свойства инфоблока
	'select'=>['VALUE']
])->fetch();

Выгрузка больших данных

Оригинал

$tokenID = 'XXXXXXXXXXXXXXXXXXXXX';
$host = 'XXXX.bitrix24.ru';
$user = 1;

/**
 * Начинаем с нуля или с какого то предыдущего шага
 */
$leadID = 0;
$finish = false;

while (!$finish)
{
   /**
    * Выполняем пока не заберем все данные, в этом случае не стоит забывать и про задержку между хитами.
    * Либо каждый раз выбираем только 50, начиная с того элемента, на котором остановилась прошлая итерация
   */

   $http = new \Bitrix\Main\Web\HttpClient();

   $http->setTimeout(5);
   $http->setStreamTimeout(50);

   $json = $http->post(
      'https://'.$host.'/rest/'.$user.'/'.$tokenID.'/crm.lead.list/',
      [
         'order' => ['ID' => 'ASC'],
         'filter' => ['>ID' => $leadID],
         'select' => ['ID', 'TITLE', 'DATE_CREATE'],
         'start' => -1
      ]
   );

   $result = \Bitrix\Main\Web\Json::decode($json);
   if (count($result['result']) > 0)
   {
      foreach ($result['result'] as $lead)
      {
         $leadID = $lead['ID'];
      }
   }
   else
   {
      $finish = true;
   }
}
Александр Тихий
Автор статьи

Александр Тихий

Fullstack-разработчик · 1С-Битрикс

Разрабатываю и развиваю интернет-магазины и корпоративные порталы на 1С-Битрикс. Интеграции с 1С, highload-нагрузки, безопасность по 152-ФЗ.


Комментарии

Комментариев еще нет, Вы можете стать первым кто его оставит

Оставьте комментарий

На сайте используется система премодерирования комментариев, поэтому ваше сообщение будет опубликовано лишь после одобрения модератором

Вы отвечаете на комментарий пользователя

Отправить

ОБРАТНАЯ СВЯЗЬ

Напишите мне

Напишите мне, чтобы обсудить задачу. Работаю удаленно, официально (самозанятый), стоимость часа — 1800 ₽, в день могу уделять проекту 3–4 часа. Отвечаю быстро, консультация бесплатная.

Во время отправки произошла ошибка, пожалуйста попробуйте еще раз через некоторое время
Сообщение отправлено успешно

Телефоны

+7(993) 007-18-96

Email

info@tichiy.ru

Адрес

Россия, г. Москва

Отправляя форму Вы автоматически подтверждаете, что ознакомились и принимаете Политику конфиденциальности сайта