Как заставить браузер перезагрузить кэшированные файлы CSS / JS?

Я заметил, что некоторые браузеры (в частности, Firefox и Opera) очень усердно используют кэшированные копии файлов .css и .js даже между сеансами браузера. Это приводит к проблеме при обновлении одного из этих файлов, но браузер пользователя продолжает использовать кэшированную копию.

Вопрос заключается в следующем: каков самый элегантный способ заставить браузер пользователя перезагрузить файл после его изменения?

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

Обновить :

После некоторого обсуждения здесь я нашел предложение Джона Милликина и da5id полезным. Оказывается, есть термин для этого: автоматическое управление версиями .

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

Еще одна идея, предложенная SCdF , заключается в добавлении в файл фиктивной строки запроса. (Некоторый код Python для автоматического использования метки времени в качестве фиктивной строки запроса был представлен пи .). Тем не менее, существует некоторое обсуждение относительно того, будет ли браузер кэшировать файл со строкой запроса. (Помните, мы хотим, чтобы браузер кэшировал файл и использовал его при будущих посещениях. Мы хотим, чтобы он снова извлекал файл только после его изменения.)

Поскольку не ясно, что происходит с фиктивной строкой запроса, я не принимаю этот ответ.

23.09.2008 03:07:13
У меня есть это в моей .htaccess, и никогда никаких проблем с кэшированных файлов: ExpiresActive On ExpiresDefault "modification".
Frank Conijn 15.05.2014 14:06:16
Я бы определенно согласился с тем, что добавление информации о версиях к URL-адресу файла - безусловно, лучший путь. Это работает, все время, для всех. Но, если вы не используете его, и вам просто нужно время от времени перезагружать этот файл CSS или JS в вашем собственном браузере ... просто откройте его на своей вкладке и нажмите SHIFT-reload (или CTRL-F5)! Вы можете эффективно сделать то же самое, используя JS, загрузив файл в (скрытый) iframe, дождавшись его загрузки и затем вызвав iframe.contentWindow.location.reload(true). См. Метод (4) stackoverflow.com/a/22429796/999120 - это касается изображений, но то же самое относится.
Doin 27.12.2015 05:17:54
Я действительно ценю то, как этот вопрос задавался и с тех пор обновлялся. Это полностью описало то, что я должен ожидать в ответах. Я собираюсь следовать этому подходу в моих вопросах отныне. Ура!
rd22 1.08.2016 09:46:08
30 ОТВЕТОВ

Обновление: переписано для включения предложений от Джона Милликина и da5id . Это решение написано на PHP, но должно быть легко адаптировано к другим языкам.

Обновление 2: Включение комментариев Ника Джонсона о том, что оригинальное .htaccessрегулярное выражение может вызвать проблемы с файлами вроде json-1.3.js. Решение состоит в том, чтобы переписать только если в конце есть ровно 10 цифр. (Потому что 10 цифр охватывают все метки времени с 9.09.2001 по 20.11.22.)

Сначала мы используем следующее правило перезаписи в .htaccess:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

Теперь мы напишем следующую функцию PHP:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

Теперь, куда бы вы ни включили свой CSS, измените его следующим образом:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

К этому:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

Таким образом, вам больше никогда не придется изменять тег ссылки, и пользователь всегда увидит последнюю версию CSS. Браузер сможет кэшировать файл CSS, но когда вы вносите какие-либо изменения в свой CSS, браузер увидит это как новый URL, поэтому он не будет использовать кэшированную копию.

Это также может работать с изображениями, значками и JavaScript. В основном все, что не генерируется динамически.

