Люди часто используют кнопку «Назад» в браузере — возможно, чаще, чем вы думаете.
А если так, то зачем сразу выкидывать страницу из памяти браузера, а потом тратить время и трафик через секунду на ее повторное открытие? Чтобы пользователь мог быстро вернуться назад, была придумана технология BFCache, о которой важно помнить при разработке интерфейсов.
Виктор Хомяков Виктор-Хомяков разобрался с «туда-обратным кэшированием» и составил таблицу совместимости BFCache с разными API. — Здравствуйте, меня зовут Виктор.
Я работаю в довольно большой команде, которая занимается страницей поиска.
Как минимум, вы уже видели такую же или похожую страницу в Google. И в частности я занимаюсь проблемой скорости загрузки этой страницы — чтобы она как можно быстрее рендерилась на сервере и как можно быстрее загружалась и показывалась клиентам.
Почему это важно? Чем меньше времени клиенту придется ждать загрузки вашей страницы, тем меньше вероятность, что он не дождется и покинет вас.
И чем больше шансов на успешную конверсию клиента во что-то другое, тем выше чистый балл продвижения.
То есть клиент с радостью расскажет всем знакомым, что это действительно классная страница — она очень быстро загружается и ею очень удобно пользоваться.
И, в конечном итоге, тем больше денег вы сможете заработать.
Или ваша компания, тогда она вам даст бонус.
Приведу ряд примеров от известных компаний.
Google провел эксперимент. Они специально встроили задержку в страницу поиска и измерили, как она влияет на производительность.
Оказалось, что в среднем на одного пользователя приходилось на полпроцента меньше поисковых запросов.
Что такое полпроцента? Посчитайте: полпроцента от сотен миллионов пользователей Google — это довольно большая цифра.
Bing провел тот же эксперимент. Они не поверили Google, поэтому решили перепроверить.
У них были схожие результаты: заметно меньший доход на пользователя, когда страница замедлялась.
Почему замедление? Потому что легче замедлить страницу на точное количество миллисекунд, чтобы ее можно было легко воспроизвести в производстве, чем ускорить ее на ту же величину.
Пример с AliExpress. Они ускорили свой сайт на 36% и получили значительно больше заказов от пользователей.
Заказы — это прямые деньги.
В общем, все уже понимают, что скорость довольно важна и влияет на заработанные деньги по определенному количеству метрик.
И еще один фактор.
Сегодня уже говорили об оптимизации изображений.
Оптимизируя свой трафик, уменьшая количество скачиваний, вы платите меньше денег своим хостерам за исходящий трафик.
Это тоже деньги, которые останутся в вашем кармане.
А что, если я вдруг предложу вам скидку 10% на трафик от любого хостера без ограничений и условий? А что, если я предложу сделать так, чтобы какой-то процент ваших страниц — например, десять процентов — загружался для пользователя практически мгновенно? Никто не откажется.
Технология, о которой я попытаюсь сегодня рассказать, — это одно из возможных решений, которое накладывает мало ограничений на то, в каком стеке вы работаете, какими технологиями, но при этом обещает дать вам столь весьма существенные выгоды.
Начнем с того, что Google собирает в своих браузерах статистику об использовании этих браузеров.
И опубликовали такую цифру: оказывается, что в среднем из всех открытий страниц, из всех навигаций в мобильном Chrome примерно 19% перемещаются вперед и назад по истории.
Подумайте, что это значит? Если округлить в большую сторону, то получится, что 20% переходов переходят на страницу, на которой только что был пользователь.
Для нас, как авторов страницы, это означает: даже если пользователь покинет ее, существует значительная вероятность того, что он вот-вот вернется.
С одной стороны, это может быть проблемой мобильных телефонов: там все маленькое, легко промахнуться пальцем, нажать на ссылку и уйти со страницы, потом сказать: «Ой, блин! Хочу вернуться».
А вот на десктопах картина примерно такая же: там количество меньше, но процент возвратов все равно значительный.
Что мы делаем в это время? Мы тратим время пользователя и трафик.
То есть мы начинаем перезагружать ту же страницу, парсить ее, пересоздавать DOM, перерисовывать все на экране, загружать, выполнять JavaScript. Браузер — довольно мощная вещь.
Он старается использовать кэши везде, где это возможно.
И большая часть ресурсов, вероятно, окажется в его тайнике.
Он не будет ждать их из сети, а подберет прямо из кэша.
Например, в движке V8 также кэшируется результат парсинга JavaScript. То есть браузер не будет повторно загружать и парсить JS, а в большинстве случаев сразу начнет его выполнять.
Но все же пересборка DOM, перезагрузка некэшированных ресурсов и выполнение JS занимают значительное количество времени.
Решение напрашивается само собой.
Что мы делаем? Когда пользователь покидает нашу страницу, мы не очищаем ее сразу.
Мы просто сохраняем его состояние и визуально скрываем от пользователя, чтобы под капотом оно оставалось в распоряжении браузера.
Что мы будем делать, если пользователь решит вернуться? Давайте просто покажем ему ту же сохраненную страницу.
Это можно показать практически мгновенно.
Эта технология называется back/forward кэшем, от слов «вперед и назад».
Сокращенно bfcache. Вот пример того, как один и тот же браузер, та же сборка ведет себя с отключенным и включенным bfcache. Открытие первой страницы происходит одинаково медленно в обоих случаях.
Но затем, когда мы начинаем двигаться вперед и назад по истории, слева появляется заметная пауза, а справа ее нет. Слева — обычное движение по истории, занимающее заметное количество времени.
Справа все происходит очень и очень быстро.
Показать гифку
Аналогичный пример из нашего поиска.
Слева — обычный Safari на macOS с отключенным bfcache, справа — тот же Safari с настройками «по умолчанию» и включенным bfcache. Это достаточно распространенный случай: человек приходит в поиск, может не знать точно, что он ищет, и может задать несколько вариантов запроса.
Первый запрос, который я задал, был что-то не так.
Второй запрос кажется лучше.
Третий - нет, хуже, возвращаемся к предыдущему запросу.
И в этот момент было бы очень хорошо не заставлять его ждать.
Он только что увидел этот предыдущий запрос, покажи сразу.
Или второй вариант, если у вас есть пагинация и несколько страниц в результатах поиска.
Мужчина просматривает результаты поиска.
Зашел на вторую, третью, четвертую страницу, посмотрел - нет, там что-то не так, вернусь.
И мы, опять же, можем практически мгновенно показать ему предыдущие страницы.
Важным вопросом является безопасность.
Пока страница, на которой находился пользователь, не находилась в скрытом состоянии, она могла получить доступ к различным API, которые позволяют вам читать состояние оборудования вашего телефона или компьютера.
Вот краткий список того, что сразу пришло на ум: геолокация, изменение положения вашего устройства в пространстве, доступ к камере, звук с микрофона.
Затем, когда страница будет раскрыта, важно, чтобы она не получила доступ ко всем событиям, произошедшим, пока она была скрыта.
В противном случае откроется дополнительный канал для слежки за пользователями.
Важно, чтобы она не получила историю ваших перемещений за все это время и записи микрофона и камеры.
Об этом не стоит забывать и разработчикам браузеров.
Поддержка API и браузера
Ближе к теме.Допустим, я тебя уже уговорил, ты такой: «Да, это хорошая тема, надо с ней поработать».
Какие API мы имеем в своем распоряжении, с чем мы можем работать, если согласимся учитывать bfcache, и как все это поддерживается браузерами? Где уже доступен bfcache, где его можно посмотреть? — Оно уже давно реализовано в браузерах Firefox, Safari (и macOS, и iOS), а также в Internet Explorer 11 (!).
Обычно мы критикуем разработчиков Microsoft, но здесь они сделали именно это.
— Реализовано в браузере UC Browser 11/12, версия для Android. — Вдруг его нет в Chrome. А в Chromium эта функция находится в стадии разработки.
Это значит, что когда это сделают в Chromium, примерно все эти браузеры (и это не полный список) рано или поздно получат этот функционал — бесплатно, без СМС и регистрации.
Есть ли какой-то API? Я хочу управлять bfcache, хочу включать и выключать его прямо из JavaScript, узнавать есть страница в bfcache или нет. Как насчет такого API? Такого API нет. И это сделано намеренно: страница не должна хотеть включать и выключать bfcache для всех и для себя.
Или узнать, есть ли кто-нибудь в этом bfcache или нет. Это все из-за безопасности.
Ссылка со слайда
Но что мы имеем? Виды навигации.
Есть тип ссылки — link prerender, когда мы хотим заранее отрендерить страницу.
Для него существует специальный тип навигации: эта страница будет открыта с типом навигации «prerender».
Если мы просто откроем страницу в браузере, то там будет «навигация».
Если мы нажмем на кнопку «Обновить», произойдет «перезагрузка».
Здесь нас интересует тип навигации «back_forward»; это ясно указывает на то, что пользователь перемещался по истории вперед и назад. Это именно тот тип навигации, при котором может работать наш bfcache.
Другой API — события PageShow PageHide. Они уже существовали в браузерах.
Соответственно, pageshow срабатывает, когда ваша страница отображается в браузере пользователю, pagehide — когда страница по каким-то причинам скрыта.
И они были дополнены сохранившимся полем.
Если страница скрыта и будет помещена в bfcache, сохраняемое поле будет иметь значение true. Если страница отображается, когда она извлекается из bfcache, то сохраняемое поле снова будет иметь значение true.
Соответственно, если браузер не собирается кэшировать страницу, то pagehide будет сохраняться false. А если браузер показывает страницу во время обычной загрузки или не использует bfcache, то Pageshow также будет сохранять false.
Ссылка со слайда
Событие поддерживается практически во всех браузерах, даже в тех, которые еще не поддерживают bfcache.
Ссылка со слайда
То же самое касается постоянного поля.
В Chrome он уже есть, но Chrome по-прежнему не поддерживает bfcache. То есть это поле будет всегда, но пока оно будет ложным.
Когда я столкнулся с таким явлением, как bfcache, мне пришлось его изучить, прощупать со всех сторон, посмотреть, как он работает. Когда я открыл ее, мне сразу захотелось увидеть на своей странице, каково значение сохраняемого поля в моих обработчиках.
Казалось бы, все довольно просто.
Я написал обработчик и выводил то, что мне приходило в console.log().
Но при открытии DevTools в некоторых браузерах bfcache может внезапно отключиться.
То есть я открыл DevTools, хожу по страницам туда-сюда, а у меня persisted всегда false, страница не попадает в bfcache. Хорошо, у меня есть еще один мощный инструмент — оповещение.
Но нет. Современные браузеры просто игнорируют предупреждение при выгрузке страницы в обработчиках pagehide, beforeunload и unload; там это просто не работает. И снова я не вижу того, чего хочу.
Ладно, у меня есть еще более смертоносное средство.
В каком-то блоке прямо на моей собственной странице, которую я исследую, я просто добавляю содержимое своего события в виде текста и тем самым все записываю.
Этот метод сработал.
Пожалуйста, вы можете использовать его.
Я отладил свой код, у меня он работает, я могу продолжить работу с ним.
Я, конечно, не забываю, что внешний статический скрипт все же лучше подойдет, чтобы не загружать одно и то же на страницу в инлайн-коде, а использовать файловое кэширование.
Я поместил этот отлаженный код во внешний скрипт.
Но нет, обработчики скрытия страниц в Safari отвалились! Почему-то они не работают от внешнего скрипта.
Хорошо, у меня уже есть рабочая версия.
Мне пришлось оставить это так.
Коротко перечислю, что мне удалось сделать всего за один день.
Во-первых, DevTools может отключить кеширование.
Вы наверняка помните, что в DevTools на вкладке «Сеть» в Chrome есть флажок «Отключить кеш».
Он отключает сетевой кеш; это может повлиять или не повлиять на bfcache. Аналогия понятна: мы открыли DevTools, а значит, мы разрабатываем и кеширование нам может не понадобиться.
Возможно, это нас беспокоит.
Вторая функция — оповещение.
Firefox и Safari молча проигнорируют его и продолжат выполнять обработчики, как будто предупреждения не было.
И только старый добрый Хром в консоли напишет красным ошибку - у вас есть оповещение, я его заблокировал, учтите это! Еще раз напомню, что обработчики из внешнего скрипта в Safari могут не вызываться, это очень странно.
И еще одна важная новость.
Если ваша страница кэширована, то есть вы получаете событие pagehide, и оно говорит persist true, а браузер говорит вам: «Да, я поместил это в кеш», это не дает никакой гарантии, что страница когда-либо будет из этого кэша извлекается и отображается обратно пользователю.
Потому что браузер может решить очистить этот кеш, если ему недостаточно памяти.
Потому что пользователь может закрыть браузер и никуда не переходить.
Помните об этом.
Особенности реализации
Я начал дальше углубляться в документацию, чтобы понять, как мне жить с этими знаниями.Удивительно, но документация была.
То есть вы можете нарыть в интернете описание того, как работает bfcache в браузерах.
Но чем больше я читал, тем больше различий накапливалось между разными браузерами.
В одном это работает так, в другом вот так.
В одном одно мешает, другое не мешает. Разработчики не знают, как правильно обработать ряд API при размещении страницы в bfcache. Они говорят: окей, если страница использует это API, то я его просто игнорирую, никогда и ни при каких обстоятельствах не кеширую.
И этот список в разных браузерах разный, каждый делает то, что ему удобно.
И тогда я начал суммировать то, что узнал, в одну таблицу.
У меня получилось что-то вроде этого:
Читал документацию по браузерам — Firefox, Safari, семейства Chromium. Для IE существовала документация, хотя она и устарела.
Мы, программисты, не любим обновлять документацию после изменений в браузере, не так ли? Когда я понял, что информация устарела, я начал тестировать свои небольшие страницы в браузерах и проверять, какой API работает, а какой нет. Этого тоже оказалось мало: я не знаю, на какие API смотреть в принципе, почему бы не пройтись по всем? И мне пришлось заглянуть в исходные коды самих браузерных движков, то есть код оказался наиболее точным и достоверным источником знаний.
В настоящее время этот знак (перед вами его кусочек, Ссылка здесь для полной версии) — наиболее полный сборник знаний о том, какие API разрешают или отключают bfcache в браузерах.
Те API, которые не мешают, отмечены галочкой и зеленым цветом; те, которые точно будут мешать попаданию страницы в bfcache, отмечены красным.
Белые поля — это пространства, которые нигде не описаны.
Fire Fox
Вот интересные подробности по конкретным браузерам.Начну с Firefox, он первый все это сделал.
Ссылка со слайда
Самое важное, что я узнал из исходников Firefox, это то, что при работе с bfcache он может записывать в текстовый журнал на диске все причины, по которым он не может кэшировать страницу.
Ссылка со слайда
И мне даже удалось узнать, как заставить его это сделать.
Есть две секретные переменные окружения: в одной указываем, что логировать, во второй — в какой файл записывать лог.
После этого запускаем бинарник и вуаля! Мы увидим что-то вроде того, что было на предыдущем слайде, строки типа «этот html не может быть кэширован по этой причине, это по другой причине».
Мы можем прочитать его сразу, это очень удобно.
Если вы хотите поэкспериментировать один раз, вы можете открыть страницу about:networking в Firefox. Там же, в разделе «Журнал», вы можете ввести эти самые поля.
Мы можем указать в двух полях, что и куда логировать, а также использовать кнопки для запуска и остановки логирования.
Ссылка со слайда
Когда Firefox перестает кэшировать страницу? Если у вас есть ожидающие запросы, включая запросы AJAX или запросы на ресурсы, такие как изображения, страница будет отклонена в bfcache. Он считает, что он не завершен, не закончил загрузку и там какая-то подозрительная активность.
Но все это не относится к фавиконке.
Если забыл фавиконку, если она не загружается, он думает, ладно, сойдет, это нормально для твоего сайта.
Если у вас долго работающий скрипт, браузер спросит: раз он отнимает время, блокирует UI, может, убить его? И если вы согласились, то такая страница считается битой, неправильной и мы ее не кешируем.
Если вы используете IndexedDB. Это поучительная история.
Раньше в первой реализации смотрели: если у вас IndexedDB и есть незавершенная транзакция, то такая страница не кэшируется, потому что непонятно, как с ней работать (пытаемся спрятать прямо посередине сделки).
Но потом при рефакторинге они просто потеряли этот кусок кода.
И как можно догадаться, никаких тестов на это у них не было.
У них даже есть ошибка в их трекере ошибок.
Ему уже два с половиной года.
Люди писали: «У меня bfcache с IndexedDB работает некорректно, что делатьЭ» Разработчики Firefox ответили — действительно ломается, мы просто потеряли этот кусок текста при рефакторинге, ладно, пусть так и продолжается.
Мораль: пишите тесты, даже если вы пишете Firefox, иначе дело может закончиться плохо.
И еще один интересный фактор, позволяющий не попасть в bfcache, — если смешанный контент явно разрешен.
Что это?
Ссылка со слайда
Допустим, ваша страница открывается по HTTPS, но вы по-прежнему загружаете некоторые ресурсы по HTTP, особенно скрипты.
То есть у вас незащищенный скрипт, его может модифицировать кто угодно.
Ссылка со слайда
По умолчанию Firefox, как и другие браузеры, в настоящее время не запускает такой незащищенный скрипт. Но если для вас это очень важно, вы зашли в настройки и разрешили его выполнение, то соответственно он не будет кешировать такую страницу.
Он скажет — окей, ты меня уговорил выполнить код, но не дальше!
Другая настройка — это размер самого bfcache. По умолчанию здесь минус один.
Это означает, что при таком объеме памяти, который имеет Firefox, он попытается кэшировать как можно больше страниц.
Но мы можем принудительно отключить кеш, установив его в ноль, или явно задав число — например, запомнить не более пяти страниц.
Предупреждение: следующий слайд содержит пример кода на ужасном языке C++, который может быть опасен на конференции по интерфейсу.
Не пытайтесь это скопировать, сделайте это в консоли браузера.
Ваш диск может отформатироваться, ваш экран может взорваться, или ваши биткойны могут начать добываться.
Я предупреждал тебя.
Ссылка со слайда
Итак, вот код Gecko. Вы можете открыть его, прочитать, посмотреть бесплатно в Интернете.
И я немного покопался.
Есть самый важный метод — CanSavePresentation(), он отвечает на вопрос: можно ли закешировать этот документ? То есть это высший источник правды о том, что реализовано в Firefox прямо сейчас.
И еще, именно оттуда я узнал, что можно прочитать лог.
Есть такая переменная — gPageCacheLog. Это лог, в который все записывается.
Вот интересная история об экскурсе в C++.
То есть вы открываете ссылку, смотрите код, ищете (есть удобный и к тому же быстрый поиск) и можете узнать текущие детали реализации в последней версии браузера — те, которых просто нет в документация.
Сафари
Самое жестокое, что делает Safari, когда страница попадает в bfcache: если у вас есть ожидающие запросы AJAX, Safari просто убивает их.
Даже если окружить их обработкой ошибок и попытаться все проверить и исправить, запроса как будто не существует вовсе.
После восстановления из bfcache у вас не будет ни ошибок, ни «ОК», вообще ничего.
Обработчики Pageshow pagehide, как я уже говорил, вызываются в Safari только в том случае, если они написаны в скрипте, встроенном в страницу.
Из внешнего скрипта они могут работать, а могут и не работать, в зависимости от вашей удачи.
Но я предупредил тебя.
Еще одно интересное отличие: обработчики beforeunload и unload не препятствуют попаданию страницы в bfcache. В этом случае beforeunload вызывается всегда, как когда он попадает в кеш, так и когда нет. Но выгрузка не вызывается, когда страница попадает в кеш.
И тут есть еще грабли: страница может оказаться в кеше и больше никогда из него не появиться, а если вы написали какой-то важный код в unload, то она в принципе может и не вызваться никогда, хотя ни одной ошибки на странице не возникло.
страница.
То есть оно корректно ушло в кеш, а из него в никуда, и ваша выгрузка никогда не будет вызвана.
Еще один интересный момент. Как вы знаете, вы можете открыть несколько окон, используя window.open().
И в то же время эти окна по-прежнему имеют ссылки друг на друга.
То есть они могут синхронно обращаться к окнам друг друга и читать/записывать разные свойства.
Открытие дочерних окон таким образом не мешает работе bfcache. И тут, скорее всего, Safari поступает так же жестоко, как и с AJAX-запросами, то есть все жестоко рвется, и до свидания.
Разработчикам Apple нравится пожестче.
Снова момент C++! Исходники WebKit также не секретны; их также можно открывать, читать и изучать.
Ссылка со слайда
Они есть на GitHub, где я выделил две важные функции:
— canCacheFrame() — проверяет, можно ли кэшировать данный кадр.
— Различные объекты на странице, такие как элемент HTML или шрифт, имеют методы canSuspendForDocumentSuspension().
Этот объект определяет, можно ли его кэшировать, заморозить или нет. И еще один важный аспект: в WebKit очень удобные тесты.
Там прямо в папке LayoutTests/fast/history в виде небольшого, компактного, лаконичного html лежат тесты работы различных API, которые реализованы в Safari с помощью bfcache. Это живой пример того, как можно писать код в Safari с помощью этих API и как они влияют или не влияют, разрешают или не разрешают попадания в bfcache. Очень интересно посмотреть.
Оттуда я узнал, что Safari также записывает все свои знания о bfcache, всех его функциях в текстовый журнал.
Но, к сожалению, я так и не узнал, как включить это логирование или, если оно включено, то где найти этот файл на диске.
Если кто знает подскажите пожалуйста, буду очень благодарен.
Хром.
Как я уже сказал, работы там еще ведутся, все закрыто под флагом.
Вы можете скачать последнюю версию Chrome Canary, перейдите к флагам.
Там есть скрытая настройка - можете попробовать с ней поиграть.
Возможно, вы что-нибудь увидите.
Что интересно, я уже говорил об открытии страницы через window.open().
В Chromium такие страницы пока не кэшируются.
Они так и не придумали, как всё это правильно разрешить, и совесть не позволит им беззастенчиво всё отрезать, как в Сафари.
Если DOMContentLoaded не произошел, страница также не будет кэшироваться.
Есть много новых API, которые начинаются с «Web» — они тоже пока сложны и непонятны, и пока что все они отключают bfcache по умолчанию.
То есть, если на странице используется что-то модное, новое, например WebGL, WebVR, WebUSB, WebBluetooth и т.п.
, то в bfcache такая страница не попадет. Сервисный работник.
Такую страницу мы тоже пока не кэшируем, но планируем корректно ее обработать и скрыть от бдительного ока Service Worker. Если геолокация разрешена, мы ее также пока не кэшируем.
Сейчас проще.
Если файлы cookie страницы повреждаются, пока страница находится в кеше, мы предполагаем, что срок действия какой-либо авторизации истек.
Возможно, это был онлайн-банкинг или что-то еще.
Это значит, что страница уже недействительна — очищаем ее из кеша.
Ссылка со слайда
Ребята из Google пошли еще дальше.
Они предложили все объединить, формализовать, унифицировать во всех браузерах, предложили спецификацию жизненного цикла страницы для всех состояний и предложили добавлять новые события для переходов между разными состояниями.
Вы можете посмотреть ссылку, чтобы увидеть, что они там придумали.
Теги: #браузеры #Google Chrome #JavaScript #интерфейсы #кэширование #Safari #Chromium #bfcache #mozilla firefox #internet explorer 11
-
Обучение Microsoft Очень Полезно.
19 Oct, 24 -
Непрерывная Интеграция С Яндекс. Часть 2
19 Oct, 24 -
Странное Поведение Googlebot
19 Oct, 24 -
Пять Причин Неудачи Windows Vista
19 Oct, 24 -
Особенности Игрового Рынка Китая – Часть 2
19 Oct, 24 -
Карта Популярности Firefox
19 Oct, 24