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

895

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

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

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

Update:

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

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

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

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

  • 0
    У меня есть это в моем .htaccess, и никогда не было никаких проблем с кэшированными файлами: ExpiresActive On ExpiresDefault "modification" .
  • 2
    Я определенно согласен с тем, что добавление информации о версии к URL-адресу файла - безусловно, лучший путь. Это работает, все время, для всех. Но, если вы не используете его, и вам просто нужно время от времени перезагружать этот файл CSS или JS в вашем собственном браузере ... просто откройте его на своей вкладке и нажмите SHIFT-reload (или CTRL-F5)! Вы можете эффективно сделать то же самое, используя JS, загрузив файл в (скрытый) iframe, подождав, пока он загрузится, и затем вызвав iframe.contentWindow.location.reload(true) . См. Метод (4) stackoverflow.com/a/22429796/999120 - это касается изображений, но то же самое относится.
Показать ещё 1 комментарий
Теги:
caching

47 ответов

402

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

Обновить 2: Включение комментариев от Nick Johnson, что исходное регулярное выражение .htaccess может вызвать проблемы с файлами типа json-1.3.js. Решение состоит только в переписывании, если в конце есть ровно 10 цифр. (Поскольку 10 цифр охватывают все временные метки с 9/9/2001 по 11/20/2286.)

Во-первых, мы используем следующее правило перезаписи в .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 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. В принципе все, что не динамически генерируется.

  • 15
    Мой собственный статический контент-сервер делает то же самое, за исключением того, что я использую параметр для управления версиями (base.css? V = 1221534296) вместо изменения имени файла (base.1221534296.css). Я подозреваю, что ваш путь может быть немного более эффективным, хотя. Очень круто.
  • 0
    Отличный ответ. Интересно, можно ли это использовать, когда статические файлы находятся на других серверах. Разве не рекомендуется хранить статические файлы на других серверах?
Показать ещё 30 комментариев
180

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

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

