В одном из моих недавних клиентских проектов я столкнулся с проблемой загрузки объявлений Google AdSense.

Как вы понимаете, я использую React для разработки этого сайта. Одна из особенностей React заключается в том, что он может оптимизировать свои модификации DOM. Он обновляет только то, что необходимо. Это топ.

За исключением Google AdSense. Это меньше вершины.

Как работает AdSense Ad Loading?

Давайте посмотрим на код, который Google просит нас добавить на нашу страницу для загрузки рекламы.

<ins class="adsbygoogle"
style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-1234567890123456"
data-ad-slot="1234567890"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>

Объявление появится там, где мы поместили тег ‘‹ins› ‹/ins›’.

Затем вы должны уведомить скрипт Google AdSense о том, что есть реклама для отображения. Это работа мини-строки JavaScript в приведенном выше примере. По сути, эта строка добавляет пустой объект в массив под названием ‘adsbygoogle’.

Почему? Поскольку загрузка скрипта Google JS выполняется асинхронно, чтобы знать, что есть X рекламных объявлений для отображения, он использует эту таблицу. Чтение таблицы при загрузке скриптов позволяет всегда знать, сколько рекламы необходимо.

После загрузки скрипта Google он не только читает таблицу, но и заменяет метод 'push' этой таблицы, чтобы напрямую загружать рекламу, когда вы делаете 'adsbygoogle.push ({})'.

Скрипт не будет проверять массив через определенные промежутки времени. Он вызывается, когда (и только когда) мы вызываем ‘.push’.

Показывать рекламу с помощью React

Версия этого кода для React практически не изменилась. Смотреть :

|const Ad = props => {
  useEffect(() => {
    window.adsbygoogle = window.adsbygoogle || []
    window.adsbygoogle.push({})
  })
  
  return (
    <div>
      <ins 
        className="adsbygoogle"
	    style="display:inline-block;width:728px;height:90px"
	    data-ad-client="ca-pub-1234567890123456"
	    data-ad-slot="1234567890"
      />
    </div>
  )
}

Мы находим тег ‘‹ ins / ›’ и ‘.push ({})’. Отправка выполняется в ‘useEffect’, поэтому она выполняется сразу после визуализации HTML-кода.

'useEffect' также позволяет повторно выполнить 'push', если HTML-код переписан Реагируйте по той или иной причине. Что интересно, потому что мы не загружаем рекламу непосредственно в этот компонент. Мы просто создаем элементы '‹ins /›' в DOM и запрашиваем (через 'push ({})' ) скрипт гугла для показа там рекламы.

Поэтому, если по какой-то причине React удаляет '‹ins /›' и воссоздает его, мы просим скрипт Google загрузить в него новую рекламу, используя 'useEffect'.

Ошибка сбить

К сожалению, это оптимизированное поведение React может вызвать новую ошибку:

|uncaught exception: TagError: adsbygoogle.push() error: All ins elements in the DOM with class=adsbygoogle already have ads in them.

Google проверяет, не запрашивает ли мы слишком много рекламы относительно мест, доступных на странице.

Я долго искал решение этой проблемы, потому что не понял ошибки: у меня есть именно '.push ({})' для '‹Ins /›' в моем коде. Я не понимаю, как можно слишком часто вызывать метод ‘.push ({})’.

Что ж, все сделал React. Или почти.

React оптимизирован и (почти) всегда хорош

Вернемся к оптимизированному способу модификации DOM в React.

Некоторые внешние по отношению к компоненту события могут указывать React на необходимость обновления этого компонента. Например, изменение свойств родительского компонента и т. Д.

Когда React хочет обновить компонент, он выполняет методы жизненного цикла компонента, включая его метод render. Таким образом, он извлекает дерево компонентов, которые происходят от этого компонента. Для « компонентных функций » это вопрос выполнения перехватчиков и восстановления возврата функции компонента.

