Мотивация
В этом документе собраны задачи, с которыми ежедневно сталкиваются веб-разработчики. Мы описали их решения в текущей реализации React и объяснили, почему эти способы неоптимальны. Библиотека bem-react-core предлагает альтернативные решения этих задач, более эффективные за счет использования уровней переопределения и возможности декларативно управлять модификацией блоков.
Декомпозиция
Веб-компонент должен решать множество задач. Из-за этого его функциональность усложняется и количество возможных вариаций увеличивается. Вариативность компонента чаще всего выражается с помощью произвольных условий в коде в императивном стиле. Большое количество конструкций с if
или switch
не позволяет быстро оценить все возможности компонента, изменить компонент или создать нужную комбинацию условий, которые выполняются в рантайме.
Кроме этого основная часть логики React-компонента заключена в методе render()
. Поэтому чтобы перенести или переопределить функциональность компонента, необходимо переписать большую часть метода.
Повторное использование кода
Количество используемых компонентов постоянно растет, при этом их функциональность усложняется. Между компонентами появляется общий код, который необходимо повторно использовать.
Текущая реализация React для переиспользования кода позволяет применять High Order Components или классическое наследование.
Наследование не позволяет комбинировать несколько ортогональных признаков без полного перепроектирования иерархии классов и обладает рядом общеизвестных проблем.
Кроссплатформенная разработка
Кроссплатформенность — одно из основных требований к веб-проекту. Чтобы поддерживать несколько платформ, чаще всего создают отдельную версию для каждой платформы или разрабатывают одну адаптивную версию.
Разработка отдельных версий требует дополнительных ресурсов: чем больше платформ необходимо поддерживать, тем больше усилий уходит на разработку. Даже если в проекте достаточно ресурсов для разработки нескольких версий, поддержка синхронного состояния продуктовых свойств в разных версиях вызовет дополнительные сложности. Также наличие разных версий обостряет проблемы повторного использования кода.
Разработка адаптивной версии усложняет код и увеличивает требования к квалификации разработчиков. Еще одна распространенная проблема адаптивных версий — снижение производительности сервиса, особенно на мобильных устройствах.
Эксперименты
Чтобы разрабатывать проекты для большой аудитории, необходимо быть уверенным в каждом изменении. A/B эксперименты — это один из способов получить такую уверенность.
Наиболее распространенные способы организации кода для проведения экспериментов:
ветвление всей кодовой базы и создание отдельных экземпляров сервиса;
точечные условия внутри одной кодовой базы.
Если в проекте много длительных экспериментов, ветвление кодовой базы может вызвать существенные дополнительные расходы. Каждую ветку с экспериментом необходимо поддерживать в актуальном состоянии и синхронизировать изменения (исправленные ошибки, продуктовую функциональность). Кроме этого, ветвление кодовой базы усложняет проведение пересекающихся экспериментов.
Точечные условия внутри одной кодовой базы работают гибче, но ведут к усложнению самой кодовой базы: условия эксперимента могут затрагивать разные части основного кода проекта. Большое количество условий затрудняет понимание кода для человека и ухудшает производительность за счет увеличения объема кода для браузера. После каждого эксперимента необходимо удалить лишний код: убрать условия и сделать код основным или полностью удалить неудачный эксперимент.
Взаимодействие общей библиотеки компонентов и разных проектов
Чтобы использовать в проекте библиотеку общих компонентов, необходимо взаимодействовать с ее исходным кодом. Например, изменять API, улучшать и оптимизировать код, добавлять функциональность.
Существует несколько способов внести изменения в компоненты из библиотеки для своего проекта:
форк библиотеки;
наследование;
патчинг кода библиотеки в рантайме.
При форке библиотеки необходимо следить за обновлениями в апстриме и накладывать патчи.
Наследование используется, если необходимо получить измененный компонент из библиотеки. Например, можно создать компонент MyButton
, унаследованный от Button
из библиотеки. Но унаследованный MyButton
не применится ко всем компонентам из библиотеки, содержащим кнопки. Например, в библиотеке может быть компонент Search
, построенный композицией из Input
и Button
. В этом случае внутри компонента Search
не появится унаследованный MyButton
.
Патчинг библиотечного кода в рантайме невозможен, если авторы библиотеки не предусмотрели возможность для внешних изменений.