Общие посетители вашего сайта не будут иметь тот же опыт, который у вас есть при разработке сайта. Поскольку средний посетитель приходит на сайт реже (может быть, только несколько раз в месяц, если вы не являетесь сетью 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, чтобы решить, следует ли ее использовать.

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

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

  • 3
    Быстрый прием генерации строки запроса с помощью Javascript прекрасно работает во время активной разработки. Я сделал то же самое с PHP.
  • 2
    Это самый простой способ достичь желаемого результата оригинального плаката. Метод mod_rewrite хорошо работает, если вы хотите принудительно перезагрузить файл .css или .js КАЖДЫЙ раз, когда вы загружаете страницу. Этот метод все еще позволяет кэшировать, пока вы действительно не измените файл и не захотите принудительно перезагрузить его.
Показать ещё 9 комментариев
111

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

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

  • 1
    Это здорово, но все еще в бета-версии. Можно ли использовать его для обслуживания предприятия?
  • 25
    Это НЕПРАВИЛЬНО (автоматическое переключение с источником), когда это явно проблема браузера. Дайте нам (разработчикам) реальное обновление мозгов: <ctrl> + F5
Показать ещё 7 комментариев
94

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

Таким образом, ваш URL-адрес будет похож на

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

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

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

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

  • 1
    к сожалению я не знаю как это реализовать. Совет пожалуйста ... подробнее ...
  • 0
    Реализация в shell, ruby и т. Д. Была бы отличной
Показать ещё 2 комментария
55

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

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

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

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

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

  • 0
    Вы можете просто использовать mycss.css?1234567890 .
  • 2
    очень элегантно, хотя я немного изменил его на <link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/> , на всякий случай некоторые аргументы на эта ветка о кешировании URL с помощью переменных GET (в предложенном формате) является правильной
Показать ещё 3 комментария
51

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

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


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

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

  • 0
    Ерунда. Строка запроса (также называемая параметрами GET) является частью URL. Они могут и будут кешироваться. Это хорошее решение.
  • 9
    @troelskn: спецификация HTTP 1.1 говорит об обратном (в отношении запросов GET и HEAD с параметрами запроса): кэши НЕ ДОЛЖНЫ обрабатывать ответы на такие URI как свежие, если только сервер не предоставит явное время истечения. См. W3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Показать ещё 6 комментариев
38

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

См. также:

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

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

Представьте, что вы являетесь пользователем, который имеет версию M SPA, загруженную в ваш браузер:

  • Консоль вашего компакт-диска развертывает новую версию N приложения на сервере
  • Вы перемещаетесь в SPA, который отправляет XHR на сервер, чтобы получить /some.template
    • (Ваш браузер не обновил страницу, поэтому вы все еще используете версию M)
  • Сервер отвечает содержимым /some.template - хотите ли вы вернуть версию M или N шаблона?

Если формат /some.template изменен между версиями M и N (или файл был переименован или что-то еще) , вы, вероятно, не хотите, чтобы версия N шаблона, отправленного в браузер, выполняла старую версию M анализатор. †

Веб-приложения сталкиваются с этой проблемой, когда выполняются два условия:

  • Ресурсы запрашиваются асинхронно после загрузки начальной страницы
  • Логика приложения предполагает вещи (которые могут измениться в будущих версиях) о содержании ресурсов

Как только ваше приложение должно обслуживать несколько версий параллельно, решение кэширования и "перезагрузка" становится тривиальным:

  • Установите все файлы сайта в версии dir: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
  • Установить заголовки HTTP, чтобы браузеры кэшировали файлы навсегда
    • (Или еще лучше, поместите все в CDN)
  • Обновить все теги <script> и <link> и т.д., чтобы указать на этот файл в одном из версий dirs

Этот последний шаг звучит сложно, так как может потребоваться вызывать построитель URL-адресов для каждого URL-адреса в вашем серверном или клиентском коде. Или вы можете просто умело использовать тег <base> и изменить текущую версию в одном месте.

† Один из способов - агрессивность в том, чтобы заставить браузер перезагружать все, когда выпущена новая версия. Но для того, чтобы завершить выполнение каких-либо операций, все же проще всего поддерживать по меньшей мере две версии параллельно: v-current и v-previous.

  • 0
    Майкл - твой комментарий очень актуален. Я пытаюсь найти решение для своего SPA. Я получил несколько указателей, но сам должен был найти решение. В конце концов, я был очень доволен тем, что придумал, поэтому я написал пост в блоге и ответил на этот вопрос (включая код). Спасибо за указатели
  • 0
    Отличный комментарий. Я не могу понять, в то время как люди продолжают говорить о разрушении кэша и HTTP-кэшировании как реальном решении проблем кэширования веб-сайтов, не комментируя новые проблемы SPA, как если бы это был незначительный случай.
Показать ещё 1 комментарий
14

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

  • 1
    Во-первых, браузеры не кэшируют, это функция HTTP. Почему http заботится о структуре URI? Есть ли официальная ссылка на спецификацию, в которой говорится, что кэширование HTTP должно понимать семантику URI, чтобы не кэшировать элементы со строкой запроса?
  • 12
    Веб-браузер, который включает в себя функции кэширования объектов (проверьте каталог кэша вашего браузера). HTTP - это протокол, включающий директивы от серверов к клиентам (прокси, браузеры, пауки и т. Д.), Предлагающий управление кэшем.
10

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

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

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

  • 0
    Пожалуйста, помогите мне, я не делаю никаких изменений в bundle.config, просто меняю в файлах css или js, тогда как я могу решить проблему с кэшированием?
9

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

Я добавил класс повторения точки [^.] в regex так .number. игнорируется.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
  • 1
    Спасибо за вклад! С тех пор, как я написал этот пост, я тоже был сожжен этим. Моим решением было переписать только если последняя часть имени файла содержит ровно десять цифр. (10 цифр охватывают все метки времени с 9.09.2001 по 20.11.22.) Я обновил свой ответ, добавив в него следующее регулярное выражение: ^(.*)\.[\d]{10}\.(css|js)$ $1.$2
8

В 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'))}}">
  • 0
    что вывод этой команды в HTML? А что, если мне нужно обновить только такие версии, как? V = 3,? V = 4 и т. Д. - Не заставляет браузер загружать css каждый раз, когда пользователь заходит на сайт
  • 0
    filemtime : «Эта функция возвращает время, когда блоки данных файла были записаны, то есть время, когда содержимое файла было изменено». src: php.net/manual/en/function.filemtime.php
8

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

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

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

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

7

Вот чистое решение для 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) для принудительного обновления страницы с сервера.

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

