Прочтите интерфейсный кеш в одной статье

браузер

Все знают, что тайник называется по-английски.cache. Но я обнаружил интересное явление: это слово у разных людей произносится по-разному. Чтобы полностью понять кеш, мы должны начать с произношения, чтобы мы могли отразить наше собственное совершенствование (би) совершенствование (гэ) при взаимодействии (чжуан) с другими коллегами (такими как ПМ).

Дружеское напоминание: статья немного длинная, вам, возможно, придется читать ее партиями, а в антракте можно выпить кофе или пива.

кеш как произносится

В зарубежных ИТ-кругах и в большинстве зарубежных видеороликов произношение «кэш»/kæʃ/(такой жеcash), что также является общепризнанным произношением. Но я обнаружил, что в китайских ИТ-кругах все еще есть немало программистов (таких как я...), которые читают как/kætʃ/(такой жеcatch). Хотя это и не совсем правильно, но не мешает взаимному общению. (Но ради чистоты его надо двигать в правильном направлении)

Кроме того, существуют некоторые нишевые методы чтения, такие как/keɪʃ/(такой жеkaysh),четное/kæʃeɪ/(как французское произношение, с ударением сзади). Поскольку они слишком малы, они могут создавать коммуникативные барьеры, и, по оценкам, их можно беспрепятственно использовать только в определенных случаях или в определенных кругах.

Кэш переднего плана/кэш внутреннего интерфейса

После некоторой бесполезности давайте сначала перейдем к части определения: что такое внешний кеш? Что противоположно бэкенд-кэшированию?

request&response

Базовый сетевой запрос состоит из трех шагов: запрос, обработка и ответ.

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

А внешнее кеширование можно сделать за оставшиеся два шага: «запрос» и «ответ». На этапе «запрос» браузер также может напрямую использовать ресурс, сохраняя результат, что напрямую устраняет необходимость отправки запроса; в то время как этап «ответ» требует сотрудничества браузера и сервера для сокращения времени передачи. путем уменьшения содержания ответа. Они обсуждаются ниже.

Эта статья в основном включает

  • Сортировка по расположению кеша (кэш памяти, дисковый кеш, Service Worker и т. д.)
  • Классификация по стратегии отказа (Cache-Control, ETagЖдать)
  • Несколько примеров, помогающих понять принцип
  • Режим кэшированного приложения

Сортировать по расположению кеша

Большинство статей, которые я видел, в которых обсуждается кеширование, начинаются непосредственно с поля кеша в заголовке HTTP, например.Cache-Control, ETag, max-ageЖдать. Но иногда я слышу, как другие обсуждают кеш памяти, кеш диска и т. д.Как связаны эти две системы классификации? Есть кроссовер?(Лично я считаю, что это самая большая ценность этой статьи, потому что я сам запутался с обеими системами классификации, прежде чем писать это)

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

Мы можем увидеть окончательный метод обработки запроса в инструментах разработчика Chrome, столбец Network -> Size: если это размер (сколько K, сколько M и т. д.), это означает, что это сетевой запрос, иначе он будет быть в спискеfrom memory cache, from disk cacheа такжеfrom ServiceWorker.

Их приоритет: (искать сверху вниз, вернуться, если найдено; продолжить, если не найдено)

  1. Service Worker
  2. Memory Cache
  3. Disk Cache
  4. сетевой запрос

memory cache

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

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

