---
layout: post
title: Эффективная загрузка стороннего кода JavaScript
subhead: Избегайте распространенных ошибок, связанных с использованием сторонних скриптов, чтобы уменьшить время загрузки и повысить удобство для пользователей.
authors:
  - mihajlija
date: 2019-08-14
description: Узнайте, как избежать распространенных ошибок, связанных с использованием сторонних скриптов, чтобы уменьшить время загрузки и повысить удобство для пользователей.
hero: image/admin/udp7L9LSo5mfI3F0tvNY.jpg
alt: Вид с воздуха на грузовые контейнеры.
codelabs: codelab-optimize-third-party-javascript
tags:
  - performance
  - javascript
---

Если сторонний скрипт [замедляет](/third-party-javascript/) загрузку страницы, можно повысить ее производительность двумя указанными ниже способами.

- Удалить скрипт, если он не приносит явную пользу сайту.

- Оптимизировать процесс загрузки.

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

1. Использование атрибутов `async` и `defer` в тегах `<script>`

2. Заблаговременное создание подключений к требуемым источникам

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

4. Оптимизация передачи сторонних скриптов

## Использование атрибутов `async` и `defer`

Так как [синхронные скрипты](/third-party-javascript/) задерживают создание DOM и рендеринг, следует всегда загружать сторонние скрипты асинхронно за исключением случаев, когда нужно запускать скрипт до рендеринга страницы.

Атрибуты `async` и `defer` сообщают браузеру, что он может продолжить синтаксический анализ HTML во время загрузки скрипта в фоновом режиме и выполнить этот скрипт по окончании его загрузки. Таким образом, загрузка скрипта не будет мешать создавать модель DOM и выполнять рендеринг страницы. В результате можно будет отобразить страницу для пользователя еще до завершения загрузки всех скриптов.

```html
<script async src="script.js">

<script defer src="script.js">
```

Разница между атрибутами `async` и `defer` появляется тогда, когда дело доходит до выполнения скриптов.

### `async`

