Розумне кешування і версійність в javascript

Для цього .js і .css файли віддаються з заголовками, що забезпечують надійне кешування.

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

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

Найпростіший спосіб кешування статичних ресурсів - використання ETag.

Досить включити відповідні налаштування сервера (для Apache включена за замовчуванням) - і до кожного файлу в заголовках буде даватися ETag - хеш, який залежить від часу оновлення, розміру файлу і (на inode-based файлових системах) inode.

Браузер кешує такий файл і на випадок повторних запитів указивет заголовок If-None-Match з ETag кеш документа. Отримавши такий заголовок, сервер може відповісти кодом 304 - і тоді документ буде взятий з кеша.

Виглядає це так:

Перший запит до сервера (кеш чистий)

Взагалі, браузер зазвичай додає ще пачку Тема типу User-Agent, Accept і т.п. Для стислості вони порізані.

Відповідь сервера Сервер посилає у відповідь документ c кодом 200 і ETag:

Наступний запит браузера При наступному запиті браузер додає If-None-Match. (Кеш ETag):

Відповідь сервера Сервер дивиться - ага, документ не змінився. Значить можна видати код 304 і не посилати документ заново.

Альтернативний варіант - якщо документ змінився, тоді сервер просто посилає 200 з новим ETag.

Аналогічним чином працює зв'язка Last-Modified + If-Modified-Since.

  1. сервер посилає дату останньої модифікації в заголовку Last-Modified (замість ETag)
  2. браузер кешує документ, і при наступному запиті того ж документа посилає дату закеширувалася версії в заголовку If-Modified-Since (замість If-None-Match)
  3. сервер звіряє дати, і якщо документ не змінився - висилає тільки код 304, без вмісту.

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

Загальний підхід для версійності - в двох словах:

Далі ми розберемо, як зробити цей процес автоматичним і прозорим.

Жорстке кешування - свого роду кувалда яка повністю прибиває запити до сервера для кеш документів.

Для цього досить додати заголовки Expires і Cache-Control: max-age.

Наприклад, щоб закеширувати на 365 днів в PHP:

Або можна закеширувати контент надовго, використовуючи mod_header в Apache:

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

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

Найпростіше - це перетворити ім'я з версією в оригінальне ім'я файлу.

На рівні Apache це можна зробити mod_rewrite:

Таке правило обробляє всі css / js / gif / png / jpg-файли, вирізаючи з імені версію.

Але крім вирізання версії - треба ще додавати заголовки жорсткого кешування до файлів. Для цього використовуються директиви mod_header:

А все разом реалізує ось такий апачевий конфиг:

Через порядку роботи модуля mod_rewrite, RewriteRule потрібно поставити в основний конфігураційний файл httpd.conf або в підключаються до нього (include) файли, але ні в якому разі не в .htaccess. інакше команди Header будуть запущені першими, до того, як встановлена ​​змінна VERSIONED_FILE.

Директиви Header можуть бути де завгодно, навіть в .htaccess - без різниці.

Як ставити версію в ім'я скрипта - залежить від Вашої шаблонної системи і, взагалі, способу додавати скрипти (стилі і т.п.).

Наприклад, при використанні дати модифікації в якості версії і шаблонізатора Smarty - посилання можна ставити так:

Функція version додає версію:

Результат на сторінці:

Щоб уникнути зайвих викликів stat. можна зберігати масив зі списком поточних версій в окремій змінної

В цьому випадку в HTML просто підставляється поточна версія з масиву.

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

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

  • Версія для друку

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

Все начебто так не теє.

Наприклад натиснувши в firefoxe кнопку reload firefox все одно шле НА ВСЕ ЕЛЕМЕНТИ ЗАПИТ забиваючи на кешування. У відповідь звичайно отримує 306 але тим не менше навіщо то запити шле.

Так, так працює релоад в Firefox. А ще можна натиснути Ctrl-F5, і він взагалі все запросить на 200. Тільки це ж релоад. А при ходінні по сторінках все працює ок.

Через порядку роботи модуля mod_rewrite, RewriteRule потрібно поставити в основний конфігураційний файл httpd.conf або в підключаються до нього (include) файли, але ні в якому разі не в .htaccess, інакше команди Header будуть запущені першими, до того, як встановлена змінна VERSIONED_FILE.

А можна поміняти порядок за допомогою order?
Не завжди є можливість використовувати головні конфиг-файли

У мене стоїть наступне завдання:
Є статичні js-файли в хедері сторінки. Розгорнуто веб-додаток з цієї однієї сторінки (html), яке є лише частиною всього великого проекту на ASP.NET.
Необхідно, щоб працювало кешування без запиту на сервер навіть з перевіркою модифікації. Тобто щоб оновити додаток можна було, якщо почистити кеш браузера. При цьому сервер IIS 7, який конфігурувати його не можна. У ASP.NET теж вдаватися небажано.
Рішення поки не знайшов.