только что упомянул,почти все запрашиваемые ресурсыВсе могут попасть в кеш памяти.Здесь есть две основные части:

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

    Учащиеся, знакомые с процессом обработки браузером, должны понимать, что когда браузер открывает веб-страницу, он сначала запрашивает HTML, а затем анализирует его. Позже, если браузер найдет js, css и другие ресурсы, которые необходимо проанализировать и выполнить, он будет использовать ресурсы ЦП для их анализа и выполнения. В старые времена (примерно до 2007 года) "запрос js/css - выполнение синтаксического анализа - запрос следующего js/css - выполнение синтаксического анализа следующего js/css" такой "последовательный" режим работы выполнялся перед каждым открытием записи страницы. Очевидно, что при разборе и выполнении сетевой запрос простаивает, поэтому есть место для игры: можем ли мы разобрать и выполнить js/css при запросе следующего (или следующего пакета) ресурса?

    Это то, что делает прелоадер. Однако официального стандарта для предварительных загрузчиков не существует, поэтому каждый браузер обрабатывает его немного по-своему. Например, некоторые браузеры также загружают css в@importсодержание или<video>изposterЖдать.

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

  2. preload (хотя он выглядит на две буквы меньше, чем предыдущий preloader). На самом деле, все должны быть более знакомы с этим, например,<link rel="preload">. Эти явно указанные предварительно загруженные ресурсы также помещаются в кеш памяти.

Механизм кэширования памяти гарантирует, что при наличии на странице двух идентичных запросов (например, двухsrcидентичный<img>,дваhrefидентичный<link>) на самом деле запрашиваются не более одного раза, чтобы избежать потерь.

Однако при сопоставлении кеша, помимо сопоставления точно таких же URL, он также будет сравнивать их типы, правила доменных имен в CORS и т. д. Поэтому ресурс, закэшированный как тип скрипта, нельзя использовать в запросе типа изображения, даже если ониsrcравный.

При извлечении кэшированного контента из кэша памяти браузеры игнорируют, например.max-age=0, no-cacheи другие конфигурации заголовка. Например, есть несколько одинаковыхsrc, даже если для них установлено значение «не кэшировать», они все равно будут считываться из кэша памяти. Это связано с тем, что кэш памяти используется только в течение короткого периода времени, и в большинстве случаев жизненный цикл состоит только из одного просмотра. а такжеmax-age=0Обычно это семантически интерпретируется как «не использовать его в следующий раз при просмотре», поэтому он не конфликтует с кешем памяти.

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

disk cache

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

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

Мы подробно обсудим поля кэширования в заголовках HTTP позже.

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

Service Worker

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

Но появление Service Worker дало нам еще один, более гибкий и прямой метод работы. По-прежнему взяв в качестве примера ввод/вывод средств, теперь мы можем обойти банковского служащего, пройти к хранилищу (конечно, отдельному небольшому хранилищу, отличному от вышеупомянутого хранилища) и самостоятельно положить или вывести деньги. Таким образом, мы можем выбирать, какие деньги класть (какие файлы кешировать), какие деньги брать при каких обстоятельствах (правила сопоставления маршрутизации) и какие деньги брать (сопоставлять и возвращать кеши).Конечно, в действительности банки не предоставляют нам такие услуги..

Кэш, с которым может работать Service Worker, отличается от кеша памяти или дискового кеша внутри браузера. Мы можем найти это отдельное «маленькое хранилище» в Chrome F12, Application -> Cache Storage. За исключением другого местоположения, этот кеш является постоянным, то есть, когда вы закрываете вкладку или браузер, он все равно будет там в следующий раз, когда вы его откроете (в то время как кеш памяти нет). Существует две ситуации, которые могут привести к очистке ресурсов в этом кэше: Вызов API вручную.cache.delete(resource)Или емкость превышает лимит и полностью опустошается браузером.

Если сервис-воркеру не удается попасть в кеш, он обычно используетсяfetch()Метод продолжает извлекать ресурс. В это время браузер переходит к кешу памяти или кешу диска для следующего поиска кеша. Примечание. После обслуживания работникfetch()Ресурс, полученный методом, будет помечен какfrom ServiceWorker. Эта ситуация показана в третьем следующем примере.

сеть запроса

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

  1. Определите, следует ли сохранять в хранилище кэша (дополнительное расположение кэша) в соответствии с обработчиком в Service Worker.
  2. Согласно соответствующим полям HTTP-заголовка (Cache-control, Pragmaи т. д.), чтобы решить, следует ли хранить в дисковом кеше
  3. кэш памяти экономит ресурсцитаты, для следующего использования.

