Pull to refresh

Comments 114

«If-Modified-Since» — это дополнительный запрос.

Идея автора статьи другая:
  • в теле html-файла передать «версию» ресурса
  • клиентским JS-ом сравнить «версию» ресурса на сервере и в localStorage
  • при совпадении «версии» — взять из localStorage
  • при различии «версии» — отправить запрос на сервер


Идея (в теле html-файла перавать «версию» ресурса) — правильная.
А вот использовать для всего остального JS — спорное решение. Навскидку:
  • при отключенном JS-е ресурсы не будут загружены вообще
  • даже если JS работает, будут сломаны все браузерные оптимизации по предсказанию и предзагрузке ресурсов. В результате может оказаться, что авторская версия, даже не выполняя запросов в сеть, будет медленнее, чем могла бы.

Ваше замечание насчет отключенного JS верно, и относится к определенной категории сайтов. Мой лично — как и 95%+ других, наверно — без JS не работает, так что это обязательно.
Насчет браузерных оптимизаций не понял. Пример?
Браузер, получая из сети html, пытается сразу же (прям в процессе получения) парсить, анализировать что можно, и использовать данную информацию.

Вариант 1
<!DOCTYPE html>
<html>
<head>
    <link href="css/main.v1.css" rel="stylesheet"/>
</head>
<body>
  ... BIG_CONTENT ...
</body>
</html>

Тут браузеру никто и ничем не мешает — получив и распарсив один лишь head, браузер поймёт, что ему нужен «css/main.v1.css».
И продолжая «качать» по сети тело html (считаем, что BIG_CONTENT долго передаётся по сети), он попытается получить валидный «css/main.v1.css».
Если браузер будет уверен в валидности своего кэша — возьмёт из кэша; если не уверен — выполнит запрос (и может оказаться, что данный запрос вернет 304 ещё до окончания докачки тела html).
Существует большая вероятность, что к моменту окончания загрузки html у браузера будут все требуемые ресурсы, и он сможет отрисовать страницу.

Вариант 2
<!DOCTYPE html>
<html>
<body>
  ... BIG_CONTENT ...

    <script>
      // load "css/main.v1.css"
    </script>
</body>
</html>

Пока не загрузится весь BIG_CONTENT, браузер даже не сможет понять, что ему нужен «css/main.v1.css».
И даже когда весь html загрузится — не поймёт :)
Браузер должен будет распарсить и выполнить JS.

Вариант 3
<!DOCTYPE html>
<html>
<head>
    <link href="css/some.v1.css" rel="stylesheet"/>
    <script>
      // load "css/main.v1.css"
    </script>
    <link href="css/other.v1.css" rel="stylesheet"/>
</head>
<body>
  ... BIG_CONTENT ...
</body>
</html>

Тут я вынес js-загрузчик «как можно раньше» и добавил пару дополнительных ресурсов.
Для «css/some.v1.css» браузер сможет применить оптимизации из первого примера.
А для «css/other.v1.css» — не сможет, пока не закончит работу JS-loader.
CSS в качестве ресурсов и только в заголовке — это только для примера. Это с таким же успехом может быть и какие-нибудь картинки в дебрях BIG_CONTENT.
Всё это верно, но к моему случаю не относится. В то время, как браузер дойдет до строки, подключающей mobile.css, там будет или уже код css из localStorage, или ссылка на ресурс, если он незакэширован. Так что проявить свой «интеллект» у браузера шансов не будет )
Я потому про связку с WebPack и упомянул, что это (для JS, в частности), уверенный и стабильный +1 запрос (в подавляющем большинстве случаев возвращающий в респонсе только заголовки — это сущие байты).

А в варианте автора, при несовпадении версий нескольких файлов, вероятна ситуация, когда клиенты сгенерируют гору запросов по каждому отдельно обновлённому компоненту приложения.
Мы, по-моему, говорим о разных вещах. Все js (как и css) файлы на продакшн сервере у меня и так в один скомпонованы без всяких WebPack-ов. Статья не об этом.
Да, получим «уверенный и стабильный +1 запрос».
И до кучи — увеличение вероятности, что придётся перекачивать весь бандл :)
Для поклонников стандартного кэша браузеров, гордо показывающих на слово «Кэшировано» в Средствах разработчика, советую открыть Fiddler и увидеть, что по каждому кэшированному ресурсу за 302 HTTP ответом всё равно идет запрос

Во-первых, не 302 («Moved Temporarily»), а 304 («Not Modified»).

Во-вторых, запрос браузером закэшированного ресурса говорит о том, что браузер «сомневается» в актуальности кэшированных данных.
Однако существуют механизмы, «убеждающие» браузер в том, что его кэш не требует ревалидации (cache-control immutable).
Ваша реализация («используется время последнего изменения файла как его «версия»») вполне себе закрывается данным механизмом. Причем будет работать даже при выключенном/заблокированном JS в браузере.
С 302 ошибся, спасибо.
По поводу cache-control immutable — как я понимаю браузерная поддержка пока слабая, особенно для смартфонов.
Статьи о «cache-control immutable» — с начала 2017 года.
Тут вот даже ссылки есть на записи в багтрекерах разных браузеров.

Думаю, что сейчас проблематично будет найти браузер (в том числе и мобильный) без поддержки «cache-control immutable».
Вы про expires слышали? Этот заголовок в RFC с появления HTTP/1.1, я специально протестировал, работает в FF3.6.

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

