Как заставить клиентов обновить файлы JavaScript?


596

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

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

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


Ответы:


529

Насколько я знаю, общим решением является добавление ?<version>ссылки на скрипт src.

Например:

<script type="text/javascript" src="myfile.js?1500"></script>

Я предполагаю, что на данный момент нет лучшего способа, чем найти-заменить, чтобы увеличить эти "номера версий" во всех тегах сценария?

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

Это будет выглядеть примерно так:

<script type="text/javascript" src="myfile.js?$$REVISION$$"></script>

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


5
Кто-нибудь знает, игнорирует ли это IE7? Кажется, он игнорирует добавленные данные и использует кэшированный файл, когда я тестирую в режиме сравнения IE8.
Шейн Рустл

4
Я всегда знал, что строки запроса являются парой ключ-значение, как в? Ver = 123. Спасибо! :)
Ankur-m

6
я думаю, что дело не в увеличении или уменьшении номера версии, а в изменении значения добавленной переменной на то, что браузер еще не смог кэшировать.
Макс Гиркенс

2
Недавно мы столкнулись с той же проблемой, и лучшее, что я смог придумать, - это простая функция, которая прикрепила «? Mod = 123456», где 123456 - это метка времени unix даты изменения файла. Кажется, это решает проблемы, но при этом позволяет кэшировать, где это уместно. Тем не менее, я все еще видел, как браузеры полностью игнорируют эту директиву и в любом случае используют старый JS, но я не знаю, что есть элегантное «полное исправление».
VPel

32
Для осведомленности: это считается взломом. Этот метод заставляет браузер думать, что указывается новый файл, поскольку он просто просматривает полное имя файла, не интерпретируя его. foo.js?1это не то же самое имя foo.js?2, поэтому браузер будет думать, что это два разных файла. Недостатком является то, что оба файла будут одновременно существовать в кеше пользователя, занимая ненужное место.
Ли Уайт

89

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

Например, чтобы принудительно кэшировать файл не более 1 дня, вы должны отправить:

Cache-Control: max-age=86400, must-revalidate

Для бета-версии, если вы хотите, чтобы пользователь всегда получал последнюю версию, вы должны использовать:

Cache-Control: no-cache, must-revalidate

3
Можете ли вы быть более конкретным?
Крекер

7
Он говорит о заголовках, отправляемых веб-сервером для каждого файла. Должен быть настраиваемым в Apache, например. Я думаю, что это будет лучший подход
Пьер де Леспин

1
где вы это настраиваете?
Диего

2
Для веб-приложения для разработки это может быть хорошим решением. Для производственного сайта, где вы не хотите аннулировать кэш навсегда, это не очень хорошее решение, если вы не знаете, что на сайт пришел каждый целевой клиентский браузер. Это заставляет меня задуматься о возможной функции веб-сервера: адаптировать параметр max-age в соответствии с настроенной датой развертывания. Это было бы круто.
Клод Бриссон

Chrome ТРЕБУЕТ эти настройки для правильного кеширования. Без них Chrome будет кэшировать файл навсегда. Mozilla использует гораздо более разумные значения по умолчанию. См. Больше на: agiletribe.wordpress.com/2018/01/29/caching-for-chrome
AgilePro

42

Google Page-Speed: не включайте строку запроса в URL для статических ресурсов. Большинство прокси-серверов, особенно Squid, работающий до версии 3.0, не кэшируют ресурсы с "?" в их URL, даже если в ответе присутствует заголовок Cache-control: public. Чтобы включить кэширование прокси для этих ресурсов, удалите строки запросов из ссылок на статические ресурсы и вместо этого закодируйте параметры в сами имена файлов.

В этом случае вы можете включить версию в URL ex: http://abc.com/ v1.2 /script.js и использовать apache mod_rewrite, чтобы перенаправить ссылку на http://abc.com/script.js . Когда вы меняете версию, клиентский браузер обновит новый файл.


