Як змусити браузер перезавантажити кешовані файли CSS / JS?


993

Я помітив, що деякі веб-переглядачі (зокрема, Firefox та Opera) дуже ретельно використовують кешовані копії файлів .css та .js , навіть між сеансами браузера. Це призводить до проблеми під час оновлення одного з цих файлів, але браузер користувача продовжує використовувати кешовану копію.

Питання: який найелегантніший спосіб змусити браузер користувача перезавантажити файл, коли він змінився?

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

Оновлення:

Після дозволу обговорити тут деякий час, я вважав , що пропозиція Джона Мілікіна та da5id є корисною. Виявляється, для цього є термін: авто-версія .

Нижче я опублікував нову відповідь, яка поєднує моє оригінальне рішення та пропозицію Джона.

Іншою ідеєю, яку запропонував SCdF, було б додавання до файлу помилкового рядка запиту. ( Пі-пір подано деякий код Python для автоматичного використання часової позначки як помилкового рядка запиту .) Однак існує певна дискусія щодо того, чи браузер кешуватиме файл чи рядок запиту. (Пам'ятайте, що ми хочемо, щоб браузер кешував файл і використовував його при майбутніх візитах. Ми хочемо, щоб він знову отримав файл, коли він змінився.)

Оскільки незрозуміло, що відбувається з помилковим рядком запиту, я не приймаю цієї відповіді.


У мене є це в моїй .htaccess, і ніколи ніяких проблем з кешованих файлів: ExpiresActive On ExpiresDefault "modification".
Френк Конійн

2
Я безумовно погоджуюся, що додавати інформацію про версії до URL-адреси файлу - це найкращий спосіб. Це працює, весь час, для всіх. Але якщо ви його не використовуєте, і вам потрібно просто періодично перезавантажувати один CSS або JS-файл у власному браузері ... просто відкрийте його на власній вкладці та натисніть SHIFT-reload (або CTRL-F5)! Ви можете зробити те саме, використовуючи JS, завантаживши файл у (прихований) кадр, дочекавшись його завантаження, а потім зателефонувавши iframe.contentWindow.location.reload(true). Дивіться метод (4) stackoverflow.com/a/22429796/999120 - це стосується зображень, але те саме стосується.
Зробіть

2
Я дуже вдячний за те, як було задано це запитання та оновлено з тих пір. Це повністю описало, чого я повинен очікувати у відповідях. Я буду дотримуватися такого підходу у своїх питаннях відтепер. Ура!
rd22

Відповіді:


455

Оновлення: Перепишіть, щоб включити пропозиції Джона Мілікіна та da5id . Це рішення написано на PHP, але його слід легко адаптувати до інших мов.

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

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


16
Мій власний сервер статичного вмісту робить точно так само, за винятком того, що я використовую параметр для версії (base.css? V = 1221534296), а не зміну імені файлу (base.1221534296.css). Я підозрюю, що ваш спосіб може бути трохи ефективнішим. Дуже круто.
Єнс Роланд

4
@Kip: Дуже гладке рішення. Переписування URL-адрес, очевидно, може запропонувати набагато більше, ніж просто гарні URL-адреси.
Джеймс П.

37
Я бачу в цьому проблему в тому, що вона звертається до файлової системи багато разів - саме - кількість посилань * кількість запитів / сек ..., що може або не може бути проблемою для вас.
Томаш Фейфар

3
@AlixAxel: Ні, браузери повторно отримують його, коли параметр змінюється, але деякі публічні проксі не кешуватимуть файли з параметрами URL-адреси, тому найкраща практика полягає в тому, щоб включити версію в шлях. А накладні витрати mod_rewrite є незначними порівняно з усіма іншими вузькими місцями у WPO
Єнс Роланд

8
Чи file_existsсправді потрібна перша перевірка? filemtimeповерне значення false у разі відмови, то чому б не просто призначити значення filemtime змінній і перевірити, чи воно false, перш ніж перейменувати файл? Це дозволить скоротити одну непотрібну операцію з файлом, яка дійсно склалася б.
Гевін

184

Проста техніка на стороні клієнта

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

Загальні відвідувачі вашого веб-сайту не матимуть такого ж досвіду, як у вас, коли ви розробляєте його. Оскільки середній відвідувач відвідує сайт рідше (можливо, лише кілька разів щомісяця, якщо ви не Google або Hi5 Networks), вони мають меншу ймовірність збереження ваших файлів у кеші, і цього може бути достатньо. Якщо ви хочете примусити нову версію до браузера, ви завжди можете додати рядок запиту до запиту та збільшити номер версії, коли внесете основні зміни:

<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 або про те, як робляться ці запити, хорошою книгою є "Високопродуктивні веб-сайти" Стіва Суудерса. Це дуже хороший вступ до теми.


3
Швидкий трюк генерування рядка запиту за допомогою Javascript чудово підходить під час активної розробки. Я робив те ж саме з PHP.
Алан Тьюрінг

2
Це найпростіший спосіб досягти бажаного результату оригінального афіші. Метод mod_rewrite добре працює, якщо ви хочете змусити перезавантажити .css або .js файл КОЖЕН раз, коли ви завантажуєте сторінку. Цей спосіб все ще дозволяє кешувати, поки ви фактично не зміните файл і дійсно хочете, щоб він змусив перезавантажити.
scott80109

@keparo, у мене є достатня кількість jquery на всіх сторінках, якщо я збираюся змінити це вручну, це займе місяць. Якщо ви можете допомогти мені вирішити все без кодування на кожній сторінці.
зломщик

1
Здається, це не працює для мого CSS, коли я використовую:<link href='myCss.css?dev=14141'...>
Ноумен,

3
Це не є прийнятним рішенням. Добре кількість браузерів просто відмовлятиметься кешувати що-небудь із рядком запиту на ньому. З цієї причини Google, GTMetrix та подібні інструменти піднімають прапор, якщо у вас є рядки запитів щодо посилань на статичний вміст. Хоча це, безумовно, гідне рішення для розвитку, це абсолютно не рішення для виробництва. Також браузер керує кешування, а не сервер. Сервер просто ПІДГОТОВЛЯЄТЬСЯ, коли його слід оновити; браузер НЕ МАЄ слухати сервер (а часто і ні). Мобільні пристрої - яскравий приклад цього.
Нейт I

113

Плагін Google mod_pagespeed для apache зробить автоматичну версію для вас. Це справді гладко.

Він аналізує HTML на виході із веб-сервера (працює з PHP, рейлами, python, статичним HTML - все що завгодно) та переписує посилання на CSS, JS, файли зображень, щоб вони включали ідентифікаційний код. Він обслуговує файли за зміненими URL-адресами з дуже довгим кеш-кером на них. Коли файли змінюються, він автоматично змінює URL-адреси, тому браузер повинен їх повторно отримати. Це в основному просто працює, без змін у вашому коді. Це навіть зменшить ваш код також на виході.


1
Це чудово, але все ж у бета-версії. Чи можна його використовувати для обслуговування підприємства?
Sanghyun Lee

26
Це WRONG (автоматичне позначення джерела), коли це явно проблема браузера. Дайте нам (розробникам) справжнє оновлення мозгу: <ctrl> + F5
T4NK3R

25
mod_pagespeed функціонально еквівалентний повністю автоматичному кроку збирання / компіляції для вашого html / css / js. Я думаю, що вам буде важко знайти будь-яких серйозних розробників, які вважають, що системи побудови неправі, або що немає нічого поганого в тому, що вона є повністю автоматичною. Аналогія чистої збірки полягає в тому, щоб очистити кеш mod_pagespeed: code.google.com/p/modpagespeed/wiki/… ?
Леопд

3
@ T4NK3R mod_pagespeed не повинен нічого робити з вашим джерелом, щоб зробити кеш керування, просто згадувалося, що це може допомогти у таких речах, як мінімізація. Що стосується того, чи це "WRONG" чи ні, це абсолютно суб'єктивно. Це може бути для вас неправильним, але це не означає, що це все-таки погано .
Madbreaks

2
Він також працює з nginx, хоча вам потрібно створити його з джерела: developers.google.com/speed/pagespeed/module/…
Rohit

93

Замість зміни версії вручну я рекомендую вам використовувати хеш MD5 фактичного файлу CSS.

Отже, ваша URL-адреса буде щось подібне

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

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

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

Просто запускайте цей сценарій щоразу, коли змінюється CSS, і ви добре. Браузер ТОЛЬКО перезавантажить ваші файли, коли вони будуть змінені. Якщо ви зробите правку, а потім скасуєте її, не виникає жодних труднощів у з'ясуванні, до якої версії потрібно повернутися, щоб відвідувачі не завантажували повторно.


1
на жаль, я не знаю, як це здійснити. Поради, будь ласка ... детальніше ...
Майкл Фелпс

Реалізація в оболонці, рубіні тощо була б чудовою
Пітер

3
Дуже приємне рішення .. але я думаю, що для обчислення хеша файлу в кожному запиті файлу (css, js, images, html..etc) витрачаються ресурси для кожного відвідування сторінки.
DeepBlue

Це стандартне рішення для тих, хто використовує пакети js або css за допомогою gulp, grunt або webpack, реалізація відрізняється для кожного рішення, але хеширование файлів як крок збирання є загальним і пропонується для сучасних пакетних додатків
Брендон Сорен Каллі

@DeepBlue - у відповіді написано "запускати цей сценарій щоразу, коли CSS змінюється" . Це НЕ в кожному відвідуванні сторінки. OTOH Відповідь залишає основні деталі - як змінений хеш стає частиною URL-адреси? Я не знаю ...
ToolmakerSteve

70

Не впевнений, чому ви, хлопці, так сильно боліте, щоб реалізувати це рішення.

Все, що вам потрібно зробити, якщо отримати змінену часову позначку файлу та додати її як запит до рядка

У PHP я зробив би це так:

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

filemtime - це функція PHP, яка повертає змінену часову позначку файлу.


Можна просто використовувати mycss.css?1234567890.
Гевін

3
дуже елегантний, хоча я трохи змінив його <link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>, на всякий випадок, якщо деякі аргументи цього потоку щодо кешування URL-адрес із змінними GET (у запропонованому форматі) є правильними
luke_mclachlan

далі до мого останнього коментаря я бачив, що wordpress використовує ?ver=так, хто знає!
luke_mclachlan

Прекрасне рішення. Крім цього для мене я виявив, що filemtime не працює для повноцінного доменного імені (FQDN), тому я використовував FQDN для href частини та $ _SERVER ["DOCUMENT_ROOT"] для частини файлового часу. EX: <link rel = "stylesheet" href = "http: //theurl/mycss.css? V = <? Php echo filemtime ($ _ SERVER [" DOCUMENT_ROOT "]. '/Mycss.css')?>" />
rrtx2000

Щиро дякую. Просто і добре. Ось це в Python: progpath = os.path.dirname (sys.argv [0]) def versionize (файл): timestamp = os.path.getmtime ('% s /../ web /% s'% (progpath , файл)) повернути '% s? v =% s'% (файл, часова марка) print <link href = "% s" rel = "стиль таблиці" '' type = "text / css" /> '\% versionize ( 'css / main.css')
dlink

52

Ви можете просто поставити ?foo=1234в кінці імпорту css / js, змінивши 1234 так, як вам подобається. Погляньте на приклад джерела SO-HTML.

Існує ідея, що це? параметри в запиті все одно відкидаються / ігноруються, і ви можете змінити це число, коли ви розгортаєте нову версію.


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

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


Дурниці. Рядок запиту (він же. Параметри GET) є частиною URL-адреси. Вони можуть і будуть кешовані. Це хороше рішення.
troelskn

9
@troelskn: Специфікація HTTP 1.1 говорить інакше (стосовно GET та HEAD-запитів із параметрами запитів): кеші НЕ ПОВИННІ обробляти відповіді на такі URI як нові, якщо сервер не надає явного часу закінчення. Дивіться w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Майкл Джонсон

4
Я спробував тип рядка запиту версії з усіма основними браузерами, і вони НЕ кешують файл, специфікації чи ні. Однак я вважаю, що краще використовувати формат style.TIMESTAMP.css, не зловживаючи рядками запитів, так як все ще існує можливість, щоб кешування програмного забезпечення проксі НЕ кешувало файл.
Томаш Андрле

34
Варто зазначити, з якої причини, що Stackoverflow сам використовує метод рядка запиту.
Жасон

2
Перевірили, що використання параметра = = не змусить браузерів повторно отримувати кешований файл при зміні параметра. Єдиний спосіб - це змінити ім’я файлу програмно на кінці сервера, як відповів Kip
arunskrish

41

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

Дивись також:


3
Дякую, я думаю, це був ще один випадок, коли моя ідея обговорювалася, я просто не знав, як вона називається, тому я ніколи не знаходив її в пошуку Google.
Кіп

27

30 відповідей, що існують приблизно 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.


Майкл - ваш коментар дуже актуальний. Я кулажу саме тут, намагаючись знайти рішення для мого SPA. Я отримав декілька покажчиків, але повинен був сам придумати рішення. Зрештою, я був дуже задоволений тим, що придумав, тому написав допис у блозі та відповідь на це питання (включаючи код). Дякую за покажчики
statler

Чудовий коментар. Я не можу зрозуміти, поки люди продовжують говорити про перебір кеш-пам'яті та кешування HTTP як справжнє рішення проблем кешування веб-сайтів, не коментуючи нових проблем SPA, як ніби це маргінальний випадок.
Девід Касільяс

1
Відмінна реакція та абсолютно ідеальна стратегія! І бонусні бали за згадку про baseтег! Що стосується підтримки старого коду: це не завжди можливість, і це не завжди гарна ідея. Нові версії коду можуть підтримувати порушення змін у інших частинах програми або можуть містити виправлення в надзвичайних ситуаціях, виправлення вразливості тощо. Мені ще належить реалізувати цю стратегію, але я завжди вважав, що загальна архітектура повинна дозволяти розгортальникам позначити стару версію як obsoleteі примусити перезавантажити наступного разу, коли буде здійснено асинхронний виклик (або просто примусово видалити всі сеанси через WebSockets ).
Джоні Асмар

Приємно бачити добре продуману відповідь щодо програм на одній сторінці.
Nate I

Це "синьо-зелене розгортання", якщо ви хочете шукати додаткову інформацію.
Філ

15

Не використовуєте 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, щоб воно не кешувало елементи із рядком запиту?
AnthonyWJones

13
Веб-браузер, який включає функціональність об'єктів кешування (перевірте кеш-каталог вашого браузера). HTTP - це протокол, що включає директиви від серверів до клієнтів (проксі, браузери, павуки тощо), що пропонує кеш керування.
tzot

13

У 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'))}}">

Приклад виводу html ( filemtimeчас повернення як часова мітка Unix )

<link rel="stylesheet" href="assets/css/your.css?v=1577772366">

Який вихід цієї команди в html? Що робити, якщо мені потрібно поновити лише такі версії, як? V = 3,? V = 4 та ін. - Не змушує браузер завантажувати css кожного разу, коли користувач заходить на веб-сайт
Gediminas

filemtime : "Ця функція повертає час, коли записуються блоки даних файлу, тобто час, коли змінено вміст файлу." src: php.net/manual/en/function.filemtime.php
Kamil Kiełczewski

11

RewriteRule потребує невеликого оновлення для js або css-файлів, що містять версію точкових нотацій. Напр. Json-1.3.js.

Я додав клас репресування точок [^.] До регулярного виразу, так що число. ігнорується.

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

2
Дякую за вклад! З тих пір, як я написав цю публікацію, мене теж спалили. Моє рішення було лише переписати, якщо остання частина імені файлу містить рівно десять цифр. (10 цифр охоплює всі часові позначки з 9.9.2001 р. По 20.11.222 р.) Я оновив свою відповідь, щоб включити цей регулярний вираз:^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip

Я розумію регулярний вираз, але не розумію, яку проблему ви тут вирішуєте [^.]. Крім того, немає користі писати \dвсередині класу символів - \d+буде те ж саме. Як розміщено, ваш шаблон буде відповідати будь-якій кількості символів (жадібно), то буквальній крапці, то не крапці, то одній чи більше цифр, то крапці, то cssабо js, а потім кінці назви файлу. Немає відповідностей для вашого зразка: regex101.com/r/RPGC62/1
mickmackusa

10

Для ASP.NET 4.5 і новіших версій ви можете використовувати пакетну скрипт .

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

Існують і інші переваги для комплектування, включаючи підвищення продуктивності при завантаженні першої сторінки з мінімізацією.


Допоможіть, будь ласка, я не вносив жодних змін у bundle.config, просто змінивши файли css чи js, то як я можу вирішити проблему кешування?
vedankita kumbhar

10

Ось чисте рішення 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.


Я спробував щось подібне, це буде працювати лише на перезавантаженій сторінці, але якщо на сайті є кілька сторінок, де обмінюються однаковими css / images, то інші сторінки все одно використовуватимуть старі ресурси.
DeepBlue

9

Цікавий пост. Прочитавши всі відповіді тут у поєднанні з тим, що у мене ніколи не було проблем із "фальшивими" рядками запитів (що я не впевнений, чому всі так неохоче використовують це), я здогадуюсь про рішення (яке знімає необхідність у перезаписуванні правил apache як у прийнятій відповіді) - обчислити короткий HASH вмісту файлу CSS (замість часу файлу date) як помилковий запит рядка.

Це призведе до наступного:

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

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


8

Для свого розвитку я вважаю, що хром має чудове рішення.

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

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

Це мій найкращий друг, і це дуже легкий спосіб отримати те, що ви хочете!


І якщо ви використовуєте Chrome як середовище розробки, іншим неінвазивним рішенням є відключення кешу: У вікні Налаштування ви можете визнати недійсним кеш диска, вибравши "Відключити кеш" (зверніть увагу: DevTools повинен бути видимим / відкритим для цього працювати).
Велоджет

7

Дякуємо компанії Kip за ідеальне рішення!

Я розширив його, щоб використовувати його як 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;
            }
        }
    }

}

Ура, дякую.


7

Не знайшли клієнтського підходу 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>

6

Google Chrome має функцію Hard Reload , а також порожній кеш і опцію Hard Reload. Ви можете натиснути і утримувати кнопку перезавантаження (в режимі огляду), щоб вибрати її.


Для того, щоб уточнити, на "Перевірте режим", вони мають в виду "Dev Tools" ака F12, ака Ctrl + Shift + I, він же ant menu> More Tools> Developer Toolsака right click> Inspect Element. Існує також налаштування, закопане десь у інструментах для розробників (я забув місце розташування) для важкого перезавантаження при кожному перезавантаженні.
Джоні Асмар

5

Ви можете примусити "кешування на всі сеанси", якщо ви додасте сеанс-ідентифікатор як надійний параметр файлу 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

Скажімо, у вас файл доступний за адресою:

/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 / тощо. Перегляд це означає, що зміни в зображеннях, на які посилаються файли CSS, будуть помічені. Це не гарантується першою схемою, тобто URL images/happy.gifщодо /styles/screen.css?v=1235IS , /styles/images/happy.gifякий не містить ніякої інформації про версії.

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


Просто додавання папки, яку ви можете перейменувати за потреби, спрацює, якщо ви використовуєте лише відносні URL-адреси. І тоді ви переконайтеся, що переспрямовано до відповідної папки з базової папки, тобто в PHP : <?php header( 'Location: folder1/login.phtml' ); ?>.
Грубер

