Как отправить состояние при закрытии страницы браузера?
Что проверяют
Знаете ли вы, что обычный fetch в момент закрытия вкладки браузер может оборвать, и какие API придуманы для этого. Следом обычно спрашивают, почему beforeunload плохо работает на мобильных и чем sendBeacon отличается от fetch с keepalive.
Как отвечать
Разделите две задачи. Состояние для следующего захода сохраняйте в localStorage или IndexedDB по ходу работы с debounce, а не в последний момент. Если же данные должны уйти на сервер, нужен механизм, который переживёт выгрузку:
navigator.sendBeacon(url, data): асинхронный POST, который браузер доставит уже после закрытия страницы. Возвращаетfalse, если запрос не удалось поставить в очередь. Заголовки задать нельзя, тип тела задаётся черезBlob;fetch(url, { method: "POST", keepalive: true, body }): то же самое, но с привычным API, заголовками и выбором метода;- ограничение объёма: суммарный размер тел одновременных keepalive-запросов ограничен примерно 64 КБ, поэтому отправляют короткую выжимку, а не весь стор.
Событие выбирайте правильно. Надёжнее всего visibilitychange с проверкой document.visibilityState === "hidden": оно срабатывает и при сворачивании браузера, и при переходе в другое приложение, после чего система может убить вкладку молча. Дополнительно слушают pagehide. beforeunload на мобильных часто не приходит вообще, а unload браузеры постепенно выводят из употребления, к тому же он мешает странице попасть в bfcache.
За beforeunload остаётся одна задача: предупредить о несохранённых изменениях. Вешайте обработчик, только пока изменения есть, и снимайте после сохранения.
Где спотыкаются
Пишут синхронный XMLHttpRequest в unload. Chrome давно запрещает синхронные запросы при закрытии страницы, и данные просто теряются.
Надеются показать своё сообщение через event.returnValue. Современные браузеры выводят только стандартный текст диалога и не показывают его, если пользователь ничего не делал на странице.
Не делают отправку идемпотентной. visibilitychange срабатывает при каждом сворачивании, поэтому сервер должен спокойно принимать повторы, например по идентификатору сессии и версии состояния.