452
6.12.2013 16:01:24
Мой собственный статический контент-сервер делает то же самое, за исключением того, что я использую параметр для управления версиями (base.css? V = 1221534296) вместо изменения имени файла (base.1221534296.css). Я подозреваю, что ваш путь может быть немного более эффективным, хотя. Очень круто.
Jens Roland 2.06.2011 20:55:14
@Kip: очень удобное решение. Переписывание URL, очевидно, может предложить гораздо больше, чем просто красивые ссылки.
James P. 6.08.2011 12:51:32
Я вижу проблему с этим в том, что он обращается к файловой системе много раз - точно - количество ссылок * количество запросов / сек ..., что может быть или не быть проблемой для вас.
Tomáš Fejfar 24.09.2012 23:34:46
@AlixAxel: Нет, браузеры будут повторно извлекать его при изменении параметра, но некоторые публичные прокси-серверы не будут кэшировать файлы с параметрами url, поэтому лучше всего указывать версию в пути. И издержки mod_rewrite незначительны по сравнению с любым другим узким местом производительности в WPO
Jens Roland 27.11.2012 11:54:52
Первая file_existsпроверка действительно необходима? filemtimeпри неудаче вернет false, так почему бы просто не присвоить значение filemtime переменной и проверить, является ли оно ложным, перед переименованием файла? Это сократило бы одну ненужную файловую операцию, которая действительно сложилась бы.
Gavin 4.06.2014 23:21:26

Вы можете просто поставить ?foo=1234в конце вашего импорта CSS / JS, изменив 1234 на то, что вам нравится. Взгляните на SO HTML источник для примера.

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


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

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

52
23.09.2008 19:47:48
Бред какой то. Строка запроса (также называемая GET-параметрами) является частью URL. Они могут и будут кешироваться. Это хорошее решение.
troelskn 23.09.2008 15:39:44
@troelskn: спецификация HTTP 1.1 говорит иначе (в отношении запросов GET и HEAD с параметрами запроса): кэши НЕ ДОЛЖНЫ обрабатывать ответы на такие URI как свежие, если сервер не предоставит явное время истечения. См. W3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Michael Johnson 23.09.2008 18:52:21
Я попробовал тип строки запроса версий со всеми основными браузерами, и они действительно кэшируют файл, спецификации или нет. Тем не менее, я думаю, что лучше использовать формат style.TIMESTAMP.css, не злоупотребляя строками запросов, так как есть вероятность, что кеширующее прокси-ПО НЕ БУДЕТ кэшировать файл.
Tomas Andrle 8.10.2009 15:24:52
Стоит отметить, по какой-либо причине, что Stackoverflow сам использует метод строки запроса.
jason 1.05.2010 20:13:35
Убедитесь, что использование параметра? = Не заставит браузеры повторно извлекать кэшированный файл при изменении параметра. Единственный способ - изменить имя файла программным способом на стороне сервера, как ответил Кип
arunskrish 2.06.2013 06:10:25

Я слышал это называется "автоматическое управление версиями". Наиболее распространенный метод - включить mtime статического файла где-то в URL-адресе и удалить его с помощью обработчиков перезаписи или URL-адресов:

Смотрите также:

41
23.09.2008 03:21:55
Спасибо, я думаю, это был еще один случай, когда моя идея обсуждалась, я просто не знал, как она называется, поэтому я не нашел ее в поиске Google.
Kip 23.09.2008 12:12:04

Вы можете принудительно использовать «кеширование всей сессии», если добавляете идентификатор сессии как простой параметр файла js / css:

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>

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

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
5
23.09.2008 03:23:48

Простая клиентская техника

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

Обычные посетители вашего сайта не будут иметь того опыта, который вы испытываете при разработке сайта. Так как среднестатистический посетитель посещает сайт реже (может быть, только несколько раз в месяц, если вы не являетесь сетью Google или hi5), тогда он с меньшей вероятностью будет хранить ваши файлы в кеше, и этого может быть достаточно. Если вы хотите принудительно ввести новую версию в браузер, вы всегда можете добавить строку запроса в запрос и увеличить номер версии при внесении серьезных изменений:

<script src="/myJavascript.js?version=4"></script>

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

С другой стороны, если вы разрабатываете веб-сайт, вы не хотите менять номер версии каждый раз, когда сохраняете изменения в своей версии для разработки. Это было бы утомительно.

Поэтому, пока вы разрабатываете свой сайт, хорошим трюком будет автоматическая генерация параметра строки запроса:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

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

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

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

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