Я пробовал? Решение и в IE8, и я получаю ошибку JavaScript. Переписывание модов является опцией, но в большинстве случаев мы не имеем такого большого контроля над сервером. Я предпочел бы добавить версию в сам файл js или иметь папку для каждой версии
Karthik Sankar

@ Hắc Huyền Minh: Но когда скрипт должен быть перезагружен, его нельзя перезагружать из прокси-кэша ...
Stefan Steiger

34

Это использование было отменено: https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

Этот ответ запаздывает только на 6 лет, но я не вижу его во многих местах ... HTML5 представил кэш приложений, который используется для решения этой проблемы. Я обнаружил, что новый серверный код, который я писал, ломал старый javascript, хранящийся в браузерах людей, поэтому я хотел найти способ истечь их javascript. Используйте файл манифеста, который выглядит следующим образом:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

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


Это решение действительно хорошо, если вы помните, чтобы обновить файл манифеста :)
Mattis

24
Пожалуйста, прочтите документацию, поскольку эта функция была удалена из веб-стандартов. Developer.mozilla.org/en-US/docs/Web/HTML/…
Flavia Obreja,

1
FWIW, я закончил тем, что не использовал это решение. Было намного проще использовать / поддерживать ?<version> подход.
amos

28

Как насчет добавления размера файла в качестве параметра загрузки?

<script type='text/javascript' src='path/to/file/mylibrary.js?filever=<?=filesize('path/to/file/mylibrary.js')?>'></script>

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

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


4
Это использует теги PHP, и если вы используете PHP, это действительно хорошая идея.
Джертер

1
Я думаю, что добавление изменений будет лучше, чем размер файла :)
Mazz

2
Моя первоначальная мысль - добавить хеш файла вместо версии.
Майк Чил

Я предполагаю, что это также работает, если добавить метку времени Unix, верно? например, '... file.js? filever = <? = time ()?>
garanda

3
используйте filemtime ($ file), он выводит временную метку файла, с time () вы не можете использовать кеш, так как он меняется каждую секунду.
neoteknic

19

Не все браузеры кэшируют файлы с помощью «?» в этом. То, что я сделал, чтобы убедиться, что оно было максимально кэшировано, я включил версию в имя файла.

Поэтому вместо того stuff.js?123, я сделалstuff_123.js

Я использовал mod_redirect(я думаю) в apache have stuff_*.jsчтобы ехатьstuff.js


Не могли бы вы рассказать, что вы делали в .htaccess с mod_redirect?
Венкат Д.

3
Подробное объяснение этого метода можно найти по адресу : letletree.com/notebook/…
Карл Бартель,

3
Было бы здорово, если бы вы могли включить свой .htaccessкод в свой ответ для дальнейшего использования.
Fizzix

1
Какие браузеры не кэшируют файлы с "?" в этом?
rosell.dk

13

Для страниц ASP.NET я использую следующее

ПЕРЕД

<script src="/Scripts/pages/common.js" type="text/javascript"></script>

ПОСЛЕ (принудительная перезагрузка)

<script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>

Добавление DateTime.Now.Ticks работает очень хорошо.


31
Этот идет против всего механизма кэширования на стороне клиента. фиктивный параметр должен быть заменен чем-то вроде "{major version} _ {minor_version} _ {build_number} _ {Revision}, который будет уникальным для каждого выпуска.
Tohid

14
Хотя это, вероятно, удачное решение в среде разработки, оно не подходит для производства. Это полностью отключит кэш каждый раз, когда страница загружается для файла. Представьте себе загрузку страниц по 10 Кб в день одним файлом по 50 Кбайт, это ежедневно 500 Мбайт файла Javascript.
PhilDulac

@PhilDulac, вы можете изменить его на Ticks, чтобы он возвращал строковое значение дня, например, месяца или недели месяца. В конечном итоге это просто показывает вам, как использовать? V подход
alex

3
@alex Действительно. Я просто хотел предупредить, что если использование, продемонстрированное в ответе, попадет в производство, это может иметь последствия, которые не проявляются в процессе разработки.
PhilDulac

