Mam trzy różne sposoby inicjowania i renderowania widoku i jego widoków podrzędnych, a każdy z nich ma inne problemy. Jestem ciekawy, czy istnieje lepszy sposób na rozwiązanie wszystkich problemów:
Scenariusz pierwszy:
Zainicjuj dzieci w funkcji inicjującej rodzica. W ten sposób nie wszystko utknie w renderowaniu, więc blokowanie renderowania jest mniejsze.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
Problemy:
Największym problemem jest to, że wywołanie renderowania na obiekcie nadrzędnym po raz drugi usunie wszystkie powiązania zdarzeń potomnych. (Wynika to z tego, jak
$.html()
działa jQuery .) Można to złagodzić, wywołującthis.child.delegateEvents().render().appendTo(this.$el);
zamiast tego, ale w pierwszym, a najczęściej w przypadku, niepotrzebnie wykonujesz więcej pracy.Dołączając dzieci, zmuszasz funkcję renderowania do znajomości struktury DOM rodziców, aby uzyskać pożądaną kolejność. Co oznacza, że zmiana szablonu może wymagać aktualizacji funkcji renderowania widoku.
Scenariusz drugi:
Zainicjuj dzieci w kadrze nadrzędnym initialize()
, ale zamiast dołączania użyj, setElement().delegateEvents()
aby ustawić element podrzędny w szablonie nadrzędnym .
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
Problemy:
- To sprawia, że
delegateEvents()
konieczne jest teraz, co jest nieznacznie ujemne, ponieważ jest konieczne tylko w przypadku kolejnych połączeń w pierwszym scenariuszu.
Scenariusz trzeci:
render()
Zamiast tego zainicjuj dzieci w metodzie rodzica .
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
Problemy:
Oznacza to, że funkcja renderowania musi być teraz powiązana z całą logiką inicjalizacji.
Jeśli edytuję stan jednego z widoków podrzędnych, a następnie wywołam render na obiekcie nadrzędnym, zostanie utworzone zupełnie nowe dziecko, a cały jego bieżący stan zostanie utracony. Co również wydaje się, że może stać się groźny z powodu wycieków pamięci.
Naprawdę ciekawi mnie, jak chłopaki się tym zajmują. Którego scenariusza byś użył? czy jest czwarty magiczny, który rozwiązuje wszystkie te problemy?
Czy kiedykolwiek śledziłeś stan renderowanego widoku? Powiedz renderedBefore
flagę? Wydaje się naprawdę szalony.
źródło
delegateEvents()
posetElement()
? Zgodnie z dokumentami: „... i przenieś delegowane zdarzenia widoku ze starego elementu na nowy”,setElement
sama metoda powinna obsłużyć delegację zdarzeń.Odpowiedzi:
To świetne pytanie. Szkielet jest świetny ze względu na brak założeń, które sprawia, ale oznacza to, że musisz (zdecydować, jak) wdrożyć takie rzeczy samodzielnie. Po przejrzeniu moich własnych rzeczy stwierdzam, że (w pewnym sensie) używam kombinacji scenariusza 1 i scenariusza 2. Nie sądzę, aby istniał czwarty magiczny scenariusz, ponieważ po prostu wszystko, co robisz w scenariuszu 1 i 2, musi być Gotowe.
Myślę, że najłatwiej byłoby wyjaśnić na przykład, jak lubię sobie z tym poradzić. Powiedzmy, że mam tę prostą stronę podzieloną na określone widoki:
Powiedzmy, że HTML po renderowaniu wygląda mniej więcej tak:
Mamy nadzieję, że to dość oczywiste, jak HTML pasuje do diagramu.
ParentView
Posiada 2 widoki dziecko,InfoView
iPhoneListView
jak również kilka dodatkowych div, z których jeden,#name
, musi być ustawiony w pewnym momencie.PhoneListView
posiada własne widoki potomne, tablicęPhoneView
wpisów.Tak więc na twoje aktualne pytanie. Inicjowanie i renderowanie wykonuję inaczej w zależności od typu widoku. Podzielam swoje poglądy na dwa typy:
Parent
widoki iChild
widoki.Różnica między nimi jest prosta,
Parent
widoki zawierają widoki potomne, aChild
widoki nie. Więc w moim przykładzie,ParentView
iPhoneListView
sąParent
widoki, podczas gdyInfoView
aPhoneView
wpisy sąChild
widoki.Jak wspomniałem wcześniej, największą różnicą między tymi dwiema kategoriami jest to, kiedy można je renderować. W idealnym świecie chcę, aby
Parent
widoki były renderowane tylko raz. Od ich widoków podrzędnych zależy, czy każde renderowanie zostanie zmienione po zmianie modelu (ów).Child
Z drugiej strony, zezwalam na ponowne renderowanie w dowolnym momencie, ponieważ nie mają na nich żadnych innych widoków.Bardziej szczegółowo, dla
Parent
widoków lubię mojeinitialize
funkcje, aby zrobić kilka rzeczy:InfoView
Zostanie przypisany#info
).Krok 1 jest dość oczywisty.
Krok 2, renderowanie, jest wykonywany, aby wszystkie elementy, na których polegają widoki potomne, już istniały, zanim spróbuję je przypisać. W ten sposób wiem, że wszystkie dzieci
events
zostaną poprawnie ustawione i mogę ponownie renderować ich bloki tyle razy, ile chcę, nie martwiąc się o konieczność ponownej delegacji. W rzeczywistości nie widzęrender
tutaj żadnych poglądów dzieci, pozwalam im to robić we własnym zakresieinitialization
.Kroki 3 i 4 są w rzeczywistości obsługiwane w tym samym czasie, w którym przechodzę
el
podczas tworzenia widoku potomnego. Lubię przekazywać tutaj element, ponieważ uważam, że rodzic powinien określić, gdzie jego zdaniem dziecko może umieścić swoją treść.Do renderowania staram się, aby
Parent
widoki były dość proste . Chcęrender
funkcja nic więcej nie renderowała, niż renderowanie widoku nadrzędnego. Bez delegowania zdarzeń, bez renderowania widoków dzieci, nic. Po prostu prosty render.Czasami jednak nie zawsze to działa. Na przykład w moim przykładzie powyżej
#name
element będzie wymagał aktualizacji za każdym razem, gdy zmieni się nazwa w modelu. Jednak ten blok jest częściąParentView
szablonu i nie jest obsługiwany przez dedykowanyChild
widok, więc omijam to. Stworzę coś w rodzajusubRender
funkcji, która zastępuje tylko zawartość#name
elementu i nie musi niszczyć całego#parent
elementu. To może wydawać się włamaniem, ale naprawdę przekonałem się, że działa lepiej niż martwić się o ponowne renderowanie całego DOM i ponowne podłączanie elementów i tym podobne. Gdybym naprawdę chciał to wyczyścić, stworzyłbym nowyChild
widok (podobny doInfoView
), który obsługiwałby#name
blok.Teraz dla
Child
widokówinitialization
jest bardzo podobny doParent
widoków, tylko bez tworzenia kolejnychChild
widoków. Więc:Child
renderowanie widoku jest również bardzo proste, wystarczy wyrenderować i ustawić zawartość mojegoel
. Znowu, nie zadzieraj z delegacją ani nic podobnego.Oto przykładowy kod tego, jak
ParentView
może wyglądać mój :Tutaj możesz zobaczyć moją implementację
subRender
. Mając do czynienia ze zmianamisubRender
zamiastrender
, nie muszę się martwić o odpalenie i odbudowanie całego bloku.Oto przykładowy kod dla
InfoView
bloku:Wiązania są tutaj ważną częścią. Wiążąc się z moim modelem, nigdy nie muszę się martwić o ręczne wywołanie
render
siebie. Jeśli model się zmieni, ten blok zrenderuje się sam, bez wpływu na inne widoki.PhoneListView
Będzie podobny doParentView
, to po prostu trzeba trochę więcej logiki w obu swoichinitialization
andrender
funkcje kolekcji klamek. Sposób obsługi kolekcji zależy od Ciebie, ale musisz przynajmniej wysłuchać wydarzeń związanych z kolekcją i zdecydować, jak chcesz renderować (dołączyć / usunąć lub po prostu zrenderować cały blok). Osobiście lubię dodawać nowe widoki i usuwać stare, a nie renderować cały widok.PhoneView
Będzie prawie identyczny jakInfoView
tylko słuchać modelowych zmian to obchodzi.Mam nadzieję, że to trochę pomogło, daj mi znać, jeśli coś jest mylące lub niewystarczająco szczegółowe.
źródło
render
winitialize
metodzie jest złą praktyką, ponieważ uniemożliwia to zwiększenie wydajności w przypadkach, gdy nie chcesz od razu renderować. Co o tym myślisz?PhoneListView
?Nie jestem pewien, czy to bezpośrednio odpowiada na twoje pytanie, ale myślę, że ma to znaczenie:
http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
Kontekst, w którym utworzyłem ten artykuł, jest oczywiście inny, ale myślę, że dwa oferowane przeze mnie rozwiązania, a także zalety i wady każdego z nich, powinny skłonić cię do pójścia we właściwym kierunku.
źródło
Dla mnie nie wydaje się najgorszym pomysłem na świecie rozróżnienie między początkową konfiguracją a kolejnymi ustawieniami twoich widoków za pomocą jakiejś flagi. Aby było to czyste i łatwe, należy dodać flagę do własnego widoku, który powinien rozszerzyć widok szkieletu (podstawy).
Podobnie jak Derick, nie jestem całkowicie pewien, czy to bezpośrednio odpowiada na twoje pytanie, ale myślę, że warto w tym kontekście wspomnieć.
źródło
Kevin Peel daje świetną odpowiedź - oto moja wersja tl; dr:
źródło
Staram się unikać łączenia takich widoków. Zazwyczaj są dwa sposoby:
Użyj routera
Zasadniczo pozwalasz funkcji routera zainicjować widok nadrzędny i podrzędny. Tak więc widok nie zna się nawzajem, ale router obsługuje to wszystko.
Przekazywanie tego samego elementu do obu widoków
Oba mają wiedzę o tym samym DOM i możesz je zamówić w dowolnym momencie.
źródło
To, co robię, to nadanie każdemu dziecku tożsamości (co Backbone już to dla ciebie zrobił: cid)
Gdy renderowanie wykonuje kontener, użycie „cid” i „tagName” generuje symbol zastępczy dla każdego dziecka, więc w szablonie dzieci nie mają pojęcia o tym, gdzie zostanie umieszczone przez kontener.
niż możesz użyć
nie jest potrzebny żaden określony symbol zastępczy, a kontener generuje tylko symbol zastępczy zamiast struktury DOM dzieci. Cotainer i Children wciąż generują własne elementy DOM i tylko raz.
źródło
Oto lekki miks do tworzenia i renderowania widoków podrzędnych, który moim zdaniem rozwiązuje wszystkie problemy w tym wątku:
https://github.com/rotundasoftware/backbone.subviews
Podejście zastosowane przez tę wtyczkę polega na tworzeniu i renderowaniu widoków podrzędnych po pierwszym renderowaniu widoku nadrzędnego. Następnie, podczas kolejnych renderowań widoku nadrzędnego, $ .dachuj elementy widoku podrzędnego, ponownie renderuj element nadrzędny, a następnie wstaw elementy podrzędne w odpowiednich miejscach i ponownie je wyrenderuj. W ten sposób widoki podrzędne są ponownie wykorzystywane w kolejnych rendererach i nie ma potrzeby ponownego delegowania zdarzeń.
Zauważ, że przypadek widoku kolekcji (gdzie każdy model w kolekcji jest reprezentowany przez jeden widok cząstkowy) jest zupełnie inny i, jak sądzę, zasługuje na własną dyskusję / rozwiązanie. Najlepszym ogólnym rozwiązaniem, jakie znam w tej sprawie, jest CollectionView in Marionette .
EDYCJA: W przypadku widoku kolekcji możesz również sprawdzić tę implementację bardziej skoncentrowaną na interfejsie użytkownika , jeśli potrzebujesz wyboru modeli na podstawie kliknięć i / lub przeciągania i upuszczania w celu zmiany kolejności.
źródło