barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

вторник, 21 июня 2016 г.

requirejs и управление браузерным кэшированием с помощью конфигурационного параметра urlArgs

Задача

В проекте, использующем requirejs, встала острая необходимость управлять кэшированием js-файлов браузером. Ситуация типичная: в приложении обновляется некоторый js-файл, но клиент продолжает пользоваться старой версией файла, т.к. браузер его закэшировал (в т.н. называемый Back/Forward Cache, или BFCache: http://www.pvsm.ru/javascript/61476). И пока кэш не просрочится, ситуация не изменится. Вот как данный случай выглядит в Firebug:


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

Решение

Конечно, управлять кэшированием можно на стороне сервера, с помощью заголовков Expires / Last-Modified / Cache-Control / Pragma. Но, во-первых, предлагаемый здесь способ более гибкий. А, во-вторых, если управлять кэшированием вы решили не на самом старте проекта, а лишь в какой-то момент его жизни после выхода в продакшен, вы никакими серверными махинациями не заставите браузер раскэшировать то, что он уже закэшировал ранее.
Итак, про requeirejs. Конфигурация requirejs имеет параметр urlArgs, позволяющий добавлять GET-аргументы к URL-ам, по которым загружаются js-файлы. Например, при задании этого параметры равным "v=1" файл formatter.js будет грузиться по URL'у formatter.js?v=1. В новых версиях requirejs есть возможность использовать функцию вместо статичной строки, но сейчас не об этом. Главное, что для браузера formatter.js и formatter.js?v=1 - различные  URL, и даже закешировав ранее файл formatter.js браузер все равно пойдет на сервер за formatter.js?v=1 (хотя, сервер, конечно, по обоим URL'ам выдаст один и тот же файл, т.к. это статичный ресурс и GET-параметры для него игнорируются).
При поиске решения я наткнулся на интересную статью: http://blog.johnnyreilly.com/2014/03/caching-and-cache-busting-with-requirejs.html. В ней приводится рекомендация по использованию параметра urlArgs для управления кэшированием, которую я успешно и применил. Автор предлагает 2 разных подхода для разработки и продуктивного развертывания проекта:
  • В процессе разработки удобно, когда вообще ничего не кэшируется, и любые правки на сервере сразу же попадают в браузер. В данном случае задаем параметр urlArgs равным "v=" +  (new Date()).getTime(). В итоге имеем при каждой загрузке страницы новые URL'ы, и браузер все js-файлы гарантированно загружает с сервера
  • В продуктивном развертывании необходим баланс между актуальностью файлов и потреблением сетевых ресурсов, так что вариант, описанный выше, не подходит: файлы на сервере меняются не часто, и на какой-то промежуток времени их все же хорошо было бы кэшировать. Здесь можно воспользоваться следующим подходом: устанавливаем параметр urlArgs  равным "v=<productVersion>", где <productVersion> - версия Вашего продукта (сайта). Т.о., при выпуске и установке в продуктив новой версии Вашего продукта все клиенты при первом обращении к сайту получат актуальные версии js-файлов, т.к. изменилась версия продукта, а, значит, и все URL-ы, по которым загружаются js-ки. Далее же, до выхода следующей версии, браузер может (и будет) спокойно кэшировать js-файлы, т.к. они гарантированно остаются неизменными.

Комментариев нет:

Отправить комментарий