Правила хорошего тона для front-end-а с практическими примерами

Front-end требует от разработчика не только знания нескольких вариантов того, как можно решить поставленную задачу, но и умения выбрать оптимальное решение. Кроме того, не стоит забывать и о таких понятиях, как читаемость и простота поддержки кода, так как довольно часто, ведение вашего проекта может быть отдано в чужие руки. О том, что такое хорошо и плохо в front-end разработке, мы и поговорим сегодня.
HTML
Семантика
HTML5 предоставляет нам множество новых элементов, основная задача которых точное описание и структурирование контента. Убедитесь, что вы используете всю выгоду от этого:
<!-- Плохо -->
<div id=\"main\">
<div class=\"article\">
<div class=\"header\">
<h1>Статья</h1>
<p>Опубликовано: <span>05.03.2015</span></p>
</div>
<p>…</p>
</div>
</div>
<!-- Хорошо -->
<main>
<article>
<header>
<h1> Статья </h1>
<p>Опубликовано: <time datetime=\"2015-03-05\">05.03.2015</time></p>
</header>
<p>…</p>
</article>
</main>
Убедитесь, что вы понимаете семантику элементов, которые вы используете. Лучше отказаться от использования элемента, чем применять его неправильно.
<!-- Плохо --><h1> <figure> <img alt=Company src=logo.png> </figure></h1>
<!-- Хорошо -->
<h1>
<img alt=Company src=logo.png>
</h1>
Краткость
Старайтесь писать максимально лаконично, откажитесь от своих вредных привычек.
<! - Плохо ->
<html lang=en>
<head>
<meta http-equiv=Content-Type content=\"text/html; charset=utf-8\" />
<title>Контакты</title>
<link rel=stylesheet href=style.css type=text/css />
</head>
<body>
<h1>Свяжитесь со мной</h1>
<label>
Email:
<input type=email placeholder=you@email.com required=required />
</label>
<script src=main.js type=text/javascript></script>
</body>
</html>
<! - Хорошо -><html lang=en> <meta charset=utf-8> <title> Контакты </title> <link rel=stylesheet href=style.css>
<h1> Свяжитесь со мной</h1> <label> Email: <input type=email placeholder=you@email.com required> </label> <script src=main.js></script></html>
Доступность
Доступность не должна быть второстепенным свойством. Вы не должны быть экспертом WCAG, чтобы улучшить свой веб-сайт. Вы можете начать немедленно, используя мелочи, которые создают огромную разницу, например:
- научитесь правильно использовать атрибут alt;
- убедитесь, что ваши ссылки и кнопки помечены правильно (без );
- не полагайтесь исключительно на цвета, чтобы передавать информацию;
- выделяйте элементы управления формой.
- убедитесь, что ваши ссылки и кнопки помечены правильно (без );
<! - Плохо ->
<h1><img alt=\"Logo\" src=\"logo.png\"></h1>
<! - Хорошо -> <h1><img alt=\"Logo\" src=\"logo.png\"></h1>
Язык
Рекомендуется всегда объявлять язык и кодировку на уровне документа, даже если они указаны в HTTP. Отдавайте предпочтение UTF-8.
<!- Плохо ->
<!doctype html>
<title>Hello, world.</title>
<!-- Хорошо -->
<!doctype html>
<html lang=en>
<meta charset=utf-8>
<title>Hello, world.</title>
</html>
Производительность
Если у вас нет уважительной причины загружать скрипты перед контентом, не затормаживайте загрузку вашей страницы. Если ваш CSS-файл слишком большой, выделите второстепенные стили и соберите их в отдельном файле с отложенной загрузкой. Конечно, два HTTP-запроса выполняются дольше, чем один, но таким образом вы создаете иллюзию быстрой загрузки, так как уменьшается время первого взаимодействия.
<!-- Плохо --><!doctype html><meta charset=utf-8><script src=analytics.js></script><title>Hello, world.</title><p>...</p>
<!-- Хорошо -->
<!doctype html>
<meta charset=utf-8>
<title>Hello, world.</title>
<p>...</p>
<script src=analytics.js></script>
CSS
Точка с запятой
Хотя точка с запятой воспринимается как чисто технический разделитель, не забывайте его ставить.
/* Плохо */div { color: red}
/* Хорошо */div { color: red;}
Box model
В идеале, модель должна быть общая для всего документа. Глобальная * { box-sizing: border-box; } это хорошо, но постарайтесь не изменять стандартные свойства модели на конкретных элементах, если это возможно.
/* Плохо */div { width: 100%; padding: 10px; box-sizing: border-box;}
/* Хорошо */div { padding: 10px;}
Поток
Не изменяйте поведение элемента по умолчанию, если вы можете этого избежать. Например, удаление пустого пространства под изображением не должно выполняться за счет изменения способа отображения.
/* Плохо */img { display: block;}
/* Хорошо */img { vertical-align: middle;}
Кроме того, не стоит менять или явно указывать позиционирование, если в этом нет необходимости.
/* Плохо */div { width: 100px; position: absolute; right: 0;}
/* Хорошо */div { width: 100px; margin-left: auto;}
Позиционирование
Есть много способов, чтобы позиционировать элементы в CSS, но старайтесь ограничивать себя. Вот список решений, которые стоит использовать, в порядке их предпочтительности:display: block;display: flex;position: relative;position: sticky;position: absolute;position: fixed;
Селекторы
Минимизация селекторов тесно связана с DOM. Постарайтесь не использовать более трех уровней селекторов.
/* Плохо */div:first-of-type :last-child > p ~ *
/* Хорошо */div:first-of-type .info
Избегайте использования лишних селекторов:
/* Плохо */img[src$=svg], ul > li:first-child { opacity: 0;}
/* Хорошо */[src$=svg], ul > :first-child { opacity: 0;}
Приоритетность селекторов
Не делайте значение селекторов трудным для понимания. Сведите к минимуму использование ID и избегайте !important.
/* Плохо */.bar { color: green !important;}.foo { color: red;}
/* Хорошо */.foo.bar { color: green;}.foo { color: red;}
Переопределение
Переопределение стилей усложняет отладку и чтение кода. Избегайте его, если это возможно.
/* Плохо */li { visibility: hidden;}li:first-child { visibility: visible;}
/* Хорошо */li + li { visibility: hidden;}
Наследование
Не дублируйте объявление стилей, которые и так будут наследоваться.
/* Плохо */div h1, div p { text-shadow: 0 1px 0 #fff;}
/* Хорошо */div { text-shadow: 0 1px 0 #fff;}
Краткость
Используйте сокращенные свойства, не объявляйте ненужные свойства и значения, которые стоят по умолчанию.
/* Плохо */div { transition: all 1s; top: 50%; margin-top: -10px; padding-top: 5px; padding-right: 10px; padding-bottom: 20px; padding-left: 10px;}
/* Хорошо */
div { transition: 1s; top: calc(50% - 10px); padding: 5px 10px 20px;}
Язык
Предпочитайте английский математике.
/* Плохо */:nth-child(2n + 1) { transform: rotate(360deg);}
/* Хорошо */:nth-child(odd) { transform: rotate(1turn);}
Префиксы
Не используйте их, если это возможно. Если вы вынуждены их использовать, пишите их перед стандартными свойствами.
/* Плохо */div { transform: scale(2); -webkit-transform: scale(2); -moz-transform: scale(2); -ms-transform: scale(2); transition: 1s; -webkit-transition: 1s; -moz-transition: 1s; -ms-transition: 1s;}
/* Хорошо*/div { -webkit-transform: scale(2); transform: scale(2); transition: 1s;}
Анимация
Transition лучше, чем animation. Избегайте использовать анимацию для чего-либо, кроме opacity и transform.
/* Плохо */div:hover { animation: move 1s forwards;}@keyframes move { 100% { margin-left: 100px; }}
/* Хорошо */div:hover { transition: 1s; transform: translateX(100px);}
Единицы
Используйте безразмерное значение, когда можете. Используйте rem, если вы используете относительные единицы. Секунды лучше миллисекунд.
/* Плохо */div { margin: 0px; font-size: .9em; line-height: 22px; transition: 500ms;}
/* Хорошо*/div { margin: 0; font-size: .9rem; line-height: 1.5; transition: .5s;}
Цвета
Если вам нужна прозрачность, используйте RGBA . В противном случае, всегда используйте шестнадцатеричный формат.
/* Плохо */div { color: hsl(103, 54%, 43%);}
/* Хорошо*/div { color: #5a3;}
Рисование
Избегайте HTTP запросов, если необходимое содержимое легко реализуется с помощью CSS.
/* Плохо */div::before { content: url(белый-круг.svg);}
/* Хорошо*/div::before { content: \"\"; display: block; width: 20px; height: 20px; border-radius: 50%; background: #fff;}
Хаки
Не используйте их.
/* Плохо */div { // position: relative; transform: translateZ(0);}
/* Хорошо*/div { /* position: relative; */ will-change: transform;}
JavaScript
Производительность
Отдайте предпочтение читаемости и выразительности кода. JavaScript довольно редко становится узким местом кода, если не доводить его до абсурда. Гораздо эффективней в плане производительности провести оптимизацию, облегчить изображения или оптимизировать DOM.
// Плохо (хотя намного быстрее) const arr = [1, 2, 3, 4];const len = arr.length;var i = -1;var result = [];while (++i < len) { var n = arr[i]; if (n % 2 > 0) continue; result.push(n * n);}
// Хорошоconst arr = [1, 2, 3, 4];const isEven = n => n % 2 == 0;const square = n => n * n;const result = arr.filter(isEven).map(square);
Аккуратность
Старайтесь, чтобы ваши функции не давали побочных результатов, не влияли на глобальную область видимости и не возвращали новых объектов. Не изменяйте свойства существующих объектов.
// Плохоconst merge = (target, ...sources) => Object.assign(target, ...sources);merge({ foo: \"foo\" }, { bar: \"bar\" }); // => { foo: \"foo\", bar: \"bar\" }
// Хорошоconst merge = (...sources) => Object.assign({}, ...sources);merge({ foo: \"foo\" }, { bar: \"bar\" }); // => { foo: \"foo\", bar: \"bar\" }
Независимость
Старайтесь использовать родные методы.
// Плохоconst toArray = obj => [].slice.call(obj);
// Хорошоconst toArray = (() => Array.from ? Array.from : obj => [].slice.call(obj))();
Присвоение
Используйте неявное присвоение, только если это имеет смысл.
// Плохоif (x === undefined || x === null) { ... }
// Хорошоif (x == undefined) { ... }
Циклы
Не используйте циклы для изменения объектов. Использование встроенных методов работы с объектами повышает читаемость и упрощает сопровождение кода.
// Плохоconst sum = arr => { var sum = 0; var i = -1; for (;arr[++i];) { sum += arr[i]; } return sum;};
sum([1, 2, 3]); // => 6
// Хорошоconst sum = arr => arr.reduce((x, y) => x + y);
sum([1, 2, 3]); // => 6
Если вы по каким-то причинам не можете использовать эти методы обратите внимание на рекурсию
// Плохоconst createDivs = howMany => { while (howMany--) { document.body.insertAdjacentHTML(\"beforeend\", \"<div></div>\"); }};createDivs(5);
// Плохоconst createDivs = howMany => [...Array(howMany)].forEach(() => document.body.insertAdjacentHTML(\"beforeend\", \"<div></div>\") );createDivs(5);
// Хорошоconst createDivs = howMany => { if (!howMany) return; document.body.insertAdjacentHTML(\"beforeend\", \"<div></div>\"); return createDivs(howMany - 1);};createDivs(5);
Аргументы
Забудьте о arguments объекта; rest будет значительно эффективней, потому что:
- ему можно присвоить имя, так что это дает вам более полное представление о том, какие именно аргументы ожидает получить функция;
- это реальный массив, что делает его проще в использовании.
// Плохоconst sortNumbers = () => Array.prototype.slice.call(arguments).sort();
// Хорошоconst sortNumbers = (...numbers) => numbers.sort();
Apply
Забудьте о Apply () . Вместо этого используйте оператор распространения:
const greet = (first, last) => `Hi ${first} ${last}`;const person = [\"John\", \"Doe"];
// Плохоgreet.apply(null, person);
// Хорошоgreet(...person);
Bind
Не используйте Bind () , когда есть более традиционное решение.
// Плохо[\"foo\", \"bar"].forEach(func.bind(this));
// Хорошо[\"foo\", \"bar"].forEach(func, this);
// Плохоconst person = { first: \"John\", last: \"Doe\", greet() { const full = function() { return `${this.first} ${this.last}`; }.bind(this); return `Hello ${full()}`; }}
// Хорошоconst person = { first: \"John\", last: \"Doe\", greet() { const full = () => `${this.first} ${this.last}`; return `Hello ${full()}`; }}
Функции высшего порядка
Избегайте вложенности функций, когда это возможно.
// Плохо[1, 2, 3].map(num => String(num));
// Хорошо[1, 2, 3].map(String);
Кэширование
Кэшируйте
// Плохоconst contains = (arr, value) => Array.prototype.includes ? arr.includes(value) : arr.some(el => el === value);contains([\"foo\", \"bar"], \"baz\"); // => false
// Хорошоconst contains = (() => Array.prototype.includes ? (arr, value) => arr.includes(value) : (arr, value) => arr.some(el => el === value))();contains([\"foo\", \"bar"], \"baz\"); // => false
Переменные
Const лучше, чем let, а let лучше, чем var.
// Плохоvar obj = {};obj[\"foo\" - \"bar"] = \"baz\";
// Хорошоconst obj = { [\"foo\" - \"bar"]: \"baz\"};
Окружение
Лучше использовать несколько IF и вернуть необходимое значение, чем использовать else if, и switch.
// Плохоvar grade;if (result < 50) grade = \"Плохо\";else if (result < 90) grade = \"Хорошо\";else grade = \"excellent\";
// Хорошоconst grade = (() => { if (result < 50) return \"Плохо\"; if (result < 90) return \"Хорошо\"; return \"excellent\";})();
Работа с объектом
Избегайте for...in, когда вы можете.
const shared = { foo: \"foo\" };const obj = Object.create(shared, { bar: { value: \"bar\", enumerable: true }
// Плохоfor (var prop in obj) { if (obj.hasOwnProperty(prop)) console.log(prop);}
// ХорошоObject.keys(obj).forEach(prop => console.log(prop));Объекты или картыКарты, как правило, лучше и мощнее.// Плохоconst me = { name: \"Ben\", age: 30};var meSize = Object.keys(me).length;meSize; // => 2me.country = \"Belgium\";meSize++;meSize; // => 3
// Хорошоconst me = Map();me.set(\"name\", \"Ben\");me.set(\"age\", 30);me.size; // => 2me.set(\"country\", \"Belgium\");me.size; // => 3
Каррирование
Каррирование, возможно, уместно в других языках, но избегайте его в JavaScript, так как оно усложняет читабельность кода.
// Плохо const sum = a => b => a + b;sum(5)(3); // => 8
// Хорошоconst sum = (a, b) => a + b;sum(5, 3); // => 8
Читабельность
Не усложняйте чтение вашего кода, используя, казалось бы, умные трюки.
// Плохоfoo || doSomething();
// Хорошоif (!foo) doSomething();
// Плохоvoid function() { /* IIFE */ }();
// Хорошо(function() { /* IIFE */ }());
// Плохоconst n = ~~3.14;
// Хорошоconst n = Math.floor(3.14);
Повторное использование кода
Не бойтесь создавать множество мелких компонуемых и многоразовых функций.
// Плохоarr[arr.length - 1];
// Хорошоconst first = arr => arr[0];const last = arr => first(arr.slice(-1));last(arr);
// Плохо
const product = (a, b) => a * b;
const triple = n => n * 3;
// Хорошо
const product = (a, b) => a * b;
const triple = product.bind(null, 3);
Зависимость
Минимизируйте зависимости, старайтесь не использовать сторонний код и не подгружать целые библиотеки для пары функций.
// Плохо
var _ = require(\"underscore\");
_.compact([\"foo\", 0]));
_.unique([\"foo\", \"foo"]);
_.union([\"foo"], [\"bar"], [\"foo"]);
// Хорошо
const compact = arr => arr.filter(el => el);
const unique = arr => [...Set(arr)];
const union = (...arr) => unique([].concat(...arr));
compact([\"foo\", 0]);
unique([\"foo\", \"foo"]);
union([\"foo"], [\"bar"], [\"foo"]);