Классификация по стратегии отказа

Кэш памяти — это собственная оптимизация браузера для увеличения скорости чтения кеша, она не контролируется разработчиком и не привязана к заголовку протокола HTTP, это черный ящик. Service Worker — это дополнительный скрипт, написанный разработчиком, и расположение кеша не зависит от него, он также появился поздно и не получил широкого распространения. То, с чем мы больше всего знакомы, на самом деле является дисковым кешем, также называемым кешем HTTP (потому что, в отличие от кеша памяти, он соответствует полям в заголовке протокола HTTP). Обычно говорят принудительный кеш, контрастный кеш иCache-Controlд., также попадают в эту категорию.

Принудительное кэширование (также называемое сильным кэшированием)

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

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

Поля, которые могут привести к принудительному кэшированию:Cache-controlа такжеExpires.

Expires

Это поле HTTP 1.0, указывающее время истечения кэша, которое является абсолютным временем (текущее время + время кэширования), например

Expires: Thu, 10 Nov 2017 08:45:11 GMT

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

Тем не менее, это поле имеет два недостатка при установке:

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

  2. Писать слишком сложно. Несколько пробелов или меньшее количество букв в строке, представляющей время, приведут к недопустимым атрибутам и неверным настройкам.

Cache-control

После того, как стали известны недостатки Expires, в HTTP/1.1 добавлено поле Cache-control, в котором указывается максимально допустимое время кеширования ресурса, в течение которого клиенту не нужно отправлять запрос на сервер.

Разница между ними в том, что первое — это абсолютное время, а второе — относительное время. следующим образом:

Cache-control: max-age=2592000

Ниже перечислены некоторыеCache-controlПолный список обычно используемых значений поля можно просмотреть :(MDN)

  • max-age: максимально допустимое время, в приведенном выше примере мы можем видеть
  • must-revalidate: если большеmax-ageвремя браузер должен отправить запрос на сервер, чтобы убедиться, что ресурс все еще действителен.
  • no-cache: хотя это буквально означает «не кэшировать», на самом деле это требует, чтобы клиент кэшировал содержимое, но использование содержимого определяется последующими сравнениями.
  • no-store: Настоящее "не кешировать". Весь контент не кэшируется, включая приведение и сравнение.
  • public: Весь контент может быть кэширован (как клиенты, так и прокси, такие как CDN).
  • private:所有的内容只有客户端才可以缓存,代理服务器不能缓存。 По умолчанию.

Эти значения могут быть смешаны, например.Cache-control:public, max-age=2592000. При смешивании их приоритеты следующие: (изображение изDevelopers.Google.com/Web/Женщины большие…)

cache-control

Вот вопрос:max-age=0а такжеno-cacheЭто эквивалентно? В буквальном смысле норма,max-ageСрок действиядолжен (ДОЛЖЕН)повторная аутентификация, в то время какno-cacheдаДОЛЖЕНПодтвердить. Но реальная ситуация зависит от реализации браузера, и в большинстве случаев поведение обоих остается одинаковым. (еслиmax-age=0, must-revalidateкакno-cacheэквивалент)

Кстати, до HTTP/1.1, если вы хотели использоватьno-cache, обычно с использованиемPragmaполя, такие какPragma: no-cache(Это тожеPragmaуникальное значение поля). Однако это поле является лишь обычной реализацией браузеров, точной спецификации нет, поэтому ему не хватает надежности. Оно должно отображаться только как поле совместимости, которое малопригодно в текущей сетевой среде.

Подводя итог, начиная с HTTP/1.1,ExpiresпостепенноCache-controlзаменять.Cache-controlЭто относительное время.Даже если время клиента изменится, относительное время не изменится, так что может поддерживаться согласованность времени между сервером и клиентом. а такжеCache-controlКонфигурируемость более мощная.

Cache-control имеет приоритет над Expires, чтобы быть совместимым с HTTP/1.0 и HTTP/1.1, мы установим оба поля в реальном проекте.

