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

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

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

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

30 ответов

Решение

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

Например:

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

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

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

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

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

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

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

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

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

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

Cache-Control: no-cache, must-revalidate

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. Когда вы меняете версию, клиентский браузер обновит новый файл.

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

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

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

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

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

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

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

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

Так что вместо stuff.js?123, Я сделал stuff_123.js

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

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

Например,

поставщик.a7561fb0e9a071baadb9.js
главный.b746e3eb72875af2caa9.js

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

Для страниц 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 работает очень хорошо.

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

В 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 последних изменений

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

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

Мы создавали 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, а если нет, то динамически загрузим его с новым токеном (или версией).

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

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

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

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

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

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

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

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

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

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

Если вы используете PHP и Javascript, то следующее должно работать для вас, особенно в ситуации, когда вы несколько раз вносите изменения в файл. Итак, каждый раз вы не можете изменить его версию. Итак, идея состоит в том, чтобы создать случайное число в PHP, а затем назначить его как версию файла JS.

      $fileVersion = rand();
<script src="addNewStudent.js?v=<?php echo $fileVersion; ?>"></script>

Несмотря на то, что это специфичная для фреймворка, Django 1.4 имеет эту функцию, которая работает аналогично ссылке на сайт "greenfelt" в ответе выше.

Location .reload(правда);

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

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

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

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

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

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

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

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

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

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

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

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

Дейв Пакетт (Dave Paquette) имеет хороший пример и объяснение уничтожения кэша здесь (внизу страницы).

Один простой способ. Изменить 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]

Простой прием, который мне подходит, чтобы предотвратить конфликты между старыми и новыми файлами javascript. Это означает: если возникнет конфликт и возникнет какая-то ошибка, пользователю будет предложено нажать Ctrl-F5.

Вверху страницы добавьте что-то вроде

       <h1 id="welcome"> Welcome to this page <span style="color:red">... press Ctrl-F5</span></h1>

выглядит как

Пусть эта строка javascript будет последней, которая будет выполняться при загрузке страницы:

       document.getElementById("welcome").innerHTML = "Welcome to this page"

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

      <script>  
  var version = new Date().getTime();  
  var script = document.createElement("script");  
  script.src = "app.js?=" + version;  
  document.body.appendChild(script);  
</script>

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

ПЕРЕДНИЙ ВАРИАНТ

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

new Date().getTime()

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

// cache-expires-after.js v1
function cacheExpiresAfter(delay = 1, prefix = '', suffix = '') { // seconds
    let now = new Date().getTime().toString();
    now = now.substring(now.length - 11, 10); // remove decades and milliseconds
    now = parseInt(now / delay).toString();
    return prefix + now + suffix;
};

// examples (of the delay argument):
// the value changes every 1 second
var cache = cacheExpiresAfter(1);
// see the sync
setInterval(function(){
    console.log(cacheExpiresAfter(1), new Date().getSeconds() + 's');
}, 1000);

// the value changes every 1 minute
var cache = cacheExpiresAfter(60);
// see the sync
setInterval(function(){
    console.log(cacheExpiresAfter(60), new Date().getMinutes() + 'm:' + new Date().getSeconds() + 's');
}, 1000);

// the value changes every 5 minutes
var cache = cacheExpiresAfter(60 * 5); // OR 300

// the value changes every 1 hour
var cache = cacheExpiresAfter(60 * 60); // OR 3600

// the value changes every 3 hours
var cache = cacheExpiresAfter(60 * 60 * 3); // OR 10800

// the value changes every 1 day
var cache = cacheExpiresAfter(60 * 60 * 24); // OR 86400

// usage example:
let head = (document.head || document.getElementsByTagName('head')[0]);
let script = document.createElement('script');
script.setAttribute('src', '//unpkg.com/sweetalert@2.1.2/dist/sweetalert.min.js' + cacheExpiresAfter(60 * 5, '?'));
head.append(script);

// this works?
let waitSwal = setInterval(function() {
    if (window.swal) {
        clearInterval(waitSwal);
        swal('Script successfully injected', script.outerHTML);
    };
}, 100);

Вы можете добавить версию файла к имени файла, чтобы он выглядел так:

https://www.example.com/script_fv25.js

fv25 => номер версии файла. 25

И в вашем.htaccess поместите этот блок, который удалит часть версии из ссылки:

RewriteEngine On
RewriteRule (.*)_fv\d+\.(js|css|txt|jpe?g|png|svg|ico|gif) $1.$2 [L]

итоговая ссылка будет такой:

https://www.example.com/script.js

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

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

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

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

Вы можете сделать это с помощью .htaccess

Добавьте в свой файл .htaccess следующие строки:

      # DISABLE CACHING
<IfModule mod_headers.c>
  <FilesMatch "\.js$">
      Header set Cache-Control "no-store, max-age=0"
  </FilesMatch>
</IfModule>
Другие вопросы по тегам