Как убрать кэширование css и js файлов. Основы клиентского кэширования понятными словами и на примерах

Многие думают, что по умолчанию CSS файлы, подключаемые через link или @import - не кэшируются. Вынужден вас разочаровать. Как раз именно css, вынесенные в отдельный файл кэшируются, причем очень хорошо, я бы сказал отлично. Эта информация достоверно проверено как на 6 и выше и других браузерах. Стоит отметить, что многими любимая кэшируется такие файлы совсем с дикой скоростью, так сказать получает первое место за это дело. Кстати во многом именно этому механизму Opera имеет во многих случаях существенную скорость по сравнению с другими браузерами. Но сразу оговорюсь, что именно эта «супер» кэширование в Opera с ней злую шутки при использовании технологии AJAX. В то время как другие при использовании AJAX вносят изменения чики пуки, Opera берет старое. Но это песня отдельной темы.

Кэширование CSS

НО! Бывают все-таки у некоторых горе проблемы в этом направлении. Это связанно, как правило, с неправильным настроенным Apache сервером, который выдает не совсем корректные заголовки. А при помощи заголовком можно управлять кэшированием файлов. По умолчанию, конечно же, кэш включен всегда. Но бывают случаи, когда кэшировать файлы не нужно. Для этого и уже профи начинают танцы с бубнами по поводу HTTP заголовков. Но если вы полностью читаете эту статью, то вам еще очень далеко до управления HTTP заголовками. Уверяю вас, что в ближайшее время вы с такой задачей не столкнетесь. И все же, если вы любопытны до глубины души, то вкратце расскажу как это происходит.

  1. шлет HTTP заголовок на WEB сервер - мол слышь, сладкий перец, дай мне CSS файл, а то у меня есть CSS, но последнее время изменения такое то.
  2. А сервер молвит ему в ответ, дык сладенький, не было с того момента никаких изменений, бери и пользуйся смело своим старенький CSS.
  3. Если же CSS изменился, то браузер тупо обновляет CSS у себя в кэш.

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

Сразу скажу нижний текст новичкам в WEB будет слабо понятен. В основном это полезно будет тем, кто столкнулся все-таки с задачами отключения и включения кэш.

Все эксперименты проводились на реальном, платном. У хорошего так сказать хостера, который позволяет изменять структуру HTTP заголовков, не имея параной, что его по HTTP заголовку взломают:)

Режимы браузеров

Итак, у любого браузера есть 2 режима :

1. Режим по умолчанию , возвращаемый заголовок:

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

2. Режим с включенным кешированием , возвращаемый заголовок:

Cache-Control: private, max-age=10800, pre-check=10800

Далее описываю поведение браузеров

FireFox 3.5 и выше

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

If-Modified-Since: "текущая дата" GMT If-None-Match: "свой хэш код"

То есть CSS загружается заново, только если он реально обновился.

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

GET / HTTP/1.1 Host: ххх.com If-Modified-Since: текущая дата GMT

и получает ответ:

HTTP/1.1 304 Not Modified

Internet Explorer 8 (IE8)

В-первом режиме Internet Explorer отправляет запросы If-Modified-Since & If-None-Match и для JavaScript и для css, то есть грузит JavaScript и CSS только если они реально обновились. То же самое если принудительно обновить страницу.

Во-втором режиме Internet Explorer так же отправляет запросы If-Modified-Since & If-None-Match и для JavaScript и для css. Но при этом он даже не пытается загрузить/обновить саму страницу, то есть, даже не отправляет запрос, то есть, ваши js/css обновятся, а шаблон и контент страницы - нет. Для обновления контента не помогает даже принудительное обновление страницы.

Opera 10 и старше

В-первом режиме Опере, в первом режиме, обновление js & CSS зависит от того, в какое значение выставлена опция Check images в настройках. Если опция выставлена в значение Always, то опера отправляет запросы с If-Modified-Since & If-None-Match для проверки обновления js & css. Если выставлено значение, например, 5 часов, то соответственно проверяться будет раз в 5 часов, либо по принудительному обновлению страницы.