Кэш сравнения (также называемый кэшем согласования)

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

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

Сравнение количества кешированных запросов согласуется с отсутствием кеша, но если это 304, он возвращает только код состояния, а фактического содержимого файла нет, поэтомуЭкономия объема тела отклика является точкой его оптимизации.. Его оптимизации охватывают последний из трех этапов запроса данных, упомянутых в начале статьи: «ответ». Сократите время передачи по сети, уменьшив размер тела ответа. Так что это небольшое улучшение по сравнению с принудительным кэшированием, но это лучше, чем отсутствие кэширования.

Кэширование сравнения можно использовать в сочетании с принудительным кэшированием в качестве запасного варианта после принудительного аннулирования кэша. Они часто появляются вместе в реальных проектах.

Контрастный кэш имеет 2 групповых полей (не два):

Last-Modified & If-Modified-Since

  1. сервер черезLast-ModifiedПоле сообщает клиенту, когда ресурс был изменен в последний раз, например.

    Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
    
  2. Браузер записывает это значение в базу данных кеша вместе с содержимым.

  3. В следующий раз, когда запрашивается тот же ресурс, браузер ищет кеш с неопределенным сроком действия в своем собственном кеше. Поэтому в заголовке запроса последнийLast-ModifiedЗначение записывается в заголовок запросаIf-Modified-Sinceполе

  4. Сервер будетIf-Modified-Sinceзначение сLast-Modifiedполя для сравнения. Если они равны, это означает, что он не был изменен и отвечает кодом 304; в противном случае это означает, что он был изменен, и он отвечает кодом состояния 200 и возвращает данные.

Но у него все же есть некоторые недостатки:

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

Etag & If-None-Match

В целях решения вышеуказанных проблем появился новый набор полейEtagа такжеIf-None-Match

EtagСохраняется специальный идентификатор файла (обычно генерируемый хэшем), а сервер хранит идентификатор файла.Etagполе. последующий процесс иLast-Modifiedпоследовательный, простоLast-ModifiedПоле и время обновления, которое оно представляет, изменены наEtagполе и хэш файла, который он представляет, поместитеIf-Modified-SinceсталIf-None-Match. Тот же сервер сравнивает возврат 304 обращений, возврат без новых ресурсов и 200.

Etag имеет приоритет над Last-Modified

Сводка кэша

Когда браузер хочет запросить ресурс

  1. Звонок сервисному работникуfetchРеакция на инцидент
  2. Просмотр кеша памяти
  3. Проверьте кеш диска. Вот разбивка:
    1. Если есть принудительный кеш и он не признан недействительным, используется принудительный кеш и сервер не запрашивается. Коды состояния в это время все 200
    2. Если есть принудительный кеш, но он был признан недействительным, используйте кеш сравнения, чтобы определить 304 или 200 после сравнения.
  4. Отправьте сетевой запрос и дождитесь ответа сети
  5. Сохраните содержимое ответа в кеше диска (если конфигурация HTTP-заголовка может его сохранить).
  6. поместите содержание ответацитатыВ кеш памяти (игнорирование настроить информацию заголовка HTTP)
  7. Сохраните содержимое ответа в хранилище кэша сервис-воркера (если скрипт сервис-воркера вызываетcache.put())

некоторые случаи

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

1. memory cache & disk cache

Мы пишем простойindex.html, а затем обратитесь к 3 ресурсам, которыеindex.js, index.cssа такжеmashroom.jpg.

Ставим все три ресурса наCache-control: max-age=86400, что означает форсировать кеширование на 24 часа. Все скриншоты ниже используют режим инкогнито Chrome.

  1. первый запрос

    First request

    Все сетевые запросы берутся без происшествий, т.к. кеша пока нет.

  2. ЗАПРОС (F5)

    Refresh

    Для второго запроса все три запроса поступают из кеша памяти. Поскольку мы не закрыли TAB, браузер добавил кешированное приложение в кеш памяти. (Требуется 0 мс, то есть в пределах 1 мс)

  3. Закройте ВКЛАДКУ, откройте новую ВКЛАДКУ и повторите запрос

    Reopen

    Поскольку TAB закрыт, кэш памяти также очищается. Но дисковый кеш является постоянным, поэтому все ресурсы поступают из дискового кеша. (занимает около 3 мс, потому что файл немного мал)

    И сравнивая 2 и 3, очевидно, что кеш памяти все же намного быстрее, чем кеш диска.

