Closed leonidborisenko closed 11 years ago
I guess I can eplain in russian.
Привет! Спасибо, что написал.
jQuery.noConflict(true)
очень криточно?
Пока есть 2 решения это проблемы если jQuery будет глобальным и оба без дополнительных плагинов.
1) Вообще включить jQuery в состав сборки LMD можно, но лучше грузить его из какого-либо CDN. (jQuery в любом случае экспортирует себя в глобалы) Тогда jQuery будет в глобалах и при старте Bacon разрулит его автоматически (this.jQuery
, this===window
)
{
"modules": {
"bacon": {
"path": "bacon.js",
"require": "jQuery", // на всякий случай
"exports": "this.Bacon"
}
}
}
2) Если очень хочется включить jQuery в сборку. Фактически jQuery является зависимостью Bacon и эта зависимость должна стартовать раньше Bacon.
{
"modules": {
"jQuery": {
"path": "jquery.js",
"exports": "jQuery"
},
"bacon": {
"path": "bacon.js",
// нужно обязательно добавить, чтобы jQuery проинициализировалась
"require": "jQuery",
"exports": "this.Bacon"
}
}
}
Спасибо за репорт!
Я добавлю возможноть сделать bind (изменить this) у 3-party модулей. Примерно так:
{
"modules": {
"bacon": {
"path": "bacon.js",
"bind": "jQuery", // "this": "jQuery" || "bind": {"jQuery": "jQuery"} || "this": {"jQuery": "jQuery"}
"exports": "this.Bacon"
}
}
}
Можно сделать временный костыль.
// myjQuery.js
var myjQuery = require('_jQuery').noConflict(true);
// Подсовываем для Bacon
window.jQuery = myjQuery;
module.exports = myjQuery;
{
"modules": {
"jQuery": "myjQuery.js",
"_jQuery": {
"path": "jquery.js",
"exports": "jQuery"
},
"bacon": {
"path": "bacon.js",
"require": "jQuery",
// Возвращаем старый jQuery
"exports": "this.jQuery = require('_jQuery'), this.Bacon" // '_jQuery' - старый закэшированный jQuery
}
}
}
Я подумал и решил, что лишний код лучше влючить в "обертку" модуля чем в плагин. Этот Юз-кейс достаточно редкий и поэтому такое решение будет уместно и оно лучше подходит под идею адаптации модуля и оно гибче (не нужно пробрасывать конфиг модуля).
function (require, module, exports) { // added by wrapper
var deps = require('deps'); // added by wrapper
return (function () { // added by wrapper
// ТУТ СТОРОННИЙ КОД
return this.Bacon; // added by wrapper
}).call({
"jQuery": require("jQuery") // added by wrapper
});
} // added by wrapper
jQuery.noconflict()
не критично. Ход с подменой this
я предпринял, руководствуясь не прагматизмом, а эстетизмом. Раз уж модули явно запрашивают зависимости, то можно задаться целью исключить или отменить утекание объектов в глобальное пространство имён. Чтобы "всё было красиво". Ну, вот и пришлось подступаться к возникшей проблеме с занятой позиции.
Брать jQuery
из CDN я не могу, потому что моё приложение предполагает оффлайновую установку и рассчитано на работу в изолированной среде, где доступ к интернету не гарантирован. require('jQuery')
в зависимостях у модуля bacon
я пробовал, и это не сработало, потому что оригинальный код в bacon.js уже завёрнут в функцию, вызывающуюся с помощью .call(this)
. "Временный костыль" -- хитро завёрнутая идея: я честно пытался разобраться, как это отменит "утекание" jQuery
в window
и не смог; потом попробовал эту конструкцию в работе и не увидел нужного результата.
Идея с явным .call
мне очень нравится, голосую за неё.
Проблема jQuery в том, что оно по своей природе утекает в глобалы - явно пишет window.jQuery = jQuery
Это да. В случае с jQuery/lodash/Backbone предотвратить утекание невозможно. Но отменить-то его можно с помощью noConflict()
в "exports"
-параметре в конфигурации LMD. После чего прочие модули не увидят эти библиотеки в window
. "Идеально" уже не получается, но подобное компромиссное решение -- отмена утекания в самом модуле сразу же после выполнения оригинального кода -- меня устраивает.
Накидал пример https://gist.github.com/azproduction/4771673 Gist не умеет папки поэтому lmd.json закинул в корень. С реальными файлами так же должно работать.
Обновил пример - зачистил jQuery
Теперь понял, спасибо. myjQuery.js, по-моему, можно переписать:
var jQuery = require('_jQuery');
require('Bacon');
module.exports = jQuery.noConflict(true);
Конфигурацию тоже следует изменить. Bacon.js предоставляет объект Bacon
, который имеет самостоятельную ценность. Поэтому Bacon.js может быть затребован отдельно и, следовательно, должен зависеть от jQuery
, чтобы определить свой плагин перед кэшированием модуля jQuery
.
{
<...>
"modules": {
<...>
"Bacon": {
"path": "bacon.js",
"require": "jQuery",
"exports": "Bacon"
}
}
}
Но жизнеспособность данного способа нивелируется тем, что Bacon.js выглядит как-то так:
(function(){
(this.jQuery || this.Zepto).fn.asEventStream = function () {
};
Bacon = this.Bacon = {};
}).call(this);
и у него нет метода, подобного noConflict()
, так что он неизбежно утекает в глобальное пространство имён (если не переопределять this
).
Ясно. В ближайшее время я добавлю возможность влиять на this. Как будет -- напишу.
Обновляйтесь lmd@1.10.4
Спасибо. Для моего случая достаточно.
Только, предполагаю, код в модуле, полагающийся на this === window
, может использовать свойства window
через this
: например, this.document
или this.history
или this.location
. При переопределении this
подобный код поломается. Из-за этого было бы удобнее писать произвольный код в значениях объекта:
"bind": {
"MyDate": "require('Date')",
"document": "window.document"
}
Возможно, я недопонял, и возможность добавлять свойства из window
в пользовательский ThisBinding
есть и в текущем варианте?
Алгоритм require такой, что если он не найдет переменную в "modules", то полезет искать в "global" (window).
Для вашего случая достаточно сделать так:
{
"MyDate": "Date",
"document": "document"
}
Отлично, спасибо за пояснение.
I've met a use-case for binding a module function (as made by LMD builder) to user-defined object.
Bacon.js adds asEventStream function to jQuery object, expecting it as a property of this. But I've made jQuery non-global by exposing it with "exports": "jQuery.noConflict(true)" in LMD configuration, so Bacon.js couldn't find it.
However, with little plugin for changing of ThisBinding and one-level indirection I now have non-global jQuery with asEventStream function.
Here is the plugin code:
and corresponfing LMD configuration:
I think it'd be useful to have this ability included in LMD, so it'd be possible to define modules like
Note that syntax for value is a bit more explicit than in original plugin; it's intentional change.
It'd also be useful to be able to merge selected properties from window into binding object by providing whitelist (merge only listed properties) or blacklist (merge all properties except listed).
I'm not sure in my cross-browser JS skills, so I'm not providing PR, sorry for that.