Там почему-то неправильные данные. Указано, что Chrome не поддерживает, но это не так. Можно убедиться на практике – попробовать отдать этот заголовок на каком-то ресурсе и попробовать нажать Ctrl+R – если он в кэше, и кэш ещё валиден – ресурс не будет скачан.

Уже вполне неплохая. Chromium поддерживает, так же как и Safari. Так что уже можно – мы пользуемся, например

Насколько я понимаю, при Html5 Application Cache невозможно обновление только одного ресурса — обновляются все скопом. При редких апдейтах сайта — да, удобней, особенно учитывая, что можно хранить шрифты и статичную графику; при относительно частых (мой вариант) — не очень. Можно комбинировать.
Не знал про Application Cache, честно говоря, спасибо за наводку.
ох, как я устарел ))
Спасибо, за поправку
Use Service Workers instead

Забавно, а с ними уже столкнулся на вебпушах, но только немного по фану, в прод на жс не пишу давно

Ну пока он работает его можно использовать. А работать он будет скорей всего долго.

При правильно выставленных заголовках (Cache-Control, Last-Modified и Expires) повторный запрос 304 отправляется только при нажатии CTRL f5 в webkit браузерах и по F5 в фаерфоксе. Либо по достижение Expires.
При типичном использовании(когда пользователь не касается F5) никаких повторных запросов не отправляется.

При этом существуют техники которые заставляют браузер всегда брать обьекты из кеша, вне зависимости от F5 или CTRL f5.

Идее хранить css js и даже шрифты в localstrage сто лет в обед. И не прижилась она по нескольким причинам:
1. вся работа с localstorage снихронна, что означает — если какая то вкладка по каким то причинам запустить длинную операцию с storage то ваш влкадка будет ждать ее завершения.

2. операция stringify не бесплатна. И уже на обьектах в 40 — 100кб вы получите 40мс на десктопе задержку что больше чем если бы браузер сам взял обьекты из своего кеша. (а на мобильный платформах и того больше).

3. Закешированые браузером обьекты генерируют запрос 304 только в случае неверных изначально заголовков кеширования или (в зависимости от браузера) по нажатию f5 или ctrl f5

4. при загрузке ресурсов(css, js, img и так далее) по необхъодимости, а не сразу всего и сразу, даже нажатие ctrl f5 не заставит браузер слать запрос 304, а будет брать их из кеша.

В результате, выгода от local storage только в том случае когда вы:
А) не можете конфигурировать свой веб сервер для правильной отдачи заголовков для кеширования.
Б) если пользователь на очень медленном соединении по каким то причинам давит часто f5 у вас на сайте, при этом вы не используете «умную загрзуку» необходимых для страницы ресурсов.

1. В рассматриваемом случае задержек нет
2. Это на порядок (порядки) меньше затрат на запрос за 304 ответом
3. и 4. Откройте Fiddler
А разве fiddler не отключает кеш?
Кеширование на стороне браузера это одна из главных проблем которой занимаются тысячи очень умных людей отвечающих за скорость работы фронтенда. Потому, когда вдруг кажется, что ты нашел святой грааль для этого вопроса, нужно сначала спросить себя — а вдруг я ошибся, погуглить статьи бородатых мужиков, и как минимум поднять свой веб сервер. Чтобы без посредников видеть что и при каких случаях реально доходит до сервера при использовании в условиях максимально близких к реальным.
А теперь о ремарках

1. В вашем конкретном случае это так. Почему? Потому что у вас одна вкладка с таким запросом. Создайте вторую страницу и обратитесь в стораже, одновременно с этим вызовите у себя. Обнаружите то о чем я говорил. Теперь представьте ситуацию. Все поняли как здорово использовать локал стораже для хранения таких больших обьектов и стали массово его использовать. Очень нередки случаи когда у людей по десятку активных вкладок.

2. Повторяю, запрос 304 не шлется при правильных заголовках Cache-Control, Last-Modified и Expires. Подымайте свой сервер. На нем разворачивайте проект. Поставьте для простой html странички cache-control паблик с max-age далеко в будущем. Аналогично для epxires. и ласт модифай в прошлом. Уберите etag заголовок. Открывайте в браузерах смотрите логи — смотрите результаты. Будет именно то что я Вам, (и не только я) говорю. Запросы 304 при таких случаях посылаются только в случаях что я обозначил выше.
Важное замечание. Ресурсы которые грузятся с параметрами кешируются по другой схеме.
Иначе говоря поведение при загрузке my.js и my.js?123 принципиально разное. Потому что в этом случае браузеры понимают такие ресурсы как динамические.

Я никогда не пользовался Fiddler. И не в курсе как он работает. Поэтому ничего о нем сказать не могу, кроме того, что любой посредник между сервером и клиентом может искажать результаты.

Об умной загрузке.
Загрузка любого веб приложения можно разделить на два этапа. Первый — это загрузка всего что нужно для первого экрана пользователя. Тут нужно влезть в 14 килобайт (все вместе html css и т.д.). И второй этап загрузка всего что может понадобится после.
То есть загрузка десятков килобайт javascript кода css файлов и прочего прочего должно идти после того как бразуер отрендерил первый экран пользователя.
«После того» тоже бывает разным. Можно опять же пытаться грузить все одним файлом как только это потребуется, или появилось время. А можно грузить отельными небольшими порциями. Все это сильно зависит от обстоятельств работы приложения и соответствующей конфигурации серверной части. Потому что, универсального варианта нет. В одном случае будет выгодно загрузить все сразу во второй фазе. В другом случае грузить десятки файлов под каждую конкретную задачу.

