[javascript] Backbone.js에서 서브 뷰 초기화 및 렌더링을 처리하는 방법은 무엇입니까?

뷰와 하위 뷰를 초기화하고 렌더링하는 세 가지 방법이 있으며 각 방법마다 문제가 있습니다. 모든 문제를 해결하는 더 좋은 방법이 있는지 궁금합니다.


시나리오 하나 :

부모의 초기화 함수에서 자식을 초기화하십시오. 이런 식으로 모든 것이 렌더링에 걸리는 것은 아니므로 렌더링에 대한 차단이 적습니다.

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

    this.$el.html(this.template());

    this.child.render().appendTo(this.$('.container-placeholder');
}

문제 :

  • 가장 큰 문제는 부모에서 렌더링을 두 번 호출하면 모든 자식 이벤트 바인딩이 제거된다는 것입니다. (이는 jQuery의 $.html()작동 방식 때문입니다 .) 이는 this.child.delegateEvents().render().appendTo(this.$el);대신 호출하여 완화 할 수 있지만 첫 번째로, 가장 빈번하게는 불필요하게 더 많은 작업을 수행합니다.

  • 자식을 추가하면 render 함수가 부모 DOM 구조에 대한 지식을 갖도록하여 원하는 순서를 얻을 수 있습니다. 즉, 템플릿을 변경하면 뷰의 렌더 기능을 업데이트해야 할 수도 있습니다.


시나리오 2 :

부모의 initialize()스틸 에서 자식을 초기화 하지만 추가하는 대신 setElement().delegateEvents()자식을 부모 템플릿의 요소로 설정하십시오.

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

    this.$el.html(this.template());

    this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}

문제 :

  • 이로 인해 delegateEvents()지금 필요 하게 되는데, 이는 첫 번째 시나리오에서 후속 호출에만 필요하다는 것보다 약간 부정적입니다.

시나리오 3 :

render()대신 부모의 방법으로 자식을 초기화하십시오 .

initialize : function () {

    //parent init stuff
},