Я использую локальное хранилище для хранения последней временной отметки посещения в браузере, но вы можете добавлять файлы cookie в микс, если вы хотите поддерживать более старые версии IE.

  • 0
    Я пробовал что-то вроде этого, это будет работать только на перезагруженной странице, но если на сайте есть несколько страниц с одинаковыми css / изображениями, то другие страницы будут по-прежнему использовать старые ресурсы.
6

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

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

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

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip 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;
            }
        }
    }

}

Приветствия и благодарности.

5

Не удалось найти сторонний подход DOM для клиента, создав динамический элемент script node (или 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>
5

Для 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

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

example.css?randomNo=Math.random()
5

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

/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/etc. это означает, что будут замечены изменения изображений, упомянутых в файлах CSS. Это не гарантируется первой схемой, т.е. URL images/happy.gif относительно /styles/screen.css?v=1235 является /styles/images/happy.gif, который не содержит информации о версии.

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

  • 0
    Простое добавление папки, которую вы можете переименовать при необходимости, будет работать, если вы используете только относительные URL-адреса. А затем убедитесь, что вы перенаправили на нужную папку из базовой папки, то есть в PHP: <?php header( 'Location: folder1/login.phtml' ); ?>
  • 1
    Используя второй метод, изменение CSS приведет к аннулированию кэшированных копий всех изображений, на которые есть ссылки с относительными URL-адресами, что может быть или не быть желательным.
5

Вы можете принудительно выполнить "кеширование по всему сеансу", если вы добавите идентификатор сеанса в качестве аргумента sys файла в jQuery:

<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>
4

Google Chrome имеет Hard Reload, а также параметр Пустой кэш и жесткий перезапуск. Вы можете нажать и удерживать кнопку перезагрузки (в режиме проверки), чтобы выбрать ее.

  • 0
    Чтобы уточнить, в «Режиме проверки» они ссылаются на «Инструменты разработчика», также называемые F12, также называемые Ctrl + Shift + I, также называемые ant menu > More Tools > Developer Tools , также называемые right click > Inspect Element . Также есть настройка, скрытая где-то в инструментах разработчика (я забыл местоположение), чтобы перезагрузить ее при каждой перезагрузке.
4

Для моего развития я считаю, что хром имеет отличное решение.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

С открытыми инструментами разработчика просто нажмите кнопку обновления и отпустите, как только вы наведите указатель мыши на "Empty Cache and Hard Reload".

Это мой лучший друг, и это очень легкий способ получить то, что вы хотите!

  • 0
    И если вы используете Chrome в качестве среды разработки, другое неинвазивное решение - отключить кэш: в разделе «Настройки» можно отключить кэш диска, выбрав «Отключить кэш» (примечание: DevTools должен быть видимым / открытым чтобы это работало).
4

Недавно я решил это с помощью 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 автоматически приведет к недействительности кэша. Работает очень хорошо, и накладные расходы не заметны.

  • 0
    os.stat () может создать узкое место?
  • 0
    @Richard stat может быть узким местом, если диск очень медленный и запросов очень много. В этом случае вы можете кэшировать метку времени где-нибудь в памяти и очищать этот кеш при каждом новом развертывании. Тем не менее, эта сложность не будет необходимой в большинстве случаев использования.
2

Просто добавьте этот код, где вы хотите выполнить жесткую перезагрузку (заставить браузер перезагрузить кэшированные файлы CSS/JS). Сделайте это внутри.load, чтобы он не обновлялся, как цикл

 $( window ).load(function() {
   location.reload(true);
});
  • 0
    Не работает на Chrome. Тем не менее загрузка ресурсов из дискового кэша
2

Если вы используете git + php, вы можете перезагружать script из кеша каждый раз, когда есть изменение в репо git, используя следующий код:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;
2

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

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

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

Более подробное объяснение того, как это работает, здесь https://www.mnot.net/cache_docs/#WORK

2

Я добавляю этот ответ как SilverStripe http://www.silverstripe.org конкретный ответ, который я искал и не нашел, но выработал из чтения: http://api.silverstripe.org/3.0/source-class-SS_Datetime.html#98-110

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

?$Now.Format(dmYHis)

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

2

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

Подробнее см.: См.: https://developer.mozilla.org/nl/docs/Web/HTML/Applicatie_cache_gebruiken#Introduction

2

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

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

В библиотеке также выполняется js/css minification, но вы можете отключить ее, если вы этого не хотите.

2

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

@TomA является правильным.

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

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

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

  • 0
    Кроме того, использование метки времени в качестве параметра строки запроса приведет к перезагрузке файла каждый раз, что означает отсутствие кэширования вообще.
  • 1
    Комментарий 2008 года в том же посте упоминает, что настройки Squid по умолчанию изменились; вопрос в том, какой процент вашего трафика обрабатывается (сейчас) устаревшими версиями Squid.
2

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

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

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

2

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

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

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

1

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

В 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>    
  • 0
    Фантастика! Спасибо! Я использовал это на главной странице, и для этого требовался следующий синтаксис `<script type =" text / javascript "src =" <% = Page.ResolveClientUrl ("~ / Scripts / main.js") + "? D =" + System.Text.RegularExpressions.Regex.Replace (System.IO.File.GetLastWriteTime (Server.MapPath ("~ / Scripts / main.js")). ToString (), "[^ 0-9]", "" )%> "> </ script>`
1

Отключить кеш script.js только для локальной разработки в чистом JS

вводит случайный 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">
1

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

// Add it to the top of the page
<?php
srand();
$random_number = rand();
?>

Затем примените случайное число к версии js следующим образом:

<script src="file.js?version=<?php echo $random_number;?>"></script>
  • 2
    Это довольно плохая идея. Это означает, что пользователь должен повторно загружать файл при каждой загрузке страницы. Кэширование - это хорошо. Этот вопрос касается того, как использовать кэширование, когда вы этого хотите, а не когда вы этого не делаете.
  • 0
    Да, пользователь должен повторно загружать файл при каждой загрузке страницы, потому что некоторые java-скрипты, включенные в (iframe), не обновляют содержимое страницы, пока пользователь не нажмет F5 или не перезагрузит страницу вручную. Так что это лучшее решение для перезагрузки контента каждый раз, когда новый посетитель посещает сайт.
1

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

В конце концов, я написал краткую страницу (включая весь код), необходимую для автоматической установки html/js index.html как части процесса публикации. Он отлично работает и обновляет только новые файлы на основе последнего изменения даты.

Вы можете увидеть мой пост в http://blueskycont.com/wp/2016/05/12/autoversion-your-spa-index-html/. Там также есть бесплатный рабочий winapp.

Кишки кода

       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");
    }
