В этой статье я хочу указать причины, по которым прогрессивное усовершенствование является очень умным способом разработки программного обеспечения, и дать вам несколько советов и рекомендаций по разработке, когда дело доходит до его применения. Я твердо верю в прогрессивное совершенствование, и у меня была справедливая доля критики за это, так как многие люди не считают это ни необходимостью, ни простым методом применения — это часто рассматривается как ненужные накладные расходы, а не как мера предосторожности.
Вот некоторые из моих идей о разработке программного обеспечения:
- Вы никогда не развиваетесь для себя, но для человека, который берет на себя от вас. Сколько раз вы клялись в коде, который вам пришлось изменить, и назвали первоначальные имена разработчиков?
- У вас никогда не будет достаточно времени, чтобы все исправить — как только вы войдете в фазу исправлений, качество кода сильно пострадает.
- Компьютеры и браузеры тупые. Они потерпят неудачу, и они потерпят неудачу так, как вы не ожидали, и вы не можете воспроизвести. Код, как будто все сломается и подготовить ловушки для этого, и вы будете смеяться.
- Вы не имеете ни малейшего представления о том, что используется для доступа к контенту, который ваш код превращает в интерфейс — не думайте, что вы знаете, что нужно людям, и не обманывайте себя, полагая, что вы можете диктовать что угодно.
Вещи, которые помогают
Примеры кода в этой статье используют библиотеку пользовательского интерфейса Yahoo, и причина, по которой я ее использую, — чистый прагматизм. Есть только три причины не использовать библиотеки JavaScript в наши дни:
- Вы создаете очень индивидуальный проект с большим трафиком для особой среды.
- Вам нравится пытаться исправить случайные, невоспроизводимые ошибки браузера и упиваться попытками найти причины, по которым что-то не работает в определенном браузере в определенной версии и в определенной операционной системе.
- Вы все еще чувствуете вину за то, что оставили своих родителей на Криптоне.
Конечно, вы можете использовать любую библиотеку, какую захотите, так как все хорошие библиотеки хотят сделать вашу жизнь разработчика проще и помогают нам работать предсказуемо, пока браузеры не выполнят то, что обещают делать последовательно, и все не обновятся до тех, кто оставит старые. за.
Я был бы очень рад видеть, как кто-то делает jQuery или прототип версии этой статьи.
Семь правил прагматического прогрессивного улучшения
Работая с прогрессивным улучшением в течение долгого времени, я обнаружил, что все следующие правила имеют большой смысл и оказывают значительное влияние на конечный продукт с точки зрения удобства сопровождения, размера кода и общей надежности:
- Разделите как можно больше
- Основываться на вещах, которые работают
- Создать зависимую разметку
- Проверьте все, прежде чем применять его
- Исследовать окружающую среду
- Нагрузка по требованию
- Модульный код
1. Разделите как можно больше
Это действительно первый шаг к прогрессивному улучшению. Только когда вы отделяете свою структуру (HTML), презентацию (CSS) и поведение (JavaScript) от других, вы получаете следующие преимущества:
- Вы оставляете за браузером возможность применять технологии, которые он поддерживает, и вам не нужно угадывать или предполагать.
- Вы знаете, где исправить проблему, когда она возникает.
- Вы разрешаете браузеру кэшировать вещи, которые он не должен перезагружать на каждой странице.
- Вы допускаете более простое обслуживание, так как сопровождающему не нужно задействовать все наборы навыков — вместо этого вы позволяете экспертам и людям, увлеченным предметом, выполнять работу — и это означает хорошую работу.
2. Основывайтесь на том, что работает
Используя JavaScript, вы можете превратить что-либо в документе в интерактивный элемент — он может реагировать на щелчки, зависание мыши, нажатие клавиш и так далее.
Тем не менее, вы симулируете реальную вещь, и симуляция никогда не бывает такой хорошей. Интерактивные элементы в браузерах имеют большое значение — они отправляют информацию на сервер, говорят о вспомогательных технологиях и, что самое важное, работают с множеством устройств ввода.
Если вы готовы к этому вызову, бог скорости и пусть вам удастся. Лично я предпочел бы опираться на вещи, которые работают, а в случае с браузерами это формы, ссылки и кнопки. Скажем, например, span с обработчиком кликов:
<span onclick=”help()”>Help</span>
Это делает работу, когда JavaScript включен и у пользователя есть мышь. Тем не менее, вы не можете добраться до него с помощью клавиатуры, и без JavaScript ничего не происходит.
Вместо этого имеет смысл использовать что-то, что работает без JavaScript (за которым может следовать поисковая система, например), и улучшать его, когда это возможно. Например, ссылка на «справочное» представление вашего приложения улучшено для вызова doHelp()
функции (в данном случае с использованием YUI):
<a href="/help" id="help">Help</a>
<script type="text/javascript">
YAHOO.example.helpdemo = function(){
function doHelp(){
// other work here…
}
YAHOO.util.Event.on('help','click',function(e){
doHelp();
YAHOO.util.Event.preventDefault(e);
});
}();
</script>
Таким образом, вы знаете, что в случае сбоя JavaScript — по какой-либо причине — ваш сайт / приложение все еще будет использоваться.
Это вопрос доверия — посетители, приходящие на ваш сайт или использующие ваше приложение, доверяют вам, чтобы дать им то, для чего они пришли. Если вы предлагаете им ссылку, а она не выполняет то, что обещает, вы нарушили их доверие. Вы потеряли компетенцию в их глазах, и они вряд ли вернутся или продолжат процесс использования вашего сайта (или, в худшем случае, проверку процесса покупки).
Вы можете сделать любую конструкцию похожей на строку меню, дерево или группу вкладок, но очень важно подумать о нескольких вещах:
- Если JavaScript недоступен, имеет ли смысл конструкция? Есть ли иерархия?
- Вы моделируете шаблон взаимодействия, который доступен в более широком мире, особенно в дизайне приложений. Вы знаете, как люди используют это там? Например, для навигации по меню необходимо использовать клавиши со стрелками, а не переходить от ссылки к ссылке с помощью клавиши табуляции.
3. Создать зависимую разметку
Иногда вам нужен HTML, который имеет смысл только тогда, когда он улучшается с помощью JavaScript. Классическим примером является ссылка «напечатать это».
<a href="window.print()">Print this document</a>
Технически, что мы здесь делаем, это моделируем поведение браузера и создаем ярлык для кнопки печати браузера. Не очень нужно, но могут быть причины для этого. Однако, чтобы сделать это безопасным, вам еще раз не нужно полагаться на доступность функциональности JavaScript.
Поэтому вместо приведенного выше примера генерируйте ссылку только тогда, когда JavaScript доступен с использованием DOM:
<div id="printthis">
<h5>Easy printing</h5>
<p>This document has a separate style for print
versions. All you need to do is print it with your
browser.</p>
</div>
<script type="text/javascript">
YAHOO.example.printThis = function(){
// test if the element with the ID printthis is available
// and can be modified
YAHOO.util.Event.onContentReady('printthis',function(){
// create a new Paragraph and append it to the element
var p = document.createElement('p');
this.appendChild(p);
// set the text content of the paragraph
var text = 'Alternatively you can use the following link: ';
p.appendChild(document.createTextNode(text));
// create a new link, set its href to window.print()
var printLink = document.createElement('a');
printLink.setAttribute('href','javascript:window.print()');
// set the text content and append it to the main element
var linkText = 'Print this document';
printLink.appendChild(document.createTextNode(linkText));
p.appendChild(printLink);
});
}();
</script>
Таким образом, вы можете сказать посетителям, как они могут распечатать документ и сделать его доступным в виде ссылки.
Примечание. Как правило, нет необходимости иметь ссылку, использующую неофициальный javascript:
псевдопротокол. Однако, поскольку мы генерируем ссылку, это короткий способ создания функциональности ссылки. Мы могли бы добавить обработчик события и вызвать window.print()
его. Но тогда нам нужен бессмысленный href
атрибут, такой как «#», чтобы сделать ссылку доступной через клавиатуру.
В этом примере используется умный метод YUI onContentAvailable()
. Этот метод проверяет, доступен ли элемент с идентификатором, который вы анализируете, и можно ли изменить его дочерние элементы. Это техника безопасности, которая является еще одной важной частью прагматического прогрессивного улучшения.
4. Проверьте все, прежде чем применять его
Это должно быть очень очевидно — в конце концов вы проверяете глубину воды, прежде чем окунуться в нее, или посмотрите, может ли лед удержать вас, прежде чем наступить на нее, — но мы все время делаем это неправильно.
Это просто: если вы пытаетесь применить что-то к чему-либо, проверьте, что это доступно. Скажем, например, что вы хотите добавить класс к элементу, когда он доступен и JavaScript включен. Вы можете сделать это следующим образом:
<script type="text/javascript" charset="utf-8">
var c = document.getElementById('intro');
c.className = 'jsenabled';
</script>
Проблема в том, что если нет элемента с идентификатором intro
, вторая строка выдаст ошибку, поскольку вы не можете изменить свойство undefined
. Более безопасный способ — проверить, c
существует ли, прежде чем пытаться изменить свойства или вызвать методы.
<script type="text/javascript" charset="utf-8">
var c = document.getElementById('intro');
if(c){
c.className = 'jsenabled';
}
</script>
if(c){}
ленивый подход тестирования. Ничто там не говорит вам, что это действительно элемент. Параноидальная версия того же кода будет:
<script type="text/javascript" charset="utf-8">
var c = document.getElementById('intro');
if(c && c.nodeType && c.nodeType === 1){
c.className = 'jsenabled';
}
</script>
Обратите внимание на тройной знак равенства. Это безопаснее, чем двойное равенство, поскольку оно проверяет как тип, так и значение. Двойной знак равенства говорит, например, что
это одно 1
и
true
то же, но вам может понадобиться целое число.
Еще один замечательный инструмент typeof
, который позволяет вам убедиться, что то, что вы хотите использовать, это то, что вам нужно.
<script type="text/javascript" charset="utf-8">
myObject = 1;
// check if myObject is an object
if(typeof myObject !== 'object'){
// if it isn't, check that it is defined
if(typeof myObject !== 'undefined'){
// if it is defined, keep a copy in stored
var stored = myObject;
}
// create a new object
var myObject = {};
// store the old information as a property
myObject.stored = stored;
}
// now it is safe to add new properties, as the
// object is created when there is none;
myObject.state = 'saved';
</script>
5. Исследуйте окружающую среду
Единственное, что вы можете сделать, когда узнаете, что JavaScript доступен, — это использовать его для изучения того, что браузер может безопасно отображать. Вы можете сделать это несколькими способами:
- Вы читаете размер окна просмотра браузера
- Вы читаете положение и размеры элементов
- Вы читаете размеры документа и сравниваете область просмотра с ним, зная, какая часть документа в данный момент видна.
- Вы читаете, как далеко пользователь прокрутил документ.
Таким образом, вы можете написать гораздо более четкие и умные решения. Например, у вас есть меню, в котором есть несколько подменю, которые появляются, когда вы наводите курсор мыши или щелкаете по нему. Это может быть проблемой, как показано на следующем рисунке:
На шаге 1 все хорошо, проблемы начинаются при расширении меню третьего уровня. Если вы просто предполагаете, что можете показать его слева от меню второго уровня (и это то, что делают * все * решения CSS), вы можете вызвать горизонтальную полосу прокрутки. Это также не конец света, но это делает меню третьего уровня недоступным для пользователей мыши (на этот раз пользователям клавиатуры — это не мешало бы). Если вы попытаетесь прокрутить документ, чтобы достичь третьего уровня меню, меню снова свернется — это разочаровывает.
Обходной путь — это прочитать ширину веб-порта браузера (окно без всякого хрома и полос прокрутки), проверить начальную точку элемента уровня меню, который вы хотите показать, и его размеры и рассчитать, подходит ли он. Если он не помещается (вертикально или горизонтально), вы пытаетесь показать его на противоположной стороне (как показано в шаге 3) и избегаете проблемы с полосой прокрутки.
Примечание. Подобные уловки являются дорогостоящими с точки зрения времени вычислений и реальной болью при работе в браузерах. Чтобы обеспечить адаптивность ваших сайтов и оставаться в здравом уме, рекомендуется использовать библиотеку JavaScript, которая решает обе эти проблемы.
6. Нагрузка по требованию
Другим аспектом прогрессивного улучшения является так называемая «отложенная загрузка» или загрузка по требованию. Это означает, что вы запускаете свои решения с как можно меньшим количеством кода и загружаете другие решения, когда и если они необходимы, а не на начальном этапе. Например, вы хотите проверить, доступен ли JavaScript и полностью ли поддерживается DOM, прежде чем загружать файлы библиотеки. Вы можете сделать это, создав новые узлы сценария с помощью DOM:
<script type="text/javascript" charset="utf-8">
(function(){
if(document.getElementById && document.createTextNode){
var s = document.createElement('script');
s.setAttribute('src','lib.js');
s.setAttribute('type','text/javascript');
var head = document.getElementsByTagName('head');
if(typeof head[0] !== 'undefined'){
head[0].appendChild(s);
}
}
})();
</script>
Эту технику можно использовать по-разному, например, вы можете проверить элемент с определенным идентификатором, имена классов в элементах (загружать классы анимации только при наличии animate
класса) — вы даже можете расширить его, включив таблицы стилей, поскольку ничто не мешает вам создавать link
элементы:
<script type="text/javascript" charset="utf-8">
(function(){
if(document.getElementById && document.createTextNode){
var s = document.createElement('script');
s.setAttribute('src','lib.js');
s.setAttribute('type','text/javascript');
var skin = document.createElement('link');
skin.setAttribute('rel','stylesheet');
skin.setAttribute('type','text/css');
skin.setAttribute('href','skin.css');
var head = document.getElementsByTagName('head');
if(typeof head[0] !== 'undefined'){
head[0].appendChild(s);
head[0].appendChild(skin);
}
}
})();
</script>
Примечание . Единственная проблема, которую вы обнаружите, заключается в том, что вы не можете рассчитывать на загрузку включенных файлов в том случае, если они вам нужны в определенном порядке. Для этого вам нужно либо написать свой сценарий включенным определенным образом (описанным в этой статье на 24ways — также на немецком языке ), либо воспользоваться утилитой Get YUI . Этот позволяет вам определять методы обратного вызова, которые запускаются, когда определенное включение (скрипт или файл CSS) было успешно получено.
7. Модульный код
Для того, чтобы разрешить загрузку по требованию, вам необходимо модулировать ваш код. Это начинается в ваших сценариях и заканчивается различными физическими включениями для разных нужд.
Вместо создания огромного куска кода, который делает все и со временем становится неуправляемым, рассмотрите возможность создания основного объекта сценария и расширьте его по мере необходимости с помощью небольших утилит, каждая из которых выполняет одну работу. Именно так создаются большие библиотеки и решения JavaScript, не делая их кошмаром в обслуживании и использовании.
Объедините эти различные утилиты в логические файлы включения (dom.js, forms.js, i18n.js, ajax.js), и вам будет намного легче отлаживать и расширять конечный продукт.
Проблемы производительности и процессы сборки
Такая модульность и разделение на множество различных файлов меньшего размера идут вразрез с некоторыми лучшими практиками в области веб-производительности, которые вы, возможно, слышали.
Каждый HTTP-запрос, который делает ваш документ во время визуализации страницы, замедляет его. Каждый запрос означает, что браузер должен инициировать запрос, найти нужные DNS-серверы, согласовать IP-адрес, загрузить и обработать ресурс. Это плохо с изображениями и CSS, но еще хуже со скриптами. Каждый раз, когда браузер встречает скрипт, он останавливает рендеринг HTML, загружает скрипт, выполняет его, а затем начинает рендеринг. Это может занять довольно много времени.
Хотя все это правда, модульность по-прежнему очень полезная вещь. Мы должны начать отходить от понятия «напиши и открой в браузере». Хотя эта простота разработки, скорее всего, является основной причиной, почему люди взялись за разработку на JavaScript, это не очень умно. Другие языки программирования имеют процессы сборки, как и веб-приложения и веб-сайты.
Процесс сборки предназначен для превращения читаемого человеком кода в читаемый компьютером код. Что вы можете сделать в процессе сборки:
- Проверьте код (JSLint — хороший инструмент для этого)
- Удалить ненужные пробелы — это сохраняет размер файла
- Замените строки внутри ваших сценариев поиском в массиве (это хардкорный совет по производительности, поскольку MSIE создает строковый объект каждый раз, когда встречает строку — даже в циклах и условных выражениях)
- Собрать несколько включений в один файл
- Проверьте код на наличие уязвимостей
Последний пункт трудно автоматизировать, но в настоящее время разрабатывается несколько проектов, которые пытаются решить эту проблему.
Используйте и расширяйте этот документ
И последнее, но не менее важное: я надеюсь, что этот документ дал вам некоторые идеи и был полезен. Он лицензирован Creative Commons, что означает, что вы можете переиздавать, использовать его и использовать в коммерческих целях для обучения — единственное, что вам нужно сделать, это упомянуть меня как первоначального автора.