Что такое control f5
Все мы знаем сотню способов загрузки скриптов. У каждого свои плюсы и минусы.
Хочу представить вам очередной метод загрузки js-файлов. Я также понимаю, что такой метод активно используется в сети, но статей про него я не видел.
Поэтому опишу способ, которым пользуюсь сам, в надежде, что он вам тоже понравится.
Цели: модульность разработки, быстрота загрузки, валидный кэш.
Бонус: индикатор загрузки
UPD. Обозначил главную цель этого метода — валидный кэш.
При использовании данного метода, у вас не будет неуверенности в том, обновится ли скрипт и будет ли он работать у конечного пользователя.
UPD 2. Для тех кто не дочитывает до конца (я вас прекрасно понимаю), в концовке сказано, как всё можно сделать намного проще.
Вместо core.633675510761.js писать core.js?v=633675510761. И там же указано, почему всё же написано так много.
UPD 3. В комментариях от david_mz, WebByte прозвучало предложение для обработки запроса использовать не JSHandler, а urlrewrite.
Под модульностью я понимаю, что каждый компонент системы расположен в отдельном файле: 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-серверов. Вообщем, на что мы повлияеть не можем.
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 >();
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;
>
>
* 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;
/* String encoding = context.Request.Headers["Accept-Encoding"];
if( encoding != null && encoding.IndexOf( "gzip", StringComparison.InvariantCultureIgnoreCase ) >= 0 &&
File.Exists( file + ".jsgz" ) )
<
file = file + ".jsgz";
context.Response.AppendHeader( "Content-Encoding", "gzip" );
>
else*/
if ( File .Exists( file + ".jsmin" ) ) file = file + ".jsmin" ;
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
Зачем нужны сочетания клавиш Alt + F4 и Ctrl + F5
Различные сочетания клавиш часто используются для шуток над неопытными пользователями. Причем касается это не только распространенных и общеупотребимых комбинаций, но и таких, которые используются в одной конкретной программе. Часто это могут быть онлайн-игры, где новичка таким образом вынуждают делать какие-либо невыгодные для него действия.
Однако среди клавиатурных сокращений, связанных с верхним рядом клавиш на клавиатуре, пользователей часто интересуют именно Alt + F4 и Ctrl + F5. Это стандартные комбинации, которые могут пригодиться каждый день. Но к сожалению, даже о таких простых вещах не все могут знать.
Сочетание Alt + F4 используется уже давно и превратилось в настоящий мем. Его задача — немедленно закрыть текущее окно или программу. При этом в некоторых программах может появляться диалоговое окно с подтверждением. Существуют также и другие комбинации, которые менее известны, а поэтому могут сбить с толку и более опытных пользователей, например Ctrl+W.
Что касается Ctrl + F5, то это довольно полезное сочетание, о котором многие не знают. Наверняка вы знаете, что F5 используется для обновления страницы в браузере. Ctrl + F5 делает практически то же самое, но есть одна особенность.
Дело в том, что при нажатии F5 или Ctrl + R происходит обновление страницы с учетом кэша. То есть браузер перезагружает страницу, но некоторые данные берет из кэша. Часто это изображения. Если что-то изменилось, то новый контент может не отобразиться на странице. Сочетания Ctrl + F5 или Shift + F5 обновляют страницу полностью. Кэш при этом также обновляется. Иначе говоря, вы точно получите актуальную версию страницы.
В чём разница между Ctrl+R и F5?
f5 - обновление страницы, Ctrl+f5 - обновляет кеш браузера. То есть, если нажать ф5 загрузится версия сайта из кеша браузера. Если нажать ктрл+ф5 кеш браузера обновится и загрузится текущая версия страницы. Ctrl+f5 полезно при работе над сайтом. Например админ внёс изменения, но пользователь, если он был на сайте раньше, попадает на версию странички сохранённую в браузере.
Для обычного пользователя нет разницы ctrl+r или f5.
Для админа, который тестит новые фишки сайта, только - ctrl+F5
Обновить страницу, не используя кеш (загрузить страницу с сайта)
Ctrl + F5
или
Ctrl + R
90% людей не знают про Ctrl+F
Дэн Рассел в компании Google занимает должность «поискового антрополога» и проводит различные исследования, как люди используют поисковые сайты. Некоторые результаты этих исследований кажутся просто невероятными. Например, по словам Рассела, 90% людей не умеют использовать Ctrl+F (Cmd+F) при поиске информации на странице: «Это проверено на выборке в несколько тысяч человек. Я лично проводил эти исследования и могу рассказать, как часто сидел у кого-нибудь дома и смотрел, как он листает длинный документ в поисках информации. В конце концов я говорил: “Позвольте показать один маленький фокус”, после чего обычно человек изумлялся: “Не могу поверить, что я раньше столько времени тратил впустую”».
Это действительно удивительный факт. Нехватка элементарных знаний приводит к напрасной трате миллионов человеко-часов. Выход тут может быть только один — основы поиска информации нужно включить в школьную программу. Вот универсальный план занятий и сопроводительные материалы, которые предлагает использовать Google для этих целей. Уроки разработаны преподавателями совместно с командой Google Search Education по модульному принципу, то есть их можно сочетать любым способом. Каждый блок состоит из трёх уроков разного уровня сложности. Специально для школ все материалы опубликованы под свободной лицензией Creative Commons Attribution Share-alike.
Вместо концовки
Возможно вы скажете, много сложностей с реализацией отдельного хэндлера.
Могу посоветовать более простой способ обработки файла. Вместо писать
Тогда не нужен JSHandler. Но я не уверен как будет работать кэш. Фактически имя файла не меняется, а появляется только дополнительный параметр. Т.е. может возникнуть та же проблема с Ctrl-F5 из-за внутренного кэша браузера или прокси-сервера.
Но мне отдельный JSHandler нужен ещё для проверки прав на доступ к скриптам, например из папки Admin отдаю только админам.
Плюс ко всему кому-то будет полезно и интересно посмотреть реализации JSHander'а и монитора файловой системы.
Если задача JSHandler только в отрезании ключа модификации, то его можно заменить urlrewrite-модулем.
Очевидно, что таким способом можно грузить другие типы файлов, например .css. Я так и делаю, это видно на первом скриншоте, я специально навёл на css-файл курсор.
Можно расширить и на другие типы, например картинки. Но это нецелесообразно. Во-первых, замучаетесь в коде проставлять нужные имена, а во-вторых, картинки меняются крайне редко, поэтому если какая-то и застрянет в кэша браузера, то не страшно. А если страшно, переименуйте эту картинку вручную.
И обещанный бонус с индикатором загрузки.
Пока грузятся ваши скрипты первый раз, визуально ускорить процесс можно показав процесс загрузки.
Помните место где я пишу включение файлов? Там на самом деле код такой:
* This source code was highlighted with Source Code Highlighter .
Рисуется два div'а. И второму по мере загрузки наращивается ширина.
В CSS это выглядит вот так:
Читайте также: