Ctrl+F5 в браузере — что это такое? Приветствую! Ctrl+F5 — комбинация клавиш для обновления страницы напрямую, не обращаясь в кэш. При этом, данные в кэше также обновятся. Если нажать
Загрузка JavaScript-файлов. Решаем проблему Ctrl-F5
Все мы знаем сотню способов загрузки скриптов. У каждого свои плюсы и минусы.
Хочу представить вам очередной метод загрузки js-файлов. Я также понимаю, что такой метод активно используется в сети, но статей про него я не видел.
Поэтому опишу способ, которым пользуюсь сам, в надежде, что он вам тоже понравится.
Цели: модульность разработки, быстрота загрузки, валидный кэш.
Бонус: индикатор загрузки
UPD. Обозначил главную цель этого метода — валидный кэш.
При использовании данного метода, у вас не будет неуверенности в том, обновится ли скрипт и будет ли он работать у конечного пользователя.
UPD 2. Для тех кто не дочитывает до конца (я вас прекрасно понимаю), в концовке сказано, как всё можно сделать намного проще.
Вместо core.633675510761.js писать core.js?v=633675510761. И там же указано, почему всё же написано так много.
UPD 3. В комментариях от david_mz, WebByte прозвучало предложение для обработки запроса использовать не JSHandler, а urlrewrite.
Ctr+F5 в браузере что это такое?
Под модульностью я понимаю, что каждый компонент системы расположен в отдельном файле: core.js, utils.js, control.js, button.js и т.д.
От этого принципа я не отказываюсь даже при загрузке страницы. Хотя я знаю, что загрузка 1 файла 100Кб быстрее 10-ти по 10Кб.
Эту проблему я решу через кэширование далее.
- изменение в одном компоненте приводит к необходимости перегрузке всего пакета
- высока вероятность дублирования компонентов в разных пакетах
Помимо использования заголовков «If-Modified-Since» и «If-None-Match» (ETag) я устанавливаю Expires через год!
Теперь почему я так смело поступаю и уверен что мой файл будет год валидным.
Потому что я приписываю к имени файла дату его последней модификации!
Т.е. есть core.js, включение происходит так
- мы получаем надежный кэш файла. Даже оперируя заголовками Last-Modified и ETag, мы не всегда получаем последнюю версию файла. Браузер или прокси-сервер, не всегда запрашивают информацию о файле. Часто они берут свой кэш. В таких случаях обычное явление сброс кэша по Ctrl-F5. Знакомо? Теперь же, взять из внутреннего кэша старый файл не возможно, т.к. мы запрашиваем фактически новый файл с новым именем.
- пропадают все запросы на проверку If-None-Match и If-Modified-Since. Даже если файл не изменился и сервер возращает Not Modified 304, всё равно каждый файл — это новый запрос — это задержка. Теперь повторные обращения к одному файлу сразу берут его из кэша. Здорово, не правда ли?
Для наглядности приведу пару скриншотов. Интернет очень медленный.
Первое обращение к странице.
Второе:
Изменяем один файл — третье обращение
Как видите из второго и третьего рисунков, браузер обновил изменённый скрипт. Также видно, что он не удосужился проверить все файлы на наличие изменений. Т.е. на странице много картинок, а он почему-то проверил только две. Тоже самое происходит со скриптами. Они обновляются не всегда. Web-сервер может устанавить дополнительные заголовки для статических файлов, вроде Set-Expires + (1-9999) минут. Плюс внутреняя логика браузера и proxy-серверов. Вообщем, на что мы повлияеть не можем.
Это была теория. Реализовать на практике это не составляет никакого труда.
Я приведу пример как я решаю это на ASP.NET. Поэтапно.
1. Для включения файлов на страницу я использую специальный объект, который проверяет на уникальность включаемого файла. А затем при рендинге пишет мои файлы с префиком даты.
public class ScriptHelper
protected StringCollection includeScripts = new StringCollection();
public void Include( String filename )
filename = filename.ToLower();
StringCollection container;
switch ( System.IO.Path.GetExtension( filename ) )
case ".js": container = includeScripts; break;
default: throw new ArgumentException( "Not supported include file: " + filename, "filename" );
>
if ( !container.Contains( filename ) ) container.Add( filename );
>
public void RegisterScripts( Page page )
StringBuilder clientScript = new StringBuilder ();
foreach ( String filename in includeScripts )
clientScript.AppendFormat( includeJS, prefix + FileSystemWatcherManager.GetModifiedName( "Scripts/" + filename ) );
page.ClientScript.RegisterClientScriptBlock( page.GetType(), "clientscripts", clientScript.ToString(), false );
>
>
* This source code was highlighted with Source Code Highlighter.
Комментарий:
prefix — префикс относительного пути папки со скриптами
FileSystemWatcherManager — менеджер по работе с физическими файлами. Этот класс позволяет избегать частых вызовов System.IO.File.GetLastWriteTimeUtc(), и является простой оболочкой монитора файловой системы. Позволю себе привести полный код.
using System;
using System.IO;
using System.Collections. Generic;
public class FileSystemWatcherManager
private static String physicalAppPath;
private static SortedList < String, Int64 >lastModifiedFiles = new SortedList< String, Int64 >();
foreach ( String pattern in filter.Split( ‘,’ ) )
FileSystemWatcher dirWatcher = new FileSystemWatcher( directory, pattern );
dirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
dirWatcher.IncludeSubdirectories = true;
dirWatcher.EnableRaisingEvents = true;
dirWatcher.Changed += new FileSystemEventHandler( OnFileSystemChanged );
dirWatcher.Created += new FileSystemEventHandler( OnFileSystemChanged );
dirWatcher.Renamed += new RenamedEventHandler( OnFileSystemRenamed );
UpdateLastModifiedFiles( directory, pattern, true );
>
>
private static void OnFileSystemRenamed( object sender, RenamedEventArgs e )
UpdateLastModifiedFiles( Path.GetDirectoryName( e.FullPath ), ( (FileSystemWatcher)sender ).Filter, true );
>
private static void OnFileSystemChanged( object sender, FileSystemEventArgs e )
UpdateLastModifiedFiles( Path.GetDirectoryName( e.FullPath ), ((FileSystemWatcher)sender).Filter, true );
>
public static void UpdateLastModifiedFiles( String directory, String filter, Boolean logAction )
lock ( lastModifiedFiles )
if ( logAction ) WL.Logger.Instance.Log( String.Format( "Update modified files at """, directory, filter ) );
foreach ( String subDir in Directory.GetDirectories( directory ) )
UpdateLastModifiedFiles( subDir, filter, false );
foreach ( String file in Directory.GetFiles( directory, filter ) )
lastModifiedFiles[file.Substring( physicalAppPath.Length ).ToLower().Replace( ‘\’, ‘/’ )] = File.GetLastWriteTimeUtc( file ).Ticks / 1000000;
>
>
public static String GetModifiedName( String clientPath )
#if DEBUG
return clientPath;
#endif
lock ( lastModifiedFiles )
Int64 ticks;
if ( !lastModifiedFiles.TryGetValue( clientPath.ToLower(), out ticks ) ) return clientPath;
return String.Format( "/.", Path.GetDirectoryName( clientPath ).Replace( ‘\’, ‘/’ ), Path.GetFileNameWithoutExtension( clientPath ), ticks, Path.GetExtension( clientPath ) );
>
>
>
* This source code was highlighted with Source Code Highlighter.
Вызов в global.asax
void Application_Start( object sender, EventArgs e )
FileSystemWatcherManager.StartDirectoryWatcher( HttpContext.Current.Request.PhysicalApplicationPath, "*.js,*.css" );
>
* This source code was highlighted with Source Code Highlighter.
Думаю, комментарии излишни, единственное отмечу, под DEBUG режимом, я использую реальные имена файлов, чтобы дебаггер мог их цеплять.
Обработчик нужен чтобы удалить префикс со временем изменения и отдать реальный файл. Также он проверяет заголовки If-None-Match, If-Modified-Since, устанавливает LastModified, ETag и Expires. Также возможен выбор файла, оригинальный, минимизированый, сжатый, проверка прав и прочее.
Привожу облечённую версию.
public class JSHandler: IHttpHandler
public void ProcessRequest( HttpContext context )
try
String filepath = context.Request.PhysicalPath;
String [] parts = filepath.Split( ‘.’ );
Int64 modifiedTicks = 0;
if ( parts.Length >= 2 )
if ( Int64.TryParse( parts[parts.Length — 2], out modifiedTicks ) )
List < String >parts2 = new List < String >( parts );
parts2.RemoveAt( parts2.Count — 2 );
filepath = String.Join( ".", parts2.ToArray() );
>
>
FileInfo fileInfo = new FileInfo( filepath );
if ( !fileInfo.Exists )
context.Response.StatusCode = 404;
context.Response.StatusDescription = "Not found";
>
else
DateTime lastModTime = new DateTime ( fileInfo.LastWriteTime.Year, fileInfo.LastWriteTime.Month, fileInfo.LastWriteTime.Day, fileInfo.LastWriteTime.Hour, fileInfo.LastWriteTime.Minute, fileInfo.LastWriteTime.Second, 0 ).ToUniversalTime();
String ETag = String.Format( """", lastModTime.ToFileTime().ToString( "X8", System.Globalization.CultureInfo.InvariantCulture ) );
if ( ETag == context.Request.Headers[ "If-None-Match" ] )
context.Response.StatusCode = 304;
context.Response.StatusDescription = "Not Modified";
>
else
if ( context.Request.Headers[ "If-Modified-Since" ] != null )
String modifiedSince = context.Request.Headers[ "If-Modified-Since" ];
Int32 sepIndex = modifiedSince.IndexOf( ‘;’ );
if ( sepIndex > 0 ) modifiedSince = modifiedSince.Substring( 0, sepIndex );
DateTime sinceDate;
if ( DateTime.TryParseExact( modifiedSince, "R", null, System.Globalization.DateTimeStyles.AssumeUniversal, out sinceDate ) &&
lastModTime.CompareTo( sinceDate.ToUniversalTime() ) == 0 )
context.Response.StatusCode = 304;
context.Response.StatusDescription = "Not Modified";
>
>
if ( context.Response.StatusCode != 304 )
String file = fileInfo.FullName;
if ( context.Request.HttpMethod == "GET" )
context.Response.TransmitFile( file );
>
context.Response.Cache.SetCacheability( HttpCacheability.Public );
context.Response.Cache.SetLastModified( lastModTime );
context.Response.Cache.SetETag( ETag );
if ( modifiedTicks != 0 )
context.Response.Cache.SetExpires( DateTime.UtcNow.AddYears( 1 ) );
context.Response.AppendHeader( "Content-Type", "text/javascript" );
context.Response.StatusCode = 200;
context.Response.StatusDescription = "OK";
>
>
>
catch ( Exception ex )
WL.Logger.Instance.Error( ex );
context.Response.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error";
>
>
* This source code was highlighted with Source Code Highlighter.
Замечание. Если заметили я отдаю либо оригинальный файл, либо.jsmin, либо.jsgz.
Это минимизированная и сжатая версии, которые строятся автоматически отдельной тулзой при билде сервера. Чтобы запретить прямой доступ к ним надо добавить в web.config
Вместо концовки
Возможно вы скажете, много сложностей с реализацией отдельного хэндлера.
Могу посоветовать более простой способ обработки файла. Вместо писать
Тогда не нужен JSHandler. Но я не уверен как будет работать кэш. Фактически имя файла не меняется, а появляется только дополнительный параметр. Т.е. может возникнуть та же проблема с Ctrl-F5 из-за внутренного кэша браузера или прокси-сервера.
Но мне отдельный JSHandler нужен ещё для проверки прав на доступ к скриптам, например из папки Admin отдаю только админам.
Плюс ко всему кому-то будет полезно и интересно посмотреть реализации JSHander’а и монитора файловой системы.
Если задача JSHandler только в отрезании ключа модификации, то его можно заменить urlrewrite-модулем.
Очевидно, что таким способом можно грузить другие типы файлов, например.css. Я так и делаю, это видно на первом скриншоте, я специально навёл на css-файл курсор.
Можно расширить и на другие типы, например картинки. Но это нецелесообразно. Во-первых, замучаетесь в коде проставлять нужные имена, а во-вторых, картинки меняются крайне редко, поэтому если какая-то и застрянет в кэша браузера, то не страшно. А если страшно, переименуйте эту картинку вручную.
И обещанный бонус с индикатором загрузки.
Пока грузятся ваши скрипты первый раз, визуально ускорить процесс можно показав процесс загрузки.
Помните место где я пишу включение файлов? Там на самом деле код такой:
* This source code was highlighted with Source Code Highlighter.
Рисуется два div’а. И второму по мере загрузки наращивается ширина.
В CSS это выглядит вот так:
Открыть главное меню браузера. Alt + F. Alt + E. ⌘ + O. Обновить страницу. F 5. Ctrl + R. ⌘ + R. Обновить страницу, не используя кеш (загрузить страницу с сайта). Shift + F 5. Ctrl + Shift + R. Shift + ⌘ + R. Закрыть браузер. Ctrl + Shift + Q. ⌘ + Q. Свернуть браузер. нет. ⌘ + H.
Ctrl+F5 в браузере — что это такое?
Приветствую! Ctrl+F5 — комбинация клавиш для обновления страницы напрямую, не обращаясь в кэш. При этом, данные в кэше также обновятся.
Если нажать просто F5 — страница будет обновлена, однако статичные элементы будут взяты из кэша (например изображения, флеш, анимация). Чтобы обновить полностью — как раз нужно нажать Ctrl+F5.
По некоторым данным, одни браузеры поддерживают Ctrl+F5, другие — Ctrl+R.
Проверено лично — Google Chrome поддерживает Ctrl+F5.
Немного подробнее о том, как это работает
- Вы открываете страницу по некому веб-адресу.
- Браузер загружает исходный код — html (язык разметки).
- Весь код состоит из тегов, которые для браузера представляют собой инструкцию по составлению страницы.
- Есть тег картинок — нужно скачать картинку, в тегах указывается адрес. Таким образом скачиваются и другие элементы — видео, аудио, анимация. Для каждого вида медиа-контента — свой тег.
- Весь процесс занимает пару секунд, на быстром интернете — меньше секунды (при современном железе). Браузер все скачал — страница построена.
- При повторном открытии страницы нет смысла все качать заново — многие элементы не изменились. Логично их брать из кэша, куда они поместились при первом посещении. И здесь напомню: F5 — просто обновить. Ctrl+F5 — удалить кэш и обновить, при этом поместив в кэш новые данные.
На заметку: чтобы в Хроме очистить кэш, зажмите кнопки Ctrl + Shift + Del — откроется вкладка:
Дальше — нажать Дополнительно, внимательно проверить галочки, лишние снять.
Кстати не совсем понятно почему кэш нельзя отключить в Хроме. Сегодня даже самый дешевый тариф интернета предлагает скорость, при которой можно обойтись без кэша. Ведь большое количество файлов в кэше на жестком диске явно скорости браузеру не прибавит.
Изучите сочетания клавиш, чтобы быстрее и эффективнее пользоваться браузером Chrome. Windows и Linux Сочета. F 5 или Ctrl + R. Обновить текущую страницу без учета кешированного контента. Shift + F 5 / Ctrl + Shift + R. Остановить загрузку страницы. Esc.
What requests do browsers’ "F5" and "Ctrl + F5" refreshes generate?
Is there a standard for what actions F5 and Ctrl + F5 trigger in web browsers?
I once did experiment in IE6 and Firefox 2.x. The F5 refresh would trigger a HTTP request sent to the server with an If-Modified-Since header, while Ctrl + F5 would not have such a header. In my understanding, F5 will try to utilize cached content as much as possible, while Ctrl + F5 is intended to abandon all cached content and just retrieve all content from the servers again.
But today, I noticed that in some of the latest browsers (Chrome, IE8) it doesn’t work in this way anymore. Both F5 and Ctrl + F5 send the If-Modified-Since header.
So how is this supposed to work, or (if there is no standard) how do the major browsers differ in how they implement these refresh features?
Большую часть времени в браузере мы открываем вкладки и окна, переключаемся между ними, ходим по ссылкам. Всё это легко и привычно делать с помощью мыши, но гораздо удобнее использовать горячие клавиши Chrome. Смотрите, как всё просто. Ctrl + F 5 или Shift + F 5 — принудительное обновление (без учёта кеша). Esc — отмена загрузки страницы. Ctrl + F — поиск. Ctrl + U — исходный код страницы. Ctrl + D — закладка для текущей страницы. Ctrl + Shift + D — закладки для всех открытых страниц. F11 — полноэкранный режим. Ctrl + плюс и Ctrl + минус — изменение масштаба страницы. Ctrl + 0 — стандартный масштаб страницы.
- https://habr.com/ru/post/51258/
- http://virtmachine.ru/ctrl-f5-v-brauzere-chto-eto-takoe.html
- https://stackoverflow.com/questions/385367/what-requests-do-browsers-f5-and-ctrl-f5-refreshes-generate