1
Використовуючи другий метод, зміна CSS призведе до недійсності кешованих копій усіх зображень, на які посилаються відносні URL-адреси, що може бути або не бажано.
TomG

5

Ви можете просто додати якесь випадкове число з таким URL-адресом CSS / JS

example.css?randomNo=Math.random()

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

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-файлу автоматично спричинить помилку кешу. Працює дуже добре, а накладні витрати не помітні.


може os.stat () створити вузьке місце?
ходжу

@ Richard stat може бути вузьким місцем, якщо диск дуже повільний, а запитів дуже багато. У такому випадку ви можете кешувати часову позначку десь у пам'яті та очищати цей кеш при кожному новому розгортанні. Однак ця складність не буде необхідною у більшості випадків використання.
пі.

4

Якщо ви використовуєте git + PHP, ви можете перезавантажувати скрипт із кеша кожного разу, коли відбувається зміна git repo, використовуючи наступний код:

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

4

Якщо ви розробник, який прагне уникнути кешування, на вкладці хромованої мережі є опція відключення кешу. В іншому випадку ви можете зробити це без структури візуалізації сервера, використовуючи два теги сценарію.

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // can't use myfile.js stuff yet
</script>')
<script type="text/javascript">
    // do something with myfile.js
</script>

4

Це запитання є надзвичайно старим і з’являється першим ділом, коли хтось гуглює цю проблему. Це не є відповіддю на питання так, як бажає оп, а це відповідь розробникам із цією проблемою під час розробки та тестування. І я не можу розмістити нове запитання на цю тему, оскільки воно буде позначене як дублікат.

