Как обновить скрипты в браузере
Я заметил, что некоторые браузеры (в частности, Firefox и Opera) очень усердно используют кэшированные копии файлов .css и .js даже между сеансами браузера. Это приводит к проблеме при обновлении одного из этих файлов, но браузер пользователя продолжает использовать кэшированную копию.
Вопрос в том, что является самым элегантным способом заставить браузер пользователя перезагрузить файл, когда он изменился?
В идеале решение не должно заставлять браузер перезагружать файл при каждом посещении страницы. Я опубликую свое собственное решение в качестве ответа, но мне любопытно, найдется ли у кого-нибудь лучшее решение, и я позволю вашим голосам решать.
Обновить :
После некоторого обсуждения здесь я нашел предложение Джона Милликина и da5id полезным. Оказывается, есть термин для этого: автоматическое управление версиями .
Ниже я разместил новый ответ, который представляет собой сочетание моего первоначального решения и предложения Джона.
Другая идея, предложенная SCdF , заключается в добавлении в файл фиктивной строки запроса. (Некоторый код Python для автоматического использования метки времени в качестве фиктивной строки запроса был представлен пи .). Тем не менее, существует некоторое обсуждение относительно того, будет ли браузер кэшировать файл со строкой запроса. (Помните, мы хотим, чтобы браузер кэшировал файл и использовал его при будущих посещениях. Мы хотим, чтобы он снова извлекал файл только после его изменения.)
Поскольку не ясно, что происходит с фиктивной строкой запроса, я не принимаю этот ответ.
У меня есть это в моей .htaccess, и никогда никаких проблем с кэшированных файлов: ExpiresActive On ExpiresDefault "modification" .
Я действительно ценю способ, которым этот вопрос был задан и был обновлен с тех пор. Это полностью описало то, что я должен ожидать в ответах. Я собираюсь следовать этому подходу в моих вопросах отныне. Ура!
Обновление: переписано для включения предложений от Джона Милликина и da5id . Это решение написано на PHP, но должно быть легко адаптировано к другим языкам.
Обновление 2: Включение комментариев Ника Джонсона о том, что оригинальное .htaccess регулярное выражение может вызвать проблемы с такими файлами, как json-1.3.js . Решение состоит в том, чтобы переписать только если в конце есть ровно 10 цифр. (Поскольку 10 цифр охватывают все метки времени с 9.09.2001 по 20.11.22.)
Сначала мы используем следующее правило перезаписи в .htaccess:
Теперь мы напишем следующую функцию PHP:
Теперь, куда бы вы ни включили свой CSS, измените его следующим образом:
Таким образом, вам больше никогда не придется изменять тег ссылки, и пользователь всегда увидит последнюю версию CSS. Браузер сможет кэшировать файл CSS, но когда вы вносите какие-либо изменения в свой CSS, браузер увидит это как новый URL, поэтому он не будет использовать кэшированную копию.
Это также может работать с изображениями, значками и JavaScript. В основном все, что не генерируется динамически.
Мой собственный статический контент-сервер делает то же самое, за исключением того, что я использую параметр для управления версиями (base.css? V = 1221534296) вместо изменения имени файла (base.1221534296.css). Я подозреваю, что ваш путь может быть немного более эффективным, хотя. Очень круто.
@Kip: очень удобное решение. Переписывание URL, очевидно, может предложить гораздо больше, чем просто красивые ссылки.
Я вижу проблему в том, что она обращается к файловой системе много раз - точно - количество ссылок * количество запросов / сек . что может быть или не быть проблемой для вас.
@AlixAxel: Нет, браузеры будут повторно извлекать его при изменении параметра, но некоторые публичные прокси не будут кэшировать файлы с параметрами url, поэтому лучше всего указывать версию в пути. И издержки на mod_rewrite незначительны по сравнению с любыми другими узкими местами производительности в WPO
Первая file_exists проверка действительно необходима? filemtime вернет false в случае неудачи, так почему бы просто не присвоить значение filemtime переменной и проверить, является ли оно false перед переименованием файла? Это сократило бы одну ненужную файловую операцию, которая действительно сложилась бы.
Простая клиентская техника
В общем, кеширование - это хорошо. Итак, есть несколько методов, в зависимости от того, решаете ли вы проблему самостоятельно при разработке веб-сайта или пытаетесь контролировать кеш в производственной среде.
Обычные посетители вашего сайта не будут иметь того опыта, который вы испытываете при разработке сайта. Так как среднестатистический посетитель посещает сайт реже (может быть, только несколько раз в месяц, если вы не являетесь сетью Google или hi5), тогда он с меньшей вероятностью будет хранить ваши файлы в кеше, и этого может быть достаточно. Если вы хотите принудительно ввести новую версию в браузер, вы всегда можете добавить строку запроса в запрос и увеличить номер версии при внесении серьезных изменений:
Это гарантирует, что каждый получит новый файл. Это работает, потому что браузер просматривает URL-адрес файла, чтобы определить, есть ли у него копия в кэше. Если ваш сервер не настроен на какие-либо действия со строкой запроса, он будет проигнорирован, но имя будет выглядеть как новый файл для браузера.
С другой стороны, если вы разрабатываете веб-сайт, вы не хотите менять номер версии каждый раз, когда сохраняете изменения в своей версии для разработки. Это было бы утомительно.
Поэтому, пока вы разрабатываете свой сайт, хорошим трюком будет автоматическая генерация параметра строки запроса:
Добавление строки запроса в запрос является хорошим способом создания версии ресурса, но для простого веб-сайта это может быть ненужным. И помните, кэширование - это хорошо.
Так что, хотите верьте, хотите нет, но на самом деле ваш сервер делает этот кеш браузера таким постоянным. Вы можете настроить параметры сервера и изменить заголовки EXPIRES, но небольшая техника, которую я написал выше, вероятно, намного проще для вас. Поскольку кэширование - это хорошо, обычно вы хотите установить эту дату далеко в будущее («Заголовок истекает в далеком будущем») и использовать метод, описанный выше, для принудительного изменения.
Суть проблемы заключается в том, что одностраничное приложение, загруженное в браузере пользователя, не знает о том, что скрипты были только что изменены, и, следовательно, необходимо обновиться. С точки зрения пользователя это выглядит как непонятные ошибки, возникающие на ровном месте, или как отказ приложения выполнять свои функции. Все это – следствие того, что версия скриптов на сервере изменилась, и приложение просто не может их найти и загрузить.
Конечно, все решается нажатием на F5, но в данной статье я покажу, как можно автоматизировать это действие, избавить пользователя от головной боли и сделать это красиво.
Данная статья представляет из себя туториал и предназначена как для опытных разработчиков, так и для новичков.
Решение, представленное здесь, разработано для VueJS версии 2.6.x, но сами алгоритмы легко могут быть портированы на любой js-фреймворк на основе webpack'а.
Статья имеет веб-интерфейс, где вы сможете пощупать решение своими руками, а также исходники на github.
Ссылки для ленивых:
Также, в качестве эксперимента, в статью будет встроено два бонуса. В бонусах я решил добавить полезную на мой взгляд информацию, которая сама по себе не достойна отдельной статьи, но о которой хотелось бы рассказать.
Идея решения заключается в введении версионности веб-приложения на уровне сборки.
Каждый раз при сборке мы сохраняем информацию о времени сборки, а во время работы приложения проверяем, изменилось ли это время. В случае изменения, обновляем страницу, попутно предупреждая пользователя об изменениях.
Решение представлено в виде плагина для Vue, который находится в коде проекта по пути /src/plugins/AutoReload. Отдельного пакета для npm не делал.
Демонстрационное веб-приложение представляет коробочную заготовку vue-cli, в которую добавлены стандартные фичи: store, роутер, axios для веб-запросов и dayjs для работы с датами. Интерфейс построен на Element.
Версионность сборки
Первая задача – сохранение информации о версии в сборке. Данную задачу можно выполнять разными способами:
Итак, рассмотрим последний вариант в контексте VueJS и Webpack. Как известно, сборщик webpack выполняет серверные js-скрипты, которые собственно и выполняют сборку. В них мы можем встроить свой код, который будет формировать специальный файл, скажем version.json, который будет содержать полезную для нас информацию.
В контексте нашей задачи достаточно записать туда дату сборки, но мы пойдем чуть дальше и добавим информацию о типе сборки (development/production/etc.) и версию проекта из файла package.json. Получим примерно такой файл:
Чтобы сформировать данный файл, нужно встроить код в скрипт vue.config.js, вот так:
Далее код самого генератора version.json:
Теперь каждый раз при сборке приложения через команды serve, build или другие будет формироваться новая версия файла version.json, которая может быть прочитана приложением через обычный get-запрос, что нам и требуется.
Примечание №1. Файл version.json помещается в папку public, содержимое которой в VueJS копируется в выходной каталог «как есть». В других фреймворках может потребоваться другое целевое расположение.
Примечание №2. Файл version.json нужно исключить из git'а, чтобы его изменение не вызывало конфликтов при командной разработке, ведь изменяться он будет постоянно даже в процессе отладки.
Плагин AutoReload
Исходя из идеи решения плагин будет выполнять следующие функции:
- Внедрение события по таймеру, которое будет проверять факт изменения даты сборки и вызывать скрипт обновления.
- Внедрение хука vue-router, который также будет проверять изменение даты сборки при попытке перехода по маршруту. Здесь требуется пояснение. Сами разработчики Vue рекомендуют использовать ленивую загрузку маршрутов, а это значит, что в момент перехода по такому маршруту будет происходить подгрузка дополнительных скриптов с сервера, которые не будут найдены, если недавно (до срабатывания таймера автоматического обновления) был деплой. Проверка версии перед переходом решает эту проблему.
- Непосредственно уведомление пользователя об изменениях и последующая перезагрузка страницы.
Конфигурация плагина
Любой уважающий себя плагин должен иметь возможности для настройки. Наш плагин не является исключением, хотя его настройки ограничены рамками задачи, для которой он разрабатывался:
- Enabled – признак включения модуля, по умолчанию true.
- CheckInterval – интервал проверки на обновление в секундах, по умолчанию 60. С такой периодичностью будет проверяться изменение даты сборки и вызываться обновление.
- Notification – признак показа уведомления об обновлении, по умолчанию true. Если уведомление отключить, обновление страницы будет происходить «молча», что может вызвать недоумение у пользователя.
- NotificationMessage – текст уведомления, по умолчанию «Система была обновлена, страница будет перезагружена.». Для уведомления используется Element, поэтому если вы используете другой фреймворк, нужно будет изменить соответствующий код. Тут, увы, из универсального решения только alert.
Код файла конфигурации можно найти в исходниках или под спойлером.
Использование плагина
Данный плагин имеет одну особенность, – он использует роутер и уведомления Element'а, поэтому его нужно включать в методе create главного компонента Vue:
Разбор кода плагина
Главной функцией является check с опциональным параметром href. Именно эта функция вызывается по таймеру или при переходе по маршруту, в этом случае передается адрес целевой страницы.
Функция получает текущую версию сборки из файла version.json и сравнивает дату сборки с ранее сохраненным значением. Если значение отличается, пользователю показывается уведомление, затем сохраняется новое значение версии сборки и происходит перезагрузка страницы.
Если обновление происходит по таймеру, эмулируется нажатие F5 (window.location.reload(true)). Если же обновление происходит при переходе по маршруту, пользователь направляется на целевую страницу маршрута. Это важно, т.к. исполнение кода никогда не дойдет до next() в хуке роутера после обновления страницы.
В принципе на этом статью можно было бы закончить, но мы пойдем чуть дальше и воспользуемся побочными эффектами нашего решения.
Бонус №1. Использование информации о версии сборки в целях отладки
Я не случайно добавил в файл version.json дополнительную информацию о версии проекта и типе сборки. В реальном проекте эта информация выводилась на специальной «скрытой» странице, в которую можно было попасть, указав ее путь в URL. В демонстрационном проекте страничка с информацией о сборке находится прямо в меню, и там можно увидеть содержимое файла version.json в удобном для человека виде.
Это может быть полезно, когда проект имеет кучу версий и веток, которые выкладываются на отдельные стенды. Посмотрев на эту страницу можно увидеть, когда проект был обновлен, и какой тип сборки там использовался.
Также туда можно добавить информацию о бэкенде, но в этом проекте его нет.
Прошу обратить внимание, что информация об интервале обновления формируется в соответствии с правилами русского языка: «60 секунд», а не «60 секунды». Программисты практически всегда пренебрегают такими мелочами, хотя решение лежит на поверхности и не требует глубоких знаний. Именно этому вопросу посвящен второй бонус статьи, под спойлером.
У любого решения есть плюсы и минусы, а также свои особенности и, конечно, простор для дальнейших доработок. Здесь проведу краткий анализ своего решения в плане его ограничений и доработок.
Проблема распределенного приложения
Если приложение работает через балансер, и сборка происходит на каждом из веб-серверов отдельно, то, очевидно, версия сборок будет отличаться минутами или секундами. Поэтому, если пользователь по какой-то причине будет перекинут на другой веб-сервер, у него произойдет обновление скриптов из-за различий во времени сборки.
Является ли это проблемой? Скорее нет, чем да, потому что балансер, как правило, настроен таким образом, чтобы пользователь всегда направлялся на один из вебов, а не прыгал между ними.
Если все-таки такая проблема актуальна для вас, я вижу два варианта решения: сборка на одном сервере или введение проверки на разность дат сборок, чтобы она превышала некий лимит.
Проблема асинхронных компонентов
В плагине решена проблема подгрузки асинхронных маршрутов, но остается нерешенной проблема, когда пользователь производит действие, вызывающее подгрузку асинхронного компонента. С точки зрения браузера это загрузка обычного js-файла с сервера. Но после деплоя и до срабатывания автоматического обновления браузер будет пытаться загрузить несуществующий файл.
У данной проблемы нет нормального решения, хотя есть вариант с перехватом/оборачиванием кода подтягивания асинхронных компонентов, чтобы в этот момент также вызывался код проверки изменения версии сборки. В любом случае, максимум, что возможно будет сделать – перезагрузить страницу целиком.
Проблема потери данных
Возможна ситуация, когда пользователь заполняет форму, и в процессе заполнения происходит обновление страницы. Данные будут потеряны, равно как и всё содержимое store. С другой стороны, есть гарантия, что пользователь не попытается загрузить старую версию формы.
Как вариант, можно не инициировать обновление страницы, а давать пользователю выбор: продолжить работу или обновить страницу.
Проблема публикации списка изменений
Честно говоря, не припомню сайты, которые публикуют какие-либо changelog'и, но было бы интересно внедрить такой функционал в модуль автообновления. Хотя лично меня всегда бесит, когда тот же телеграм пишет мне в личку список новых возможностей. Каждый раз я удаляю тот чат, но он все равно оживает при следующем обновлении. А для веба это, наверное, и не нужно вовсе.
Если вы хотите увидеть плагин в действии, воспользуйтесь демонстрационным стендом.
Нажмите на кнопку, которая сбрасывает сохраненную в плагине версию, что приведет к срабатыванию автоматического обновления после истечения минутного таймера или при переходе в другой раздел меню.
Надеюсь, что данная статья поможет вам решить проблему с автоматическим обновлением скриптом или хотя бы даст направление исследования. Также прошу оценить, насколько вам зашли «бонусы», и стоит ли их использовать в дальнейшем?
Я разрабатываю веб-сайт на основе исходного кода Wordpress через XAMPP. Иногда я меняю код CSS, сценарии или что-то еще и замечаю, что моему браузеру требуется время, чтобы применить изменения. Это заставляет меня использовать несколько браузеров для обновления одного, и если новые стили не применяются, я пробую второй, и он всегда так.
Есть ли способ избежать этой проблемы? Иногда я меняю код, не замечая предыдущих модификаций.
Кеш браузеров. Каждый раз, когда вы вносите изменения в CSS, JavaScript и просматриваете обновленную страницу, вы увидите эту проблему. Вы можете принудительно выполнить обновление, нажав CTRL + F5 в браузере, и он получит последнюю версию. Я обнаружил, что Chrome иногда требует эту последовательность пару раз .
Общее решение
Нажатие Ctrl + F5 (или Ctrl + Shift + R ) для принудительной перезагрузки кеша. Я считаю, что Mac используют Cmd + Shift + R .
В PHP вы можете отключить кеш, установив дату истечения срока действия на время в прошлом с заголовками:
Кеш Chrome можно отключить, открыв инструменты разработчика с помощью F12 , щелкнув значок шестеренки в правом нижнем углу и выбрав Отключить кеш в диалоговом окне настроек, например:
Изображение взято из этого ответа .
Fire Fox
Для Chrome интерфейс, которым вы поделились, немного изменился. Просто нажмите вкладку «Сеть», и вы увидите там флажок.
Если вы хотите избежать этого на стороне клиента, вы можете добавить что-то вроде ?v=1.x ссылки на файл css при изменении содержимого файла. например, если он был, вы можете изменить его, чтобы обойти кеширование.
Хороший момент, но тогда браузер всегда спрашивает о файле. Конечно, сервер вернет «304 Not Modified».
@ajozwik Я тестировал Firefox 26.0 и Chromium 31.0.1650.57. Как вы и сказали, Firefox действительно делает новый запрос каждый раз, если URL-адрес CSS содержит вопросительный знак (и сервер дает ответ 304 с пустым телом); Firefox не выполняет новый запрос, если URL-адрес CSS не содержит вопросительного знака. Chromium не делает новый запрос даже со знаком вопроса. Наверное, это можно считать ошибкой Firefox.
Я нашел во многих других местах тот же подход, что и в этом ответе, я считаю, что это работало в прошлом, но больше не работает. Если вы находитесь в режиме разработки, лучший и универсальный подход - отключить кеширование в браузере с помощью метатегов HTML. Смотрите мой ответ ниже.
Если вы умеете писать php, вы можете написать:
Он всегда будет обновляться!
РЕДАКТИРОВАТЬ: Конечно, это непрактично для всего веб-сайта, так как вы не добавляете это вручную для всего.
Так не работает! Только если имя файла другое, браузер перезагружается. Или, может быть, с этой подсказкой работают только некоторые браузеры, если она у вас сработала
Предполагая, что ваш файл css - это foo.css , вы можете заставить клиента использовать последнюю версию, добавив строку запроса, как показано ниже.
Точка зрения разработчика
Если вы находитесь в режиме разработки (как в исходном вопросе), лучший подход - отключить кеширование в браузере с помощью метатегов HTML. Чтобы сделать этот подход универсальным, вы должны вставить как минимум три метатега, как показано ниже.
Таким образом, вам, как разработчику, достаточно обновить страницу, чтобы увидеть изменения. Но не забывайте комментировать этот код в процессе производства, в конце концов, кеширование - это хорошо для ваших клиентов.
Рабочий режим
Поскольку в производственной среде вы разрешаете кэширование, и вашим клиентам не нужно знать, как принудительно выполнить полную перезагрузку или какой-либо другой трюк, вы должны гарантировать, что браузер загрузит новый файл. И да, в этом случае лучший подход, который я знаю, - это изменить имя файла.
Он обновится при изменении.
Очень хорошее решение для всех тех проектов, которые не используют веб-пакет или инструмент упаковки, который автоматически добавляет версии к ресурсам.
Я не отрицал его, но на сегодняшний день это решение плохо работает в последней версии Chrome, оно все еще загружает предыдущую версию файла JavaScript
Убедитесь, что это не происходит из вашего DNS. Например, у Cloudflare есть он, где вы можете включить режим разработки, в котором он принудительно очищает ваши таблицы стилей и изображения, поскольку Cloudflare предлагает ускоренный кеш. Это отключит его и заставит обновлять каждый раз, когда кто-то посещает ваш сайт.
Всем привет!!
Разираюсь с nginx и включил кеширование статичных файлов, ну там скриптов, CSS, картинок и т.д на стороне пользовательского браузера. Так вот, когда я меняю что-то в CSS или javascript, то изменения не применяются пока не нажать последовательно CTRL+R CTRL+F5. Только тогда браузер скачивает новые скрипты и стили. Ладно я могу так сделать пока тестирую и разрабатываю сайт, но пользователь же так не будет делать, да и возможно не знает о таком. Как можно заставить пользовательский браузер обновить эти файлы, когда включено кеширование?
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Подписчики
Чтобы обновить javascript или CSS файлы, необходимо, чтобы url этих файлов немного отличался. Но для этого вовсе не обязательно менять имя самого файла. Можно просто добавлять в url знак вопроса и число. Тогда браузер при скачивании страницы обнаружит, отличающийся url и загрузит обновлённый скрипт заново. В HTML коде это будет выглядеть так:
Таким образом, когда вы выкатываете новую версию скрипта или стилей, достаточно будет поменять число, например увеличить на единицу.
Кто-то ещё делает автоматический расчёт контрольной суммы и добавляет её вместо вручную введённой цифры. Расчёт делается сервером, но это только добавит нагрузку на мой взгляд, которая не имеет смысла. Скриптов всё равно может быть не так уж и много, что можно вручную поменять пару циферок. Во всяком случае на небольшом ресурсе это можно сделать и вручную, а на крупном проекте новые обновления не так часто выкатываются, что можно также поменять пару циферок.
К слову говоря, например, ВКонтакте по всей видимости так и делают, если взглянуть на url их скрипта openapi.js, который служит для добавления всяких виджетов на сайты.
Комментарии
Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Многие веб-сайты в Интернете содержат JavaScript, язык программирования сценариев, который работает в веб-браузере, чтобы сделать конкретные функции на веб-странице функциональными. Если JavaScript был отключен в вашем браузере, содержание или функциональность веб-страницы могут быть ограничены или недоступны. В этой статье описаны шаги для включения JavaScript в веб-браузерах.
Дополнительная информация
Исследователь Интернета
Чтобы все веб-сайты в зоне Интернета запускали скрипты в Internet Explorer:
В меню веб-браузера нажмите «Инструменты» или значок «Инструменты» (который выглядит как шестерня) и выберите параметры Интернета.
При открытии окна "Интернет-опционы" выберите вкладку Безопасности.
На вкладке "Безопасность" убедитесь, что интернет-зона выбрана, а затем нажмите на "Таможенный уровень. " Кнопку.
В настройках безопасности — диалоговом поле «Интернет-зона» нажмите «Включить» для активного сценария в разделе Сценарий.
Когда открывается окно "Предупреждение!" и спрашивает: "Вы уверены, что хотите изменить настройки для этого zone?" выберите "Да"
Нажмите OK в нижней части окна Опционов Интернета, чтобы закрыть диалог.
Чтобы разрешить написание сценариев на определенном веб-сайте, оставляя сценарий отключенным в зоне Интернета, добавьте определенный веб-узел в зону «Доверенные сайты»:
При открытии окна "Интернет-опционы" выберите вкладку Безопасности.
На вкладке "Безопасность" выберите зону "Доверенные сайты", а затем нажмите кнопку "Сайты".
Нажмите Закрыть, а затем нажмите OK в нижней части окна Интернет опционов, чтобы закрыть диалог.
Google Chrome
Чтобы включить JavaScript в Google Chrome, пожалуйста, просмотрите и следуйте инструкциям, предоставленным на Enable JavaScript в вашем браузере, чтобы увидеть объявления на вашемсайте.
Firefox корпорации Mozilla
Для включения JavaScript в Firefox, пожалуйста, просмотрите и следуйте инструкциям, предоставленным в настройках JavaScript для интерактивных веб-страниц.
Читайте также: