Передо мной возникла одна задачка - повысить Google Speed на одном из интернет-ресурсов.

Задачка ещё та, учитывая то, что большинство пунктов выполнено, но при этом просадка капитальная. А всё из-за чего? Куча метрик, яндекс информеры (оцени Я.Маркет, рейтинг Я.Маркет) и... Jivosite.

Демонстрирую как влияет Jivosite на Google Speed
Демонстрирую как влияет Jivosite на Google Speed

Просадка -14 в мобильном и -8 на десктоп. Эти показатели могут варьироваться в зависимости от времени блокировки потока.

В сети нашел код отложенной загрузки Jivosite, но при первом взгляде он показался мне не надежным, т.к. во первых, уменьшал просадку не всю, а во вторых, при загрузке отключал и инициализировал заново некоторые JS события Jivosite. А это чревато, при обновлении движка чата.

Я пошел иным путём. Решил ПОЛНОСТЬЮ убрать влияние Jivosite на Google Speed. Каким образом?

  1. Убрать влияние чата на показатели можно лишь в одном случае - не загружая его вовсе. (Ахах, классный выход из ситуации :D). Не-не, слушай дальше.

  2. Инициализируем Jivosite при первом визите на сайт, ТОЛЬКО после того, как пользователь куда-нибудь нажмет, или проскроллит страницу, либо подвигает мышью.

Логично? Ведь Google Speed совершенно бездействует на сайте. Таким образом для него чата не будет вовсе, а для пользователя в первый визит он появится после какого-либо действия. При последующих же визитах всё стандартно.

И так, релиз в пост!

  • Вначале проверяем в сессии существование ключа, который свидетельствует о том, что ранее отложенная по действию пользователя загрузка Jivosite уже осуществлялась, а значит загружаем в обычном режиме.

    Я сделал именно сессией т.к. на больших проектах желательно на куках экономить, дабы не вывалило кому-то ошибку 500 из-за большого количества кук.
    Но, если Вы владелец сайта не на фреймворке с единой точкой входа, то в этом случае целесообразнее использовать куки.

<?php if ( !isset($_SESSION['jivoLazy']) ) { ?>
	<?php $_SESSION['jivoLazy'] = 'ready'; ?>
	<!-- Здесь будет новый способ загрузки, отложенный -->		
<?php } else { ?>
	<!-- Сюда впишем обычный способ загрузки Jivosite -->
<?php } ?>
  • Уже прилично давно Jivosite упростила код инициализации своего чата. Обычный инициализатор выглядит вот так:

<script src="//code.jivosite.com/widget.js" jv-id="#widgetID#" async></script>
  • Я не приверженец какой-либо кастомизации стороннего кода, но именно один раз нам придется его загрузить старым способом, который раньше предлагал Jivo. Это связано с тем, что как я не крутил с новой версией инициализатора, при отложенной загрузке он не видит ID виджета.
    По этому вот старый инициализатор Jivosite

