Làm cách nào để xử lý việc khởi tạo và hiển thị các cuộc phỏng vấn trong Backbone.js?


199

Tôi có ba cách khác nhau để khởi tạo và hiển thị chế độ xem và các lượt xem của nó, và mỗi cách trong số chúng có các vấn đề khác nhau. Tôi tò mò muốn biết liệu có cách nào tốt hơn để giải quyết tất cả các vấn đề:


Kịch bản thứ nhất:

Khởi tạo trẻ trong chức năng khởi tạo của cha mẹ. Bằng cách này, không phải mọi thứ đều bị kẹt trong kết xuất để ít bị chặn hơn khi kết xuất.

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

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

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

Vấn đề:

  • Vấn đề lớn nhất là việc gọi render trên cha mẹ lần thứ hai sẽ loại bỏ tất cả các ràng buộc sự kiện của con. (Điều này là do cách thức $.html()hoạt động của jQuery .) Điều này có thể được giảm thiểu bằng cách gọi this.child.delegateEvents().render().appendTo(this.$el);thay thế, nhưng sau đó là trường hợp đầu tiên và thường xuyên nhất, bạn đang làm việc nhiều hơn một cách không cần thiết.

  • Bằng cách nối thêm cho trẻ em, bạn buộc chức năng kết xuất phải có kiến ​​thức về cấu trúc DOM của cha mẹ để bạn có được thứ tự bạn muốn. Điều đó có nghĩa là thay đổi mẫu có thể yêu cầu cập nhật chức năng kết xuất của chế độ xem.


Kịch bản hai:

Khởi tạo con trong phần initialize()còn lại của cha mẹ , nhưng thay vì nối thêm, hãy sử dụng setElement().delegateEvents()để đặt con thành một thành phần trong mẫu bố mẹ.

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

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

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

Các vấn đề:

  • Điều này làm cho sự delegateEvents()cần thiết bây giờ, đó là một tiêu cực nhỏ so với nó chỉ cần thiết cho các cuộc gọi tiếp theo trong kịch bản đầu tiên.

Kịch bản thứ ba:

render()Thay vào đó, khởi tạo con theo phương pháp của cha mẹ .

initialize : function () {

    //parent init stuff
},

