Смотрите, предположим нам поставили задачу реализовать простую форму:
var MyFormView = Marionette.ItemView.extend({
template: 'myForm',
className: 'my-form',
ui: {
$submit: '.js-submit'
},
events: {
'click @ui.$submit': 'submit'
},
send: function () {
myAsyncSubmit
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occured!')
});
}
});
<template id="myForm">
<form>
<div class="my-form-wrapper">
<div class="block">
<label for="input1">Your Name:</label><br>
<input type="text" id="input1">
</div>
<div class="block">
<label for="input2">Your Region:</label><br>
<input type="text" id="input2">
</div>
<div class="block">
<label for="input3">Your City:</label><br>
<input type="text" id="input1">
</div>
<button type="submit" class="js-send">Send</button>
</div>
</form>
</template>
Все просто, но у посетителя есть возможность сабмитить форму сколько угодно раз после уже отправки запроса. Блокируем кнопку до того, как станет понятно, что произошло с запросом:
var MyFormView = Marionette.ItemView.extend({
template: 'myForm',
className: 'my-form',
ui: {
$submit: '.js-submit'
},
events: {
'click @ui.$submit': 'submit'
},
submit: function () {
this.ui.$submit.prop('disabled', true);
myAsyncSubmit
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occurred!')
})
.always(function () {
this.ui.$submit.prop('disabled', true);
});
}
});
Далее форма долго или не очень живет своей жизнью до того дня, как ее не понадобится немного модифицировать. Теперь форма должна выглядеть так:
Поле «Your ZIP» забирает на сервере информацию по заданному ZIP и подставляет ее в поля ниже. Это позволяет обойтись без ввода «Your Region» и «Your City», но поэтому при нажатии кнопки «Set», блокируются поля «Your Region», «Your City» и кнопка «Submit» до завершения запроса, вот так:
Правим код:
var MyFormView = Marionette.ItemView.extend({
template: 'myForm',
className: 'my-form',
ui: {
$inputZip: '#inputZip',
$setZip: '.js-set-zip',
$inputRegion: '#inputRegion',
$inputCity: '#inputCity',
$submit: '.js-submit'
},
events: {
'click @ui.$setZip': 'setZip',
'click @ui.$submit': 'submit'
},
setZip: function () {
toggleZipInputs(true);
myAsyncSetZip
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
toggleZipInputs(false);
});
function toggleZipInputs (value) {
this.ui.$inputZip.prop('disabled', value);
this.ui.$setZip.prop('disabled', value);
this.ui.$inputRegion.prop('disabled', value);
this.ui.$submit.prop('disabled', value);
}.bind(this);
},
submit: function () {
this.ui.$submit.prop('disabled', true);
myAsyncSubmit
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
this.ui.$submit.prop('disabled', true);
});
}
});
<template id="myForm">
<form>
<div class="my-form-wrapper">
<div class="block">
<label for="inputName">Your Name:</label><br>
<input type="text" id="inputName">
</div>
<div class="block">
<label for="inputZip">Your ZIP:</label><br>
<input type="text" id="inputZip">
<button type="button" class="js-set-zip">Set</button>
</div>
<div class="block">
<label for="inputRegion">Your Region:</label><br>
<input type="text" id="inputRegion">
</div>
<div class="block">
<label for="inputCity">Your City:</label><br>
<input type="text" id="inputCity">
</div>
<button type="submit" class="js-send">Send</button>
</div>
</form>
</template>
Заметили, как удлиннился код? Пришлось создавать ссылки до каждого элемента, который нам нужно заблокировать, а также появился целый блок кода, состоящий из одних только манипуляций DOM-деревом, вынесенный в функцию 'toggleZipInputs'. При этом здесь нет ни строчки бизнес-логики. Тем временем, форма снова расширилась:
Увеличение на этот раз лишь количественное, смысл работы полей остался тот же самый — кнопка «Set Your Car Number» на время выполнения запроса блокирует поля «Your Car Model» и «Your Car Age», кнопка «Set Your Social ID» делает тоже самое с полями «Your Gender», «Your Age» и «Your Sexual Orientation». Обе они также блокируют кнопку «Submit»:
Ооокей, пишем код:
var MyFormView = Marionette.ItemView.extend({
template: 'myForm',
className: 'my-form',
ui: {
$inputZip: '#inputZip',
$setZip: '.js-set-zip',
$inputRegion: '#inputRegion',
$inputCity: '#inputCity',
$inputCarNumber: '#inputCarNumber',
$setCarNumber: '.js-set-car-number',
$inputCarModel: '#inputCarModel',
$inputCarAge: '#inputCarAge',
$inputSocialId: '#inputSocialId',
$setSocialId: '.js-set-social-id',
$inputGender: '#inputGender',
$inputAge: '#inputAge',
$inputSexualOrientation: '#inputSexualOrientation',
$submit: '.js-submit'
},
events: {
'click @ui.$setZip': 'setZip',
'click @ui.setCarNumber': 'setCarNumber',
'click @ui.$submit': 'submit'
},
setZip: function () {
toggleZipInputs(true);
myAsyncSetZip
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
toggleZipInputs(false);
});
function toggleZipInputs (value) {
this.ui.$inputZip.prop('disabled', value);
this.ui.$setZip.prop('disabled', value);
this.ui.$inputRegion.prop('disabled', value);
this.ui.$submit.prop('disabled', value);
}.bind(this);
},
setCarNumber: function () {
toggleCarInputs(true);
myAsyncSetCarNumber
.done(function () {
alert('Car Number set!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
toggleCarInputs(false);
});
function toggleCarInputs (value) {
this.ui.$inputCarNumber.prop('disabled', value);
this.ui.$setCarNumber.prop('disabled', value);
this.ui.$inputCarModel.prop('disabled', value);
this.ui.$inputCarAge.prop('disabled', value);
this.ui.$submit.prop('disabled', value);
}.bind(this);
},
setSocialId: function () {
toggleSocialInputs(true);
myAsyncSetSocial
.done(function () {
alert('Social ID set!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
toggleSocialInputs(false);
});
function toggleSocialInputs (value) {
this.ui.$inputSocialId.prop('disabled', value);
this.ui.$setSocialId.prop('disabled', value);
this.ui.$inputGender.prop('disabled', value);
this.ui.$inputAge.prop('disabled', value);
this.ui.$inputSexualOrientation.prop('disabled', value);
this.ui.$submit.prop('disabled', value);
}.bind(this);
},
submit: function () {
this.ui.$submit.prop('disabled', true);
myAsyncSubmit
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
this.ui.$submit.prop('disabled', true);
});
}
});
<template id="myForm">
<form>
<div class="my-form-wrapper">
<div class="col">
<div class="block">
<label for="inputName">Your Name:</label><br>
<input type="text" id="inputName">
</div>
<div class="block">
<label for="inputZip">Your ZIP:</label><br>
<input type="text" id="inputZip">
<button type="button" class="js-set-zip">Set</button>
</div>
<div class="block">
<label for="inputRegion">Your Region:</label><br>
<input type="text" id="inputRegion">
</div>
<div class="block">
<label for="inputCity">Your City:</label><br>
<input type="text" id="inputCity">
</div>
</div>
<div class="col">
<div class="block">
<label for="inputCarNumber">Your Car Number:</label><br>
<input type="text" id="inputCarNumber">
<button type="button" class="js-set-car-number">Set</button>
</div>
<div class="block">
<label for="inputCarModel">Your Car Model:</label><br>
<input type="text" id="inputCarModel">
</div>
<div class="block">
<label for="inputCarAge">Your Car Age:</label><br>
<input type="text" id="inputCarAge">
</div>
</div>
<div class="col">
<div class="block">
<label for="inputSocialId">Your Social ID:</label><br>
<input type="text" id="inputSocialId">
<button type="button" class="js-set-social-id">Set</button>
</div>
<div class="block">
<label for="inputGender">Your Gender:</label><br>
<input type="text" id="inputGender">
</div>
<div class="block">
<label for="inputAge">Your Age:</label><br>
<input type="text" id="inputAge">
</div>
<div class="block">
<label for="inputSexualOrientation">Your Sexual Orientation:</label><br>
<input type="text" id="inputSexualOrientation">
</div>
</div>
<button type="submit" class="js-send">Send</button>
</div>
</form>
</template>
Следите за руками — примитивная форма, ни строчки бизнес-логики, а уже куча кода. Кода, который надо писать, тестировать и поддерживать. Кода, который надо читать новоприбывшим коллегам. Кода, за написание которого надо заплатить программистам. Ну а дальше… Ну вы поняли:
Думаю, дальше можно не объяснять — при дальнейшем усложнении формы нам нужно будет создавать ссылки на каждый элемент формы, состояние которого будет меняться, а затем вручную изменять его состояние. Насколько это плохо в реальности, а не на демо-примере? Очень плохо. Я проработал с BackboneJS и его окружением два года, и это самый большой минус этого фреймворка — постоянно растущее количество кода и сложность его обслуживания.
С момента появления фреймворков нового поколения типа React+Redux, целесообразность использования BackboneJS лично для меня стала равна нулю. Вот тот же код с использованием React+какой-нибудь контроллер. Сравните даже не столько количество кода, а смысл происходящего в нем — здесь нет ни одной манипуляции с DOM вручную, той кучи сложноподдерживаемого кода из прошлых примеров:
var MyForm = React.createClass({
var self = this;
this.setState({ isZipSetting: true });
myAsyncSetZip
.done(function () {
alert('Zip set!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
self.setState({ isZipSetting: false });
});
},
setCarNumber: function () {
var self = this;
this.setState({ isCarNumberSetting: true });
myAsyncSetCarNumber
.done(function () {
alert('Car number set!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
self.setState({ isCarNumberSetting: false });
});
},
setSocialId: function () {
var self = this;
this.setState({ isSocialIdSetting: true });
myAsyncSetSocialId
.done(function () {
alert('Social ID set!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
self.setState({ isSocialIdSetting: false });
});
},
submit: function () {
var self = this;
this.setState({ isSubmitting: true });
myAsyncSubmit
.done(function () {
alert('Form submitted!');
})
.fail(function () {
alert('Error occured!')
})
.always(function () {
self.setState({ isSubmitting: false });
});
},
render: function () {
return (
<form>
<div className="my-form-wrapper">
<div className="col">
<div className="block">
<label for="inputName">Your Name:</label><br>
<input type="text" id="inputName">
</div>
<div className="block">
<label for="inputZip">Your ZIP:</label><br>
<input type="text" id="inputZip" { isZipSetting ? 'disabled' : '' }>
<button type="button" onClick={this.setZip} { isZipSetting ? 'disabled' : '' }>Set</button>
</div>
<div className="block">
<label for="inputRegion">Your Region:</label><br>
<input type="text" id="inputRegion" { isZipSetting ? 'disabled' : '' }>
</div>
<div className="block">
<label for="inputCity">Your City:</label><br>
<input type="text" id="inputCity" { isZipSetting ? 'disabled' : '' }>
</div>
</div>
<div className="col">
<div className="block">
<label for="inputCarNumber">Your Car Number:</label><br>
<input type="text" id="inputCarNumber" { isCarNumberSetting ? 'disabled' : '' }>
<button type="button" onClick={this.setCarNumber} { isCarNumberSetting ? 'disabled' : '' }>Set</button>
</div>
<div className="block">
<label for="inputCarModel">Your Car Model:</label><br>
<input type="text" id="inputCarModel" { isCarNumberSetting ? 'disabled' : '' }>
</div>
<div className="block">
<label for="inputCarAge">Your Car Age:</label><br>
<input type="text" id="inputCarAge" { isCarNumberSetting ? 'disabled' : '' }>
</div>
</div>
<div className="col">
<div className="block">
<label for="inputSocialId">Your Social ID:</label><br>
<input type="text" id="inputSocialId" { isSocialIdSetting ? 'disabled' : '' }>
<button type="button" onClick={this.setSocialId} { isSocialIdSetting ? 'disabled' : '' }>Set</button>
</div>
<div className="block">
<label for="inputGender">Your Gender:</label><br>
<input type="text" id="inputGender" { isSocialIdSetting ? 'disabled' : '' }>
</div>
<div className="block">
<label for="inputAge">Your Age:</label><br>
<input type="text" id="inputAge" { isSocialIdSetting ? 'disabled' : '' }>
</div>
<div className="block">
<label for="inputSexualOrientation">Your Sexual Orientation:</label><br>
<input type="text" id="inputSexualOrientation" { isSocialIdSetting ? 'disabled' : '' }>
</div>
</div>
<button type="submit" onClick={this.submit} { (isSubmitting || isZipSetting || isCarNumberSetting || isSocialIdSetting) ? 'disabled' : '' }>Submit</button>
</div>
</form>
);
}
});
Послесловие.
Если у вас возник вопрос, зачем такие очевидные вещи писать в javascript-хабе, ведь React-одепты итак достали уже абсолютно всех, то у вас очень резонное замечание, потому что они конкретно достали и меня тоже. Но я не смог забить на это после того как встретил непробиваемое невежество в мирном с виду топике, приведя там похожий код. После чего не поленился и написал эту статью с примерами. Самый жир:
Пишу на BB давно и много. От прямой манипуляции с DOM отказался сразу, при этом давления со стороны библиотеки не испытал.
Не ради холивара, но поймите ничем особым ангуляр или реакт или еще что либо не лучше и не хуже бекбона. Главное понимать что, как и почему так работает.
PS Да реакт клевый, было время я думал на него перевести приложения. Но, стильно/модно/молодежно — оставим для новичков. После глубокого анализа — бенефит был нулевой.
Если вы не умеете готовить бекбон — это ваши личные проблемы.
Мне даже шаблоны не нужны. А вот вы нагородили кучу всего. Повторюсь, вникните в суть фреймворков.
P.S.
- Если вы считаете, что я привел слишком редкий для разработки кейс, то приведите примеры нередких кейсов.
- Если вы считаете код на Marionette предвзятым, напишите свой код, лучше и проще, я сделаю апдейт поста.
- Если вы по своей воле выбираете Backbone-like фреймворк для старта вашего проекта в 2016 году, то мне очень интересно узнать, почему вы так поступаете.
Update.
В процессе общения варианты попросить дизайнеров поменять дизайн формы или пойти посмотреть какой-то посторонний код я не рассматривал, потому что демо-пример достаточно простой чтобы можно было ясно выразить свою мысль в коде не прилагая больших усилий.
За время обширной дискуссии только два человека не поленились и подтвердили свои слова делом, за что им спасибо:
— Решение с epoxy от alexgrom
— Решение с View-оберткой от Delphinum
Я еще не успел нормально разобраться в коде их примеров и понять их плюсы и минусы, поэтому добавлю свой комментарий чуть позже. Но то, что отличные от моих примеров решения есть — это факт.
Комментарии (247)
napa3um
09.05.2016 08:14+12Ничего не мешает в приложении на бэкбоне в качестве слоя V использовать knockout.js, vue.js, virtual-dom.js или даже сам реакт (который тоже является лишь слоем V), чтобы избежать «ручных» операций с DOM. Бэкбон же не про работу с DOM, он про структуру приложения, в Backbone.View можно подключить любой удобный шаблонизатор.
Описываемые в статье различия касаются не «поколений фреймворков», а, скорее, подходов к организации логики приложения — функциональное реактивное программирование (основанное на потоках данных), либо императивное (основанное на последовательностях операций). И выбор между ними не ограничивается одними лишь DOM-манипуляциями. И у каждого подхода могут быть свои плюсы и минусы (для одного приложения может оказаться удобным один подход, для другого — другой).Synoptic
09.05.2016 08:22-7Никто не мешает, хотя минусы у этого решения тоже есть. Только в статье речь не о конструкторе, а о минусах конкретного фреймворка — MarionetteJS.
napa3um
09.05.2016 08:26+43Статья «Почему нельзя использовать Backbone.js в 2016 году» рассказывает о минусах конкретного фреймворка — MarionetteJS. Логично.
Synoptic
09.05.2016 08:28-2Статья о минусах целого класса фреймворков, поощряющих манипуляцию DOM. В качестве примера выбран MarionetteJS. Backbone+React — это интересная тема, но что там остается от Backbone, кроме моделей и коллекций? Это все-таки совсем другая тема для разговора.
napa3um
09.05.2016 08:41+12Вы явно не в курсе MVC/HMVC/MVVM и прочих непонятных букв из документации фреймворков. Для вас существует только V, а все фреймворки, на ваш взгляд, нужны лишь для того, чтобы заменить jQuery в операциях с DOM-ом. Расхожая точка зрения среди начинающих программистов, пришедших из верстальщиков.
Synoptic
09.05.2016 13:56Это не так. В статье рассматривается один конкретный кейс, именно с V. Не потому, что я не знаю про остальное, а потому, что одного этого кейса многим достаточно, чтобы принять решение о выборе фреймворка перед стартом проекта.
napa3um
09.05.2016 14:18+8Возможно, вам стоит написать статью «Не нужно использовать MVC-фреймворки, когда вам нужна всего лишь библиотека биндингов с DOM». И описать ваш подход к построению веб-приложений. Это было бы убедительнее, чем эмоции в ^этой^ статье.
Synoptic
09.05.2016 14:21Тоже неправда. Речь об MV* фреймворках, а не только про вью. Вью — это тоже часть фреймворка, и она тоже может создавать проблемы при разработке. И по этой части тоже можно определять выбор фреймворка, она важна.
babylon
09.05.2016 15:01+3MVC это паттерн. Либо Вы умеете им пользоваться, либо нет. Вы не умеете, потому что не понимаете. DOM это уже часть вида и часть данных для браузера. Это надо иметь ввиду. Относитесь к DOM как к контейнеру. Следовательно контейнер может содержать код в виде JS и «биндинги». Всё остальное это кастомизация. ReactJS предлагает программный рендер модели именно для того чтобы вы не делали это руками. Недостаток ReactJS это невозможность его перепрограммирования ибо это уже код. И код будет плодиться как от проекта к проекту так в рамках и одного проекта. От этого никуда не деться пока код не станет частью данных, а не данные и вид будут частью кода. Но это уже JSONNET а пока юзайте то, что есть.
Synoptic
09.05.2016 15:17-3Вы слишком глубокий теоретик, судя по вашим комментариям, чтобы мне, больше практику, втягиваться с вами в беседу — как-то страшновато.
Я в этом топике ищу решение практической конкретной задачи. Так что если у вас оно есть — приведите, если нет, я пойду мимо.
maxpsyhos
09.05.2016 09:07+13>> но что там остается от Backbone, кроме моделей и коллекций?
Ну, вообще-то это ПОЛОВИНА фреймворка, причём часто именно ради неё и выбирают. Неотделимым комплектом идут система событий и изкоробочный REST-клиент.
Ещё есть роутинг, но его используют только на приложениях-одностраничниках, так что он и так не везде нужен.DriverEntry
09.05.2016 11:40+3Не говоря уже про беднягу Marionette, который шире Backbone, но от которого в статье используется только @ui.
Synoptic
09.05.2016 13:58-2Ну так покажите, как правильно использовать MarionetteJS, если я слишком узко его использую. Я же не гоню на этот фреймворк, я пытаюсь выяснить альтернативные решения. С моей точки зрения на марионетке сделать проще и лучше чем с использованием реакта такое нельзя.
leschenko
09.05.2016 17:44+3Мы используем backbone ради моделей, коллекций и событий. Т.е. только M. VC нет, т.к. используем VVM от knockout. И я не вижу смысла отказываться от текущей реализации M (в моем 2016), только потому что кому-то не нравится V из этого фреймворка.
vintage
09.05.2016 17:47А чем реализация M из KnockOut не угодила?
faiwer
09.05.2016 17:52А чем реализация M из KnockOut не угодила?
А разве она там как таковая расписана? По большому счёту KnockoutJS говорит разработчику: вот тебе разные виды observable's, вот тебе удобные binding-и для DOM-а, вот тебе component-ы для DOM-а, используй их как тебе будет удобно. Мне не совсем ясно, а где тут конкретная M?
vintage
09.05.2016 17:59Из этих самых observable's и получается отличная M.
Ну, грубо говоря:
class UserModel { data = ko.computed( () => getOrLoadData() ) name = ko.computed( () = > this.data()['name'] ) age = ko.computed( () => this.data()['age'] ) }
Или как-то так, я в KO давно ковырялся.
faiwer
09.05.2016 18:03Из этих самых observable's и получается отличная M.
Я о том же. Это отдаётся на откуп разработчику. Тут нет ни требований, ни ограничений, ни knockout-way. Как хочешь так и крутись. Knockout просто поставляет набор различных observable's. А как разработчик выстроит архитектуру — это его трудности, либа тут не причём. Вот leschenko для этого, видимо, использует Backbone. Он ж для этого и задумывался, разве нет?
vintage
09.05.2016 18:14Просто не вижу смысла париться с backbone, если observables использовать проще и гибче.
leschenko
09.05.2016 18:18Модели надо как-то сохранять на сервер. Их надо как-то получать с сервера.
Backbone в этом случае предлагает удобный REST.
А чем здесь поможет observables из knockout?vintage
09.05.2016 18:23Ничем. Если у вас сервер сделан под backbone модели, то разумеется их использовать проще. Во всех остальных случаях используется мост для абстрагирования модели от протокола. И тут уже не важно к бэкбону пилить этот мост или к нокауту.
leschenko
09.05.2016 18:15Knockout отлично подходит для создания ViewModel. И биндинга всего этого на UI.
Backbone предоставляем модели, которые «умеют REST делать красиво».
А View делается руками и практически всегда только «запускает» Knockout, который через VM дергает Backbone.
О том, какое именно View использовать — говорит Route из того же Backbone.
Разные View между собой общаются через Events из того же Backbone.vintage
09.05.2016 18:19Нокаут отлично подходит для любых моделей (если закрыть глаза на необходимость троттлингов) и моделей отображения в том числе. Его основная функция — синхронизация состояний. M <-> M, M <->VM, VM <->VM, VM <->V.
А вы получается скрестили ужа с ежом :-)leschenko
09.05.2016 18:33+1Мы никого не крестили. Мы используем и backbone и knockout по их прямому назначению.
Использовать всякие observable для модели просто нет смысла, т. к. модель не должна быть связана ни с вью, ни с вьюмодел. Более того, модель чаще всего используется для общения с сервером. И по идее только она и должна использоваться для этой цели. накаут эту задачу никак не решает, а бекбон делает это легко и изящно.vintage
09.05.2016 18:39-1Отображение должно быть связано с моделью. observable позволяет реализовывать эту связку легко и просто. Или у вас так называемая "глупая модель"?
leschenko
09.05.2016 19:03+2модель не должна быть связана с чем либо вообще. она обязана быть «глупой». иначе это какая-то ересь получается (все что угодно, но не mvvm).
связанными могут быть только view (dom) и viewmodel.
биндингов между v — m, vm — m, v — v, vm-vm, m-m быть не может. только v — vm
nwalker
10.05.2016 12:22react/virtualdom.js — мешает мутабельность бэкбоновских коллекций. скрещивать мутабельные коллекции с реактом — занятие для мазохиста.
maxpsyhos
09.05.2016 08:18+8Извините, но у вас какая-то фигня.
Во-первых, вы используете два разных подхода. В первом рендерите исходную форму, а потом все изменения делаете во вьюхе. Во втором — изменения делаете в модели и перерендериваете всю форму целиком. Backbone, внезапно, тоже так умеет, только у него модель — это отдельная сущность и её надо задавать явно.
Во-вторых, не понятно, зачем вы КАЖДОМУ элементу делаете отдельную ссылку, если их можно делать коллекциями через служебные классы типа «zip-input car-input» и т.д.
В третьих — в обоих случаях приличная часть кода никак не зависит от представления — сериализация формы (которую вы тактично опустили) и взаимодействие с сервером.Synoptic
09.05.2016 08:30-2См. конец статьи. Приведите код, решающий задачку. Если будет по-существу, я конечно проапдейчу пост.
DriverEntry
09.05.2016 11:33+1Тут действительно нет смысла приводить код. Посмотрите для примера эту библиотеку для двусторонней привязки модель-представление https://nytimes.github.io/backbone.stickit/. Я её часто использую для форм. Грубо говоря, она будет делать за вас то, что вы делаете руками в своих примерах — изменяет dom без полной перерисовки представления. Но только реактивно, по событиям модели. Модель также обновляется по событиям формы, т.е. вы получаете и сериализацию тоже.
AndreyRubankov
09.05.2016 08:18+2Вы часто делаете формы ввода сложнее, чем второй пример с выбором локации по ZIP?
Если пользователю дать форму приведенную последней* он вас возненавидит! И ему будет плевать, что у вас там под капотом Крутой Реакт или Плохой-ББ ;-)
*- habrastorage.org/files/ec9/814/337/ec98143373b74e54820851c91d572901.png
А вот для формы из второго примера — без разницы какой фреймворк использовать, тут можно даже на Ванильном JS все сделать, будет даже быстрее =)
Ах, да, есть варианты, где React не уместен по тем или иным причинам: e-commerce, как вариант.Synoptic
09.05.2016 08:32Увы, я UI developer, а дизайнер уже потом — мне не дают принимать решения, какие формы реализовать. То что в примере — далеко не самый худший случай из практики.
Не использовал react в e-commerce, почему он там не уместен?AndreyRubankov
09.05.2016 08:56Прошу прощения, имел ввиду Redux, пока аппрувили коммент, истекло время для редактирования.
Для e-commerce в виде интернет-магазинов на много выгоднее использовать максимум статического контента для продвижения в поисковых выдачах. Идеальный вариант, это когда каждый продукт имеет свою собственную страницу, и поисковые выдачи можно оформлять как отдельные страницы (по крайней мере без ajax прогрузки первой страницы выдачи).
Подход старый, очень неприятный для многих современных разработчиков, но тем не менее более действенный.
SPA хоть и выглядит шикарно, но в данном случае для результата будет лучше использовать старые технологии. А прикручивать SPA стек там, где он не нужен, согласитесь, не лучшее решение.Houston
09.05.2016 13:32На всякий случай вставлю, что react-views можно рендерить на сервере.
VasilioRuzanni
09.05.2016 16:24react-views просто использует React в качестве движка шаблонов на сервере (они и сами говорят, что он не имеет особого отношения к Реакту на клиенте). А так — node.js-сервер может рендерить Реакт-преложение (или отдельные компоненты — как угодно), а это да — полностью решает проблему серверного пре-рендеринга.
devian
09.05.2016 13:53Ну, все-таки, это все решается и на react+redux.
Серверная генерация кода + SPA с полноценным роутингом и сменой урлов. В итоге у вас у каждого продукта свой урл, весь код сразу доступен при загрузке страницы без javascript и все это SPA. У медузы, например, примерно так и сделано (meduza.io), а им видимость в поисковых системах нужна не меньше, а то и вовсе жизненно необходима.
Yozi
09.05.2016 13:53+1Оу, но вообще-то на React можно написать изоморфное приложение, такое, что оно и поисковому роботу отдельную полностью отрендеренную страницу отдаст, и у пользователя SPA останется. И всё это без несколько костыльного, на мой взгляд, PhantomJS на сервере, который часто предлагается применить для марионета.
github.com/webpack/react-webpack-server-side-example — очень простой пример.
xakepmega
09.05.2016 13:53можно изначально разрабатывать SPA с возможностью рендеринга на сервере, или же написать crawler на phantomjs который будет рендерить всю страницу и отдавать результат работы
indestructable
09.05.2016 13:53Так реакт как раз и уместен, на нем можно организовать серверный рендеринг наименьшими (среди других клиентских джаваскриптовых фреймворков) усилиями и без ужасов браузера-на-сервере.
svsool
09.05.2016 13:53Связка React + Redux + React Router умеет рендерить статику на сервере (aka SSR) и отдавать ее в готовом виде клиенту, первый рендер со всеми данными делается на сервере, все последующие рендеры делаются на клиенте, фетчинг данных на сервере реализуется через статические методы компонентов. Поэтому с e-commerce все впорядке.
Synoptic
09.05.2016 13:54Извините, что поздно отмодерировал, и получилось столько одинаковых мнений. Каждое из них — независимое :)
koceg
09.05.2016 08:32А в чём принципиальное отличие BB от React с точки зрения e-commerce?
AndreyRubankov
09.05.2016 09:12Как написал в комменте по другой ветке, имел ввиду Redux + e-commerce, но и на текущий вопрос могу ответить:
На данный момент повезло (или не очень) работать на проектах с AEM (бывший Adobe CQ), у этой платформы своя компонентная модель (java + jsp у CQ 5, и java + sightly у AEM), которая хранится в репозитории самой платформы и отдается как статика.
На данный момент есть небольшой опыт по использованию lodash+bbq+dust в одном проекте и BB (как набор моделей и некий pub\sub для общения) в другом.
По ощущениям BB использовать в текущих условиях приятнее: отсутствие кодогенерации из шаблонов при сборке дает преимущество во время разработки и отладки (не нужно делать редеплой, чтобы сгенерировать новый шаблон и залить его в репозиторий платформы и при необходимости можно непосредственно в репозитории сделать правку).
Компонентная модель реакта очень привлекательная, но в данном случае не очень применима: придется прикручивать компонентную модель одного фреймворка к компонентной модели другого, а это всегда чревато проблемами.koceg
09.05.2016 11:09+1Ну, это всё не с точки зрения e-commerce, а с точки зрения вашего конкретного стека. Но я понял вашу мысль.
VasilioRuzanni
09.05.2016 16:29Ну, неприменимость React в вашем данном случае — это отдельная история.
Но отдельно хотелось уточнить — чем вот именно Redux «не подходит»? Это же просто контейнер состояния клиента, который можно смело заполнить на сервере.
mr47
09.05.2016 10:30+4Троллинг в конце как мне кажется лишний.
По сути. BB можно использовать в 2016 вполне себе. По скорости он вполне адекватен и сопоставим (react/angular). Я — как очень маленький участник сообщества но активно следящий за BB. Могу сказать что будущего у BB пока нет. Сам автор BB (Джереми) если кто не в курсе уже не занимается, там только несколько человек все еще «разрабатывают» и лидер пока что Джастин.
Будущее туманно, но есть лучи озарения.
PS: Насчет react'а:
Компонентный подход слишком удобен для больших команд и продуктов. Reuse очень и очень велик таких компонентов (учите что нужно верно разбивать на компоненты иначе такого не достичь).
PS2: Сам начинал на BB, сейчас использую react. Если технология развивается — развиваемся и мы, что означает — выигрывают все.
Miraage
09.05.2016 11:41+3Вообще-то, чтобы выключить несколько элементов, можно на них повесить общий класс, дабы уменшить кол-во селекторов.
А если необходимо выключить вообще всю форму — после формы сразу должен идти тэг fieldset.
boodda
09.05.2016 11:44+7Как то странно вы все сделали в первом случае вы натыкали кучу кода во вью, а во втором унесли все в шаблон, и заново его перерендериваете целиком, ну так вот вам вопрос: а почему тот же шаблон нельзя было использовать с ББ и рендерить шаблон заново при изменении данных в модели(или сразу нескольких моделей от которых зависит форма?
В чем конкретно вся соль вашего решения проблемы?MiKXMan
09.05.2016 13:22На маленькой форме мы можем себе позволить рендерить шаблон заново при изменении данных, но в целом это плохой подход для производительности.
Соль решения на React в том, что можно писать код в стиле «рендерить шаблон заново», а как эффективно его обновить в DOM — забота не программиста, а Реакта.
Synoptic
09.05.2016 13:48-1Нет соли у этого решения. Это то, как решается задача. Решается не только мной, судя по моему опыту — я часто вижу такой код. Я понял ваше решение, не рассматривая даже проблем с медленным перерендерингом(морганием) больших форм, оно требует двойного связывания для нормальной работы, и оно явно не проще примера с реактом.
Если вы знаете, как сделать то что вы описание без двойного биндинга, и при этом будет проще чем с реактом — напишите код, очень интересно посмотреть.boodda
09.05.2016 14:42+2В данном примере все решается декомпозицией, вашу гигантскую форму надо делить на логические куски, где каждый кусок будет обладать своей моделью и своим шаблоном(а может и подчинённым вью), и все это сводится в четкую, ровную и понятную иерархическую структуру.
Главное не сорваться на манипуляции DOM. Вью рендерит шаблон исходя из начального состояния модели связанной с этим вью и подписывается на события которые происходят в отображении(click, change, submit), события всплывают вверх и вью изменяет нужную модель, а так же вью следит за изменением модели и перерендеривает нужный шаблон.
Тут два процесса:
первый: template event->view->model change
второй: model change->view->template render
Даже для такой большой формы все будет тривиально и очень понятно, даже начинающему.
Проблемы появляются именно тогда, когда кто то начинает думаать в стиле, а че я заново буду рендерить этот кусок, тут всего то надо класс затоглить.
и все… реализация этой мысли будет фатальной ошибкой. Если приложение где то вначале пути или активно развивается. так как эти допущения будут разноситься как инфекция.Delphinum
09.05.2016 14:45Я об этом уже писал автору (https://habrahabr.ru/post/283054/#comment_8885102), но автору важнее то, что Backbone не имеет из коробки обертку над механизмами манипулирования DOM (она ему и не нужна), чем то, как с помощью Backbone декомпозировать, структурировать и манипулировать моделями и JS классами (для чего он собственно и создавался).
Synoptic
09.05.2016 15:47В том-то и дело, что декомпозиция формы возможна далеко не всегда. Уж точно это касается разбиения на подчиненные вью, и часто касается декомпозиции вообще.
Я привел самый тривиальный пример, чтобы донести мысль, а хитросплетение мыслей в сумрачном разуме людей под названием UX-дизайнеры может быть совсем не таким логичным, как программистское мышление.
Без разбиения на сабвью форму придется перерендеривать целиком. Это решение, безусловно, и наверное его стоит добавить в апдейт для статьи — если хоть кто-то наконец приведет уже код, но оно точно не лучше решения с реактом, который делает перерендеринг идеально. Плюс, это, как вы сами отметили, не самое тривиальное решение, и люди не разобравшись в нем могут легко его сломать.boodda
09.05.2016 18:24Мне кажется, вы как то, не так как я, представляете себе рендеринг в ББ. Типичный реднеринг сводится к следующему
```
var HugeForm = Backbone.View.extend({
events: {
'change .js-block1 input[name=«user_name»]': changeModel1UserName,
'change .js-block2 input[name=«zip»]': changeModel2Zip
},
initialize: function(){
this.main_model = new MainModel({});
this.main_tpl = _.template(main_tpl);
this.listenTo(this.main_model, 'change', this.renderMain);
this.model1 = new Model1({});
this.model2 = new Model2({});
this.model3 = new Model3({});
this.form_part1 = _.template(tpl1);
this.form_part2 = _.template(tpl2);
this.form_part3 = _.template(tpl3);
this.listenTo(this.model1, 'change', this.renderFormPart1);
this.listenTo(this.model2, 'change', this.renderFormPart2);
this.listenTo(this.model3, 'change', this.renderFormPart3);
},
changeModel1UserName: function(event){
this.model.set('user_name', $(event.target).val());
},
changeModel2Zip: function(event){
this.model.set('zip', $(event.target).val());
},
renderMain: function(){
return this.$el.html(this.main_tpl(this.main_model.toJSON()));
},
renderFormPart1: function(){
return this.$el.find('.js-block1').html(this.form_part1(this.model1.toJSON()));
},
renderFormPart2: function(){
return this.$el.find('.js-block2').html(this.form_part2(this.model2.toJSON()));
},
renderFormPart3: function(){
return this.$el.find('.js-block3').html(this.form_part3(this.model3.toJSON()));
},
render: function(){
this.renderMain();
this.renderFormPart1();
this.renderFormPart2();
this.renderFormPart3();
return this;
}
});
```Synoptic
10.05.2016 08:14Это решение работает, если разметка формы бьется на четкие части, как вы делаете в этом коде:
this.form_part1 = _.template(tpl1); this.form_part2 = _.template(tpl2); this.form_part3 = _.template(tpl3);
…
this.renderFormPart1(); this.renderFormPart2(); this.renderFormPart3();
А если нет? Если мы имеем чересполосицу инпутов, которые надо блокировать? Перерендерить каждый?boodda
10.05.2016 09:55Что я могу сказать по этому поводу?
Наверно если форму нельзя разделить на четкие части, по семантике, или еще чему-то, то тут либо надо пересмотреть форму, либо смириться и сделать немного грязно. Ведь абсолютного решения для всего и вся нет, ну просто нет и все, это не проблема, это данность. надо просто смириться и сделать максимально понятно, пусть полотно кода, пусть 10 файлов. Напишите комментарий в котором вы просто поясните проблему и в чем суть вашего решения этой проблемы.
Лично моё мнение грязный хак или костыль в ББ будет намного понятнее, чем такой же хак в ангуляре.
Кстати когда у меня, как у программиста, возникают проблемы с тем, что дал дизайнер, я иду к начальству и говорю о проблеме.
Описываю возможные решения. Начальство зачастую понимает мои доводы, мы берем дизайнера и смотрим, задаём себе вопрос, может что то не так спроектировали. или что то не так нарисовали. Это же нормально, люди делают работу в потоке, и могут не увидеть очевидного, забыть о какой то вроде как мелочи, а на деле важном моменте. Или они просто не знают о других подходах. Все бывает.
В ваших руках показать им что возможно существуют другие решения.Synoptic
10.05.2016 10:52+1Я описал проблему в посте :) Высокая и сложная связность состояний компонентов формы. Видимо, нужно было сразу привести пример как раз с такой формой, которую неудобно разбить.
Пересмотреть форму бывает просто нельзя. Часто бывает, что нет обратной связи с дизайнерами. Реже бывает, что есть, но они не поймут ваши доводы в пользу переделки формы. Хорошее взаимодействие дизайнер-девелопер бывает практически никогда. В любом случае на это уйдет время. А на реализацию функционала есть сроки.
Я показал уже, что используя Backbone, по крайней мере так как это делаю я в своих примерах, эта проблема становится существенной при росте сложности формы. А с React-like фреймворками — нет.
Пока лучшее, что показали — это решение с двойным биндингом с доп. плагином epoxy, я укажу это в посте, но сначала хочу хорошо его рассмотреть. Смогу сделать это только вечером.oxidmod
10.05.2016 13:54+1>> описал проблему в посте :) Высокая и сложная связность состояний компонентов формы. Видимо, нужно было сразу привести пример как раз с такой формой, которую неудобно разбить.
приведите, но не с абстрактными полями, а реальный кейс. вполне возможно, что нужно просто вправить руки вашему UX специалисту.Synoptic
10.05.2016 22:16Пост уже умер, он слишком тяжелый, чтобы обсудить еще и этот кейс. Но ок, я подберу показательный пример и закомменчу сюда же позже.
Вправить руки не всегда возможно. Да, надо стремиться к взаимодействию между UX и UI dev team, надо пытаться что-то исправить, но не все работают в продуктовых компаниях, где вероятность этого выше. Я работаю в энтерпрайз секторе далеко не с самыми худшими штатовскими заказчиками, и поверьте уровень дизайнеров там реально низкий, а из офшора что-то советовать затруднительно(но можно).oxidmod
11.05.2016 06:04Глупая отговорка. Библиотек не виновата в низком уровне кадров.
Synoptic
11.05.2016 10:29Это не отговорка. Такие описанные проблемы возникают не только в случае плохого дизайна, хотя очень часто — из-за них.
oxidmod
11.05.2016 13:40но реальный кейс вы так и не привели. я на 99% уверен, что любую сложную форму можно распилить на простые, логичные составные части.
зы. то что нельзя распилить форму, потому что нету обратной связи с дизайнерами — это проблема организации рабочего процесса, а не бекбона.Synoptic
11.05.2016 13:43Мы о разном говорим. Вы говорите о том, как должно быть в идеале, я говорю о том, как бывает на самом деле обычно. И что один инструмент позволяет решать подобного рода задачи лучше другого.
Реальный кейс я подберу и приложу, когда появится время, я выше написал.
Nadoedalo
09.05.2016 11:59-1Блокировать поля ввода в 2016? Пффф.
onClick в 2016 это, конечно же, лучший вариант решения.
Кому нужны манипуляции с ДОМ вручную если можно написать правило и описать условия в моделе?
Как вы будете делать редизайн/наследование логики, если у вас эти формы повсюду ± пару полей?
Вообщем, не нужно наезжать на Backbone попивая молоко у себя в React. Примеры не очень, всё описанное можно сделать и на Backbone. Да и Backbone не про манипуляции с DOM, он про организацию приложения.
У Backbone есть свои минусы, в частности зависимости от underscore и jquery-like либки, плюс в проекте он обростает всякими requireJS с плагинами, иногда «для простоты» подключается jqueryUI(местами ненавижу себя за это решение. Ребятки, JqueryUI это плохо в long-term проектах) а потом что бы перенести один модуль приходится тащить с собой 15 файликов и организовывать инфраструктуру на месте.
Ну и код обычно либо качественнее нужно писать либо больше. В зависимости от прокладки между стулом и клавиатурой. По моему опыту — больше кода = больше кода и геммороя. Лучше написать мало кода
Заголовок спойлераА потом искать ошибку через 5 слоёв абстракции только для того что бы понять что ошибка в коде библиотеки от 3-х лицSynoptic
09.05.2016 13:45Много писать не буду, мнение понял, только спрошу — как вы предлагаете обходиться без блокирования кнопок и форм, тоесть без промежуточных состояний во время выполнения асинхронных запросов?
Nadoedalo
09.05.2016 20:12Нужны «автодополнения» — рисуй в уголку/возле инпута крутилку и ОБЯЗАТЕЛЬНО убивай вызов при blur/focusout(что бы очень быстрому человеку-роботу не вывалило всё разом).
Нужно организовать переход на другой экран? Сделай прелодер на текущую часть экрана и НЕ ТРОГАЙ навигацию/не относящиеся к отправке данных форме. Можно как ютуб, рисовать полоску-прогресс бар вверху экрана.
Данным, в принципе уже пофиг на то что там в форме изменится когда пользователь их уже отправил. А ещё можно поплевать на блокировку случайного двойного запроса одних и тех же данных со стороны юзабилити и решить эту проблему со стороны принимающего АПИ.
А если бэк такого делать не хочет то очень просто — тупо переносим пользователя на следующий экран, делается это моментально и ждать ничего не нужно(правда возникают вопросы с поддержкой ошибок, хотя это «проверенный веками» способ).
Вообщем вариантов — миллион. А блокировать ввод/отзывчивость интерфейса — это плохая идея. Пользователи бывают разные.Synoptic
10.05.2016 07:26>Данным, в принципе уже пофиг на то что там в форме изменится когда пользователь их уже отправил.
Понятно, вы у вас подход программиста, а не дизайнера. Убивать юзабилити приложения таким образом ни один нормальный дизайнер не даст. Пользователя нельзя оставлять без реакции со стороны приложения при выполнении задач, выполняющихся более 150мс. Любой учебник по UX дизайну.
Delphinum
09.05.2016 14:16У Backbone есть свои минусы, в частности зависимости от underscore и jquery-like либки, плюс в проекте он обростает всякими requireJS с плагинами, иногда «для простоты» подключается jqueryUI
Это не минусы Backbone. Пакет весит копейки, потому имеет зависимости от сторонних пакетов, и это нормально (для меня это даже хорошо). К примеру, если вам нужен от Backbone только клиент REST, то это сильно облегчит вам задчу, так как достаточно будет подключить всего две зависимости (можно даже обойтись без jquery like), а не тянуть зависимости, связанные с отображением. Разбейте тот же Angular на пакеты и у него появятся свои зависимости.Nadoedalo
09.05.2016 20:19Привожу пример. У меня модульный проект на backbone-jquery-jqueryUI-requireJS-polyglotJS-underscore и ещё с пяток либ для разного фукнционала.
Так вот что бы дать возможность людям попользоваться моими модулями в ДРУГОМ проекте — мне приходится переносить почти всю инфраструктуру(шаблоны, переводы, вьюхи-модели-коллекции-config-main.js-app.js-templateHelper.js-commonViewMethods.js и т.п) просто что бы оно заработало. Внутри проекта меня ВСЁ устраивает, а вот между проектами возникают проблемы с тем что что бы ОДИН модуль заработал приходится тащить кучу файликов.
Пока не сообразил как писать так что бы модули можно было спокойно отдавать «как есть»(модель-вьюха-коллекция + вьюКоллекции и т.п вещи) т.е чисто необходимую логику а все зависимости подтянулись бы или были бы встроены.
Т.е реально каждый раз когда другой проект хочет кусок кода — им приходится подключать ВСЮ ключевую инфраструктуру просто что бы получить ОДИН модуль.
Как пример у нас есть бэк на erlang где у человека ~70 репозиториев с разными модулями, которые полностью независимые и могут быть переиспользованы не только внутри проекта но и между проектами.
Понятно что тот же Angular тупо написал свои велосипеды, и мне нравится подход Backbone в плане модульности ВНУТРИ проекта. Но между проектами получается неудобненько.
anatooly
09.05.2016 12:07Хорошая статья. От себя хотел бы добавить.
1) Backbone — является фреймворком из коробки, дающий возможность сразу разбить приложения на некоторые логические части и использовать роутинг. React — это только Backbone.View по сути;
2) Результирующий размер JS файлов на выходе мне кажется с Backbone (и либами) будет в разы меньше, не смотря на возможно большую простыню кода. Более простой порог входа, так как если ориентироваться на React & Angular2 надо уже применять все новые фишечки JSX/ES6 и компилить приложение;
3) Для текущего примера от Backbone правильнее было бы использовать какой-либо 'Two Way Data Binding' плагин и при изменении параметров модели перерисовывать весь темплейт, куча логики бы ушла в шаблон, аналогично React;
4) К своему стыду мало знаком с Marionetta, но текущий пример вовсе не требует ее наличия (плюс заголовок статьи говорит о чистой «кости»);boodda
09.05.2016 22:00-2Backbone это все таки библиотека, а не фреймворк. Ключевое отличие, С библиотекой ты говоришь, что хочешь и она делает, а фреймворк говорит тебе как ты должен это делать.
В первом случае это кроткая проститутка, во втором bdsm госпожа
Synoptic
09.05.2016 12:44+2Все, кому не нравятся мои примеры. Напишите свое решение, посмотрим на него. Простой де пример, займёт минут 15.
Что значит «не имеет смысла приводить пример» — везде имеет, а тут, значит, не имеет. Интересная логика.
mr_molodoy
09.05.2016 13:37+1Не хотелось бы начинать комментарий с критики, поэтому сначала, скажу что согласен с Вами. Использовать манипуляции DOM в 2016 году глупо.
Однако, а теперь критика, подбор фреймворка, в большей мере (лично для меня, а не общепринятый факт), это выбор инструмента для описания бизнес логики самого приложения. Вы же описали View компонент, который к бизнес логике отношения никакого не имеет. Более того, никогда не чувствовал проблем с интеграцией в backbone стороннего View, более того сам фреймворк это поддерживает, как говорится «из коробки».
PS: Мне кажется что заголовок и некоторые выдержки из статьи сильно громкие — Вы громко кричите, но не о том, что хотите сказать на самом деле.
Думаю, что начиная от заголовка Вы описываете не сове мнение, либо его искажаете. Но, блин, Вы делаете это так громко!Synoptic
09.05.2016 13:44-3Заголовок для привлечения дополнительного трафика, каюсь :) Очень хотелось получить фидбек.
mihaChiken
09.05.2016 13:37Думаю что код действительно является немного предвзятым, ведь если бы Вы выделяли отдельный класс в ДОМе для полей, которые будут деактивированы по нажатию на какую-либо кнопку (disable-on-set-zip, disable-on-submit), то длинные полотенца в toggleZipInputs можно будет заменить на одну строчку:
this.ui.disableOnSetZip.prop('disabled', value);
Я сам уже несколько проектов написал с помощью React, а сейчас по стечению обстоятельств снова пишу на Marionette. React действительно классный инструмент и я согласен, что он имеет множество преимуществ перед jquery подходами, но как по мне Backbone хоронить ещё рано.
P.S. Вы обещали сделать апдейт поста :)Synoptic
09.05.2016 13:39Обещал, и сдержу обещание. Более того, проапдейчу примерами, даже если код будет не лучше или хуже чем мой явно не лучший пример с ui {} ссылками на каждый контрол. Уже хочется просто посмотреть на альтернативные решения.
Только сделайте нормальный пример кода, у меня же семплы нормальные, а не просто на словах расписанные.
superartgun
09.05.2016 13:37Думаю сравнивать Backbone и React это совсем не инженерный подход. Вы сравниваете MVC библиотеку с V библиотекой, а упомянутый Angular так вообще фреймворк. Если говорить о формах конечно React или Angular оставляют BB позади, но если говорить о масштабируемости архитектуры имхо BB оставляет вышеупомянутых позади. У каждого инструмента свой класс задач и область применения. Мы ведь не выкидываем отвертки если покупаем шуруповерт.
Synoptic
09.05.2016 13:42+1Нет, я сравниваю Backbone/Marionette либо другой фреймворк, в котором нужно писать руками код типа .addClass('active') и React+какой нибудь контроллер, например Redux, где этого не надо делать. Я это указал в статье.
Причем это не сравнительный обзор фреймворков, а разбор всего лишь одного кейса. Но очень типового кейса в повседневной разработке.Delphinum
09.05.2016 14:11Backbone + React, и о чудо, не нужно работать с DOM.
Synoptic
09.05.2016 14:18Цитата, самое начало поста:
Она призвана с помощью небольшого примера показать один серьезный недостаток фреймворков прошлого поколения и, в частности, BackboneJS с обвесом типа Marionette, вынуждающих программиста вручную манипулировать DOM-деревом
Предлагаете сравнивать React с React? Зачем это надо? Речь не о Backbone+React.Delphinum
09.05.2016 14:27Я предлагаю использовать для решения конкретной задачи тот механизм, который наиболее для этого подходит. Если вам очень по душе работать с представлением в контексте React, я предлагаю использовать свяжку Backbone + React, если же вам React не по душе, я предлагаю Backbone + VanillaJS или Backbone + Marionette. Если вам нравится «расширять» HTML, я предлагаю работать с Angular.
Synoptic
09.05.2016 14:32В посте конкретная задача. Приведите тот механизм на базе Backbone/Marionette(о нем речь в статье), который наиболее подходит к ее решению. В виде кода, а не слов. Слова все умеют писать.
Я свои примеры привел, от вас пока не видел ни строчки кода не видел ни в прошлом посте, ни тут.
VasilioRuzanni
09.05.2016 16:35А почему вы называете Redux «контроллером»? Он вполне однозначно определяется как «контейнер состояния» (сами рекламируют его как «predictable state container»). Причем тут контроллер?
Synoptic
09.05.2016 16:42По привычке и потому, что он очень похож на контроллер. По хорошему, надо, конечно, называть так как вы написали, но у меня язык не поворачивается.
VasilioRuzanni
09.05.2016 17:34А чем он на «контроллер» то похож? Обычно в архитектуре клиентского приложения контроллер — это что-то, отвечающее за подготовку данных для View в определенном контексте. Redux только хранит состояние и реализует структурированную смену состояния приложения (Flux), его можно вообще без View, React или чего бы то ни было использовать.
Synoptic
09.05.2016 17:43Хорошо. Как я и говорил, это дело привычки после других фреймворков и библиотек.
VasilioRuzanni
09.05.2016 16:40+1Есть один момент по поводу «React — это View».
На мой взгляд, самое распространенное заблуждение по поводу React — это то, что его называют «V из MVC». Чаще всего, конечно, в сравнении с Angular — мол, Angular это полноценный MV*, а Реакт — только View и поэтому их сравнивать нельзя. Не совсем так, ибо React — это, по сути, View + Controller (в виде компонента), а в Angular все равно нет нормального «M» ($resource не считается, все равно его почти никто не использует). То, что Angular добавляет из коробки всяких сервисов, i18n, фильтры, и прочее — это, к MVC, в общем-то, отношения не имеет. А все остальное к обоим все равно лепится внешними модулями/пакетами.
Ну, это так, к слову :)superartgun
09.05.2016 18:10Согласен. В принципе рассматривать React в контексте MVC даже не корректно, это обособленный компонентный подход. Тем более говорить что BB устарел в 2016 в противовес приводя React я считаю неправильно. Если бы автор привел к примеру Ember, я бы еще может и согласился бы с некоторыми оговорками что это более современная замена BB. Но опять таки Ember фреймворк со своей идеологией, а BB библиотека.
VasilioRuzanni
09.05.2016 20:28Да, это верно — в Бэкбоне не было компонентного подхода в таком виде.
Собственно говоря, у каждого фреймворка в чем то своя идеология, но все современные движутся в общем направлении — Ember тоже отошел от View и Controller в пользу компонентов. Angular 2, React, Aurelia — все построены на них изначально.
Delphinum
09.05.2016 14:09+1Надеюсь хоть в этой статье не сольетесь и ответите на мой вопрос:
Замечательно что вы это знаете, а чем они являются?
Synoptic
09.05.2016 14:17+1Конечно, сразу как только приведете пример своего решения :)
Delphinum
09.05.2016 14:24Значит что я зря надеялся. Ну ок, тогда другой вопрос — у вас есть такая задача: Web сайт отдает HTML в виде отрендеренного представления модели домена, тобишь все таблицы, списки, формы и т.д. от сервера отдаются в готовом виде, их не нужно перерендеривать на стороне клиента (JS). Все что нужно от JS, это разделить эти HTML страницы на блоки и восстановить из них как JS модели (модели представлений), так и навешать на них различные события. Другими словами на основе HTML кода нужно сформировать такой JS код, который будет одновременно структурирован (не лапша из jquery) и разделен на модули (не один файл парсинга HTML). К великому сожалению использовать Angular нельзя, потому что дизайнер занимается версткой и он против этого фреймворка, руководитель против этого фреймворка по своим идейным соображениям. Ваши предложения?
Synoptic
09.05.2016 14:28+1Мне, чтобы привлечь людей к обсуждению, пришлось написать в некоторой степени аргументированный пост. Сделайте тоже самое, приду, обсудим. Здесь — не хочу, почему я должен это делать если вы это делать отказываетесь?
Delphinum
09.05.2016 14:32-1Сделайте тоже самое
Я давно хочу обсудить эту, крайне интересную тему, но у всех сегодня праздник, а мне проект сдавать )
почему я должен это делать если вы это делать отказываетесь?
Вы мне предлагаете странную вещь, а именно — реализуйте вот такую задачу с помощью вот таких инструментов и получите что-то иное, нежели то, что вы привели )) Поверьте, моя реализация от вашей (возможно), будет отличаться либо количеством шаблонных решений, либо количеством классов, в остальном подход будет аналогичным. Так зачем это делать? В реальном проекте, вполне возможно, я бы не выбрал для реализации этой задачи Backbone, а взял бы Angular или даже VanillaJS. Ваша проблема в том, что вы не можете понять причину этого, скорее всего потому, что вы не можете понять чем отличается React от Backbone.Synoptic
09.05.2016 14:40+1У меня никаких проблем нет :)
Я предлагаю эту вещь не конкретно вам, а лишь тем, кто не согласен с посылом поста, кому не нравятся мои решения. Покажите свое, лучше, в виде кода, а не слов. Мы сравним, и или я посыплю голову пеплом, или тот, кто предложил. Что тут сложного?Delphinum
09.05.2016 14:43Покажите свое, лучше, в виде кода, а не слов
Так для конкретно этой задачи я не буду выбирать Backbone. Вам же выше уже объяснили, что Backbone это не только V. Зачем вы навязываете людям свое решение и пытаетесь их убедить в том, что если для этого решения Backbone не подходит, то он не подходит не для какого решения?
Мы сравним
Что сравним то? ))Synoptic
09.05.2016 14:52Я не навязываю, я показываю — что будет в коде. Как человек с нулевой кармой может кому-то что-то навязывать.
Убеждаю людей — потому что мной приведен типовой кейс. Любой UI developer, верстающий мало-мальски сложную форму и использующий тот же Marionette когда-нибудь столкнется с такой ситуацией. И придется ему менять вьюхи на реакт, лезть в дебри epoxy, и прочее прочее. Когда проблем такого класса можно вообще избежать.
Этот типовой кейс, кстати, называется — спиннер. Блокировка инпута на время запроса — вариант спиннера, он показывает пользователю, что что-то происходит, и показывает момент завершения этого «что-то». Спиннер присутствует в любых пользовательских интерфейсах. Более типовой кейс сложно придумать.
Если UI фреймворк не подходит для решения типовых задач — это как минимум повод высказать мнение по этому поводу. Что я и сделал.
Что сравним? Сравним — два куска кода, решающих одну и ту же задачу — обеспечение связности между элементами формы.Delphinum
09.05.2016 15:00-1Я не навязываю, я показываю — что будет в коде
А кто с вами спорит?
И придется ему менять вьюхи на реакт, лезть в дебри epoxy, и прочее прочее. Когда проблем такого класса можно вообще избежать
Вы несомненно молодец, но рефакторинг никто не отменял, да и прикрутить к Backbone тот же React не сильно сложно, если уж прижмет.
Если UI фреймворк не подходит для решения типовых задач
Backbone это не UI фреймворк, не надо путать.
Сравним — два куска кода, решающих одну и ту же задачу — обеспечение связности между элементами формы
Я бы написал вам пример решения этой задачи на чистом Backbone, но честно, я работаю сегодня, а завтра мне уже будет лень это делать, да и вы остыните уже к тому времени.Synoptic
09.05.2016 15:08В этом треде, к счастью, никто особо не спорит — пока вижу как минимум комментарии с описанием подхода, как решить задачу на марионетке по-другому.
В прошлом треде: спорили. Из того треда вырос этот пост.
По-поводу рефакторинга, это вы, видимо, никогда не пытались объяснить людям, которые принимают решения, зачем вам нужны ресурсы команды разработчиков на ближайшие две недели, когда уже спринты на два месяца вперед запланированы, и какой с этого заказчику будет профит. На этом разговоры о рефакторинге обычно заканчиваются, если проект не в 500-1000 строчек.
Именем Backbone здесь называется Marionette в том числе, если вы не поняли. Никто давно не пишет ничего серьезного на чистом Backbone — кому это надо потом поддерживать.
Насчет примера, ну как обычно, я не удивлен. Много слов, мало дела.Delphinum
09.05.2016 15:10не пытались объяснить людям, которые принимают решения, зачем вам нужны ресурсы команды разработчиков на ближайшие две недели
Я уже вот как полтора года живу без ресурсов на рефакторинг, приходится по ночам в тихушку )
Именем Backbone здесь называется Marionette
Не надо так, речь о Backbone.
Никто давно не пишет ничего серьезного на чистом Backbone
Любите вы выдавать желаемое за действительное.
Насчет примера, ну как обычно, я не удивлен. Много слов, мало дела
Простите меня грешного (boodda
09.05.2016 15:48я вот прям сейчас пишу клиентское приложение на ББ. и думаю у меня все получится не плохо
vintage
09.05.2016 16:56В общем случае задача восстановления модели из отображения — довольно сложная, ибо процесс рендеринга в подавляющем числе случаев сопровождается потерей информации. Можно исходные данные рендерить дополнительно в скрытые области, например, обрамлять человекочитаемую дату в тэг и в атрибуте писать её в машиночитаемом формате, но этот подход ничем не лучше, чем просто отдельно загрузить исходные данные в машиночитаемом формате. В любом случае, пляски с полусерверным рендерингом и подхватыванием отображения, — это огромный пласт проблем, которые замедляют разработку и повышают число багов. Так что лучшее решение — рендерить только на клиенте. А для случаев, когда необходим серверный рендеринг — использовать реализацию клиента на сервере (через node-webkit или изоморфный ui-фреймворк).
Delphinum
09.05.2016 17:00Собственно задача уже реализована на Backbone (путем переноса идей из YUI3, вечная ему память), и да, она маленько попахивает. Привел пример для того, чтобы автор, возможно, таки понял, что задачи бывают разные, и часто не упираются в один только рендеринг из модели представления во вьюхи.
k3nny
09.05.2016 15:44Чувак, тебе выше написали уже что надо просто перерендеривать шаблоны по модели и все. По части времени — ничего не будет нигде моргать.
Когда начинался этот хайп с реактом — пошел, глянул на него, решил что штука лично мне ненужная. На работе последние несколько лет пишу SPA на бэкбоне. Основная его фича в том, что он дает необходимый контроль. Время от времени бывают ситуации когда представление данных зависит не только от модели данных, а и от, скажем, триггеров, которые к рендеру вьюхи привели. И лишние модели там воротить не в кассу вообще. И тут не то что реакт не подойдет как-то, но в нем будет тот же код, что и в бэкбоньей вьюхе примерно.
Основной вопрос короче — «нахера?». Я вот не страдаю что у меня вьюхи рендерятся прям в DOM из шаблонов, без ненужной промежуточной абстракции.Synoptic
09.05.2016 15:51Я сталкивался с морганием при перерендере больших форм.
k3nny
09.05.2016 16:47http://output.jsbin.com/loniloyuku
большая форма?Synoptic
09.05.2016 16:53Большая, у меня были конечно значительно меньше по количеству инпутов, но не такие. Размер — примерно один экран 1280х800, количество инпутов, навскидку, штук 80, очень разных, в том числе кастомных. Но было много repaint и reflow согласно дизайну.
k3nny
09.05.2016 16:55какие вообще repaint и reflow при обсчете нового элемента?
Synoptic
09.05.2016 17:03Хорошо, врать не буду, смотрел в dev tools на цифры и взял высокие оценки по repaint/reflow оттуда. Возможно в том случае был говнокод.
Это решение уже не раз упоминалось — стоит о нем написать. Сделаете образец кода, по примеру последнего в статье? Я добавлю в пост.k3nny
09.05.2016 17:06да штука в том, что тут лень всем писать пример этот. включая меня) вот со стилизацией сделал зато. даже с box-shadow. он уж точно тяжелый для непосредственно рендера
http://output.jsbin.com/lavugoqoce
vintage
09.05.2016 17:08+1Типичные проблемы:
- Медленный ререндер.
- Дублирующиея ререндеры.
- Сброс позиций скроллингов.
- Сброс фокуса.
- Сброс введённых значений.
k3nny
09.05.2016 17:201. примеры в студию. я вот думаю что все не так. в смысле оно медленнее конечно получается, но не настолько, экономия на спичках.
2. что? ну 2 рендера подряд. у вас там тоже 2 обсчета вашего охуенного виртуального дома будет в таком случае.
3. типа чувак писал что-то в огромной textarea, она проскроллилась и потом он нажал на сабмит и она откатилась наверх что ли? если так — то да, fail, backbone -1 очко. но у меня лично не было таких ситуаций, а если будет — напишу какие-нибудь кастомные пару строк. вот в этой кастомной теме backbone и хорош.
4. вообще проблема, да. но у меня чета тоже не было требований никогда таких. обычно либо чувак жмет на кнопку руками, либо появляется прелоадер какой-то и все норм. но вообще вот тут есть неудобство, ага.
5. полная херня. чо бы они сбрасывались, если вьюха перерисовывается по модели?vintage
09.05.2016 17:45- https://github.com/nin-jin/todomvc/tree/master/benchmark решения с полным ререндером ощутимо тупят на сравнительно небольших списках в 100 элементов.
- У меня не виртуальный дом используется. Так что не будет. Эта проблема характера для всех событийных фреймворков (реакт, бэкбон, нокаут).
- Накостылять-то можно в любом фреймворке. Но лучше всё же, когда костыли не требуются.
- Типичная ситуация — пользователь что-то вводит и тут по сокетам прилетает обновление, которое приводит к ререндеру текущей вьюшки.
- Если мы производим полный ререндер, а не просто патч дома, то сбрасывается всё, что специально не сохранено.
k3nny
09.05.2016 18:221. ну чо, по этой ссылке backbone быстрее реакта
3. ну охуеть теперь, требования бизнес-логики, если они вообще такие есть, выходящие за рамки того что отличненько ложится на архитектуру фреймворка сразу причисляем к костылянию. 2016 ёпта, пойду подключу пакет для скролла текстэрии вверх. а вообще это практически ключевой момент. я люблю backone в частности за простоту «накостыляния». это всегда такой трейдоф, гибкость vs функциональность. мой опыт толкает меня чуть дальше в сторону гибкости
4. супер типичная ситуация
5. ну так оно должно быть специально сохранено, если задумывается ререндер такой. one-way binding значит нуженvintage
09.05.2016 18:34- В 4 раза медленнее лидеров. И то, реализация на бэкбоне не ререндерит весь список, а только отдельные строки.
3,4,5. Я перечислил типичные проблемы подхода полного ререндера, а не фичи бизнес логики. Что вы пытаетесь доказать?
- В 4 раза медленнее лидеров. И то, реализация на бэкбоне не ререндерит весь список, а только отдельные строки.
faiwer
09.05.2016 18:37- knockoutJS и полный rerender? why? binding-и не ререндерят лишнего, если конечно, не заменилась сущность целиком.
vintage
09.05.2016 18:40А при чём тут нокаут? :-)
faiwer
09.05.2016 18:44Может быть я вас недопонял, но ваши слова:
Дублирующиея ререндеры.
- У меня не виртуальный дом используется. Так что не будет. Эта проблема характера для всех событийных фреймворков (реакт, бэкбон, нокаут).
vintage
09.05.2016 18:52"Эта проблема" — "дублирующиеся ререндеры" (например, когда меняются две модели, от которых зависит одна вьюшка), а не "ререндер всего".
faiwer
09.05.2016 18:57Если из коробки включён
deferred: true
, то хоть 10 моделей к ряду,dom-binding
сработает отложенно и единожды. Использовать knockout как либо иначе, ИМХО, имеет смысл только в экзотических ситуациях. Но да, раньше этой опции просто не было.vintage
09.05.2016 19:19О, замечательно, что она наконец появилась :-)
А похожую проблему с каскадным обновлением они тоже решили?
faiwer
09.05.2016 19:31Хм. Кажется, да:
b = ko.observable(); d = ko.observable(); c = ko.computed(() => d()); a = ko.computed(() => { b(); c(); console.log('refresh a'); }); // cli: refresh a (1 раз) /* правим B и D единовременно: */ b(3); d(3); // cli: refresh a (1 раз)
Получается, что да?
Markdown на хабре какой-то ну уж совсем глючный =(
vintage
09.05.2016 15:47+3у посетителя есть возможность сабмитить форму сколько угодно раз после уже отправки запроса. Блокируем кнопку до того, как станет понятно, что произошло с запросом
Надо идемпотентные запросы делать, а не кнопки блокировать.
Это позволяет обойтись без ввода «Your Region» и «Your City», но поэтому при нажатии кнопки «Set», блокируются поля «Your Region», «Your City» и кнопка «Submit» до завершения запроса.
Значения по умолчанию стоит загружать в фоне, не блокируя интерфейс.
С момента появления фреймворков нового поколения типа React+Redux, целесообразность использования BackboneJS лично для меня стала равна нулю.
С момента появления фреймворков нового поколения типа $mol, целесообразность использования React+Redux лично для меня стала равна нулю :-D
Для сравнения, шаблон для вашего финального примера:
Заголовок спойлера$my_register : $mol_rower child < col1 : $mol_view child < col1Fields < namer : $my_register_field title : =Your Name value < name : = < zipper : $my_register_fieldMaster title : =Your ZIP value < zip : = < regioner : $my_register_field title : =Your Region value < region : = < citier : $my_register_field title : =Your City value < city : = < col2 : $mol_view child < col2Fields < carNumber : $my_register_fieldMaster title : =Your Car Number value < carNumb : = < carModeller : $my_register_field title : =Your Car Model value < carModel : = < carAger : $my_register_field title : =Your Car Age value < carAge : = < col3 : $mol_view child < col3Fields < socialer : $my_register_fieldMaster title : =Your Social ID value < social : = < sexer : $my_register_field title : =Your Gender value < sex : = < ager : $my_register_field title : =Your Age value < age : = < sexOrienter : $my_register_field title : =Your Sexual Orientation value < sexOrient : = < submitter : $mol_clicker child : =Submit type : =major clicks < submits : null $my_register_field : $mol_view tagName : =label child < titler : $mol_view child < title : = < stringer : $mol_stringer value < value : = $my_register_fieldMaster : $my_register_field child < titler < stringer < submitter : $mol_clicker child : =Set clicks < submits : null type : =major
Synoptic
09.05.2016 15:55vintage вы, очевидно, не дизайнер. О том, что пользователю нужно давать моментальную реакцию на его действия вас соответственно не учили. С технической точки зрения тоже провис — неважно идемпотентный запрос или нет, он выполняется не моментально, и давать его дублировать нельзя.
Значения по умолчанию могут быть изначально не востребованными, а поход за ними в базу или их объем может быть слишком большим чтобы каждый раз грузить их на клиента.
Код ваш не смотрел, ибо фреймворк не знаю и учить его не планирую. И, поверьте, его не знает подавляющее большинство веб-разработчиков — он им просто не нужен.Delphinum
09.05.2016 16:06+1неважно идемпотентный запрос или нет, он выполняется не моментально, и давать его дублировать нельзя
Если RESTful таки RESTful (с идемпотентностью), то не важно, дублируется запрос или нет, это проблемы клиента, а не сервера. В частности, если на базе вашего API кто то соберется писать свое Android/iOS приложение не договорившись с вами, и решит не использовать блокировку кнопок при запросах, то идемпотентное API этому не помешает, а вот заставить такого стороннего разработчика именно блокировать кнопки чтоб ваше API не сломалось вы не сможете. О том и речь.Synoptic
09.05.2016 16:15+2Я в курсе, что такое идемпотентный запрос, спасибо. Я говорю, что не блокировать кнопку неправильно, идемпотентный запрос или нет — это основы UX.
Кроме того, подозреваю, что у в данном примере не только запрос на сервер уходит, а все-таки какая-то реакция на UI после resolve/reject запроса происходит. Если оставить пользователю возможность тыкать кнопку до посинения, то success/fail колбек выполнится неограниченное число раз. И чтобы предотвратить это, придется писать дополнительную логику, возможно и на сервере тоже.
Нормальные люди в таком случае просто блокируют кнопку.vintage
09.05.2016 17:30В data-flow архитектуре описанных вами проблем нет. От слова вообще. Какие-либо процессы запускаются не по факту какого-либо события, а по факту изменения какого-либо состояния. При этом у вас может быть хоть 1000 событий, но если все они приводят в конечном счёте к одному и тому же состоянию — реакция на изменения этого состояния будет вызвана лишь один раз.
Synoptic
09.05.2016 17:39Речь про поведение UI, а не про архитектуру. Еще раз — что по вашему надо делать после нажатия на кнопку? Давать нажать ее с тем же эффектом еще неограниченное число раз?
vintage
09.05.2016 17:55+1control-flow:
- click button -> open popup
- click button -> open popup 2
- Зачем мне открыли два попапа? Плохой, негодный UX! Нужно запрещать пользователю нажимать кнопку дважды!
data-flow:
- click button -> popupOpened = true -> changed popupOpened -> open popup
- click button -> popupOpened = true
- Класс, хоть 10 нажимай, а открывается лишь один попап.
Ещё раз: ваши проблемы с UX — следствие кривой архитектуры, при использовании прямой архитектуры таких проблем не возникает.
Synoptic
09.05.2016 17:58-2Прекрасное решение, в мире с единорогами. В вебе с его задержками надо давать пользователю реакцию в течение максимум 150ms. За это время ваш запрос не выполнится, попап, если он зависим от внешних данных — не откроется. А пользователь будет негодовать и жать на кнопку еще и еще.
Synoptic
09.05.2016 18:00По поводу popup, раз уж пошла речь. Если имеется в виду модальное окно, то дважды он на кнопку не нажмет в силу модальности. Если имеется в виду именно попап, то поведение при повторном нажатии должно предусматриваться требованиями и дизайнером. Кнопка обычно не блокируется.
Это не как в идеале, это так как есть в реальности.vintage
09.05.2016 18:11Ок, замените "open popup" на "send message", а "popupOpened = true" на "messageToSend = 'Hello'".
Synoptic
09.05.2016 18:20Тогда снова не понял и снова повторю вопрос — сколько раз должен выполниться код, обрабатывающий нажатие кнопки по завершению запроса, если пользователь нажмет ее 10 раз подряд?
vintage
09.05.2016 18:41Очевидно, одно сообщение должно быть послано лишь один раз, независимо от числа нажатий на кнопку.
Synoptic
10.05.2016 07:30-1То есть нужно все же написать логику, реализующую неотнократный вызов .done коллбека и весь смысл вашего предложения только в «неблокировании кнопки» — тоесть даже не в поле техническом, а в поле чисто UX.
В этом поле вы абсолютно неправы и кнопку надо блокировать, это основы пользовательского взаимодействия, я повторяюсь, что бы вам ни хотелось :) Это десятки лет назад выясненные и подсчитанные вещи, базирующиеся на человеческой психологии.Synoptic
10.05.2016 10:22«неотнократный» = однократный.Но суть не в этом, даже допустим не будем рассматривать количество кода.
mayorovp
10.05.2016 17:07Если пользователь хочет нажать кнопку второй раз — а она заблокирована потому что уже нажата — это называется «лаг». Пользователя бесит, когда приложение лагает.
Если есть возможность построить UI так, чтобы он не лагал — надо так и делать.
PS я делаю кнопки блокирующимися — но не потому что это самое лучшее решение — а потому что самое простое. Мне не дают времени чтобы сделать самый лучший интерфейс, и решения о дизайне принимаю тоже не я…oxidmod
10.05.2016 17:09можно вообще вместо кнопки рисовать лоадер)) юзеру не по чему кликать и втоже время он видит что чтото происходит
mayorovp
10.05.2016 17:23Смотрите — пользователь же второй раз нажимает на кнопку вовсе не случайно (хотя случайно тоже может, и от этого обычно и страхуются блокировкой кнопки).
К примеру, это может быть желание отредактировать форму. В таком случае правильное поведение — отменить первый запрос (если он еще не завершился) и отправить новый. В этом сценарии и пригодится идемпотентность запросов, точнее более сильное свойство, когда новый пришедший на сервер запрос затирает старый.
Или же это может быть желание добавить в базу вторую запись. Редкий случай — но так тоже бывает. Если предполагается такой вариант — надо при нажатии на кнопку очищать форму, готовя ее к вводу новой порции данных.
Или не очищать, если данные могут быть похожими.
Как вариант — чат. После отправки сообщения его надо показать пользователю сразу же, а реальную отправку делать в фоне.Synoptic
10.05.2016 17:37Посмотрите как реализованы популярные чаты, к примеру — в Skype. Они не блокируют кнопку, но всегда дают реакцию, чтобы видно было, отправлено сообщение, или нет. В скайпе крутится спиннер.
Synoptic
10.05.2016 17:38Это то, что делает блокирование кнопки как раз. Такая упрощенная форма спиннера.
Synoptic
10.05.2016 17:35Не понимаю основной мысли ваших комментариев. Я начал писать про блокирование инпутов, потому что второй собеседник сказал, что этого делать не надо. Я спросил — а что же тогда делать? Получил ответ — ничего, давать пользователю нажимать кнопку столько раз, а он хочет. Я заметил, что даже в таком случае надо давать пользователю реакцию — показывать хотя бы спиннер.
Теперь пришли вы и… что вы предлагаете?mayorovp
10.05.2016 17:47+1Я заметил, что даже в таком случае надо давать пользователю реакцию — показывать хотя бы спиннер.
Следите за тем, что пишите — тогда будет понятно почему с вами спорят :)
Вы написали, что блокировать кнопки — это основы UI и вообще vintage не дизайнер.
Вам бросились доказывать, что блокировать кнопки не обязательно.
Первый раз слово «спиннер» в этом диалоге вы написали в ответ на мой комментарий.
Раз вы признаете, что спиннер — разумная альтернатива блокировке кнопок, предлагаю перестать ходить по кругу из одних и тех же аргументов.Synoptic
10.05.2016 18:23Я общался с vintage и мы оба были в контексте разговора. Вы написали свой комментарий не зная контекста. Не ваша вина, но и не моя.
vintage
10.05.2016 21:32+3Больше похоже, что вы спорили с воображаемым оппонентом, который предлагает не показывать фидбек пользователю.
Synoptic
10.05.2016 21:43Надо идемпотентные запросы делать, а не кнопки блокировать.
Ваш первый комментарий, который я решил прокомментировать. Из него вполне следует, что вы предлагаете заменить блокировку кнопки идемпотентными запросами ничего не предлагая взамен. В ответ я заметил что вы видимо не дизайнер и что:
О том, что пользователю нужно давать моментальную реакцию на его действия вас соответственно не учили.
Это самые первые комментарии, сразу же. Прекрасно было понятно, что меня смутило отсутствие реакции для пользователя при таком подходе. Далее я еще раз двадцать раз упомянул в каждом ответе на ваш комментарий про моментальную реакцию, но вы начали рассказывать про data-flow архиитектуру, и одном из ответов эта тема вами не затрагивается.
Так скажите, можно ли таки использовать кнопки-спиннеры, в число которых входит и заблокированная кнопка? И закроем уже вопрос.
vintage
10.05.2016 21:58Блокировка кнопки не является индикатором прогресса. Как минимум она ещё служит индикатором незаполенности формы, отсутствия прав и просто сломавшихся скриптов.
Всё, на чём я настаиваю — не блокировать интерфейс. Как показать пользователю, что его запрос выполняется — совершенно перпендикулярный вопрос.
vintage
10.05.2016 21:25То есть нужно все же написать логику
Не нужно.
реализующую неотнократный вызов .done коллбека
Промисы — типичный control-flow с типичными, описанными вами, проблемами.
это основы пользовательского взаимодействия
Synoptic
10.05.2016 21:35Да ну как же не надо. Как тогда отличить, нажал человек на кнопку один раз или несколько?
Про демагогию. Я каждую свою отсылку на авторитеты (которая была всего одна из кучи комментариев — это была отсылка на букварь для дизайнера — примерно то же самое, что Макконелл для программиста) подтверждаю конкретными примерами на пальцах.
Например, любой человек, даже ничего не понимающий в дизайне, может легко представить себе интерфейс, никак не реагирующий моментально после нажатия на кнопку. И вспомнить ситуацию, когда он после этого нажимал на эту кнопку еще раз. А если запрос подвис, то может быть гневно нажимал несколько раз и рефрешил страницу или вообще уходил с нее.
Это то, что предлагаете делать вы.
Любой человек может также вспомнить ситуацию, когда он также нажимал на кнопку, и она блокировалась. А если интерфейс хороший, то в кнопке еще и появлялся спиннер, показывающий, что процесс идет, надо только подождать. И человек ждал гораздо дольше, чем в предыдущем случае, потому что на этот раз он не думал, что интерфейс подвис. Он уже приучен к этому паттерну и спокойно ждал завершения запроса.
Это то, что предлагаете вы.vintage
10.05.2016 21:47Да ну как же не надо. Как тогда отличить, нажал человек на кнопку один раз или несколько?
Я вам уже говорил, что отличать это вовсе не нужно.
Я каждую свою отсылку на авторитеты (которая была всего одна из кучи комментариев — это была отсылка на букварь для дизайнера — примерно то же самое, что Макконелл для программиста) подтверждаю конкретными примерами на пальцах.
Конкретную цитату приведите для начала.
Например, любой человек, даже ничего не понимающий в дизайне, может легко представить себе интерфейс, никак не реагирующий моментально после нажатия на кнопку. И вспомнить ситуацию, когда он после этого нажимал на эту кнопку еще раз. А если запрос подвис, то может быть гневно нажимал несколько раз и рефрешил страницу или вообще уходил с нее.
Это то, что предлагаете делать вы.
Нет, это соломенное чучело.
Любой человек может также вспомнить ситуацию, когда он также нажимал на кнопку, и она блокировалась. А если интерфейс хороший, то в кнопке еще и появлялся спиннер, показывающий, что процесс идет, надо только подождать. И человек ждал гораздо дольше, чем в предыдущем случае, потому что на этот раз он не думал, что интерфейс подвис. Он уже приучен к этому паттерну и спокойно ждал завершения запроса.
Это то, что предлагаете вы.
Не я.
Знаете, очень бесит, когда надо быстро создать 10 задач, а мне приходится ждать, пока каждая из них отправится на сервер.
Synoptic
10.05.2016 22:09Я вам уже говорил, что отличать это вовсе не нужно.
popupOpened = true — это не логика? а что это?
Конкретную цитату приведите для начала.
«Длительность ответа, поступающего от компьютера, R, может оказывать неожиданный эффект на действия пользователя. Если при использовании какого-то управляющего элемента на экране монитора в течение приблизительно 250 мс ничего не возникает, пользователь, скорее всего, может почувствовать беспокойство, решит сделать еще одну попытку или подумает, что система неисправна.»
http://raskin-interface.narod.ru/interface/chapter4.htm
Сейчас эта цифра уточнена до 150мс
Нет, это соломенное чучело.
Это то, что следует из ваших комментариев. Если вы хотели донести конкретную мысль, вам это сделать в первом комментарии не удалось, из чего получилась длинная, но возможно кому-то в будущем полезная дискуссия.vintage
10.05.2016 22:25popupOpened = true — это не логика? а что это?
Это идемпотентная логика.
http://raskin-interface.narod.ru/interface/chapter4.htm
И где тут про блокировку кнопок?
Это то, что следует из ваших комментариев.
Не следует. Прекратите домысливать тезисы за оппонента. В виду узкого кругозора вы это делаете неправильно.
Synoptic
10.05.2016 22:44О, уже логика. Как мило. Особая, идемпотентная логика. Напомню для зрителей, цитата за пару комментов до:
Я вам уже говорил, что отличать это вовсе не нужно.
Я уже объяснил свою позицию несколько раз — речь о реакции на действия пользователя, а не о самой блокировке.
В виду узкого кругозора вы это делаете неправильно.
«Вы не понимаете суть фреймворков»
Synoptic
10.05.2016 22:52Еще в догонку примеров, когда контрол блокировать надо:
http://uxmovement.com/forms/form-field-techniques-to-control-user-input/
Synoptic
10.05.2016 22:11Знаете, очень бесит, когда надо быстро создать 10 задач, а мне приходится ждать, пока каждая из них отправится на сервер.
Это тоже логическая уловка, в которых вы обвиняете меня. Я не предлагал блокировать кнопки всегда — в этом случае дизайнер должен был предусмотреть обратное. Я лишь говорил, что блокирование UI — это вполне приемлемый паттерн. Есть нередкие кейсы, когда блокировать кнопку надо, потому что от выполнения запроса зависит следующий вопрос.vintage
10.05.2016 22:27Дизайнер думает лишь о 80% пользователей, которым надо создавать не более одной задачи в час.
faiwer
09.05.2016 18:01+1Ещё раз: ваши проблемы с UX — следствие кривой архитектуры, при использовании прямой архитектуры таких проблем не возникает.
По вашему в data-flow варианте не нужно лочить кнопку? А логика в чём? Я просто вешаю
disabled: isSubmitting === true
на кнопку, чтобы не путать юзера нестандартным поведением. А в случае ошибки (того же таймаута) восстанавливаю в false. И волки сыты, и овцы целы. Блокирование кнопки на время запроса как минимум — очевидное поведение. И data-flow тут не причём. Пока кнопка активна, по мнению юзера, она должна что-нибудь делать!vintage
09.05.2016 18:50+1Совсем не очевидное и совсем не стандартное.
С точки зрения UX надо не кнопку лочить, а показывать, что запрошенное действие выполняется.
С точки зрения хорошего UX нужно позволить пользователю исправить опечатку и снова отправить форму, не дожидаясь ответа от неправильно заполенной формы.
С точки зрения data-flow — ничего страшного, если пользователь дважды нажмёт кнопку.Synoptic
10.05.2016 07:37Сколько должны выполняться запросы, чтобы успеть исправить опечатку во время выполнения запроса? Тут явно большие проблемы с UX, чем проблема с блокированием кнопок. Это далеко не самый частый кейс, нужный дааалеко не для всех UI, зато требующий дополнительного кода.
Тем не менее, если вы хотите дать пользователю возможность редактировать данные после отправки запроса, оставить кнопку не заблокированной, то все равно нужно показывать спиннер, сразу же, чтобы человек видел, что — обратную связь нужно дать. Заблокированная кнопка обычно играет роль такого спиннера. Альтернатива — спиннер — встроенный в кнопку, без блокировки.
Просто оставлять пользователя наедине с UI без реакции — нельзя.mayorovp
10.05.2016 17:09Ну так со спиннерами никто и не спорит. Спор идет вокруг блокировки кнопок, причем с такими аргументами, как будто это единственный способ показать пользователю что его запрос обрабатывается.
Synoptic
10.05.2016 18:43Спор идет не об этом.
mayorovp
10.05.2016 19:28Разве? Не вижу. Спор начался и идет вокруг вот этого комментария:
Надо идемпотентные запросы делать, а не кнопки блокировать.
Разве там написано, что надо оставить пользователя вообще без индикатора хода запроса?
faiwer
10.05.2016 22:04+1Вы знаете, все вокруг считают, что спор идёт о блокировке кнопки, и только вы спорите о каком-то своём контексте. То и дело срываясь на личности ("не дизайнер" и пр.). Может хватит уже?
vintage
09.05.2016 17:22+1vintage вы, очевидно, не дизайнер.
Потрясающая наблюдательность! :-D
О том, что пользователю нужно давать моментальную реакцию на его действия вас соответственно не учили.
А где вас учили заламывать пользователю руки после каждого действия? Я повидал очень много таких форм как ваша — кнопку выключили, а включить не смогли (упал скрипт или ждём таймаута 5 минут или валидация багованная). И нет никакой возможности вручную повторить запрос.
С технической точки зрения тоже провис — неважно идемпотентный запрос или нет, он выполняется не моментально, и давать его дублировать нельзя.
Можно. Запросы имеют свойство теряться. Сервера имеют свойство зависать. Интернет имеет свойство пропадать.
Значения по умолчанию могут быть изначально не востребованными, а поход за ними в базу или их объем может быть слишком большим чтобы каждый раз грузить их на клиента.
Казалось бы, при чём тут suggest? :-)
Код ваш не смотрел, ибо фреймворк не знаю и учить его не планирую. И, поверьте, его не знает подавляющее большинство веб-разработчиков — он им просто не нужен.
Казалось бы, при чём тут "бабуины" и "непробиваемое невежество"? :-)
faiwer
09.05.2016 17:37кнопку выключили, а включить не смогли (упал скрипт
Ну в desktop-приложениях это стандартное поведение, а если что-нибудь и отвалилось, то залоченная кнопка может помочь избежать усугубления ситуации. Дескать не отловленный exception, на то и не отловленный, что штатным не является, и, следовательно, из такого вот поглючившего состояния двигаться дальше опасно. Хотя вопрос конечно спорный :) Но если исходить из блокировки кнопок, то как минимум построение UI проще и быстрее выходит.
Synoptic
09.05.2016 17:34+1Я повидал очень много таких форм как ваша — кнопку выключили, а включить не смогли (упал скрипт или ждём таймаута 5 минут или валидация багованная)
Странный аргумент. Я вам про UX, вы мне про глючный код. При желании можно и не блокирующий кнопку код написать через одно место. Повторюсь — это основы пользовательского взаимодействия. Раскин, Нильсен в помощь, если уж на то пошло — там с цифрами и формулами.
Про, что запросы имеют свойство теряться, сервера имеют свойство зависать, а интернет имеет свойство пропадать UX дизайнеры, особенно с хайлоада, в курсе и существуют проверенные временем паттерны, как и в каких ситуациях поступать. Среди этих паттернов и рекомендаций нет ни одной на тему «не блокировать кнопки при отправки асинхронного запроса». Если найдете и покажете в авторитетном источнике — это будет для меня большим открытием. Покажете? В догонку сюда же — и про колбек после завершения запроса вы как-то промолчали. Его сколько раз выполнять нужно, каждый раз при резолве промиса? Десять раз показать алерт «settings saved»?
Про suggest я не понял. Есть случаи, когда выгрести данные из базы занимает время, а данные нужны в 1 случае из 10. Никакого смысла грузить их всем подряд нет.
И бабуинов непонятно — я отвечаю просто фактами, без оскорблений. То, что вы не дизайнер — это просто констатация факта, на что здесь обижаться, я не хотел вас оскорбить.vintage
09.05.2016 19:06+4Странный аргумент. Я вам про UX, вы мне про глючный код.
А я вам про пользовательский опыт. Когда случается ошибка (а она случается), у пользователя должны быть варианты выхода из неприятной ситуации. Или по вашему аварийный выход в самолётах — это плохой UX?
Если найдете и покажете в авторитетном источнике — это будет для меня большим открытием.
Вы только чужим мозгом думать умеете? ;-)
Десять раз показать алерт «settings saved»?
В data-flow архитектуре вы колбэк можете вызывать хоть 1000 раз, а сообщение "settings saved" будет выведено лишь раз.
Про suggest я не понял. Есть случаи, когда выгрести данные из базы занимает время, а данные нужны в 1 случае из 10. Никакого смысла грузить их всем подряд нет.
Вот и грузите их, когда они нужны. Какие проблемы? Если ваш сервер затупил или запрос потерялся или 100500 любых других причин — пользователь должен иметь возможность быстро ввести данные вручную. Расскажите об этом Раскину, Нильсону и любой другой фамилии.
И бабуинов непонятно — я отвечаю просто фактами, без оскорблений. То, что вы не дизайнер — это просто констатация факта, на что здесь обижаться, я не хотел вас оскорбить.
Речь о том, что вы ведёте себя также как те, кого критикуете. Сидите и держитесь за свою точку зрения, боясь расширить кругозор.
Synoptic
10.05.2016 07:44Я умею думать собственным опытом, цифрами и проведенными исследованиями, которые, почему-то обычно совпадают.
По поводу data-flow и неблокирующихся кнопок — ответил здесь. Оставлять пользователя наедине с UI после нажатия на кнопку без реакции — это UX-антипаттерн.
Вот и грузите их, когда они нужны. Какие проблемы? Если ваш сервер затупил или запрос потерялся или 100500 любых других причин — пользователь должен иметь возможность быстро ввести данные вручную. Расскажите об этом Раскину, Нильсону и любой другой фамилии.
Я так и делаю. В чем ко мне претензия в этом плане? На формах в статье это видно. Формы блокируются на момент запроса, делающего автозаполнение, чтобы после выполнения запроса у юзера не слетело то, что он вводил, пока запрос выполняется.
Держусь я так: на факт отвечаю контр-фактом и так до тех пор, пока не станет видно, кто прав, а кто — нет. Пока в плане кода ваши ответы с моей точки зрения грамотны и полезны и я о минусах перерендера — сброса фокуса, позиции скролла итд в том решении — напишу в после, когда сделаю пример с кодом. Но в плане UX — совершенно безграмотны, а настаиваете вы на них как профессионал в этой области.vintage
10.05.2016 21:39Формы блокируются на момент запроса, делающего автозаполнение, чтобы после выполнения запроса у юзера не слетело то, что он вводил, пока запрос выполняется.
Так сделайте так, чтобы не слетало.
Держусь я так: на факт отвечаю контр-фактом и так до тех пор, пока не станет видно, кто прав, а кто — нет.
Все ваши "факты" сводятся к:
в плане UX — совершенно безграмотны, а настаиваете вы на них как профессионал в этой области
Synoptic
10.05.2016 21:46-1Чтобы не слетало можно сделать лишь одним образом — показывать после завершения запроса уведомление, что-де приехали данные, которые вы запрашивали в форме, разрешите их подставить, но введенные вами данные потеряются. И это очередной антипаттерн.
Ваши комментарии сводятся «вы не понимаете суть фреймворка» одного персонажа из-за которого и был создан этот топик. Возможно, ради вас стоит выкроить минутку и для UX-ликбеза? Не сольетесь потом в нормальной дискусии с примерами по делу, также как поступил этот персонаж?vintage
10.05.2016 22:05Чтобы не слетало можно сделать лишь одним образом — показывать после завершения запроса уведомление, что-де приезали данные, которые вы запрашивали в форме, разрешите их подставить, но введенные вами данные потеряются.
Если пользователь ввёл значение не дождавшись его загрузки — значит загруженное значение его не волнует. Зачем показывать ему уведомления?
Возможно, ради вас стоит выкроить минутку и для UX-ликбеза?
Дерзайте. Не забывайте подкреплять слова результатами двойных слепых тестирований с уровнем доверия не ниже 95% и поправкой на множественные сравнения.
Delphinum
09.05.2016 18:14+1Чтобы автор уж совсем сильно не огорчался, что никто не написал пример реализации его задачи на Backbone, я таки выкрал 30 минут времени и быстренько накидал следующее решение: http://output.jsbin.com/punopejuha
Знающие сильно не пинайте, нет ни времени, ни желания доводить это до красоты.
Для автора вот резюме этого кода: https://habrahabr.ru/post/283148/#comment_8887914
Aries_ua
09.05.2016 23:21-1Вот это дискуссия. Кто кого? Борьба добра со злом или наоборот?
В предыдущей статье я пытался донести, что биндинг одно или двухсторонний — не столь важен или принципиален, как автор хочет преподнести.
Давайте разберемся и посмотрим что к чему.
Итак, делаем новое приложение. Подумаем, что там надо.
— Хорошая структура проекта. Возможность создавать пакеты, модули, а в них JS основные файлы.
— Работа со списками и объектами. События для них. REST. Возможность типизации и наследования. Валидация пропертей объекта.
— Роутинг
— Хендреры для сборки и обработки данных.
— Обработка всплывающих сообщений.
— Модальные окна.
— Генератор форм.
— UI компоненты (поля, кнопки, списки, календари итд).
— Управление «жизнью» DOM и события к ним.
— Шаблонизация.
Как видим — одно или двухсторонний биндинг, всего один мелкий пункт приложения.
Теперь пройдемся что есть, а чего нет.
Работа со списками и моделями есть в бекбоне. Немного допилим модель для типизации данных и добавим промисы для валидации. Больше ничего делать не надо. Что из этого предоставит React?
Роутинг есть и там и там.
Хендлеров нет ни там, ни там. Надо дописывать. (Под хендлерами я имею ввиду основные обработчики данных).
Всплывающие сообщения — нет.
Модальные окна — нет.
Генератор форм — нет.
UI компоненты — нет.
Управление DOM — В реакте через биндинт, в бекбоне через UI и события.
Шаблонизация — в реакте по умолчанию. В бекбоне нет. Но! Мы используем шаблонизатор NunjuckJS, который позваляет много чего. Рендерить как на сервере шаблоны, так и на клиенте. Прекомпиляция. В общем навороченный инструмент. Кто не знаком — welcome mozilla.github.io/nunjucks Добавить в бекбон дело 10 строчек кода.
Так вот скажете, в чем преимущество реакта? Много чего надо допиливать и там и там. Не однократно я вам указывал, что вы забываете и бизнес логике приложения, а вы упорно цеплялись за датабиндинг. Хотя я вам привел примеры, что и на бекбоне можно это сделать легко. Но вас уже не остановить. Даже статью целую написали.
Разберитесь в конце концов в предмете.Delphinum
09.05.2016 23:26Под хендлерами я имею ввиду основные обработчики данных
Что это за обработчики такие? Парсеры приходящих с сервера данных шоль?
Управление DOM — В реакте через биндинт, в бекбоне через UI и события
Правильнее сказать, что Backbone не предлагает механизмов для рендеринга представления. Тобишь реализовываем как душе угодно.Aries_ua
10.05.2016 09:11Что бы объяснить что такое хендлеры, стоит начать с Backbone.Router. По умолчанию, его не очень удобно использовать. В частности, мне хотелось сделать вызов роута для user как-то так — userRoute.href('user', {id: 10})
Второй недостаток. Это один объект содержит все routes и в одном объекте надо было прописывать логику. Как по мне — неудобно.
Так родился объект Base.Handler. Немного кода, что бы понять суть
// Class Handler var UserRoute = Base.Handler.extend({ route: '!/user/:id', index: function(id) { // Здесь создаем модель, обновляем ее с севера, создаем форму и вставляем в регион } }); // Создаем роут var router = new Base.Router({ edit: new UserHandler() }); // Теперь можно удобно использовать router.href('edit', {id: 10});
Теперь объект может содержать большую бизнес логику — к примеру — сборку данных их разных коллекций, зависимости итд. Сам объект можно наследовать, а значит переопределять методы итд.
Что касаемо представлений — да, не предоставляет и это как по мне — отлично! Так как можно самому выбрать, как будет происходит шаблонизация. Мы используем NunjuckJS. Выше приводил линк. Он генерит любые сложные шаблоны. Имеет макросы, инклюды, наследование и еще много чего.
Что касаемо биндинга — тут все просто. Нужен клик — забиндили итд. Что касаемо списков — у нас генератор форм сам может обновить список, если обновилась коллекция. Да, не спорю, в начале пришлось написать форм генератор — зато на выходе одно удобство и гибкость.
Synoptic
10.05.2016 08:06В этом посте слова, без конкретных предложений по реализации вполне конкретной задачи, а по-хорошему — кода, лично мне не интересны, извините. Ваше умение вести объемные дискуссии, перемежая словами типа «разберитесь в предмете» без предложения конкретного решения — бесспорно еще с предыдущего поста, тут я снимаю шляпу.
Но я лучше потрачу время на разбор примеров коллег, которые не поленились и показали на практике возможные решения. Эт этого будет польза, а от вас — нет.Aries_ua
10.05.2016 09:12+1Решил не спорить с вами — вы глухи к доводам.
Synoptic
10.05.2016 09:31Конечно, настолько глух, что теперь знаю видимо все возможные решения. Правда, для этого пришлось затеять дискуссию размером в пост. Не стоило тогда и начинать, спор не по существу, без конкретных примеров, не приведет к выяснению решения.
vintage
10.05.2016 22:15+1Но я лучше потрачу время на разбор примеров коллег, которые не поленились и показали на практике возможные решения. Эт этого будет польза, а от вас — нет.
Код ваш не смотрел, ибо фреймворк не знаю и учить его не планирую. И, поверьте, его не знает подавляющее большинство веб-разработчиков — он им просто не нужен.
Чудесный образец. :-)
boolive
10.05.2016 18:24Убойная особенность react – компонентная модель. Приложение как иерархия компонентов. Отдельно состояние по flux архитектуре. Компоненты меняют и реагируют на состояние. Биндинга не нужно, не нужны селекторы по DOM, не нужны крутые шаблонизаторы со своими магическими синтаксисами – это неприятная практика выносить шаблон в отдельный файл, однако создавать жесткие связи селекторами или спец атрибутами. Зачем? С VirtualDOM разработка реально проще, код красивее. Работа с коллекциями, объектами — ES2015. События для них даёт flux архитектура. UI –никаких проблем, одно удовольствие, пишешь <Modal ../> и получаешь модальное окно. Роутинг, бизнес логика, контроллеры делаются теми же компонентами. Не хочешь – выноси, используй дополнительные либы, создавай свои объекты. Используй все богатство NPM со сборщиком webpack. Созданное приложение можно рендерить (запускать) на сервере, решая вопрос с SEO. Единая кодовая база для мобильных приложений – iOS, Android с адаптивностью и нативностью для каждой. Удобно прототипировать UI, оживляя его постепенно в реальное приложение. Верстальщакам, знакомым с JS, можно доверить работать с комоннентами, верстать прямо в них, меньше опасений в нарушении логики. Вхождение в react проект для новичка, не знающего react — пара дней.
В чем преимущество backdone? Какие у него перспективы? Какая ситуация на рынке труда, есть ли рост числа специалистов по нему? Почему его стоит использовать для новых проектов? Что поможет успешно «продать» его с текущей конкуренцией?
Delphinum
10.05.2016 20:20В чем преимущество backdone?
Легковесность, простота, гибкость, модульность, нацеленность на структурирование кода, а не на отображение моделей.
Какие у него перспективы?
Да ктож знает, такие же, какие у любой другой либы, о которой знают.
Какая ситуация на рынке труда, есть ли рост числа специалистов по нему?
А это есть плюс либы? В смысле, вы изучили только те языки и инструменты, за которые больше платят? ))
Почему его стоит использовать для новых проектов?
Если вам нужна простая библиотека, являющаяся по сути клеем между различными пакетами и вы используете предлагаемый ей REST клиент (хотя и не обязательно, вот недавно начал новый проект на Backbone без использования этого клиента).
Что поможет успешно «продать» его с текущей конкуренцией?
Вы клиентам продаете решение по кускам? В смысле:
Сайт-визитка:
— ZF2 — 90 000 рублей
— Symphony — 85 000 рублей
— + Angular — 42 000 рублей
— + React — 51 000 рублей
Вашему покупателю есть дело до того, с помощью какого инструмента вы пишите качественный код, решающий его задачи?boolive
10.05.2016 22:07+1Ну не в этом же смысле продать =) Обосновать выбор надо команде и заказчику, разбирающемся в технологическом вопросе (заказчики крупных проектов имеют свой it штат для поддержки и дальнейшего развития). Не убедишь, не продашь. И тут же вопрос рынка. Найдет ли заказчик специалистов для поддержки?
Delphinum
11.05.2016 13:47Аналогично, если вам нужно гибкое и легкое решение, для структурирования вашего JS, то вот вам Backbone. Еще его я часто применяю в унаследованных проектах на лапшекоде, как средство рефакторинга.
alexgrom
09.05.2016 23:21+2В корне не согласен с автором и вот почему: jsfiddle.net/00o9mh4d
(полный пример решения задачи со спиннерами, прелоадерами и 2мя состояниями на BB+Epoxy).
Стало так обидно за BB что не поленился импементировать задачу. А автор мог бы и привести полный код на реакте, чтобы было с чем сравнить.
По-моему, всё очень показательно. Различия с точностью до «декларативные биндинги vs. коллбеки в шаблоне». Другими словами, от решения на реакте отличается только отсутствием JSX со всеми вытекающими. И сдается мне, в этом и есть суть претензий к BB. Но во-первых, помнится от JSX долго все морщились, а во-вторых, как тут уже несколько раз написали, BB чуть более комплексный инструмент, в отличии от библиотеки-реализации virtualdom (не в обиду, просто же разные инструменты с акцентами на разные аспекты).
Работаю с backbone больше 3х лет и часто приходится делать такого рода функционал, не имея возможности делать слишком радикальные изменения в проекте. Там где тяжелые и запутанные состояния — положение спасают потоки данных и биндинги.
Конечно, когда пришел virtualdom и jsx, странно брать что-то другое в новый проект, но и существующие инструменты позволяют отлично решать задачи и не стоит так категорично их оценивать. И вообще, BB тем и славен, что чрезвычайно гибок и масштабируем: сколько одних только расширений для него.Synoptic
10.05.2016 08:01Всем спасибо за решения, вчера уже устал, сегодня обязательно добавлю в пост, когда все посмотрю, ну и обсудим.
napa3um
10.05.2016 08:33> Конечно, когда пришел virtualdom и jsx, странно брать что-то другое в новый проект
Почему странно? Web Components и Shadow DOM + Templates обычно не очень хорошо сочетаются с Virtual DOM (он им оказывается ненужным).alexgrom
10.05.2016 12:04>Почему странно?
Потому что virtualdom уже стал мейнстримом благодаря в основном реакту, а shadow dom, мне кажется, пока нет. Во всяком случае такого же хайпа вокруг него не наблюдаю, а значит и жизнь вокруг него не кипит :) Даже проектов с ним пока не видел.
Пока кажется что подход недостаточно зрелый, чтобы взять его в любой проект, не зная куда его выведет кривая меняющихся требований или просто заведомо большой. IMHO
В то время как для приложений с реактом уже всё в достаточной мере стабилизировалось, не в последнюю очередь по части построения архитектуры (не одним реактом, ясное дело, а в плане куда его встраивать, какого должна быть его роль, что нужно кроме него и для чего etc). Грубо говоря, уже на всё готовые рецепты. Плюс бумирующее коммьюнити.
Кроме того, чтобы начать строить приложение с веб-компонентами и shadow dom нужно в очередной раз сломать мозги в направлении новой, скажем так, парадигмы :)
Как до этого было при появлении (очень утрированно)
— ajax (вау, не надо перезагружать страницу, можно что-то подгрузить),
— mvc (вау, всё как в бекэнде),
— mvvm (ого, так можно не фигачить императивно в/из DOM, а декларативно описывать связи между данными и DOM),
— frp (ого, а потоки данных то дают колоссальное упрощение)
и теперь вот virtualdom стал откровением для многих, кто пропустил что-то из этого списка в свое время :)napa3um
10.05.2016 13:02Хайп ещё не значит мейнстрим, стать мейнстримом шансов у стандартов от W3C всё-таки больше, чем у технологии от Facebook (хотя и мёртворожденных стандартов в истории W3C хватает). Причём, больше шансов по вполне технологическим, а не политическим причинам — React тянет одеяло в сторону монолитных приложений с изоморфным кодом (что помогает небольшим командам делать чуть больше на старте проекта), тогда как W3C двигает архитектуру в сторону слабосвязанности, компонентности и микросервисности с чётким разделением жизненных циклов кодовой базы клиентов и серверов (что помогает в дальнейшем масштабировании проекта). Думаю, экономика готовых компонентов и межвендорной универсальности (невозможные без системы стандартов) всё-таки победит экономику снижения издержек «здесь и сейчас» с низкой возможностью реиспользования «потом» (каждый «готовит» React по-своему). IMHO.
mrsum
10.05.2016 11:24Все очень субъективно конечно же, никто не заставляет строить форму с 4мя инпутами «в лоб». И конечно, никто не мешает на BB реализовать абстрактный слой компонентов, и кода будет немногим больше чем в реакт/риот/вуе.
Не бывает плохих и хороших библиотек, бывает недоработанная архитектура и пожалуй все.
boramod
10.05.2016 12:01Пишу на Backbone.js 3 года. Всегда удивляли люди, называющие библиотеку — фреймворком. Из-за подобного отношения и возникают подобные статьи. В т.ч., и в 2016 году.
Synoptic
10.05.2016 12:06Здесь не место для дискуссии «библиотека или фреймворк». В посте описаны конкретные вещи, и библиотекой это названо или фреймворком здесь не важно.
oxidmod
10.05.2016 13:47-1конкретные вещи в этой статье — просто бред. приведенная форма легко распиливается на несколько саб-вью со своими моделями, которые при помощи встроенных ивентов (request, sync) тоглят необходимые инпуты.
ifgeny87
10.05.2016 13:39Любезные, не смог для себя раскрыть сабж.
Объясните, если не сложно, почему бэкбон в 2016 не актуален?Aries_ua
10.05.2016 13:51+2Человек в 2016 году не может отделить логику от шаблонизации. Отсюда все проблемы.
А так, вполне актуален и с задачами справляется.Synoptic
10.05.2016 13:57Вы — можете? Покажите, как?
Aries_ua
10.05.2016 14:05Идете в статью, и там смотрите код, который я приводил. Если не понятно с первого раза — читаете до просветления. Если все же не поймете, придете ко мне — выдам вам табличку — «Почетный бабуин».
Synoptic
10.05.2016 14:09+1Можно конкретный линк на конкретный код, реализующий конкретно это задачу? В этом топике общаемся не абстрактно, а предметно. Не для себя прошу, а для всех(я-то знаю, что вы выдрали кусок кода из своего приложения, и приложили в комментарий).
Aries_ua
12.05.2016 09:53Предметно мы общались в первом топике, пока вы не перешли на грубость.
Кому интересно, могут пойти по ссылке https://habrahabr.ru/post/283054/ и посмотреть код.
Вам приводить нет смысла — вы видите только то, что хотите видеть.
domix32
16.05.2016 21:57-2Почему код на backbone все больше начинает смахивать на php с его пальцеубийственным объявлением переменных?
koceg
Ваша точка зрения имеет право на жизнь и вы даже говорите правильные вещи. Это я, как многолетний пользователь BB, говорю.
Но слова про «бабуинов» и «непробиваемое невежество» сильно понижают авторитет статьи, переводя её на уровень брызганья слюной.
Если цель написания статьи была в том, чтобы помочь коллегам, открыть им глаза, то оскорбления никак этому не способствуют.
Если вы хотели показать, какой вы д'Артаньян, как легко решаете UI-задачи и экономите время коллег и деньги клиентов, а все вокруг быдло — это неблагородная и неблагодарная цель и этим, действительно, лучше заниматься не на Хабре, а в Твиттере.
Synoptic
Нет, это не так в данном случае. Я погорячился в первом комментарии той статьи, человек действительно старался и писал про ES6 применительно к Backbone и можно было и промолчать.
Но я говорю совсем не про автора статьи, а про других комментаторов. Изначально цель была наводящими примерами, без иронии, показать неправоту, но свелось к троллингу с двумя невеждами.
koceg
Я тоже говорю не про автора статьи, а про участников дискуссии.
Цель дискусии — распространение знаний, обмен опытом. И если находятся люди, которые говорят, что вы не совсем правы, нужно попытаться понять, в чём именно, а не скатываться на уровень оскорблений. А если дискуссия вам неинтересна, стоит просто прекратить в ней участвовать. Хабр не место для «троллинга» «невежд».
Ещё раз повторю основную мысль — вне зависимости от того, какую цель вы преследовали, вы выбрали неправильное средство её достижения.
Например, если вдруг сейчас ваша карма поедет вниз, не думайте, что это из-за «бабуинов», которые ослепли от вашего величия и пытаются задавить ваш гений. Просто вы не уважаете собеседников, а это никому не нравится.
napa3um
http://s00.yaplakal.com/pics/pics_original/6/1/4/7251416.jpg