184
8.01.2013 19:25:40
Быстрый прием генерации строки запроса с помощью Javascript прекрасно работает во время активной разработки. Я сделал то же самое с PHP.
Alan Turing 21.05.2013 18:04:15
Это самый простой способ достичь желаемого результата оригинального плаката. Метод mod_rewrite хорошо работает, если вы хотите принудительно перезагрузить файл .css или .js КАЖДЫЙ раз, когда вы загружаете страницу. Этот метод все еще позволяет кэшировать, пока вы действительно не измените файл и не захотите принудительно перезагрузить его.
scott80109 19.02.2014 00:22:46
@keparo, у меня достаточно страниц jquery на всех страницах, если я собираюсь изменить это вручную, это займет месяц. Если вы можете помочь мне решить все без кодирования для каждой страницы.
cracker 26.11.2014 11:42:20
Я пробовал это решение с различными браузерами: добавление номера версии в конце URL-адреса файла JS. Интересно, что Opera 25.0, Firefox 34.0 и Chrome 39.0.2171.65 НЕ будут сохранять файл в кэше, как только в конце будет номер версии, даже если номер не изменится. IE 11.0 и Safari 5.1.7 работают как положено.
Chris Neve 19.01.2015 14:07:58
Это не жизнеспособное решение. Множество браузеров просто откажутся кешировать что-либо со строкой запроса. По этой причине Google, GTMetrix и другие подобные инструменты будут поднимать флаг, если у вас есть строки запроса на ссылки на статический контент. Хотя это, безусловно, достойное решение для развития, это абсолютно не решение для производства. Кроме того, браузер управляет кэшированием, а не сервером. Сервер просто предлагает, когда он должен быть обновлен; браузер НЕ ДОЛЖЕН слушать сервер (и часто этого не делает). Мобильные устройства являются ярким примером этого.
Nate I 24.04.2018 17:59:24

Изменение имени файла будет работать. Но обычно это не самое простое решение.

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

Короче говоря, если вы хотите использовать железо-жесткий контроль кэша

Cache-Control: no-cache, no-store, must-revalidate

в заголовках вашего ответа.

-3
23.09.2008 05:54:51
Проблема этого подхода заключается в том, что он генерирует обратную передачу на сервер для всего такого контента. Это не хорошо.
AnthonyWJones 23.09.2008 08:14:19
Это решение не идеально, но оно работает для всех ситуаций, включая статические веб-страницы. И если вы делаете это только для ограниченного числа файлов, скажем, ваших CSS-файлов, то это не должно увеличить время загрузки страницы.
Bill 5.10.2008 15:16:01

Не используйте foo.css? Version = 1! Браузеры не должны кэшировать URL-адреса с помощью переменных GET. Согласно http://www.thinkvitamin.com/features/webapps/serving-javascript-fast , хотя IE и Firefox игнорируют это, Opera и Safari этого не делают! Вместо этого используйте foo.v1234.css и используйте правила перезаписи, чтобы удалить номер версии.

15
23.09.2008 06:02:44
Во-первых, браузеры не кешируют, это функция HTTP. Почему http заботится о структуре URI? Есть ли официальная ссылка на спецификацию, в которой говорится, что кэширование HTTP должно понимать семантику URI, чтобы не кэшировать элементы со строкой запроса?
AnthonyWJones 23.09.2008 08:17:43
Веб-браузер, который включает в себя функции кэширования объектов (проверьте каталог кэша вашего браузера). HTTP - это протокол, включающий директивы от серверов к клиентам (прокси, браузеры, пауки и т. Д.), Предлагающий управление кэшем.
tzot 29.09.2008 15:47:47

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

Таким образом, ваш URL будет что-то вроде

http://mysite.com/css/[md5_hash_here]/style.css

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

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

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