Так вот, возвращаясь к нашему вопросу. Все ресурсы (с правильными заголовками) загруженные во второй фазе, будут браться из кеша всегда если они там лежат, без дополнительных запросов. Исключение Firefox который по ctrl f5 все же пошлет для изображений 304. Для прочих ресурсов нет.

Начну с конца — в FF и js и css ресурсы (не только картинки) по F5 лезут за 304 ответом — проверял сегодня на тестовой странице. По ctrl+f5 все обновляется.

Во-вторых, у вас вариант когда браузер может то запросить ресурс, то не запросить (про F5 точно выяснили, но наверно есть и другие нюансы браузерной логики, которые гарантировать нельзя). У меня вариант, что он запрашивает ТОЛЬКО ТОГДА, когда ресурс меняется и его надо обновить. Для меня лично выбор очевиден, другим ничего не навязываю. Просто поделился.

В-третьих, вы советуете мах-age поставить. Но у меня сайт развивается, css и js частенько меняются, что делать? Параметр после "?" использовать нельзя, как вы сами сказали, значит нужно менять имя? Некрасиво, как по мне. Да и кэш браузерный захламлять.

Про вкладки не понял. Вы апеллируете, что для каждой вкладки будет запускаться движок локал стораджа с моими 500 кб и это нагрузит систему?
Вот смотрите, у меня сейчас около 20 вкладок открыто, полдня где-то. Я не работаю, просто читаю-пишу в нете. Вот загрузка:
image
Вы найдете там несчастные 500 кб моих скриптов и стилей?

Про умную загрузку понял. Вы бы о ней Google рассказали с его сайтами)
Я хочу, чтобы при показе первого экрана пользователь сразу мог кликнуть на меню и оно открылось. Чтоб он не кликал и не расстраивался, что оно заработает только через несколько секунд. Для этого надо загрузить JQuery и 70% (по весу) скриптов. Какой смысл разбивать? Нету. Я пробовал, правда. И css и js 30%-70% нужно вверх. Такой сайт и менять его поведение я не буду, потому что оно хорошее. Вместо этого кэш в локал сторадж решает все проблемы. Топор тоже не предназначен гвозди забивать, но прекрасно справляется с этой работой.
> Начну с конца — в FF и js и css ресурсы (не только картинки) по F5 лезут за 304 ответом — проверял сегодня на тестовой странице. По ctrl+f5 все обновляется.

Ну так правильно, пользователь просит обновить страницу с сервера, браузер это делает. Так и должно быть.

> У меня вариант, что он запрашивает ТОЛЬКО ТОГДА, когда ресурс меняется и его надо обновить.

Как принудительно обновить страницу с сервера в обход кеша?

> Параметр после "?" использовать нельзя, как вы сами сказали, значит нужно менять имя?

Во-первых, можно, он наверно ошибся, во-вторых, можно менять имя папки: /css/v11/styles.css. Вы бы основы протокола HTTP изучили, глядишь и не пришлось бы кривые велосипеды изобретать.

> Вы апеллируете, что для каждой вкладки будет запускаться движок локал стораджа с моими 500 кб и это нагрузит систему?

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

> Я хочу, чтобы при показе первого экрана пользователь сразу мог кликнуть на меню и оно открылось.

У вас же блокирующий код с document.write. Почитайте про асинхронную загрузку скриптов на досуге.

> У меня все работает

Тогда не пишите туториалов и не учите других людей неправильным вещам. Если вы беретесь писать статью, изучите хотя бы минимально теорию, а то иначе один неграмотный человек учит других.
1. Об отличии в поведении FF и webkit браузеров я писал с самого начала. FF всегда пошлет 304 по f5 на те ресурсы которые были обьявлены на тсранице. webkit Только по ctrl f5.
2. Браузер что FF что webkit НИКОГДА не запросит даже 304 если ресурс подгружался динамически во второй фазе. Исключение если ресурс грузится с параметром.
3. Да менять имя. И так это и делают все нормальные люди. Меняют имя не руками конечно. Какое в проекте имя и какое отдается в страницу — вопрос организации работы. Кеш так вы не захламите. Браузеры очень оперативно исключают из кеша то, что не используется даже с большим max-ege.
4. по вкладкам, я имел ввиду когда много разных ресурсов начнут активно пользовать большой обьем данных в локалстораже. В результате вы получите ситуацию когда ваша вкладка может висеть и ждать своей очереди.
5. Гугл именно так и работает везде где это возможно. Откройте google.com и посмотри в его внутренности. Подымите свое понимание относительно того, как писать быстрые страницы на новый уровень. А для гугла, каждая миллисикунда стоит больших денег. Кстати в гугловсом Pagespeed тесте этому уделяется очень большое внимание.
6. То что Вам для работы меню нужен JQuery говорит о том, что вы неправильно выбрали инструмент для решения своих проблем. В настоящий момент просто открытие меню со свистоперделками можно писать на чистом CSS без javascript кода вообще. Или с минимальным кодом в несколкьо команд.

Главное.
Вам не только я пытаются сказать, что в вашем материале неверный посыл. Работа через LocalStorasge имеет смысл только в очень специфической ситуации (неспособность правильно выставить заголовки кеширования на веб сервере). Во всех же остальных случаях, работа с кешем бразуера не хуже, а зачастую лучше чем велосипед с LocalStorage, который может еще и вредить производительности.

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

Вы сейчас всех веб-разработчиков, использующих JQuery, загнали в стойло лузеров )

