Closed gwer closed 7 years ago
Скорее всего вы не должны этого хотеть. В клиентском JS есть (может быть) BEMHTML. Можете подробнее описать кейс?
@Realetive, я неоднократно задавался вопросом наличия такового желания, и избавиться от него не удалось. Например, из данных, прилетающих от бэкэнда, нужно уметь формировать отчёт как в HTML, так и в более простом текстовом формате. Для построения отчёта данные нужны как в сыром виде, так и в некотором отформатированном. Можно эти данные отформатировать на бэкэнде, и продублировать их в сыром и форматированном виде на клиент. Но, допустим, что мы не хотим или не можем этого сделать.
@tadatuta, надо потыкать, но выглядит отличным решением проблемы. Благодарю.
Потыкал. Но всё же проблема решена не до конца.
Создаю ym-модуль. Допустим, desktop.blocks/my/my.js. Добавляю в deps, в .enb/make.js прописываю модуль в requires. Модуль доступен в клиентском JS, в клиентском BEMHTML. А в серверном BEMHTML чуда не происходит. Так понимаю, проблема в том, что модуль относится к клиентской технологии и в серверную сборку не попадает. Если это так, каким образом его наиболее аккуратно можно подсунуть в нужное место?
@gwer
Если смотреть на конфиг сборки в project-stub
или bem-express
, то за сборку серверного и клиентского BEMHTML отвечают отдельные части конфига: сервер и клиент.
Результатом использования опции requires является генерация вызова onInit()
, где происходит предоставление запрошенного модуля так, что он становится доступен по this.require('lib-name')
внутри любого шаблона. При этом такое подключение достаточно универсальное: можно подключить как просто файл с глобальными переменными, так и CommonJS-модуль — это разрулится в build time.
Чтобы написать универсальный модуль, который будет работать и как 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);
});
Надеюсь, это отвечает на вопрос.
@tadatuta, спасибо за развёрнутые ответы.
1). Это понятно. 2, 3) Так и не понял, можно серверной части скормить YM или обязательно предоставлять второй интерфейс (CommonJS, например)?
В доке сказано, что для CommonJS нужен относительный путь. Столкнулся с проблемой, заключающейся в том, что удаётся заставить работать только с абсолютными. Глубоко не копал, но, на первый взгляд, проблема в том, что внутри require путь разворачивается до абсолютного, а в качестве аргумента передаётся относительный.
Бросаемая ошибка: Error: Cannot find module '../../desktop.blocks/my/my2.js'
, изнутри этого самого require.
Путь правильный (содержимое инлайнится, да и при неправильном пути ошибки другие).
Если передавать абсолютный путь, то всё работает.
Пакеты npm и project-stub актуальные на текущий час.
Что-то делаю не так?
Пока что удаётся завести лишь с модулем, имеющим два интерфейса (YM, CommonJS), и с выбором способа доставки модуля внутри BEMHTML в зависимости от окружения (require(path)
/ this.require(name)
). Это уже неплохо, работает, но всё ещё грязновато. Хочется чище, насколько это позволяют инструменты.
Так и не понял, можно серверной части скормить 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)
, для этого все и задумывалось.
В остальном, вроде, звучит нормально.
Идеи понял. Всё завелось. Спасибо.
Какие существуют практики для сабжа?
Допустим, есть некие общие функции, которые необходимы и в клиентском JS, и в bemhtml.
Как наиболее корректно и с минимумом костылей избежать копипаста? Пока напрашивается некий модуль, который будет:
Это выглядит работающим решением, но вызывает сомнения в плане адекватности. Можно ли изящнее?