2
Возможный способ гарантировать, что новые копии загружаются один раз в день, может состоять в использовании типа '<script src = "/ Scripts / pages / common.js? Ver <% = DateTime.Now.ToString (" yyyyMMdd ")%>" = "текст / JavaScript"> </ скрипт>». Так что он загружается один раз в начале дня, затем кешируется.
Робб Садлер

7

Для 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();
    }
    ...
}

Я использую .Ticks (см. Мой ответ на этой странице)
Ravi Ram

5

В настоящее время обычной практикой является создание хеш-кода содержимого как части имени файла, чтобы браузер, особенно IE, перезагружал файлы javascript или css.

Например,

поставщик. a7561fb0e9a071baadb9 .js
main. b746e3eb72875af2caa9 .js

Обычно это работа для инструментов сборки, таких как webpack. Вот более подробная информация, если кто-то хочет попробовать, если вы используете веб-пакет.


4

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

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


Мне нравится это решение, так как его проще всего поддерживать. Если вы обновите файл .js, это все, что вам нужно сделать. Не нужно также обновлять ссылки на файл, поскольку ваш код автоматически добавит последнюю обновленную метку времени.
NL3294

3

Функция jQuery getScript также может быть использована для гарантии того, что файл js действительно загружается при каждой загрузке страницы.

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

$(document).ready(function(){
    $.getScript("../data/playlist.js", function(data, textStatus, jqxhr){
         startProgram();
    });
});

Проверьте функцию на http://api.jquery.com/jQuery.getScript/

По умолчанию $ .getScript () устанавливает для параметра кэша значение false. Это добавляет параметр запроса с меткой времени к URL запроса, чтобы браузер загружал скрипт каждый раз, когда его запрашивают.


8
Нам нужно кэшировать файлы, если не происходит никаких изменений.
Лео Ли

3

В PHP :

function latest_version($file_name){
    echo $file_name."?".filemtime($_SERVER['DOCUMENT_ROOT'] .$file_name);
}

В HTML :

<script type="text/javascript" src="<?php latest_version('/a-o/javascript/almanacka.js'); ?>">< /script>

Как это работает:

В HTML напишите filepathи имя так, как вы хотите, но только в функции. PHP получает filetimeфайл и возвращает filepath+name+"?"+timeпоследнее изменение


3

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

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

предоставленная пользователю ссылка на скрипт

<script src="https://thesaasdomain.com/somejsfile.js" data-ut="user_token"></script>

файл сценария

if($('script[src^="https://thesaasdomain.com/somejsfile.js?"]').length !== 0) {
   init();
} else {
   loadScript("https://thesaasdomain.com/somejsfile.js?" + guid());
}

var loadscript = function(scriptURL) {
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = scriptURL;
   head.appendChild(script);
}

var guid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

var init = function() {
    // our main code
}

Объяснение:

Пользователь прикрепил предоставленный им сценарий на своем веб-сайте, и мы проверили, существует ли уникальный токен, прикрепленный к сценарию, или нет с помощью селектора jQuery, а если нет, то динамически загрузите его с новым токеном (или версией).

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

Отказ от ответственности: не используйте, если производительность является большой проблемой в вашем случае.


2

В asp.net mvc вы можете использовать @ DateTime.UtcNow.ToString () для номера версии файла js. Номер версии автоматически меняется с датой, и вы заставляете браузер клиентов автоматически обновлять файл js. Я использую этот метод, и это хорошо работает.

<script src="~/JsFilePath/JsFile.js?v=@DateTime.UtcNow.ToString()"></script>

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

Вы можете использовать ниже код для вашей причины, кэш - файл с номером версии <скриптом SRC = «~/JsFilePath/JsFile.js?v=@GetAppVersionNumber ()»> </ скрипт>
dragonal

2

location.reload (истина);

см. https://www.w3schools.com/jsref/met_loc_reload.asp

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


