Архитектура фронтэнд приложений
Что такое хорошая архитектура веб-приложения
Есть разные теоретические определения и обоснования данного понятия, но грубо говоря это следующее.
Допустим ты разрабатываешь проект, уже на финальном этапе. К тебе подходит заказчик и говорит что-то типа: я хочу добавить тень ко всем кнопкам, я хочу изменить UI библиотеку, мы решили поддерживать несколько языков, мы решили заменить вот этот модуль на другой, нужно сделать более детальную адаптивность для разных разрешений, мы решили добавить темы, мы меняем провайдера услуг по обработке платежей, у нас повышается число пользователей / сайт тормозит / надо его ускорить, надо повысить уровень безопасности сайта, мы взяли мидл программиста, введи его в курс дела быстро, и пусть он добавляет функционал или фиксит баги и т.п.
Хорошая архитектура фронтенд приложения позволяет выполнить всё это наиболее эффективно с наименьшими затратами.
ООП на фронтенде
Многие приходя из разработки на объектно-ориентированных языках пытаются использовать концепции ООП и на фронте и программировать с классами, наследованием, полиморфизмом и прочим.
Это ошибка. Основные вычисления на фронте связаны с UI - показ данных, обработка взаимодействия с пользователем. Системы классов и микросервисов на них просто не нужны. Конечно, можно создать базовую кнопку, обернуть ее в два кастомных компонента и считать это наследованием, но в коде этого не получается. Поэтому надо учиться мыслить другими категориями - компоненты, композаблы, js модули. И использовать свою мощь JavaScript-a, а не пытаться натянуть его на ООП.
В то же время неплохой аналогией объекта с глобальным ("статичным") и локальным ("объектным") состоянием является композабл функция. Это можно использовать в определённых ситуациях.
Многопоточность и асинхронность
Многопоточность - это когда программист может создать отдельный поток и запустить в нем свой определенный код. Например, можно создать два потока, один в бесконечном цикле выводит в консоль "Поток А", а второй - "Поток В". Эти строки будут чередоваться в выводе. Можно задать приоритеты потокам, и тогда поток в приоритетом 3 будет выводить сообщения в 3 раза чаще, чем поток с приоритетом 1.
Данные два потока должны выполняться одновременно, но это условно. Потому что если потоков 10, а процессор двухядерный с двумя своими потоками на ядро, то чисто физически больше 4 одновременных потоков выполнять нельзя. Поэтому потоки делятся на кусочки кода, и они выполняются по очереди - это называется конкурентностью.
В мире JavaScript cтановиться в одну очередь потоки могут на уровне JS рантайма, OS, CPU. Обычные десктопные приложения (например, IDE) тоже бывают с хорошей поддержкой распараллеливания на многоядерные процессоры - и тогда они используют CPU максимально эффективно, или нагружающие только одно ядро. В последнем случае апгрейд процессора на более современный многоядерный почти ничего не даст, если у старого и нового примерно одна частота.
Асинхронность - это когда есть некая неблокирующая функция, точное время исполнения которой неизвестно. И программист может указать код, который должен выполниться после исполнения данной функции (передать callback
). Промисы и async/await
это нетривиальные, но всё же просто удобные обертки над данной логикой.
Итак, при асинхронности обычно есть некая пограничная функция, зависящая от внешних обстоятельств (fetch, nextTick, fs.readdir, setTimeout
). Её "псевдопараллельное" исполнение в одном потоке вместе с основным кодом обеспечивается механизмом Event Loop
.
Таким образом, это абсолютно разные вещи как по целевому назначению, так и по использованию. Асинхронность в JavaScript реализована через промисы и async/await, многопоточность или её подобие реализуется райнтаймом (Web workers
, worker_threads
).
Асинхронность нужна для общения с "внешним миром", включая отрисовку браузера. Многопоточность позволяет выделить ресурсоемкие вычисления (а также сетевую загрузку) в отдельные потоки (Web workers
), что снижает нагрузку на основной поток, который отвечает и за рендеринг. Это благоприятно сказывается на отзывчивости вашего UI.
Полезные советы
Избегайте зависимостей
Если есть возможность не вводить новую зависимость (не подключать новый npm пакет) - не делайте это. Это может немного сэкономит ваше время, но сторонняя библиотека рано или поздно устареет, перестанет поддерживаться, в ней могут обнаружить уязвимость. Кроме того она увеличивает размер вашего бандла, что напрямую влияет на производительность вашего приложения. Tree shaking хорош в рекламных зазываниях, но работает реально далеко не всегда.
Если вам нужна, например, функция debounce
, не торопитесь подключать её вместе с какой-либо библиотекой. Загуглите её реализацию и скопируйте 15 строк кода, сделав свою функцию.
Используйте обертки над компонентами UI библиотек и сторонних утилит
Если вы используете компоненты какой-то UI библиотеки, не используйте их напрямую - сделайте обертки для них. Например, BaseButton
или BaseInput
. Это намного облегчит стилизацию компонент, а также сильно упростит переход на другую библиотеку, или замену на свои компоненты.
То же самое с утилитами.
Регулярный рефакторинг
Время от времени делайте рефакторинг кода, перемещая компоненты и модули туда, где они должны быть логически, переименовывая их, и структурируя и улучшая сам код. Это полезно и для кода, и для вашего профессионального роста.
Используйте CSS3
Старайтесь использовать для адаптивноcти чистый CSS3. Не привязывайтесь к колоночным моделям CSS и UI библиотек, если только это не проект из разряда: "сделал и забыл, пусть заказчик дальше с ним мучается".
Потратьте пару дней на изучение Флексбокса и вы сможете верстать на нём быстрей и лучше, чем на любом Тайлвинде.
Используйте современные стандарты и возможности языков
Например, семантические элементы HTML5 - aside, header, section, article, details и.т.д.
CSS3 - Flexbox и Grid, Container query
Документируйте проект
Рисуйте use-case и другие диаграммы, описывайте основной функционал проекта и важные детали его реализации - это как минимум повысит ваш уровень как разработчика ПО.
VitePress - очень удобный инструмент для технического документирования.