У нас с вами просто разное понимание о том, какой должен быть сайт, разные сферы деятельности. Нет общего предмета обсуждения.
Нет. Я просто сказал что JQuery это отличный инструмент для своих задач.
Мой текущий предмет деятельности — это миллисекунды которые нужно выиграть чтобы проект работал быстро. И это не зависит от того что перед Вами — сайт или веб приложение. Методы для оптимизации этого едины.

Вы в своей статье даете вредный и чудовищно неправильный посыл.
О чем я еще раз заявляю. И подчеркиваю — ТАК ДЕЛАТЬ НЕЛЬЗЯ.
Да автор просто ничего не понимает. Я про кеширование, про Expires и Last-modified читал еще 10 лет назад — тогда это была горячая тема, а сейчас все уже давно придумано и изобретать тут нечего.
Хватит писать фигню. Я тестировал на встроенном в PHP веб-сервере в FF3.6 — он показывает, что ресурс, закешированный через expires, не запрашивается при переходе на другую страницу. А вам сообщу, что часто в браузерах при открытии инструментов разработчика отключается кеш.
Объясните, пожалуйста, что такое «умная загрузка»?
Идея ооочень старая. Лет 5 назад юзал на парочке SPA с помощью github.com/TradeMe/bootup.js. Давно отказался, все же localStorage для другого создавался.
Какая разница для чего создавался localStorage, если это работает и дает ощутимую выгоду для пользователя?
Да просто есть инструменты, которые как раз создавались для этого. Не понимаю любовь к велосипедам эксплуатирующим инструменты созданные для другого.