93
23.09.2008 13:25:04
к сожалению я не знаю как это реализовать. Советы, пожалуйста ... подробнее ...
Michael Phelps 20.10.2014 09:56:19
Реализация в shell, ruby ​​и т. Д. Была бы великолепна
Peter 9.12.2014 19:28:27
Очень хорошее решение ... но я думаю, что для вычисления хеша файла при каждом запросе файла (css, js, images, html..etc) требуется каждый ресурс.
DeepBlue 2.05.2015 22:18:09
Это стандартное решение для тех, кто использует js или css-связку с gulp, grunt или webpack, реализация отличается для каждого решения, но хэширование ваших файлов в качестве шага сборки является обычным явлением и предлагается для современных связанных приложений
Brandon Søren Culley 13.04.2018 22:45:04
@DeepBlue - в ответе говорится «запускать этот скрипт каждый раз, когда меняется CSS» . Это НЕ на каждом посещении страницы. OTOH Ответ опускает основные детали - как измененный хеш становится частью URL? Я не знаю ...
ToolmakerSteve 14.04.2019 21:35:12

Я недавно решил это с помощью Python. Вот код (должен быть легко адаптирован к другим языкам):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

Этот код добавляет метку времени файлов в качестве параметра запроса к URL-адресу. Вызов следующей функции

script("/main.css")

приведет к

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

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

4
23.09.2008 13:54:40
может os.stat () создать узкое место?
hoju 23.07.2012 03:19:02
@Richard stat может быть узким местом, если диск очень медленный и запросов очень много. В этом случае вы можете кэшировать метку времени где-нибудь в памяти и очищать этот кеш при каждом новом развертывании. Тем не менее, эта сложность не будет необходимой в большинстве случаев использования.
pi. 23.07.2012 11:06:17

Скажем, у вас есть файл, доступный по адресу:

/styles/screen.css

Вы можете добавить параметр запроса с информацией о версии в URI, например:

/styles/screen.css?v=1234

или вы можете добавить информацию о версии, например:

/v/1234/styles/screen.css

ИМХО, второй метод лучше подходит для CSS-файлов, потому что они могут ссылаться на изображения с использованием относительных URL, что означает, что если вы укажете background-imageподобное, то так:

body {
    background-image: url('images/happy.gif');
}

его URL будет эффективно:

/v/1234/styles/images/happy.gif

Это означает, что если вы обновите номер используемой версии, сервер будет воспринимать это как новый ресурс и не будет использовать кэшированную версию. Если вы основываете свой номер версии на Subversion / CVS / и т.д. пересмотр означает, что будут замечены изменения в изображениях, на которые есть ссылки в файлах CSS. Это не гарантируется первой схеме, то есть URL images/happy.gifотносительно /styles/screen.css?v=1235IS , /styles/images/happy.gifкоторый не содержит никакой информации о версии.

