it-swarm.com.ru

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

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

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

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

Обновление:

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

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

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

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

907
Kip

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

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

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

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

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

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

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

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

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

К этому:

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

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

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

427
Kip

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

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

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

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

176
keparo

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

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

111
Leopd

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

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

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

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

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

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

90
levik

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

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

В PHP я бы сделал это следующим образом:

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

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

59
Phantom007

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

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


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

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

51
SCdF

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

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

38
John Millikin

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

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

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

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

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

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

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

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

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

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

22
Michael Kropat

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

14
airrob

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

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

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

10
user3738893

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

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

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
9
Nick Johnson

Вот чистое решение 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.

8
Lloyd Banks

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

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

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

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

8
Michiel

В 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'))}}">
8
Kamil Kiełczewski

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

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

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

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

    public function autoRefreshRewriter($filePath) {

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

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

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

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

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

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

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

                return $filePath;
            }
        }
    }

}

Ура и спасибо.

6
lony

Не нашли подход 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>
5
GreQ

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

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

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

/styles/screen.css

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

/styles/screen.css?v=1234

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

/v/1234/styles/screen.css

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

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

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

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

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

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

5
Walter Rumsby

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

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

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

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>
5
helios

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

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

4
unknown123

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

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

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

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

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

script("/main.css")

приведет к

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

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

4
pi.

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

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

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

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

4
Frank Bryce

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

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

?$Now.Format(dmYHis)

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

2
pinkp

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

@ TomA прав. 

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

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

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

2
Tai Li

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

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

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

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

2
commonpike

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

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

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

2
Drew Noakes

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

В ASP.NET

<link rel="stylesheet" href="~/css/[email protected](System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />

<script type="text/javascript" src="~/js/[email protected](System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    
2
Luis Cambustón

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

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

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

2
Dan Burzo

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

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

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

2
holmis83

Если вы используете 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;
2
readikus

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

 $( window ).load(function() {
   location.reload(true);
});
2
Karan Shaw

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

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

2
Jos

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

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

1
ThomasH

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

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

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

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

1
Scott Arciszewski

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

вставляет случайный скрипт.js? wizardry = 1231234 и блокирует обычный скрипт.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
Pawel

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

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

Я пришел к этому вопросу, когда искал решение для моего 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
statler

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

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

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

#!/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
undefined

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

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

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

0
Bangash

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

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

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

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

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

0
AmbroseChapel

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

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

 Chrome DevTools

0
Jajikanth pydimarla

Ну, я заставил его работать по-своему, меняя версию 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>
0
Al3abMizo Games