Як і багато інших, я просто хотів ненадовго видалити кешування.

"keep caching consistent with the file" .. його шлях занадто багато клопоту ..

Взагалі кажучи, я не проти завантажувати більше - навіть завантаження файлів, які не змінилися, - у більшості проектів - практично не має значення. Під час розробки програми - ми здебільшого завантажуємо з диска, localhost:port тому ця increase in network trafficпроблема не є проблемою, яка є найважливішою .

Більшість малих проектів просто розігруються - вони ніколи не закінчуються у виробництві. тож для них вам нічого більше не потрібно ..

Якщо ви користуєтеся інструментами Chrome Dev , ви можете дотримуватися цього способу відключення кешування, як на зображенні нижче: як змусити хром перезавантажувати кешовані файли

І якщо у вас є проблеми з кешуванням Firefox : Як змусити перезавантажити активи на Firefox

як відключити кешування в Firefox під час розробки Робіть це лише в процесі розробки, вам також потрібен механізм примусового перезавантаження для виробництва, оскільки ваші користувачі використовуватимуть старі модулі, які недійсні в кеші, якщо ви часто оновлюєте додаток і не надаєте спеціальний механізм синхронізації кешу, як описаний у відповідях вище.

Так, ця інформація вже є в попередніх відповідях, але мені потрібно було знайти пошук Google, щоб знайти її.