Во-втором режиме, Опера не проверяет обновление js & CSS (не делает GET-запросов), а так же не делает GET запрос на саму страницу, то есть, ни обновление js & css, ни обновление контента мы не увидим, как в прочем и в других браузерах. А вот с принудительным обновлением у Оперы лучше. В отличие от IE & FF, Опера явно запрашивает содержимое страницы без If-Modified-Since & If-None-Match. Запросы на обновление js & CSS при принудительном обновлении идут уже с If-Modified-Since & If-None-Match.

Выводы

  1. Кеширование, если точно не представлять себе как оно работает в разных браузерах и какие последствия - достаточно опасная вещь.
  2. Кеширование можно включать только если страница обновляется редко (то есть, если на сайте нет страниц, которые обновляются в реальном времени) и даже в этом случае обязательно нужно ставить ограничение на период ограничения кеширования (например несколько часов или день)
  3. FireFox ведет себя, на мой взгляд, чуть умнее чем IE, так как даже при отключенном кешировании не проверяет постоянно обновление JavaScript, что выглядит логично, ведь JavaScript обновляется очень редко.
  4. Опера позволяет гибко управлять обновлением изображений, JavaScript и CSS с помощью настройки Check images, что есть плюс. Так же Опера ведет себя лучше чем IE & FF при включенном кешировании и принудительном обновлении, так как, напомню, Опера в этом случае полностью обновляет содержимое страницы, а IE & FF - оставят вас в счастливом неведении.

Удачи вам и прибыльных сайтов.

Подключая внешние CSS и Javascript, мы хотим снизить до минимума лишние HTTP-запросы.

Для этого.js и.css файлы отдаются с заголовками, обеспечивающими надежное кеширование.

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

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

Простое кеширование ETag

Самый простой способ кеширования статических ресурсов - использование ETag .

Достаточно включить соответствующую настройку сервера (для Apache включена по умолчанию) - и к каждому файлу в заголовках будет даваться ETag - хеш, который зависит от времени обновления, размера файла и (на inode-based файловых системах) inode.

Браузер кеширует такой файл и при последующих запросах указывет заголовок If-None-Match с ETag кешированного документа. Получив такой заголовок, сервер может ответить кодом 304 - и тогда документ будет взят из кеша.

Выглядит это так:

Первый запрос к серверу (кеш чистый) GET /misc/pack.js HTTP/1.1 Host: сайт

Вообще, браузер обычно добавляет еще пачку заголовоков типа User-Agent, Accept и т.п. Для краткости они порезаны.

Ответ сервера Сервер посылает в ответ документ c кодом 200 и ETag: HTTP/1.x 200 OK Content-Encoding: gzip Content-Type: text/javascript; charset=utf-8 Etag: "3272221997" Accept-Ranges: bytes Content-Length: 23321 Date: Fri, 02 May 2008 17:22:46 GMT Server: lighttpd Следующий запрос браузера При следующем запросе браузер добавляет If-None-Match: (кешированный ETag): GET /misc/pack.js HTTP/1.1 Host: сайт If-None-Match: "453700005" Ответ сервера Сервер смотрит - ага, документ не изменился. Значит можно выдать код 304 и не посылать документ заново. HTTP/1.x 304 Not Modified Content-Encoding: gzip Etag: "453700005" Content-Type: text/javascript; charset=utf-8 Accept-Ranges: bytes Date: Tue, 15 Apr 2008 10:17:11 GMT

Альтернативный вариант - если документ изменился, тогда сервер просто посылает 200 с новым ETag .

Аналогичным образом работает связка Last-Modified + If-Modified-Since:

  1. сервер посылает дату последней модификации в заголовке Last-Modified (вместо ETag)
  2. браузер кеширует документ, и при следующем запросе того же документа посылает дату закешированной версии в заголовке If-Modified-Since (вместо If-None-Match)
  3. сервер сверяет даты, и если документ не изменился - высылает только код 304, без содержимого.

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

Умное кеширование. Версионность

Общий подход для версионности - в двух словах:

  1. Во все скрипты добавляется версия (или дата модификации). Например, http://сайт/my.js превратится в http://сайт/my.v1.2.js
  2. Все скрипты жестко кешируются браузером
  3. При обновлении скрипта версия меняется на новую: http://сайт/my.v2.0.js
  4. Адрес изменился, поэтому браузер запросит и закеширует файл заново
  5. Старая версия 1.2 постепенно выпадет из кеша

Жесткое кеширование

Жесткое кеширование - своего рода кувалда которая полностью прибивает запросы к серверу для кешированных документов.

Для этого достаточно добавить заголовки Expires и Cache-Control: max-age.

Например, чтобы закешировать на 365 дней в PHP:

Header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT"); header("Cache-Control: max-age="+86400*365);

Или можно закешировать контент надолго, используя mod_header в Apache:

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

Большинство браузеров (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими.

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

P.S А вот Firefox кеширует адреса с вопросительными знаками..

Автоматическое преобразование имен

Разберем, как автоматически и прозрачно менять версии, не переименовывая при этом сами файлы.

Имя с версией -> Файл

Самое простое - это превратить имя с версией в оригинальное имя файла.

На уровне Apache это можно сделать mod_rewrite:

RewriteEngine on RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 [L]

Такое правило обрабатывает все css/js/gif/png/jpg-файлы, вырезая из имени версию.

Например:

/images/logo.v2.gif -> /images/logo.gif
/css/style.v1.27.css -> /css/style.css
/javascript/script.v6.js -> /javascript/script.js

Но кроме вырезания версии - надо еще добавлять заголовки жесткого кеширования к файлам. Для этого используются директивы mod_header:

Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" Header add "Cache-Control" "max-age=315360000"

А все вместе реализует вот такой апачевый конфиг:

RewriteEngine on # убирает версию, и заодно ставит переменную что файл версионный RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 # жестко кешируем версионные файлы Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE

Из-за порядка работы модуля mod_rewrite, RewriteRule нужно поставить в основной конфигурационный файл httpd.conf или в подключаемые к нему(include) файлы, но ни в коем случае не в.htaccess , иначе команды Header будут запущены первыми, до того, как установлена переменная VERSIONED_FILE .

Директивы Header могут быть где угодно, даже в.htaccess - без разницы.

Автоматическое добавление версии в имя файла на HTML-странице

Как ставить версию в имя скрипта - зависит от Вашей шаблонной системы и, вообще, способа добавлять скрипты (стили и т.п.).

Например, при использовании даты модификации в качестве версии и шаблонизатора Smarty - ссылки можно ставить так:

Функция version добавляет версию:

Function smarty_version($args){ $stat = stat($GLOBALS["config"]["site_root"].$args["src"]); $version = $stat["mtime"]; echo preg_replace("!\.(+?)$!", ".v$version.\$1", $args["src"]); }

Результат на странице:

Оптимизация

Чтобы избежать лишних вызовов stat , можно хранить массив со списком текущих версий в отдельной переменной

$versions["css"] = array("group.css" => "1.1", "other.css" => "3.0", }

В этом случае в HTML просто подставляется текущая версия из массива.

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

Применимость

Такой способ кеширования работает везде, включая Javascript, CSS, изображения, flash-ролики и т.п.

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

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

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

Как сделать кэширование в JavaScript? Об этом данная статья.

Кэширование, с помощью объекта

Метод, о котором хочу рассказать — это кэширование с помощью объекта JavaScript. Суть в том, что мы создаем хэш-массив объект, имена полей которого совпадают с именами локаций. Значения этих полей — это кэшируемые тексты.

//создаем глобальный объект
var CACHE = new Object();

Как записать в кэш что-то? Проще простого!

alert(CACHE.key);

Кроме того, нужно помнить, что объекты в JS - это по сути, ассоциативные массивы. То есть

CACHE.key == CACHE;

Вооружившись этим знанием, приступаем к построению нашего тестового приложения.

Структура html

Для начала, определимся с html. У меня получилось что-то такое:



jQurey cache ajax




1 |
2 |
3


Как видно, я решил оставил задел, чтобы сделать красивую навигацию по методу AlexSol-а. Делать я ее не буду, но прикрутить ее потом будет делом двух минут. Пока что, давайте узнаем, что делает функция getData?

var CACHE = new Object(); //создаем объект кэша

function getData(id_loc, url2load) {
if (CACHE) { //если в кэше еще нет нужных данных
//загружаем требуемый файл и вызываем функцию cache_n_go,
//которой передаем содержимое файла и id нажатой ссылки
$.get(url2load, function(data) {cache_n_go(data, id_loc)});
}
//если в кэше уже есть нужные нам данные
else {
//получаем данные из кэша
showRes(CACHE + "(из кэша)");
}
}

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

Теперь посмотрим что творится в функции cahce_n_go.

function cache_n_go(text, id_loc) {
CACHE = text;
showRes(text);
}

Ничего сверхъестественного в ней нет. Она кэширует полученый текст и отправляет его функции showRes, чтобы та его показала. Кстати, вот и она:

function showRes(text) {
var res = $("#res");
res.empty();
res.html(text);
}

Как видно, она принимает текст, опустошает блок для вывода контента, и выводит в него то, что ей передали. Вот и все.

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

К счастью, мы можем воспользоваться возможностями JavaScript"a и написать функцию cache , способную кэшировать результаты работы других методов, чтобы, в случае надобности, повторно не вызывать эти самые методы:

Function cache(key, value) { if (typeof value == "undefined") { return cache; } cache = value; }

Теперь на примерах рассмотрим как будет вести себя написанная выше функция cache.

Пример 1. Кэширование querySelector

// _io_q - обертка под querySelector, сохраняющая данные в кэш _io_q = function(selector) { if (!cache(selector)) { cache(selector, document.querySelector(selector)); } return cache(selector); }

Теперь посмотрим на скорость работы _io_q и querySelector:

Console.time("regular querySelector"); for (var i = 0; i < 1000000; i++) { document.querySelector("h1"); } console.timeEnd("regular querySelector"); // regular querySelector: 100.6123046875ms console.time("cached _io_q"); for (var i = 0; i < 1000000; i++) { _io_q("h1"); } console.timeEnd("cached _io_q"); // cached _io_q: 5.77392578125ms

Пример 2. Кэширование запросов с сервера

Напишем функцию, которая отправляет очень тяжелый запрос на сервер:

Function longRequestToServer(params) { var key = params.endpoint + "_" + params.value; if (!cache(key)) { var result = 0; for (var i = 0; i < 999999999; i++) { result++; } cache(key, result); } return cache(key); }

Посмотрим как будет вести себя функция:

Console.time("first run"); longRequestToServer({ endpoint: "/loadExample", value: 10}); console.timeEnd("first run"); // first run: 1012.068115234375ms console.time("second run"); longRequestToServer({ endpoint: "/loadExample", value: 10}); console.timeEnd("second run"); // second run: 1.31884765625ms console.time("other request"); longRequestToServer({ endpoint: "/loadSomeOtherData", value: 15 }); console.timeEnd("other request"); // other request: 1033.783203125ms

TL;DR

Используя возможности JavaScript"a можно эффективно кэшировать данные, выигрывая в производительности. Но будьте аккуратны, ведь выигрывая в производительности можно потерять на ресурсах и по неосторожности добиться утечки памяти.

Подключая внешние CSS и Javascript, мы хотим снизить до минимума лишние HTTP-запросы.

Для этого.js и.css файлы отдаются с заголовками, обеспечивающими надежное кеширование.

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

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

Простое кеширование ETag

Самый простой способ кеширования статических ресурсов - использование ETag .

Достаточно включить соответствующую настройку сервера (для Apache включена по умолчанию) - и к каждому файлу в заголовках будет даваться ETag - хеш, который зависит от времени обновления, размера файла и (на inode-based файловых системах) inode.

Браузер кеширует такой файл и при последующих запросах указывет заголовок If-None-Match с ETag кешированного документа. Получив такой заголовок, сервер может ответить кодом 304 - и тогда документ будет взят из кеша.

Выглядит это так:

Первый запрос к серверу (кеш чистый) GET /misc/pack.js HTTP/1.1 Host: сайт

Вообще, браузер обычно добавляет еще пачку заголовоков типа User-Agent, Accept и т.п. Для краткости они порезаны.

Ответ сервера Сервер посылает в ответ документ c кодом 200 и ETag: HTTP/1.x 200 OK Content-Encoding: gzip Content-Type: text/javascript; charset=utf-8 Etag: "3272221997" Accept-Ranges: bytes Content-Length: 23321 Date: Fri, 02 May 2008 17:22:46 GMT Server: lighttpd Следующий запрос браузера При следующем запросе браузер добавляет If-None-Match: (кешированный ETag): GET /misc/pack.js HTTP/1.1 Host: сайт If-None-Match: "453700005" Ответ сервера Сервер смотрит - ага, документ не изменился. Значит можно выдать код 304 и не посылать документ заново. HTTP/1.x 304 Not Modified Content-Encoding: gzip Etag: "453700005" Content-Type: text/javascript; charset=utf-8 Accept-Ranges: bytes Date: Tue, 15 Apr 2008 10:17:11 GMT

Альтернативный вариант - если документ изменился, тогда сервер просто посылает 200 с новым ETag .

Аналогичным образом работает связка Last-Modified + If-Modified-Since:

  1. сервер посылает дату последней модификации в заголовке Last-Modified (вместо ETag)
  2. браузер кеширует документ, и при следующем запросе того же документа посылает дату закешированной версии в заголовке If-Modified-Since (вместо If-None-Match)
  3. сервер сверяет даты, и если документ не изменился - высылает только код 304, без содержимого.

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

Умное кеширование. Версионность

Общий подход для версионности - в двух словах:

  1. Во все скрипты добавляется версия (или дата модификации). Например, http://сайт/my.js превратится в http://сайт/my.v1.2.js
  2. Все скрипты жестко кешируются браузером
  3. При обновлении скрипта версия меняется на новую: http://сайт/my.v2.0.js
  4. Адрес изменился, поэтому браузер запросит и закеширует файл заново
  5. Старая версия 1.2 постепенно выпадет из кеша

Жесткое кеширование

Жесткое кеширование - своего рода кувалда которая полностью прибивает запросы к серверу для кешированных документов.

Для этого достаточно добавить заголовки Expires и Cache-Control: max-age.

Например, чтобы закешировать на 365 дней в PHP:

Header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT"); header("Cache-Control: max-age="+86400*365);

Или можно закешировать контент надолго, используя mod_header в Apache:

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

Большинство браузеров (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими.

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

P.S А вот Firefox кеширует адреса с вопросительными знаками..

Автоматическое преобразование имен

Разберем, как автоматически и прозрачно менять версии, не переименовывая при этом сами файлы.

Имя с версией -> Файл

Самое простое - это превратить имя с версией в оригинальное имя файла.

На уровне Apache это можно сделать mod_rewrite:

RewriteEngine on RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 [L]

Такое правило обрабатывает все css/js/gif/png/jpg-файлы, вырезая из имени версию.

Например:

/images/logo.v2.gif -> /images/logo.gif
/css/style.v1.27.css -> /css/style.css
/javascript/script.v6.js -> /javascript/script.js

Но кроме вырезания версии - надо еще добавлять заголовки жесткого кеширования к файлам. Для этого используются директивы mod_header:

Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" Header add "Cache-Control" "max-age=315360000"

А все вместе реализует вот такой апачевый конфиг:

RewriteEngine on # убирает версию, и заодно ставит переменную что файл версионный RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 # жестко кешируем версионные файлы Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE

Из-за порядка работы модуля mod_rewrite, RewriteRule нужно поставить в основной конфигурационный файл httpd.conf или в подключаемые к нему(include) файлы, но ни в коем случае не в.htaccess , иначе команды Header будут запущены первыми, до того, как установлена переменная VERSIONED_FILE .

Директивы Header могут быть где угодно, даже в.htaccess - без разницы.

Автоматическое добавление версии в имя файла на HTML-странице

Как ставить версию в имя скрипта - зависит от Вашей шаблонной системы и, вообще, способа добавлять скрипты (стили и т.п.).

Например, при использовании даты модификации в качестве версии и шаблонизатора Smarty - ссылки можно ставить так:

Функция version добавляет версию:

Function smarty_version($args){ $stat = stat($GLOBALS["config"]["site_root"].$args["src"]); $version = $stat["mtime"]; echo preg_replace("!\.(+?)$!", ".v$version.\$1", $args["src"]); }

Результат на странице:

Оптимизация

Чтобы избежать лишних вызовов stat , можно хранить массив со списком текущих версий в отдельной переменной

$versions["css"] = array("group.css" => "1.1", "other.css" => "3.0", }

В этом случае в HTML просто подставляется текущая версия из массива.

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

Применимость

Такой способ кеширования работает везде, включая Javascript, CSS, изображения, flash-ролики и т.п.

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