
Долгое время думала, что использовать паттерны на фронте незачем и это больше тема для собесов
Но недавно все-таки удалось использовать паттерн фабрику для фронта и моему счастью не было предела, когда после 30-минутного рефакторинга разъехавшейся вёрстки через Claude, я попросила: "брат, слушай это ж паттерн фабрика, сделай BaseModal тонким, который просто решает, какой компонент отрисовать"
Технически, классический GoF Factory Method подразумевает наследование, а здесь у меня скорее Simple Factory — функция, выбирающая что создать. Но в обиходе все называют это "фабрикой", и я не буду усложнять.
Классический паттерн фабрика на Java:
// Интерфейс interface Modal { void open(); void close(); } // Конкретные реализации class Dialog implements Modal { ... } class BottomSheet implements Modal { ... } class FullscreenSheet implements Modal { ... } // Фабрика — решает какой класс создать class ModalFactory { static Modal create(String type, boolean isMobile) { if (!isMobile) return new Dialog(); return switch (type) { case "bottom-sheet" -> new BottomSheet(); case "fullscreen" -> new FullscreenSheet(); default -> new Dialog(); }; } } // Использование Modal modal = ModalFactory.create("bottom-sheet", isMobile); modal.open();
Как это работает во Vue?
На фронте есть
<component :is="..."/>- динамический компонент, который рендерит то, что ему передадут. Это и есть наш аналогModalFactory.create(...).
<!-- BaseModal.vue — фабрика --> <template> <component :is="modalComponent" v-bind="$props" @close="emit('close')" > <slot /> </component> </template> <script setup> // Фабричный метод — выбирает компонент const modalComponent = computed(() => { // Desktop → всегда Dialog (центрированный) if (!isMobile.value) return BaseDialog // Mobile → зависит от mobileStyle switch (props.mobileStyle) { case 'fullscreen': return BaseFullscreenSheet case 'bottom-sheet': return BaseBottomSheet default: return BaseDialog } }) </script>
Получился BaseModal, который сам почти ничего не делает.
Он не знает, как устроен dialog.
Не знает, как анимируется bottom sheet.
Не знает, как выглядит fullscreen-модалка.
Он просто маршрутизирует:
BaseModal.vue ├─ BaseDialog.vue ├─ BaseBottomSheet.vue └─ BaseFullscreenSheet.vue
А каждая конкретная реализация живёт отдельно и отвечает только за себя.
Почему это лучше, чем один большой компонент?
Потому что большой универсальный компонент очень быстро превращается в кашу:
<!-- Каша в template --> <div class="modal" :class="{ 'modal--open': open, 'modal--mobile': isMobile, 'modal--desktop': !isMobile, 'modal--fullscreen': isMobile && mobileStyle === 'fullscreen', 'modal--bottom-sheet': isMobile && mobileStyle === 'bottom-sheet', }" >
А потом туда добавляются:
разные анимации
разные отступы и размеры
разное поведение закрытия
разные transition
разные layout-правила
И компонент, который должен был быть базовой модалкой, внезапно становится местом, куда страшно заходить.
С фабрикой проще:
BaseDialogотвечает за centered dialogBaseBottomSheetотвечает за bottom sheetBaseFullscreenSheetотвечает за fullscreenBaseModalтолько выбирает, что показать
То есть вместо одного монолита появляется тонкий слой выбора и несколько изолированных компонентов.
И кстати, поделитесь: насколько паттерны актуальны сейчас? Или про них всё рассказали 20 лет назад и хватит говорить о них?















