С самого зарождения индустрии программирования люди создавали продукты на основе веб-стандартов, чтобы облегчить разработку.
Было время, когда каждый браузер по-своему интерпретировал практически все. К счастью, эта эпоха давно позади.
Некоторые фреймворки и библиотеки фактически привели к эволюции самих веб-стандартов, например jQuery и React.
Теперь пришло время поразмыслить и внимательно посмотреть на наши практики разработки.
Все больше абстракций, усложняющиеся стратегии сборки и пакетирования, все больший размер в мегабайтах NPM-пакетов, предназначенных для чего угодно.
Разбиение на пакеты, “тряска” деревьев, ленивая загрузка частей пакета, виртуальная модель DOM, синтетические события — список можно продолжать.
Неужели мы сошли с ума?
Актуальные веб-стандарты? Кажется, мы потеряли с ними связь.
Недавно опрос State of HTML показал, что многие так называемые старшие разработчики не дотягивают даже до уровня младших специалистов (юниоров), когда дело доходит до базового понимания веб-стандартов.
По результатам опроса я попал в топ-1% с результатом 107/131, а один мой знакомый сообщил, что попал в топ-25% с результатом всего 80/131.
Вот об этом я и твержу все время: каждый из таких “старших” разработчиков полагается на свой фреймворк (или пакет, специально предназначенный для конкретного фреймворка), чтобы добиться результата, и даже не утруждает себя поиском прямого/нативного пути.
Хочу сказать таким специалистам: пока вы отсутствовали, веб-мир вырос и стал ярким, ответственным взрослым человеком, который вполне способен делать все то, что делали вы, но более легко и непринужденно, следуя принципам независимости и стабильности.
Пора изменить подход
Вот какой эксперимент я провел. Я создал очень простой, улучшенный веб-компонентами, не требующий сборки и зависимостей шаблон для PWA (прогрессивного веб-приложения), используя только средства современной веб-среды.
Вот ссылка на этот проект:
Искусство дзен-медитации
Работа над проектом всегда похожа на дзен-медитацию: она отрезвляет, очищает, убирает все лишнее и просветляет.
Это действительно отличная идея — не начинать с фреймворка или любой другой большой зависимости. Вы ставите себе планы по достижению того-то и того-то и думаете, как заставить все это работать вместо того, чтобы обеспечить продукт всем необходимым. Не советую поступать так.
Слишком часто разработчики начинают с фреймворка, а затем думают, как добиться определенных результатов с его помощью, и находят соответствующие способы.
Я обнаружил, что современная веб-среда — прекрасное место. Выбирайтесь из своих кроличьих нор, созданных фреймворками, и отправляйтесь в исследовательское путешествие!
Вот что конкретно я имею в виду.
- Начните со структуры HTML. Постарайтесь сделать так, чтобы она отображала то, что вам нужно. Не пытайтесь приукрасить ее, лучше сосредоточьтесь на смысле:
<label>
<span data-label>Animation</span>
<input type="range" min="0" max="1" step="1" name="animation" />
</label>
Обратите внимание: я заключил входные данные в <label>. Это позволяет избежать как атрибута ‘id’ во вводе, так и атрибута ‘for’ в метке. К тому же такая структура в полной мере поддерживается всеми браузерами.
- Вы видите, что семантически элемент управления выражает переключение между двумя значениями: “0” и “1”.
- Только когда вы будете довольны базовой структурой, попробуйте заставить ее работать без скриптов. В данном случае submit уже отправит сюда значение “1” или “0”.
- Теперь, если нужно, дополните структуру с помощью скриптов, но делайте это, используя семантически подходящий пользовательский тег, а код расширения напишите в прилагаемом веб-компоненте.
Пример: элемент range с двоичными значениями, действующий как “переключатель”.
<range-switch>
<label data-form-element="switch" data-selected="@useAnimations">
<span data-label>Animation</span>
<input type="range" min="0" max="1" name="animation" />
</label>
</range-switch>
Скрипт:
import { CustomElement, enQueue} from "../../common.js";
customElements.define(
"range-switch",
/**
* Превращает вход [type="range"] в функцию Switch Control
*/
class RangeSwitch extends CustomElement {
static get observedAttributes() {
return ["value"];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "value" && newValue) {
this.querySelector("input").value = parseInt("0" + newValue);
}
}
connectedCallback() {
const condition = () => {
return range.value === "1";
};
const range = this.querySelector('input[type="range"]');
range.addEventListener("input", (e) => {
this.setAttribute("value", condition() ? 1 : 0);
});
enQueue(() => {
this.setAttribute("value", condition() ? 1 : 0);
});
}
}
);
Класс CustomElement подробно рассмотрен в readme репозитория GitHub.
Теперь упорядочьте код:
range-switch {
input[type="range"] {
appearance: none;
width: 3rem;
height: 1.2rem;
background: var(--color-surface-mixed-100);
outline: none;
border-radius: 50px;
cursor: pointer;
transition: .2s ease-in-out;
padding: 0;
&:focus {
outline: 2px solid var(--color-accent);
}
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 1.4rem;
height: 1.4rem;
background: var(--color-accent);
filter: saturate(0);
border-radius: 50%;
cursor: pointer;
transition: .2s ease-in-out;
}
}
&[value="1"] {
input[type="range"] {
&::-webkit-slider-thumb {
filter: none
}
}
}
}
Принципы PurePWA
Помните: это радикальный эксперимент. Я не призываю использовать только те принципы, которых придерживаюсь. Я сам применяю их лишь для того, чтобы доказать одну вещь: вам нужны только чистые веб-стандарты, чтобы разрабатывать отличные/прогрессивные веб-приложения.
Говорим “да”:
- семантическому HTML;
- полноценному прогрессивному веб-приложению;
- веб-компонентам;
- прогрессивному расширению;
- стопроцентным результатам по всем категориям Lighthouse;
- импорту ES-модулей во время выполнения;
- иконкам в формате SVG-спрайтов;
- “нативному” внешнему виду и пользовательскому опыту.
Говорим “нет”:
- NPM;
- системе сборки;
- полифиллам¹;
- пакетированию;
- TypeScript (используем только 100 % ванильный JavaScript);
- зависимостям.
Что в результате?
Приложения, созданные с помощью этого подхода, не страдают от недостатка сложности.
Фактически, конечные пользователи не заметят разницы.
Что они могут заметить, так это огромную скорость и легкость начальной загрузки.
Кроме того, в эпоху мобильных устройств стоит уделять большое внимание достижению вау-эффекта и улучшать пользовательский опыт с помощью так называемых микроанимаций.
Начинайте работу, думая о конечном пользователе
Всегда полезно работать в обратном направлении, отталкиваясь от того, чего хочет конечный пользователь.
Как я уже говорил, сообщество веб-разработчиков слишком много внимания уделяет опыту разработчиков и слишком мало — конечным пользователям.
Мы все забываем, что у конечных пользователей нет ни таких высококлассных устройств, как у нас, ни такой пропускной способности интернета, к которой мы так привыкли. Поэтому, когда мы устанавливаем себе свои приложения и переходим с одного раздела на другой, нам кажется, что все в порядке. В то же время владельцу старого телефона на базе Android с плохим сигналом Wi-Fi или, что еще хуже, 3G/4G приходится ждать несколько секунд, чтобы хоть что-то увидеть, и еще больше, чтобы перейти к функциям полноценной интерактивности.
Следующие шаги
Этот эксперимент очень интересен. Но он еще не окончен.
Я не планировал использовать полифиллы, но не очень современный браузер под названием Safari заставил меня переосмыслить некоторые свои убеждения относительно инфраструктуры.
Сноска:
¹ Добавлены полифиллы для навигации: события navigate и метода Document: startViewTransition().
Читайте также:
- Реализация паттерна доступа к данным при работе с Drizzle
- Как работает Supabase — альтернатива облачной платформе Firebase
- Проект инженерии данных с DAG Airflow «от и до». Часть 2
Читайте нас в Telegram, VK и Дзен
Перевод статьи Marc van Neerven: PurePWA — A Radical U-Turn in Web Development