Feature-Sliced Design (FSD) — это методология организации кода, направленная на разделение приложения на независимые, но при этом взаимосвязанные слои. Каждый слой представляет собой конкретную функциональность приложения, что позволяет легко масштабировать проект, поддерживать его и интегрировать новые фичи без переписывания уже существующего кода.
В контексте Solid.js, который ориентирован на создание реактивных пользовательских интерфейсов с максимальной производительностью, использование FSD помогает создать чистую архитектуру, повышая удобство разработки и улучшая поддержку кода.
Основная идея FSD — разделение проекта на несколько “срезов”, каждый из которых представляет собой отдельную функциональную единицу. Это разделение можно применить к любой крупной системе, включая фронтенд-приложения на Solid.js. Структура проекта может быть организована следующим образом:
Каждая фича представляет собой независимый модуль, который включает в себя все необходимое для своей работы: компоненты, состояния, логику и стили. Такой подход позволяет команде разработчиков работать над отдельными фичами без риска затронуть другие части приложения.
В Solid.js каждая фича может быть представлена в виде самостоятельного компонента с собственными реактивными состояниями. Это позволяет легко добавлять новые фичи без изменений в основной кодовой базе. Расширение приложения не требует переписывания старого кода, а лишь добавления новых срезов.
Важной частью FSD является разделение на слои, где каждая
функциональность инкапсулируется в своем модуле. Компоненты, хуки и
утилиты, которые могут быть использованы в разных частях приложения,
помещаются в слой shared. Это позволяет избежать
дублирования кода и повысить его повторное использование.
Каждый срез, фича или сущность в Solid.js можно протестировать независимо от других частей системы. В FSD легко выделить отдельные компоненты и провести их юнит-тестирование. Это значительно упрощает процессы QA и автоматического тестирования, ускоряя доставку качественного продукта.
FSD помогает снизить взаимозависимость между различными частями приложения. Каждая фича зависит только от своей внутренней логики и того, что она получает через публичный интерфейс. Это позволяет легко заменять или улучшать функциональность в одном месте без изменения других частей системы.
При реализации Feature-Sliced Design в Solid.js необходимо учитывать несколько важных аспектов, таких как структура состояний, маршрутизация и работа с реактивностью.
Каждая фича должна быть инкапсулирована в своем модуле. Например, если приложение содержит функционал корзины покупок, то для него создается отдельный модуль, включающий компоненты, хуки и бизнес-логику:
/src
/features
/cart
index.tsx
cartStore.ts
cartActions.ts
cartItems.tsx
В этом примере:
index.tsx — основной компонент корзины.cartStore.ts — содержит состояние корзины.cartActions.ts — функции для добавления/удаления
товаров.cartItems.tsx — отображение товаров в корзине.Solid.js предоставляет мощную реактивную систему, которая позволяет
эффективно управлять состоянием. Например, для управления состоянием
корзины можно использовать createSignal для хранения списка
товаров и их количества:
import { createSignal } from 'solid-js';
const [cartItems, setCartItems] = createSignal([]);
Для добавления товаров в корзину можно создать хук, который будет изменять состояние:
export function useCart() {
const [cartItems, setCartItems] = createSignal([]);
const addItem = (item) => {
setCartItems([...cartItems(), item]);
};
return { cartItems, addItem };
}
Этот хук может быть использован в любом компоненте, который требует доступа к состоянию корзины.
В Solid.js маршруты можно организовать с помощью библиотеки
solid-app-router. Каждая страница может быть реализована
как отдельный компонент в слое pages, что позволяет гибко
управлять маршрутами:
/src
/pages
/home
HomePage.tsx
/cart
CartPage.tsx
Компоненты страниц подключаются в файле маршрутизации:
import { Routes, Route } from 'solid-app-router';
import HomePage from './pages/home/HomePage';
import CartPage from './pages/cart/CartPage';
function App() {
return (
<Routes>
<Route path="/" component={HomePage} />
<Route path="/cart" component={CartPage} />
</Routes>
);
}
Каждая страница может использовать компоненты и функциональность из фич, организуя приложение по принципу изоляции логики.
В слое shared размещаются утилиты, хуки и компоненты,
которые могут быть использованы в разных частях приложения. Например,
кнопка может быть вынесена в общий компонент, чтобы использоваться на
разных страницах:
// shared/Button.tsx
import { JSX } from 'solid-js';
export default function Button(props: JSX.IntrinsicElements['button']) {
return <button {...props} />;
}
Такой подход позволяет избежать дублирования кода и сосредоточить повторно используемые элементы в одном месте.
Состояния в Solid.js могут быть разделены по срезам, каждый из
которых отвечает за свою часть данных. Важно помнить, что состояние
должно быть локализовано в соответствующем срезе. Для этого используются
реактивные хранилища (например, createSignal,
createStore) или глобальные менеджеры состояний, такие как
store для более сложных сценариев.
Рассмотрим проект, в котором есть фичи “пользователь”, “товары” и “корзина”. Состояние для каждого из этих слоев может быть изолировано и управляться отдельно:
// /features/user/userStore.ts
import { createSignal } from 'solid-js';
const [user, setUser] = createSignal(null);
export function useUser() {
return { user, setUser };
}
// /features/products/productsStore.ts
import { createSignal } from 'solid-js';
const [products, setProducts] = createSignal([]);
export function useProducts() {
return { products, setProducts };
}
В этом примере состояние пользователя и товаров независимы, и каждый модуль может управлять своим состоянием.
Использование Feature-Sliced Design в Solid.js помогает создать четкую архитектуру, упрощает управление состоянием и повышает масштабируемость приложения. Правильное разделение на фичи, сущности и общие компоненты позволяет эффективно работать с проектом, поддерживать его в будущем и интегрировать новые функциональности без необходимости переписывать существующий код.