Ускорение работы сайта. Боремся с Google PageSpeed
Rus
Eng
Ускорение работы сайта. Боремся с Google Page Speed

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

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

Прелоад файлов и классов на 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;
   }
}

Комментарии

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

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

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

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

Отправить

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

Напишите мне

Вы разрабатываете новый сервис, вносите доработки в существующий и хотите лучше чем у конкурентов? Вы обратились по адресу. Предлагаю вам комплексную разработку сайтов студийного уровня. У меня вы можете заказать дизайн, верстку, програмированние, разработку нетрадиционного функционала, реализацию связи между CMS, CRM и Data Analitics, а так же все остальное касаемое сайтов, кроме продвижения.

Обращайтесь, я всегда проконсультирую по всем вопросам и помогу подобрать наиболее эффективное решение для Вашего бизнеса. Я занимаюсь созданием сайтов в Новосибирске и в других регионах России, также работаю со странами СНГ. Вы останетесь довольны нашим сотрудничеством

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

Телефоны

+7(993) 007-18-96

Email

info@tichiy.ru

Адрес

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

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

Написать мне
Отправить
Отправляя форму Вы автоматически подтверждаете, что ознакомились и принимаете Политику конфиденциальности сайта
Отправка успешна!
Thank you for your feedback. I will answer you within the next working hours
Отправка не удалась
Во время отправки запроса произошла ошибка. Пожалуйста, подождите и попробуйте снова через некоторое время или свяжитесь со мной