render : function () {

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

    this.child = new Child();

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

Các vấn đề:

  • Điều này có nghĩa là chức năng kết xuất bây giờ cũng phải được gắn với tất cả logic khởi tạo.

  • Nếu tôi chỉnh sửa trạng thái của một trong các chế độ xem con, sau đó gọi kết xuất trên cha mẹ, một đứa trẻ hoàn toàn mới sẽ được tạo và tất cả trạng thái hiện tại của nó sẽ bị mất. Điều này cũng có vẻ như có thể gây xúc động cho rò rỉ bộ nhớ.


Thực sự tò mò để có được những người của bạn về điều này. Kịch bản nào bạn sẽ sử dụng? hoặc có một phép thuật thứ tư giải quyết tất cả những vấn đề này?

Bạn đã bao giờ theo dõi trạng thái kết xuất cho Chế độ xem chưa? Nói một renderedBeforelá cờ? Có vẻ thực sự tưng bừng.


1
Tôi thường không sotring các tham chiếu đến các quan điểm con trên các quan điểm chính vì hầu hết các giao tiếp xảy ra thông qua các mô hình / bộ sưu tập và các sự kiện được kích hoạt bởi các thay đổi đối với các quan điểm này. Mặc dù trường hợp thứ 3 là gần nhất với những gì tôi đang sử dụng hầu hết thời gian. Trường hợp 1 nơi nó có ý nghĩa. Ngoài ra, hầu hết thời gian bạn không nên đăng ký lại toàn bộ chế độ xem mà thay vào đó là phần đã thay đổi
Tom Tu

Chắc chắn, tôi chắc chắn chỉ hiển thị các phần đã thay đổi khi có thể, nhưng ngay cả khi đó tôi nghĩ rằng chức năng kết xuất nên có sẵn và dù sao cũng không phá hủy. Làm thế nào để bạn xử lý không có một tài liệu tham khảo cho trẻ em trong cha mẹ?
Ian Storm Taylor

Thông thường tôi nghe các sự kiện trên các mô hình liên quan đến các khung nhìn con - nếu tôi muốn làm một cái gì đó tùy chỉnh hơn thì tôi ràng buộc các sự kiện với các cuộc phỏng vấn. Nếu loại 'giao tiếp' này là bắt buộc thì tôi thường tạo phương thức trợ giúp để tạo các cuộc phỏng vấn gắn kết các sự kiện, v.v.
Tom Tu

1
Để có một cuộc thảo luận liên quan ở cấp độ cao hơn, hãy xem: stackoverflow.com/questions/10077185/õ .
Ben Roberts

2
Trong Kịch bản Hai, tại sao cần phải gọi delegateEvents()sau setElement()? Theo tài liệu: "... và di chuyển các sự kiện được ủy nhiệm của khung nhìn từ phần tử cũ sang phần tử mới", setElementchính phương thức sẽ xử lý việc ủy ​​quyền lại các sự kiện.
ndamborsky 29/03/13

Câu trả lời:


260

Đâ là một câu hỏi tuyệt vời. Xương sống là tuyệt vời vì thiếu các giả định mà nó đưa ra, nhưng nó có nghĩa là bạn phải (quyết định làm thế nào) tự thực hiện những điều như thế này. Sau khi xem qua nội dung của riêng tôi, tôi thấy rằng tôi (loại) sử dụng kết hợp kịch bản 1 và kịch bản 2. Tôi không nghĩ kịch bản ma thuật thứ 4 tồn tại bởi vì, đơn giản, mọi thứ bạn làm trong kịch bản 1 & 2 phải là làm xong.

Tôi nghĩ rằng sẽ dễ dàng nhất để giải thích cách tôi muốn xử lý nó bằng một ví dụ. Nói rằng tôi có trang đơn giản này được chia thành các chế độ xem được chỉ định:

Sự cố trang

Giả sử HTML là, sau khi được hiển thị, một cái gì đó như thế này:

<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>

Hy vọng rằng HTML khá phù hợp với sơ đồ.

Các ParentViewtổ chức 2 lần xem đứa trẻ,InfoViewPhoneListViewcũng như một vài div bổ sung, một trong số đó #name, cần được đặt ở một số điểm. PhoneListViewgiữ quan điểm con của riêng nó, một loạt các PhoneViewmục.

Vì vậy, câu hỏi thực tế của bạn. Tôi xử lý khởi tạo và kết xuất khác nhau dựa trên kiểu xem. Tôi chia quan điểm của tôi thành hai loại, Parentquan điểm và Childquan điểm.

Sự khác biệt giữa chúng là đơn giản, các Parentkhung nhìn giữ các khung nhìn con trong khi các Childkhung nhìn thì không. Vì vậy, trong ví dụ của tôi, ParentViewPhoneListViewlà các Parentkhung nhìn, trong khiInfoView và các PhoneViewmục là các Childkhung nhìn.

Giống như tôi đã đề cập trước đây, sự khác biệt lớn nhất giữa hai loại này là khi chúng được phép kết xuất. Trong một thế giới hoàn hảo, tôi muốnParent khung nhìn chỉ hiển thị một lần. Tùy thuộc vào chế độ xem con của họ để xử lý bất kỳ kết xuất lại nào khi (các) mô hình thay đổi. Childmặt khác, tôi cho phép hiển thị lại bất cứ lúc nào họ cần vì họ không có bất kỳ quan điểm nào khác dựa vào họ.

Chi tiết hơn một chút, để Parentxem tôi thíchinitialize chức năng thực hiện một số điều:

  1. Khởi tạo quan điểm của riêng tôi
  2. Kết xuất quan điểm của riêng tôi
  3. Tạo và khởi tạo bất kỳ khung nhìn con.
  4. Chỉ định mỗi đứa trẻ xem một yếu tố trong chế độ xem của tôi (ví dụ: InfoViewsẽ được chỉ định #info).

Bước 1 là khá tự giải thích.

Bước 2, kết xuất, được thực hiện sao cho mọi phần tử mà khung nhìn con dựa vào đã tồn tại trước khi tôi cố gắng gán chúng. Bằng cách này, tôi biết tất cả trẻ em eventssẽ được đặt chính xác và tôi có thể kết xuất lại các khối của chúng nhiều lần như tôi muốn mà không phải lo lắng về việc phải ủy quyền lại bất cứ điều gì. Tôi thực sự không rendercó bất kỳ quan điểm trẻ em nào ở đây, tôi cho phép chúng làm điều đó trong chính chúnginitialization .

Bước 3 và 4 thực sự được xử lý cùng lúc khi tôi vượt qua el trong khi tạo chế độ xem con. Tôi muốn thông qua một yếu tố ở đây vì tôi cảm thấy phụ huynh nên xác định nơi nào trong quan điểm riêng của mình, đứa trẻ được phép đặt nội dung của nó.

Để kết xuất, tôi cố gắng giữ nó khá đơn giản để Parentxem. Tôi muốn renderchức năng không làm gì hơn là hiển thị khung nhìn cha. Không có phái đoàn sự kiện, không hiển thị quan điểm trẻ em, không có gì. Chỉ là một kết xuất đơn giản.

Đôi khi điều này không phải lúc nào cũng làm việc. Ví dụ trong ví dụ của tôi ở trên, #namephần tử sẽ cần được cập nhật bất cứ khi nào tên trong mô hình thay đổi. Tuy nhiên, khối này là một phần của ParentViewmẫu và không được xử lý bởi Childchế độ xem chuyên dụng , vì vậy tôi làm việc xung quanh đó. Tôi sẽ tạo ra một số loại subRenderchức năng chỉ thay thế nội dung của #namephần tử, và không phải rác toàn bộ #parentphần tử. Điều này có vẻ giống như một hack, nhưng tôi thực sự thấy nó hoạt động tốt hơn là phải lo lắng về việc kết xuất lại toàn bộ DOM và gắn lại các yếu tố và như vậy. Nếu tôi thực sự muốn làm cho nó sạch sẽ, tôi sẽ tạo một Childchế độ xem mới (tương tự như InfoView) sẽ xử lý #namekhối.

Bây giờ đối với các Childkhung nhìn, cái initializationnày khá giống với các Parentkhung nhìn, chỉ cần không tạo ra bất kỳ Childkhung nhìn nào nữa . Vì thế:

  1. Khởi tạo quan điểm của tôi
  2. Thiết lập liên kết lắng nghe bất kỳ thay đổi nào đối với mô hình tôi quan tâm
  3. Hiển thị quan điểm của tôi

Childxem kết xuất cũng rất đơn giản, chỉ cần kết xuất và thiết lập nội dung của tôi el. Một lần nữa, không gây rối với phái đoàn hoặc bất cứ điều gì như thế.

Dưới đây là một số mã ví dụ về những gì tôi ParentViewcó thể trông như thế nào:

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);
    }
});