2. no-cache & no-store

мы вindex.htmlВнутри есть некоторый код, который выполняет две цели:

  • Каждый ресурс (синхронно) запрашивается дважды
  • Добавить скрипт для асинхронного запроса изображений
<!-- 把3种资源都改成请求两次 -->
<link rel="stylesheet" href="/static/index.css">
<link rel="stylesheet" href="/static/index.css">
<script src="/static/index.js"></script>
<script src="/static/index.js"></script>
<img src="/static/mashroom.jpg">
<img src="/static/mashroom.jpg">

<!-- 异步请求图片 -->
<script>
    setTimeout(function () {
        let img = document.createElement('img')
        img.src = '/static/mashroom.jpg'
        document.body.appendChild(img)
    }, 1000)
</script>
  1. Когда сервер отвечает наCache-Control: no-cache, мы обнаружили, что после открытия страницы все три ресурса запрашивались только1Второсортный.

    只请求一次

    只请求一次

    Это иллюстрирует две проблемы:

    • Что касается синхронных запросов, браузер автоматически сохраняет ресурсы текущего HTML-кода в кеш-память, чтобы те жеsrcИзображение будет автоматически считано из кеша (но не будет отображаться в Сети)

    • Что касается асинхронных запросов, браузер также напрямую считывает кеш и возвращает без отправки запроса. Но опять же он не появится в сети.

    В целом, как указано выше,no-cacheСЕМАНТИЧНО, это означает, что следующий запрос не напрямую использует кэш и необходимо сравнить, и не ограничивает этот запрос. Следовательно, браузер может безопасно использовать кэш при обработке текущей страницы.

  2. При настройке ответа сервера наCache-Control: no-store, ситуация меняется и запрашиваются все три ресурса2Второсортный. А картинка из-за того, что есть еще один асинхронный запрос, итого3Второсортный. (красный прямоугольник — это асинхронный запрос)

    请求多次

    请求多次

    Это также гласит:

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

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

3. Service Worker & memory (disk) cache

Пробуем добавить в него Service Worker. мы пишемserviceWorker.js, и напишите следующее: (в основном предварительно кэшируйте 3 ресурса, сопоставьте кэш и вернитесь, когда будет сделан фактический запрос)

// serviceWorker.js
self.addEventListener('install', e => {
  // 当确定要访问某些资源时,提前请求并添加到缓存中。
  // 这个模式叫做“预缓存”
  e.waitUntil(
    caches.open('service-worker-test-precache').then(cache => {
      return cache.addAll(['/static/index.js', '/static/index.css', '/static/mashroom.jpg'])
    })
  )
})

self.addEventListener('fetch', e => {
  // 缓存中能找到就返回,找不到就网络请求,之后再写入缓存并返回。
  // 这个称为 CacheFirst 的缓存策略。
  return e.respondWith(
    caches.open('service-worker-test-precache').then(cache => {
      return cache.match(e.request).then(matchedResponse => {
        return matchedResponse || fetch(e.request).then(fetchedResponse => {
          cache.put(e.request, fetchedResponse.clone())
          return fetchedResponse
        })
      })
    })
  )
})