1

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

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

В качестве примера здесь приведен простой bash script, который читает список имен файлов из stdin и записывает json файл, содержащий хэши, в stdout:

#!/bin/bash
# create a json map from filenames to md5s
# run as hashes.sh < inputfile.list > outputfile.json

echo "{"
delim=""
while read l; do
    echo "$delim\"$l\": \"`md5 -q $l`\""
    delim=","
done
echo "}"

Затем этот файл можно загружать при запуске сервера и ссылаться вместо чтения файловой системы.

1

Другое предложение для веб-сайтов ASP.Net,

  • Установите различные значения кеша: максимальные значения для разных статических файлов.
  • Для файлов css/js вероятность изменения этих файлов на сервере высока, поэтому установите минимальный кеш-контроль: максимальное возрастание 1 или 2 минуты или что-то, что соответствует вашим потребностям.
  • Для изображений установите дату date как cache-control: максимальное значение возраста, скажем 360 дней.
  • Таким образом, когда мы делаем первый запрос, все статическое содержимое загружается на клиентский компьютер с ответом 200 OK.
  • В последующих запросах и через две минуты мы видим 304-Not Modified запросы на css и js файлы, которые позволяют избежать использования css/js-версий.
  • Файлы изображений не будут запрашиваться, поскольку они будут использоваться из кэшированной памяти до истечения срока действия кэша.
  • Используя нижеприведенные конфигурации web.config, мы можем достичь описанного выше поведения,                                                            
