Розумне кешування і версійність в 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.
- сервер посилає дату останньої модифікації в заголовку Last-Modified (замість ETag)
- браузер кешує документ, і при наступному запиті того ж документа посилає дату закеширувалася версії в заголовку If-Modified-Since (замість If-None-Match)
- сервер звіряє дати, і якщо документ не змінився - висилає тільки код 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 теж вдаватися небажано.
Рішення поки не знайшов.