По моему мнению, в целом данный способ рабочий, но для очень узкого спектра веб-приложений. В основном SPA. Сам использовал и для тех задач, это было норм, но с появлением Service Worker данный велосипед вообще теряет смысл.
Напишите разъяснительный пост на хабре как с помощью Service Worker кэшировать ресурсы. буду благодарен.
А поиск вам на что? Вот, например, уже написанный пост: Service Workers. Инструкция по применению. (https://habr.com/post/345552/). Там и про localStorage упоминается.
Хорошая статья, спасибо.
Только я не совсем понял, почему в localStorage не следует хранить свой css, а через Service Workers можно.
Концептуально, кроме синхронности localStorage в чем проблемы?
Понятно, что Service Workers удобней и функциональней, но почему кэшировать пару текстовых файлов в localStorage вредно?
Я не пытаюсь вас убедить в том что это вредно. На самом деле для некоторых типов проектов, например, каких-то простеньких браузерных игр, это даже полезно. Можно сказать, что это некий вид «инсталяции» веб-приложения прямо на устройство пользователя. Если веб-приложение подразумевает подобный механизм, т.е. пользователь ожидает чего-то подобного, то почему бы и нет. Этого явно не подразумевают обычные веб-сайты и большая часть SPA приложений тоже. К тому же, при наличии достаточного количества ассетов, например графики, объем localStorage довольно быстро переполняется и нужно искать комбинированные решения.

Из очевидных минусов этого подхода, по сравнению с Service Worker:
  1. Создан как хранилище ключ-значение, а не для кэширования файлов, поэтому может иметь ряд ограничений для этой цели как в настоящем, так и в будущем.
  2. Имеет синхронный интерфейс и управляется из js, т.е. «вешает» страницу и вообще не работает если вдруг наблюдаются проблемы со скриптами (на мобильном интернете это сплошь и рядом)
  3. В отличие от Service Worker, который просто прокси, полностью подменяет собой браузерные механизмы загрузки ресурсов, что может быть чревато. Service Worker подключается 3-мя строчкам и ничего не ломает, даже если браузер его вообще не поддерживает.


Из плюсов этого подхода, которые лично мне импонировали 5 лет назад, когда я его использовал:
  1. Единственный способ нарисовать более-менее «честный» прогресс-бар.
  2. Единственный способ действительно грузить ресурсы параллельно (обычно 7 потоков) во всех бразуерах начиная с IE 8
Вы — нет, а другие называют вредителем, и, судя по тону, скоро дойдет до личных угроз даже. Как будто я покусился на что-то святое.

Я не призывал пихать в localStorage графику и шрифты. Не говорил, что это универсальное решение. Конкретно по моему случаю — пара текстовых ресурсов, оба в любом случае грузятся блокирующими синхронными запросами (и синхронность localStorage тут естественна — просто один программный поток). localStorage я использую просто как глобальную переменную — он для этого и создан. Сравнивать его по функционалу и предназначению с Service Worker все равно что сравнивать массив строковых переменных и программный фреймворк со своей базой данных.

У меня стояла небольшая локальная задача, я ее решил так. Пользователю это не вредит — если браузер выделяет под это 5 мегабайт, то, наверное, подразумевается, что там не только коротенькие имя-фамилия юзера будут храниться. Поднявшийся антагонизм не понимаю…
Честно говоря, в вашем случае смысла действительно нет особого. Нравится — используйте, но как я понял сообщество взъелось лишь из-за того, что вы советуете это другим. Многие не смогу верно оценить пользу и вред, особенно если пример вашего сайта действительно не показателен. Я бы тоже рекомендовал перейти на Service Worker. Вам даже делать ничего не придётся, например можно взять: github.com/GoogleChromeLabs/sw-toolbox и 3 строки кода.
Не думаю, что после этого поста за мной последуют толпы последователей.

Я смотрел Service Worker, мне понравилось. В будущем перейду. Единственное, на самом деле что пока не дает — необходимость https. У многих моих пользователей слабый инет, а я как-то долго сидел на EDGE с максимум несколькими кб в секунду. Так вот на такой скорости разница между http и https дискретная — первый как-то качает, второй — нет.
Вам ваша специфика виднее, но лично я ратую за использование SSL повсеместно.
Вопрос, стоит ли хранить javascript и css ресурсы веб-страницы в LocalStorage браузера или позволить ему самому отрабатывать кэширование, не имеет однозначного ответа

Вообще-то есть, и это однозначно нет.


Для этой задачи есть Cache API или на крайний случай IndexedDB, что вместе с Service Worker является стандартом де-факто в современных приложениях, нацеленных на работу offline.

Обоснуете однозначность ответа?

Cache API экспериментален и не поддерживается в некоторых браузерах, работает только по https — я теряю на нем 95% своих пользователей
IndexedDB сложен и сам по себе, и для этой задачи. Микроскопом заколачивать гвозди
localStorage это синхронное хранилище для текстовой информации небольших объемов. Тут нечего больше объяснять.

Cache API и правда не поддерживается в старых браузерах. Оставьте их в покое, если обновляться не хотят. Про HTTPS есть Let's Encrypt, так что если вы уважаете ваших пользователей, у вас нет никакого оправдания не включать HTTPS на всех сайтах что под вашим контролем.

IndexedDB сложен, но позволяет хранить бинарные данные в нужных объемах. Аналогия с микроскопом тут ни к чему.
5 мегабайт на текстовую информацию небольших объемов — противоречия не находите?
Что за текстовая информация размером в мегабайты?
Неудовлетворительное объяснение.

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

Нет, не нахожу. 5МиБ это совсем не много. А если засунете туда пару неизбежно связанных с CSS картинок в base64 и место закончится. Не говоря о том, как неудобно и медленно оттуда синхронно доставать данные.


именно потому, что я уважаю своих пользователей на слабом интернете, никакого https там не предполагается

Ознакомьтесь с HTTP/2. А потом серьезно пересмотрите критерии оценки уважения к пользователям.

У меня и моих пользователей пока что там прекрасно лежат 400 килобайт css и js в виде простого текста, которые очень быстро (относительно ненужных HTTP запросов) оттуда достаются, и не понимаю, чем вам это так сильно не нравится
Вообще nazarpc понять гораздо легче, чем вас. На чем вы экономите? На двух запросах (css + js), которые потенциально могут вернуть 304? Тогда вопросы — а сколько изображений на типичной странице вашего сайта? Никаких счетчиков конечно же на странице нет?

Честно говоря, ваши рассуждения выглядят неубедительно, тем более, что они не подкреплены тестами.
Картинки не играют никакой роли. У них вообще lazy load

До того, как пользователь увидит буковки в открывающейся странице и начнет читать, должно произойти, грубо говоря, 3 запроса:
1. к странице
2. к css файлу
3. к js файлу

Это блокирующие запросы браузера. Я экономлю на 2 запросах из 3. Еще раз, если вы посмотрите время выполнения запросов на pingdom.com, например, то увидите, что пустой запрос за 304 ответом отнимает примерно столько же времени, как и запрос за ресурсом в несколько десятков килобайт. В зависимости от типа сети.

Итого в среднем я выигрываю пускай примерно 50% времени от клика пользователя на ссылку до появления на экране страницы.
Так понятней?
Дело не в объеме скачанной информации, а времени появления страницы. Три запроса.
Понятно, если под загрузкой страницы понимать появление её на экране, без ожидания отработки неблокирующих запросов.

Мне все-таки как то понятнее вот так:
window.onload is not the best metric for measuring website speed
(с) Steve Souders
Источник, 5 лет статье
Google Pagespeed и другие метрики считают по-другому. Основной критерий оптимизации сайта там — скорость появления текста на экране. И это правильно.
Мне кажется, что тенденция все-таки — First Meaningful Paint (FMP) — первая значимая отрисовка. Вы и сами знаете, что пользователь сильно не любит, когда страницу «колбасит» и чаще всего просто ждет полной отрисовки контента. Я говорю о посетителях сайта, а не о всяких там SEO-шных заморочках.
Нет, ваша технология заточена на другую метрику — First Paint (FP) — первая отрисовка.
Позвольте внести вклад в ваше образование. Если вы освоите асинхронную загрузку скриптов, то вам хватит только 2, а не 3 запросов, для отображения страницы.

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

Нет. Картинки у меня плавно появляются не двигая разметку. Например, в блоге, изначально на месте картинок находятся заголовки статей. Затем всплывает картинка, заголовок плавно меняет свой цвет с темного на светлый и становится капшном картинки с полупрозрачным темным оверлеем.
Размер шрифта заголовка вычисляется заранее таким образом, чтобы заголовок не занимал больше 50% площади картинки.
Если пользователь прокручивает экран неспеша (со скоростью чтения) картинка успевает загрузиться и вставиться до момента появления ее в видимой части экрана.
Вот так это работает. Мне нравится.
Причем тут «мегабайта-двух» не понял.
Так это тоже важное примечание к вашей статье (кроме медленного канала).

Т.е. для того, чтобы посетитель сайта видел хоть какой-то эффект от применения данной технологии верстка сайта должна соответствовать следующим требованиям: 1)… 2)… 3)…

Кмк, овчинка выделки точно не стоит.
Для того чтобы указать браузеру что нет нужны каждый раз проверять ресурс и получать 304 ответ достаточно указать expires в будущем и большой max-age в cache-control, например:
expires: Mon, 12 Oct 2037 00:00:00 GMT
cache-control: public, max-age=315360000

developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=ru
F5 в Файрфоксе наплевать на эту комбинацию хидеров. А не должно быть.

