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

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

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

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

Обновление:

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

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

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

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

Ответы [ 48 ]

2 голосов
/ 31 июля 2018

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

В 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;
    }
}
2 голосов
/ 16 сентября 2014

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

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

2 голосов
/ 07 июля 2016

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

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

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

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

1 голос
/ 12 мая 2016

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

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

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

1 голос
/ 12 декабря 2014

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

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

В качестве примера, вот простой скрипт bash, который читает список имен файлов из 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 голос
/ 12 августа 2014

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

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

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

" />

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

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

1 голос
/ 11 февраля 2018

Отключить кеш 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">
0 голосов
/ 07 сентября 2018

Небольшое улучшение по сравнению с существующими ответами ...

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

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