bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Расшаривание логики между технологиями #1243

Closed gwer closed 7 years ago

gwer commented 7 years ago

Какие существуют практики для сабжа?

Допустим, есть некие общие функции, которые необходимы и в клиентском JS, и в bemhtml.

Как наиболее корректно и с минимумом костылей избежать копипаста? Пока напрашивается некий модуль, который будет:

  1. Средствами сборщика импортироваться в блок, реализующий клиентский JS, который потом цепляется зависимостями куда надо.
  2. Расширять BEMContext, но для этого модуль тоже надо импортировать сначала.

Это выглядит работающим решением, но вызывает сомнения в плане адекватности. Можно ли изящнее?

Realetive commented 7 years ago

Скорее всего вы не должны этого хотеть. В клиентском JS есть (может быть) BEMHTML. Можете подробнее описать кейс?

tadatuta commented 7 years ago

@gwer Есть решение на уровне сборки: https://github.com/enb/enb-bemxjst#%D0%9F%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D1%81%D1%82%D0%BE%D1%80%D0%BE%D0%BD%D0%BD%D0%B8%D1%85-%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA

gwer commented 7 years ago

@Realetive, я неоднократно задавался вопросом наличия такового желания, и избавиться от него не удалось. Например, из данных, прилетающих от бэкэнда, нужно уметь формировать отчёт как в HTML, так и в более простом текстовом формате. Для построения отчёта данные нужны как в сыром виде, так и в некотором отформатированном. Можно эти данные отформатировать на бэкэнде, и продублировать их в сыром и форматированном виде на клиент. Но, допустим, что мы не хотим или не можем этого сделать.

@tadatuta, надо потыкать, но выглядит отличным решением проблемы. Благодарю.

gwer commented 7 years ago

Потыкал. Но всё же проблема решена не до конца.

Создаю ym-модуль. Допустим, desktop.blocks/my/my.js. Добавляю в deps, в .enb/make.js прописываю модуль в requires. Модуль доступен в клиентском JS, в клиентском BEMHTML. А в серверном BEMHTML чуда не происходит. Так понимаю, проблема в том, что модуль относится к клиентской технологии и в серверную сборку не попадает. Если это так, каким образом его наиболее аккуратно можно подсунуть в нужное место?

tadatuta commented 7 years ago

@gwer

  1. Если смотреть на конфиг сборки в project-stub или bem-express, то за сборку серверного и клиентского BEMHTML отвечают отдельные части конфига: сервер и клиент.

  2. Результатом использования опции requires является генерация вызова onInit(), где происходит предоставление запрошенного модуля так, что он становится доступен по this.require('lib-name') внутри любого шаблона. При этом такое подключение достаточно универсальное: можно подключить как просто файл с глобальными переменными, так и CommonJS-модуль — это разрулится в build time.

  3. Чтобы написать универсальный модуль, который будет работать и как CommonJS и в браузере с ym, можно добавить экспорт по условию. Что-то в духе (не тестровал, могут быть опечатки):

    
    (function(global) {
    
    // эта строка символизирует некий полезный универсальный код
    var myModule = function() {};
    
    // добавляем экспорт в зависимости от окружения
    if (module && module.exports) {
        module.exports = myModule;
    } else if (modules) {
        modules.define('my-module', function(provide) {
            provide(myModule);
        });
    } else {
        global.myModule = myModule;
    }

})(this);


Если же код, который хочется использовать на клиенте и в серверных шаблонах является внешним модулем, то в шаблоны его по-прежнему можно подключать просто указав к нему путь, а для оборачивания в `ym`-модуль создать блок вида
```js
modules.define('moment', function(provide) {
    /* borschik:include:../../node_modules/moment/moment.js */
    provide(this.moment);
});

Надеюсь, это отвечает на вопрос.

gwer commented 7 years ago

@tadatuta, спасибо за развёрнутые ответы.

1). Это понятно. 2, 3) Так и не понял, можно серверной части скормить YM или обязательно предоставлять второй интерфейс (CommonJS, например)?

В доке сказано, что для CommonJS нужен относительный путь. Столкнулся с проблемой, заключающейся в том, что удаётся заставить работать только с абсолютными. Глубоко не копал, но, на первый взгляд, проблема в том, что внутри require путь разворачивается до абсолютного, а в качестве аргумента передаётся относительный. 2017-02-06 23 59 55

Бросаемая ошибка: Error: Cannot find module '../../desktop.blocks/my/my2.js', изнутри этого самого require. Путь правильный (содержимое инлайнится, да и при неправильном пути ошибки другие). Если передавать абсолютный путь, то всё работает. Пакеты npm и project-stub актуальные на текущий час. Что-то делаю не так?

Пока что удаётся завести лишь с модулем, имеющим два интерфейса (YM, CommonJS), и с выбором способа доставки модуля внутри BEMHTML в зависимости от окружения (require(path) / this.require(name)). Это уже неплохо, работает, но всё ещё грязновато. Хочется чище, насколько это позволяют инструменты.

tadatuta commented 7 years ago

Так и не понял, можно серверной части скормить YM или обязательно предоставлять второй интерфейс (CommonJS, например)?

Можно лишь при условии, что ym будет доступна на сервере (она-то кроссплатформенная и потенциально работать может, но есть ли смысл ее тянуть? Я бы предпочел добавить экспорт в CommonJS, но это скорее дело вкуса).

В доке сказано, что для CommonJS нужен относительный путь

Кажется, дока вероломно брешет, у нас в коде вот так:

[techs.bemtreeI18N, {
    target: '?.placeholders.bemtree.{lang}.js',
    sourceSuffixes: ['bemtree.js', 'bemtree'],
    lang: '{lang}',
    requires: {
        moment: { commonJS: 'moment' },
        querystring: { commonJS: 'querystring' },
        later: { commonJS: 'later' },
        lodash: { commonJS: 'lodash' },
        url: { commonJS: 'url' },
        lib: { commonJS: path.resolve('./lib') } // вот тут резолвим путь до абсолютного
    }
}]

и с выбором способа доставки модуля внутри BEMHTML в зависимости от окружения (require(path) / this.require(name))

Верно ли я понял, что речь о том, что в одних и тех же шаблонах на клиенте и на сервере приходится использовать разный способ получения модуля? Если так, то определенно можно собрать так, чтобы всегда использовать только this.require(name), для этого все и задумывалось.

В остальном, вроде, звучит нормально.

gwer commented 7 years ago

Идеи понял. Всё завелось. Спасибо.