Beacon API

Оригинал данной статьи находится здесь Logging Activity With The Web Beacon API

Краткий обзор: Beacon API — это легковесный и эффективный путь для передачи информации с веб-страницы на сервер. Узнайте, как он может быть использован и что делает его таким отличным от традиционных техник Ajax.

Beacon API — это основанное на языке Javascript Web API для отправки небольших порций данных от браузера веб-серверу без ожидания ответа последнего. В данной статье, вы посмотрим на то, для чего эта технология может оказаться полезной, что отличает её от похожих технологий, вроде XMLHTTPRequest (Ajax), и как начать ею пользоваться.

Для чего предназначена Beacon API?

Beacon API используется для отправки небольшого количества данных серверу без ожидания ответа сервера. Последний момент очень важен и это является тем, что делает Beacon API такой полезной технологией — наш код даже никогда не увидит ответ сервера, даже если сервер и будет его посылать обратно. Beacon API специально предназначена для того, чтобы сначала отправить данные, а затем просто о них забыть. Мы не ожидаем ответа и не получаем его.

Думайте об этом, как об открытке, которую вы отправляете себе домой во время отпуска. Вы помещаете небольшое количество данных в нее (вроде «Хотел бы, что вы были здесь» и «Погода была замечательная»), кладете это в почтовый ящик, и не ожидаете ответа. Никогда не отправит вам в ответ открытку с подписью «Да, спасибо большое».

Для современных веб-сайтов и приложений, существует некоторое количество случаев использования, которые применяют примерно такой же тип шаблона «отправить и забыть».

Передача статистических и аналитических данных

