好的,所以在我看來有很多方法可以解決這個問題。我會列出所有我想到的,希望能夠與您合作,或者至少能夠激發您找到最佳解決方案。
我並不完全反對TJ的回答。如果您只是在網站加載時對所有產品執行collection.fetch()操作(用戶通常希望涉及一些加載時間),那麼您就擁有了所有數據,並且只需將數據傳遞給他提到。他建議的和我通常做的之間唯一的區別是,我通常在我所有的觀點中都有對應用程序的引用。所以,例如在我的初始化功能app.js我會做這樣的事情。
initialize: function() {
var options = {app: this}
var views = {
productsView: new ProductsView(options)
};
this.collections = {
products: new Products()
}
// This session model is just a sandbox object that I use
// to store information about the user's session. I would
// typically store things like currentlySelectedProductId
// or lastViewedProduct or things like that. Then, I just
// hang it off the app for ease of access.
this.models = {
session: new Session()
}
}
然後在我的productsView.js初始化功能我這樣做:
initialize: function(options) {
this.app = options.app;
this.views = {
headerView: new HeaderView(options),
productsListView: new ProductsListView(options),
footerView: new FooterView(options)
};
}
,我在productsView.js初始化創建的子視圖是任意的。我主要是試圖證明我繼續將該選項對象傳遞給視圖的子視圖。
這樣做是否允許每個視圖,無論是頂層視圖還是深層嵌套的子視圖,每個視圖都知道每個其他視圖,並且每個視圖都具有對應用程序數據的引用。
這兩個代碼示例還介紹了儘可能精確地確定功能範圍的概念。不要試圖去看待一切。將功能傳遞給其他視圖,以便每個視圖都有一個特定的用途。這將促進視圖的重用。特別複雜的模式。
現在回到實際的話題。如果您打算繼續前面加載所有產品,您應該在哪裏獲取它們?因爲就像你說過的,你不想讓一個空白頁面坐在你的用戶面前。所以,我的建議是欺騙他們。儘可能多地加載頁面,並且只阻止需要加載數據的部分。對用戶來說,這個頁面看起來像是在加載,而你實際上正在幕後進行工作。如果您可以誘使用戶認爲頁面正在穩定加載,那麼他們不太可能對頁面加載不耐煩。
因此,引用來自productsView.js的初始化,您可以繼續並讓headerView和footerView呈現。然後,您可以在productsListView的呈現中執行您的獲取。
現在,我知道你在想什麼。我失去了主意嗎?如果您在渲染函數中執行提取操作,那麼在我們觸及實際呈現productsViewList模板的那一行之前,調用將沒有時間返回。好吧,幸運的是,有幾種方法。一種方法是使用Promises。但是,我通常這樣做的方式就是使用render函數作爲自己的回調函數。我來給你展示。
render: function(everythingLoaded) {
var _this = this;
if(!!everythingLoaded) {
this.$el.html(_.template(this.template(this)));
}
else {
// load a spinner template here if you want a spinner
this.app.collection.products.fetch()
.done(function(data) {
_this.render(true);
})
.fail(function(data) {
console.warn('Error: ' + data.status);
});
}
return this;
}
現在,通過這種結構化渲染,實際模板將不會加載,直到數據完全加載。
雖然我們在這裏有渲染功能,但我想介紹一下我在哪裏使用的另一個概念。我稱之爲postRender。這是一個函數,當模板加載完成後,我執行任何依賴於DOM元素的代碼。如果你只是編寫一個普通的.html頁面,那麼這是傳統上在$(document).ready(function(){})中的代碼;。值得注意的是我沒有爲我的模板使用.html文件。我使用嵌入式JavaScript文件(.ejs)。接下來,postRender函數是我已經基本添加到我的鍋爐板代碼中的一個函數。因此,無論何時,我都會在代碼庫中調用渲染來查看視圖,我立即將postRender鏈接到它。我也像使用渲染一樣使用postRender作爲自己的回調。所以,基本上以前的代碼示例在我的代碼庫中看起來像這樣。
render: function(everythingLoaded) {
var _this = this;
if(!!everythingLoaded) {
this.$el.html(_.template(this.template(this)));
}
else {
// load a spinner template here if you want a spinner
this.app.collection.products.fetch()
.done(function(data) {
_this.render(true).postRender(true);
})
.fail(function(data) {
console.warn('Error: ' + data.status);
});
}
return this;
},
postRender: function(everythingLoaded) {
if(!!everythingLoaded) {
// do any DOM manipulation to the products list after
// it's loaded and rendered
}
else {
// put code to start spinner
}
return this;
}
通過像這樣鏈接這些函數,我們保證它們將按順序運行。
============================================== ===========================
所以,這是解決問題的一種方法。但是,您提到您不希望預先加載所有產品,因爲擔心請求可能需要很長時間。
側面說明:你真的應該考慮購買與產品有關的任何信息,請致電可能導致調用花費相當長的時間,使大塊的信息的單獨請求。我有一種感覺,如果用戶能夠快速獲得核心信息,並且與每個產品相關的縮略圖需要稍長的時間才能加載,那麼用戶將會更加容忍數據需要加載一段時間才能加載。世界。這只是我的看法。
另一種方式來解決這個問題,如果你只想去一個特定的產品頁面,然後只需實現渲染/ postRender模式我上述對個人的ProductView。但是請注意,您的productView.js可能要看起來像這樣:
initialize: function(options) {
this.app = options.app;
this.productId = options.productId;
this.views = {
headerView: new HeaderView(options),
productsListView: new ProductsListView(options),
footerView: new FooterView(options)
};
}
render: function(everythingLoaded) {
var _this = this;
if(!!everythingLoaded) {
this.$el.html(_.template(this.template(this)));
}
else {
// load a spinner template here if you want a spinner
this.app.collection.products.get(this.productId).fetch()
.done(function(data) {
_this.render(true).postRender(true);
})
.fail(function(data) {
console.warn('Error: ' + data.status);
});
}
return this;
},
postRender: function(everythingLoaded) {
if(!!everythingLoaded) {
// do any DOM manipulation to the product after it's
// loaded and rendered
}
else {
// put code to start spinner
}
return this;
}
這裏唯一的區別是,產品ID是沿着選項傳遞對象來初始化,然後多數民衆贊成拉出使用在渲染函數中的.fetch中。
============================================== =========================== 總之,我希望這有助於。我不確定我是否回答了您的所有問題,但我認爲我對他們做了很好的傳球。爲了這個太久,我現在就要停下來,讓你消化這個問題,並提出你有任何問題。我想我可能不得不做至少1更新這篇文章進一步沖刷出來。
我相信你所說的是延遲獲取是不可避免的,但是在加載模型時顯示旋鈕或處理錯誤的複雜性不是模板的一部分。你有三個不同的模板? - 一個用於成功,一個用於「加載調試器」的情況,另一個用於獲取失敗的情況?我開始相信沒有真正的乾淨解決方案。 'render()'中的承諾構造優雅。 'postRender'解決了一個不同的問題,即將子視圖嵌入到尚未附加到根DOM的父元素中。 –
由於您設置的方式,是的,我相信延遲獲取是不可避免的。但是,我在很多地方都成功地做到了這一點。除非失敗模板變得複雜,否則我通常不會創建一個全新的失敗模板。相反,我通常會做的只是將代碼包裝在模板中的try/catch中,並且在catch中我會放入類似'
另外,你是正確的,postRender完全解決了完全不同的問題。不過,我認爲這將防止未來的問題,所以我只是繼續前進,然後撒了它。但是,我會提到,如果你最終要做任何嵌套視圖,然後我會看[殭屍視圖](https:// lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/)並創建一個[assign function](http://ianstormtaylor.com/assigning-backbone-subviews訂做偶數清潔劑/)。這些是在我工作的代碼庫中遍佈各處的兩件事情。 –