Ваш пример я понял как "из домена финансов нам надо взять класс в домен заказа".
Если вы берете класс из финансов и в заказах делаете внедрение, то в этом случае я бы предпочел вынести из финансов класс в общий домен, условный shared, и тогда домены не будут иметь связи друг с другом.
А это значит, что описание класса существует в общем домене, в финансах будет просто создание экземпляра, а в заказах создание и внедрение в этот класс.
Аналогично делается и с композаблами: помните выше я писал про фабрики и функции высшего порядка?
Если уж так хочется, то и в композабл можно сделать внедрение как в класс (таким же механизмом), но тогда мы снова возвращаемся к вопросу: зачем?
Я бы еще понял классы в Vue 2, но в 3...
Полного аналога внедрения как в классе у композабла нет, конечно, но есть другие механизмы для реализации и причина, по которой вами выбраны классы, скорее в привычке использования классов, а не в том что описано в статье и в комментариях.
Про кол-во определения свойств такое может быть, но зависит от того как написать композабл и от преследуемой цели. Это может быть как портянка свойств, так и 1 свойство, это может быть функция, извлекающая нужные свойства и вот мы уже 20 строк перечисления свойств родительского композабла превратили в 1 строку.
А чтобы не обмазываться проверками дополнительными, мы же можем создать функцию высшего порядка, которая за нас это будет делать? Определить в любом композабле её можно, да хоть как хелпер отдельный - вот уже и решили эту проблему.
А, если это запросы API, то вообще интерцепторы используем и catch становится нужен в исчезающе редких случаях, а где нужно, там он скорее всего будет специфичную логику иметь для конкретной реализации и это в дополнение к тому, что прописано в интерцепторе, т.к. логика в нём максимально базовая и агностичная к пользователю вызова.
Композаблы про создание самодостаточного или служебного кода: про вычисления, про что угодно - зависит лишь от задачи. Это просто фабрика.
Композабл может быть создан как для компонента, так и для других композаблов, а так же и для использования внешним потребителем (экспортируем из ui, например).
Мы в проекте сейчас, например, используем наравне Pinia и композаблы:
Pinia удобна для стора, остальное - композаблы
Некоторым сторам создали фабрики для создания разных инстансов сторов со своими дополнениями/изменениями, но в базе своей использующих 1 стор - аналог наследования...
Есть совсем базовые композаблы, которые используются композаблами, которые используются композаблами и даже компонентами, которые используют композаблы, которые используют композаблы - снова наследование...
Есть композаблы, которые являются адаптерами - снова что-то из ООП...
Как видим принципы ООП прекрасно ложатся на композаблы.
По вашим ответам/вопросам поясню, что композабл:
Можно передать куда угодно и состояние динамическое: хотите инкапсуляцию и персональное состояние на композабл - пожалуйста, хотите синглтон - пожалуйста
Не надо думать с композаблом об этом - используем где надо и только то что надо. Хоть в композабле, хоть в классе можно написать 100500 методов, которые будут выполнять только console.log, но это не значит что он привязан к конкретному компоненту - всё зависит от того что написать в композабле
Кода будет не драмматически больше, а иногда и меньше, ибо мы так же будем использовать другой композабл и, что более полезно, мы будем явно видеть откуда у нас та или иная сущность взялась и при больших цепочках наследования защищены от возможности что-то сломать или изменить поведение, как это при наследовании может произойти, т.к. мы явно видим что используем и при6имаем решение осознанно
Про внедрение уже сказал: мы можем хоть паровоз из композаблов построить в 100500 этажей. А если надо где-то что-то взять и сунуть в прототип как в классах, чтобы доступно стало всем, то - спасибо - не хочу искать это место "где-то в проекте", чтобы разгребать почему метод не такой как ожидается
Зачем вы тащите классы в Vue? Что мешает написать композабл? Любой класс можно переписать на композабл и это:
Следует парадигме Vue
Использовать все возможности Vue
Расширяемо
Переиспользуемо
ООП-эшно, внезапно! Ибо композабл можно использовать в другом композабле, да хоть цепочку устроить в N потомков
Повышает комфорт разработки ибо не развивает зоопарка, где часть использует композаблы, а другая, видимо "трушная" часть использует классы и не приходится склеивать 2 системы, а пользуемся тем что даёт фреймворк из-под капота
И да, если цель "не учить новое", то это и не надо, т.к. используются те же возможности, что и в клмпонентах
О чем и речь: any - зло, если только на проекте, которым можно полноценно управлять.
Если мы говорим о внешних зависимостях, то any вполне применим, т.к. типобезопасность ts - миф и легко обходится, да и сам ts тупит нормально и, порой, только костылями его можно заставить думать как хотим мы.
p.s.
хоть речь не о нем, а лишь в качестве примера, тот же cloneDeep, когда я смотрел последний раз, был на js и в jsDoc у него был указан аргумент как * - any
Функция глубокого копирования по аналогии с lodash cloneDeep, блоки catch.
Т.е. речь о ситуациях, когда нам придётся либо обвешиваться type guard, что не гарантирует что все случаи предусмотрели (человеческий фактор / неопытность разработчика), либо как раз создадим головную боль потребителю, которому ts будет ругаться, что его тип не совместим с unknown.
Например в случае с глубоким копированием, лично я, не вижу смысла делать эту функцию типобезопасной, потому что туда передать могут реально практически что угодно - в этом смысл, а следовательно и проще / дешевле для разработки использование any будет более чем оправданно.
any достаточно полезен может быть при написании библиотек, чтобы не приходилось изгаляться с ограниченными типами, которые могут быть использованы, но автор их не предусмотрел (не описал).
НО, это должно быть осознанное использование any автором библиотеки, а не "я художник - я так вижу".
"Используй unknown", скажет кто-то, но unknown - головна боль приведения типа каждый раз и не всегда это уместно, поэтому, чтобы этого избежать и пользователи сказали "спасибо", вместо мата в 3 этажа, имеет смысл указать any
Можно, пожалуйста, хотя-бы один аргумент про удобство и почему это - извращение?
Можно:)
Задача: - Сделать строку поиска - Введенные значения отправляются на сервер и результаты отдаются на фронт
РЕШЕНИЕ 1. Делать через computed: - реагировать на изменение значения в поиске - отправлять запрос на сервер ТАК НЕЛЬЗЯ делать и это и назвал извращением, потому что computed не для того создан.
РЕШЕНИЕ 2. Делать через computed, но при этом computed - лишь часть логики, которая будет выглядеть как: - ввели значение в строке поиска: сработала v-model - среагировали вотчером на изменение значения переменной - записали полученные с сервера данные в новую переменную - обработали результаты поиска в computed
Как-то много телодвижений...
РЕШЕНИЕ 3. Метод на событие input элемента / компонента строки поиска: - ввели значение в строке поиска - среагировали на событие элемента / компонента - отправили запрос (1 функция) - обработали данные в методе и записали в переменную результата (2-ая функция внутри первой функции)
Расходов на понимание со стороны разработчика и обслуживание кода со стороны движка получается много меньше и для разработчика много понятнее "что откуда и куда" берётся без распутывания цепочки с вотчерами и компьютедами, т.к. всё стало более явно, а явное всегда лучше.
Вы же, напротив, зачем-то уходите к общему случаю, когда тут очевидно речь о валидации формы .
Если вам это очевидно - ну ок :) Из-за таких "это же очевидно" потом и возникают "а я думал вы поняли / имели это в виду". Нет, не очевидно, если об этом не сказано прямо. Да и косвенных тому намёков нет. Если вы угадали, что это про валидацию, могу только порадоваться вашей догадливости.
Вероятнее валидировать придется на событии change, либо в режиме realtime, раз уж на то пошло.
Я четко написал задание: валидировать форму при сабмите, а это значит, что требование, чтобы пользователь видел ошибки только после клика на сабмит.. Но возникает вопрос: зачем валидировать через computed, если это можно сделать 1 раз при клике на сабмит, если пользователь узнает о состоянии валидации только при клике на сабмит? Вы предлагаете computed и я не говорю, что это решение плохое - вполне нормальное, но я лишь говорю что дешевле сделать то же самое методом.
очевидно у вас возникнут проблемы с перфом из-за валидации формы
всё зависит от конкретной ситуации, ведь это может быть как форма на 2 инпута, так и форма, состоящая из десятков, а то и сотен полей (привет no-code / low-code конструкторы) и вот получили тормозного монстра, потому что computed обсчитывает валидацию на каждый чих в форме, поэтому каждой задаче своё решение и постулировать "В реальной жизни, к счастью, никто не следит на событиями элементов формы в vue, особенно в рамках валидации полей " не стоит. Как уже видно из примеров выше, следим когда это требуется.
Не было контекста про валидацию, равно как и был пример в статье про поиск. Это уже ваше сообщение про валидацию и, если говорить только о валидации или чем-то другом - уход от общего к частному, что вы и делаете. Речь же о более широком использовании.
И да, я не против computed для обработки валидации, но опять же это не всегда уместно - зависит от требований: вероятно валидировать нужно всего 1 раз при нажатии на сабмит и тогда предпочтительней будет метод, а не вычисляемое свойство, которое запускается на каждое изменение, ведь computed не бесплатен.
Согласен с предыдущим оратором, т.к. не валидацией единой живем. Тот же запрос при вводе в поле поиска удобнее сделать через событие элемента, а не через вотчеры. И computed тут вообще никак не поможет.
p.s. при желании можно и через него сделать, но это извращение
Можно писать на js так же: с подсветкой ошибок и выводом их в консоль, + подсказки работают в ide, только не будет крашиться приложение, если тип ошибочен.
Это достигается связкой Ts + jsdock + включенная валидация js в vs code
Но! При этом мы пишем js, а не ts и не имеем той массы условностей и многословности, которые заставляет нас делать ts
Ts установлен в dev dep и служит для того для чего и нужен: сопоставляет типы
Пожалуйста, старайтесь использовать shallowmount и только потом mount, когда он реально нужен или без него никак не получается, т.к. это убережет от кучи проблем, связанных с mount, аля "сломалось что-то на 3-4 уровня вниз, а падает именно этот тест и теперь ищи и разбирайся почему".
Не понятна проблема стабать состояние компонента ui? Он же по дефолту имеет значения пропсов и в тестируемом компоненте он маунтится именно с дефолтным состоянием изначально. Если же нет, то надо понимать откуда берется это состояние: - если из глобального стейта, то перед выполнение теста установить нужное значение в стейте - если в ходе работы логики тестируемого компонента - вообще не стоит переживать, т.к. это поведение компонента и это даже можно протестировать - если хардкодом прописано значение пропса - можно стабать вручную
Но, в любом случае, по возможности, надо стараться по минимуму стабать руками компоненты, т.к. это несет много банальных рисков: "добавили фичу / исправили баг в компоненте, а тест не обновили".
Зачем стабать вручную дочерние компоненты, если это уже сделал shallowmount? Если это только из-за слота, так дефолтный слот можно рендерить с помощью renderStubDefaultSlot
Без явной необходимости не стоит стабать вручную дочерний компонент и не будет (почти) проблем в будущем при измении их контрактов, потому что тест будет смотреть на реальную реализацию компонента, а не его ручной стаб в тесте.
Имхо. Очень мало ситуаций когда надо стабать вручную компоненты. Как пример на скорую мысль, приходит только: у тестируемой сущности нет четкого шаблона (тесты в ядре vue) или надо настабать не дефолтные слоты, что может решиться использованием mount, а не shallowmount.
Сброс моков: для примера это ок, чтобы наглядно было, но в проекте это можно вынести в глобальную область, т.к., когда 1 компонент, это всё интересно и прикольно, но, когда из раза в раз приходится писать, утомляет. Это вкусовщина, на самом деле. Упоминаю на всякий случай: вдруг не в курсе возможности и это можно описать отдельным абзацем на самом деле.
Часть импортов из vitest можно не делать: работает по дефолту, т.к. всегда доступно в тестах, если настроить vite.config
Мы решили это так: если есть какой-то компонент локальный для сущности слоя, то структура будет, например: entities/parent-component/components/child-component, где child-component, в свою очередь, будет иметь свои папки (utils, store, styles и т.д.). Таким образом получается, что сущности, которые точно не переиспользуемые, не размазываются по приложению.
Да, это не чистый fsd, но и в доке написано, что её может потребоваться модифицировать. Зато это даёт как свободу и переиспользуемость fsd, а также снижает порог входа для новых разработчиков (или старых тех кто с каким-то модулем столкнётся впервые).
Ваш пример я понял как "из домена финансов нам надо взять класс в домен заказа".
Если вы берете класс из финансов и в заказах делаете внедрение, то в этом случае я бы предпочел вынести из финансов класс в общий домен, условный shared, и тогда домены не будут иметь связи друг с другом.
А это значит, что описание класса существует в общем домене, в финансах будет просто создание экземпляра, а в заказах создание и внедрение в этот класс.
Аналогично делается и с композаблами: помните выше я писал про фабрики и функции высшего порядка?
Если уж так хочется, то и в композабл можно сделать внедрение как в класс (таким же механизмом), но тогда мы снова возвращаемся к вопросу: зачем?
Я бы еще понял классы в Vue 2, но в 3...
Полного аналога внедрения как в классе у композабла нет, конечно, но есть другие механизмы для реализации и причина, по которой вами выбраны классы, скорее в привычке использования классов, а не в том что описано в статье и в комментариях.
Значит они изменили доменную область и должны находиться в других местах, согласно новым реалиям.
Что так же применимо и к классам
Про кол-во определения свойств такое может быть, но зависит от того как написать композабл и от преследуемой цели. Это может быть как портянка свойств, так и 1 свойство, это может быть функция, извлекающая нужные свойства и вот мы уже 20 строк перечисления свойств родительского композабла превратили в 1 строку.
А чтобы не обмазываться проверками дополнительными, мы же можем создать функцию высшего порядка, которая за нас это будет делать? Определить в любом композабле её можно, да хоть как хелпер отдельный - вот уже и решили эту проблему.
А, если это запросы API, то вообще интерцепторы используем и catch становится нужен в исчезающе редких случаях, а где нужно, там он скорее всего будет специфичную логику иметь для конкретной реализации и это в дополнение к тому, что прописано в интерцепторе, т.к. логика в нём максимально базовая и агностичная к пользователю вызова.
Композаблы это не про разбиение кода компонента.
Композаблы про создание самодостаточного или служебного кода: про вычисления, про что угодно - зависит лишь от задачи. Это просто фабрика.
Композабл может быть создан как для компонента, так и для других композаблов, а так же и для использования внешним потребителем (экспортируем из ui, например).
Мы в проекте сейчас, например, используем наравне Pinia и композаблы:
Pinia удобна для стора, остальное - композаблы
Некоторым сторам создали фабрики для создания разных инстансов сторов со своими дополнениями/изменениями, но в базе своей использующих 1 стор - аналог наследования...
Есть совсем базовые композаблы, которые используются композаблами, которые используются композаблами и даже компонентами, которые используют композаблы, которые используют композаблы - снова наследование...
Есть композаблы, которые являются адаптерами - снова что-то из ООП...
Как видим принципы ООП прекрасно ложатся на композаблы.
По вашим ответам/вопросам поясню, что композабл:
Можно передать куда угодно и состояние динамическое: хотите инкапсуляцию и персональное состояние на композабл - пожалуйста, хотите синглтон - пожалуйста
Не надо думать с композаблом об этом - используем где надо и только то что надо. Хоть в композабле, хоть в классе можно написать 100500 методов, которые будут выполнять только console.log, но это не значит что он привязан к конкретному компоненту - всё зависит от того что написать в композабле
Кода будет не драмматически больше, а иногда и меньше, ибо мы так же будем использовать другой композабл и, что более полезно, мы будем явно видеть откуда у нас та или иная сущность взялась и при больших цепочках наследования защищены от возможности что-то сломать или изменить поведение, как это при наследовании может произойти, т.к. мы явно видим что используем и при6имаем решение осознанно
Про внедрение уже сказал: мы можем хоть паровоз из композаблов построить в 100500 этажей. А если надо где-то что-то взять и сунуть в прототип как в классах, чтобы доступно стало всем, то - спасибо - не хочу искать это место "где-то в проекте", чтобы разгребать почему метод не такой как ожидается
Зачем вы тащите классы в Vue? Что мешает написать композабл? Любой класс можно переписать на композабл и это:
Следует парадигме Vue
Использовать все возможности Vue
Расширяемо
Переиспользуемо
ООП-эшно, внезапно! Ибо композабл можно использовать в другом композабле, да хоть цепочку устроить в N потомков
Повышает комфорт разработки ибо не развивает зоопарка, где часть использует композаблы, а другая, видимо "трушная" часть использует классы и не приходится склеивать 2 системы, а пользуемся тем что даёт фреймворк из-под капота
И да, если цель "не учить новое", то это и не надо, т.к. используются те же возможности, что и в клмпонентах
О чем и речь: any - зло, если только на проекте, которым можно полноценно управлять.
Если мы говорим о внешних зависимостях, то any вполне применим, т.к. типобезопасность ts - миф и легко обходится, да и сам ts тупит нормально и, порой, только костылями его можно заставить думать как хотим мы.
p.s.
хоть речь не о нем, а лишь в качестве примера, тот же cloneDeep, когда я смотрел последний раз, был на js и в jsDoc у него был указан аргумент как * - any
Функция глубокого копирования по аналогии с lodash cloneDeep, блоки catch.
Т.е. речь о ситуациях, когда нам придётся либо обвешиваться type guard, что не гарантирует что все случаи предусмотрели (человеческий фактор / неопытность разработчика), либо как раз создадим головную боль потребителю, которому ts будет ругаться, что его тип не совместим с unknown.
Например в случае с глубоким копированием, лично я, не вижу смысла делать эту функцию типобезопасной, потому что туда передать могут реально практически что угодно - в этом смысл, а следовательно и проще / дешевле для разработки использование any будет более чем оправданно.
anyдостаточно полезен может быть при написании библиотек, чтобы не приходилось изгаляться с ограниченными типами, которые могут быть использованы, но автор их не предусмотрел (не описал).НО, это должно быть осознанное использование
anyавтором библиотеки, а не "я художник - я так вижу"."Используй
unknown", скажет кто-то, ноunknown- головна боль приведения типа каждый раз и не всегда это уместно, поэтому, чтобы этого избежать и пользователи сказали "спасибо", вместо мата в 3 этажа, имеет смысл указатьanyДля чего публиковать настройку для 8 версии, если уже надо на 9 бы, т.к. для 8 много инфы.
Какие-то нюансы? Не понятно
Это да, но его можно подключить для 9 версии: у нас в проекте это настроено.
Мучался с конфигом неделю, наверное, чтобы разобраться в Eslint 9, чтобы всё работало, но успех успешен:)
Vue3 + Vite + TS + Eslint 9+
Все плюсы и минусы описаны в доке. Весь проект можно писать на ref и reactive вообще не использовать.
Зато синтакис property.value, в подавляющем большинстве случаев, сразу будет говорить о том, что используется реактивная переменная, а не что-то еще.
Можно, пожалуйста, хотя-бы один аргумент про удобство и почему это - извращение?
Можно:)
Задача:
- Сделать строку поиска
- Введенные значения отправляются на сервер и результаты отдаются на фронт
РЕШЕНИЕ 1. Делать через computed:
- реагировать на изменение значения в поиске
- отправлять запрос на сервер
ТАК НЕЛЬЗЯ делать и это и назвал извращением, потому что computed не для того создан.
РЕШЕНИЕ 2. Делать через computed, но при этом computed - лишь часть логики, которая будет выглядеть как:
- ввели значение в строке поиска: сработала v-model
- среагировали вотчером на изменение значения переменной
- записали полученные с сервера данные в новую переменную
- обработали результаты поиска в computed
Как-то много телодвижений...
РЕШЕНИЕ 3. Метод на событие input элемента / компонента строки поиска:
- ввели значение в строке поиска
- среагировали на событие элемента / компонента - отправили запрос (1 функция)
- обработали данные в методе и записали в переменную результата (2-ая функция внутри первой функции)
Расходов на понимание со стороны разработчика и обслуживание кода со стороны движка получается много меньше и для разработчика много понятнее "что откуда и куда" берётся без распутывания цепочки с вотчерами и компьютедами, т.к. всё стало более явно, а явное всегда лучше.
Вы же, напротив, зачем-то уходите к общему случаю, когда тут очевидно речь о валидации формы .
Если вам это очевидно - ну ок :) Из-за таких "это же очевидно" потом и возникают "а я думал вы поняли / имели это в виду". Нет, не очевидно, если об этом не сказано прямо. Да и косвенных тому намёков нет. Если вы угадали, что это про валидацию, могу только порадоваться вашей догадливости.
Вероятнее валидировать придется на событии change, либо в режиме realtime, раз уж на то пошло.
Я четко написал задание: валидировать форму при сабмите, а это значит, что требование, чтобы пользователь видел ошибки только после клика на сабмит..
Но возникает вопрос: зачем валидировать через computed, если это можно сделать 1 раз при клике на сабмит, если пользователь узнает о состоянии валидации только при клике на сабмит? Вы предлагаете computed и я не говорю, что это решение плохое - вполне нормальное, но я лишь говорю что дешевле сделать то же самое методом.
очевидно у вас возникнут проблемы с перфом из-за валидации формы
всё зависит от конкретной ситуации, ведь это может быть как форма на 2 инпута, так и форма, состоящая из десятков, а то и сотен полей (привет no-code / low-code конструкторы) и вот получили тормозного монстра, потому что computed обсчитывает валидацию на каждый чих в форме, поэтому каждой задаче своё решение и постулировать "В реальной жизни, к счастью, никто не следит на событиями элементов формы в vue, особенно в рамках валидации полей " не стоит. Как уже видно из примеров выше, следим когда это требуется.
Не было контекста про валидацию, равно как и был пример в статье про поиск. Это уже ваше сообщение про валидацию и, если говорить только о валидации или чем-то другом - уход от общего к частному, что вы и делаете. Речь же о более широком использовании.
И да, я не против computed для обработки валидации, но опять же это не всегда уместно - зависит от требований: вероятно валидировать нужно всего 1 раз при нажатии на сабмит и тогда предпочтительней будет метод, а не вычисляемое свойство, которое запускается на каждое изменение, ведь computed не бесплатен.
"Не валидацией единой живем"
Согласен с предыдущим оратором, т.к. не валидацией единой живем. Тот же запрос при вводе в поле поиска удобнее сделать через событие элемента, а не через вотчеры. И computed тут вообще никак не поможет.
p.s. при желании можно и через него сделать, но это извращение
Нет проблем: вызываем файл в PowerShell (./createPage.sh) или через терминал гита.
Тоже написал баш скрипт для генерации новой страницы: папки, файлы с нужными импортами, константами, подключениями и т.д.
Можно писать на js так же: с подсветкой ошибок и выводом их в консоль, + подсказки работают в ide, только не будет крашиться приложение, если тип ошибочен.
Это достигается связкой Ts + jsdock + включенная валидация js в vs code
Но! При этом мы пишем js, а не ts и не имеем той массы условностей и многословности, которые заставляет нас делать ts
Ts установлен в dev dep и служит для того для чего и нужен: сопоставляет типы
Пожалуйста, старайтесь использовать shallowmount и только потом mount, когда он реально нужен или без него никак не получается, т.к. это убережет от кучи проблем, связанных с mount, аля "сломалось что-то на 3-4 уровня вниз, а падает именно этот тест и теперь ищи и разбирайся почему".
Не понятна проблема стабать состояние компонента ui? Он же по дефолту имеет значения пропсов и в тестируемом компоненте он маунтится именно с дефолтным состоянием изначально. Если же нет, то надо понимать откуда берется это состояние:
- если из глобального стейта, то перед выполнение теста установить нужное значение в стейте
- если в ходе работы логики тестируемого компонента - вообще не стоит переживать, т.к. это поведение компонента и это даже можно протестировать
- если хардкодом прописано значение пропса - можно стабать вручную
Но, в любом случае, по возможности, надо стараться по минимуму стабать руками компоненты, т.к. это несет много банальных рисков: "добавили фичу / исправили баг в компоненте, а тест не обновили".
Несколько моментов за которые зацепился глаз:
Зачем стабать вручную дочерние компоненты, если это уже сделал shallowmount? Если это только из-за слота, так дефолтный слот можно рендерить с помощью renderStubDefaultSlot
Без явной необходимости не стоит стабать вручную дочерний компонент и не будет (почти) проблем в будущем при измении их контрактов, потому что тест будет смотреть на реальную реализацию компонента, а не его ручной стаб в тесте.
Имхо. Очень мало ситуаций когда надо стабать вручную компоненты. Как пример на скорую мысль, приходит только: у тестируемой сущности нет четкого шаблона (тесты в ядре vue) или надо настабать не дефолтные слоты, что может решиться использованием mount, а не shallowmount.
Сброс моков: для примера это ок, чтобы наглядно было, но в проекте это можно вынести в глобальную область, т.к., когда 1 компонент, это всё интересно и прикольно, но, когда из раза в раз приходится писать, утомляет. Это вкусовщина, на самом деле. Упоминаю на всякий случай: вдруг не в курсе возможности и это можно описать отдельным абзацем на самом деле.
Часть импортов из vitest можно не делать: работает по дефолту, т.к. всегда доступно в тестах, если настроить vite.config
Мы решили это так: если есть какой-то компонент локальный для сущности слоя, то структура будет, например: entities/parent-component/components/child-component, где child-component, в свою очередь, будет иметь свои папки (utils, store, styles и т.д.). Таким образом получается, что сущности, которые точно не переиспользуемые, не размазываются по приложению.
Да, это не чистый fsd, но и в доке написано, что её может потребоваться модифицировать. Зато это даёт как свободу и переиспользуемость fsd, а также снижает порог входа для новых разработчиков (или старых тех кто с каким-то модулем столкнётся впервые).