Bạn có thể thấy việc thực hiện của tôi subRenderở đây. Bằng cách thay đổi ràng buộc subRenderthay vì render, tôi không phải lo lắng về việc nổ tung và xây dựng lại toàn bộ khối.

Đây là mã ví dụ cho InfoViewkhối:

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());
    }
});

Các ràng buộc là phần quan trọng ở đây. Bằng cách ràng buộc với mô hình của tôi, tôi không bao giờ phải lo lắng về việc tự gọi rendermình. Nếu mô hình thay đổi, khối này sẽ tự kết xuất lại mà không ảnh hưởng đến bất kỳ chế độ xem nào khác.

Điều PhoneListViewnày sẽ tương tự như ParentView, bạn sẽ chỉ cần thêm một chút logic trong cả chức năng initializationrenderchức năng của bạn để xử lý các bộ sưu tập. Cách bạn xử lý bộ sưu tập thực sự tùy thuộc vào bạn, nhưng ít nhất bạn sẽ cần lắng nghe các sự kiện của bộ sưu tập và quyết định cách bạn muốn kết xuất (nối / xóa hoặc chỉ hiển thị lại toàn bộ khối). Cá nhân tôi thích nối các khung nhìn mới và xóa các khung nhìn cũ, không hiển thị lại toàn bộ khung nhìn.

PhoneViewsẽ gần giống với InfoView, chỉ lắng nghe mô hình thay đổi mà nó quan tâm.

Hy vọng rằng điều này đã giúp một chút, xin vui lòng cho tôi biết nếu có gì khó hiểu hoặc không đủ chi tiết.


8
Thực sự giải thích kỹ lưỡng cảm ơn. Mặc dù vậy, tôi đã nghe và có xu hướng đồng ý rằng việc gọi renderbên trong initializephương thức là một cách thực hành tồi, bởi vì nó ngăn bạn trở nên hiệu quả hơn trong trường hợp bạn không muốn kết xuất ngay lập tức. Bạn nghĩ gì về điều này?
Ian Storm Taylor

Chắc chắn rồi. Hmm ... Tôi chỉ cố gắng để các chế độ xem được khởi tạo nên được hiển thị ngay bây giờ (hoặc có thể được hiển thị trong tương lai gần, ví dụ: một biểu mẫu chỉnh sửa bị ẩn ngay bây giờ nhưng được hiển thị khi nhấp vào liên kết chỉnh sửa), vì vậy chúng sẽ được hiển thị ngay lập tức. Nếu nói, tôi có trường hợp một khung nhìn mất một lúc để hiển thị, cá nhân tôi không tự tạo chế độ xem cho đến khi nó cần được hiển thị, do đó, không có khởi tạo và kết xuất sẽ diễn ra cho đến khi cần thiết. Nếu bạn có một ví dụ tôi có thể cố gắng đi sâu vào chi tiết hơn về cách tôi xử lý nó ...
Kevin Peel