Добавление в onload="location.reload();"мою форму позволяет мне получить новый JS после обновления, а не перезапускать мою страницу. Это гораздо более элегантное решение. Спасибо!
ZX9

Спасибо, мог бы использовать это с проверкой, если IP-адрес распознан, но не использовался для входа в систему с момента последнего обновления, выполнить это на странице индекса после первоначального входа пользователей.
Фи Хоран

OnLoad = "location.reload (истина);" Выше не работает для меня (используя флягу и текущую версию Chrome) также: w3schools.com/jsref/met_loc_reload.asp
aspiringGuru

1

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

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

Наиболее распространенным решением, по-видимому, является добавление метки времени или номера редакции в само имя файла. Это немного больше работы, потому что ваш код должен быть изменен, чтобы запросить правильные файлы, но это означает, что, например, версия 7 вашей snazzy_javascript_file.js(то есть snazzy_javascript_file_7.js) кэшируется в браузере, пока вы не выпустите версию 8, а затем ваш код изменится на получить snazzy_javascript_file_8.jsвместо этого.


1

Преимущество использования file.js?V=1над a fileV1.jsзаключается в том, что вам не нужно хранить несколько версий файлов JavaScript на сервере.

Проблема, с которой я сталкиваюсь, file.js?V=1заключается в том, что у вас может быть зависимый код в другом файле JavaScript, который ломается при использовании новой версии библиотечных утилит.

Ради обратной совместимости, я думаю, что гораздо лучше использовать jQuery.1.3.jsдля ваших новых страниц и позволить использовать существующие страницы jQuery.1.1.js, пока вы не будете готовы обновить старые страницы, если это необходимо.


1

Используйте GETпеременную версии для предотвращения кэширования браузера.

Добавление ?v=AUTO_INCREMENT_VERSIONдо конца вашего URL предотвращает кеширование браузера - избегая любых и всех кэшированных скриптов.


1

Мой коллега только что нашел ссылку на этот метод сразу после того, как я опубликовал (со ссылкой на css) по адресу http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/ . Приятно видеть, что другие используют это, и это, кажется, работает. Я предполагаю, что на данный момент нет лучшего способа, чем найти-заменить, чтобы увеличить эти "номера версий" во всех тегах сценария?



1

Очистка кэша в ASP.NET Core с помощью помощника по тегам поможет вам в этом и позволит вашему браузеру сохранять кэшированные сценарии / css до тех пор, пока файл не изменится. Просто добавьте вспомогательный тег asp-append-version = "true" в тег сценария (js) или link (css):

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true"/>

Дэйв Paquette есть пример хорошего и объяснение кэша перебора здесь (внизу страницы) Кэш Разорение


Разве это не работает в обычном ASP.NET? Я попытался добавить asp-append-version к моему тегу script, и все, что видит браузер, - это тег script в точности так, как он отображается в источнике, включая атрибут asp-append-version.
tolsen64

Это атрибут .NET Core, связанный с Tag Helpers. Он добавляет имя скрипта с версией, чтобы сервер / браузер всегда видел последнюю версию и загружал файлы
ccherwin

0

Самое простое решение? Не позволяйте браузеру кешировать вообще. Добавить текущее время (в мс) в качестве запроса.

(Вы все еще в бета-версии, так что вы можете обоснованно не оптимизировать производительность. Но YMMV здесь.)


13
ИМХО это плохое решение. Что делать, если вы не в бета-версии и вы выпускаете важные обновления?
d -_- б

0

Один простой способ. Изменить htaccess

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} \.(jpe?g|bmp|png|gif|css|js|mp3|ogg)$ [NC]
RewriteCond %{QUERY_STRING} !^(.+?&v33|)v=33[^&]*(?:&(.*)|)$ [NC]
RewriteRule ^ %{REQUEST_URI}?v=33 [R=301,L]

Это вызывает перенаправление, которое с точки зрения производительности является неоптимальным, но работающим решением.
дважды

0

Ниже работало для меня:

<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
</head>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.