Comments 47
Объясните мне, человеку, который интересуется разработкой сайтов, зачем выносить бизнес-логику на сторону клиента? Для чего? Какой прок от этого?
Мои взгляды могут показаться консервативными, но всё же такие штуки должны обрабатываться на стороне сервера, а на стороне клиента должен происходить вывод всего этого. При этом сайт должен быть легковесным, интуитивно понятным и доступным. То бишь каждый занят своим дело. Фронт раскрашивает кнопочки и добавляет "реактивность", бэк пишет логику и хранит данные. Всё чётко.
На данный момент на стороне бэкенда популярен паттерн REST-api, что говорит о том, что они отдают чистые данные, связанные с конкретными сущностями. Клиент на данный момент не только рисует кнопки и добавляет реактивность, а в том числе сращивает все эти запросы между собой.
Часто данные приходят не в удобном виде, и нам приходится писать адаптеры, добавлять форматирование и так далее.
Часть логики, связанную с рендерингом, считается хорошей практикой выносить клиент.
Например, последняя моя задача: есть страница для сбора паспортных данных, она включает в себя с 10 разных экранов, которые должны показаться в разных случаях. Условно, сначала мы приходим на первый экран, при успехе попадаем на экран 2, при провале попадаем на экран 3, если у экрана 2 успех, то мы переходим на экран 4 и т.д. Где хранить эту логику? Она очень прямо связана с бизнес-логикой - это флоу клиента, но при этом она связана с отображением экранов на клиенте. Мы храним эту логику на клиенте, потому что нам проще регулировать отображение экранов, чем если бы это делали на сервере.
Но вы сами в своём примере разбили логику действий на составляющие. Что позволит бэк-енд разработчику подготовить представление данных на своей стороне и правильно их подать на фронт. Привет взаимодействию в команде.
У сервера мощностей по более будет, чем у клиента в браузере, который будет тратить некоторое время и самое главное трафик, пока будут производиться вычисления. Всё таки скрипты порядком больше потребляют трафика, даже с учётом сжатия данных и вот это вот всё. Это нам повезло, что у нас трафик дешёвый, в той же Америке и Европе он стоит порядком дороже.
Не понимаю смысла спора, это современный стандарт, часть бизнес-логики так или иначе переносится на фронт, дабы уменьшить нагрузку на сервер и убрать зависимость сервера от клиента. То есть АПИ строится таким образом, что им не нужно знать о том, как работает клиент, и любой клиент сможет использовать это АПИ.
Я не спорил с вами, а всего лишь пытаюсь понять, когда наступил этот переломный момент когда часть тех.процессов отдали на откуп клиентам. И да, популярность не всегда залог успешного.
С момента, как появилась технология SPA, она впринципе возникла из-за потребности разделить логику между клиентом и сервером. Это не "популярно", это стандарт и считается нормой на сегодняшний день. Пользователям нравится SPA, когда все плавно и без задержек, без выноса части логики на клиент - это невозможно.
Каким образом нынче сохраняетеся микроразметка? Как решается вопрос с seo?
Фронты за это отвечают, они могут подключить SSR при необходимости, либо сделать статическую генерацию для ботов, зависит от проекта все
А вообще я давненько не вижу запроса на какую-то работу с микроразметкой/seo, сейчас часто используют другие каналы продвижения (типа соцсетей или мессенджеров)
Иногда бизнес просит сделать рекламный статичный сайт-визитку для продвижения через поиск, а с основным сайтом не запариваются
Бэк хранит данные, валидирует их, управляет ими и предоставляет API для доступа для клиентов. Которыми может быть не только люди.
Фронт - это “человеческий API”. На один и тот же бэк может быть: мобилка, десктоп, браузер-экстеншен, генератор pdf/cvs/etc.
Современный фронт (реакт/вью/etc) - это, обычно, модель данных, которая определять визуальное представление. Эта модель, обычно, подвергается нормализации. И обновляется не полностью по действию пользователя, а инкрементально: нажали кнопку, поменяли один флаг, отправили на бэк, получили результат, изменили эту часть модели (а еще отработали состояние ожидания ответа, отработали ошибки).
А дальше аппетиты растут: кеши на фронте - чтобы снизить нагрузку на сеть и бэк (и механизмы их инвалидации), использование параметров урла для хранения данных, шеринг каких-нибудь данных на весь апликейшн (ACL, например). Работа со сложными формами: это все вложенные переиспользуемые компоненты, которые валидируются и мапятся на модель, которая, в свою очередь, законтрактована с моделью на бэке.
На самом деле на фронте бизнес-логики часто не катастрофически много.
Сеть является самым тормозным местом. Поэтому, чтобы интерфейс работал максимально быстро, часть вещей переносят на фронт, где это можно и имеет смысл. Я анимирую часто на фронте элементы, и уже интервал 100ms (0.1 s) хорошо заметен: можно с уверенностью сказать стало быстрее или медленнее. Поэтому этот интервал использую как шаг для грубой настройки анимации.
На фронте логика интерфейса, на сервере логика обработки данных. Каждый занят своим делом и у каждого есть подходящие возможности для реализации этой логики. На бекенде делать что-то связанное с интерфейсом и фронтом не так удобно. Тогда фронт находится на в своей стихии.
Если да, то максимально корректный в парадигме Vue — это provide/inject.
А если с этим не заморачиваться, то (имхо) самый удобный — event-bus на руте (либо внешний через mixin/globals). В нем можно как райзить события-данные, так и события-логику, получают их все подписанные на нужные события компоненты, что очень удобно. Расплата за это только одна — отследить типы событий и где они порождаются и где ловятся — сложно. Но, если именование событий стандартизировать, то ищется в проекте элементарно поиском по названию события.
Не увидел event-bus в статье, кстати — но может быть потому, что неверно задачу понял. Поясните, если не трудно, можно просто простым примером задачи.
Здесь нет конкретной задачи, есть идея о том, какие сервисы мне хочется проектировать. Вводные требования я прописала в конце статьи, эти требования покрывают большую часть узких кейсов, которыми страдают остальные решения.
Provide/inject в плане выноса логики страдает проблемой общего родителя (он обязателен) + необходимость встраивать в родителя, что засоряет код ненужными вещами собственно говоря для родителя. + еще несколько минусов, описанных в секции cons, но эти 2 самые весомые для меня.
Да, я действительно забыла указать EventBus, но он слишком неочевидный, как вы уже сами сказали, отследить вызов события и его listener - не очень удобно.
Я хотела сделать логику, при которой сервис как таковой вообще не будет связан с Vue, потому что если мы выносим логику в сервис, то фреймворк там уже не должен иметь значения. То есть я нацелилась конкретно на классы, и как можно удобно использовать их в компонентах.
Вам будет понятнее, если вы посмотрите реализацию (ссылка на репозиторий указана в конце статьи), либо дождетесь второй и третьей части, где я буду более подробно рассказывать о том, что я сделала.
Общий родитель есть всегда - это рут. Чтобы его не загрязнять, можно то что провайдится, затащить внешним файлом js.
Про отслеживание событий я написал ниже.
А так вы затащили в проект ещё одну зависимость, которую, во-первых, нужно понимать всем, кто работает в проекте, а во-вторых - поддерживать. Вместо того, чтобы использовать сам vue и его парадигмы, которые понятны всем и доступны из коробки.
ИМХО, это пятая нога - вы облегчили одно и усложнили другое.
Но, вообще, чтобы понять что решали, было бы очень неплохо увидеть реальную задачу.
Event-bus - мега не очевидно. provide/inject - тоже, но в меньшей степени.
В большом проекте надо постараться, чтобы найти родителя в котором сделан provide. Можно ставить коммент, как вариант, у inject, но родителя перенесли/переименовали и финита ля комедия - ищи снова родителя.
Сам я использовал provide/inject и это удобно, но это был маленький проект и там было всё очевидно. В больших проектах лучше искать более удобные решения, даже если это "велосипеды"
Чем event-bus меганеочевиден?
Если договориться о стандарте именования, например, сделать события вида [НазваниеКомпонента]_[Событие]_[ID], где ID - это идентификатор экземпляра компонента, если компонентов такого вида может быть несколько.
После такого соглашения понимать что за компонент райзнул событие очень просто, как и найти подписчиков на него. Можно для дебага перехватывать все события и кидать в консоль и тд.
Я бы не сказал, что это неочевидней provide/inject, а самое главное - никаких внешних зависимостей и парадигм тащить не надо - работает из коробки.
Плюсов масса - если компоненты каким-то образом подписаны на события друг-друга, например когда есть модальный лоадер, а другие компоненты при асинхронке кидают события типа Loader_On/Off, то, достаточно просто подключить компонент лоадера и любой другой компонент, вообще ничего не зная про связь - и они подружатся сами. А если вы не подключили лоадер - никакой ошибки не будет, тк событие будет уходить, но подписчиков у него нет.
Ну если надо хранить глобально несколько экземпляров вместо синглтона, то шаблон давно известен: мультитон. Правда тут вопрос как понять, когда экземпляры в мультитоне надо убить, если вдруг их там может быть порождено очень много.
Я раньше очень много писал на Symfony, и там просто великолепный Dependency Injection Container. Очень сильно не хватало этого на стороне фронта, на Vue. Использовать контейнеры с декораторами не хотелось ( ну не перевариваю я их).
И вот недавно я открыл для себя RSDI. Оказалась потрясная штука, сейчас использую вовсю. В потом еще я подключил авто-маппинг сервисов из rsdi с помощью provide/inject, и теперь могу вызвать сервис из любого компонента, где мне это нужно и в тоже самое воемя четко видить все зависимости и в одном месте иметь описание состава контейнера. Мне прям зашло. Если интересно - могу поделиться деталями.
P.S. rsdi и под vue 2 будет работать.
Напишите статью!) Очень мало способов решения именно на просторах интернета, что и подтолкнуло меня написать статью
Концепция vue/react подразумевает, что интерфейс является отражением данных в любой момент времени через механизмы, обеспечивающие их связность. Реактивность.
Если сервисы хранят данные, нужные для вьюхи, то не ломается ли вьюха за счет отсутствия реактивности?
Если не хранят, то не проще ли использовать статические классы? Или использовать синглтоны, если нужны инстансы?
Но на самом деле я не очень понимаю смысла использовать di, когда есть vuex/pinia.
Я правильно понял, что вы создали свой DI контейнер, когда могли просто использовать inversifyjs?
Про этот пакет в курсе, я использовала его на практике на проекте, где компоненты были написаны как классы. Но при изменениях в классе ломался hot-reloading, при этом ломался так, как будто у тебя ошибка какая-то происходит, что очень сильно путало. И по какой-то причине не удалось встроить в стор и роутер файлы, но уже не помню по какой, пришлось там просто синглтон получать, что свело на нет пользу пакета.
Я создала типа упрощённую версию, которая будет работать при любых вводных, так как она максимально простая.
Идея мне понравилась. Это как MobX в реакте, посмотрите provide/inject в библиотеке mobx-react. Там не нужно писать так много кода, и запоминать работу фреймворка (вы переопределили сеттер, чтобы vue мог установить proto).
То есть можно использовать классы, а с помощью mobX они уже реактивны, а если нужен мультитон, то оборачивать сервис в свой декоратор (написать самостоятельно).
Я например сейчас работаю на проекте, где использован Vuex, и другие зависимости вводить соответственно не очень. Плюс в нативке как никак разобраться можно без дополнительных запросов в гугл.
В целом я знакома с mobx, и работала с ним на реакте. Вроде как есть реализация под Vue, надо будет глянуть, как они это сделали.
Насчёт того, что приходится помнить особенности фреймворка, согласна. У меня была идея сделать декораторы, чтобы объявление свойства в классе могло выглядеть как-то так:
@primitive
@readonly
_prop = 'something'
@object
@validated
_obj = { }
Тогда в этих декораторах можно было бы зашить Vue-специфичную логику. Но в нативном JS декораторы ещё не въехали в стандарт, вот в TS - это возможно.
Почему пропущен вариант использовать Composables?
Потому что статья про Vue 2
В чем проблема использовать Composables во Vue 2 ?
Спасибо за слив кармы за абсолютно оправданное замечание(смотрите ссылку ниже). Парни и девчонки, которые знают, что я был прав, пожалуйста, накидайте плюсов в карму! Верните мне веру в Хабр, что здесь еще можно нормально общаться хотя бы на технические темы!
Статья написана про старые проекты на Vue 2, написанные на Options Api, которые не используют Composition Api. Там нет такой опции.
Начиная с версии 2.7 во Vue 2 появилась опция setup. В доказательство этому, выше я уже скидывал ссылку на Changelog. Даже если Вам не нравится это вариант, мне было бы интересно прочитать сравнение с плюсами и минусами. Только поэтому я спрашиваю.
Я не говорила, что мне не нравится этот вариант, мне впринципе Vue 3 больше нравится, и там я использовала только Composition Api.
Просто тема статьи другая, я это наверное явно не обозначила, но опция Composable на некоторых проектах не стоит, так как весь проект на Options Api, и composable не совсем вписывается.
Для 3 версии Vue я потом хотела сделать решение, где как раз таки класс встраивается в компонент через composable.
так как весь проект на Options Api, и composable не совсем вписывается.
Они прекрасно сосуществуют вместе. Не вижу ни одной причины продвигать философию «одного подхода».
Смысл об этом спорить? Я проектировала решение для проекта, на котором я работаю. Там версия Vue 2.5, composable нет впринципе, а класс мне понадобился сейчас до предполагаемого обновления на Vue 3
Я отвечал на другой ваш аргумент.
Смотрите, Вы сделали интересное инженерное решение. Но зачем оно нужно? Вы предлагаете обоснования и вот с этими обоснованиями я и спорю.
Т.е. если единственная причина подобного решения: vue2.5, то - ок.
Была бы доступна опция composable, я бы использовала её, но так как её нет, я сделала другое решение, которое хотя бы снаружи будет знакомо другим фронтам из команды (с помощью доп. функций)
Но также другим людям, которые прочитают мою статью, и которые хотели использовать классы в проектах - возможно будет более предпочтительный вариант без composable по их каким-то личным предпочтениям
Смешались в кучу кони, люди стейты, функции, миксины...
Автор понимает, что такое сервис? Верней, как автор понимает что такое сервис? Судя по тексту, Math.round() для нее сервис. И весь Vuex стор тоже.
О каких сервисах речь? Stateless, stateful? Откуда к нему нужен доступ? Какие данные передаются? Какие задачи решаются? Нужна ли реактивность? О чём статья, вообще?
"Как можно инкапсулировать логику", "Сервисная архитектура во Vue 2" - статья точно не об этом.
Это статья-рассуждение о том, что я чаще всего видела в проектах, когда появлялась необходимость вынести логику. И рассуждение как раз и сводилось к тому, что чаще используют инструменты, не приспособленные хранить и реализовывать внутри себя логику, которая уже начинает оформляться в сервис.
В блоке вводные я как раз описала, как на мой взгляд сервис должен работать, и какие ситуации покрывать.
Претенциозное название статьи абсолютно не соответствует её содержимому. Погуглите термин "сервисная архитектура" и посмотрите, что обычно публикуется в этом контексте в IT
Сервисная архитектура во Vue 2 | Какие собственно варианты?