Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BEM-XJST и React: ответы на вопросы #1212

Open
awinogradov opened this issue Jan 11, 2017 · 2 comments
Open

BEM-XJST и React: ответы на вопросы #1212

awinogradov opened this issue Jan 11, 2017 · 2 comments

Comments

@awinogradov
Copy link

Историческая справка

До недавнего времени мы работали над возможностью генерации Virtual DOM на основе BEMHTML-шаблонов. Мы разработали специальный движок для bem-xjst – xjst-ddsl, который может превращать шаблоны в некий DDSL, который с помощью дополнительного хелпера – ddsl-react превращался в React. Это было сделано для того, чтобы отделить специфику React и сделать закладку на тот случай, если захочется другую реализацию Virtual DOM. Об этом рассказывал я, например, на Я.Субботнике. Мы выдвинули и даже математически доказали гипотезу об эффективности этого метода, поскольку он позволял в 10 раз ускориться на сервере относительно нативного рендеринга на React, ведь на сервере мы можем использовать стандартный рендер в HTML, так как шаблоны одни.

Спустя некоторое время

Все это время мы делали подход в опенсоре – react-bl и внутри на реализацию компонентов для React на BEMHTML-шаблонах. Внутри мы попробовали очень большой объем компонентов, в том числе и составные/сложные с попапами и модальными окнами.

В чем разница открытой версии и внутренней?

В react-bl мы пробовали писать React-код для клиента "сверху" верстки и шаблонов, чтобы не трогать сами шаблоны и пробрасывать биндинги через пропсы компонентов. То есть получался "настоящий" React-компонент, где только разметка генерировалась с помощью BEMHTML. Такой подход нёс в себе несколько проблем. Во-первых, не работали уровни переопределения для клиентского кода, только для верстки в шаблонах. Во-вторых, добавлял иногда большие трудности при пробрасывании пропсов во вложенные элементы, тем более, если они строились динамически и имели вариативность.

Основываясь на этом опыте внутри мы попробовали писать клиентский код прямо в BEMHTML шаблонах. Например так:

block('link')(
   tag()('a'),
   js()(function() {
      return {
        onClick: () => console.log('hi there')
      };
   })
);

Это позволило решить проблемы с уровнями переопределения. И декларацией клиентского кода для вложенных элементов. Но... Нам пришлось добавить немного магии в ddsl-react (тот самый хелпер, что превращает DDSL в React). Мы добавили автоматический биндинг контекста. Ведь клиентский код в шаблонах ничего не знал про инстанс React-компонента и мы сделали это автоматически.

Кроме того, скорее всего вы спросите...

Как же было дело со вложенными компонентами?

Вопрос о том, что если кнопка использует иконку, а иконка тоже компонент? Ведь вызов иконки описан в BEMHTML и это просто BEMJSON, а не какой ни React-component. Мы решили эту проблему полуавтоматически: во время обьявления класса мы используем хелпер (ddsl-react), который первым аргументом принимает обьект с матчингом блоков на React-компоненты. Внутри во время рендера, когда встречается блок из матчинга он подменяется на React-компонент. Ведь разработчик знает, что за блоки используется в составном среди BEMHTML.

Что стало?

К сожалению, мы не учли несколько нюансов, и как мы ни пытались их обойти, сделать это эффективно у нас не получилось. А именно:

  • проблема автоматического биндинга контекста: это очень большая магия и возникают неоднозначности при обращении к элементам из блока или к другим элементам из элемента
  • проблема проставления refs, в 99% случаев все хорошо, но только не когда нужен ref на вложенный элемент в блоке, который вложен в другой блок
  • слишком строгие правила для написания шаблонов чтобы серверный рендер работал действительно быстро. То есть клиентский код должен быть вынесен в другой уровень относительно верстки и это надо учитывать в сборке. В противном случае среди рендера BEMHTML функции React по прежнему выполнялись, что сильно замедляло процесс

Всё прям плохо?

На самом деле нет. В 99% случаев решение работает и работает хорошо. Просто ожидаемый профит показался недостаточным. Нам хотелось быстрый рендер на сервере и удобное написание React-компонентов. Опытным и трезвым умом нам показалось что мы этого не достигли. Тем не менее использовать BEMHTML-шаблоны в React можно, но базировать на этом библиотеку с компонентами не стоит. Если у вас есть желание работать с этим решением, я с радостью помогу разобраться и раздам прав. Мейнетейнеры нынче в цене.

Что дальше?

Мы проверяем новую гипотезу в bem-react-core. Это очень маленькая по обьему библиотека, которая позволяет декларировать React-компоненты в BEMHTML подобном синтаксисе, писать на последнем стандарте, собираться чем угодно(webpack, gulp, rollap, babel), поддерживает уровни переопределения и все все, что мы так любим. В репозитории есть набор примеров, которые можно посмотреть и понять. Мы работаем над новым набором компонентов в bem-react-components. Внутри мы так же попробовали очень много компонентов и кажется все очень хорошо. У нас уже почти готова документация и скоро она появится в репозитории. И так же скоро появится поддержка i18n ;) Приходите с PR и хейтерством. Все что мы запланировали пока, кажется отражено в issues.

Код для привлечения внимания

@JiLiZART
Copy link

Выглядит все вкусно, даже очень.

        this._onClick = this._onClick.bind(this);
        this._onFocus = this._onFocus.bind(this);
        this._onBlur = this._onBlur.bind(this);
        this._onMouseEnter = this._onMouseEnter.bind(this);
        this._onMouseLeave = this._onMouseLeave.bind(this);

но вот такие штуки заставляют чесаться глаз

@megatolya
Copy link

@JiLiZART Кажется тут BEM-XJST не причем, это особенность js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants