company_banner

BFCache, или Туда и обратно. Доклад Яндекса

    Люди пользуются кнопкой возврата на предыдущую страницу в браузере очень часто — возможно, чаще, чем вы думаете. А если так, то зачем сразу выбрасывать страницу из памяти браузера, а спустя секунду тратить время и трафик на её повторное открытие? Чтобы пользователь мог быстро вернуться назад, была придумана технология BFCache, о которой важно помнить при разработке интерфейсов. Виктор Хомяков victor-homyakov разобрался в «кэшировании туда и обратно» и составил таблицу совместимости BFCache с разными API.


    — Здравствуйте, меня зовут Виктор. Я работаю в составе довольно большой команды, которая занимается страницей поиска.



    Как минимум, такую же или аналогичную страницу в Google вы уже видели. И в частности, я занимаюсь проблемой скорости загрузки этой страницы — чтобы она максимально быстро рендерилась на сервере и максимально быстро скачивалась и показывалась клиентам. Почему это важно? Чем меньше ожидания у клиента, пока загрузится ваша страница, тем меньше шансов, что он не дождется и уйдет от вас. И тем больше шансов, что будет успешная конверсия клиента во что-то еще, тем больше будет net promotion score. То есть клиент будет радостно рассказывать всем знакомым, что здесь офигенно классная страница — очень быстро грузится, очень удобно пользоваться. И в конечном счете, тем больше денег вы сможете заработать. Или ваша компания, тогда она выдаст вам премию.

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

    Bing провел такой же эксперимент. Они не верили Google, решили перепроверить. У них получились аналогичные результаты: заметно меньше доходов на пользователя при замедлении страницы. Почему замедление? Потому что легче замедлить страницу на точное число миллисекунд, чтобы это легко воспроизводилось в продакшене, чем ускорить ее на столько же.

    Пример от AliExpress. Они ускорили свой сайт на 36% и получили заметно больше заказов от пользователей. Заказы — это прямые деньги. В общем, всем уже понятно, что скорость довольно важна, влияет через какое-то количество метрик на зарабатываемые деньги.

    И еще один фактор. Сегодня уже говорили про оптимизацию картинок. Оптимизируя свой трафик, уменьшая количество загрузок, вы платите меньше денег вашим хостерам за исходящий трафик. Это тоже деньги, которые останутся у вас в кармане. Что если я внезапно предложу вам скидку 10% на трафик у любого хостера без ограничений и условий? А если я предложу сделать так, чтобы доля ваших страниц — например, десять процентов — грузилась у пользователя практически мгновенно? Никто не откажется.

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

    Начнем с того, что Google в своих браузерах собирает статистику о том, как эти браузеры используются. И они опубликовали такое число: оказывается, в среднем из всех открытий страниц, из всех навигаций в мобильном Chrome примерно 19% — это перемещение по истории вперед-назад. Подумайте, что это значит? Если округлить, получится, что 20% навигаций — это перемещение на страницу, где пользователь только что был.

    Для нас как для авторов страниц это означает: даже если пользователь с нее ушел, есть немалый шанс, что он вот-вот вернется обратно. С одной стороны, это может быть именно проблемой мобилок: там все маленькое, легко промахнуться пальцем, нажать на ссылку и уйти со страницы, потом сказать: «А, черт! Я хочу вернуться». Но на десктопах примерно такая же картина: там число меньше, но все равно есть значительный процент возвратов.

    Что мы в это время делаем? Бездарно тратим время пользователя и трафик. То есть мы начинаем обратно перезагружать ту же самую страничку, парсить ее, пересоздавать DOM, перерисовывать все на экране, загружать, выполнять JavaScript.

    Браузер — довольно мощная штука. Он пытается использовать кэши везде, где только можно. И большая часть ресурсов, возможно, окажется у него в кэше. Он не будет ждать их из сети, а поднимет прямо из кэша. Например, в движке V8 результат парсинга JavaScript тоже кэшируется. То есть браузер не будет повторно загружать и парсить JS, а в большинстве случаев примется сразу его выполнять. Но все равно пересоздание DOM, перезагрузка не кэшированных ресурсов и выполнение JS занимают заметное время.

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

    Что мы сделаем, если пользователь решит вернуться? Просто покажем ему ту же самую сохраненную страницу. Ее можно показать практически мгновенно.



    Эта технология называется back/forward cache, от слов «вперед-назад». Сокращенно — 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 — link prerender, когда мы хотим заранее отрендерить страницу. Для него есть специальный тип навигации: эта страница будет открыта с NavigationType “prerender”. Если мы просто открываем страницу в браузере, то там будет “navigate”. Если жмем на кнопку «Обновить» — будет “reload”.

    Нас здесь интересует тип навигации “back_forward”, это явно говорит о том, что пользователь перемещался по истории вперед-назад. Это именно тот тип навигации, при котором может сработать наш bfcache.



    Еще один API — это события pageshow pagehide. Они уже и так существовали в браузерах. Соответственно, pageshow срабатывает, когда ваша страница показывается в браузере пользователю, pagehide — когда страница скрывается по какой-либо причине. И они были дополнены полем persisted. Если страница скрывается и при этом будет помещена в bfcache, то в поле persisted будет true. Если страница показывается при поднятии ее из bfcache, то в поле persisted опять же будет true.

    Соответственно, если браузер не собирается кэшировать страницу, то на pagehide будет persisted false. И если браузер показывает страницу при обычной загрузке, или он не использует bfcache, то при pageshow будет тоже persisted false.


    Ссылка со слайда

    Поддержка события есть практически во всех браузерах, даже в тех, которые пока не поддерживают bfcache.


    Ссылка со слайда

    То же самое касается поля persisted. Оно уже есть в Chrome, при этом Chrome все еще не поддерживает bfcache. То есть в нем это поле всегда будет, но пока оно будет false.

    Когда я столкнулся с этим явлением, bfcache, мне пришлось изучать его, обстукивать со всех сторон, смотреть, как оно работает. Я сразу захотел увидеть у себя на странице при открытии, чему равно значение поля persisted в моих обработчиках.



    Казалось бы, все довольно просто. Я написал обработчик и вывел в console.log() то, что мне приходит. Но при открытии DevTools в некоторых браузерах bfcache может внезапно отключиться. То есть я открыл DevTools, хожу по страницам вперед-назад, и у меня persisted всегда false, страница в bfcache не попадает. Окей, у меня есть еще одно мощное средство — alert.

    Но нет. Современные браузеры при выгрузке страницы в обработчиках pagehide, beforeunload и unload просто игнорируют alert, он там просто не работает. И я опять не вижу того, чего хочу.



    Окей, у меня есть еще более убойное средство. Я в какой-то блок прямо на своей же странице, которую исследую, просто добавляю текстом содержимое моего события и тем самым все логирую. Такой способ сработал.



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

    Я вынес во внешний скрипт этот отлаженный код.



    Но нет, обработчики pageshow pagehide отвалились в Safari! Из внешнего скрипта они почему-то не работают.

    Окей, у меня уже есть рабочий вариант. Мне пришлось оставить его в таком виде.



    Вкратце перечислю, на что я успел наступить буквально за один день. Во-первых, DevTools могут отключать кэширование. Вы, наверное, помните, что в DevTools на вкладке Network в Chrome есть галочка «Отключать кэш». Она отключает сетевой кэш, на bfcache она может не влиять, а может и влиять. Аналогия понятна: открыли DevTools — значит, мы разрабатываемся и кэширование нам может быть не нужно. Может, оно нам мешает.



    Вторая особенность — alert. Firefox и Safari молча его проигнорируют и продолжат дальше выполнять обработчики, как будто там не было никакого alert. И только один старый-добрый Chrome в консоли напишет красным цветом ошибку — у вас alert, я его заблокировал, знайте об этом!

    Еще раз напоминаю, что обработчики из внешнего скрипта в Safari могут не вызываться, это очень странно.

    И еще одна важная новость. Если ваша страница ушла в кэш, то есть вы получили событие pagehide, и в нем написано persisted true, а браузер вам говорит: «Да, я положил ее в кэш», — это не дает никаких гарантий, что страница когда-либо потом будет из этого кэша поднята и показана обратно пользователю. Потому что браузер может решить почистить этот кэш, если ему не хватает памяти. Потому что пользователь может закрыть браузер и не навигироваться никуда. Запомните это.

    Особенности реализации


    Я стал дальше копаться в документации, исследовать, как мне с этим знанием жить. На удивление, документация была. То есть можно нарыть в интернетах описание, как работает bfcache в браузерах. Но, чем дальше я читал, тем больше накапливалось различий между разными браузерами.

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

    И тогда я начал сводить то, что я узнавал, в одну таблицу. У меня получилось примерно следующее:



    Я читал документацию по браузерам — по Firefox, Safari, семейству Chromium. По IE была доступная документация, хотя и устаревшая. Мы же, программисты, не любим обновлять документацию после изменений в браузере? Когда я понял, что информация устарела, то начал тестировать в браузерах свои маленькие странички и проверять, какое API работает, а какое нет.

    Этого тоже оказалось недостаточно: я же не знаю, какие API смотреть в принципе, не перебирать же вообще все-все подряд? И мне пришлось заглянуть в исходные коды самих движков браузеров, то есть код оказался самым точным и достоверным источником знаний. На текущий момент эта табличка (перед вами ее кусочек, вот ссылка на полную версию) — самое полное собрание знаний о том, какие API разрешают или запрещают работу bfcache в браузерах.

    Галочкой и зеленым цветом помечены те API, которые не мешают, красным — те, которые однозначно будут мешать попаданию страницы в bfcache. Белые поля — пробелы, которые нигде не описаны.

    Firefox


    Вот интересные детали из конкретных браузеров. Начну с Firefox, он первый это все сделал.


    Ссылка со слайда

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


    Ссылка со слайда

    И мне даже удалось узнать, как его заставить это сделать. Есть две секретных переменных окружения: в одной мы указываем, что логировать, во второй — в какой файл писать лог. После этого мы запускаем бинарник, и вуаля! Мы увидим примерно то, что на предыдущем слайде, строчки вида «такой html не может быть закэширован по такой причине, такой — по другой причине». Мы это cможем сразу прочитать, очень удобно.



    Если вы хотите один раз поэкспериментировать, можно в Firefox открыть страницу about:networking. Там в разделе «Журнал» можно ввести эти же самые поля. Мы можем указать в двух полях, что и куда логировать, и кнопочками начинать и останавливать журнал.


    Ссылка со слайда

    Когда Firefox отказывается от кэширования страницы? Если у вас есть незавершенные запросы, в том числе AJAX-запросы или запросы за ресурсами типа картинок, то он откажется помещать страницу в bfcache. Он считает, что она не завершена, не закончила загрузку, там есть какая-то подозрительная активность. Но все это не распространяется на favicon. Если вы забыли favicon, если она не грузится, он считает — окей, пойдет, для вашего сайта это нормально.

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

    Если вы используете IndexedDB… Это поучительная история. Раньше, в первой реализации, они смотрели: если у вас IndexedDB и есть незавершенная транзакция, то такая страница не кэшируется, потому что непонятно, как с ней работать (мы прямо в середине транзакции пытаемся ее спрятать). Но потом они в ходе рефакторинга этот кусок кода просто потеряли. И как вы догадываетесь, тестов на это у них не было. У них в багтрекере даже есть баг. Ему уже два с чем-то года. Люди написали: «У меня bfcache с IndexedDB работает некорректно, что делать?» Разработчики Firefox ответили — действительно ломается, мы этот кусок текста при рефакторинге просто потеряли, окей, пусть так будет и дальше. Мораль: пишите тесты, даже если вы пишете Firefox, иначе все может закончиться печально.

    И еще один интересный фактор непопадания в bfcache — если явно разрешен mixed content. Что это такое?


    Ссылка со слайда

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


    Ссылка со слайда

    По умолчанию Firefox, как и другие браузеры, такой несекьюрный скрипт сейчас не выполняет. Но если вам очень важно, вы залезли в настройки и разрешили его выполнять, то он, соответственно, такую страницу кэшировать не будет. Он скажет — хорошо, ты меня уломал выполнить код, но дальше ни-ни!



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

    Предупреждение: следующий слайд содержит пример кода на страшном языке C++, на фронтенд-конференции это может быть опасным. Не пытайтесь это копировать, выполнять в консоли браузера. У вас может отформатироваться диск, взорваться экран, или начнут майниться биткоины. Я вас предупредил.


    Ссылка со слайда

    Итак, код Gecko. Его можно открыть, прочитать, посмотреть бесплатно в интернете. И я покопался. Там есть самый главный метод — CanSavePresentation(), он отвечает на вопрос: можно ли кэшировать данный документ? То есть это конечный источник правды о том, что прямо сейчас реализовано в Firefox. И еще — именно оттуда я узнал о том, что можно прочитать лог. Есть такая переменная — gPageCacheLog. Это и есть тот лог, в который все пишется. Вот такая интересная история про экскурс в C++.

    То есть вы открываете ссылку, смотрите код, ищете (там удобный и притом быстрый поиск) и можете узнавать актуальные детали реализации в последней версии браузера — такие, которые в документации просто отсутствуют.

    Safari


    Самое жестокое, что делает Safari при попадании страницы в bfcache: если у вас есть незавершенные AJAX-запросы, Safari их просто убивает.



    Даже если вы обложили их обработками ошибок и пытаетесь все проверять, исправлять — запроса как будто вообще не было в принципе. После восстановления из bfcache у вас не будет ни ошибки, ни «OK», вообще ничего.



    Обработчики pageshow pagehide, как я уже говорил, в Safari вызываются, только если они написаны в скрипте, который заинлайнен в страницу. Из внешнего скрипта они могут работать или не работать — как повезет. Но я вас предупредил.



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



    Еще один интересный момент. Как вы знаете, можно через window.open() открывать несколько окон. И при этом у этих окон друг на друга остаются ссылки. То есть они могут синхронно лазить в window друг друга, читать/писать разные свойства. Такое открытие дочерних окон не мешает bfcache. И здесь, скорее всего, Safari так же жестоко поступает, как и с AJAX-запросами, то есть все по-жесткому рвет, и до свидания. Разработчики Apple любят пожестче.

    Снова минутка C++! Исходники WebKit тоже не секретны, их тоже можно открывать, читать и изучать.


    Ссылка со слайда

    Они лежат на GitHub, там я выделил две важных функции:

    — canCacheFrame() — проверка, можно ли кэшировать данный фрейм.
    — В разных объектах на странице, типа HTML-элемента или шрифта, есть методы canSuspendForDocumentSuspension(). Этот объект отвечает за то, может ли он закэшироваться, заморозиться, или не может.

    И еще один важный аспект: у WebKit есть очень удобные тесты. Там прямо в папке LayoutTests/fast/history в виде маленьких, компактных, лаконичных html лежат тесты на работу разных API, которые реализованы в Safari с bfcache. Это живой пример того, как можно писать код в Safari с этими API и как они влияют или не влияют, разрешают попадание в bfcache или не разрешают. Очень интересно посмотреть.



    Оттуда же я узнал, что Safari тоже пишет все свое знание о bfcache, все особенности, в текстовый лог. Но, к сожалению, я так и не раскопал, как включить это логирование или, если оно включено, где найти этот файл на диске. Если кто-то знает, скажите мне, я буду очень благодарен.

    Chromium.




    Как я уже говорил, там работа пока в процессе, все закрыто под флагом. Вы можете загрузить свеженький Chrome Canary, зайти во флаги. Там спрятана настроечка — можно попробовать ей поиграться. Возможно, вы что-то увидите.



    Из интересного — я уже говорил про открытие страницы через window.open(). В Chromium пока что такие страницы не кэшируются. Они не придумали, как корректно все это разрулить, а бессовестно все обрывать, как в Safari, им совесть не позволяет.

    Если не наступил DOMContentLoaded, то страница тоже не будет кэшироваться.

    Есть много новых API, которые начинаются с «Web» — с ними тоже пока сложно и непонятно, и пока что все они по умолчанию выключают bfcache. То есть если на странице используется что-то модное, новое, типа WebGL, WebVR, WebUSB, WebBluetooth и прочее, — в bfcache такая страница не попадет.

    Service Worker. Тоже пока что не кэшируем такую страницу, но планируем ее корректно обрабатывать, прятать от зорких глаз Service Worker.

    Если разрешена геолокация — тоже пока не кэшируем. Так проще пока что.

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


    Ссылка со слайда

    Ребята из Google пошли еще дальше. Они предложили все объединить формализовать, унифицировать во всех браузерах, предложили спецификацию page lifecycle по всем стейтам, предложили добавить новых событий на переходы между разными стейтами. Вы можете посмотреть по ссылке, что они там придумали.


    Ссылка со слайда

    Исходники. Как вы знаете, исходники Chromium тоже доступны. Все это лежит в классе, который называется BackForwardCacheImpl — очень хороший нейминг, почти не пришлось искать. Главный метод, который проверяет, может ли документ сохраниться, называется CanStoreDocument(). Там же есть метод GetDisallowedFeatures(), в котором просто перечислены все новые фичи и API, которые не поддерживаются в bfcache. Очень удобно: в одном месте сосредоточено, прочитал и понял, что на текущий момент можно, а что нельзя использовать.

    Internet Explorer 11


    Экскурс в историю для тех, у кого все еще есть IE 11. Для тех, у кого все плохо.



    Там bfcache есть, и это главная проблема, потому что приходится с этим бороться. В документации написано, что bfcache якобы работает только по HTTP. На самом деле в продакшене он еще и по HTTPS почему-то работает. Мораль: если вы разработчики, пожалуйста, уделяйте внимание своей документации. Потом приходится из-за нее страдать.

    Если есть обработчик beforeunload, то он помешает попаданию в кэш. Про unload они в документации ничего не сказали — возможно, не знали или забыли про него.

    Если не завершилась загрузка страницы, она тоже не кэшируется. Если кто-то использлвад ActiveX-компоненты, тоже не кэшируем. И если там открыты DevTools — тоже. Это важный момент.



    Как без багов? Добавили поле persisted, но оно иногда не срабатывает. То есть страница реально попадает в кэш, возвращается из него, и при этом persisted не выставлено в true. Что делать?

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



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



    При этом как узнать, что страница из кэша? Мы считаем время ее загрузки. Если она загрузилась с сервера за 50 миллисекунд или быстрее, то такого в принципе не может быть в IE — значит, она из кэша! :)



    Про навигацию по истории я уже упоминал. Если у нас тип навигации back_forward, то мы перешли по истории, и если страница из кэша — значит, это bfcache, других вариантов в IE не дано.

    Что дальше?


    Что же дальше делать с этими знаниями? Не хотелось бы, чтобы вы вышли и все это забыли как страшный сон.

    — Во-первых, вот самое ценное, на что я натолкнулся и к чему хочу вас подтолкнуть: пользуйтесь открытыми исходниками браузеров! В открытом доступе в интернете прямо сейчас лежат исходники всех ведущих браузеров, которыми пользуются наши клиенты. И это самая актуальная документация по тому, что и как поддержано, где и как работает. В том числе там есть тесты, которые прямо написаны на HTML и JS. Пользуйтесь, смотрите!

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

    Открыть гифку

    Пользователь вводит четыре запроса, видит четыре выдачи. После этого возвращается назад, видит выдачу 3 и запрос 4. Еще одна предыдущая выдача — 2 и запрос 3. То есть у него состояние страницы рассогласовано — содержимое строки поиска и выдачи противоречат друг другу. Учитывайте это у себя в приложениях.

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

    Как и обещал, ссылка на материалы.
    Яндекс
    Как мы делаем Яндекс

    Похожие публикации

    Комментарии 22

      +1

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

        –2
        В первых строках статьи рассказывается (*) о базовом свойстве любого пристойного браузера.
        Вопросы:
        0) хорошие бразуеры умеют это делать даже после отключения инета. Или они плохие?
        1) что мешает такому ожидаемому поведению?
        2) у Яндекса есть брендированный браузер — он так умеет? Я перестал читать статью после возникновения вопросов выше, а также по причине, указанной в конце этого моего коммента — см. (**) ниже.
        3) про что еще я забыл спросить? (с)

        Заранее благодарю за восполнение этих очевидных(!(?)) пробелов.
        Ведь пробелы случаются у всех — как у читателей, так и у писателей...
        была придумана технология BFCache, о которой важно помнить при разработке интерфейсов
        чем BFCache отличается от (*)?
        Причем тут интерфейс?
        «Интерфейс» браузера или страницы поисковой выдачи?

        Как много вопросов… это только я такой тупой? (**)
        Или тот же вопрос помягше: если с самых первых строк статьи возникают вопросы, на которые эти строки ответа даже не обещают — многие ли станут читать такие статьи сегодня, когда эмодзи заполонили все?
        Или сэкономить на «введении»(тм) — это надежда прорваться к тем, кто читать лонгриды неспособен? Но и тогда статья вышла слишком длинной... Или задачи продать слона и не стояло?
          0
          0. То, что вы видели, это, скорее всего, PWA или Service Worker.
          1. Это не совсем то, о чем идет речь в статье. Если реализовывать такое через Service Worker'ы, то будет очень больно.
          2. «Брендированный браузер Яндекса» — обычный Chromium с кастомным интерфейсом и фишками.
          3. На что я ещё забыл ответить?

          P.S. На статью не гоните, она отличная.
            –1
            Задрал дергающийся рендер
            Задрал рендер, дергающийся и тормозной из-за медленной подкачки местных скриптов с серверов Хабра.
            На что я ещё забыл ответить?
            0) Или они плохие?
            1) Нипанятна ваще — для ламера вроде меня, но...
            На статью не гоните, она отличная.
            Тогда на кого она рассчитана? На крутейших слушателей исходного доклада? Тогда для Хабра надо бы адаптировать… или «нинннннада!»?
            2) Он так умеет?
            3) Мой коммент драматически короче статьи, которую Вы хвалите, но даже из моего коммента остались вопросы, которые Вы пропустили — их список см. выше. «Это заставляет задуматься...» (с)
              0
              0. Они не для этого предназначены. Service Worker'ы используют для кеширования статического контента, на этом и построена технология PWA. В случае использования PWA вы не сможете закешировать страницу как показано в статье (а там страница открывается моментально). Да, вы можете закешировать запросы к API, но рендер всей страницы остаётся.
              1. См. пункт 0.
              2. В статье написано, что данная фича находится в разработке = нет.
              3. BFCache кеширует то, чего вы не сможете закешировать с использованием Service Worker'ов, например какие-то просчитанные внутренние данные DOM.
              4. Не понял вопроса про интерфейс, как и цитаты из статьи, в интерфейсе браузера есть кнопочки «Вперёд» и «Назад», при использовании BFCache они работают как будто вы переключаете вкладку, без — как простой переход на адрес.

              P. S. Я не обязан отвечать на все ваши вопросы.
                –1
                Я не обязан отвечать на все ваши вопросы.
                +1, но тогда ст0ило ли стараться, если все равно ответа для ламера сформулировать не удалось?
                пп. 0 + 1 = нипанятна ни черта
                На статью не гоните, она отличная.
                Тогда на кого она рассчитана? Нет ответа… (с)
                JFYI habr.com/ru/news/t/496698/#comment_21488004
                Тут народ про IDА PRO и ее неоднозначного автора с его неоднозначной борьбой с пиратами не знаком — и возмущается, и комментов там больше, чем здесь, хотя тема явно менее популярная.
                Выводы об успешности статей в корпо-блоге, не имеющих комментов, можно сделать очень неоднозначные
                  +1
                  хорошие бразуеры умеют это делать даже после отключения инета. Или они плохие?

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

                  Тогда на кого она рассчитана? Нет ответа… (с)
                  JFYI habr.com/ru/news/t/496698/#comment_21488004
                  Тут народ про IDА PRO и ее неоднозначного автора с его неоднозначной борьбой с пиратами не знаком — и возмущается, и комментов там больше, чем здесь, хотя тема явно менее популярная.
                  Выводы об успешности статей в корпо-блоге, не имеющих комментов, можно сделать очень неоднозначные…

                  А может у людей просто не осталось вопросов?
                    –1
                    Спасибо, что не бросаете меня в неведении.
                    хорошие бразуеры умеют это делать даже после отключения инета. Или они плохие?
                    Браузеры этого не умеют, это определенные сайты умеют кешировать себя.
                    Тогда как понимать термин offline browser ваще и соотв. фичу у некоторых хороших oNline браузеров в их меню, в частности?
                    А может у людей просто не осталось вопросов?
                    А может: Хабр - не место для дискуссий! (с) Денискин
                    который ограничивает авторов на комменты даже в их собственных статьях, например: позволяя recovery mode, но отрезая авторов от возможности (очевидно оперативного, а не раз в час или даже в сутки) обсуждения их статьи. Потому что Денискину нужны только статьи — но ничего сверх того, и это никак не скрывается даже в местном ФАКе.
                    Как тонко, а?
                    И таких примеров много: см. местные кармасрачи, если интересны душераздирающие подробности.

                    Это к тому, что узнавать у Вас дальше про комменты к местным статьям мне не интересно — я все понял,
                    а вот про браузеры Вы явно знаете больше моего: мой вопрос выше.
                      0
                      Тогда как понимать термин offline browser ваще и соотв. фичу у некоторых хороших oNline браузеров в их меню, в частности?

                      Использовал множество разных браузеров: Chrome, Firefox, Safari. Такой фичи не замечал. Возможно, даже если она существует, то это просто полное кеширование сайтов. А это не очень хорошо работает со всякими новомодными штуками типа Vue, React и т.п.

                      А может: Хабр — не место для дискуссий! (с) Денискин

                      Возможно, Хабр не для вас? Философию хабра создает и поддерживает сообщество, то есть большинство. Если вы не входите в большинство, то извините, вам не будет тут хорошо. И так не только на хабре, так везде. Жизнь уж такая. Советую написать об этом всем статью и изложить свои мысли там.
                        –1
                        Использовал множество разных браузеров: Chrome…
                        Такой фичи не замечал
                        Первая строчка в гугле:
                        How to Enable Offline Browsing in Chrome
                        www.howtogeek.com › how-to-enable-offlin…
                        12 июл. 2017 г. — To enable the built-in offline mode in Chrome, type chrome://flags/#show-saved-copy in the address bar and press “Enter”. This will take you ...
                        Поэтому мне интересно:
                        Возможно, Хабр не для вас?
                        Вам тут все еще уютно?
                          +1
                          > Первая строчка в гугле:

                          Это костыль. И не имеет ничего общего с BFCache. Эта штука работает, (загружает данные из диска, скорее всего) когда не удается загрузить данные из сети. BFCache же хранит данные в оперативной памяти и используется только тогда, когда вы используете навигацию назад или вперёд.

                          > Вам тут все еще уютно?

                          Да
            +1

            Автор вам ничего не должен. Если вы не можете соблюдать элементарную вежливость, то не длите ответов.

              +3

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


              0) Хорошие браузеры умеют только быстро выполнять JS и предоставлять ему нужные API. После отключения инета отображением страницы занимается Service Worker (если он есть), подсовывая на запросы браузера нужные ресурсы. И всё равно в этом случае браузер строит страницу заново, запрашивает HTML-документ, парсит его, запрашивает встреченные в документе ресурсы, выполняет JS. Это требует времени и сильно отличается от "показать ту же самую страницу, которая уже была загружена". А если разработчик страницы не прикрутил к ней Service Worker, то после отключения инета браузер её открыть не сможет.


              1) Ожидаемому поведению "браузер открывает все страницы очень быстро" мешает много что (например, тормознутость самих страниц, или большой RTT в мобильных сетях). Раньше о таком никто вообще не задумывался — страницы наподобие https://www.motherfuckingwebsite.com/ и так грузились быстро. Но шли годы, количество JS, CSS и картинок на странице росло, всё больше разработчиков вступало в клуб "мой бандл больше одного мегабайта", и мы оказались там, где оказались — на бюджетных устройствах большинство современных страниц открывается медленно, даже несмотря на все оптимизации в браузерах и новые технологии типа 4G, 5G, HTTP2 (очень часто для фронтендеров новая технология — это лишь повод впихнуть на страницу что-то ещё).


              2) Да, свой брендированный браузер есть, и некоторые реализованные в нём фичи даже потом оказываются в коде Chromium, но готового bfcache в Yandex Browser нет. Код bfcache пишут разработчики из Google.


              3) Спрашивайте, постараюсь ответить.


              при разработке интерфейсов

              Я имел в виду интерфейс страницы, фронтенд. Это яндексовая терминология (есть "разработчики бэкэнда" и есть "разработчики интерфейсов").


              сэкономить на «введении»

              Это субъективная оценка. Извините, я не смог сделать введение таким, чтобы все были удовлетворены. Кому-то мало, кто-то, наоборот, после доклада говорил: "зачем такое длинное введение, и так всё понятно".

                0
                Спасибо за появление здесь!
                Очень быстро показать страницу, с которой ушёл пользователь (именно ту самую страницу, а не её похожую копию) — это далеко не базовое свойство любого пристойного браузера.
                Какие традиции мешают иметь фичу: тупо показать как было (сохранено в локальном кэше), после чего опционально реагировать на ручной F5 ?
                Страницу надо где-то сохранить
                А для чего же тогда ваще существует локальный кэш браузера?
                подсовывая на запросы браузера нужные ресурсы

                если разработчик страницы не прикрутил к ней Service Worker, то после отключения инета браузер её открыть не сможет.
                Очень часто вижу заглушки вместо картинок с неоригинального сервера. Также нередко бывают жалобы от тех, у кого страница (не на Хабре) никак недозагружается — а поэтому и не показывается ваще, никак, даже «частично». Такое успешно забарывают укорочением таймаута на такие запросы — и снова показывают заглушки вместо этих сокровищ со стороны.
                Чем это хуже «ваще ничего не покажу, а буду ждать бесконечно!» (с) тупые браузеры.
                кто-то, наоборот, после доклада
                моя претензия была только на тему: доклад для неизвестной мне аудитории и статья на нынешнем Хабре (см. про нынешнюю аудиторию на примере IDA PRO) — это очень разные продукты должны быть, имхо.
                зачем такое длинное введение
                «должны» хотя бы потому, что доклад вживую не имеет удобной «перемотки», а статья легко навигируется по разделам, первым из которых м.б. это самое «введение». А может и не быть — зависит от целей публикации…

                Спасибо за остальные ответы.
                Спрашивайте, постараюсь ответить.
                Других столь же тупых и\или ламерских вопросов у меня еще вагон есть и\или появятся, но сразу вываливать их все не я буду — уч0ный Денискиным уже, как можно заметить тут же, гы…
                  0
                  * я не буду
                    0
                    Какие традиции мешают иметь фичу: тупо показать как было (сохранено в локальном кэше)

                    Здесь противоречие уже в самом вопросе. "Тупо показать как было" не равняется "сохранено в локальном кэше".


                    Нигде, кроме bfcache, не сохраняется "как было видно на экране с учётом выполненных на данный момент скриптов и действий пользователя на UI (отметки чекбоксов, разворачивания деревьев и т.п.)".


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

                      0
                      «Тупо показать как было» не равняется «сохранено в локальном кэше».
                      я подразумевал, что все браузеры позволяют сохранить страницу локально, и даже с разной полнотой\в разных форматах.
                      Почему не модно делать это автоматически для каждой посещенной страницы и показывать такое в ответ на кнопку «назад»?
                      И (ес-сно (опционально)) для этого хранилища ограничивать его общий размер, дату протухания каждой страницы, и многое другое.
                      надо заново всё распарсить, выполнить, отрендерить.
                      1) есть разница, откуда брать исходники: из локального кэша или из рваного, тонкого и нестабильного (мобильного) инета;
                      2) «а еще можно» сохранять сразу в графическом виде — «для предельной скорости», чтобы парсер даже не участвовал. А чтобы расположенные внутри ссылки заработали — пущай юзер F5 жмет снова: это его выбор, а приятно всем. А кому неприятно — тому фича «отключить это». А для особо тупых — отключить это из коробки, но в настройках можно и включить.
                      И всё равно после этого будет видна страница в первоначальном виде, а не в том, в котором её оставил пользователь.
                      пусть «страница в первоначальном виде» — зато доп. выбор для юзера. Выбор — разве это плохо для конкурентного преимущества?
                      Чтобы «показать как было», нужны специальные действия самой страницы (она должна сохранять своё состояние, и после перезагрузки читать и восстанавливать его).
                      я ламер: если «снимки системы» для бекапа имеют общепризнанный смысл, то почему не имеет смысла такой же «снимок» для страницы? Для «простоты» — прямо картинкой и сохранять, чтобы не тратиться на парсер вообще.

                      И еще раз особо подчеркиваю: я ламер, и вопросы мои сознательно звучат так.
                        0
                        я подразумевал, что все браузеры позволяют сохранить страницу локально, и даже с разной полнотой\в разных форматах. Почему не модно делать это автоматически для каждой посещенной страницы и показывать такое в ответ на кнопку «назад»?

                        Это и есть bfcache. Как я уже говорил и писал, это очень нетривиальная в реализации вещь, но когда страницы становятся всё более тормознутыми, соотношение "профит от ускорения/сложность реализации" меняется, и теперь bfcache — это модно и выгодно.


                        есть разница, откуда брать исходники: из локального кэша или из рваного, тонкого и нестабильного (мобильного) инета

                        Ещё раз повторю, что даже при 100% попадании в кэш ресурсов исходники всё равно надо распарсить и выполнить, чтобы они превратились в DOM+CSSOM+запущенный JS, и на современных страницах или SPA с большим количеством JS это всё равно занимает значительное время.


                        «а еще можно» сохранять сразу в графическом виде — «для предельной скорости», чтобы парсер даже не участвовал

                        Напомнило старую шутку — сделать скриншот рабочего стола, положить его фоновой картинкой, а все настоящие иконки удалить. Попробуйте такое сделать самостоятельно и почувствуйте разницу: на картинке не будет hover, фокуса, выделения текста, тултипов, меню, клавиатурной навигации, скринридеров, вообще никакой интерактивности. Такое никто из разработчиков браузеров не выпустит в продакшен — засмеют.


                        пусть «страница в первоначальном виде» — зато доп. выбор для юзера

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


                        если «снимки системы» для бекапа имеют общепризнанный смысл, то почему не имеет смысла такой же «снимок» для страницы?

                        Имеет смысл. И это называется bfcache. В качестве аналогии в ОС я бы лучше привёл гибернацию, которая тоже позволяет сохранить и потом восстановить текущее состояние со всеми запущенными процессами, и которая тоже была реализована в ОС далеко не сразу.

                          0
                          все браузеры позволяют сохранить страницу локально
                          Это и есть bfcache.
                          тогда я совсем запутался: до объявления bfcache это имело другое название? Кроме названия — в чем еще разница?
                          … сделать скриншот рабочего стола, положить его фоновой картинкой, а все настоящие иконки удалить. Такое никто из разработчиков браузеров не выпустит в продакшен — засмеют.
                          у первого айфона была позорная убогость с СМС и много чего еще смешного. И над «почесыванием» смеялись очень многие — а теперь юзеров с нормальной не виртуальной клавой ваще ни во что не ставят. И мобильный инет все еще недостаточно хорош, чтобы отказываться от скриншота рабочего стола с возможностью его Ф5ячки только тогда, когда нужно будет не только посмотреть на него… но я совсем не Джобс — это тоже бесспорно.
                            0
                            все браузеры позволяют сохранить страницу локально

                            А, понял, о чём это. Сохранить страницу локально на диске в виде набора HTML, CSS и JS — это совсем не то. После этого не каждая страница сможет открыться в том же виде. И опять же, при открытии браузер проходит всё тот же путь заново — парсит файлы HTML, CSS, JS, собирает DOM, CSSOM, выполняет JS,… дальше см. доклад и мои предыдущие комментарии.

                              0
                              Вроде бы я все понял (что смог).
                              Спасибо за все ответы и потраченное время.
                0
                дергающийся рендер местных скриптов задрал

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое