— Сначала вы его отрицаете, потом вы его ненавидите, а потом вы не можете без него жить.
из доклада Артема Курбатова «БЭМ: мастер-класс»


Методология БЭМ существует достаточно долго и принята на вооружение в Google, EPAM Systems, BBC, Альфа-Банке. При этом она все еще вызывает беспокойство у типичного разработчика и менеджера проектов среднего звена.




У некоторых смельчаков изучение БЭМ не ушло дальше ограничения возможностей CSS для получения более предсказуемых результатов. И хотя БЭМ давно вышел за пределы верстки, до сих пор на вопрос «Знаете ли вы БЭМ?» можно услышать: «Конечно, это про подчеркивания в классах».


Если ваше представление о БЭМ близко к этому, я отвечу вам словами работодателя при приеме на работу новоиспеченного выпускника: «Забудьте о том, что вы слышали о БЭМ ранее». Методология БЭМ настолько интересна, насколько большинству о ней ничего не известно. Чтобы понять всю прелесть БЭМ, необходимо иметь представление обо всех технологиях, библиотеках, фреймворках и инструментах, которые БЭМ предоставляет. Изучите их, оставайтесь инопланетянином, ребенком, который удивляется тому, с чем взрослые смирились.


Оглавление



Что для меня БЭМ?


Наверняка вы слышали истории, как дерзкие вундеркинды, закрывшись в гараже, изобрели очередной стартап. Первые успехи позволили им привлечь инвестиции и выйти на рынок с потрясающим продуктом. Как это им удалось, как удалось выдержать пиковые нагрузки и не погрязнуть в вечном рефакторинге? Как надо, чтобы не как обычно?


БЭМ — это лайфхак для стартаперов. По сравнению со многими другими фреймворками и технологиями начало работы с БЭМ требует определенных усилий, но это окупается архитектурой, обеспечивающей устойчивую расширяемость. Во все технологии, фреймворки и библиотеки БЭМ изначально заложены принципы декларативного подхода. И именно это делает их такими непонятными на первый взгляд и любимыми в итоге.


Прочитав данную статью вы не постигните дзен БЭМ, но точно сможете ответить взрослым, что научились создавать полноценные динамические проекты по БЭМ, используя не только CSS. И если завтра ваше приложение постигнет участь в +10 К пользователей в сутки, будьте спокойны — у вас останется времени отметить это событие.


Сегодня мы расскажем о новом шаблонном репозитории для динамических проектов bem-express, который не только позволяет развернуть БЭМ-проект в один клик, но и делает автоматическую пересборку проекта и перезагрузку браузера. На его основе разработаем динамическое приложение и опишем процесс взаимодействия различных БЭМ-технологий. Мы сознательно не будем рассматривать вопросы верстки и клиентского JavaScript, чтобы избежать участи «подчеркивания в классах».


Что мы будем разрабатывать?


Поисковое приложение Social Services Search Robot (сокр. SSSR), которое по запросу покажет последние твиты и видео с YouTube.


Будем использовать:



Для начала потребуется установить:



Важно! Пользователям операционной системы Windows необходимо дополнительно установить Git Bash.

Используемые обозначения


Чтобы статья получилась ярче, мы немного порисуем:


  • folder — директория;
  • file — файл;
  • add folder — создать директорию;
  • add file — создать файл;
  • edit file — отредактировать файл.

Используемые технологии


В БЭМ нет разделения технологий на главные и второстепенные. Есть набор, а выбор применения определяется индивидуально:


  • BEMDECL — технология для описания деклараций в БЭМ.
  • DEPS — технология для описания зависимостей в БЭМ.
  • BEMTREE — шаблонизатор преобразующий данные в BEMJSON.
  • BEMHTML — шаблонизатор преобразующий BEMJSON в HTML.
  • i-bem.js — JavaScript-фреймворк для БЭМ.

Подробнее о BEMJSON-формате входных данных.

Давайте рассмотрим их подробнее.


BEMDECL


Определяет список БЭМ-сущностей для страницы.


Такой список в БЭМ называется декларацией. Задача декларации — определить, что и в каком порядке подключать в сборку.


Декларации описываются в файлах с расширением .bemdecl.js.


Пример


exports.blocks = [
    { name: 'page' },
    { name: 'header' },
    { name: 'body' },
    { name: 'footer' }
];  

Когда количество блоков переходит рубеж «легко запомнить», возникает проблема их перечисления в нужном порядке. Поэтому обычно декларируют какой-то определенный блок, который следует рассматривать как центральную «точку входа».


Все остальные БЭМ-сущности попадают в сборку по зависимостям.


DEPS


Определяет зависимости между БЭМ-сущностями, которые разнесены по файловой структуре проекта и не отражены в декларации.


Зависимости описываются в виде JavaScript-объекта в файлах с расширением .deps.js.


Пример


/* page зависит от header */
({
    block: 'page',
    shouldDeps: [
        { block: 'header' }
    ]
})

Название технологии происходит от английского слова dependence и обозначает желание подключить в сборку какую-то БЭМ-сущность. Изучающие БЭМ порой забывают о декларативности технологий и недоумевают: почему для описанного в шаблоне блока, не собираются его стили и скрипты.


Помните: когда вы описываете шаблон (BEMHTML или BEMTREE) с каким-то блоком внутри (дочерний узел), вы просто добавляете новый HTML-элемент. Чтобы стили и скрипты этого блока попали в сборку, необходимо описать зависимость от него.


Например, для того чтобы добавить в сборку блоки header, body и footer, необходимо описать зависимость от них:


/* page зависит от header, body, footer */
({
    block: 'page',
    shouldDeps: [
      'header',
      'body',
      'footer'
    ]
})

Нижеследующая схема показывает логику сборки по зависимостям:


index(DECL)               # Декларация блока page
|
L--> page(DEPS)           # Зависимость блока page от header, body, footer
    |
    +--> header(DEPS)
    |    |
    |    L--> ...
    |
    +--> body(DEPS)
    |    |
    |    L--> ...
    |
    L--> footer(DEPS)
         |
         L--> ...

BEMTREE


Является частью шаблонизатора bem-xjst и преобразует данные в BEMJSON.


Шаблоны описываются в BEMJSON-формате в файлах с расширением .bemtree.js.


Вход и выход шаблонизатора:


BEMTREE


BEMHTML


Является частью шаблонизатора bem-xjst и преобразует BEMJSON-описание страницы в HTML.


Шаблоны описываются в файлах с расширением .bemhtml.js.


Вход и выход шаблонизатора:


BEMHTML


i-bem.js


Клиентский JavaScript-фреймворк для веб-разработки в рамках БЭМ-методологии.


JavaScript-код описывается в файлах с расширением .js.


Позволяет:


  • разрабатывать веб-интерфейс в терминах блоков, элементов, модификаторов;
  • описывать логику работы блока в декларативном стиле — как набор состояний;
  • легко интегрировать код JavaScript с BEMHTML-шаблонами и CSS;
  • гибко переопределять поведение библиотечных блоков.

Приложение Hello, World


У программистов есть традиция: начинать программирование на новом языке или фреймворке с приложения Hello, World. Приложение обычно выводит слова «Hello, World» в выходной поток, демонстрируя тем самым, что оно запускается и выполняет операции ввода/вывода.


Давайте создадим его, а затем расширим до желаемого SSSR.


Нам потребуется локальная копия шаблонного репозитория bem-express. Ее можно сделать с помощью Git.


Примечание. Для пользователей OS X или Linux все команды выполняются в терминале. Пользователям Windows потребуется Git Bash. Убедитесь, что Git Bash запущен от имени администратора.

Шаблонный репозиторий


При решении задач по разработке динамических приложений в рамках БЭМ создан шаблонный репозиторий bem-express. Он содержит необходимый минимум конфигурационных файлов и решает целый класс задач, таких как сборка проекта, настройка линтеров, подключение библиотек и др.


В него по умолчанию подключены основные БЭМ-библиотеки:



Быстрый старт


Чтобы создать приложение Hello, World:


  1. Склонируйте bem-express:


    git clone https://github.com/bem/bem-express.git sssr-project

    Примечание. В данном примере используется bem-express версии 2.00.

  2. Перейдите в директорию проекта:


    cd sssr-project

  3. Удалите историю версионирования исходного репозитория:


    rm -rf .git

  4. Инициализируйте собственный Git-репозиторий:


    git init

  5. Установите зависимости:


    npm install

    Примечание. Не используйте права суперпользователя root при установке npm-зависимостей.

  6. Соберите проект и запустите сервер:


    npm run dev

    Примечание. За сборку отвечает ENB.

    При запуске приложения в терминале выводится сообщение о том, что сервер выполняется на порте 3000:


    Server is listening on 3000.


    Примечание. Если порт 3000 используется другой программой, его можно переназначить. Например, на 8000:

    Способ 1. Изменение значения при запуске приложения.
    PORT=8000 npm run dev


    Способ 2. Изменение значения по умолчанию в файле server/config.js.
    defaultPort: 8000,

    На компьютере запустился:


    • сервер — отвечает за обработку динамических данных;
    • nodemon — следит за изменениями в файловой структуре и перезапускает сервер;
    • chokidar — следит за изменениями в файлах директорий *.blocks/ и перестраивает структуру проекта;
    • livereload — обновляет страницу в браузере.

  7. Откройте браузер и введите адрес localhost:3000.


    Должна открыться страница со следующим контентом:


    Index page content
    footer content

    Примечание. Если при запуске приложения в Windows, выводится уведомление от Брандмауэра:
    1. Отключите опцию Общественные сети (Public Network).
    2. Установите опцию Частные сети (Private Network).
    3. Разрешите доступ.


  8. Откройте файл server/index.js и внесите следующие изменения (см. комментарии) в код начинающегося строкой app.get('/', function(req, res):


    /**
     * Функция обрабатывает все GET-запросы с главной страницы приложения
     * @function
     * @param {object} req - Запрос.
     * @param {object} res - Ответ.
     */
    app.get('/', function(req, res) {
        var hello = 'Hello';                  // Инициализируем переменную `hello`
        var world = 'World';                  // Инициализируем переменную `world`
        render(req, res, {
            view: 'page-index',
            title: 'Main page',
            meta: {
                description: 'Page description',
                og: {
                    url: 'https://site.com',
                    siteName: 'Site name'
                }
            },
            hello: hello,                     // Передаем переменную `hello` в `this.data.hello`
            world: world                      // Передаем переменную `world` в `this.data.world`
        })
    });

  9. Откройте файл common.blocks/page-index/page-index.bemtree.js и замените его содержимое на следующее:


    block('page-index').content()(function() {
        // Получаем данные из глобального объекта `this`
        var data = this.data;
        // Возвращаем полученные данные: `data.hello: 'Hello'`, `data.world: 'World'`
        return data.hello + ', ' + data.world;
    });

    После сохранения сервер автоматически перезапустится и контент страницы изменится на:


    Hello, World
    footer content


Приложение Hello, World готово.


Не получилось?


Если при создании приложения возникли сложности, поищите решение на форуме. Если готового ответа не нашлось, задайте вопрос.


Приложение Social Services Search Robot


Собственно мы дошли до шага разработки приложения SSSR. Напомню, что по запросу приложение будет выводить последние твиты и видео с YouTube.


Сразу забежим в недалекое будущее — выглядеть приложение будет так:



Схема работы приложения


Вы ее видели во вводной части статьи.

Chart of Social Services Search Robot


Шаг 1. Запрос


Пользователь отправляет запрос на сервер.


Шаг 2. Получение данных


Приложение обращается за данными к Twitter Search API и YouTube Data API в соответствии с полученным запросом.


Шаг 3. BEMTREE-шаблонизация


Приложение передает полученные данные BEMTREE-шаблонизатору, который преобразует данные в BEMJSON.


Шаг 4. BEMHTML-шаблонизация


Приложение передает BEMJSON BEMHTML-шаблонизатору, который преобразует BEMJSON в HTML.


Шаг 5. Отправка результата пользователю


Приложение возвращает результат (HTML-страницу) пользователю.


Используемые модули Node


Базовая реализация Node остается настолько простой, насколько это возможно. Вместо того, чтобы встраивать все возможные компоненты прямо в Node, разработчики предоставляют дополнительную функциональность в виде отдельных модулей (пакетов).


Система модулей Node построена по образцу системы CommonJS, механизма создания взаимодействующих модулей. Центральное место в системе занимает контракт, который должен выполняться разработчиками, чтобы их модули нормально взаимодействовали с другими.


Все пакеты установленные с помощью менеджера пакетов npm находятся в директории node_modules.


Подключение модулей происходит при помощи команды require. Если пакет установлен с использованием npm, указывать путь не нужно. Достаточно указать имя:


var express = require('express');

При подключении собственного локального модуля, необходимо указать к нему путь:


var someModule = require('./somefolder/somemodule');

Важной особенностью любого модуля является то, что он должен быть рассчитан на взаимодействие с Node. Для этого модуль нужно экспортировать с помощью module.exports:


module.exports = {
    // some module
};

Для работы приложения потребуются следующие модули:


  • express — предоставляет большую часть функциональности, необходимой для построения веб-приложения;
  • passport — предоставляет различные стратегии аутентификации для приложений Node.js;
  • passport-youtube-v3 — предоставляет механизм аутентификации на Youtube посредством аккаунта Youtube и токенов OAuth 2.0;
  • twitter — клиентская библиотека для работы с Twitter REST API;
  • googleapis — клиентская библиотека для работы с Google REST API;
  • moment — JavaScript библиотека для синтаксического анализа, валидации и форматирования дат.

Установить их можно одной командой:


npm install express passport passport-youtube-v3 twitter googleapis moment --save

Подготовка структуры проекта


Прежде чем начать писать код, немного изменим структуру взятого за основу приложения Hello, World.


Изменения для:



Изменения для статических файлов


static


Директория static


  • Создайте поддиректорию images.


  • Перенесите фавиконку в поддиректорию images.

Директория common.blocks


  • Отредактируйте файл root/root.bemtree.js.


    Измените:


    favicon: '/favicon.ico',

    На:


    favicon: '/images/favicon.ico',

    Полный код root.bemtree.js.



Директория server


  • Отредактируйте файл index.js.


    Измените:


    .use(favicon(path.join(staticFolder, 'favicon.ico')))

    На:


    .use(favicon(path.join(staticFolder, '/images/favicon.ico')))

    Полный код index.js.



Изменения для серверного кода


server-changes


Директория server


  • Создайте поддиректории:


    • controllers — контроллеры;
    • helpers — хелперы;
    • middleware — модули промежуточного звена.

  • Создайте пустые JS-файлы для будущих модулей:


    • app.js — модуль монтирования промежуточных модулей (делает их доступными в приложении);
    • auth.js — модуль аутентификации на YouTube;
    • routes.js — модуль маршрутизации веб-запросов.

  • Добавьте следующий код в файл app.js.


  • Добавьте следующий код в файл routes.js.


  • Измените расширение файла config:


    config.js —> config.json


  • Отредактируйте файл config.json.


    Измените:


    module.exports = {
      staticFolder: 'static',
      defaultPort: 3000,
      cacheTTL: 30000,
      sessionSecret: 'REPLACE_ME_WITH_RANDOM_STRING'
    };

    На:


    {
    "staticFolder": "static",
    "defaultPort": 3000,
    "cacheTTL": 30000,
    "sessionSecret": "REPLACE_ME_WITH_RANDOM_STRING"
    }

  • Измените весь текущий контент файла index.js на следующий.


    Примечание. В index.js остается только функциональность, отвечающая за запуск приложения и прослушивание запросов на порте.


Директория controllers


  • Создайте пустой JS-файл:


    • index.js — контроллер обработки запросов и рендеринга HTML.

  • Добавьте следующий код в файл index.js.

Директория helpers


  • Создайте пустые JS-файлы:


    • index.js — входная точка для хелперов;
    • twitter.js — модуль-хелпер для работы с Twitter Search API;
    • youtube.js — модуль-хелпер для работы с YouTube Data API.


Директория middleware


  • Создайте пустой JS-файл:


    • auth.js — модуль проверки прохождения аутентификации на YouTube.


Получение OAuth-токенов


Сервисы Twitter и Google хранят различные данные пользователей — твиты, видео на Youtube, письма в Почте, фотографии и так далее. Чтобы обеспечить удобный доступ к этим данным из других приложений или сторонних сервисов, они используют открытый протокол авторизации OAuth 2.0.


Согласно протоколу, разработчик регистрирует приложение на OAuth-сервере и запрашивает доступ к определенным данным. Авторизованный пользователь разрешает или запрещает его.


Получение OAuth-токена для Twitter


Twitter предлагает приложениям возможность выдавать аутентифицированные запросы от имени самого приложения.


С чего начать?


  1. Изучите документацию.
  2. Зарегистрируйте приложение и получите ключи (Consumer Key, Consumer Secret).
  3. Установите Postman любым удобным для вас способом.
  4. Закодируйте строку Consumer Key:Consumer Secret методом Base64.
  5. Получите OAuth-токен в обмен на код.
  6. Используйте полученные токен и ключи в запросах к Twitter Search API.


    Примечание. Postman поможет получить OAuth-токен с помощью POST-запроса в обмен на код, полученный методом Base64.


Как закодировать строку?


Чтобы закодировать строку методом Base64:


  1. Сформируйте строку вида: Consumer Key:Consumer Secret.


    Пример


    xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg


    Примечание. Получить ключи Consumer Key и Consumer Secret можно, перейдя на вкладку Keys and Access Tokens вашего приложения.

  2. Запустите терминал или Git Bash.
  3. Выполните команду echo -n "xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg" | base64.
  4. Скопируйте полученный код.


    Пример


    eHZ6MWV2RlM0d0VFUFRHRUZQSEdFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==



Примечание. Если возникли сложности, воспользуйтесь онлайн-ресурсом base64encode.org.

Как получить OAuth-токен в обмен на код?


Чтобы получить токен в обмен на код:


  1. Запустите Postman.


    Примечание. По умолчанию открывается вкладка, в которой необходимо сформировать POST-запрос к OAuth-серверу Twitter.

  2. Выберите тип запроса POST.
  3. Введите адрес сервера https://api.twitter.com/oauth2/token.
  4. Перейдите на вкладку Headers.
  5. Введите в поле Key заголовок Authorization со значением (поле Value) Basic <закодированная строка Consumer Key:Consumer Secret>.


    Пример


    Authorization: Basic eHZ6MWV2RlM0d0VFUFRHRUZQSEdFS2hab2xHQzB2SldMdzhpRUo4OERSZHlPZw==


    Примечание. Basic указывает на базовый метод авторизации.

  6. Введите второй заголовок Content-Type со значением application/x-www-form-urlencoded;charset=UTF-8.


    Пример


    Content-Type: application/x-www-form-urlencoded;charset=UTF-8


  7. Перейдите на вкладку Body.
  8. Выберите опцию x-www-form-urlencoded.
  9. Введите в поле Key тело запроса grant_type со значением client_credentials.
  10. Нажмите кнопку Send.


    OAuth-сервер вернет токен в JSON-формате:


    {
      "token_type": "bearer",
      "access_token": "AAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAA%3DAAAAAAAAAAAAAAAAAA"
    }

    Важно! Сохраните полученные токен и ключи (Consumer Key и Consumer Secret). Они потребуются для конфигурационного файла приложения.


Получение OAuth-токена для Google


Google предлагает приложениям возможность выдавать аутентифицированные запросы от имени самого приложения.


Примечание. За получение и обновление OAuth-токена с помощью POST-запроса в обмен на код авторизации отвечает модуль passport-youtube-v3.

С чего начать?


  1. Изучите документацию.
  2. Зарегистрируйте приложение и получите Client ID и Client Secret.
  3. Укажите callback URL (в нашем случае это http://localhost:3000) в учетной записи вашего приложения.
  4. Используйте полученные Client ID и Client Secret в запросах к YouTube Data API.

Важно! Сохраните полученные ключи (Client ID и Client Secret). Они потребуются для конфигурационного файла приложения.

Конфигурация приложения


После того как все ключи и токены получены, их необходимо добавить в конфигурационный файл приложения:


  • Добавьте в файл server/config.json поле services.


    "services": {
    "twitter": {
      "consumer_key": "",
      "consumer_secret": "",
      "bearer_token": ""
    },
    "youtube": {
      "client_id": "",
      "client_secret": "",
      "redirect_url": "http://localhost:3000"
    }
    }

    Полный код config.json.


  • Заполните одноименные поля полученными данными.
  • Скройте файл server/config.json от системы контроля версий Git, чтобы случайно не добавить личные ключи в репозиторий файлов.


    # файл .gitignore
    server/config.json

    Полный код .gitignore.



Работа с Twitter Search API


Twitter Search API позволяет найти последние или популярные твиты, опубликованные на сайте Twitter.com за последние 7 дней.


Для успешного вызова API необходимо сделать следующие изменения:


twitter-changes


Директория controllers


  • Измените весь текущий контент файла index.js на следующий.

Директория helpers


  • Добавьте в файл index.js следующий контент:


    module.exports = {
      twitter: require('./twitter')
    };

  • Добавьте следующий код в файл twitter.js.

Работа с YouTube Data API


YouTube Data API позволяет найти видеоролики, опубликованные на сайте Youtube.com. По умолчанию в набор результата поиска включены следующие ресурсы: видео, каналы, списки воспроизведения.


Для успешного вызова API необходимо сделать следующие изменения:


youtube-changes


Директория server


  • Добавьте следующий код в файл auth.js.


  • Отредактируйте файл routes.js.


    Измените:


    var router = require('express').Router(),
      controllers = require('./controllers');
    
    router
      .get('/ping/', function(req, res) {
          res.send('ok');
      })
      .get('/', controllers.getContent);
    
    module.exports = router;

    На:


    var router = require('express').Router(),
      controllers = require('./controllers'),
      passportYouTube = require('./auth'),
      middleware = require('./middleware/auth'),
      isAuthenticated = middleware.isAuthenticated;
    
    router
      .get('/auth/youtube', passportYouTube.authenticate('youtube'))
      .get('/auth/youtube/callback', passportYouTube.authenticate('youtube', { failureRedirect: '/error', failureFlash: true }), (req, res) => {
          res.redirect('/');
      })
      .get('/', isAuthenticated, controllers.getContent);
    
    module.exports = router;


Директория controllers


  • Измените весь текущий контент файла index.js на следующий.

Директория helpers


  • Добавьте в файл index.js следующий контент (см. комментарий):


    module.exports = {
      twitter: require('./twitter'),
      youtube: require('./youtube')        // Подключаем модуль `youtube.js`
    };

  • Добавьте следующий код в файл youtube.js.

Директория middleware


  • Добавьте в файл auth.js следующий контент:


    module.exports = {
      isAuthenticated: function(req, res, next) {
          if (req.isAuthenticated()) return next();
    
          return res.redirect('/auth/youtube');
      }
    };


Верстка


Мы сознательно не рассматривали вопросы верстки и клиентского JavaScript. Это привело бы к большему объему, а, значит, и к меньшей практической ценности данной статьи.


Процесс верстки сведен к следующим шагам:


  1. Удалите все блоки из директории common.blocks.
  2. Склонируйте следующие блоки в директорию common.blocks.
  3. Добавьте logo.svg в директорию static/images.
  4. Перезапустите сервер: npm run dev.

Приложение Social Services Search Robot готово.


Не получилось?


Если при создании приложения возникли сложности, поищите решение на форуме. Если готового ответа не нашлось, задайте вопрос.

Комментарии (42)


  1. vasIvas
    05.09.2017 22:37
    +2

    Все бы статьи так оформлялись, просто загляденье. Но к сожалению, это не делает вышеописанное понятным. Такое ощущение, что это внутренности монстра из прошлого, а не то что использует аж сам Альфа-Банк.
    Удивился, если бы увидел завтра статьи на тему — «прощай angular, здравствуй бэм», " бэм vs react", «создателя redux уличили в подготовки покушения на создателя бэм»… Жуть…


    1. tadatuta
      05.09.2017 23:02
      +3

      Удивился, если бы увидел завтра статьи на тему — «прощай angular, здравствуй бэм», " бэм vs react", «создателя redux уличили в подготовки покушения на создателя бэм»

      На самом деле всё чуть-чуть иначе: БЭМ в симбиозе с React, мастер-класс по внедрению возможностей БЭМ в проекта на redux и так далее.

      А что именно осталось непонятным после прочтения статьи? Мы бы с удовольствием дополнили и/или упростили.


      1. justboris
        06.09.2017 13:41
        +3

        Я бы назвал это немного иначе: это не БЭМ в симбиозе React, а попытка использовать React при куче легаси кода, написанного на i-bem и других внутренних технологиях.


        1. tadatuta
          06.09.2017 14:07
          +1

          Борис, «по ссылкам не ходи @ комментарий пиши»?
          Там ровно ноль связи с i-bem и каким-либо легаси ;)


          1. justboris
            06.09.2017 14:36

            Допустим, легаси в виде кода там нет. Но легаси образ мышления некоторых разработчиков все равно присутствует.


            1. dima117
              06.09.2017 16:49

              Ваша логика восхитительна! Поставил вам плюс в карму)


    1. staticlab
      05.09.2017 23:40
      +3

      Такое ощущение, что это внутренности монстра из прошлого, а не то что использует аж сам Альфа-Банк.

      Так Альфа-Банк и не использует у себя этого монстра. У нас это "про подчеркивания в классах", но, конечно, не пишем их ручками, а с помощью собственного инструмента. Целую библиотеку компонентов на Реакте так написали.


  1. i360u
    06.09.2017 04:17
    +12

    Только я не понял к чему все эти восторги? Куча специфичных зависимостей, жесткая привязка к спорному тул-чейну без надежды на относительно безболезненную миграцию в будущем, ради чего? БЭМ в CSS — не нужен, потому что есть ShadowDOM как и более легковесные принципы именования, применимые в современной компонентной разработке. БЕМ как шаблонизатор — это жуткий монстр… Половина статьи посвящана получению данных и вообще не связана именно с БЭМ. Что я упускаю, где та "жемчужина" ради которой я отвернусь от своего любимого стека и посмотрю в сторону БЭМ?


    1. ArVaganov
      06.09.2017 07:18
      +1

      более легковесные принципы именования, применимые в современной компонентной разработке
      Можно поподробней, пожалуйста? Тоже использую другой стек (React/Vue + immutable state), но что касается наименования классов в template/jsx кроме БЭМ не встречал более менее адекватных стандартов. Интересно было бы почитать. Что касается самой системы БЭМ, в полном ее понимании, и разумности ее применения – имхо это специфика разработки в большой корпорации, у них там на все заготовленные 'бест-практикс' готовые блоки/модули компоненты в целом и пр. Тем кто хочет идти работать в Y, думаю, точно стоит вчитаться и попробовать сделать проект по этой статье. Недавно была статья как они внедряют новые фичи в свои ресурсы, берут из базы подобный компонент, модифицируют, тестируют делают ревью, добавляют в базу компонентов и потом ставят на прод. Возможно тут с БЭМ действительно получается быстрее.


      1. vintage
        06.09.2017 10:15
        +1

        Мы используем что-то типа облегчённого БЭМ.


        Там, где в БЭМ будет:


        .my-app__menu > .mol-page__head {}

        У нас будет просто:


        [my_app_menu_head] {}

        То есть каскад вообще не требуется. Генерится такой атрибут автоматом на основе дерева компонент. То есть дом-дерево будет сгенерено примерно такое:


        <div my_app mol_book mol_view>
            <div my_app_menu mol_page mol_view>
                <div my_app_menu_head mol_page_head mol_view>

        Из вот такого вот исходника:


        $my_app $mol_book
            pages /
                <= Menu $mol_page

        Это позволяет задавать стили для компонент (mol_page, например), для их элементов (mol_page_head, например), для элементов компонент, которые сами выступают в роли элементов других компонент (my_app_menu_head, например) и так далее до любого уровня вложенности компонент (my_app_menu_head_close_icon_path, например).


        1. staticlab
          06.09.2017 10:23
          +1

          У вас не возникает проблем с атрибутами в IE/Edge?


          1. vintage
            06.09.2017 11:28

            Нет, у нас очень мало CSS правил (не больше нескольких сотен) и мало элементов единовременно рендерится (не больше тысячи), чтобы это хоть как-то серьёзно влияло на общее время рендеринга.


            1. Akuma
              06.09.2017 17:31

              А все же, стало интересно. Почему именно атрибуты, а не классы?


              1. vintage
                06.09.2017 18:05
                +1

                Модификаторы удобно засовывать в значения атрибута:


                <div my_button="big accent">

                [my_button] { ... }
                [my_button~="big"] { ... }
                [my_button~="accent"] { ... }

                Хотя, у нас эта возможность пока и не используется. :-)


      1. knotri
        06.09.2017 11:29
        +1

        Так Css Modules и всякие там StyledComponent.
        Считаю что бэм не нужен.


      1. kashey
        07.09.2017 05:54
        +1

        Есть HTML driven by CSS — это BEM, где у вас нет проблемы с CSS, но все остальное пляшет под его дудку
        Есть CSS driven by CSS — это Tachyons/Turrent и другой atom/functional CSS. Где CSS-а практически нет, зато в html.

        <button class="bg-purple f6 br3 ph3 pv2 white hover-bg-light-purple">

        И есть CSS-in-JS/ShadowDOM который разными путями немного исправляет генетику CSS+HTML и к нему следует применять немного другие подходы, потому что НЕ нужно именовать что либо по BEM-методолигии так как нет _необходимости_.

        >Тем кто хочет идти работать в Y, думаю, точно стоит вчитаться и попробовать
        В Яндексе есть много мест где никакого БЕМа нет.


        1. vintage
          07.09.2017 06:57

          НЕ нужно именовать что либо по BEM-методолигии так как нет необходимости

          Есть. Именование — оно прежде всего для человека. Если человек работает с сотней сущностей, то у всех у них должны быть уникальные имена, иначе он легко запутается.


          1. kashey
            07.09.2017 07:34

            Но функции, классы и переменные вы по БЕМу(и венгерской нотации) не называете?
            Декомпозиция и изоляция — вечные друзья и спутники программиста.


            1. vintage
              07.09.2017 08:03

              Ещё как называю :-) Благодаря этому как раз и работает автоматическое детектирование зависимостей без портянок import/export.


              1. kashey
                07.09.2017 08:07

                А в bem-react так вообще импорты вычисляемые…


                1. vintage
                  07.09.2017 08:32

                  Это как?


                  1. kashey
                    07.09.2017 12:15

                    import BlockElem from 'b:block e:elem m:modName';

                    github.com/bem/webpack-bem-loader


                    1. vintage
                      07.09.2017 12:30

                      Зачем такие сложности?


                      1. kashey
                        07.09.2017 12:34

                        Но это же лучше чем там -> в статье наверху?


                        1. vintage
                          07.09.2017 12:49

                          Чем лучше?


                          1. kashey
                            07.09.2017 12:59

                            Я уже год как не Яндексоид, и мне как-то и то и другое не очень.


                            1. babylon
                              08.09.2017 13:06

                              Кто такие Яндесоиды? Очевидно, что BEM это в корне (или из корня)неверная технология.
                              Но другой нет. Т.е. есть — JSONNET, но её никто не использует. 21 век. Все по-прежнему делается руками.


                              tadatuta


                              Ты же знаешь ответ ;)
                              Зависит от отдела == опыта команды и требований

                              Зависит от руководства.
                              Я бы ваш отдел не моргнув глазом распустил.
                              Единственное детей и женщин жалко:)


              1. DarthVictor
                07.09.2017 11:03

                Благодаря этому как раз и работает автоматическое детектирование зависимостей без портянок import/export.

                Рискну предположить, что заодно и без автокомплита и перехода к определению по Cmd+Click.


                1. vintage
                  07.09.2017 11:12

                  Да нет, всё работает благодаря тайпскрипту.


                  1. DarthVictor
                    07.09.2017 11:38

                    А тайпскрипт как работает без импортов/экспортов?


                    1. vintage
                      07.09.2017 12:04

                      Как положено он работает.


    1. stardust_kid
      06.09.2017 15:20
      +1

      ЗАТОКАКУЯНДЕКС!!!!!111


  1. vintage
    06.09.2017 11:13

    BEMDECL. Определяет список БЭМ-сущностей для страницы.
    DEPS. Определяет зависимости между БЭМ-сущностями, которые разнесены по файловой структуре проекта и не отражены в декларации.

    Зачем отдельная сущность "страница"? Что делать, если мне нужно отобразить одну страницу в качестве блока на другой странице? Почему бы не сделать страницу таким же блоком, как любой другой без своего уникального способа задания зависимостей?


    Изучающие БЭМ порой забывают о декларативности технологий и недоумевают: почему для описанного в шаблоне блока, не собираются его стили и скрипты. когда вы описываете шаблон (BEMHTML или BEMTREE) с каким-то блоком внутри (дочерний узел), вы просто добавляете новый HTML-элемент. Чтобы стили и скрипты этого блока попали в сборку, необходимо описать зависимость от него.

    Зачем вообще вручную рулить всеми этими зависимостями, если можно подтягивать их автоматически по факту использования? Например, у нас стоит только воспользоваться где-нибудь в своём приложении компонентом $foo_bar, то автоматически будут подключены все стили/скрипты/шаблоны/остальное из директории /foo/bar. А если оной не окажется, то она может быть автоматически склонирована с гитхаба, если есть соответствующий маппинг в общем реестре. То есть вот это вот не нужно делать: "Склонируйте bem-express и тд".


    i-bem.js Клиентский JavaScript-фреймворк для веб-разработки в рамках БЭМ-методологии.

    Почему не TypeScript или хотя бы Flow? Вы же позиционируете фреймворк для больших проектов (с +10 К пользователей в сутки, ага :-)).


    Я работал с этим фреймворком на одном из проектов и мне не понравилась работа с модифиакторами посредством триггеров. Это куча ручной работы. Да и сами по себе они весьма ограниченны. Современные фреймворки используют реактивное программирование, которое позволяет задавать инварианты и рантайм уже сам их поддерживает.


    moment — JavaScript библиотека для синтаксического анализа, валидации и форматирования дат.

    На $jin.time не смотрели?


    Чтобы закодировать строку методом Base64

    btoa("xvz1evFS4wEEPTGEFPHBog:L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg")

    Приложение Social Services Search Robot готово.

    Возможно я плохо смотрел, но я не заметил там работы с базой данных. Вы делаете запрос к апи сервисов на каждый запрос пользователя? Если так, то такой сервис очень быстро заблокируют по лимитам. Какие уж там "+10 К пользователей в сутки".


    1. godfreyd Автор
      07.09.2017 11:00

      но я не заметил там работы с базой данных.

      Все верно, работы с базой данных нет, так как не стояло такой задачи. Зачем? Задача показать, какие технологии за что отвечают, и какая из них (BEMTREE) ориентирована на работу с данными. Дальше решает сам разработчик.
      +10 К пользователей в сутки

      Эта фраза относится к тому, что БЭМ обеспечивает надежную и масштабируемую архитектуру, а не к теме поста.


      1. vintage
        07.09.2017 11:06

        Клиент запускается на машине каждого пользователя. Сервер упирается в масштабируемость nodejs+express. Что такого волшебного может дать БЭМ для "+10 К пользователей в сутки"?


  1. asavin
    06.09.2017 22:23

    Представим, что в компании Яндекс планируют на следующей неделе начать разработку нового проекта. Какова вероятность, что этот проект будет использовать i-bem и bemhtml, а не тот же React?


    1. tadatuta
      07.09.2017 14:56

      Ты же знаешь ответ ;)
      Зависит от отдела == опыта команды и требований.


  1. devlev
    06.09.2017 23:56
    -1

    Интересно, хоть один человек вдохновился этой статьей и перешел на БЭМ? Мне вот почему то интуиция подсказывает что нет. Да и что это за Hello World, в котором мало того что нужно склонировать начальный проект из репозитория, так еще пару файлов подправить чтобы все заработало. И это на минуточку Hello World! Автор статьи даже не удосужился ее опробывать на windows, ибо на виндоус не выполнится команда rm -rf .git. У меня даже собрать проект не получилось потому что инструкция орентирована только на linux пользователей.


    1. godfreyd Автор
      07.09.2017 10:39

      Привет. На Windows мы проверяли, все работало и работает сейчас. Попробовал заново все пройти. Все замечательно. Вы GitBash используете?

      Результат:

      rm -rf .git



      Проект SSSR:



      Данные по системе


      1. devlev
        07.09.2017 21:20

        Извеняюсь, теперь и правда все собралось и работает. До этого просто всегда пользовался обычной консолью винды и видимо предупреждение о том что запускать надо только через GitBash осталось в слепой зоне. Спасибо!


    1. godfreyd Автор
      07.09.2017 10:52

      Да и что это за Hello World, в котором мало того что нужно склонировать начальный проект из репозитория, так еще пару файлов подправить чтобы все заработало.

      Вы не проект клонируете, а шаблонный репозиторий, на основе которого можно собирать свои динамические проекты.


      1. devlev
        07.09.2017 21:26

        Мне просто показалось логичнее шаблонный репозиторий сразу сделать Hello World. Ну или загрузить его как отдельный подпроект готовой сборкой. Скачал, установил, запустил.