Первый пример использования, который приходит в голову большинства людей, это аналитика. Крупные решения, вроде Google Analytics могут дать хороший обзор таких вещей, как визиты на страницу, но что если мы хотели чего-нибудь более специфического? Мы могли бы написать некоторый Javascript-код для передачи того, что происходит на странице (возможно того, как пользователь взаимодействует с компонентом, как далеко он прокручивает страницу, или как статьи были отображены, прежде чем они привели к CTA (Call-To-Action), но нам затем нужно отправить эти данные серверу, когда пользователь покидает страницу. Beacon API является отличным решением для всего этого, так как мы всего-лишь отправляем данные и нам не нужен ответ сервера.

Нет причин, по которым мы не молги бы также упомянуть о разновидности повседневных задач, часто выполняемых Google Analytics, отправку отчетов пользователю и возможностей устройств и браузеров. Если известный вам пользователь открыл сессию, вы можете даже привязать эту статистику к известной вам личности. Какие бы данные вы ни получали, вы можете отправлять их назад на сервер с Beacon API.

Отладка и регистрация данных

Другим полезным приложением этого поведения является регистрация  информации из вашего Javascript-кода. Представьте, что у вас есть сложный интерактивный компонент на вашей странице, который работает отлично для всех ваших тестов, но иногда падает в продакшне. Вы знаете, что это падение, но вы не можете увидеть ошибку, поэтому у вас не получится приступить к ее отладке. Если сможете отловить строку, в которой приложение падает, вы таким образом сможете собрать диагностическую информацию и использовать Beacon API для отправки её в лог-файл сервера.

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

Не могли бы мы это уже сделать?

Я знаю, о чем вы сейчас думаете. Ничто из этого не ново, не так ли? Вы уже и так умеем общаться из браузера с сервером, используя XMLHTTPRequest на протяжении более чем десяти лет. Гораздо чаще мы прибегаем к Fetch API, который делает те же самые вещи с использованием более современного интерфейса на основе промисов. Имея всё это, зачем нам нужен еще какой-то Beacon API?

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

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

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

Типичной ситуацией является то, когда вы используете как событие unload, так и beforeunload для выполнения операции регистрации данных. Они происходят тогда, когда пользователь делает что-то вроде щелчка на ссылке, после чего пользователь покидает страницу. Проблемой здесь является то, что код, запускаемый при возникновении одного из событий unload, может блокировать исполнение кода и вызывать задержку исполнения функции, обрабатывающей данное событие. Если происходит задержка при выгрузке страницы, тогда следующая загружаемая страница также вызывает задержку запуска скрипта, и тогда все происходит слишком медленно.

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

Beacon API обходит эту проблему стороной при помощи постановки запроса в очередь запросов без блокировки исполнения кода, возвращая управление назад немедленно вашему скрипту. Браузер затем следит за отправкой этого запроса в фоновом режиме без блокировки. Это приводит тому, что всё работает очень быстро, что делает пользователей более счастливыми и позволяет нам всем не потерять работу.

Начинаем

Таким образом, мы теперь понимаем, что такое Beacon API, и почему мы могли бы его использовать, так что давайте начнем с некоторого кода. Следующий код не мог быть представлен еще как-то проще:

let result = navigator.sendBeacon(url, data);

Результат этого — булево значение, true, если принял и поставил в очередь запрос, и false, если произошли какие-то проблемы.

Использование navigator.sendBeacon()

navigator.sendBeacon принимает на вход две переменные. Первая — это URL, на который будет отправлен запрос. Этот запрос представлен в виде HTTP POST-запроса, который отправляет любые данные, представленные второй переменной.

Переменная данных может быть одного из нескольких форматов, все из которых взяты прямо из Fetch API. Это могут быть Blob, BufferSource, FormData или URLSearchParams — в основном, любые из типов тела запроса, использованные при создании запроса с помощью Fetch.

Мне нравится использовать FormData для базовых данных типа «ключ-значение», поскольку он несложен и легок для чтения.

// URL to send the data to
let url = '/api/my-endpoint';
// Create a new FormData and add a key/value pair
let data = new FormData();
data.append('hello', 'world');
let result = navigator.sendBeacon(url, data);
if (result) {
    console.log('Successfully queued!');
} else {
    console.log('Failure.');
}

Поддержка браузеров

Поддержка Beacon API в браузерах очень хорошая, с одним лишь значимым исключением, а именно, Internet Explorer (но работает на Edge) и Opera Mini. Для большинства вариантов использования этого должно хватать, но на всякий случай поддержку данной технологии браузерами лучше всего тестировать, используя navigator.sendBeacon.

Это легко сделать:

if (navigator.sendBeacon) {
    // Beacon code
} else {
    // No Beacon. Maybe fall back to XHR?
}

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

Пример: Регистрация времени на странице

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

Поскольку нас волнует только время, проведенное (не конкретное время суток), то мы может использовать performance.now() для получения начальной метки времени, когда страница загружается:

let startTime = performance.now();

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

let logVisit = function() {
    // Test that we have support
    if (!navigator.sendBeacon) return true;
    // URL to send the data to, e.g.
    let url = '/api/log-visit';
    // Data to send
    let data = new FormData();
    data.append('start', startTime);
    data.append('end', performance.now());
    data.append('url', document.URL);
    // Let's go!
    navigator.sendBeacon(url, data);
};

Наконец, нам нужно вызвать эту функцию, когда пользователь покидает эту страницу. Моей первой мыслью было использовать событие unload, но Safari на Маке, кажется, блокирует такие запросы с выдачей предупреждения безопасности, так что здесь нам поможет событие beforeunload.

window.addEventListener('beforeunload', logVisit);

Когда страница выгружается (или, прямо перед этим), ваша функция logVisit() будет вызвана и наш Beacon-запрос будет отправлен.

(Помните, что если у браузера нет поддержки Beacon, мы возвращаем true и считаем, что все идет отлично. Возврат же false приведет к отмене события и остановке выгрузки страницы. Это будет не слишком-то приятно).

Учет при отправки данных

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

GDPR

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

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

DNT: DO NOT TRACK

В дополнение к требованиям законодательства, большинство браузеров имеют настройку, которая позволяет пользователям самим устанавливать, хотят ли они, чтобы их действия отслеживались или нет . Do Not Track отправляет HTTP-заголовок с запросом, который выглядит примерно так:

DNT: 1

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

В PHP, например, вы можете очень легко протестировать этот заголовок, например так:

if (!empty($_SERVER['HTTP_DNT'])) {
    // User does not wish to be tracked ...
}

Заключение

Beacon API это действительно полезный путь для передачи данных со страницы на сервер. Поддержка браузерами этой технологии достаточно широкая, и это позволяет вам плавно записывать данные без негативного влияния на работу пользователя и на производительность вашего сайта. Неблокирующая природа запросов означает, что производительность будет более высокой, нежели у альтернативных технологий, таких как XHR и Fetch.

Если вы хотите прочесть больше о Beacon API, то следующие сайты будут вам большим подспорьем в этом.

Добавить комментарий