(function(){ var widget_id = '#widgetId#';var d=document;var w=window;function l(){
var s = document.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);}if(d.readyState=='complete'){l();}else{if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
  • Теперь напишем просто JS функцию, которая будет инициализировать чат при отложенной загрузке.

function jivoAsync()
{
	if ( typeof window.jivoLazyReady === 'undefined' )
	{
		window.jivoLazyReady = true;
		window.jivoLazyTimeout = setTimeout(function()
		{
      // Сюда вставляем старый способ инициализации Jivo
      // Я его привёл выше
      console.log('jivosite load from lazy');
      clearTimeout(window.jivoLazyTimeout);
    },
		1000);
	}
}
  • В данной функции мы проверяем был ли уже ранее загружен чат, если нет, то загружаем.

  • Далее инициализируем функцию jivoAsync()
    В данном инициализаторе мы слушаем события Движение мыши, Клик, Скроллинг страницы. И инициализируем написанную нами функцию jivoAsync.

['mouseover','click','scroll'].forEach(function(event)
{
	var elm = event == 'click' ? document.getElementsByTagName('body')[0] : window;

  if ( typeof window.addEventListener === 'undefined' ) {
  	elm.addEvent(event, jivoAsync);
  } else {
    elm.addEventListener(event, jivoAsync, false);
  }
});

Данный скрипт инициализирует чат только 1 раз. Ведь кликов, движений мыши, скроллинга может быть много, но функция контролирует этот момент.

В мобильном срабатывают события Клик куда угодно и скроллинг страницы.

Таким образом Jivosite загрузится через секунду после того, как пользователь первый раз вошел на сайт и что-либо сделал на нём. Согласитесь, чат не очень-то и нужен прямо сразу. Ну а при сёрфинге Jivosite загрузится уже стандартно.

Полный код примера
<?php if ( !isset($_SESSION['jivoLazy']) ) { ?>
  <?php $_SESSION['jivoLazy'] = 'ready'; ?>

  <script type="text/javascript">
    function jivoAsync()
    {
      if ( typeof window.jivoLazyReady === 'undefined' )
      {
        window.jivoLazyReady = true;
        window.jivoLazyTimeout = setTimeout(function()
                                            {
          (function(){ var widget_id = '#widgetID#';var d=document;var w=window;function l(){
            var s = document.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);}if(d.readyState=='complete'){l();}else{if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();

          console.log('jivosite load from lazy');
          clearTimeout(window.jivoLazyTimeout);
        },
        1000);
      }
    }

    ['mouseover','click','scroll'].forEach(function(event)
		{
      var elm = event == 'click' ? document.getElementsByTagName('body')[0] : window;

      if ( typeof window.addEventListener === 'undefined' ) {
        elm.addEvent(event, jivoAsync);
      } else {
        elm.addEventListener(event, jivoAsync, false);
      }
    });
  </script>
<?php } else { ?>
	<script src="//code.jivosite.com/widget.js" jv-id="#widjetID#" async></script>
<?php } ?>

Поздравляю, Google Speed добавил +10-15 пунктов на Вашем проекте!

Не забудьте в двух местах #widgetID# изменить на ID виджета Jivosite.

Всем пока, всем мира.

Комментарии (39)


  1. namikiri
    23.11.2021 12:43
    +4

    Google Speed это, конечно, мило и здорово, но грошь цена вашим разработкам, если этот треклятый живосайт специально дожидается клика пользователя по странице, чтобы не просто ВНЕЗАПНО выскочить поверх всего, но ещё и издать звук.


    1. webSasha1990 Автор
      23.11.2021 15:00

      Ну он же и так выскакивает). Тут вопрос в приоритетах на конкретном проекте. У нас поставлен вот такой приоритет. Что лучше пользователю 1 раз в сессии пострадать, чем просадка -10)


      1. namikiri
        23.11.2021 23:21

        Лучше убрать или спрятать его до востребования самим пользователем.


  1. AlexeyCaTHaR
    23.11.2021 12:55
    +1

    Сейчас бы для каждой строчки писать

    <?php .... ?>

    И мои супер любимые конструкции вида

    <?php } ?>

    Что же вы так не любите всех, кто после вас будет работать с кодом?


    1. webSasha1990 Автор
      23.11.2021 15:02

      Просто внутри будет Javascript) мне показалось так читабельнее написать.


  1. mobi
    23.11.2021 13:01
    +2

    1. А зачем делать clearTimeout в обработчике setTimeout?
    2. Зачем вообще ради какой-то пузомерки заставлять пользователей страдать еще дольше, так как теперь мало дождаться загрузки страницы, она ведь еще будет "лагать" какое-то время (те самые 1,46 секунды из отчета PSI/Lighthouse) после первого скролла (причем не сразу, а через 1 секунду, см. setTimeout).


    1. webSasha1990 Автор
      23.11.2021 14:57

      clearTimeout - очищаю инициализатор. А так можно и не чистить конечно.

      А по поводу страданий пользователя - тут конечно каждому своё). На моём проекте приоритет отдается скорости Google Speed. Ну а так кому как конечно. Если важно прогружать чат сазу - то так не пойдет.

      setTimeout можно попробовать убрать, я его сделал чтоб 100500 скролла не было, возможно лишнее.


      1. mobi
        23.11.2021 19:58
        +2

        Вы, наверное, путаете setTimeout и setInterval, т.к. первый никогда не будет вызван дважды (поэтому результат setTimeout нужно сохранять только в одном случае: если нужно будет отменить таймер до того, как он будет вызван). Если же цель в том, чтобы удалить переменную, которую непонятно зачем создали в объекте window, то для этого есть оператор delete. Как-то так.


      1. mobi
        23.11.2021 19:59

        А еще хорошим тоном считается отписываться от событий, когда это уже не нужно.


  1. Druj
    23.11.2021 15:04

    Автор изобрёл клоакинг, поздравляю.


    1. webSasha1990 Автор
      23.11.2021 15:13

      Он ведь не вредит никому, при том один раз в сессию. Это мизер неудобств.


      1. OlegIva
        26.11.2021 11:19

        Он ведь не вредит никому

        Мне, например, такая подлянка с чатом вредит, вплоть до отвращения к сайту и ухода с него. Следовательно, ваше утверждение ложно. Но 10 долларов попугаев Google speed - это 10 попугаев...


        1. webSasha1990 Автор
          26.11.2021 18:17

          Вы же бездействуете на сайте пару секунд. Если не знать о lazy чата, то разница на мой взгляд несущественна. Наоборот, вы зашли и ничего не выскакивает, поскролили - высочило. При том такая прогрузка лишь при первом визите.

          Но кому как. Я не настаиваю на абсолютной целесообразности способа. Просто когда мобила всего 20, 10 попугаев уже играет роль, когда заказчику это важно.


          1. OlegIva
            26.11.2021 18:36

            зашли и ничего не выскакивает, поскролили - высочило

            Вот это и убивает напрочь.


  1. Acsac
    23.11.2021 17:07

    Pagespeed это просто тест. Данные о скорости сайта есть синтетические и реальные полевые. Поисковик собирает данные по реальным. Об этом написано в описании Pagespeed например. Если есть субъективные факторы понижения баллов именно в тесте, стоит ли так гнаться за синтетикой? Всё равно скорость будет фиксироваться по Network браузера каждого посетителя, а у каждого своё железо и свой канал. Может стоит таки читать описание теста?)


    1. webSasha1990 Автор
      23.11.2021 18:01
      +1

      Не всегда можно не делать то, что просят сделать). У меня была конкретная задача, подтянуть Google Speed.

      Мы вертелись как могли XD)


      1. Acsac
        23.11.2021 20:04

        И продолжаете писать тут что Pagespeed так важен. Надо объяснять же тем кто не понимает, что Pagespeed просто образец синтетик проверки, которая не зависит зачастую от скорости сайта как её измеряют поисковики. Люди понятно что им проще глянуть 1 тест цифры, чем хотя бы прочесть описание теста, но надо объяснять, а не культивировать дальше мифы.


        1. webSasha1990 Автор
          23.11.2021 20:32

          Я в первую очередь написал это для тех, кому это нужно. Даже на сайте Jivosite описана данная проблема. У меня она также возникла, нашел решение. А в СЕО я слаб, не могу проанализировать с этой стороны. Была задача. Вот решение. Плохое или хорошее решать вам. На проекте всех устроило. Поэтом решил поделиться здесь.


          1. Acsac
            23.11.2021 20:41

            Это не SEO - это описание теста Pagespeed. Полевое и синтетическое исследование. Бьётесь за то, за что бороться не стоит. Ибо вместо цифр синтетики в тесте смотреть надо Network - чтобы понимать тормозит Jivosite сайт или нет.


            1. webSasha1990 Автор
              23.11.2021 20:48

              Вы предлагаете в этом случае не делать задачу?) я и не говорил про тормоза сайта, я говорил про проседания г. спид. Может еще кому-то надо за счет jivo её поднять. Если не надо, то и не надо. Чат для пользователя не влияет на скорость погрузки информации.


              1. Acsac
                23.11.2021 21:07

                Вот стоило бы замерить скорость именно, а не баллы за синтетик оценку, показать заказчику, дать почитать описание Pagespeed - документация. То что вы сделали хорошо, но бесполезно в плане скорости.


  1. Denai
    23.11.2021 18:52

    Почитайте что такое фасады


  1. YChebotaev
    23.11.2021 20:18
    +4

    clearTimeout(window.jivoLazyTimeout);

    Лишняя конструкция

    document.getElementsByTagName('body')[0]

    Можно просто `document.body`

    if ( typeof window.addEventListener === 'undefined' ) {
    	elm.addEvent(event, jivoAsync);
    } else {
    	elm.addEventListener(event, jivoAsync, false);
    }

    IE8 поддерживаете?

    В этом же фрагменте не увидел как вы снимаете листенер. Получается, он у вас на каждый скролл, клик и движение мышкой отрабатывает, но потом выходит из функции jivoAsync на строчке `if ( typeof window.jivoLazyReady === 'undefined' )`. Не идеально.

    Лучше так (с учетом предыдущего замечания):

    var eventTypes = ['mouseover', 'click', 'scroll']
    
    function listener() {
      var target = this
      
      jivoAsync()
      
      eventTypes.forEach(function(eventType) {
        target.removeEventListener(eventType, listener)
      })
    }
    
    events.forEach(function(eventType) {
      var elm = event === 'click' ? document.body : window
    
      elm.addEventListener(eventType, listener)
    })

    Теперь, благодаря тому что `jivoAsync` запускается только один раз, удастся избавиться от глобальных переменных и проверок внутри.

    Также, поскольку код виджета был предназначен для использования внутри `<script>`, оборачивать код инициализации виджета в функцию нет смысла.

    function jivoAsync() {
      setTimeout(function() {
      	var widget_id = '#widgetID#';
        var d = document;
        var w = window;
    
        function l() {
            var s = document.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);
        }
        if (d.readyState == 'complete') {
            l();
        } else {
            if (w.attachEvent) {
                w.attachEvent('onload', l);
            } else {
                w.addEventListener('load', l, false);
            }
        }
      }, 1000)
    }

    Поскольку у нас на момент таймаута страница уже загрузилась, то код инициализации виджета можно сильно сократить. Также, нет никакого смысла выискивать первый скрипт на странице, можно просто вставить в конец body:

    function jivoAsync() {
      setTimeout(function() {
        var widget_id = '#widgetID#';
        var s = document.createElement('script');
        s.type = 'text/javascript';
        s.async = true;
        s.src = '//code.jivosite.com/script/widget/' + widget_id;
        document.body.appendChild(s)
      }, 1000)
    }

    Это то что в глаза бросилось


    1. webSasha1990 Автор
      23.11.2021 20:37

      С removeEventListener спасибо. Надо будет допилить)


    1. webSasha1990 Автор
      23.11.2021 20:55

      Но, позвольте, разве removeEventListener не удалит чужие экшны? Это здесь код чистый. А на реальном проекте 100500 скриптов. Т.к. window, body общие для всех элементы, разве это безопасно?


      1. set
        23.11.2021 21:41

        Нет. Ровно тот «слушатель», который вы навесили, вы и удаляете.


        1. webSasha1990 Автор
          23.11.2021 22:25

          Спасибо, прокачали мне знания)


  1. zkrvndm
    24.11.2021 10:01
    +1

    Пффф, с таким же успехом я могу вообще не грузить ничего кроме ключей и текста, а затем уже как будет клик или прокрутка подгружать реальную страницу со всеми картинками и структурой.


    1. webSasha1990 Автор
      24.11.2021 10:02

      Про картинки - так и делают) называется ленивая загрузка.


  1. kvadrokot
    24.11.2021 12:34
    +1

    а раскажите поподробнее про "Я сделал именно сессией т.к. на больших проектах желательно на куках экономить, дабы не вывалило кому-то ошибку 500 из-за большого количества кук."

    как 500ая ошибка может появиться из-за большого кол-ва кук?


    1. webSasha1990 Автор
      24.11.2021 12:35
      -1

      Если на сайте много кук, и их общий вес слишком большой, то да, в этом случае выскакивает у пользователя 500.


      1. kvadrokot
        24.11.2021 17:02
        +1

        куки не хранятся "на сайте", куки хранятся только в браузере пользователя


        1. webSasha1990 Автор
          24.11.2021 20:26
          -1

          Куки отправляет из браузера на сервер. Если их вес слишком велик, сервер выдает 500


          1. kvadrokot
            25.11.2021 11:31
            +1

            а насколько "большие" должны быть куки, чтобы сервер выдал 500?

            в вашем случае достаточно было добавить 1 куку - jivoLazy=ready


            1. webSasha1990 Автор
              25.11.2021 12:28
              -1

              Кука да куки, 2 куки) дело не в том, что она маленькая, дело в экономии. На боевом сайте с метриками и прочим, очень много кук. И экономия, как по мне, не лишнее.

              Чтобы выдало 500 зависит от настроек сервера. По-моему размер буфера для клиента. Возможно ошибаюсь, но чаще всего он 8кб-16кб, можно ручками расширить, если позволяет хостинг.


              1. kvadrokot
                25.11.2021 13:48
                +1

                ни разу не встречал такого размера кук

                и как-то странно "экономит"ь 16 байт из 8кбайт, храня что-то дополнительно в сессии (а она как раз на сервере хранится)


                1. webSasha1990 Автор
                  25.11.2021 16:47
                  -1

                  Дело вкуса)


  1. wadowad
    24.11.2021 17:29

    Я бы стал грузить код не сразу при скролле, а по завершении первого скролла при остановке. Тогда, для пользователя по ощущениям "тормозов" должно быть меньше.

    P.S. И сюда бы ещё завернуть всякие всякие метрики и аналитиксы. Только наверно поведенческие факторы у сайта понизятся.


    1. webSasha1990 Автор
      24.11.2021 17:31

      По сути setTimeout в 1с и говорит о завершении скролла.

      По метрикам. аналитике - да, точность упадет.