Код для регистрации ПО здесь повторяться не будет. Кроме того, мы также устанавливаем серверCache-Control: max-age=86400для включения дискового кэша. Наша цель состоит в том, чтобы увидеть приоритет двух.

  1. Когда мы заходим в первый раз, мы видим, что браузер (точнее, Service Worker) делает 3 дополнительных запроса в дополнение к обычным запросам. Это из предварительно кэшированного кода.

    预缓存

  2. При втором посещении (независимо от закрытия TAB и повторного открытия или непосредственного нажатия F5 для обновления) можно увидеть, что все запросы отмечены какfrom SerciceWorker.

    缓存命中

    from ServiceWorkerЭто означает только то, что запрос прошел Service Worker.Что касается того, попал ли он в кеш или продолжилfetch()Невозможно узнать метод, просто взглянув на эту запись. Следовательно, мы должны смотреть на последующие записи сети. Поскольку после этого дополнительных запросов нет, определяется попадание в кеш.

    Service Worker 服务器

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

  3. Если измененоserviceWorker.jsизfetchКод прослушивателя событий изменен на следующий:

    // 这个也叫做 NetworkOnly 的缓存策略。
    self.addEventListener('fetch', e => {
      return e.respondWith(fetch(e.request))
    })
    

    Можно обнаружить, что эффект от последующих посещений и до модификацииточно так же. (т.е. сеть помечена только какfrom ServiceWorkerнесколько запросов, а сервер не печатает логи доступа к 3 ресурсам)

    Очевидно, что уровень сервис-воркеров не читает собственный кеш, а напрямую используетfetch()обратиться с просьбой.所以此时其实是Cache-Control: max-age=86400Настройка , то есть кеш памяти/диска. Но конкретная память или диск известны только самому браузеру, потому что он нам явно не говорит. (Личное предположение - это память, потому что независимо от того, занимает ли она 0 мс или никогда не закрывает TAB, это больше похоже на кеш памяти)

поведение браузера

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

  • Откройте веб-страницу и введите адрес в адресную строку: Проверьте, есть ли совпадение в кеше диска. Используйте, если доступно; отправьте сетевой запрос, если нет.
  • Обычный сброс (F5): поскольку вкладка не закрыта, кеш памяти доступен и будет использоваться первым (если он совпадает). Второй — дисковый кеш.
  • Принудительное обновление (Ctrl + F5): Браузер не использует кеш, поэтому заголовок запроса отправляется сCache-control: no-cache(Для совместимости также принеситеPragma: no-cache). Сервер возвращает 200 и последний контент напрямую.

Режим кэшированного приложения

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

Шаблон 1: Ресурсы, которые меняются нечасто

Cache-Control: max-age=31536000

Обычно, имея дело с такими ресурсными ресурсами, дайте имCache-Controlнастроить большойmax-age=31536000(один год), чтобы последующие запросы того же URL-адреса браузером попадали в принудительный кеш. Чтобы решить проблему обновления, необходимо добавить динамические символы, такие как хэш и номер версии, к имени файла (или пути), а затем изменить динамические символы для достижения цели изменения ссылочного URL-адреса, тем самым сделав недействительным предыдущий обязательный кеш (на самом деле он сразу не стал неэффективным, просто больше не использовался).

Библиотеки классов, доступные в Интернете (например, jquery-3.3.1.min.js, lodash.min.js и т. д.), используют этот шаблон. Если конфигурация также увеличиваетсяpublicЕсли это так, CDN также можно кэшировать, и эффект выдающийся.

Вариантом этого шаблона является добавление параметров после URL-адреса реферера (например,?v=xxxили?_=xxx), так что вам не нужно включать динамические параметры в имя файла или путь, как это нравится некоторым перфекционистам. При каждой сборке проекта обновление дополнительных параметров (например, установка их на текущее время сборки) гарантирует, что браузер всегда сможет запрашивать самое последнее содержимое после каждой сборки.

обращать внимание:При работе с сервисными работниками относитесьsw-register.js(зарегистрированный сервисный работник) иserviceWorker.js(Сам Service Worker) требует особой осторожности. Если эти два файла также используют этот режим, вы должны рассмотреть возможные будущие обновления и контрмеры.

Шаблон 2: часто меняющиеся ресурсы

Cache-Control: no-cache