Скрипты с атрибутом `async` выполняются при первой возможности после завершения их загрузки и до появления события [load](https://developer.mozilla.org/docs/Web/Events/load) окна. Соответственно, возможно (и вероятно), скрипты с атрибутом `async` не будут выполняться в том порядке, в котором они появляются в HTML. Это также означает, что они могут прервать процесс создания DOM, если их загрузка будет завершена во время работы средства синтаксического анализа.

{% Img src="image/admin/tCqsJ3E7m4lpKOprXu5B.png", alt="Схема, на которой скрипт с атрибутом async блокирует работу средства синтаксического анализа", width="800", height="252" %}

### `defer`

Скрипты с атрибутом `defer` выполняются после полного завершения синтаксического анализа HTML, но до появления события [`DOMContentLoaded`](https://developer.mozilla.org/docs/Web/Events/DOMContentLoaded). При использовании атрибута `defer` скрипты будут гарантированно выполняться в том порядке, в котором они появляются в HTML, и не будут блокировать работу средства синтаксического анализа.

{% Img src="image/admin/Eq0mcvDALKibHe15HspN.png", alt="Схема потока средства синтаксического анализа со скриптом с атрибутом defer", width="800", height="253" %}

- Используйте атрибут `async`, если важно, чтобы скрипт выполнялся на ранних этапах процесса загрузки.

- Используйте атрибут `defer` для менее важных ресурсов. Например, для видеоплеера в части страницы, расположенной за пределами экрана.

Используя эти атрибуты, можно значительно ускорить загрузку страницы. Например, [недавно на веб-сайте Telegraph стали отложенными все скрипты](https://medium.com/p/a0a1000be5#4123), в том числе связанные с рекламными объявлениями и аналитикой, после чего время загрузки рекламных объявлений уменьшилось в среднем на четыре секунды.

{% Aside %} Скрипты аналитики обычно загружаются на ранних этапах, поэтому вы не пропустите ценные аналитические данные. К счастью, существуют [шаблоны для отложенной инициализации аналитики](https://philipwalton.com/articles/the-google-analytics-setup-i-use-on-every-site-i-build/), позволяющие сохранять данные на ранних этапах загрузки страницы. {% endAside %}

## Создание заблаговременных подключений к необходимым источникам

Вы можете сэкономить 100–500 мс, [заблаговременно создавая подключения](/preconnect-and-dns-prefetch/) к важным сторонним источникам.

Здесь вам могут помочь два указанных ниже типа атрибута [`<link>`](https://developer.mozilla.org/docs/Web/HTML/Element/link).

- `preconnect`

- `dns-prefetch`

### `preconnect`

Атрибут `<link rel="preconnect">` сообщает браузеру, что страница намеревается создать подключение к другому источнику и что нужно начать этот процесс как можно скорее. При создании запроса на получение ресурса из предварительно подключенного источника загрузка начинается немедленно.

```html
<link rel="preconnect" href="https://cdn.example.com">
```

{% Aside 'caution' %} Предварительное подключение следует выполнять только к критически важным доменам, которые потребуется использовать в ближайшее время, потому что браузер закрывает любое подключение, которое не используется в течение 10 секунд. Ненужные предварительные подключения могут замедлять получение других важных ресурсов, поэтому ограничьте количество предварительно подключенных доменов и [проверьте, какое влияние оказывают предварительные подключения](https://andydavies.me/blog/2019/08/07/experimenting-with-link-rel-equals-preconnect-using-custom-script-injection-in-webpagetest/). {% endAside %}

### `dns-prefetch`

Атрибут `<link rel="dns-prefetch>` выполняет небольшую часть действий, выполняемых атрибутом `<link rel="preconnect">`. При создании подключения выполняется поиск DNS и подтверждение TCP, а для безопасных источников — согласование TLS. Атрибут `dns-prefetch` сообщает браузеру, что нужно только сопоставлять DNS определенного домена до совершения явного вызова к нему.

Атрибут `preconnect` лучше всего использовать только для самых важных подключений; для менее важных сторонних доменов используйте атрибут `<link rel=dns-prefetch>`.

```html
<link rel="dns-prefetch" href="http://example.com">
```

[Браузеры поддерживают атрибут `dns-prefetch`](https://caniuse.com/#search=dns-prefetch) немного по-другому, чем [атрибут `preconnect`](https://caniuse.com/#search=preconnect), поэтому атрибут `dns-prefetch` можно использовать в качестве резервного варианта для браузеров, которые не поддерживают атрибут `preconnect`. Для безопасной реализации этого подхода используйте отдельные теги ссылок:

```html
<link rel="preconnect" href="http://example.com">
<link rel="dns-prefetch" href="http://example.com">
```

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

Если внедренные сторонние ресурсы плохо разработаны, они могут значительно снижать скорость загрузки страниц. Если они не являются критически важными или находятся за пределами экрана (то есть если пользователям нужно прокрутить страницу для их просмотра), отложенная загрузка — хороший способ повысить скорость работы страницы и улучшить метрики отображения содержимого. Таким образом можно повысить удобство для пользователей, так как они будут быстрее получать содержимое главной страницы.

<figure data-float="left">{% Img src="image/admin/uzPZzkgzfrv2Oy3UQPrN.png", alt="Схема веб-страницы, отображаемой на мобильном устройстве, с прокручиваемым содержимым, выходящим за пределы экрана. Содержимое, расположенное за нижним краем экрана, не отображается, так как оно еще не загружено.", width="366", height="438" %}</figure>

Один из эффективных методов — отложенная загрузка стороннего контента после загрузки содержимого главной страницы. Этот метод хорошо подходит для рекламных объявлений.

Рекламные объявления — важный источник дохода для многих сайтов, но пользователи приходят за контентом. За счет отложенной загрузки рекламных объявлений и более быстрой доставки основного контента вы можете увеличить общий процент просмотра объявления. Например, сайт MediaVine перешел на [отложенную загрузку рекламных объявлений](https://www.mediavine.com/lazy-loading-ads-mediavine-ads-load-200-faster/), после чего скорость загрузки страниц сайта увеличилась на 200%. В [официальной документации](https://support.google.com/dfp_premium/answer/4578089#lazyloading) DoubleClick есть руководство по отложенной загрузке объявлений.

Альтернативный метод — загружать сторонний контент только тогда, когда пользователи прокручивают страницу вниз до соответствующего раздела.

[Intersection Observer](https://developer.chrome.com/blog/intersectionobserver/) — это API браузера, который эффективно определяет, когда элемент входит в окно просмотра браузера и выходит из него. Его можно использовать для реализации этого метода. [lazysizes](/use-lazysizes-to-lazyload-images/) — это популярная библиотека JavaScript для отложенной загрузки изображений и элементов [`iframe`](http://afarkas.github.io/lazysizes/#examples). Она поддерживает внедренные элементы YouTube и [виджеты](https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/unveilhooks). В ней также имеется [дополнительная поддержка](https://github.com/aFarkas/lazysizes/blob/097a9878817dd17be3366633e555f3929a7eaaf1/src/lazysizes-intersection.js) API IntersectionObserver.

{% Aside 'caution' %} Будьте осторожны при отложенной загрузке ресурсов с помощью JavaScript. Если не удается загрузить JavaScript, например из-за нестабильной работы сети, ваши ресурсы вообще не загрузятся. {% endAside %}

Использование атрибута [`loading` для отложенной загрузки изображений и элементов iframe](/browser-level-image-lazy-loading/) — отличная альтернатива методам с использованием JavaScript, и недавно этот атрибут стал доступен в Chrome 76.

## Оптимизация передачи сторонних скриптов

### Размещение скриптов в сторонних CDN

Сторонние поставщики часто предоставляют URL-адреса размещаемых у них файлов JavaScript (обычно они размещены в [сетях доставки контента [CDN]](https://en.wikipedia.org/wiki/Content_delivery_network)). Преимущество такого подхода заключается в том, что вы можете быстро начать использовать скрипты — достаточно скопировать и вставить URL-адрес. При этом у вас не будет дополнительных затрат на обслуживание. Сторонний поставщик выполняет настройку сервера и обновляет скрипты.

Но так как скрипты не находятся в том же источнике, что и остальные ваши ресурсы, для загрузки файлов из общедоступных CDN требуются затраты на передачу данных по сети. Браузеру необходимо выполнить поиск в DNS, создать подключение HTTP и (при использовании защищенных источников) выполнить подтверждение SSL для сервера поставщика.

Когда вы используете файлы со сторонних серверов, вы можете контролировать кеширование только в редких случаях. Использование чужой стратегии кэширования может привести к слишком частому повторному получению скриптов по сети.

### Размещение сторонних скриптов на собственном ресурсе

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

- Уменьшение времени поиска DNS и кругового пути.
- Улучшение [HTTP-кэширования](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching) заголовков.
- Использование [операций push сервера HTTP/2](/performance-http2/).

Например, веб-сайту Casper удалось [уменьшить время загрузки на 1,7 секунды](https://medium.com/caspertechteam/we-shaved-1-7-seconds-off-casper-com-by-self-hosting-optimizely-2704bcbff8ec), разместив скрипт A/B-тестирования на собственном ресурсе.

Тем не менее у размещения скриптов на собственных ресурсах есть один большой недостаток: скрипты могут устаревать и не будут получать автоматические обновления (при изменении API) или исправления для системы безопасности.

{% Aside 'caution' %}

При ручном обновлении скриптов может возникнуть много накладных расходов в рамках процесса разработки. Кроме того, вы можете пропустить важные обновления. Если вы не используете хостинг CDN для передачи всех ресурсов, вам не будет доступно [кэширование на периферии](https://www.cloudflare.com/learning/cdn/glossary/edge-server/) и потребуется оптимизировать функцию сжатия на сервере. {% endAside%}

### Использование служебных сценариев для кеширования скриптов со сторонних серверов

Альтернатива размещению скриптов на собственных ресурсах, которая позволяет лучше управлять кешированием и при этом пользоваться преимуществами сторонних CDN, — [использование служебных сценариев для кэширования скриптов со сторонних серверов](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#cross-origin-considerations). Вы сможете управлять периодичностью повторной загрузки скриптов из сети и создать стратегию загрузки, с помощью которой можно будет ограничивать запросы к несущественным сторонним ресурсам до тех пор, пока страница не достигнет ключевого для пользователя момента. В этом случае можно в некоторой степени уменьшить расходы на передачу данных по сети, создавая заблаговременные подключения с использованием атрибута `preconnect`.
