Мы не будем создавать монолитный проект, который подразумевает объединение нескольких компонентов и публикацию их в виде единого пакета. Вместо этого мы подготовим модульный проект, в котором каждый компонент будет управляться исходным кодом отдельно и публиковаться независимо от других.
Почему? Потому что мы хотим создать масштабируемую систему, которая позволит постепенно производить обмен и совместно работать с более сложными компонентами. Тем самым можно будет ускорить разработку проекта намного эффективнее, чем при помощи конечной библиотеки.
Мы будем использовать Bit, инструмент с открытым исходным кодом, который предоставляет очень интересные возможности:
- разработка изолированных компонентов, включая изолированные превью, тесты и сборки;
- управление источниками компонентов: по сути, Git для отдельных компонентов;
- управление зависимостями компонентов: автоматическое создание графиков зависимостей компонентов и интеллектуальное управление зависимостями.
Bit также содержит инструменты, упрощающие разработку компонентов:
- предварительно настроенные среды разработки (например, CRA для компонентов);
- инструменты, автоматически генерирующие
package.json
; - документацию по компонентам, использующую многомерные выражения, совместимые с Bit;
- предварительный просмотр изолированных компонентов и многое другое.


Создайте рабочее пространство Bit
Рабочее пространство Bit — это место, где компоненты будут разрабатываться и версионироваться независимо друг от друга.
Инициализируйте рабочее пространство Bit
- Установите Bit на свой компьютер.
Мы начнем с установки Bit version manager (BVM) и используем его для установки Bit.
$ npm i -g @teambit/bvm
$ bvm install
Для получения дополнительной информации и устранения неполадок см. документацию.
2. Инициализируйте новое рабочее пространство Bit с помощью шаблона react-workspace
. Назовем рабочее пространство my-component-library
:
$ bit new react-workspace my-component-library
новое рабочее пространство успешно создано на path/to/my-component-library
3. Установите все зависимости рабочего пространства:
$ bit install
Настройка удаленной области для размещения компонентов
Компоненты с индивидуальным управлением исходным кодом перемещаются не на стандартный хостинг Git (например, GitHub), а в удаленные области. Каждая область может содержать несколько компонентов (в качестве независимых мини-репозиториев). Это позволяет нам устанавливать разные разрешения для разных компонентов, даже если они созданы в одном и том рабочем пространстве Bit.
Bit.dev — это реестр пакетов и облачный хостинг для компонентов. Мы будем использовать его для публикации и установки пакетов, а также для совместной работы над компонентами. Вы можете изменить настройки рабочего пространства, чтобы использовать свой собственный хостинг компонентов и реестр пакетов узлов (например, реестр npm). Смотрите документацию Bit, чтобы получить более подробную информацию.
- Перейдите на bit.dev, чтобы зарегистрировать бесплатный аккаунт и создать свою собственную удаленную область (или коллекцию).
- Откройте файл
workspace.jsonc
и настройте свойствоdefaultScope
для вашего имени пользователя и названия области.
// file: workspace.jsonc
{
"$schema": "https://static.bit.dev/teambit/schemas/schema.json",
"teambit.workspace/workspace": {
"name": "my-component-library",
"defaultDirectory": "{scope}/{name}",
// <scope-owner>.<scope-name>
"defaultScope": "our-org.my-scope"
// ...
}
Запустите сервер Bit Dev и пользовательский интерфейс рабочего пространства
Пользовательский интерфейс рабочего пространства позволяет нам исследовать компоненты, управляемые этим пространством, и получать обратную связь в режиме реального времени о состоянии каждого компонента (обнаруженные проблемы и т. д.). С его помощью можно также запускать различные службы разработки в “режиме наблюдения” (например, тестирование, компиляция и т. д.).
$ bit start
НАЗВАНИЕ СРЕДЫ URL СТАТУС
teambit.react/react http://localhost:3100 RUNNING
Теперь вы можете просмотреть компоненты 'my-component-library' в браузере.
Сервер Bit работает на http://localhost:3000

Создайте файлы компонента
Мы будем использовать готовый шаблон Bit для компонентов React. Назовем компонент button (кнопка) и поместим его в пространство имен inputs (вводы). Пространства имен помогают нам организовать компоненты в рабочем пространстве, а затем в удаленной области.
$ bit create react-component inputs/button
the following 1 component(s) were created
my-scope/inputs/button
location: my-scope/inputs/button
env: teambit.react/react
Использовать шаблон компонента необязательно. Подробнее об этом читайте в документации.
Отслеживание нового компонента
Файлы компонентов были сгенерированы и теперь отслеживаются как один компонент. Проверьте файл рабочего пространства .bitmap
(в корневом каталоге рабочего пространства), чтобы убедиться, что компонент button был добавлен.

Свойство version
пусто, так как компонент еще не помечен версией выпуска.
Перейдите к пользовательскому интерфейсу рабочего пространства (работает на http://localhost:3ooo) для отображения вашего компонента.
Поскольку Bit Harmony находится в бета-версии, вы можете столкнуться с ошибками. Если это произойдет, перезапустите сервер разработки Bit (Ctrl+C и bit start
).

Давайте теперь рассмотрим сгенерированные файлы компонента, чтобы сделать из него настоящую кнопку.
Файл реализации: button.tsx
Сейчас компонент возвращает элемент div, а не кнопку. Давайте изменим это в файле button.tsx
. Мы также добавим к нему состояние loading
.
import React, { ButtonHTMLAttributes } from 'react';
export type ButtonProps = {
/* Determines whether a button is in 'loading' state */
isLoading?: boolean;
} & ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({
children,
isLoading,
disabled,
...rest
}: ButtonProps) {
return (
<button disabled={isLoading || disabled} {...rest}>
{isLoading ? 'Loading...' : children}
</button>
);
}
Button.defaultProps = {
disabled: false,
isLoading: false,
};
Предварительный просмотр изолированных компонентов: button.composition.tsx

Композиции — это своего рода мини-приложения, которые тестируют компонент в возможных контекстах. Они используются для предварительного просмотра компонента. С их помощью мы как авторы и специалисты по сопровождению получаем подтверждение того, что компонент успешно интегрируется в будущие приложения (до момента его совместного использования).
Композиции не отражают последние обновления. Изменим это, заменив текущую композицию двумя новыми — одной для кнопки в состоянии по умолчанию, а другой — в момент загрузки:
import React from 'react';
import { Button } from './button';
export const ButtonInDefaultState = () => <Button>Click Me!</Button>;
export const ButtonInLoadingState = () => <Button isLoading>Click Me!</Button>;

Документация по компонентам: button.docs.mdx
Это файл документации многомерных выражений, совместимый с Bit, который позволяет нам интегрировать JSX с Markdown и добавлять специальные Bit-свойства (метаданные). Файл doc загружается с помощью Bit и отображается на странице обзора компонента.
Файл doc уже импортирует сюда компонент button, чтобы сделать его доступным для рабочих версий документа. Все, что нам осталось, — это изменить документацию, чтобы лучше описать измененный компонент:
---
description: 'A basic button component.'
labels: ['react', 'input']
---
import { Button } from './button';
Это базовая кнопка с состоянием *'loading'*.
### Использование кнопки
```js
<Button>Submit</Button>
```
### Рабочая версия: Установка кнопки в состояние 'loading'
Добавьте или удалите `isLoading` для изменения ее состояния.
```js live
<Button isLoading>Submit</Button>
```

Тестирование компонентов: button.spec.tsx
Текущее тестирование пытается использовать ранее удаленные композиции.

Давайте обновим тестовый файл с помощью соответствующих тестов, используя новые композиции:
import React from 'react';
import { render } from '@testing-library/react';
import { ButtonInDefaultState, ButtonInLoadingState } from './button.composition';
describe('Button', () => {
it('should render with its default text', () => {
const { getByText } = render(<ButtonInDefaultState />);
const rendered = getByText('Click Me!');
expect(rendered).toBeTruthy();
});
it('should render in a loading state', () => {
const { getByText } = render(<ButtonInLoadingState />);
const rendered = getByText('Loading...');
expect(rendered).toBeDisabled();
});
});
Еще раз проверим состояние теста в пользовательском интерфейсе рабочего пространства:

Создайте компонент и пометьте его версией выпуска
Теперь, когда компонент button готов, давайте пометим его первой версией выпуска:
$ bit tag inputs/button 1.0.0 --message "first release version"
...
новые компоненты
(первая версия для компонентов)
> inputs/[email protected]
Команда bit tag
выполнит процесс сборки компонента, прежде чем пометит его новой версией выпуска. Процесс сборки определяется средой разработки (в нашем случае средой разработки React) и несколькими предварительно настроенными задачами сборки Bit. Сам процесс сборки полностью настраиваемый.
В процесс сборки компонентов входят следующие этапы:
- тестирование компонента в изолированном каталоге, удаленном из его рабочего пространства;
- компиляция исходного кода (в React env используется компилятор TypeScript);
- автоматическое создание распространяемого пакета Node при помощи Bit (вместе с автоматически генерируемым файлом
package.json
); - объединение предварительных просмотров (композиций) компонента и документации (в React env используется webpack);
- создание журнала сборки;
- и т. д.
Наш версионированный компонент будет содержать в себе все встроенные артефакты, а также настройки среды разработки и график зависимостей.
Версия выпуска хранится в виде Git-подобных объектов в каталоге .bit
/ .git/bit
(также известном под названием локальная область).
Экспорт: отправка и публикация компонентов
Теперь, когда готова неизменяемая версия выпуска компонента, мы можем экспортировать ее, чтобы поделиться с другими пользователями и сотрудниками.
$ bit export
exported the following 1 component(s):
our-org.my-scope/inputs/button
При экспорте версия выпуска компонента (объекты, подобные Git) отправляется на настроенный удаленный хостинг, а пакет компонента публикуется в реестре bit.dev (но, опять же, это можно изменить).

Импорт: клонирование компонента в рабочее пространство
Пакеты компонентов могут быть установлены в рабочих пространствах Bit с помощью Bit (например, bit install <package-name>
). Их также можно установить в не-Bit проектах с использованием стандартных клиентов NPM (таких как npm или Yarn). Подробнее об этом читайте в документации.
Но самое интересное в компонентах, совместно используемых с Bit, заключается в том, что над ними можно работать, “импортируя” (клонируя) их в рабочие пространства Bit.
Давайте импортируем компонент dots-loader из области удаленного обучения, принадлежащей teambit. Для этого перейдем на страницу компонента dots-loader, скопируем команду импорта и запустим ее.

$ bit import teambit.teaching/ui/elements/dots-loader
...
successfully imported one component
- added teambit.teaching/ui/elements/dots-loader new versions: 0.0.1, 0.0.2, 0.0.3, currently used version 0.0.3
Импортированный компонент теперь доступен в локальном рабочем пространстве. Его исходные файлы находятся в каталоге teaching
, а пакет — в каталоге node_modules
под именем владельца (teambit).

Изменение исходных файлов компонента приведет к повторной компиляции. Вы можете продолжать использовать пакет компонентов, ориентируясь на имя пакета, а не на исходные файлы, при разработке компонента. Это верно как для импортированных компонентов, так и для компонентов, созданных в одном рабочем пространстве.
Чтобы просмотреть настройки импортированных компонентов, выполните следующую команду:
$ bit show ui/elements/dots-loader

Обработка зависимостей компонентов

Теперь, когда у нас есть компонент dots-loader, давайте используем его для замены текста “Загрузка…” (“Loading…”) в компоненте button (отображается в состоянии загрузки). Мы сделаем это с помощью импорта компонента, используя его имя пакета.
import React, { ButtonHTMLAttributes } from 'react';
import { DotsLoader } from '@teambit/teaching.ui.elements.dots-loader';
export type ButtonProps = {
/* Determines whether a button is in 'loading' state */
isLoading?: boolean;
} & ButtonHTMLAttributes<HTMLButtonElement>;
export function Button({
children,
isLoading,
disabled,
...rest
}: ButtonProps) {
return (
<button disabled={isLoading || disabled} {...rest}>
{isLoading ? <DotsLoader/> : children}
</button>
);
}
Button.defaultProps = {
disabled: false,
isLoading: false,
};
Перейдите в пользовательский интерфейс рабочего пространства, чтобы просмотреть новые композиции компонентов:

Теперь у нас есть компонент, зависящий от другого компонента.

Как только компонент dots-loader будет изменен и помечен новой версией выпуска, Bit автоматически протестирует, соберет и пометит зависимый компонент button.
Обновления компонента dots-loader теперь будут инициировать процессы сборки и пометок в его зависимом компоненте button.
Если бы существовали какие-либо другие прямые или косвенные зависимые компоненты, процессы сборки и пометок также касались бы и их тоже.
При использовании Bit вместе с Bit.dev этот волновой эффект выходит за пределы локального рабочего пространства и распространяется на все экспортируемые компоненты во всех удаленных областях.

Заключение
Надеюсь, из этой статьи вы узнали общую суть работы с Bit. Это перспективный инструмент, который может радикально изменить способ сотрудничества при работе над веб-проектами.
Читайте также:
- 5 продвинутых шаблонов React на пальцах
- 6 актуальных советов по созданию чистого кода React
- Создаем приложение React с нуля в 2021 году
Читайте нас в Telegram, VK и Дзен
Перевод статьи Alicia Jones: How To Create a React Component Library