Я реализовал решение для кэширования, используя эту технику с сервлетами Java, и просто обрабатываю запросы /v/*с помощью сервлета, который делегирует базовый ресурс (т /styles/screen.css. Е. ). В режиме разработки я установил кэширование заголовков , которые говорят клиенту всегда проверять свежесть ресурса с сервером (обычно это приводит к 304 , если вы делегировать для Tomcat DefaultServletи .css, .jsи т.д. файл не изменился) , а в режиме развертывания Я установил заголовки, которые говорят «кешировать навсегда».

5
24.09.2008 19:58:56
Простое добавление папки, которую вы можете переименовать при необходимости, будет работать, если вы используете только относительные URL-адреса. И тогда вы убедитесь , что для перенаправления на нужную папку из базовой папки, то есть в PHP: <?php header( 'Location: folder1/login.phtml' ); ?>.
Gruber 20.09.2012 08:56:50
Используя второй метод, изменение CSS сделает недействительными кэшированные копии всех изображений, на которые есть ссылки с относительными URL-адресами, что может быть или не быть желательным.
TomG 6.11.2013 15:55:52

Я предлагаю реализовать следующий процесс:

  • Версии ваших файлов CSS / JS при каждом развертывании, например: screen.1233.css (номер может быть вашей версией SVN, если вы используете систему управления версиями)

  • минимизируйте их, чтобы оптимизировать время загрузки

2
24.09.2008 11:38:17

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

-1
24.09.2008 20:06:02

Мой способ сделать это состоит в том, чтобы элемент link на стороне сервера включал:

<!--#include virtual="/includes/css-element.txt"-->

где содержимое css-element.txt

<link rel="stylesheet" href="mycss.css"/>

поэтому в тот день, когда вы захотите сделать ссылку на my-new-css.css или что-то еще, вы просто измените include.

0
25.09.2008 01:40:46

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

Это приведет к следующему:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

Конечно, решения datetime также работают в случае редактирования файла CSS, но я думаю, что это касается содержимого файла css, а не файла datetime, так зачем смешивать их?

9
24.06.2009 23:20:10

RewriteRule нуждается в небольшом обновлении для файлов js или css, которые содержат версионную запись в конце. Например, json-1.3.js.

Я добавил класс отрицания точек [^.] В регулярное выражение, так что .number. игнорируется

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
11
5.08.2010 21:00:23
Спасибо за вклад! С тех пор, как я написал этот пост, я тоже был сожжен этим. Мое решение состояло в том, чтобы переписать только если последняя часть имени файла содержит ровно десять цифр. (10 цифр охватывают все временные метки с 9.09.2001 по 20.11.22.) Я обновил свой ответ, включив в него следующее регулярное выражение:^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip 5.08.2010 21:07:05
Я понимаю регулярные выражения, но я не понимаю, какую проблему вы решаете [^.]здесь. Кроме того, нет смысла писать \dвнутри класса персонажей - \d+будет делать то же самое. После публикации ваш шаблон будет соответствовать любому количеству символов (жадно), затем буквальной точке, затем не точке, затем одной или нескольким цифрам, затем точке, затем cssили js, затем концу имени файла. Нет совпадений для вашего примера ввода: regex101.com/r/RPGC62/1
mickmackusa 5.12.2019 06:52:54

Спасибо Кипу за его идеальное решение!

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

Надеюсь, это поможет кому-то еще.

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}

Приветствия и спасибо.

7
7.01.2011 05:58:27

Плагин mod_pagespeed от Google для apache сделает для вас автоматическое управление версиями. Это действительно гладко.

Он анализирует HTML на выходе из веб-сервера (работает с PHP, rails, python, статическим HTML - чем угодно) и переписывает ссылки на CSS, JS, файлы изображений, чтобы они включали код id. Он обслуживает файлы по измененным URL-адресам с очень длинным контролем кэша. Когда файлы изменяются, он автоматически меняет URL-адреса, поэтому браузер должен повторно их получить. В основном это работает, без каких-либо изменений в вашем коде. Это даже уменьшит ваш код на выходе.

113
11.04.2011 22:13:21
Это здорово, но все еще в бета-версии. Можно ли использовать его для обслуживания предприятия?
Sanghyun Lee 15.07.2011 04:00:10
Это НЕПРАВИЛЬНО (автоматическое переключение с источником), когда это явно проблема браузера. Дайте нам (разработчикам) настоящее обновление мозгов: <ctrl> + F5
T4NK3R 20.09.2011 12:50:06
mod_pagespeed функционально эквивалентен полностью автоматическому шагу сборки / компиляции для html / css / js. Я думаю, вам будет сложно найти серьезных разработчиков, которые думают, что системы сборки по своей сути неверны, или что что-то не так с тем, что они полностью автоматические. Аналогом чистой сборки является очистка кеша mod_pagespeed : code.google.com/p/modpagespeed/wiki/… ?
Leopd 20.09.2011 17:38:13
@ T4NK3R mod_pagespeed ничего не должен делать с вашим источником для управления кешем, просто упоминалось, что он может помочь с такими вещами, как минификация. Что касается того, является ли это "НЕПРАВИЛЬНЫМ", это совершенно субъективно. Это может быть неправильно для вас, но это не значит, что это изначально плохо .
Madbreaks 3.08.2012 18:10:13
Он работает и с nginx, хотя его нужно собрать из исходного кода: developers.google.com/speed/pagespeed/module/…
Rohit 22.01.2015 07:41:02

Я поместил MD5-хэш содержимого файла в его URL. Таким образом, я могу установить очень длинную дату истечения срока действия, и мне не придется беспокоиться о пользователях, имеющих старый JS или CSS.

Я также вычисляю это один раз для файла во время выполнения (или для изменений файловой системы), так что нет ничего смешного во время разработки или в процессе сборки.

Если вы используете ASP.NET MVC, вы можете проверить код в моем другом ответе здесь .

2
23.05.2017 12:18:23

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

<?php
//Replace the 'style.css' with the link to the stylesheet.
echo "<style type='text/css'>".file_get_contents('style.css')."</style>";
?>

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

-3
3.11.2011 17:34:42
Проблема в том, что вы теряете возможность кэшировать файл, что замедляет работу пользователя.
Austin 3.11.2011 18:10:58

Извините за возвращение мертвой нити.

@ Тома прав.

Использование метода querystring не будет кэшироваться, как указано Стивом Соудерсом ниже:

... что Squid, популярный прокси, не кэширует ресурсы с помощью строки запроса.

@ TomA предложение использовать style.TIMESTAMP.css - это хорошо, но MD5 будет намного лучше, поскольку только когда содержимое действительно изменилось, MD5 также изменится.

2
23.05.2017 11:55:10
Кроме того, использование метки времени в качестве параметра строки запроса приведет к перезагрузке файла каждый раз, что означает отсутствие кэширования вообще
Luca 10.10.2012 23:40:25
Комментарий 2008 года в том же посте упоминает, что настройки Squid по умолчанию изменились; вопрос в том, какой процент вашего трафика обрабатывается (сейчас) устаревшими версиями Squid.
TomG 6.11.2013 15:59:44

Я вижу проблему с подходом использования дифференциатора на основе временной метки или хеша в URL ресурса, который удаляется по запросу на сервере. Страница, содержащая ссылку, например, на таблицу стилей, также может кэшироваться . Таким образом, кэшированная страница может запрашивать более старую версию таблицы стилей, но будет обслуживаться самой последней версией, которая может работать или не работать с запрашивающей страницей.

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

2
14.05.2019 13:27:24

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

Все, что вам нужно сделать, если получить измененную временную метку файла и добавить ее в качестве строки запроса к файлу

В PHP я бы сделал это так:

<link href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">

filemtime - это функция PHP, которая возвращает метку времени изменения файла.

69
1.12.2018 18:50:18
Вы можете просто использовать mycss.css?1234567890.
Gavin 4.06.2014 23:43:33
очень элегантно, хотя я немного изменил его <link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>, на случай, если некоторые аргументы в этой теме о кэшировании URL-адресов с помощью переменных GET (в предлагаемом формате) верны
luke_mclachlan 24.12.2015 13:52:49
В дополнение к моему последнему комментарию, я видел, что WordPress использует ?ver=так, кто знает!
luke_mclachlan 24.12.2015 14:25:54
Отличное решение. Кроме того, я обнаружил, что filemtime не работает для полного доменного имени (FQDN), поэтому я использовал FQDN для части href и $ _SERVER ["DOCUMENT_ROOT"] для части filemtime. Пример: <link rel = "stylesheet" href = "http: //theurl/mycss.css? V = <? Php echo filemtime ($ _ SERVER [" DOCUMENT_ROOT "]. '/Mycss.css')?>" />
rrtx2000 18.08.2016 16:56:40
Большое спасибо. Просто и хорошо. Вот это в Python: progpath = os.path.dirname (sys.argv [0]) def versionize (файл): timestamp = os.path.getmtime ('% s /../ web /% s'% (progpath) , file)) return '% s? v =% s'% (file, timestamp) print <link href = "% s" rel = "stylesheet" '' type = "text / css" /> '\% versionize ( 'css / main.css')
dlink 24.12.2017 22:00:27

«Еще одна идея, предложенная SCdF, заключается в добавлении фиктивной строки запроса в файл. (Некоторый код Python для автоматического использования метки времени в качестве фиктивной строки запроса был представлен pi.) Однако существует некоторое обсуждение того, является ли или не браузер будет кэшировать файл со строкой запроса. (Помните, мы хотим, чтобы браузер кэшировал файл и использовал его при будущих посещениях. Мы хотим, чтобы он снова получал файл, когда он изменился.) Поскольку это не ясно что происходит с фиктивной строкой запроса, я не принимаю этот ответ ".

<link rel = "stylesheet" href = "file.css? <? = hash_hmac ('sha1', session_id (), md5_file (" file.css "));?>" />

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

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

1
29.03.2013 14:06:43

Для среды сервлетов Java вы можете обратиться к библиотеке Jawr . Страница функций объясняет, как она обрабатывает кэширование:

Jawr сделает все возможное, чтобы заставить ваших клиентов кешировать ресурсы. Если браузер спрашивает, изменился ли файл, заголовок 304 (не измененный) отправляется обратно без содержимого. С другой стороны, с Jawr вы будете на 100% уверены, что новые версии ваших пакетов загружаются всеми клиентами. Каждый URL-адрес ваших ресурсов будет содержать автоматически сгенерированный, основанный на контенте префикс, который изменяется автоматически при каждом обновлении ресурса. После развертывания новой версии URL-адрес пакета также изменится, поэтому клиент не сможет использовать более старую кэшированную версию.

Библиотека также выполняет минификацию js / css, но вы можете отключить ее, если не хотите.

2
11.06.2013 12:16:51

Вы можете просто добавить случайное число с помощью CSS / JS url, например

example.css?randomNo=Math.random()
5
4.07.2013 10:01:51

Для ASP.NET я предполагаю следующее решение с расширенными возможностями (режим отладки / выпуска, версии):

Файлы Js или Css включены таким образом:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix и Global.CssPostfix вычисляются в Global.asax следующим образом:

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}
5
3.08.2013 13:28:49

Другой способ, о котором я не упоминал для файлов js, - это использование jQuery $.getScriptв сочетании с $.ajaxSetupопцией cache: false.

Вместо того:

<script src="scripts/app.js"></script>

Вы можете использовать:

$.ajaxSetup({
  cache: false
});

$.getScript('scripts/app.js'); // GET scripts/app.js?_1391722802668
-2
6.02.2014 21:43:23
идея не в том, чтобы полностью предотвратить кэширование. Вы просто хотите, чтобы пользователь получил последний файл, как только файл будет изменен.
Kip 6.02.2014 22:18:00

Еще одно предложение для сайтов ASP.Net,

  1. Установите разные параметры управления кэшем: значения максимального возраста для разных статических файлов.
  2. Для файлов css / js шансы на изменение этих файлов на сервере высоки, поэтому установите минимальный контроль кэша: максимальное значение возраста 1 или 2 минуты или что-то, что соответствует вашим потребностям.
  3. Для изображений установите дальнюю дату в качестве элемента управления кэшем: значение максимального возраста, скажем, 360 дней.
  4. Таким образом, когда мы делаем первый запрос, все статическое содержимое загружается на клиентский компьютер с ответом 200-OK.
  5. При последующих запросах и через две минуты мы видим 304-неизмененные запросы к файлам css и js, что позволяет нам избежать версий css / js.
  6. Файлы изображений не будут запрашиваться, так как они будут использоваться из кэшированной памяти до истечения срока действия кэша.
  7. Используя приведенные ниже конфигурации web.config, мы можем добиться описанного выше поведения,
1
12.08.2014 13:19:18

Если вы используете современный браузер, вы можете использовать файл манифеста, чтобы сообщить браузерам, какие файлы необходимо обновить. Это не требует никаких заголовков, никаких версий в URL и т. Д.

Для получения дополнительной информации см .: См .: https://developer.mozilla.org/nl/docs/Web/HTML/Applicatie_cache_gebruiken#Introduction

2
16.09.2014 15:09:15

Для ASP.NET 4.5 и выше вы можете использовать связывание скриптов .

Запрос http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81предназначен для пакета AllMyScripts и содержит пару строк запроса v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. Строка запроса v имеет маркер значения, который является уникальным идентификатором, используемым для кэширования. Пока пакет не изменяется, приложение ASP.NET будет запрашивать пакет AllMyScripts, используя этот токен. Если какой-либо файл в пакете изменяется, платформа оптимизации ASP.NET сгенерирует новый токен, гарантируя, что запросы браузера для пакета получат последний пакет.

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

10
30.09.2014 20:11:38
Пожалуйста, помогите мне, я не делаю никаких изменений в bundle.config, просто меняю файлы css или js, тогда как я могу решить проблему с кэшированием?
vedankita kumbhar 27.02.2017 06:43:04