Ресурсы здесь — это не только статические ресурсы, но также могут быть веб-ресурсы, такие как сообщения в блогах. Характерной чертой этого типа ресурса является то, что URL-адрес не может измениться, но содержимое может (и часто) меняться. мы можем установитьCache-Control: no-cacheЧтобы заставить браузер запрашивать у сервера подтверждение того, что ресурс действителен для каждого запроса.

Поскольку упоминается проверка, она должна бытьETagилиLast-ModifiedИграть. Эти поля будут посвящены общей библиотеке классов, предназначенной для статического ресурса (например,koa-static) автоматически добавляется без участия разработчика.

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

Паттерн 3: очень опасное сочетание шаблонов 1 и 2 (контрпример)

Cache-Control: max-age=600, must-revalidate

Не знаю, вдохновлялись ли какие-либо разработчики режимами 1 и 2: в режиме 2 установитеno-cache, эквивалентноmax-age=0, must-revalidate. У меня своевременность приложений не такая сильная, но я не хочу делать слишком длинный принудительный кеш, можно настроить напримерmax-age=600, must-revalidateКак насчет этой компромиссной настройки?

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

Например: когда у нас есть 3 ресурса:index.html, index.js, index.css. Затем мы выполняем конфигурацию этих трех человек, предполагая, что во время визита,index.jsбыл очищен кеш и не существует, ноindex.html, index.cssвсе еще существует в кэше. В это время браузер запросит новыйindex.js, затем в паре со старымindex.html, index.cssотображается пользователю. Риск очевиден: совмещение разных версий ресурса, и ошибка — очень вероятный исход.

В дополнение к проблемам, вызванным автоматической очисткой, проблемы также могут быть вызваны разным временем запроса для разных ресурсов. Например, запрос страницы АA.jsа такжеall.css, а страница BB.jsа такжеall.css. Если мы будем посещать страницы в порядке A -> B, это неизбежно приведет кall.cssкэшируется раньше, чемB.js. Тогда есть та же скрытая опасность несоответствия версии ресурса при доступе к странице B в будущем.


Друг-разработчик (wd2010) задал очень хороший вопрос в области комментариев Zhihu:

Если я не использую must-revalidate, а просто Cache-Control: max-age=600, не будет ли выполняться механизм автоматической очистки кеша браузера? Если выполняется механизм автоматической очистки кеша браузера, то последующий index.js очищается и ситуация та же!

Этот вопрос включает в себя несколько небольших моментов, которые я добавлю:

  1. В чем разница между 'max-age=600' и 'max-age=600, must-revalidate'?

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

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

  2. Разве 'max-age=600' тоже не вызовет проблем?

    да. Возникновение проблемы не имеет никакого отношения к тому, указано ли в списке «must-revalidate», проблема несоответствия версий JS CSS и других файлов все равно будет. Поэтому, когда на обычном веб-сайте необходимо использовать разные JS CSS-файлы для разных страниц, если вы хотите использовать max-age для надежного кэширования, не устанавливайте слишком короткое время.

  3. Где можно использовать такой более короткий максимальный возраст?

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

    1. Весь сайт использует одни и те же JS и CSS, то есть объединенные файлы. Это больше подходит для небольших сайтов, иначе может быть слишком избыточно и влиять на производительность. (Однако его все еще можно очистить из-за собственной стратегии очистки браузера, и все еще есть скрытые опасности)

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


постскриптум

Эта статья действительно немного длинная, но она охватила большую часть внешнего кэширования, включая кэширование в протоколе HTTP, Service Worker и некоторые оптимизации для браузера Chrome (Memory Cache). Я надеюсь, что разработчики хорошо используют кэширование, потому что часто о нем проще всего думать, и это стратегия оптимизации производительности, которая улучшает больше всего.

Справочная статья

A Tale of Four Caches(Но в этой статье приоритет Service Worker ставится между кешем памяти и кешем диска, что не соответствует эффекту моего эксперимента. Я подозреваю, что это может быть изменение политики Chrome через 2 года?)

Caching best practices & max-age gotchas