А может быть наоборот игнорирование кеша по f5 фича, а не баг?

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

Игнорирование Кеша сделано намеренно, на случай, если в кеш попало что-то, что не должно было. В вашем варианте такого решения нет (нажать ctrl+shift+r знают и умеют многие, чистить localstorage — я бы тоже не допер)

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

Только если они жмут Ф5 (что, кстати, обосновывает проверку локального кэша через отправку ХЭД запросов) при обычном переходе по ссылкам фаерфокс для закэшированных ресурсов сеть дергать не будет.
Обычные переходы внутри сайта у меня и так на XHR реализованы. Мне важны заход на сайт и F5.

Я так и не могу четко услышать «минусы» моего подхода, кроме «локальное хранилище для этого не предназначено».
«Минусы» кэша браузера есть — бесполезные походы за 304 ответом. А «минусы» кастомного кэша в локальном хранилище?

Помните, я говорил, что localstorage чистится в случайное время? Так вот, он не чистится. В итоге если пользователь хоть раз зашёл и ушёл навсегда — ваши данные будут впустую захламлять ему место. К счастью, от этого решения 5 лет назад все отказались и забыли о нем.


Ещё недостаток — мне было бы неприятно, если бы моя страница фризилась на пол секунды. А ещё веб воркер гораздо лучше подходит — клиент не будет вообще ни за какими данными кроме небольшого json с текстом ходить и работать будет мгновенно.

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

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

https://developer.mozilla.org/ru/docs/Web/HTTP/%D0%9A%D1%8D%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5#Freshness


Вообще тут великолепно описано что и как. И самый простой и эффективный и правильный способ — Cache Control. И никаких там 304 не бывает (если ресурс еще не expired). И никаких лишних запросов. Повторюсь, в Fiddler это сделано намеренно (им проще с 304 жить).


Полагаю, мои 400 кб данных не сильно утяжелят его терабайтный диск с кучей реально ненужного хлама )

Вообще не сочтите за наезд, но 400 кб данных у пользователей с терабайтными дисками и жаловаться на скорость загрузки — я представить себе такую задачу не могу. Либо у пользователь мобильные телефоны с GPRS и надо бы переделать с 400кб на не более ста, сперва, да и вообще чем больше отдаст сервер уже зарендеренного, тем лучше, либо проблема высосана из пальца и решается политиками кеша и CDN. (А еще один сайт 400кб, второй, а через месяц вникуда отожраны пару ГБ. У меня, например, на походном ноуте 128 гб памяти, а не терабайты и я там подсчитываю кто обнаглел).


  • Service workers, не web workers, попутал немного.
Повторюсь, в Fiddler это сделано намеренно (им проще с 304 жить).

Ну объясните мне, пожалуйста, каким образом Fiddler влазит в политику браузера — посылать запрос к ресурсу ли нет?

У пользователей средние телефоны, средние компьютеры, и плохая сеть. Провинция, ничего необычного. Ключевое слово — плохая сеть. Избавляемся от лишних ненужных запросов браузера, которые могут занимать секунды. Для этого реализуем постоянный кэш из localstorage. как наиболее эффективное решение проблемы.
Ну объясните мне, пожалуйста, каким образом Fiddler влазит в политику браузера — посылать запрос к ресурсу ли нет?

Вы статью по ссылке читали? Шлет max-age: 0 или типа того. В статье же четко написано — если кеш истёк — браузер спрашивает страницу, если пришёл 304 — рисует из кеша. Не истёк — ничего не спрашивает.

Мне важны заход на сайт и F5.

Но по итогу вы оптимизируете только Ф5 (сомнительное занятие, на мой взгляд).
Только в вашем случае при открытии сайта — вам еще нужно застопарить парсинг на том месте где вы дергаете и проверяете свой скрипт.
Просто добавьте вашему файлу стилей версию в название файла и поставьте max-age на миллион лет — будет тот же эффект, даже лучше.
А вы проводили замеры скорости загрузки страницы с вашей оптимизацией и без? И после изменения файлов? Просто что то подсказывает что отклик будет выше. Особенно на нормальном интернете.
Вы правы, нужно было в самом начале более четко предупредить, что данная тема только для сайтов с пользователями со слабым интернетом. Если у тебя мощный компьютер и толстый канал, о таких вещах можно не задумываться.
Замеры не делал и не вижу в них необходимости — сайт ощутимо быстрей прорисовывается, потому что нет двух блокирующих запросов к стилям и скриптам в шапке страницы. Запросы за 304 HTTP ответом на слабом интернете занимают секунды. Даже если хорошо настроены временные валидаторы, такие ненужные запросы в определенных случаях будут. В моем варианте — нет.

А может тогда не нужно пихать блокирующие стили и тем более js в шапку? Проблеме сто лет в обед, и лезть внутрь механизма кеширования далеко не лучшая идея.

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

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

Может вы объясните?

