diff --git a/README.ru.md b/README.ru.md new file mode 100644 index 00000000..f4579072 --- /dev/null +++ b/README.ru.md @@ -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). diff --git a/website/docs/guides/lazy.md b/website/docs/guides/lazy.md index 62d7911f..44feee49 100644 --- a/website/docs/guides/lazy.md +++ b/website/docs/guides/lazy.md @@ -63,7 +63,7 @@ export const withMod = withBemMod( ) ``` -> **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 diff --git a/website/docs/introduction/motivation.md b/website/docs/introduction/motivation.md new file mode 100644 index 00000000..c7c3418a --- /dev/null +++ b/website/docs/introduction/motivation.md @@ -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. diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/guides/lazy.md b/website/i18n/ru/docusaurus-plugin-content-docs/current/guides/lazy.md new file mode 100644 index 00000000..d16705eb --- /dev/null +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/guides/lazy.md @@ -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 = () => Loaded dynamicly + +// 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( + cnBlock(), + { + mod: true, + }, + (Block) => (props) => { + const DynamicPart = lazy(() => import('./Block_mod.async.tsx')) + + return ( + Updating...}> + + + + + ) + }, +) +``` + +> **Примечание.** Если вам нужен 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 не загружен */} + + + {/* чанк с DynamicPart загружен */} + + ) +} +``` diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/guides/structure.md b/website/i18n/ru/docusaurus-plugin-content-docs/current/guides/structure.md new file mode 100644 index 00000000..d336f54c --- /dev/null +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/guides/structure.md @@ -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 }) => +``` + +Реализация для десктопа: + +```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' +``` diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/introduction/motivation.md b/website/i18n/ru/docusaurus-plugin-content-docs/current/introduction/motivation.md new file mode 100644 index 00000000..289ab65b --- /dev/null +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/introduction/motivation.md @@ -0,0 +1,61 @@ +--- +id: motivation +title: Зачем БЭМ, если есть React? +--- + +В этом документе собраны задачи, с которыми ежедневно сталкиваются веб-разработчики. Мы описали их решения в текущей реализации [React](https://reactjs.org/) и объяснили, почему эти способы неоптимальны. Библиотека bem-react-core предлагает альтернативные решения этих задач, более эффективные за счет использования [уровней переопределения](https://ru.bem.info/methodology/redefinition-levels/) и возможности декларативно управлять [модификацией блоков](https://ru.bem.info/methodology/block-modification/). + +* [Декомпозиция](#декомпозиция) +* [Повторное использование кода](#повторное-использование-кода) +* [Кроссплатформенная разработка](#кроссплатформенная-разработка) +* [Эксперименты](#эксперименты) +* [Взаимодействие общей библиотеки компонентов и разных проектов](#взаимодействие-общей-библиотеки-компонентов-и-разных-проектов) + +## Декомпозиция + +Веб-компонент должен решать множество задач. Из-за этого его функциональность усложняется и количество возможных вариаций увеличивается. Вариативность компонента чаще всего выражается с помощью произвольных условий в коде в императивном стиле. Большое количество конструкций с `if` или `switch` не позволяет быстро оценить все возможности компонента, изменить компонент или создать нужную комбинацию условий, которые выполняются в рантайме. + +Кроме этого основная часть логики React-компонента заключена в методе `render()`. Поэтому чтобы перенести или переопределить функциональность компонента, необходимо переписать большую часть метода. + +## Повторное использование кода + +Количество используемых компонентов постоянно растет, при этом их функциональность усложняется. Между компонентами появляется общий код, который необходимо повторно использовать. + +Текущая реализация React для переиспользования кода позволяет применять [High Order Components](https://reactjs.org/docs/higher-order-components.html) или классическое наследование. + +Наследование не позволяет комбинировать несколько ортогональных признаков без полного перепроектирования иерархии классов и обладает рядом [общеизвестных](https://en.wikipedia.org/wiki/Composition_over_inheritance) проблем. + +## Кроссплатформенная разработка + +Кроссплатформенность — одно из основных требований к веб-проекту. Чтобы поддерживать несколько платформ, чаще всего создают отдельную версию для каждой платформы или разрабатывают одну адаптивную версию. + +Разработка отдельных версий требует дополнительных ресурсов: чем больше платформ необходимо поддерживать, тем больше усилий уходит на разработку. Даже если в проекте достаточно ресурсов для разработки нескольких версий, поддержка синхронного состояния продуктовых свойств в разных версиях вызовет дополнительные сложности. Также наличие разных версий обостряет проблемы повторного использования кода. + +Разработка адаптивной версии усложняет код и увеличивает требования к квалификации разработчиков. Еще одна распространенная проблема адаптивных версий — снижение производительности сервиса, особенно на мобильных устройствах. + +## Эксперименты + +Чтобы разрабатывать проекты для большой аудитории, необходимо быть уверенным в каждом изменении. A/B эксперименты — это один из способов получить такую уверенность. + +Наиболее распространенные способы организации кода для проведения экспериментов: +* ветвление всей кодовой базы и создание отдельных экземпляров сервиса; +* точечные условия внутри одной кодовой базы. + +Если в проекте много длительных экспериментов, ветвление кодовой базы может вызвать существенные дополнительные расходы. Каждую ветку с экспериментом необходимо поддерживать в актуальном состоянии и синхронизировать изменения (исправленные ошибки, продуктовую функциональность). Кроме этого, ветвление кодовой базы усложняет проведение пересекающихся экспериментов. + +Точечные условия внутри одной кодовой базы работают гибче, но ведут к усложнению самой кодовой базы: условия эксперимента могут затрагивать разные части основного кода проекта. Большое количество условий затрудняет понимание кода для человека и ухудшает производительность за счет увеличения объема кода для браузера. После каждого эксперимента необходимо удалить лишний код: убрать условия и сделать код основным или полностью удалить неудачный эксперимент. + +## Взаимодействие общей библиотеки компонентов и разных проектов + +Чтобы использовать в проекте библиотеку общих компонентов, необходимо взаимодействовать с ее исходным кодом. Например, изменять API, улучшать и оптимизировать код, добавлять функциональность. + +Существует несколько способов внести изменения в компоненты из библиотеки для своего проекта: +* форк библиотеки; +* наследование; +* патчинг кода библиотеки в рантайме. + +При форке библиотеки необходимо следить за обновлениями в апстриме и накладывать патчи. + +Наследование используется, если необходимо получить измененный компонент из библиотеки. Например, можно создать компонент `MyButton`, унаследованный от `Button` из библиотеки. Но унаследованный `MyButton` не применится ко всем компонентам из библиотеки, содержащим кнопки. Например, в библиотеке может быть компонент `Search`, построенный композицией из `Input` и `Button`. В этом случае внутри компонента `Search` не появится унаследованный `MyButton`. + +Патчинг библиотечного кода в рантайме невозможен, если авторы библиотеки не предусмотрели возможность для внешних изменений. diff --git a/website/i18n/ru/docusaurus-plugin-content-docs/current/introduction/quick-start.md b/website/i18n/ru/docusaurus-plugin-content-docs/current/introduction/quick-start.md new file mode 100644 index 00000000..4ccbf215 --- /dev/null +++ b/website/i18n/ru/docusaurus-plugin-content-docs/current/introduction/quick-start.md @@ -0,0 +1,10 @@ +--- +id: installation +title: Быстрый старт +--- + +Для начала работы установите все необходимые пакеты: + +```sh +npm i -P @bem-react/core @bem-react/di @bem-react/classname +``` diff --git a/website/sidebars.js b/website/sidebars.js index 45d00aa0..06adad0f 100755 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -1,6 +1,6 @@ module.exports = { docs: { - Introduction: ['introduction/installation'], + Introduction: ['introduction/motivation', 'introduction/installation'], Guides: ['guides/naming', 'guides/structure', 'guides/lazy', 'guides/experiments'], }, api: {