Как вынести компоненты чтобы использовать их в другом проекте?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Извлечение и переиспользование компонентов в разных проектах
Это отличный вопрос о масштабировании и code reuse. Есть несколько подходов, от простых до продвинутых, в зависимости от масштаба и требований.
1. Монорепозиторий (Monorepo)
Для одной организации с несколькими проектами — это самый практичный подход:
projects/
├── web-app/
│ ├── src/
│ ├── package.json
│ └── tsconfig.json
├── mobile-app/
│ ├── src/
│ ├── package.json
│ └── tsconfig.json
└── packages/
├── ui-components/ <- Общие компоненты
│ ├── src/
│ │ ├── Button.tsx
│ │ ├── Card.tsx
│ │ └── index.ts
│ ├── package.json
│ └── tsconfig.json
├── shared-utils/ <- Утилиты
├── shared-hooks/ <- Кастомные хуки
└── shared-types/ <- TypeScript типы
Инструменты для монорепо:
# Npm workspaces (встроено в npm 7+)
# package.json в корне
{
"workspaces": [
"packages/*",
"projects/*"
]
}
# Yarn workspaces
yarn install # Устанавливает всё одно раз
# pnpm
pnpm install
# Turborepo (для масштабирования)
turbo run build --filter=ui-components
# Nx (полнофункциональное решение)
nx generate @nrwl/react:library ui-components
nx build ui-components
Пример package.json в пакете:
{
"name": "@myorg/ui-components",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsc",
"test": "vitest"
}
}
Использование в другом проекте внутри монорепо:
// web-app/src/App.tsx
import { Button, Card } from '@myorg/ui-components';
export function App() {
return (
<Card>
<Button variant="primary">Click me</Button>
</Card>
);
}
2. NPM пакет (отдельный репозиторий)
Если компоненты нужны в совсем разных проектах или они public:
# Создать npm пакет
mkdir ui-components
cd ui-components
npm init
Структура проекта-пакета:
ui-components/
├── src/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.test.tsx
│ │ └── index.ts
│ ├── Card/
│ │ ├── Card.tsx
│ │ ├── Card.test.tsx
│ │ └── index.ts
│ └── index.ts
├── dist/ <- Скомпилированный код
├── package.json
├── tsconfig.json
├── rollup.config.js <- Конфиг для bundler
└── README.md
package.json для публикации:
{
"name": "@mycompany/ui-components",
"version": "1.0.0",
"description": "Reusable React components",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/index.d.ts"
},
"./Button": {
"import": "./dist/Button/index.js",
"types": "./dist/Button/index.d.ts"
}
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"react": "^18.0.0",
"typescript": "^5.0.0"
},
"scripts": {
"build": "rollup -c",
"test": "vitest",
"prepublishOnly": "npm run build && npm run test"
}
}
Rollup конфиг для сборки:
// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import react from '@vitejs/plugin-react';
export default {
input: 'src/index.ts',
output: [
{
file: 'dist/index.js',
format: 'es',
},
{
file: 'dist/cjs/index.js',
format: 'cjs',
},
],
external: ['react', 'react-dom'],
plugins: [ ypescript(), react()],
};
3. GitHub Packages (приватные пакеты)
Для приватных компонентов в GitHub:
# .npmrc в проекте
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" > ~/.npmrc
echo "@mycompany:registry=https://npm.pkg.github.com" >> ~/.npmrc
Публикация:
npm publish # Публикуется в GitHub Packages
Установка в другом проекте:
npm install @mycompany/ui-components
4. Скриптовое извлечение (для legacy случаев)
Если нет возможности создать отдельный пакет:
# Copy-paste компонентов (самый простой способ)
cp src/Button/Button.tsx ../other-project/src/shared/
# Git subtree (более правильно)
git subtree add --prefix packages/ui shared-components main
git subtree pull --prefix packages/ui shared-components main
# Git submodule
git submodule add https://github.com/company/ui-components.git packages/ui
Лучшие практики для извлечения
1. Не зависит от конкретного проекта:
// Плохо — привязано к project-specific логике
const Button = () => {
const store = useProjectStore(); // !! Зависимость от проекта
return <button>{store.theme}</button>;
};
// Хорошо — принимает всё через props
interface ButtonProps {
theme: 'light' | 'dark';
onClick: () => void;
}
const Button = ({ theme, onClick }: ButtonProps) => (
<button className={theme}>{label}</button>
);
2. Чистые зависимости:
// Плохо — много зависимостей
interface ComplexComponent {
reduxStore: any;
apiClient: any;
analyticsService: any;
authContext: any;
}
// Хорошо — минималистично
interface SimpleComponent {
data: string;
onSubmit: (value: string) => void;
}
3. TypeScript типы в отдельном файле:
// src/types.ts
export interface ButtonProps {
variant: 'primary' | 'secondary';
size: 'sm' | 'md' | 'lg';
children: React.ReactNode;
onClick?: () => void;
}
// src/Button.tsx
import type { ButtonProps } from './types';
export const Button = (props: ButtonProps) => { ... };
4. Документация и примеры:
# UI Components
## Button
```tsx
import { Button } from '@mycompany/ui-components';
<Button variant="primary" size="lg">
Click me
</Button>
Props
variant: primary | secondary | dangersize: sm | md | lgdisabled: boolean
**5. Версионирование (Semantic Versioning):**
MAJOR.MINOR.PATCH 1.0.0 ^ ^ ^ | | └─ Patch: bug fixes (1.0.1) | └─── Minor: new features (1.1.0) └───── Major: breaking changes (2.0.0)
**6. Changelog:**
```markdown
# Changelog
## [1.1.0] - 2024-04-01
### Added
- New `disabled` prop to Button
### Changed
- Improved TypeScript types
## [1.0.0] - 2024-03-15
### Added
- Initial release with Button, Card components
Рекомендуемый workflow
# Для стартапа / одной компании
1. Монорепо (Npm workspaces) <- START HERE
├─ Просто
├─ Синхронизированные версии
└─ Легко обновлять
# Для растущей организации
2. Монорепо + Turborepo
├─ Быстрые сборки
├─ Кэширование
└─ Масштабирование
# Для коммерческих компонентов
3. Отдельный npm пакет
├─ Публичная документация
├─ Версионирование
└─ Большая гибкость
# Для open source
4. Отдельный git репозиторий + npm registry
├─ Лицензирование
├─ Сообщество
└─ CI/CD автоматизация
Итог
Для большинства случаев монорепо с npm workspaces — оптимальное решение:
- Простая настройка
- Синхронизированные версии
- Легко обновлять компоненты
- Масштабируется с Turborepo при росте
Для публичных/коммерческих пакетов — отдельный npm репозиторий с полноценным CI/CD.