Давайте на чистоту. Если бы все было так просто, браузер бы внутри работал так же. Но у него другая логика, значит на это есть причины.


  1. Когда у ресурса отдаётся cache policy, браузер не ходит второй раз за ресурсом, иногда это даже создаёт проблемы. В случае с fiddler — у них намеренно настроена политика таким образом, чтобы браузер переспрашивал, потому что отдаваемые ресурсы меняются часто, без этого он бы только засрал и кеш и свои хранилища (файлами с случайным хешем).


  2. Браузер вправе кешировать не сырые данные, а уже AST или даже результат работы JIT, ещё он компилирует и кеширует шаблоны и вообще умён в этом. Хранение в localStorage обходится дорогими операциями изменения DOM и перепарсинга сорцов. Вообще они сперва грузятся в одно место, потом копируются в другое, затем идут в третье и там снова парсятся.


  3. Для первой страницы и первого взаимодействия обычно достаточно крошечных, иногда in-line стилей, js не нужен вообще, его стоит убрать в футер и написать async. Когда первый вид догрузится — может догружать что хочет, пользователь уже видит страницу, но ща первые 3 секунды вряд ли куда будет кликать.
  1. Использовать HTTP2, preload и прочее, например Server push.


  2. Первую страницу рендерить на бэке, догружать только куски


  3. Грамотно побить на чанки, убрать лишние зависимости.


  4. Как уже сказали — использовать предназначенные для этого web workers.


  5. Использовать CDN

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


Либо у вас простенький сайт и проще обойтись вообще без js, либо оно очень быстро упрется в ограничения, из-за которых браузеры сбрасывают кеш по f5

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

Вам давно ответили, что прописать заголовок Cache-Contoll: maxage=9999999, но вы уперлись в какие-то бредовые отговорки типа «на fiddler ходит с 304» и «браузер игнорирует это поведение при нажатии на f5”, хоть я вам и сказал почему происходит и первое и второе. И пруфы, что отправить заголовок и получить заголовок занимает 3 и более секунды скриншотом в студию. Это даже для GPRS многовато.


Но если хотите делать по-уму — я расписал как. В чем претензии?

Где вы видите претензии?
Но, извините, предпочту остаться со своим небольшим вариантом, минусов которого я так и не услышал.

А ещё я уверен, что при достижении такого Кеша 100 кб вкладка начнёт подвисать в момент обновления, а при 500кб вообще валиться с «перестала отвечать».

В сумме css и js у меня как раз килобайт на 400 — не замечаю ничего подобного.
Комп средний, вкладки иногда зависают, но точно не на моем сайте.
UFO just landed and posted this here
Любой современный сервер/хостер автоматически использует gzip/deflate.
Все нужные ресурсы минифицированы. естественно. Это не относится к теме поста.
Доказательства того, что «дорогие операции изменения DOM и перепарсинга сорцов» отнимают больше времени, чем несколько секунд блока страницы на хилом 3G из-за похода за 304 ответом у вас есть?
Я сужу с точки зрения обывателя. пялящегося в экран своего смартфона
UFO just landed and posted this here
Браузер не может крэшиться из-за того, что в нем где-то лежит пара мегабайт текста. Браузер падает из-за кода, который в нем исполняется.

Я поздравляю вас и ваших пользователей с хорошим интернет каналам. Моя тема об обратной ситуации.
Сам лично видел краш сафари на iPad именно при получении нескольких мегабайт текста по xhr (версию устройства не скажу, не разбираюсь в них в нуль)
Даже не помню, фронт тогда смог что нить с этим сделать или нет, но было забавно очень )
Есть не то что большая — огромная разница между загрузкой мегабайтного json-а (или не json-а) по xhr, парсинга его и прочей js-програмной мутотени, и считывания 300 кб (у меня) текста из локального хранилища и вставкой его в виде текста же в html страницу для последующего парсинга стандартными средствами браузера.
А что если пользователь намеренно(или нет) изменит скрипт в вашем хранилище? Он будет запускать сайт и у него он будет падать. Или же localstorage переполниться и будут повреждены файлы? Вы проверяете на целостность данных?
Нет.
Мазохистов и психопатов я не отслеживаю и за их здоровье ответственности не несу. Это написано в Пользовательском соглашении сайта.

п.с. Попробуйте в своей винде в system32 удалить что-нибудь. И предъявите потом Гейтсу.
Для этих целей действует служба защиты программного обеспечения, которая не даст ненамеренно удалить файлы. Я вел к тому, что вы должны хотя бы проверять размер ресурсов, не говоря уже о хеш-сумме.
Уверяю вас, залезть с изменениями в localStorage и удалить что-нибудь важное из system32 — задачи одного уровня сложности. Если кто-то это захочет — он сделает. Хоть сознательно, хоть случайно. Но это тема другого хаба.
И с резиновым диском. Явно не для телефонов на андроид с 8 Гб внутренней памяти, да?
Замеры не делал и не вижу в них необходимости

Ну это просто классика!

Если я правильно помню, заголовок max-age позволяет не отправлять запрос, а использовать версию из кэша браузера.
В целом — спасибо автору, я столько полезных комментов прочёл…
Вы занимаетесь вредительством. В браузере есть кеш, и с помощью специальных HTTP заголовков (Expires, Cache-control) вы можете сказать браузеру закешировать файл «навсегда». Естественно, кеш не резиновый и файлы других сайтов будут вытеснять ваши.

Локальное хранилище — другая вещь. Оно для сохранения пользовательских данных. Данные из него не удаляются и само по себе оно не очищается.

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

Очень жаль, что такие статьи подаются под видом «туториалов». Это пример того, как делать ни в коем случае нельзя. Неграмотность автора поражает — он не только не разбирается в кешировании, но и правильно JS код написать без document.write не может.

А как быть с режимом инкогнито? Сафари (и мобильный и нет) в режиме инкогнито имеют нулевой размер локалстораджа.

Никак. Работает с обычным кэшем.
У меня нормальный интернет и с отключенным кэшем все нормально работает.

Мы в Поиске Яндекса тоже когда-то давно догадались до оптимизации путём складывания статики в LocalStorage.


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