Сподіваємось, ця відповідь дуже зрозуміла, і тепер цього не потрібно.


ОП запитав щось і відповів щось інше. Йдеться не про силове навантаження в місцевому, а у виробництві, і ви не можете попросити кінцевих користувачів слідувати вище, щоб відключити кеш тощо.
Jitendra Pancholi

3

Здається, всі відповіді тут говорять про якусь версію в схемі іменування, яка має свої недоліки.

Браузери повинні добре знати, що кешувати, а що не кешувати, читаючи відповідь веб-серверів, зокрема заголовки http - наскільки довго цей ресурс діє? був оновлений цей ресурс з моменту його останнього пошуку? etcetera.

Якщо все налаштовано "правильно", просто оновлення файлів вашої програми повинно (в певний момент) оновити кеші браузера. Наприклад, ви можете налаштувати свій веб-сервер, щоб вказати браузеру ніколи не кешувати файли (це погана ідея).

Більш поглиблене пояснення того, як це працює, знаходиться тут https://www.mnot.net/cache_docs/#WORK


3

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

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

Не працює в Chrome. Досі завантажуються активи з кеш-диска
Джейсон Кім

3

Просто використовуйте код на стороні сервера, щоб додати дату файлу ... таким чином він буде КЕШУВАННЯ та завантажений лише тоді, коли файл зміниться

В 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

Я пропоную здійснити наступний процес:

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

  • мінімізуйте їх, щоб оптимізувати час завантаження

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.