1

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

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

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

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

1

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

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

0

Один из лучших и быстрых методов, которые я знаю, - это изменить имя папки, в которой есть CSS или JS файлы. ИЛИ для разработчиков. Измените имя ваших файлов CSS/js как версии.

<link rel="stylesheet" href="cssfolder/somecssfile-ver-1.css"/>

Сделайте то же самое для своих js файлов.

0

Небольшое улучшение от существующих ответов...

Использование случайного числа или идентификатора сеанса приведет к перезагрузке каждого запроса. В идеале нам может потребоваться изменить только в том случае, если некоторые изменения кода были выполнены в любом файле js/css. При использовании общего JSP файла в качестве шаблона для многих других файлов jsp и js Добавьте ниже в обычный JSP файл

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var = "version" scope = "application" value = "1.0.0" />

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

<script src='<spring:url value="/js/myChangedFile.js?version=${version}"/>'></script>

Преимущества:

1) Этот подход поможет вам изменить номер версии только в одном месте.

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

Другой полезный совет:

Если вы используете Chrome-браузер, вы можете отключить кеширование, когда Dev Tool открыт. В Chrome Hit F12> F1 выделите Настройки> Настройки> Сеть> Отключить кеширование (в то время как DevTools открыт)

Изображение 2202

0

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

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

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

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

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

  • 0
    Так что же он никогда не кешируется?
0

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

-2

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

Заголовок кеша HTTP-кеша "no-cache" не всегда работает, как вы заметили. Спецификация HTTP 1.1 позволяет использовать пространство для пользовательских агентов, чтобы решить, следует ли запрашивать новую копию. (Он не интуитивно понятен, если вы просто смотрите на названия директив. Go прочитайте фактическую спецификацию HTTP 1.1 для кеша... она делает немного больше смысла в контексте.)

В двух словах, если вы хотите, чтобы жесткий контроль кеша использовал

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

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

  • 0
    Проблема этого подхода заключается в том, что он генерирует обратную передачу на сервер для всего такого контента. Это не хорошо.
  • 0
    Это решение не идеально, но оно работает для всех ситуаций, включая статические веб-страницы. И если вы делаете это только для ограниченного числа файлов, скажем, ваших CSS-файлов, то это не должно увеличить время загрузки страницы.
-3

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

Вместо:

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

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

$.ajaxSetup({
  cache: false
});

$.getScript('scripts/app.js'); // GET scripts/app.js?_1391722802668
  • 0
    идея не в том, чтобы полностью предотвратить кэширование. Вы просто хотите, чтобы пользователь получил последний файл, как только файл будет изменен.
-3

Самый простой способ - использовать функции чтения файлов 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, в зависимости от языка есть несколько вариантов, но почти все языки имеют способ распечатать содержимое файла. Поместите его в нужное место (в разделе), и таким образом вам не нужно полагаться на браузер.

  • 7
    Проблема в том, что вы теряете возможность кэшировать файл, что замедляет работу пользователя.
Сообщество Overcoder
Наверх
Меню