Так вот, внедрили кэширование в LS, увидели драматическое уменьшение размера HTML, не смогли увидеть ухудшение в DevTools – обрадовались и стали использовать.


Шли годы, скорость обрастала аналитикой и прочим А/Б тестированием, и в голову пришла идея перепроверить – примерно на двухтысячном тикете про баги в фиче, где не оттестировали сценарий, когда статика берётся из кэша. Дело близилось к середине 2017, на тот момент мы уже умели получать со всей аудитории скоростной фидбек в виде метрик. У нас были:


  • все стандартные метрики Navigation Timing API
  • время отрисовки шапки (страница отдаётся чанками, и шапка приходит сильно раньше выдачи)
  • начало парсинга выдачи
  • окончание инициализации клиентского фреймворка
  • разработанные другими командами метрики поведения пользователя на странице

Провели А/Б эксперимент и увидели:


  • +12% времени скачивания HTML – логично, мы же начали передавать по сети то, что раньше не передавали;
  • -3% времени до отрисовки шапки – уже не так логично – мы же оптимизацию отключаем;
  • -1% времи до начала парсинга выдачи, и примерно на столько же – времени до инициализации клиентского фреймворка;
  • уменьшилось время до первого клика по выдаче, что косвенно говорит о том, что выдача раньше отрисовывается и становится доступной пользователю для взаимодействия; позже у нас появились более конкретные метрики на эту тему, но тогда не было.

После этого кэширование в LS благополучно отключили, а после удаления машинерии по управлению этим кэшированием и множества noscript на случай отключенного JS, увидели на потоке ещё большее ускорение.


И про ServiceWorker мне есть что рассказать. Как-то раз мы решили его попробовать, и начать не сразу с места в карьер, а с умом и пошагово – сперва снять показания с самой лёгкой вариации. Добавили sw.js, который ничего не умел, кроме как устанавливаться и навешивать пустой обработчик на событие fetch.


И снова поймали замедление. Сотни миллисекунд по всем метрикам, начиная со времени до первого байта. Так что для кэширования я бы рекомендовал SW только после серьёзного исследования – иначе есть риск сделать всё не правильно, а совсем наоборот. Если попадание в HTTP-кэш хорошее (у нас 80–90%) – будет точно медленнее.


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


В итоге сейчас мы пришли к следующей схеме:


  • весь CSS инлайновый – так быстрее рисуется; даже если стили в дисковом кэше – так быстрее; даже не пытаемся кэшировать это в браузерных хранилищах – всё равно так останется быстрее;
  • jQuery (да, у нас всё ещё jQuery) и бандл с фреймворком i-bem и общими сущностями загружаются внешними скриптами – с хитрыми префетчами для прокачки кэша;
  • скрипты асинхронны (defer) и расположены в первом чанке HTTP-ответа для WebKit, в остальных – синхронные с предзагрузкой с помощью link[rel=preload] из первого чанка (отключено для FF – там пока баги с preload); это связано с разными нюансами работы разных движков с синхронными и асинхронными скриптами;
  • вычисляем медленные запросы и отправляем на лёгкую версию – там в плане схемы со статикой всё то же самое, но функциональность сильно беднее, и вёрстка сильно хитрее свёрстана – всё с целью уменьшения размера; про это даже есть отдельный пост.

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


Есть, конечно, очевидные вещи, которые можно внедрять не размышляя: вечное кэширование всего, что можно, gzip/brotli для сжатия, скрипты не должны блокировать отрисовку, не нужно сушить кошку в микроволновке. Но в остальном – нужно измерять скорость на реальных пользователях своего сервиса, считать статистику и полагаться на эксперименты. Сервисы разные, пользователи разные – оптимизации подходят разные.

Ну, наконец-то, Yandex немного реабилитировал мой подход.
Вот оказывается кто грузил localStorage пользователей рунета все эти годы, а шишки все на меня )

По вашим замерам скорости LS и SW, думаю, все дело в интернет-канале. У вас 92 процента быстрых пользователей, на которых кэш+запрос за 304 ответом сравнимы с доступом к LS. Это по пользователям, а по запросам, полагаю, запросы от «бабуль» занимают менее 1% всех обращений. Поэтому для подавляющего числа юзеров прогнать пару пакетов по сети проще, чем, например, запускать SW фреймворк, особенно если браузер перегружен вкладками. отсюда такая статистика.

У меня 80% пользователей — «мобильщики» на EDGE и перегруженном 3G. Время отклика сети сами понимаете какое. Поэтому в моем случае экономия на двух блокирующих запросах из трех — благо.
Пост исключительно про мой случай.

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

И не важно что LocalStorage типа «не для этого» — JavaScript вообще сам по себе не для этого создавался, чем сейчас есть.

Практика — критерий, как верно отметил Andre_487
Но в остальном – нужно измерять скорость на реальных пользователях своего сервиса, считать статистику и полагаться на эксперименты.

К сожалению не так всё хорошо ) Процент медленных запросов на мобильных сетях не превосходит 10%, но достаточно близок. Это примерно и есть доля Бабули.


Учитывая наш опыт, я бы всё-таки рекомендовал попробовать вместо кэширования в LS сжатие в brotli и вечное кэширование в HTTP-кэше для преодоления проблем сети:


Cache-Control: max-age=315360000, public, immutable

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


Статистику о типе навигации можно собрать через Navigation Timing API, свойство performance.navigation.type.

Спасибо за советы.
Cache-Control: immutable уже точно решил попробовать, тем более если данные о поддержке устарели.
Sign up to leave a comment.

Articles