Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.ru.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# bem-react · [![github (ci)](https://github.com/bem/bem-react/workflows/ci/badge.svg?branch=master)](https://github.com/bem/bem-react/workflows/ci/badge.svg?branch=master)

Набор инструментов для разработки пользовательских интерфейсов по [методологии БЭМ](https://ru.bem.info) на [React](https://github.com/facebook/react). BEM React поддерживает аннотации типов [TypeScript](https://www.typescriptlang.org/index.html).

## Пакеты

| Пакет | Версия | Размер |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`@bem-react/classname`](packages/classname) | [![npm (scoped)](https://img.shields.io/npm/v/@bem-react/classname.svg)](https://www.npmjs.com/package/@bem-react/classname) | [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/@bem-react/classname.svg)](https://bundlephobia.com/result?p=@bem-react/classname) |
| [`@bem-react/classnames`](packages/classnames) | [![npm (scoped)](https://img.shields.io/npm/v/@bem-react/classnames.svg)](https://www.npmjs.com/package/@bem-react/classnames) | [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/@bem-react/classnames.svg)](https://bundlephobia.com/result?p=@bem-react/classnames) |
| [`@bem-react/core`](packages/core) | [![npm (scoped)](https://img.shields.io/npm/v/@bem-react/core.svg)](https://www.npmjs.com/package/@bem-react/core) | [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/@bem-react/core.svg)](https://bundlephobia.com/result?p=@bem-react/core) |
| [`@bem-react/di`](packages/di) | [![npm (scoped)](https://img.shields.io/npm/v/@bem-react/di.svg)](https://www.npmjs.com/package/@bem-react/di) | [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/@bem-react/di.svg)](https://bundlephobia.com/result?p=@bem-react/di) |
| [`@bem-react/eslint-plugin`](packages/eslint-plugin) | [![npm (scoped)](https://img.shields.io/npm/v/@bem-react/eslint-plugin.svg)](https://www.npmjs.com/package/@bem-react/eslint-plugin) | — |

## Участие в разработке

Bem React Core — библиотека с открытым исходным кодом, которая активно развивается и используется в [Яндексе](https://yandex.com/company/).

Если у вас есть предложения по улучшению API, отправьте нам [Pull Request](https://github.com/bem/bem-react-core/pulls).

Если вы нашли ошибку, создайте [issue](https://github.com/bem/bem-react-core/issues) с описанием проблемы.

Подробное руководство по участию в разработке — в [CONTRIBUTING.md](CONTRIBUTING.md).

## Лицензия

© 2018 [Яндекс](https://yandex.com/company/). Код распространяется под лицензией [Mozilla Public License 2.0](LICENSE.md).
2 changes: 1 addition & 1 deletion website/docs/guides/lazy.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const withMod = withBemMod<BlockModProps>(
)
```

> **NOTE** If your need SSR replace React.lazy method for load `Block_mod.async.tsx` module to [@loadable/components](https://www.smooth-code.com/open-source/loadable-components/) or [react-loadable](https://github.com/jamiebuilds/react-loadable)
> **NOTE** If your need SSR replace React.lazy method for load `Block_mod.async.tsx` module to [@loadable/component](https://www.smooth-code.com/open-source/loadable-components/) or [react-loadable](https://github.com/jamiebuilds/react-loadable)

## Usage

Expand Down
61 changes: 61 additions & 0 deletions website/docs/introduction/motivation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
id: motivation
title: Why BEM if there's React?
---

This document covers tasks that web developers face every day. We describe how they are solved in [React](https://reactjs.org/) today and explain why those solutions are suboptimal. The bem-react-core library offers alternative solutions that are more efficient thanks to [redefinition levels](https://en.bem.info/methodology/redefinition-levels/) and the ability to declaratively manage [block modification](https://en.bem.info/methodology/block-modification/).

* [Decomposition](#decomposition)
* [Code reuse](#code-reuse)
* [Cross-platform development](#cross-platform-development)
* [Experiments](#experiments)
* [Sharing a component library across projects](#sharing-a-component-library-across-projects)

## Decomposition

A web component has to solve many tasks. As a result, its functionality grows more complex and the number of possible variations increases. This variability is usually expressed with arbitrary imperative conditions in code. A large number of `if` or `switch` statements makes it hard to quickly grasp everything the component can do, to modify it, or to produce the right combination of conditions evaluated at runtime.

Besides that, most of a React component's logic lives in the `render()` method. So moving or overriding a component's functionality means rewriting most of that method.

## Code reuse

The number of components keeps growing, and their functionality gets more complex. Components start to share common code that needs to be reused.

For code reuse, React today offers [Higher-Order Components](https://reactjs.org/docs/higher-order-components.html) or classic inheritance.

Inheritance doesn't let you combine several orthogonal features without completely redesigning the class hierarchy, and it comes with a number of [well-known](https://en.wikipedia.org/wiki/Composition_over_inheritance) problems.

## Cross-platform development

Cross-platform support is one of the core requirements for a web project. To support multiple platforms, teams usually either build a separate version for each platform or develop a single responsive version.

Building separate versions takes extra resources: the more platforms you need to support, the more effort development takes. Even if the project has enough resources to develop several versions, keeping product features in sync across them adds extra complexity. Having different versions also aggravates the code reuse problem.

A responsive version complicates the code and raises the skill requirements for developers. Another common problem with responsive versions is degraded performance, especially on mobile devices.

## Experiments

Developing a project for a large audience requires confidence in every change. A/B experiments are one way to gain that confidence.

The most common ways to organize code for experiments are:
* branching the entire codebase and running separate service instances
* targeted conditionals within a single codebase

If a project runs many long-lived experiments, branching the codebase can incur significant extra costs. Every experiment branch has to be kept up to date, with changes (bug fixes, product features) synchronized. Branching also makes overlapping experiments harder to run.

Targeted conditionals within a single codebase are more flexible but complicate the codebase itself: experiment conditions may touch different parts of the project's core code. A large number of conditions makes the code harder for humans to understand and hurts performance by increasing the amount of code shipped to the browser. After every experiment, the extra code has to be removed: either drop the conditions and make the code permanent, or delete the failed experiment entirely.

## Sharing a component library across projects

To use a shared component library in a project, you need to interact with its source code. For example, to change the API, improve and optimize the code, or add functionality.

There are several ways to modify library components for your project:
* forking the library
* inheritance
* patching the library code at runtime

With a fork, you have to track upstream updates and apply patches.

Inheritance is used when you need a modified version of a library component. For example, you can create a `MyButton` component that inherits from the library's `Button`. But the inherited `MyButton` won't be applied to all library components that contain buttons. For example, the library may have a `Search` component built as a composition of `Input` and `Button`. In that case, the inherited `MyButton` won't appear inside the `Search` component.

Patching library code at runtime is impossible unless the library authors have provided a way to make external changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
id: lazy
title: Ленивые модификаторы
---

Более эффективное разделение кода с помощью React.lazy и динамических импортов.

## Структура

```
src/components/Block/
├── Block.tsx
├── _mod
│   └── Button_mod.async.tsx
│   └── Button_mod.css
│   └── Button_mod.tsx
```

## Объявление

**Шаг 1.** Объявите часть кода, которая должна загружаться динамически.

```tsx
// Block/_mod/Block_mod.async.tsx
import React from 'react'
import { cnBlock } from '../Block'

import './Block_mod.css'

export const DynamicPart: React.FC = () => <i className={cnBlock('Inner')}>Loaded dynamicly</i>

// default export нужен для React.lazy
export default DynamicPart
```

**Шаг 2.** Объявите модификатор.

```tsx
// Block/_mod/Block_mod.tsx
import React, { Suspense, lazy } from 'react'
import { cnBlock } from '../Block'

export interface BlockModProps {
mod?: boolean
}

export const withMod = withBemMod<BlockModProps>(
cnBlock(),
{
mod: true,
},
(Block) => (props) => {
const DynamicPart = lazy(() => import('./Block_mod.async.tsx'))

return (
<Suspense fallback={<div>Updating...</div>}>
<Block {...props}>
<DynamicPart />
</Block>
</Suspense>
)
},
)
```

> **Примечание.** Если вам нужен SSR, для загрузки модуля `Block_mod.async.tsx` используйте вместо React.lazy [@loadable/component](https://www.smooth-code.com/open-source/loadable-components/) или [react-loadable](https://github.com/jamiebuilds/react-loadable).

## Использование

```ts
// App.tsx
import {
Block as BlockPresenter,
withMod
} from './components/Block/desktop';

const Block = withMod(BlockPresenter);

export const App = () => {
return (
{/* чанк с DynamicPart не загружен */}
<Block />

{/* чанк с DynamicPart загружен */}
<Block mod />
)
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
id: structure
title: Структура компонента
---

Обычно базовая структура компонента выглядит примерно так:

```
src/components/Button/
├── Button.css
├── Button.tsx
├── Button@desktop.css
├── Button@desktop.tsx
├── desktop.ts
├── _view
│ ├── Button_view_default.css
│   └── Button_view_default.tsx
└── hooks
└── useCheckedState.ts
```

## Платформы

Если у компонента есть особенности, специфичные для платформы, — например, состояние при наведении, — создайте реализацию для этой платформы и реэкспортируйте всё из общей реализации.

Общая реализация:

```tsx
// src/components/Button/Button.tsx
import React from 'react'
import './Button.css'

export const Button = ({ children }) => <button className="Button">{children}</button>
```

Реализация для десктопа:

```tsx
// src/components/Button/Button@desktop.tsx
export * from './Button'
import './Button@desktop.css'
```

## Публичный API

Публичный API позволяет контролировать, что должно быть доступно разработчику, а также скрывает от потребителя, что для конкретной платформы часть кода может отсутствовать.

> Очень важно, чтобы в проекте был настроен [tree shaking](https://webpack.js.org/guides/tree-shaking/), иначе в итоговый бандл может попасть неиспользуемый код.

Пример:

```ts
// src/components/Button/desktop.ts
export * from './Button@desktop'
export * from './_view/Button_view_default'
export * from './hooks/useCheckedState'
```

Использование:

```ts
// src/components/Feature/Feature.tsx
import {
Button as ButtonDesktop,
withViewDefault,
useCheckedState,
} from 'components/Button/desktop'
```
Loading
Loading