내 응용 프로그램에서 다소 깊을 수있는 중첩보기 설정이 있습니다. 하위 뷰를 초기화, 렌더링 및 추가하는 방법에는 여러 가지가 있지만 일반적인 관행이 무엇인지 궁금합니다.
내가 생각한 커플은 다음과 같습니다.
initialize : function () {
this.subView1 = new Subview({options});
this.subView2 = new Subview({options});
},
render : function () {
this.$el.html(this.template());
this.subView1.setElement('.some-el').render();
this.subView2.setElement('.some-el').render();
}
장점 : 추가로 올바른 DOM 순서를 유지하는 것에 대해 걱정할 필요가 없습니다. 뷰는 초기에 초기화되므로 렌더링 기능에서 한 번에 수행 할 작업이 많지 않습니다.
단점 : 비싼 이벤트 일 수있는 이벤트를 다시 위임해야합니까? 부모 뷰의 렌더 기능은 발생해야하는 모든 서브 뷰 렌더링으로 인해 복잡합니까? tagName
요소 를 설정할 수 없으므로 템플릿은 올바른 tagName을 유지해야합니다.
또 다른 방법:
initialize : function () {
},
render : function () {
this.$el.empty();
this.subView1 = new Subview({options});
this.subView2 = new Subview({options});
this.$el.append(this.subView1.render().el, this.subView2.render().el);
}
장점 : 이벤트를 다시 위임 할 필요가 없습니다. 빈 자리 표시 자만 포함하는 템플릿이 필요하지 않으며 tagName이 뷰에서 다시 정의됩니다.
단점 : 이제 올바른 순서로 항목을 추가해야합니다. 부모 뷰의 렌더링은 여전히 서브 뷰 렌더링에 의해 복잡해집니다.
onRender
이벤트 와 함께 :
initialize : function () {
this.on('render', this.onRender);
this.subView1 = new Subview({options});
this.subView2 = new Subview({options});
},
render : function () {
this.$el.html(this.template);
//other stuff
return this.trigger('render');
},
onRender : function () {
this.subView1.setElement('.some-el').render();
this.subView2.setElement('.some-el').render();
}
장점 : 이제 서브 뷰 로직이 뷰의 render()
메소드 와 분리되었습니다 .
onRender
이벤트 와 함께 :
initialize : function () {
this.on('render', this.onRender);
},
render : function () {
this.$el.html(this.template);
//other stuff
return this.trigger('render');
},
onRender : function () {
this.subView1 = new Subview();
this.subView2 = new Subview();
this.subView1.setElement('.some-el').render();
this.subView2.setElement('.some-el').render();
}
나는이 모든 예제에서 혼합되어 다양한 사례를 일치 시켰습니다 (죄송합니다)하지만 유지하거나 추가 할 것은 무엇입니까? 그리고 당신은 무엇을하지 않겠습니까?
사례 요약 :
initialize
또는에서 하위 뷰를 인스턴스화render
합니까?render
또는에서 모든 하위 뷰 렌더링 논리를 수행onRender
합니까?- 사용
setElement
또는append/appendTo
?
답변
나는 일반적으로 몇 가지 다른 솔루션을 보았거나 사용했습니다.
해결책 1
var OuterView = Backbone.View.extend({
initialize: function() {
this.inner = new InnerView();
},
render: function() {
this.$el.html(template); // or this.$el.empty() if you have no template
this.$el.append(this.inner.$el);
this.inner.render();
}
});
var InnerView = Backbone.View.extend({
render: function() {
this.$el.html(template);
this.delegateEvents();
}
});
이것은 첫 번째 예와 비슷하며 몇 가지 변경 사항이 있습니다.
- 하위 요소를 추가하는 순서는 중요합니다
- 외부 뷰에는 내부 뷰에서 설정할 html 요소가 포함되어 있지 않습니다 (내부 뷰에서 tagName을 지정할 수 있음)
render()
내부 뷰의 요소가 DOM에 배치 된 후에는 내부 뷰의render()
메소드가 다른 요소의 위치 / 크기 (내 경험에서 일반적인 사용 사례)를 기반으로 페이지에 배치 / 크기 조정 하는 경우 유용합니다
해결책 2
var OuterView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
this.$el.html(template); // or this.$el.empty() if you have no template
this.inner = new InnerView();
this.$el.append(this.inner.$el);
}
});
var InnerView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function() {
this.$el.html(template);
}
});
해결 방법 2가 더 깨끗해 보일 수 있지만 경험상 이상한 일이 발생하여 성능에 부정적인 영향을 미쳤습니다.
나는 일반적으로 몇 가지 이유로 솔루션 1을 사용합니다.
- 내 견해의 많은 부분은 이미
render()
메소드 에서 DOM에 의존하고 있습니다. - 외부 뷰를 다시 렌더링하면 뷰를 다시 초기화 할 필요가 없으므로 다시 초기화하면 메모리 누수가 발생할 수 있으며 기존 바인딩에 이상한 문제가 발생할 수 있습니다
호출 할 new View()
때마다 초기화하는 경우 어쨌든 render()
해당 초기화가 호출 delegateEvents()
됩니다. 당신이 표현한 것처럼 반드시 “con”일 필요는 없습니다.
답변
이것은 Backbone의 영원한 문제이며 내 경험상이 질문에 대한 만족스러운 답변은 없습니다. 특히이 사용 사례가 얼마나 일반적인 지에도 불구하고 지침이 거의 없기 때문에 좌절감을 공유합니다. 즉, 나는 보통 두 번째 예와 비슷한 것을 간다.
우선, 나는 당신이 사건을 다시 위임 할 것을 요구하는 것을 기각 할 것입니다. 백본의 이벤트 중심 뷰 모델은 가장 중요한 구성 요소 중 하나이며, 응용 프로그램이 중요하지 않기 때문에 해당 기능을 잃어 버리는 것은 프로그래머의 입에 나쁜 맛을 남길 수 있습니다. 첫 번째 스크래치입니다.
세 번째 예와 관련해서는 기존 렌더링 실습을 마무리 짓고 많은 의미를 부여하지 않는다고 생각합니다. 아마도 실제 이벤트 트리거링 (예를 들어, ” onRender
“이벤트가 아닌 이벤트)을 수행하는 경우 해당 이벤트를 render
자체에 바인딩하는 것이 좋습니다. 당신이 발견하면render
다루기 힘든 복잡한되고, 당신은 너무 적은 파단이있다.
두 번째 예를 다시 살펴보면, 아마도 세 가지 악 가운데 작은 것입니다. 다음은 내 PDF 버전의 42 페이지에있는 Recipe with With Backbone 에서 가져온 예제 코드입니다 .
...
render: function() {
$(this.el).html(this.template());
this.addAll();
return this;
},
addAll: function() {
this.collection.each(this.addOne);
},
addOne: function(model) {
view = new Views.Appointment({model: model});
view.render();
$(this.el).append(view.el);
model.bind('remove', view.remove);
}
이것은 단지 두 번째 예보다 약간 더 복잡한 설정입니다 : 그들은 함수의 집합을에서 지정할, addAll
및addOne
더러운 작업을 수행합니다. 나는이 접근법이 효과적이라고 생각한다. 그러나 여전히 기괴한 뒷맛을 남깁니다. (이 모든 혀 은유를 용서하십시오.)
올바른 순서로 추가하는 요점 : 엄격하게 추가하는 경우에는 제한 사항입니다. 그러나 가능한 모든 템플릿 구성표를 고려해야합니다. 실제로 자리 표시 자 요소 (예 : 비어 div
있거나 ul
)를 replaceWith
원할 경우 적절한 하위 뷰를 보유하는 새 (DOM) 요소를 사용할 수 있습니다. Appending이 유일한 해결책은 아니며 주문 문제를 그렇게 많이 신경 쓰면 확실히 해결할 수는 있지만 디자인 문제로 인해 문제가 발생한다고 생각합니다. 서브 뷰에는 서브 뷰가있을 수 있으며, 적절하다면 서브 뷰를 사용해야합니다. 이렇게하면 다소 트리와 같은 구조가 있습니다. 각 하위보기는 상위보기가 다른 하위보기를 추가하기 전에 모든 하위보기를 순서대로 추가합니다.
안타깝게도 솔루션 # 2는 기본 제공 백본을 사용하기에 가장 좋은 방법 일 것입니다. 타사 라이브러리를 체크 아웃하는 데 관심이 있다면, 내가 보았지만 실제로 아직 시간을 보지 못한 것은 Backbone.LayoutManager 이며 하위 뷰를 추가하는 더 건강한 방법 인 것 같습니다. 그러나 그들조차도 이와 비슷한 문제에 대한 최근 논쟁 을 벌 였습니다.
답변
아직 언급되지 않은 것에 놀랐지 만 마리오네트 사용을 진지하게 고려하고 있습니다.
그것은 특정 뷰 타입 (포함 백본 앱 좀더 구조를 적용 ListView
, ItemView
, Region
및 Layout
), 적절한 추가 Controller
들 및 더 많은.
다음은 Github의 프로젝트 이며 Addy Osmani의 Backbone Fundamentals 책에 있는 훌륭한 안내서 입니다.
답변
나는이 문제에 대한 매우 포괄적 인 해결책을 가지고 있다고 생각합니다. 컬렉션 내의 모델을 변경할 수 있으며 전체 컬렉션이 아닌 뷰만 다시 렌더링 할 수 있습니다. 또한 close () 메소드를 통해 좀비보기 제거를 처리합니다.
var SubView = Backbone.View.extend({
// tagName: must be implemented
// className: must be implemented
// template: must be implemented
initialize: function() {
this.model.on("change", this.render, this);
this.model.on("close", this.close, this);
},
render: function(options) {
console.log("rendering subview for",this.model.get("name"));
var defaultOptions = {};
options = typeof options === "object" ? $.extend(true, defaultOptions, options) : defaultOptions;
this.$el.html(this.template({model: this.model.toJSON(), options: options})).fadeIn("fast");
return this;
},
close: function() {
console.log("closing subview for",this.model.get("name"));
this.model.off("change", this.render, this);
this.model.off("close", this.close, this);
this.remove();
}
});
var ViewCollection = Backbone.View.extend({
// el: must be implemented
// subViewClass: must be implemented
initialize: function() {
var self = this;
self.collection.on("add", self.addSubView, self);
self.collection.on("remove", self.removeSubView, self);
self.collection.on("reset", self.reset, self);
self.collection.on("closeAll", self.closeAll, self);
self.collection.reset = function(models, options) {
self.closeAll();
Backbone.Collection.prototype.reset.call(this, models, options);
};
self.reset();
},
reset: function() {
this.$el.empty();
this.render();
},
render: function() {
console.log("rendering viewcollection for",this.collection.models);
var self = this;
self.collection.each(function(model) {
self.addSubView(model);
});
return self;
},
addSubView: function(model) {
var sv = new this.subViewClass({model: model});
this.$el.append(sv.render().el);
},
removeSubView: function(model) {
model.trigger("close");
},
closeAll: function() {
this.collection.each(function(model) {
model.trigger("close");
});
}
});
용법:
var PartView = SubView.extend({
tagName: "tr",
className: "part",
template: _.template($("#part-row-template").html())
});
var PartListView = ViewCollection.extend({
el: $("table#parts"),
subViewClass: PartView
});
답변
서브 뷰 작성 및 렌더링에 대해서는이 믹스 인을 확인하십시오.
https://github.com/rotundasoftware/backbone.subviews
렌더링 순서, 이벤트를 다시 위임 할 필요 등을 포함하여이 스레드에서 논의 된 많은 문제를 해결하는 미니멀리스트 솔루션입니다. 컬렉션 뷰의 경우 (컬렉션의 각 모델이 하나로 표시됨) subview)는 다른 주제입니다. 내가 알고있는 가장 일반적인 해결책 은 Marionette 의 CollectionView입니다 .
답변
위의 솔루션 중 어느 것도 정말로 좋아하지 않습니다. 렌더 방법에서 수동으로 작업 해야하는 각보기 보다이 구성을 선호합니다.
views
뷰 정의의 객체를 반환하는 함수 또는 객체 일 수 있습니다.- 부모
.remove
를 호출 할 때.remove
가장 낮은 순서의 중첩 된 자식을 호출해야합니다 (하위 서브-하위 뷰에서 항상). - 기본적으로 상위 뷰는 자체 모델 및 컬렉션을 전달하지만 옵션을 추가하고 재정의 할 수 있습니다.
예를 들면 다음과 같습니다.
views: {
'.js-toolbar-left': CancelBtnView, // shorthand
'.js-toolbar-right': {
view: DoneBtnView,
append: true
},
'.js-notification': {
view: Notification.View,
options: function() { // Options passed when instantiating
return {
message: this.state.get('notificationMessage'),
state: 'information'
};
}
}
}
답변
백본은 의도적으로 구축되어이 문제와 다른 많은 문제와 관련하여 “공통적 인”관행이 없었습니다. 가능한 한 의견이 맞지 않아야합니다. 이론적으로 Backbone에 템플릿을 사용할 필요조차 없습니다. render
뷰 의 함수 에서 javascript / jquery를 사용 하여 뷰의 모든 데이터를 수동으로 변경할 수 있습니다. 더 극단적으로 만들기 위해 하나의 특정 render
기능 조차 필요하지 않습니다 . renderFirstName
dom에서 이름 renderLastName
을 업데이트하고 dom에서 성을 업데이트 하는 함수가있을 수 있습니다 . 이 방법을 사용하면 성능 측면에서 더 좋을 것이므로 이벤트를 수동으로 다시 위임 할 필요가 없습니다. 코드는 코드를 읽는 사람에게도 의미가 있습니다 (더 길거나 더 간결한 코드 임에도 불구하고).
그러나 일반적으로 템플릿을 사용하고 단순히 전체 뷰를 파괴하고 다시 작성하는 것에 대한 단점은 없으며 질문자가 다른 작업을 수행하지 않았기 때문에 각 렌더 호출마다 하위 뷰입니다. 그것이 대부분의 사람들이 그들이 접하는 거의 모든 상황에 대해하는 일입니다. 이것이 바로 의견이있는 프레임 워크가이를 기본 동작으로 만드는 이유입니다.