render : function () {

    this.$el.html(this.template());

    this.child = new Child();

    this.child.appendTo($.('.container-placeholder').render();
}

문제 :

  • 이는 렌더링 기능이 이제 모든 초기화 로직과 연결되어야한다는 것을 의미합니다.

  • 자식 뷰 중 하나의 상태를 편집 한 다음 부모에서 렌더링을 호출하면 완전히 새로운 자식이 만들어지고 현재 상태가 모두 손실됩니다. 또한 메모리 누수로 인해 문제가 발생할 수 있습니다.


당신의 사람들이 이것에 걸릴 정말 궁금합니다. 어떤 시나리오를 사용 하시겠습니까? 아니면이 모든 문제를 해결하는 네 번째 마법이 있습니까?

뷰의 렌더링 상태를 추적 한 적이 있습니까? 발언권 renderedBefore플래그를? 정말 고약한 것 같습니다.



답변

이것은 좋은 질문입니다. 백본은 가정이 부족하기 때문에 훌륭하지만, 이와 같은 것을 직접 구현해야하는 방법을 결정해야합니다. 내 자신의 것들을 살펴본 후, 나는 (종류의) 시나리오 1과 시나리오 2의 혼합을 사용한다는 것을 알았습니다. 나는 단순히 시나리오 1과 2에서하는 모든 것이 반드시 있어야하기 때문에 네 번째 마법 시나리오가 있다고 생각하지 않습니다. 끝난.

예제로 처리하는 방법을 설명하는 것이 가장 쉽다고 생각합니다. 이 간단한 페이지가 지정된 뷰로 나뉘어 있다고 가정 해보십시오.

페이지 분류

렌더링 된 후 HTML이 다음과 같다고 가정 해 봅시다.

<div id="parent">
    <div id="name">Person: Kevin Peel</div>
    <div id="info">
        First name: <span class="first_name">Kevin</span><br />
        Last name: <span class="last_name">Peel</span><br />
    </div>
    <div>Phone Numbers:</div>
    <div id="phone_numbers">
        <div>#1: 123-456-7890</div>
        <div>#2: 456-789-0123</div>
    </div>
</div>

HTML이 다이어그램과 어떻게 일치하는지 분명히 알 수 있습니다.

ParentView두 아이 뷰, 보유 InfoView하고 PhoneListView, 그 중 하나뿐만 아니라 몇 가지 추가 된 div, #name어떤 시점에서 설정해야합니다. PhoneListView자체 PhoneView항목 의 자식 뷰를 보유 합니다.

실제 질문에 대해서도 마찬가지입니다. 뷰 유형에 따라 초기화 및 렌더링을 다르게 처리합니다. 뷰를 Parent뷰와 Child뷰의 두 가지 유형으로 나눕니다 .

그들 사이의 차이점은 간단합니다. Parent보기는 자식보기를 유지하지만 Child보기는 그렇지 않습니다. 그래서 내 예에서 ParentViewPhoneListView있는 Parent동안, 뷰 InfoViewPhoneView항목은 다음과 Child전망을 제공합니다.

앞에서 언급했듯이이 두 범주의 가장 큰 차이점은 렌더링이 허용되는 시점입니다. 완벽한 세상에서, 나는 Parent뷰가 한 번만 렌더링 되기를 원합니다 . 모델이 변경 될 때 다시 렌더링을 처리하는 것은 자녀의 관점에 달려 있습니다. Child반면에, 나는 다른 의견이 없기 때문에 필요할 때마다 다시 렌더링 할 수 있습니다.

좀 더 자세하게, Parent뷰의 경우 initialize함수가 몇 가지 작업을 수행하는 것을 좋아합니다.

  1. 내 자신의보기를 초기화
  2. 내 자신의 견해를 표현하십시오
  3. 자식 뷰를 만들고 초기화합니다.
  4. 각 자식보기에 내보기 내의 요소를 지정하십시오 (예 : InfoView할당 됨 #info).

1 단계는 설명이 필요 없습니다.

2 단계 렌더링은 하위 뷰에 의존하는 요소가 할당을 시도하기 전에 이미 존재하도록하기 위해 수행됩니다. 이렇게하면 모든 어린이 events가 올바르게 설정되어 있음을 알 수 있으며 아무 것도 다시 위임하지 않아도 걱정없이 블록을 원하는만큼 다시 렌더링 할 수 있습니다. 나는 실제로 render여기에 어떤 아이의 견해 도 없으며 , 그들 스스로의 견해를 허용합니다 initialization.

3 단계와 4 단계는 실제로 el자식보기를 만드는 동안 전달하는 동시에 처리됩니다 . 부모가 자신의 관점에서 자녀가 내용을 넣을 수있는 곳을 결정해야한다고 생각하면서 여기에 요소를 전달하고 싶습니다.

렌더링을 위해 Parent뷰를 간단하게 유지하려고합니다 . render함수가 부모보기를 렌더링하는 것 이상을 수행하기를 원합니다 . 이벤트 위임, 자식 뷰 렌더링, 아무것도 없습니다. 간단한 렌더링입니다.

때로는 이것이 항상 작동하지는 않습니다. 예를 들어 위의 예 #name에서 모델 내 이름이 변경 될 때마다 요소를 업데이트해야합니다. 그러나이 블록은 ParentView템플릿의 일부이며 전용 Child보기로 처리되지 않으므로이 문제를 해결합니다. 요소 의 내용 바꾸고 전체 요소 를 휴지통에 버릴 필요가없는 일종의 subRender함수를 만들 것 입니다. 이것은 해킹처럼 보일 수 있지만 전체 DOM을 다시 렌더링하고 요소 등을 다시 연결하는 것에 대해 걱정하는 것보다 더 효과적이라는 것을 알았습니다. 정말로 깨끗하게 만들고 싶다면 블록을 처리 하는 새 보기 (와 유사)를 만듭니다 .#name#parentChildInfoView#name

이제 Child뷰 의 경우 추가 뷰를 만들지 않고 뷰 initialization와 매우 유사합니다 . 그래서:ParentChild

  1. 내 관점을 초기화
  2. 내가 관심있는 모델의 변경 사항을 수신 대기하는 설정
  3. 내 관점을 렌더링

Child뷰 렌더링도 매우 간단합니다. 렌더링하고 내 내용을 설정하십시오 el. 다시 말하지만, 위임이나 그와 비슷한 것을 망칠 필요가 없습니다.

다음은 내가 어떻게 ParentView보일지에 대한 예제 코드입니다 .

var ParentView = Backbone.View.extend({
    el: "#parent",
    initialize: function() {
        // Step 1, (init) I want to know anytime the name changes
        this.model.bind("change:first_name", this.subRender, this);
        this.model.bind("change:last_name", this.subRender, this);

        // Step 2, render my own view
        this.render();

        // Step 3/4, create the children and assign elements
        this.infoView = new InfoView({el: "#info", model: this.model});
        this.phoneListView = new PhoneListView({el: "#phone_numbers", model: this.model});
    },
    render: function() {
        // Render my template
        this.$el.html(this.template());

        // Render the name
        this.subRender();
    },
    subRender: function() {
        // Set our name block and only our name block
        $("#name").html("Person: " + this.model.first_name + " " + this.model.last_name);
    }
});

내 구현을 볼 수 있습니다 subRender. subRender대신에 변경 사항을 적용함으로써 render블래스트를 없애고 전체 블록을 다시 작성하는 것에 대해 걱정할 필요가 없습니다.

InfoView블록 의 예제 코드는 다음과 같습니다 .

var InfoView = Backbone.View.extend({
    initialize: function() {
        // I want to re-render on changes
        this.model.bind("change", this.render, this);

        // Render
        this.render();
    },
    render: function() {
        // Just render my template
        this.$el.html(this.template());
    }
});

바인딩은 여기서 중요한 부분입니다. 내 모델에 바인딩함으로써 수동으로 전화하는 것에 대해 걱정할 필요가 없습니다 render. 모델이 변경되면이 블록은 다른 뷰에 영향을주지 않고 다시 렌더링됩니다.

(가) PhoneListView받는 유사합니다 ParentView, 당신은 모두 당신에 좀 더 논리가 필요합니다 initializationrender핸들 컬렉션에 기능을. 컬렉션을 처리하는 방법은 실제로 당신에게 달려 있지만 최소한 컬렉션 이벤트를 듣고 렌더링하려는 방법을 결정해야합니다 (추가 / 제거 또는 전체 블록을 다시 렌더링). 개인적으로 새로운 뷰를 추가하고 이전 뷰를 제거하고 전체 뷰를 다시 렌더링하지 않습니다.

PhoneView받는 사람 거의 동일합니다 InfoView단지 그것을 대한 관심 모델이 변경을 듣고.

잘만되면 이것이 약간 도움이되었으므로, 혼란 스럽거나 상세하지 않은 사항이 있으면 알려주십시오.


답변

이것이 귀하의 질문에 직접 대답하는지 확실하지 않지만 관련이 있다고 생각합니다.

http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/

물론이 기사를 설정 한 맥락은 다르지만, 내가 제공하는 두 가지 솔루션은 각각의 장단점과 함께 올바른 방향으로 나아가 야한다고 생각합니다.


답변

나에게 어떤 종류의 깃발을 통해 뷰의 초기 설정과 후속 설정을 구별하는 것은 세계에서 최악의 생각처럼 보이지 않습니다. 이 깨끗하고 쉽게 만들려면 고유 한 뷰에 플래그를 추가하여 백본 (기본) 뷰를 확장해야합니다.

Derick과 동일 이것이 귀하의 질문에 직접 대답하는지 확실하지는 않지만 적어도이 맥락에서 언급 할 가치가 있다고 생각합니다.

참조 : 백본에서 이벤트 버스 사용


답변

Kevin Peel은 훌륭한 답변을 제공합니다. 여기 내 tl; dr 버전이 있습니다.

initialize : function () {

    //parent init stuff

    this.render(); //ANSWER: RENDER THE PARENT BEFORE INITIALIZING THE CHILD!!

    this.child = new Child();
},


답변

이러한 뷰 사이의 연결을 피하려고합니다. 내가 보통 두 가지 방법이 있습니다.

라우터 사용

기본적으로 라우터 기능을 통해 부모 및 자식보기를 초기화 할 수 있습니다. 따라서 뷰에는 서로에 대한 지식이 없지만 라우터는 모든 것을 처리합니다.

동일한 엘을 두 뷰 모두에 전달

this.parent = new Parent({el: $('.container-placeholder')});
this.child = new Child({el: $('.container-placeholder')});

둘 다 동일한 DOM에 대한 지식이 있으며 원하는대로 주문할 수 있습니다.


답변

내가하는 일은 각 어린이에게 정체성을 부여하는 것입니다 (Backbone은 이미 당신을 위해 그것을했습니다 : cid)

Container가 렌더링을 수행 할 때 ‘cid’및 ‘tagName’을 사용하면 모든 자식에 대한 자리 표시자가 생성되므로 템플릿에서 자식은 Container가 어디에 배치 될지 모릅니다.

<tagName id='cid'></tagName>

당신이 사용할 수있는 것보다

Container.render()
Child.render();
this.$('#'+cid).replaceWith(child.$el);
// the rapalceWith in jquery will detach the element 
// from the dom first, so we need re-delegateEvents here
child.delegateEvents();

지정된 자리 표시자가 필요하지 않으며 컨테이너는 자식의 DOM 구조가 아닌 자리 표시 자만 생성합니다. Cotainer와 Children은 여전히 ​​고유 DOM 요소를 한 번만 생성합니다.


답변

다음은 서브 뷰를 생성하고 렌더링하기위한 경량 믹스 인입니다.이 스레드의 모든 문제를 해결합니다.

https://github.com/rotundasoftware/backbone.subviews

이 플러그에 의해 취해진 접근법 은 상위 뷰가 처음 렌더링 된 후 서브 뷰를 작성하고 렌더링 하는 것입니다. 그런 다음 상위 뷰의 후속 렌더링에서 하위 뷰 요소를 $ .detach하고 상위 뷰를 다시 렌더링 한 다음 하위 뷰 요소를 적절한 위치에 삽입하고 다시 렌더링하십시오. 이런 식으로 하위 뷰 객체는 후속 렌더링에서 재사용되며 이벤트를 다시 위임 할 필요가 없습니다.

컬렉션 뷰 (컬렉션의 각 모델이 하나의 하위 뷰로 표시되는)의 경우는 상당히 다르며 자체 토론 / 솔루션이 장점이라고 생각합니다. 내가 알고있는 가장 일반적인 해결책 은 MarionetteCollectionView입니다 .

편집 : 컬렉션 뷰의 경우 클릭 및 / 또는 드래그 앤 드롭을 기반으로 모델을 선택 해야하는 경우이 UI 중심 구현 을 확인하고 싶을 수도 있습니다 .