[英]Single Object Model for AngularJS
我正在AngularJS中嘗試一些特別是設計模型的最佳實踐。 我認為AngularJS的一個真正的力量是
'當模型更改視圖得到更新時,反之亦然'
。 這導致了明顯的事實
“在任何特定時間,模型都是應用程序狀態的唯一真實來源”
現在,在閱讀了關於設計正確模型結構的各種博客文章后,我決定使用類似“單一對象”的方法。 這意味着整個應用程序狀態都保存在一個JavaScript對象中。
例如待辦事項
$scope.appState = {
name: "toDoApp",
auth: {
userName: "John Doe",
email: "john@doe.com",
token: "DFRED%$%ErDEFedfereRWE2324deE$%^$%#423",
},
toDoLists: [
{ name: "Personal List",
items: [
{ id: 1, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0},
{ id: 2, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 1},
{ id: 3, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}]
},
{ name: "Work List",
items: [
{ id: 1, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : -1},
{ id: 2, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0},
{ id: 3, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}]
},
{ name: "Family List",
items: [
{ id: 1, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 2},
{ id: 2, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0},
{ id: 3, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 5}]
}
]
};
根據應用程序的復雜性,此對象將變為巨大。 關於這一點,我有以下的擔憂,並將其標記為問題。
這種方法是否可取? 當應用程序開始擴展時,我將面臨哪些缺點和缺陷?
如果對象的一小部分被更新,則表示優先級增加,角度會單獨智能地重新渲染增量,還是會考慮對象被更改並重新渲染整個屏幕? (這將導致表現不佳),如果是這樣的話,有什么作用?
現在,由於整個DOM被平滑地轉換為一個JavaScript對象,因此應用程序必須繼續操作此對象。 我們是否有適合復雜JavaScript對象操作的工具,比如jQuery是DOM操縱器的王者?
有了上述疑慮,我強烈地發現了以下優點。
數據已經整齊抽象和組織良好,以便隨時可以將其序列化為服務器,firebase或本地導出到用戶。
實現崩潰恢復很容易,將此功能視為桌面中的“Hibernate”選項。
模型和視圖完全分離。 例如,公司A可以編寫模型來維護狀態,而很少有明顯的控制器來更改模型和一些基本視圖來與用戶交互。 現在,該公司A可以邀請其他開發人員公開編寫自己的視圖,並請求公司A獲取更多控制器和REST方法。 這將有助於精益開發。
如果我開始將此對象版本化到服務器並且我可以以相同的方式向用戶播放他看到網站並且可以繼續工作而不會有麻煩。 這將作為單頁應用程序的真正后退按鈕。
在我的日常工作中,我們在大型企業AngularJS應用程序中使用“單個對象中的狀態”模式。 到目前為止,我只能看到好處。 讓我來解答你的問題:
這種方法是否可取? 當應用程序開始擴展時,我將面臨哪些缺點和缺陷?
我看到兩個主要好處:
1)調試。 當應用程序擴展時,最難回答的問題是我的應用程序現在發生了什么? 當我在一個對象中擁有所有應用程序狀態時,我可以在應用程序運行時的任何時間點在控制台上打印它。
這意味着更容易理解發生錯誤時發生的事情,甚至可以在生產中使用應用程序,而無需使用調試工具。
使用大多數處理狀態對象(或部分對象)的純函數編寫代碼,在控制台中注入這些函數和狀態,並且您將擁有最好的調試工具。 :)
2)簡單。 當你使用一個對象,你很清楚的知道你的應用程序有什么變化狀態,什么讀取狀態。 它們是完全獨立的代碼片段。 讓我用一個例子來說明:
假設您有一個“結賬”屏幕,其中包含結賬摘要,運費選項和付款選項。 如果我們實現具有單獨和內部狀態的Summary
, Freight
和PaymentOptions
,這意味着每次用戶更改其中一個組件時,您必須明確更改其他組件 。
因此,如果用戶更改了Freight
選項,則必須以某種方式調用Summary
,以告知其更新其值。 如果用戶選擇具有折扣的PaymentOption
,則必須發生相同的情況。 你能看到意大利面條代碼建設嗎?
當您使用集中狀態對象時,事情會變得更容易,因為每個組件只與狀態對象交互 。 Summary
只是一個國家的純粹功能。 當用戶選擇新的運費或付款選項時,狀態會更新。 然后, Summary
會自動更新,因為狀態剛剛更改。
如果對象的一小部分被更新,則表示優先級增加,角度會單獨智能地重新渲染增量,還是會考慮對象被更改並重新渲染整個屏幕? (這將導致表現不佳),如果是這樣的話,有什么作用?
使用帶有角度的架構時,我們遇到了一些性能問題。 當您在對象上使用觀察者時,角度臟檢查非常有效,而當您使用昂貴的功能時則不太好。 因此,我們在查找性能瓶頸時通常會將函數結果保存在$scope
上設置的“緩存”對象中。 每次狀態改變時,我們再次計算函數並將其保存到緩存中。 然后在視圖上引用此緩存。
現在,由於整個DOM被平滑地轉換為一個JavaScript對象,因此應用程序必須繼續操作此對象。 我們是否有適合復雜JavaScript對象操作的工具,比如jQuery是DOM操縱器的王者?
是! :)由於我們有一個大對象作為我們的狀態,因此可以在這里使用為操作對象而編寫的每個庫。
您提到的所有好處都是正確的:它可以更輕松地拍攝應用程序的快照,序列化它,實現撤消...
我在博客中寫了更多關於集中式狀態架構的實現細節 。
這種方法是否可取? 當應用程序開始擴展時,我將面臨哪些缺點和缺陷?
我總體而言,這可能不是一個好主意,因為它會對這個大型物體的可維護性產生重大問題,並且在整個應用程序中引入副作用的可能性使得難以正確測試。
基本上你沒有得到任何封裝,因為應用程序中任何地方的任何方法都可能改變了數據模型的任何部分。
如果對象的一小部分被更新,則表示優先級增加,角度會單獨智能地重新渲染增量,還是會考慮對象被更改並重新渲染整個屏幕? (這將導致表現不佳),如果是這樣的話,有什么作用?
當角度出現摘要時,處理所有手表以確定更改的內容,所有更改都將導致調用手表的處理程序。 只要你意識到你正在創建多少個DOM元素並且很好地管理這個數字性能並不是一個大問題,那么還有像bind-once這樣的選項可以避免擁有太多的觀察者,如果它變成了問題(Chrome分析工具非常適合確定您是否需要處理這些問題並找到正確的性能目標)
現在,由於整個DOM被平滑地轉換為一個JavaScript對象,因此應用程序必須繼續操作此對象。 我們是否有適合復雜JavaScript對象操作的工具,比如jQuery是DOM操縱器的王者?
您仍然可以使用jQuery但是您希望在指令中進行任何DOM操作。 這允許您在視圖中應用它們,該視圖實際上是DOM的驅動程序。 這可以防止您的控制器與視圖綁定並保持一切可測試。 Angular包含jQLite jQuery的較小衍生物,但是如果你在angular之前包含jQuery,它將使用它而你可以在Angular中使用jQuery的全部功能。
數據已經整齊抽象和組織良好,以便隨時可以將其序列化為服務器,firebase或本地導出到用戶。 實現崩潰恢復很容易,將此功能視為桌面中的“Hibernate”選項。
這是事實,但我認為如果你提出一個定義良好的保存對象,它可以保留恢復狀態所需的信息,而不是在一個地方存儲應用程序的所有部分的整個數據模型。 隨着時間的推移對模型的更改將導致保存的版本出現問題。
模型和視圖完全分離。 例如,公司A可以編寫模型來維護狀態,而很少有明顯的控制器來更改模型和一些基本視圖來與用戶交互。 現在,該公司A可以邀請其他開發人員公開編寫自己的視圖,並請求公司A獲取更多控制器和REST方法。 這將有助於精益開發。
只要您在控制器中編寫模型,這仍然是正確的,並且是一個優勢。 您還可以編寫自定義指令來封裝功能和模板,這樣可以大大降低代碼的復雜性。
如果我開始將此對象的版本控制到服務器並且我可以以他看到網站的相同方式向用戶播放並且可以繼續工作而不會有麻煩。 這將作為單頁應用程序的真正后退按鈕。
我認為ngRoute或ui-router真的已經為你覆蓋了這個並且掛起來保存狀態真是一條更好的路線(沒有雙關語意)。
也只是我在這里的擴展意見,但我認為從視圖到模型的綁定是使用Angular給我們帶來的偉大事情之一。 就最強大的部分而言,我認為它實際上是在指令中允許您擴展HTML的詞匯並允許大量的代碼重用。 依賴注入也值得一提,但無論你如何排名他們,都是很棒的功能。
我認為這種方法沒有任何優勢。 一個巨大的對象如何比應用程序模型(應用程序名稱,版本等),用戶模型(憑據,令牌,其他身份驗證的東西),toDoList模型(具有名稱和任務集合的單個對象)更抽象。
現在關於視圖和模型的分離。 假設你需要一個小部件來顯示當前用戶的名字。 在單對象方法中,您的視圖看起來像這樣:
<div class="user" ng-controller="UserWidgetCtrl">
<span>{{appState.auth.userName}}</span>
</div>
與此比較:
<div class="user" ng-controller="UserWidgetCtrl">
<span>{{user.userName}}</span>
</div>
當然,你可能會認為,這是達UserWidgetController提供訪問用戶對象,這樣的hidding結構appState
。 但是再一次,控制器必須耦合到appState
結構:
function UserWidgetCtrl($scope) {
$scope.user = $scope.appState.auth;
}
與此比較:
function UserWidgetCtrl($scope, UserService) {
UserService.getUser().then(function(user) {
$scope.user = user;
})
}
在后一種情況下,控制器無法決定用戶數據來自何處。 在前一種情況下, appState
層次結構中的任何更改appState
意味着必須更新控制器或視圖。 但是,您可以在幕后保留單個對象,但是應該通過專用服務來抽取對單獨部分(在這種情況下是用戶)的訪問。
並且不要忘記,隨着對象結構的發展,你的$watch
會變慢並消耗更多內存,特別是在打開對象相等標志的情況下。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.