5
Đó là một hạn chế lớn khi không thể kết xuất lại ParentView. Tôi có một tình huống về cơ bản tôi có một điều khiển tab, trong đó mỗi tab hiển thị ParentView khác nhau. Tôi muốn chỉ hiển thị lại ParentView hiện tại khi một tab được nhấp lần thứ hai, nhưng làm như vậy sẽ khiến các sự kiện trong ChildViews bị mất (tôi tạo con trong init và hiển thị chúng trong kết xuất). Tôi đoán tôi 1) ẩn / hiển thị thay vì kết xuất, 2) ủy nhiệm trong kết xuất của ChildViews hoặc 3) tạo trẻ em trong kết xuất hoặc 4) không lo lắng về sự hoàn hảo. trước đó là một vấn đề và tạo lại ParentView mỗi lần. Hmmmm ....
Paul Hoenecke

Tôi nên nói rằng đó là một hạn chế trong trường hợp của tôi. Giải pháp này sẽ hoạt động tốt nếu bạn không thử kết xuất lại phụ huynh :) Tôi không có câu trả lời hay nhưng tôi có cùng câu hỏi với OP. Vẫn hy vọng tìm được cách 'xương sống' thích hợp. Tại thời điểm này, tôi đang nghĩ ẩn / hiển thị và không kết xuất lại là cách phù hợp với tôi.
Paul Hoenecke

1
Làm thế nào để bạn vượt qua bộ sưu tập vào đứa trẻ PhoneListView?
Trí Nguyễn


6

Đối với tôi, nó dường như không phải là ý tưởng tồi tệ nhất trên thế giới để phân biệt giữa thiết lập quan hệ và thiết lập quan điểm tiếp theo của bạn thông qua một số loại cờ. Để làm cho điều này rõ ràng và dễ dàng, cờ nên được thêm vào Chế độ xem của chính bạn, điều này sẽ mở rộng Chế độ xem Xương sống (Cơ sở).

Giống như Derick Tôi không hoàn toàn chắc chắn nếu điều này trực tiếp trả lời câu hỏi của bạn nhưng tôi nghĩ nó có thể ít nhất là đáng được đề cập trong bối cảnh này.

Xem thêm: Sử dụng Eventbus trong xương sống


3

Kevin Peel đưa ra một câu trả lời tuyệt vời - đây là phiên bản tl; dr của tôi:

initialize : function () {

    //parent init stuff

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

    this.child = new Child();
},

2

Tôi đang cố gắng tránh khớp nối giữa các chế độ xem như thế này. Có hai cách tôi thường làm:

Sử dụng bộ định tuyến

Về cơ bản, bạn để chức năng bộ định tuyến của mình khởi tạo chế độ xem cha và con. Vì vậy, khung nhìn không có kiến ​​thức về nhau, nhưng bộ định tuyến xử lý tất cả.

Vượt qua cùng một el cho cả hai quan điểm

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

Cả hai đều có kiến ​​thức về cùng một DOM và bạn có thể đặt hàng chúng theo bất cứ cách nào bạn muốn.


2

Những gì tôi làm là cung cấp cho mỗi đứa trẻ một danh tính (mà Backbone đã làm điều đó cho bạn: cid)

Khi Container thực hiện kết xuất, sử dụng 'cid' và 'tagName' sẽ tạo một trình giữ chỗ cho mọi trẻ em, do đó, trong mẫu, trẻ em không biết nơi Container sẽ được đặt ở đâu.

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

hơn bạn có thể sử dụng

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();

không có trình giữ chỗ được chỉ định là cần thiết và Container chỉ tạo trình giữ chỗ chứ không phải cấu trúc DOM của trẻ em. Cotainer và Children vẫn đang tạo các phần tử DOM riêng và chỉ một lần.


1

Đây là một mixin trọng lượng nhẹ để tạo và hiển thị các cuộc phỏng vấn, mà tôi nghĩ rằng giải quyết tất cả các vấn đề trong chủ đề này:

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

Cách tiếp cận được thực hiện bởi trình cắm này là tạo và kết xuất các lượt xem sau lần đầu tiên chế độ xem chính được hiển thị. Sau đó, trong các lần hiển thị tiếp theo của chế độ xem chính, $ .detach các phần tử xem phụ, kết xuất lại phần gốc, sau đó chèn các phần tử xem phụ vào các vị trí thích hợp và kết xuất lại chúng. Bằng cách này, các đối tượng phỏng vấn được sử dụng lại trong các lần kết xuất tiếp theo và không cần phải ủy quyền lại các sự kiện.

Lưu ý rằng trường hợp của chế độ xem bộ sưu tập (trong đó mỗi mô hình trong bộ sưu tập được thể hiện bằng một khung nhìn phụ) khá khác nhau và đáng để tôi thảo luận / giải pháp riêng. Giải pháp chung tốt nhất tôi biết trong trường hợp đó là CollectionView trong Marionette .

EDIT: Đối với trường hợp chế độ xem bộ sưu tập, bạn cũng có thể muốn kiểm tra triển khai tập trung vào giao diện người dùng nhiều hơn này , nếu bạn cần lựa chọn các mô hình dựa trên các lần nhấp và / hoặc kéo và thả để sắp xếp lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.