То же самое для каждого уровня иерархии компонентов. В результате React дает полное дерево, представляющее нашу страницу. Мы также называем это виртуальным DOM. Теперь React сравнит виртуальную модель DOM с моделью DOM и обнаружит различия. Таким образом, он может вносить только необходимые изменения, а не полное обновление DOM для увеличения скорости.

И это описывает нашу ошибку. Перехватчики / методы жизненного цикла компонента всегда выполняются, но DOM не обязательно изменяется. Код для нашего компонента ‘‹ Ad / ›’ очень прост, его редко нужно обновлять. С другой стороны, внешние события могут заставить React выполнять хуки / методы жизненного цикла обновления.

Поэтому ‘.push ({})’ иногда выполняется в тех случаях, когда часть DOM, касающаяся нашей рекламы, не обновляется. Поскольку модель DOM не обновляется, реклама уже присутствует в теге ‘‹ ins / ›’ компонента. Таким образом, код Google Analytics вызывает ошибку.

Обновляйте компонент только при необходимости

Чтобы исправить это, мы больше не должны позволять React решать (гадать) для себя, когда обновлять наш рекламный компонент.

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

|const Ad = props => {
  const { currentPath } = props
  
  useEffect(() => {
    window.adsbygoogle = window.adsbygoogle || []
    window.adsbygoogle.push({})
  })
  
  return (
    <div key={currentPath}>
      <ins 
        className="adsbygoogle"
	    style="display:inline-block;width:728px;height:90px"
	    data-ad-client="ca-pub-1234567890123456"
	    data-ad-slot="1234567890"
      />
    </div>
  )
}

Использование свойства компонентов `key`

Первое, что нужно сделать, это использовать свойство 'key' JSX '‹div /›'. элемент. Цель этого свойства - сообщить React, какие элементы были изменены, добавлены или удалены из DOM. Таким образом, он быстрее обрабатывает виртуальную модель DOM, поскольку ему не нужно самостоятельно определять изменения, выполняемые в этом месте.

Другими словами, в нашем случае, пока ключ не изменится, React не будет пытаться обновить рекламу. С другой стороны, изменение ключа заставит React изменить DOM и, следовательно, повторно сгенерировать рекламу.

Я использую 'path' текущей страницы в качестве ключа, который заставляет React воссоздавать рекламные теги, когда пользователь меняет страницы, но не позволяет ему вносить изменения, пока пользователь остается на той же странице.

Теперь мы точно знаем, когда будет обновлена ​​DOM. Но ошибка все еще существует, поскольку хуки всегда выполняются слишком часто.

|const Ad = props => {
  const { currentPath } = props
  
  useEffect(() => {
    window.adsbygoogle = window.adsbygoogle || []
    window.adsbygoogle.push({})
  }, [currentPath])
  
  return (
    <div key={currentPath}>
      <ins 
        className="adsbygoogle"
	    style="display:inline-block;width:728px;height:90px"
	    data-ad-client="ca-pub-1234567890123456"
	    data-ad-slot="1234567890"
      />
    </div>
  )
}

Уменьшенная частота выполнения крюка

Последняя модификация нашего компонента находится во втором аргументе ‘useEffect’. Мы передаем ему таблицу значений для проверки перед выполнением. Пока значения в этом массиве не изменятся, ловушка выполняться не будет.

Используя то же свойство ‘currentPath’, я синхронизирую выполнение ловушки с обновлением DOM. Ошибка устранена.

В случае «классов компонентов» эквивалент достигается путем переопределения метода ‘shouldComponentUpdate’.

|class Ad {  
  shouldComponentUpdate(nextProps) {
      return this.props.currentPath !== nextProps.currentPath
  }
    
  componentDidUpdate() {
    // .push({})
  }
  
  render() {
    // <ins />
  }
}

Сниженная частота выполнения методов жизненного цикла

В последнем случае свойство 'key' элемента '‹div /›' не может быть используется, потому что метод рендеринга вообще не вызывается, когда 'shouldComponentUpdate' возвращает 'false'.