Я заметил, что некоторые браузеры (в частности, Firefox и Опера) очень рьяно используют кэшированные копии файлов .css и .js даже между сессиями браузера. Это приводит к проблеме, когда вы обновляете один из этих файлов, но браузер пользователя продолжает использовать кэшированную копию.
Каков наиболее элегантный способ заставить браузер пользователя перезагружать файл после его изменения?
В идеале решение не заставит браузер перезагружать файл при каждом посещении страницы.
Я нашел полезными предложения Джона Милликина и da5id's. Оказывается, для этого есть термин: автоматическое управление версиями.
Я опубликовал новый ответ ниже, который представляет собой комбинацию моего исходного решения и предложения Джона.
Другая идея, предложенная SCdF, - добавить в файл фиктивную строку запроса. (Некоторый код Python для автоматического использования метки времени в качестве ложной строки запроса был представленный число Пи..)
Однако есть некоторые дискуссии о том, будет ли браузер кэшировать файл со строкой запроса. (Помните, мы хотим, чтобы браузер кэшировал файл и использовал его при будущих посещениях. Мы хотим, чтобы он снова загружал файл, когда он был изменен.)
Я определенно согласен с тем, что добавление информации о версиях к URL-адресу файла - безусловно, лучший способ. Это всегда работает для всех. Но, если вы его не используете, и вам просто нужно время от времени перезагружать этот файл CSS или JS в вашем собственном браузере ... просто откройте его на отдельной вкладке и нажмите SHIFT-reload (или CTRL-F5)! Вы можете эффективно сделать то же самое с помощью JS, загрузив файл в (скрытый) iframe, дождавшись его загрузки и затем вызвав iframe.contentWindow.location.reload(true). См. Метод (4) stackoverflow.com/a/22429796/999120 - это касается изображений, но то же самое.
Я очень ценю то, как был задан этот вопрос, и с тех пор он обновляется. Он полностью описал то, чего я должен ожидать в ответах. С этого момента я буду следовать этому подходу в своих вопросах. Ваше здоровье!
Для справки: удаленный ответ da5id - это «Если обновление достаточно большое / важное, я обычно меняю имя файла»..
Если изменения происходят не очень часто, у меня есть предложение. Просто измените имя файла и отредактируйте исходный код, чтобы включить новое имя файла. Тогда браузер не сможет прочитать кешированный файл.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Это решение написано на PHP, но его легко адаптировать к другим языкам.
Исходное регулярное выражение .htaccess может вызывать проблемы с такими файлами, как json-1.3.js. Решение состоит в том, чтобы перезаписать только в том случае, если в конце ровно 10 цифр. (Поскольку 10 цифр покрывают все временные метки с 9.09.2001 по 20.11.2266.)
Во-первых, мы используем следующее правило перезаписи в .htaccess:
RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ . [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. В основном все, что не генерируется динамически.
Мой собственный сервер статического содержимого делает то же самое, за исключением того, что я использую параметр для управления версиями (base.css? V = 1221534296), а не изменение имени файла (base.1221534296.css). Я подозреваю, что ваш способ может быть немного более эффективным. Очень круто.
Отличный ответ. Интересно, можно ли это использовать, когда статические файлы находятся на других серверах. Разве не рекомендуется хранить статические файлы на других серверах?
@ Кип: Очень хорошее решение. Перезапись URL-адресов, очевидно, может предложить гораздо больше, чем просто красивое оформление URL-адресов.
@Sangdol: То, что вы можете сделать с PHP, - это копия и md5, чтобы проверить, изменился ли файл. Если md5 файла изменился => версия ++.
Примечание: если вы используете Zend Framework, это правило перезаписи должно работать поверх других правил.
Это хорошо, но я работаю над системой, в которой некоторые css / js загружаются с другого сервера, поэтому через URL-адрес, а не по абсолютному пути. Это всего лишь мысль, но я думаю, что функция php может быть расширена, чтобы охватить этот случай, путем вызова php get_headers (), получения последней измененной даты, преобразования ее в строку поиска mtime и выполнения preg_replace как есть.
FWIW, я использовал приведенный выше код: auto_version() и rewriteRules во многих небольших файлах JS / CSS (~ 20, в основном 1–100 КБ). Это, по-видимому, сильно загружает мой кеш браузера (Chrome v19), потому что вентилятор на моем Mac (OS X Lion) начинает сходить с ума. Я смог исправить это, отключив кеш в инструментах разработчика. Кто-нибудь знает, почему это происходит?
Я вижу проблему в том, что он обращается к файловой системе много раз - точно - количество ссылок * количество запросов в секунду ... это может быть или не быть проблемой для вас.
@JensRoland: Мне любопытно узнать ваш комментарий. Мне кажется, что при использовании строк запроса не потребуются дополнительные накладные расходы правила mod_rewrite. Есть ли какой-нибудь конкретный браузер, который не загружает файл повторно с другой строкой запроса, или есть другие причины для вашего подозрения?
@AlixAxel: Нет, браузеры будут повторно загружать его при изменении параметра, но некоторые общедоступные прокси-серверы не кэшируют файлы с параметрами URL-адреса, поэтому рекомендуется включать версию в путь. И накладные расходы mod_rewrite ничтожны по сравнению с любым другим узким местом производительности в WPO.
@JensRoland: Достаточно честно. Я по-прежнему предпочитаю подход со строкой запроса, он проще и имеет меньше зависимостей, о которых нужно беспокоиться.
Никто не хочет изобретать велосипед, есть ли для этого простой API / библиотека? Я имею в виду без полноценного MVC / Rail-подобного фреймворка, и его можно просто вставить и заменить в существующий код.
Просто отметим, что мы использовали * .css? V = 2342323423, и он не работал до трех обновлений и очистки кеша.
Я изменил это, чтобы поиграть с "/" лучше. gist.github.com/clouddueling/6877043
@ TomášFejfar, это не будет проблемой, если вы кешируете вывод php в каком-то обратном прокси, таком как Squid или Varnish.
Как это выглядит с NGINX? Если кто-то может ответить на этот вопрос, он также может ответить на этот вопрос: stackoverflow.com/questions/9423138/…
@MichaelCalkins У меня была такая же проблема. Не используйте имя переменной со значением. Просто используйте *.css?2342323423. Похоже, это сработало для меня.
Действительно ли необходима первая проверка file_exists? filemtime вернет false в случае сбоя, так почему бы просто не присвоить значение filemtime переменной и проверить, является ли оно ложным, прежде чем переименовывать файл? Это сократило бы одну ненужную файловую операцию, которая действительно увеличила бы объем.
Я сократил функцию до этого: $filetime = strpos($file, '/') === 0 ? filemtime($_SERVER['DOCUMENT_ROOT'] . $file) : false; if ($filetime) return preg_replace('{\.([^./]+)$}', ".$filetime).$1", $file); else return $file;
Я сделал что-то подобное (на основе jsp-тегов), но это не решает проблему одного из этих ресурсов javascript программно, включая .css, изображения, другие javascript или другие ресурсы. Для этого почти требуется, чтобы javascript был переписан во время сборки или ... волшебный метод Google, упомянутый в другом ответе.
Умное решение! Но использовать его с публичным CDN непросто. Перезапись имени файла также должна выполняться в файлах CDN. В то время как с решением для строки запроса CDN может справиться с этим (опция Amazon Cloudfront: «Перенаправить строки запроса»)
Отличный пост о проблеме кеширования строки запроса: bizcoder.com/caching-resources-with-query-strings
с одной стороны - это хорошо выглядит. Но с другой стороны - если новый разработчик берет этот проект, допустим, ему нужно запустить его на другом сервере, он помещает файлы проекта и запускается в браузере. И видит - wtf, почему эти стили и javacripts не работают? Он не знает, что на сервере apache должна быть какая-то конфигурация для работы. Или одним из решений может быть - при неудачной загрузке файла - ведение журнала с сообщением - вам необходимо настроить apache для использования перезаписи файлов.
@ TomášFejfar - когда это может быть проблемой, что это касается файловой системы? Файловая система все равно сильно затронута - например, база данных. Может ли действительно проверка времени последнего изменения файла быть долгой по сравнению с другими вещами, такими как база данных? И, кстати, я вижу в документах, что результат filemtime кэшируется php.net/manual/en/function.filemtime.php
Еще одна вещь для тех, кто придет сюда в будущем: в PHP двойная обратная косая черта в регулярном выражении работает, но, похоже, технически некорректна. Смотрите regex101.com/r/eA4zP3/1 против regex101.com/r/eA4zP3/2.
@arcodesign Регулярное выражение .htaccess изменяет base.1221534296.css на base.css, которое является фактическим именем файла в файловой системе. Регулярное выражение в PHP не имеет двойной обратной косой черты - у нас есть строка PHP, содержащая регулярное выражение. \\ в строке PHP кодирует одну обратную косую черту.
Итак, скажем, вы используете varnish в качестве прокси-сервера для кеширования, нужно ли изменять какой-либо код в ответе? @PatrickJamesMcDougle
@JohnJackson нет, если у вас есть подходящий механизм для аннулирования кеша
Каков выигрыш в производительности, если вы сделаете это таким образом, по сравнению с простым добавлением filemtime в качестве запроса к файлу?
@Kip Как изменить номер версии файла. только когда меняется не каждый раз?
Спасибо, @Kip. Но я не знаю, как переписать правило в .htaccess в Javascript
Это единственное законное решение. Добавление строк запроса к ссылкам на статический контент - плохая идея, поэтому все Google, GTMetrix и т. д. Поднимают для этого флаги. Изменение имени совершенно элегантно и заставляет браузер обновляться ТОЛЬКО, когда вам это нужно. Строки запроса нарушают кеширование для некоторых браузеров, что НИКОГДА не следует рассматривать как решение (это взлом, а не решение).
@NateI даже с потенциальной потерей производительности из-за нескольких запросов? Я думаю, что строки запроса могут быть идеальным временным решением, просто чтобы пользователи не увидели сломанный веб-сайт во время основного перехода.
@JarrodW. Я могу определенно согласиться с тем, что иногда вам просто нужно временное решение, мне просто не нравится то, что работает только в определенных браузерах. Браузеру в любом случае необходимо сделать запрос на JS; мы здесь вообще не добавляем запрос. Добавление строки запроса по-прежнему требует, чтобы браузер запрашивал ее с сервера, и весь смысл обхода кеширования заключается в том, чтобы в первую очередь принудительно выполнить этот запрос.
@Kip нет смысла писать \d внутри символьного класса. Кроме того, вероятно, лучше использовать разделитель шаблонов, который не имеет особого значения в регулярном выражении - это улучшит удобочитаемость человека. Двойная косая черта в php может быть одинарной. Возможно, избегайте группы захвата. На ваше рассмотрение: 3v4l.org/PPbUW
@Kip, вы сказали, что это можно легко адаптировать к JavaScript, но, честно говоря, я вроде как новичок в JS и ничего не знаю о PHP. Как мне адаптировать это к JS? о чем этот файл .htaccess?
Что делать, если файлы находятся в хранилище CDN, а не на сервере сайта?
может кто-нибудь сказать мне, где я должен написать функцию php. В файле .htaccess или я должен создать новый файл php.
Вы можете просто поместить ?foo=1234 в конец импорта CSS / JavaScript, изменив 1234 на все, что вам нравится. Взгляните на исходный HTML-код Stack Overflow для примера.
Идея заключается в том, что параметры ? в любом случае отбрасываются / игнорируются в запросе, и вы можете изменить это число при развертывании новой версии.
Примечание: Есть некоторые аргументы относительно того, как именно это влияет на кеширование. Я считаю, что общая суть заключается в том, что запросы ПОЛУЧАТЬ с параметрами должен или без них могут быть кэшируемыми, поэтому вышеуказанное решение должно работать.
Тем не менее, и веб-сервер должен решить, хочет ли он придерживаться этой части спецификации, и браузер, который использует пользователь, поскольку он может просто пойти прямо и запросить новую версию в любом случае.
Ерунда. Строка запроса (также известная как параметры GET) является частью URL-адреса. Они могут и будут кэшироваться. Это хорошее решение.
@troelskn: В спецификации HTTP 1.1 сказано иное (в отношении запросов GET и HEAD с параметрами запроса): кеши НЕ ДОЛЖНЫ обрабатывать ответы на такие URI как свежие, если сервер не предоставляет явное время истечения срока действия. См. w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Я пробовал использовать тип строки запроса для управления версиями со всеми основными браузерами, и они ДОЛЖНЫ кешировать файл, спецификации или нет. Однако я думаю, что лучше использовать формат style.TIMESTAMP.css, в любом случае не злоупотребляя строками запроса, потому что все еще существует вероятность того, что программное обеспечение для кеширования прокси НЕ будет кэшировать файл.
По какой-то причине стоит отметить, что сам Stackoverflow использует метод строки запроса.
Браузеры Android 2.2 не работают в этом случае и вообще не загружают CSS.
@Petrogad - ну это точно ошибка в браузере андроида. Был ли отправлен отчет об ошибке?
Подтвердили, что использование параметра? = Не заставит браузеры повторно загружать кешированный файл при изменении параметра. Единственный способ - программно изменить имя файла на стороне сервера, как ответил Кип.
@boscharun: попробуйте? v = параметр ins
Я слышал, что это называется «автоматическое управление версиями». Наиболее распространенный метод - включить время модификации статического файла где-нибудь в URL-адрес и удалить его с помощью обработчиков перезаписи или конфигураций URL-адресов:
Смотрите также:
Спасибо, думаю, это был еще один случай, когда моя идея обсуждалась, я просто не знал, как она называется, поэтому не нашел ее в поисках Google.
Вы можете принудительно выполнить «кеширование всего сеанса», если добавите идентификатор сеанса в качестве ложного параметра файла JavaScript / 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>
В общем, кеширование - это хорошо ... Итак, есть несколько методов, в зависимости от того, решаете ли вы проблему самостоятельно при разработке веб-сайта или пытаетесь ли вы управлять кешем в производственной среде.
Обычные посетители вашего сайта не будут иметь того опыта, который вы испытываете при разработке сайта. Поскольку средний посетитель заходит на сайт реже (может быть, всего несколько раз в месяц, если вы не являетесь пользователем Google или hi5 Networks), то вероятность того, что ваши файлы будут храниться в кеше, будет ниже, и этого может быть достаточно.
Если вы хотите принудительно запустить новую версию в браузере, вы всегда можете добавить строку запроса в запрос и увеличить номер версии при внесении серьезных изменений:
<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. Когда браузер делает запрос к серверу, частью ответа является заголовок Истекает ... дата, которая сообщает браузеру, как долго он должен храниться в кеше. В следующий раз, когда браузер встречает запрос на тот же файл, он видит, что у него есть копия в кеше, и смотрит на дату Истекает, чтобы решить, следует ли его использовать.
Так что, хотите верьте, хотите нет, но на самом деле именно ваш сервер делает этот кеш браузера таким постоянным. Вы можете настроить параметры своего сервера и изменить заголовки Истекает, но небольшая техника, которую я написал выше, вероятно, намного проще для вас. Поскольку кэширование - это хорошо, вы обычно хотите установить эту дату далеко в будущее («Заголовок« Истекает срок в далеком будущем »») и использовать описанную выше технику для принудительного изменения.
Если вас интересует дополнительная информация о HTTP или о том, как эти запросы выполняются, хорошей книгой является «Высокопроизводительные веб-сайты» Стива Содерса. Это очень хорошее введение в предмет.
Быстрый трюк с генерацией строки запроса с помощью Javascript отлично работает во время активной разработки. Я сделал то же самое с PHP.
Это самый простой способ достичь желаемого результата оригинального плаката. Метод mod_rewrite хорошо работает, если вы хотите принудительно перезагружать файл .css или .js КАЖДЫЙ раз при загрузке страницы. Этот метод по-прежнему позволяет кэшировать, пока вы действительно не измените файл и не захотите принудительно перезагрузить его.
@keparo, у меня достаточно jquery на всех страницах, если я собираюсь изменить это вручную, это займет месяц. Если вы можете помочь мне решить все без кодирования на каждой странице.
Я пробовал это решение с разными браузерами: добавляя номер версии в конце URL-адреса файла JS. Интересно, что Opera 25.0, Firefox 34.0 и Chrome 39.0.2171.65 НЕ сохранят файл в кеше, как только в конце будет номер версии, даже если номер не изменится. Однако IE 11.0 и Safari 5.1.7 работают как положено.
Будет ли работать этот сценарий строки запроса в отдельном файле js, а не добавляться ко всем страницам сайта, и если да, то как?
Если вам не повезло, вам следует подумать об использовании Date.now () вместо случайного значения.
Кажется, это не работает для моего CSS, когда я использую: <link href='myCss.css?dev=14141'...>
Указатели здесь были действительно удобными. У меня есть SPA, и все, что мне нужно сделать, это обновить единственный index.html при публикации. Сделать это вручную все равно было бы ужасно. Я написал код и приложение, которое это делает - я написал ответ на вопрос (который, вероятно, находится в самом конце, потому что он новый, а не потому, что он плохой), который описывает, как он работает, и включает код. Надеюсь, это поможет кому-то другому развиваться в подобных ситуациях.
Это лучшее и наиболее надежное решение, перечисленное по той простой причине, что оно позволяет избежать манипуляций с сервером, ненадежного кэширования заголовков HTTP и сохраняет автоматическое кеширование браузеров. Если вы начнете перехватывать сервер и переписывать URL-адреса, вы создаете потенциальный беспорядок из обращений к вашим пользователям и от них, которым не управляет. Этот простой трюк со строкой запроса используется с 1990-х годов, поэтому большинство детей не знают, что он там есть. Образы и сценарии кэша браузера и сервера изначально используют заголовки http. Вот почему маршрутизация AngularJS и прочие цирковые трюки не нужны, если вы разбираетесь в кешировании.
Это не жизнеспособное решение. Многие браузеры просто откажутся кэшировать что-либо со строкой запроса. Это причина, по которой Google, GTMetrix и аналогичные инструменты поднимают флаг, если у вас есть строки запроса по ссылкам на статический контент. Хотя это, безусловно, достойное решение для разработки, это абсолютно не решение для производства. Кроме того, кешированием управляет браузер, а не сервер. Сервер просто ПРЕДЛАГАЕТ, когда его следует обновить; браузер НЕ ДОЛЖЕН слушать сервер (и часто этого не делает). Мобильные устройства - яркий тому пример.
Решение document.write работает слишком хорошо, теперь я не могу установить точку останова в Chrome, потому что URL-адрес постоянно меняется и, таким образом, продолжает обновляться и терять мои точки останова!
Я предпочитаю это решение, поскольку оно не требует исправления на стороне сервера. Я больше не использую серверный код (например, .php, .asp и т. д.). Сейчас я разрабатываю строго клиентскую сторону, на сервер отправляются только вызовы ajax для получения данных, а также код js и стили css. И Chrome, похоже, относится к css, как банк к золотым слиткам. МОЙ!
Изменение имени файла подействует. Но обычно это не самое простое решение.
Как вы заметили, заголовок HTTP-управления кешем «no-cache» не всегда работает. Спецификация HTTP 1.1 позволяет пользовательским агентам свободно решать, запрашивать новую копию или нет. (Это не интуитивно понятно, если вы просто посмотрите на названия директив. Прочтите фактический Спецификация HTTP 1.1 для кеша ... это имеет немного больше смысла в контексте.)
Вкратце, если вы хотите использовать жесткий кеш-контроль, используйте
Cache-Control: no-cache, no-store, must-revalidate
в заголовках ваших ответов.
Проблема с этим подходом заключается в том, что он генерирует обратный путь к серверу для всего такого контента. Это не хорошо.
Это решение не идеально, но оно работает во всех ситуациях, включая статические веб-страницы. И если вы делаете это только для ограниченного количества файлов, скажем, для ваших файлов CSS, тогда это не должно увеличивать значительное количество времени на загрузку страницы.
Первое предложение может относиться к удаленный ответ da5id -«Если обновление достаточно большое / важное, я обычно меняю имя файла»..
Не используйте foo.css?version=1!
Браузеры не должны кэшировать URL-адреса с помощью переменных GET. Согласно http://www.thinkvitamin.com/features/webapps/serving-javascript-fast, хотя Internet Explorer и Firefox игнорируют это, Опера и Сафари этого не делают! Вместо этого используйте foo.v1234.css и используйте правила перезаписи, чтобы вырезать номер версии.
Во-первых, браузеры не кешируют, это функция HTTP. Зачем http заботиться о структуре URI? Есть ли официальная ссылка на спецификацию, в которой говорится, что кеширование HTTP должно понимать семантику URI, чтобы оно не кэшировало элементы со строкой запроса?
Веб-браузер, который включает в себя функции кеширования объектов (проверьте каталог кеша вашего браузера). HTTP - это протокол, включающий директивы от серверов к клиентам (прокси, браузеры, пауки и т. д.), Предлагающие управление кешем.
Ссылка thinkvitamin.com не работает (домен существует, но нет репозитория).
Вместо того, чтобы изменять версию вручную, я бы порекомендовал вам использовать MD5-хэш фактического файла CSS.
Таким образом, ваш URL-адрес будет примерно таким
http://mysite.com/css/[md5_hash_here]/style.css
Вы по-прежнему можете использовать правило перезаписи для удаления хэша, но преимущество состоит в том, что теперь вы можете установить для своей политики кеширования значение «кэшировать навсегда», поскольку, если URL-адрес тот же самый, это означает, что файл не изменился.
Затем вы можете написать простой сценарий оболочки, который вычислит хэш файла и обновит ваш тег (вы, вероятно, захотите переместить его в отдельный файл для включения).
Просто запускайте этот сценарий каждый раз, когда изменяется CSS, и все будет хорошо. Браузер перезагружает ваши файлы ТОЛЬКО при их изменении. Если вы внесете правку, а затем отмените ее, вам не составит труда определить, к какой версии вам нужно вернуться, чтобы посетители не могли повторно загрузить ее.
к сожалению, я не знаю, как это реализовать. Посоветуйте пожалуйста ... подробнее ...
Было бы здорово реализовать в оболочке, рубине и т. д.
Очень хорошее решение ... но я думаю, что для вычисления хэша файла в каждом запросе файла (css, js, images, html..etc) для каждого отдельного посещения страницы требуются ресурсы.
Это стандартное решение для тех, кто использует js или css в комплекте с gulp, grunt или webpack, реализация отличается для каждого решения, но хеширование ваших файлов на этапе сборки является обычным и рекомендуется для современных связанных приложений.
@DeepBlue - в ответе написано "запускать этот скрипт каждый раз, когда изменяется CSS". Это НЕ при каждом посещении страницы. OTOH Ответ не учитывает основные детали - как измененный хеш становится частью URL-адреса? Я не знаю...
Используйте метку времени файла вместо хеша MD5. Все, что вам нужно, это уникальная ценность: File.GetLastWriteTime(filePath).Ticks.ToString().
Недавно я решил это с помощью 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 %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 автоматически приведет к аннулированию кеша. Работает очень хорошо и накладных расходов не заметно.
может ли os.stat () создать узкое место?
@Richard stat может стать узким местом, если диск очень медленный и запросов очень много. В этом случае вы можете кэшировать метку времени где-нибудь в памяти и очищать этот кеш при каждом новом развертывании. Тем не менее, эта сложность не понадобится в большинстве случаев использования.
Я знаю, что это очень давно, но для всех, кто читает, временная метка слишком агрессивна. Это означает, что у вас вообще никогда не будет кеширования, и если вы этого хотите, вы можете управлять этим с помощью настраиваемых заголовков для статических файлов.
@LarryBud: это метка времени файла, а не текущая метка времени. Кеширование обязательно будет.
Допустим, у вас есть файл по адресу:
/styles/screen.css
Вы можете либо добавить параметр запроса с информацией о версии в URI, например:
/styles/screen.css?v=1234
Или вы можете добавить информацию о версии, например:
/v/1234/styles/screen.css
IMHO, второй метод лучше подходит для файлов 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=1235 - это /styles/images/happy.gif, который не содержит никакой информации о версии.
Я реализовал решение кэширования, используя эту технику с сервлетами Java, и просто обрабатываю запросы к /v/* с помощью сервлета, который делегирует базовый ресурс (например, /styles/screen.css). В режиме разработки я устанавливаю заголовки кеширования, которые говорят клиенту всегда проверять актуальность ресурса с сервером (обычно это приводит к 304, если вы делегируете Tomcat DefaultServlet, а файл .css, .js и т. д. Не изменился), в то время как в режиме развертывания я устанавливаю заголовки с надписью «кэш навсегда».
Простое добавление папки, которую вы можете переименовать при необходимости, будет работать, если вы используете только относительные URL-адреса. И затем убедитесь, что вы перенаправили в нужную папку из базовой папки, то есть в PHP: <?php header( 'Location: folder1/login.phtml' ); ?>.
Используя второй метод, изменение CSS сделает недействительными кэшированные копии всех изображений, на которые есть ссылки с относительными URL-адресами, что может быть или нежелательно.
Предлагаю реализовать следующий процесс:
обновляйте свои файлы CSS и JavaScript при каждом развертывании. Что-то вроде: screen.1233.css (номер может быть вашей версией SVN, если вы используете систему управления версиями)
уменьшите их, чтобы оптимизировать время загрузки
Если вы используете jQuery, есть опция, называемая кешем, которая добавляет случайное число.
Я знаю это не полный ответ, но он может сэкономить вам время.
Мой способ сделать это - просто включить элемент ссылки на стороне сервера:
<!--#include virtual = "/includes/css-element.txt"-->
где содержимое css-element.txt
<link rel = "stylesheet" href = "mycss.css"/>
поэтому в тот день, когда вы захотите создать ссылку на my-new-css.css или что-то еще, вы просто измените include.
Интересный пост. Прочитав все ответы здесь в сочетании с тем фактом, что у меня никогда не было проблем с "поддельными" строками запроса (что я не уверен, почему все так неохотно используют это), я предполагаю решение (которое устраняет необходимость в правилах перезаписи Apache как в принятом ответе) заключается в вычислении короткого хэш содержимого файла CSS (вместо даты и времени файла) в качестве фиктивной строки запроса.
Это приведет к следующему:
<link rel = "stylesheet" href = "/css/base.css?[hash-here]" type = "text/css" />
Конечно, решения datetime также выполняют свою работу в случае редактирования файла CSS, но я думаю, что речь идет о содержимом файла CSS, а не о дате и времени файла, так зачем их путать?
RewriteRule нуждается в небольшом обновлении для файлов JavaScript или CSS, которые содержат в конце точечную нотацию с указанием версий. Например, json-1.3.js.
Я добавил к регулярному выражению класс отрицания точки [^.], Поэтому .number. игнорируется.
RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ . [L]
Спасибо за вклад! С тех пор, как я написал этот пост, меня это тоже обожгло. Мое решение заключалось в том, чтобы переписать только в том случае, если последняя часть имени файла содержит ровно десять цифр. (10 цифр охватывают все временные метки с 9/9/2001 по 20/11/2286.) Я обновил свой ответ, включив это регулярное выражение: ^(.*)\.[\d]{10}\.(css|js)$ .
Я понимаю регулярное выражение, но не понимаю, какую проблему вы здесь решаете с [^.]. Кроме того, нет смысла писать \d внутри символьного класса - \d+ будет делать то же самое. Как было опубликовано, ваш шаблон будет соответствовать любому количеству символов (жадно), затем буквальной точке, затем без точки, затем одной или нескольким цифрам, затем точке, затем css или js, затем к концу имени файла . Не соответствует вашему образцу ввода: regex101.com/r/RPGC62/1
Спасибо Кип за его идеальное решение!
Я расширил его, чтобы использовать как 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)$ . [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;
}
}
}
}
Плагин Google mod_pagespeed для Apache сделает за вас автоматическое изменение версий. Это действительно здорово.
Он анализирует HTML на выходе с веб-сервера (работает с PHP, Рубин на рельсах, Python, статическим HTML - чем угодно) и переписывает ссылки на CSS, JavaScript, файлы изображений, чтобы они включали код идентификатора. Он обслуживает файлы по измененным URL-адресам с очень длинным контролем над ними. Когда файлы меняются, он автоматически меняет URL-адреса, поэтому браузеру приходится их повторно загружать. В основном он просто работает без каких-либо изменений в вашем коде. Он даже уменьшит ваш код на выходе.
Это здорово, но все еще в стадии бета-тестирования. Можно ли его использовать для корпоративных услуг?
Это НЕПРАВИЛЬНО (автоматическое возня с источником), когда это явно проблема браузера. Дайте нам (разработчикам) настоящее обновление мозгов: <ctrl> + F5
mod_pagespeed функционально эквивалентен полностью автоматическому этапу сборки / компиляции для вашего html / css / js. Я думаю, вам будет сложно найти серьезных разработчиков, которые думают, что системы сборки ошибочны по своей сути или что нет ничего плохого в том, что они полностью автоматические. Аналогия с чистой сборкой - очистить кеш mod_pagespeed: code.google.com/p/modpagespeed/wiki/…?
@ T4NK3R mod_pagespeed не должен ничего делать с вашим источником для управления кешем, просто было упомянуто, что может помогает с такими вещами, как минимизация. Что касается того, «НЕПРАВИЛЬНО» это или нет, это полностью субъективно. Возможно, это неправильно для вас, но это не означает, что изначально это Плохо.
Однако это только перемещает проблему с клиента на сервер, поскольку, если агрегированные таблицы стилей обновлены, но кеш скорости страницы не установлен, пользователь получит устаревшие таблицы стилей.
ОЧЕНЬ ловко! В последней версии все ссылки css и js объединены в один лайнер с добавленным номером версии. Я выполнил эти инструкции для своего debian box, digitalocean.com/community/articles/…
Он также работает с nginx, хотя вам нужно собрать его из исходного кода: developers.google.com/speed/pagespeed/module/…
@ T4NK3R - не только мы, разработчики, используем веб-сайты. Итак, ctrl + F5 - не лучшее решение для обычного пользователя, и мы не хотим, чтобы он обычно видел старую версию.
Я помещаю MD5-хэш содержимого файла в его URL-адрес. Таким образом, я могу установить очень долгую дату истечения срока действия и не беспокоиться о том, что у пользователей есть старый JS или CSS.
Я также рассчитываю это один раз для каждого файла во время выполнения (или при изменении файловой системы), поэтому во время разработки или в процессе сборки делать нечего.
Если вы используете ASP.NET MVC, вы можете проверить код в моем другом ответе здесь.
Самый простой способ - воспользоваться функцией чтения файла 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, есть некоторые вариации в зависимости от языка, но почти во всех языках есть способ распечатать содержимое файла. Поместите его в нужное место (в раздел), и тогда вам не придется полагаться на браузер.
Проблема в том, что вы теряете возможность кэшировать файл, что замедляет работу пользователя.
Ответ Тома прав.
Использование метода "querystring" не будет кэшироваться, как указано в Стив Содерс ниже:
...that Squid, a popular proxy, doesn’t cache resources with a querystring.
Предложение TomA использования style.TIMESTAMP.css - это хорошо, но MD5 будет намного лучше, поскольку только когда содержимое было действительно изменено, MD5 также изменится.
Кроме того, использование метки времени в качестве параметра строки запроса будет приводить к перезагрузке файла каждый раз, что означает полное отсутствие кеширования.
В комментарии 2008 года к тому же сообщению в блоге упоминается, что настройки Squid по умолчанию изменились; вопрос в том, какой процент вашего трафика обрабатывается (сейчас) устаревшими версиями Squid.
Я вижу проблему с подходом к использованию дифференциатора на основе метки времени или хеша в URL-адресе ресурса, который удаляется по запросу на сервере. Страница, содержащая ссылку, например, на таблица стилей также может быть кешировано. Таким образом, кэшированная страница может запрашивать более старую версию таблицы стилей, но она будет обслуживать последнюю версию, которая может работать или не работать с запрашивающей страницей.
Чтобы исправить это, вам нужно либо защитить запрашивающую страницу заголовком no-cache, либо метаданными, чтобы убедиться, что она обновляется при каждой загрузке. Или вам нужно поддерживать все версии файла стиля, который вы когда-либо развертывали на сервере, каждый как отдельный файл и с неповрежденным отличительным признаком, чтобы запрашивающая страница могла получить версию файла стиля, для которой она была разработана. В последнем случае вы в основном связываете версии HTML-страницы и таблицы стилей вместе, что может быть выполнено статически и не требует какой-либо серверной логики.
Я не уверен, почему вы, ребята, так стараетесь реализовать это решение.
Все, что вам нужно сделать, это получить измененную временную метку файла и добавить ее в качестве строки запроса к файлу.
В PHP я бы сделал это так:
<link href = "mycss.css?v=<?= filemtime('mycss.css') ?>" rel = "stylesheet">
filemtime () - это функция PHP, которая возвращает временную метку измененного файла.
Вы можете просто использовать mycss.css?1234567890.
очень элегантно, хотя я немного изменил его на <link rel = "stylesheet" href = "mycss.css?<?php echo filemtime('mycss.css') ?>"/>, на всякий случай некоторые аргументы в этом потоке о кешировании URL-адресов с помощью переменных GET (в предложенном формате) верны
В дополнение к моему последнему комментарию, я видел, что wordpress использует ?ver=, так что кто знает!
Отличное решение. Вдобавок я обнаружил, что 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')?>" />
Большое спасибо. Просто и хорошо. Вот он в Python: progpath = os.path.dirname (sys.argv [0]) def versionize (file): 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')
В этом подходе есть несколько ошибок. Во-первых, это полностью исключает кеширование этого файла. Вопрос требовал принудительного обновления актива, КОГДА ОНО ИЗМЕНЕНО, не предотвращая полностью кеширование (что в целом является ОЧЕНЬ плохой идеей). Во-вторых, строки запроса к статическим файлам - плохая идея, поскольку некоторые браузеры не кэшируют их вообще, другие кешируют их независимо от того, что это за строка запроса. В целом, это очень младшее решение, и вопрос, почему люди обсуждали правильное решение (в отличие от взлома), просто показывает общее непонимание этого вопроса.
Совершенно идеальное быстрое решение.
"Another idea which was suggested by SCdF would be to append a bogus query string to the file. (Some Python code to automatically use the timestamp as a bogus query string was submitted by pi.) However, there is some discussion as to whether or not the browser would cache a file with a query string. (Remember, we want the browser to cache the file and use it on future visits. We only want it to fetch the file again when it has changed.) Since it is not clear what happens with a bogus query string, I am not accepting that answer."
<link rel = "stylesheet" href = "file.css?<?=hash_hmac('sha1', session_id(), md5_file("file.css")); ?>" />
Хеширование файла означает, что после его изменения изменится и строка запроса. В противном случае он останется прежним. Каждый сеанс также вызывает перезагрузку.
При желании вы также можете использовать перезапись, чтобы браузер решил, что это новый URI.
Для среды Сервлет Java вы можете посмотреть Библиотека Jawr. На странице функций объясняется, как он обрабатывает кеширование:
Jawr will try its best to force your clients to cache the resources. If a browser asks if a file changed, a 304 (not modified) header is sent back with no content. On the other hand, with Jawr you will be 100% sure that new versions of your bundles are downloaded by all clients. Every URL to your resources will include an automatically generated, content-based prefix that changes automatically whenever a resource is updated. Once you deploy a new version, the URL to the bundle will change as well so it will be impossible that a client uses an older, cached version.
Библиотека также выполняет минификацию JavaScript и CSS, но вы можете отключить это, если не хотите.
Вы можете просто добавить случайное число с URL-адресом CSS и JavaScript, например
example.css?randomNo = Math.random()
Для ASP.NET я предлагаю следующее решение с расширенными параметрами (режим отладки / выпуска, версии):
Включите файлы JavaScript или 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();
}
...
}
Другой способ для файлов JavaScript - использовать jQuery $.getScript вместе с опцией $.ajaxSetupcache: false.
Вместо:
<script src = "scripts/app.js"></script>
Вы можете использовать:
$.ajaxSetup({
cache: false
});
$.getScript('scripts/app.js'); // GET scripts/app.js?_1391722802668
идея состоит не в том, чтобы полностью предотвратить кеширование. вы просто хотите, чтобы пользователь получил последний файл, как только файл будет изменен.
Еще одно предложение для веб-сайтов ASP.NET,
Установите разные значения cache-control:max-age для разных статических файлов.
Для файлов CSS и JavaScript вероятность изменения этих файлов на сервере высока, поэтому установите минимальное значение cache-control: максимальный возраст, равное 1 или 2 минутам, или что-то еще, что соответствует вашим потребностям.
Для изображений установите дальнюю дату как значение cache-control: максимальный возраст, скажем, 360 дней.
Таким образом, когда мы делаем первый запрос, все статическое содержимое загружается на клиентскую машину с ответом 200-ОК.
При последующих запросах и через две минуты мы видим запросы 304-Не изменено в файлах CSS и JavaScript, что позволяет нам избежать управления версиями CSS и JavaScript.
Файлы изображений не будут запрашиваться, поскольку они будут использоваться из кэшированной памяти до истечения срока действия кеша.
Используя приведенные ниже конфигурации web.config, мы можем добиться описанного выше поведения,
<system.webServer>
<modules runAllManagedModulesForAllRequests = "true"/>
<staticContent>
<clientCache cacheControlMode = "UseMaxAge" cacheControlMaxAge = "00.00:01:00"/>
</staticContent>
<httpProtocol>
<customHeaders>
<add name = "ETAG" value = ""/>
</customHeaders>
</httpProtocol>
</system.webServer>
<location path = "Images">
<system.webServer>
<staticContent>
<clientCache cacheControlMode = "UseMaxAge" cacheControlMaxAge = "180.00:00:00" />
</staticContent>
</system.webServer>
</location>
Если вы используете современный браузер, вы можете использовать файл манифеста, чтобы сообщить браузерам, какие файлы необходимо обновить. Для этого не нужны заголовки, версии в URL-адресах и т. д.
Подробнее см .: Использование кеша приложения
Для ASP.NET 4.5 и выше вы можете использовать пакетирование скриптов.
The request
http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81is for the bundle AllMyScripts and contains a query string pair v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. The query stringvhas a value token that is a unique identifier used for caching. As long as the bundle doesn't change, the ASP.NET application will request the AllMyScripts bundle using this token. If any file in the bundle changes, the ASP.NET optimization framework will generate a new token, guaranteeing that browser requests for the bundle will get the latest bundle.
Объединение дает и другие преимущества, в том числе повышенную производительность при первой загрузке страницы с минификацией.
Пожалуйста, помогите мне, я не делаю никаких изменений в bundle.config, просто меняю файлы css или js, тогда как я могу решить проблему кеширования?
Многие ответы здесь рекомендуют добавить метку времени к URL-адресу. Если вы не изменяете производственные файлы напрямую, временная метка файла вряд ли будет отражать время, когда файл был изменен. В большинстве случаев это приведет к тому, что URL-адрес будет изменяться чаще, чем сам файл. Вот почему вы должны использовать быстрое хеширование содержимого файла, такое как MD5, как предлагает левик и другие.
Имейте в виду, что значение следует рассчитывать один раз при сборке или запуске, а не каждый раз при запросе файла.
В качестве примера приведем простой сценарий bash, который считывает список имен файлов из стандартный ввод и записывает файл JSON, содержащий хэши, в стандартный вывод:
#!/bin/bash
# Create a JSON map from filenames to MD5 hashes
# Run as hashes.sh < inputfile.list > outputfile.json
echo "{"
delim = ""
while read l; do
echo "$delim\"$l\": \"`md5 -q $l`\""
delim = ","
done
echo "}"
Затем этот файл можно было бы загрузить при запуске сервера и ссылаться на него вместо чтения файловой системы.
Вот чистое решение JavaScript
(function(){
// Match this timestamp with the release of your code
var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);
var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');
if (lastCacheDateTime){
if (lastVersioning > lastCacheDateTime){
var reload = true;
}
}
localStorage.setItem('lastCacheDatetime', Date.now());
if (reload){
location.reload(true);
}
})();
Вышеупомянутое будет искать последний раз, когда пользователь посещал ваш сайт. Если последнее посещение было до того, как вы выпустили новый код, он использует location.reload(true) для принудительного обновления страницы с сервера.
Обычно у меня это самый первый скрипт в <head>, поэтому он проверяется перед загрузкой любого другого контента. Если потребуется перезагрузка, она вряд ли заметна пользователю.
Я использую локальное хранилище для хранения отметки времени последнего посещения в браузере, но вы можете добавить файлы cookie в смесь, если хотите поддерживать более старые версии IE.
Я пробовал что-то подобное, это будет работать только на перезагруженной странице, но если на сайте есть несколько страниц, использующих одни и те же css / изображения, другие страницы по-прежнему будут использовать старые ресурсы.
Ответ, относящийся к SilverStripe, получен из чтения: http://api.silverstripe.org/3.0/source-class-SS_Datetime.html#98-110:
Надеюсь, это поможет кому-то, кто использует шаблон SilverStripe и пытается принудительно перезагружать кэшированное изображение при каждом посещении / обновлении страницы. В моем случае это GIF-анимация, которая воспроизводится только один раз и поэтому не воспроизводится после кэширования. В моем шаблоне я просто добавил:
?$Now.Format(dmYHis)
до конца пути к файлу, чтобы создать уникальную отметку времени и заставить браузер рассматривать его как новый файл.
Ссылка api.silverstripe.org не работает (404).
Что касается моей разработки, я считаю, что Chrome предлагает отличное решение.
https://superuser.com/a/512833
Когда инструменты разработчика открыты, просто нажмите и удерживайте кнопку обновления и отпустите, когда наведете курсор на «Очистить кеш и выполнить полную перезагрузку».
Это мой лучший друг, и это очень легкий способ получить то, что вы хотите!
И если вы используете Chrome в качестве среды разработки, другим неинвазивным решением является отключение кеша: в настройках cog вы можете сделать недействительным дисковый кеш, выбрав `` Отключить кеш '' (примечание: DevTools должны быть видимыми / открытыми чтобы это работало).
Что такое "длинный щелчок"?
Ссылка (фактически) не работает. Он перенаправляет на общую страницу "Chrome DevTools" - developers.google.com/web/tools/chrome-devtools
@PeterMortensen Когда вы нажимаете и удерживаете кнопку щелчка.
30 или около того существующих ответов - отличный совет для сайта примерно 2008 года. Однако, когда дело доходит до современного одностраничное приложение (SPA), возможно, пришло время переосмыслить некоторые фундаментальные предположения… в частности, идею о том, что веб-сервер желательно обслуживать только одну, самую последнюю версию файла.
Представьте, что вы пользователь, у которого в ваш браузер загружена версия M SPA:
/some.template./some.template - вы хотите, чтобы он возвращал версию M или N шаблона?Если формат /some.template изменился между версиями M и N (или файл был переименован или что-то еще) вы, вероятно, не хотите, чтобы версия N шаблона отправлялась в браузер, в котором запущена старая версия M парсера. †
Веб-приложения сталкиваются с этой проблемой при соблюдении двух условий:
Если ваше приложение должно обслуживать несколько версий параллельно, решение кеширования и «перезагрузки» становится тривиальным:
/v<release_tag_1>/…files…, /v<release_tag_2>/…files…<script> и <link> и т. д., Чтобы они указывали на этот файл в одном из каталогов с поддержкой версий.Этот последний шаг звучит сложно, так как может потребоваться вызов построителя URL-адресов для каждого URL-адреса в коде на стороне сервера или на стороне клиента. Или вы можете просто разумно использовать Тег <base> и изменить текущую версию в одном месте.
† Один из способов обойти это - настойчиво заставлять браузер перезагружать все при выпуске новой версии. Но для того, чтобы завершить выполнение любых незавершенных операций, проще всего поддерживать как минимум две версии параллельно: v-current и v-previous.
Михаил - ваш комментарий очень актуален. Я здесь как раз пытаюсь найти решение для своего СПА. У меня есть подсказки, но решение пришлось искать самому. В конце концов, я был очень доволен тем, что придумал, поэтому написал сообщение в блоге и ответ на этот вопрос (включая код). Спасибо за указатели
Замечательный комментарий. Я не могу понять, в то время как люди продолжают говорить о блокировке кеша и HTTP-кешировании как о реальном решении проблем с кешированием веб-сайтов, не комментируя новые проблемы SPA, как если бы это был крайний случай.
Отличный ответ и абсолютно идеальная стратегия! И бонусные баллы за упоминание тега base! Что касается поддержки старого кода: это не всегда возможность и не всегда хорошая идея. Новые версии кода могут поддерживать критические изменения в других частях приложения или могут включать аварийные исправления, исправления уязвимостей и т. д. Мне еще предстоит реализовать эту стратегию, но я всегда чувствовал, что общая архитектура должна позволять развертываниям помечать старую версию как obsolete и принудительно перезагружать ее при следующем асинхронном вызове (или просто принудительно деаутентифицировать все сеансы через WebSockets).
Приятно видеть хорошо продуманный ответ в отношении одностраничных приложений.
Это «сине-зеленое развертывание», если вы хотите найти дополнительную информацию.
Я пришел к этому вопросу, когда искал решение для своего SPA, в котором есть только один файл index.html, в котором перечислены все необходимые файлы. Хотя у меня было несколько потенциальных клиентов, которые мне помогли, я не мог найти быстрого и легкого решения.
В конце концов, я написал быструю страницу (включая весь код), необходимую для автоматической версии файла HTML / JavaScript index.html в рамках процесса публикации. Он работает отлично и обновляет новые файлы только в зависимости от даты последнего изменения.
Вы можете увидеть мой пост на Автоверсия вашего SPA index.html. Там же есть отдельное приложение для Windows.
Внутренняя часть кода:
private void ParseIndex(string inFile, string addPath, string outFile)
{
string path = Path.GetDirectoryName(inFile);
HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
document.Load(inFile);
foreach (HtmlNode link in document.DocumentNode.Descendants("script"))
{
if (link.Attributes["src"]!=null)
{
resetQueryString(path, addPath, link, "src");
}
}
foreach (HtmlNode link in document.DocumentNode.Descendants("link"))
{
if (link.Attributes["href"] != null && link.Attributes["type"] != null)
{
if (link.Attributes["type"].Value == "text/css" || link.Attributes["type"].Value == "text/html")
{
resetQueryString(path, addPath, link, "href");
}
}
}
document.Save(outFile);
MessageBox.Show("Your file has been processed.", "Autoversion complete");
}
private void resetQueryString(string path, string addPath, HtmlNode link, string attrType)
{
string currFileName = link.Attributes[attrType].Value;
string uripath = currFileName;
if (currFileName.Contains('?'))
uripath = currFileName.Substring(0, currFileName.IndexOf('?'));
string baseFile = Path.Combine(path, uripath);
if (!File.Exists(baseFile))
baseFile = Path.Combine(addPath, uripath);
if (!File.Exists(baseFile))
return;
DateTime lastModified = System.IO.File.GetLastWriteTime(baseFile);
link.Attributes[attrType].Value = uripath + "?v = " + lastModified.ToString("yyyyMMddhhmm");
}
Я не нашел подход к DOM на стороне клиента, динамически создающий элемент скрипта (или CSS):
<script>
var node = document.createElement("script");
node.type = "text/javascript";
node.src = 'test.js?' + Math.floor(Math.random()*999999999);
document.getElementsByTagName("head")[0].appendChild(node);
</script>
Какой имеют вы тогда нашли? Вы можете прояснить это? Желательно редактирование вашего ответа (но без "Edit:", "Update:" или подобное), а не здесь, в комментариях.
Кажется, все ответы здесь предполагают какое-то управление версиями в схеме именования, что имеет свои недостатки.
Браузеры должны хорошо знать, что кэшировать, а что нет, читая ответ веб-сервера, в частности заголовки HTTP - как долго этот ресурс действителен? Обновлялся ли этот ресурс с момента последнего его получения? и Т. Д.
Если все настроено «правильно», простое обновление файлов вашего приложения должно (в какой-то момент) обновить кеши браузера. Вы можете, например, настроить свой веб-сервер, чтобы браузер никогда не кэшировал файлы (что является плохой идеей).
Более подробное объяснение того, как это работает, находится в Как работают веб-кеши.
Что ж, я заставил это работать по-своему, изменяя версию файла JavaScript каждый раз при загрузке страницы, добавляя случайное число в версию файла JavaScript следующим образом:
// Add it to the top of the page
<?php
srand();
$random_number = rand();
?>
Затем примените случайное число к версии JavaScript следующим образом:
<script src = "file.js?version=<?php echo $random_number;?>"></script>
Это очень плохая идея. Это означает, что пользователь должен повторно загружать файл при каждой загрузке страницы. Кеширование - это хорошо - этот вопрос о том, как использовать кеширование, когда вы хотите, но не когда вы этого не сделаете.
Да, пользователь должен повторно загружать файл при каждой загрузке страницы, потому что некоторый java-скрипт, включенный в (iframe), не обновляет содержимое страницы, если пользователь не нажмет F5 или не перезагрузит страницу вручную. Так что это лучшее решение для перезагрузки контента каждый раз, когда новый посетитель посещает веб-сайт.
Если вы используете Git и PHP, вы можете перезагружать скрипт из кеша каждый раз, когда в репозитории Git происходят изменения, используя следующий код:
exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo ' <script src = "/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;
В Laravel (PHP) мы можем сделать это следующим ясным и элегантным способом (используя временную метку модификации файла):
<script src = "{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>
И аналогично для CSS
<link rel = "stylesheet" href = "{{asset('css/your.css?v='.filemtime('css/your.css'))}}">
Пример вывода HTML (время возврата filemtime как Отметка времени Unix)
<link rel = "stylesheet" href = "assets/css/your.css?v=1577772366">
что выводит эта команда в html? А что, если мне нужно обновить только такие версии, как? V = 3,? V = 4 и т. д. - не заставляет браузер загружать css каждый раз, когда пользователь входит на сайт
Просто добавьте этот код туда, где вы хотите выполнить полную перезагрузку (заставьте браузер перезагрузить кешированные файлы CSS и JavaScript):
$(window).load(function() {
location.reload(true);
});
Сделайте это внутри .load, чтобы он не обновлялся как цикл.
Не работает в Chrome. Все еще загружает ресурсы из кеша диска
В Google Chrome есть параметр Жесткая перезагрузка, а также параметр Очистить кеш и выполнить полную перезагрузку. Вы можете нажать и удерживать кнопку перезагрузки (в Режим проверки), чтобы выбрать один.
Чтобы уточнить, под «Режимом проверки» они ссылаются на «Инструменты разработчика», также известные как F12, они же ctrl + shift + i, также известные как ant menu> More Tools> Developer Tools, aka right click> Inspect Element. Также есть настройка, спрятанная где-то в инструментах разработчика (я забыл местоположение), чтобы жестко перезагружать при каждой перезагрузке.
Отключите кеширование script.jsтолько для местного развития в чистом JavaScript.
Он вводит случайный script.js? wizardry = 1231234 и блокирует обычный script.js:
<script type = "text/javascript">
if (document.location.href.indexOf('localhost') !== -1) {
const scr = document.createElement('script');
document.setAttribute('type', 'text/javascript');
document.setAttribute('src', 'scripts.js' + '?wizardry=' + Math.random());
document.head.appendChild(scr);
document.write('<script type = "application/x-suppress">'); // prevent next script(from other SO answer)
}
</script>
<script type = "text/javascript" src = "scripts.js">
Просто используйте серверный код, чтобы добавить дату файла ... таким образом, воля будет кэшироваться и перезагружаться только при изменении файла.
В ASP.NET:
<link rel = "stylesheet" href = "~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />
<script type = "text/javascript" src = "~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>
Это можно упростить до:
<script src = "<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type = "text/javascript"></script>
Добавив в проект метод расширения для расширения Страница:
public static class Extension_Methods
{
public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
{
string sFilePath = oPg.Server.MapPath(sRelPath);
string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");
return oPg.ResolveClientUrl(sRelPath) + "?d = " + sDateHashed;
}
}
Небольшое улучшение по сравнению с существующими ответами ...
Использование случайного числа или идентификатора сеанса приведет к его перезагрузке при каждом запросе. В идеале нам может потребоваться внести изменения, только если некоторые изменения кода были внесены в какой-либо файл JavaScript или CSS.
При использовании обычного файла JSP в качестве шаблона для многих других файлов JSP и JavaScript добавьте приведенный ниже код в общий файл JSP.
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core"%>
<c:set var = "version" scope = "application" value = "1.0.0" />
Теперь используйте указанную выше переменную во всех местах, как показано ниже, во включенных файлах JavaScript.
<script src='<spring:url value = "/js/myChangedFile.js?version=${version}"/>'></script>
Преимущества:
- This approach will help you in changing version number at one location only.
- Maintaining a proper version number (usually build/release number) will help you to check/verify your code changes being deployed properly (from developer console of the browser).
Еще один полезный совет:
Если вы используете браузер Chrome, вы можете отключить кеширование, когда Инструменты разработчика открыт. В Chrome нажмите F12 → F1 и прокрутите до Настройки → Предпочтения → Сеть → * Отключить кеширование (пока DevTools открыт)
Один из лучших и самых быстрых подходов, который я знаю, - это изменить имя папки, в которой находятся файлы CSS или JavaScript.
Или для разработчиков: измените имена ваших файлов CSS и JavaScript, например, на версии.
<link rel = "stylesheet" href = "cssfolder/somecssfile-ver-1.css"/>
Сделайте то же самое для своих файлов JavaScript.
Вы можете использовать НИИ, чтобы сломать кеш браузера. Вам нужно только каждый раз обновлять файл index.html новым хешем SRI. Когда браузер загружает HTML и обнаруживает, что хэш SRI на странице HTML не совпадает с хешем кэшированной версии ресурса, он перезагрузит ваш ресурс с ваших серверов. У этого также есть хороший побочный эффект - обход блокировки чтения из разных источников.
<script src = "https://jessietessie.github.io/google-translate-token-generator/google_translate_token_generator.js" integrity = "sha384-muTMBCWlaLhgTXLmflAEQVaaGwxYe1DYIf2fGdRkaAQeb4Usma/kqRWFWErr2BSi" crossorigin = "anonymous"></script>
Какие браузеры, в т. версии, поддерживаете это? Ответьте, обновив свой ответ (не здесь, в комментариях).
Для разработки: используйте настройку браузера: например, Chromenetwork tab имеет параметр disable cache.
Для производства: добавьте к запросу уникальный параметр запроса (Например, q?Date.now()) с серверной структурой рендеринга или чистым кодом JavaScript.
// Pure JavaScript unique query parameter generation
//
//=== myfile.js
function hello() { console.info('hello') };
//=== end of file
<script type = "text/javascript">
document.write('<script type = "text/javascript" src = "myfile.js?q=' + Date.now() + '">
// document.write is considered bad practice!
// We can't use hello() yet
</script>')
<script type = "text/javascript">
hello();
</script>
Этот пример требует редактирования. Идея хороша, но есть путаница с тегами начала и конца сценария в приведенном выше.
Для разработчиков, у которых возникла эта проблема при разработке и тестировании:
На короткое время удалите кеширование.
"keep caching consistent with the file" .. это слишком много хлопот ..
Вообще говоря, я не против загрузки еще - даже повторная загрузка файлов, которые не изменились - в большинстве проектов - практически не имеет значения. При разработке приложения - мы в основном загружаемся с диска на localhost:port - поэтому эта проблема increase in network traffic - не проблема срыва сделки.
Большинство небольших проектов просто играют - они никогда не заканчиваются в производстве. Так что для них больше ничего не нужно ...
Таким образом, если вы используете Chrome DevTools, вы можете следовать этому подходу с отключением кеширования, как показано на изображении ниже:
И если у вас есть проблемы с кешированием Fire Fox:
Делайте это только в разработке. Вам также понадобится механизм принудительной перезагрузки для производства, поскольку ваши пользователи будут использовать старые недействительные модули кеша, если вы часто обновляете свое приложение и не предоставляете специальный механизм синхронизации кеша, подобный тем, которые описаны в ответах выше.
Да, эта информация уже есть в предыдущих ответах, но мне все равно нужно было выполнить поиск в Google, чтобы найти ее.
ОП что-то спросил и что-то ответил. Речь идет не о принудительной загрузке локально, а в производственной среде, и вы не можете просить конечных пользователей следовать выше, чтобы отключить кеш и т. д.
У нас есть одно решение с другим способом реализации. Мы используем для этого вышеуказанное решение.
datatables?v=1
Мы можем обработать версию файла. Это означает, что каждый раз, когда мы меняем наш файл, меняем и его версию. Но это неподходящий способ.
Другой способ использовал GUID. Это тоже не подходило, потому что каждый раз извлекает файл и не использует его из кеша браузера.
datatables?v=Guid.NewGuid()
Последний способ, который является наилучшим:
Когда происходит изменение файла, измените и версию. Проверьте следующий код:
<script src = "~/scripts/[email protected](Server.MapPath("/scripts/main.js")).ToString("yyyyMMddHHmmss")"></script>
Таким образом, когда вы изменяете файл, LastWriteTime также изменяется, поэтому версия файла изменится, и в следующий раз, когда вы откроете браузер, он обнаружит новый файл и загрузит его.
Простое решение для статических файлов (только для целей разработки), которое добавляет случайный номер версии к URI сценария, используя инъекции тегов сценария.
<script>
var script = document.createElement('script');
script.src = "js/app.js?v = " + Math.random();
document.getElementsByTagName('head')[0].appendChild(script);
</script>
Вот мое решение для очистки кеша на основе сценария Bash:
Пример записей index.html для .js и .css с отметкой времени:
<link rel = "stylesheet" href = "bla_bla.css?v=my_timestamp">
<script src = "scripts/bla_bla.js?v=my_timestamp"></script>
Файл timestamp.txt должен содержать только ту же временную метку my_timestamp (будет найден и заменен скриптом позже)
Наконец, вот сценарий (назовем его cache_buster.sh: D)
old_timestamp=$(cat timestamp.txt)
current_timestamp=$(date +%s)
sed -i -e "s/$old_timestamp/$current_timestamp/g" index.html
echo "$current_timestamp" >timestamp.txt
(Пользователи Visual Studio Code) вы можете поместить этот скрипт в ловушку, чтобы он вызывал каждый раз, когда файл сохраняется в вашей рабочей области.
Я решил эту проблему, используя ETag:
ETag or entity tag is part of HTTP, the protocol for the World Wide Web. It is one of several mechanisms that HTTP provides for Web cache validation, which allows a client to make conditional requests. This allows caches to be more efficient and saves bandwidth, as a Web server does not need to send a full response if the content has not changed. ETags can also be used for optimistic concurrency control,1 as a way to help prevent simultaneous updates of a resource from overwriting each other.
Не могли бы вы немного уточнить? Сейчас это фактически ответ только по ссылке.
Я обновил свой ответ
В ASP.NET Core этого можно добиться, добавив asp-append-version:
<link rel = "stylesheet" href = "~/css/xxx.css" asp-append-version = "true" />
<script src = "~/js/xxx.js" asp-append-version = "true"></script>
Он сгенерирует HTML:
<link rel = "stylesheet" href = "/css/xxx.css?v=rwgRWCjxemznsx7wgNx5PbMO1EictA4Dd0SjiW0S90g" />
Платформа будет генерировать новый номер версии каждый раз, когда вы обновляете файл.
У меня есть это в моем .htaccess, и никогда не было проблем с кешированными файлами:
ExpiresActive On ExpiresDefault "modification".