Этот проект - блог, созданный с использованием React/ReduxToolkit. Он представляет возможность просматривать список статей и страницы статей. Проект ценен, в первую очередь, не за функционал или дизайн, а за обилие используемых инструментов, библиотек, их правильной настройки и современных подходов production-уровня. Сейчас я использую этот проект в качестве экспериментального полигона для опробования разных технологий и в качестве портфолио.
Проект написан в соответствии с методологией Feature Sliced Design
shared
— переиспользуемый код, не имеющий отношения к специфике приложения/бизнеса.entities
(сущности) — бизнес-сущности.features
(фичи) — взаимодействия с пользователем, действия, которые несут бизнес-ценность для пользователя.widgets
(виджеты) — композиционный слой для соединения сущностей и фич в самостоятельные блоки.pages
(страницы) — композиционный слой для сборки полноценных страниц из сущностей, фич и виджетов.app
— настройки, стили и провайдеры для всего приложения.
- Lang:
typescript
- UI:
React
- Data model:
Redux Toolkit
- Fetch data:
RTK-Query
/redux AsyncThunk
- Styles:
SCSS Modules
- i18n:
i18next
- Lint:
TS:
eslint
SCSS:stylelint
- Formatter:
prettier
- Testing:
Unit:
jest
,react testing library
E2E:cypress
Screenshot:storybook
,loki
,reg-cli
- Builders/Bundlers:
webpack
,babel
,@babel/plugin-transform-runtime
orvite
Пользователи могут авторизоваться, просматривать список статей, искать, фильтровать, сортировать их. Так же есть возможность открывать страницу со статьей, оценивать статью, оставлять комментарии, просматривать рекомендации. Еще можно редактировать свой профиль.
Необходим nodejs 18 и выше
Установить зависимости
npm install
Запуск приложения в dev режиме
npm run start:dev
или
npm run start:dev:vite
npm run start
- Запуск frontend проекта на webpack dev servernpm run start:vite
- Запуск frontend проекта на vitenpm run start:dev
- Запуск frontend проекта на webpack dev server + backend (json server)npm run start:dev:vite
- Запуск frontend проекта на vite + backend (json server)npm run start:dev:server
- Запуск backend сервера (json server)npm run build
- Сборка в prod режимеnpm run build:dev
- Сборка в dev режимеnpm run lint:ts
- Линт ts/tsx файловnpm run lint:ts:fix
- Автофикс ts файлов линтеромnpm run lint:typecheck
- Тайпчекинг (tsc с флагом noEmit)npm run lint:scss
- Линт scss файловnpm run lint:scss:fix
- Автофикс scss файлов style линтеромnpm run lint:all
- Линт ts и scssnpm run test:unit
- Запуск unit тестов с jestnpm run test:ui
- Запуск скриншотных тестов с lokinpm run test:ui:ok
- Подтверждение новых скриншотовnpm run test:ui:update
- Обновление скриншотовnpm run test:ui:ci
- Запуск скриншотных тестов в CInpm run test:ui:report
- Генерация полного отчета для скриншотных тестовnpm run test:ui:json
- Генерация json отчета для скриншотных тестовnpm run test:ui:html
- Генерация HTML отчета для скриншотных тестовnpm run storybook
- запуск Storybooknpm run storybook:build
- Сборка storybook билда (используется при скриншотном тестировании в CI)npm run prepare
- прекоммит хуки
В проекте используется библиотека i18next
для работы с переводами.
Файлы с переводами хранятся в public/locales
Для VSCode есть расширение lokalise.i18n-ally
с подсказками для ключей переводов.
Подробнее о тестах - документация по тестам
В проекте используется eslint
для проверки typescript кода и stylelint
для проверки файлов со стилями.
Также для строгого контроля главных архитектурных принципов
используется собственный eslint plugin eslint-plugin-fsd-tools-mishakin
,
который содержит 3 правила
- path-checker - запрещает использовать абсолютные импорты в рамках одного модуля, имеет auto fix
- layer-imports - проверяет корректность использования слоев с точки зрения FSD (например widgets нельзя использовать в features и entitites)
- public-api-imports - разрешает импорт из других модулей только из public api. Имеет auto fix
В проекте для компонентов описываются стори-кейсы.
Запросы на сервер мокаются с помощью storybook-addon-mock
.
Файл со сторикейсами должен лежать рядом с компонентом с расширением .stories.tsx
Подробнее о Storybook
Для разработки проект содержит 2 конфига:
- Webpack -
./config/build
- Vite -
vite.config.ts
Оба сборщика адаптированы под основные фичи приложения.
Вся конфигурация хранится в /config
/config/babel
- babel/config/build
- конфигурация webpack/config/jest
- конфигурация тестовой среды/config/storybook
- конфигурация сторибука
В папке scripts
находятся различные скрипты для рефакторинга\упрощения написания кода\генерации отчетов и тд.
В проекте настроены абсолютные импорты с использованием алиаса @
на папку src
.
Таким образом, согласно FSD, нужно делать такие абсолютные импорты при при импорте из другого слоя:
import { Product } from '@/entities/Product';
Конфигурация github actions находится в /.github/workflows
.
В ci прогоняются все виды тестов, сборка проекта и сторибука, линтинг.
В прекоммит хуках проверяем проект линтерами, конфиг в /.husky
Взаимодействие с данными осуществляется с помощью redux toolkit
.
По возможности переиспользуемые сущности необходимо нормализовать с помощью EntityAdapter
Запросы на сервер отправляются с помощью RTK query
Для асинхронного подключения редюсеров (чтобы не тянуть их в общий бандл) используется DynamicModuleLoader
Разрешено использование feature flags только с помощью хелпера toggleFeatures и его компонентной версии ToggleFeatures, для удобства автоматизации и кодогенерации
toggleFeatures принимает объект с опциями:
{
name: название фича-флага,
on: функция, которая отработает после Включения фичи
of: функция, которая отработает после ВЫключения фичи
}
Пример:
const avatarSize = toggleFeatures({
name: 'newAvatarSize',
on: () => 50,
off: () => 30,
});
<ToggleFeatures
name="isArticleRatingEnabled"
on={<ArticleRating articleId={id} />}
off={<Card>{t('Оценка статей скоро появится!')}</Card>}
/>
Для автоматического удаления фичи можно использовать скрипт remove-feature.ts
,
который принимает 2 аргумента
- Название удаляемого фича-флага
- Состояние (on\off)
Пример:
ts-node scripts/remove-feature.ts isArticleRatingEnabled on
Код выше превратится в:
const avatarSize = 50;
и
<ArticleRating articleId={id} />