AngularJS的单个对象模型

时间:2014-06-05 08:10:51

标签: javascript angularjs model-view-controller model angularjs-scope

我正在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或本地导出到用户。

  • 实施崩溃恢复很容易,请将此功能视为桌面中的“休眠”选项。

  • 模特&查看完全脱钩。例如,公司A可以编写模型来维护状态,而很少有明显的控制器来改变 模型和一些与用户交互的基本视图。现在这家公司A 可以邀请其他开发者公开写自己的观点和 请求公司A获得更多控制器和REST方法。这个 将支持精益发展。

  • 如果我开始将此对象版本化为服务器,并且我可以以相同的方式向用户播放,他看到网站并可以继续工作而不会有麻烦。这将作为单页应用程序的真正后退按钮。

3 个答案:

答案 0 :(得分:3)

在我的日常工作中,我们在大型企业AngularJS应用程序中使用“单个对象中的状态”模式。到目前为止,我只能看到好处。让我来解答你的问题:

  

这种方法是否可取?当应用程序开始扩展时,我将面临哪些缺点和缺陷?

我看到两个主要好处:

1)调试。当应用程序扩展时,最难回答的问题是我的应用程序现在发生了什么?当我将所有应用程序状态放在一个时对象,我可以在应用程序运行时的任何时间点上打印它

这意味着更容易理解发生错误时发生的事情,甚至可以在生产中使用应用程序,而无需使用调试工具。

使用大多数处理状态对象(或部分对象)的纯函数编写代码,在控制台中注入这些函数和状态,并且您将拥有最好的调试工具。 :)

2)SIMPLICITY。当您使用单个对象时,您可以非常清楚地知道应用程序更改状态以及读取状态。它们是完全独立的代码片段。让我举一个例子来说明:

假设您有一个“结帐”屏幕,其中包含结帐摘要,运费选项和付款选项。如果我们使用单独的内部状态实现SummaryFreightPaymentOptions,则意味着每次用户更改其中一个组件时,您必须明确更改其他组件< / em>的

因此,如果用户更改Freight选项,则必须以某种方式调用Summary,以告知其更新其值。如果用户选择具有折扣的PaymentOption,则必须发生相同的情况。你能看到意大利面条代码建设吗?

当您使用集中状态对象时,事情会变得更容易,因为每个组件只与状态对象交互。 Summary只是国家的一个纯粹功能。当用户选择新的运费或付款选项时,状态会更新。然后,Summary会自动更新,因为状态刚刚更改。

  

当对象的一小部分被更新时说优先级增加会有角度巧妙地重新渲染增量,还是会认为对象被更改并重新渲染整个屏幕? (这将导致表现不佳),如果是这样的话有什么作用?

使用此架构时,我们遇到了一些性能问题。当您在对象上使用观察者时,角度脏检查非常有效,而当您使用昂贵的功能时则不太好。因此,我们在查找性能瓶颈时通常会将函数结果保存在$scope上设置的“缓存”对象中。每次状态改变时,我们再次计算函数并将其保存到缓存中。然后在视图上引用此缓存。

  

现在,由于整个DOM被平滑地转换为一个JavaScript对象,因此应用程序必须继续操作此对象。我们是否有适合复杂JavaScript对象操作的工具,比如jQuery是DOM操纵器的王者?

是的! :)由于我们有一个大对象作为我们的状态,因此可以在这里使用为操作对象而编写的每个库。

您提到的所有好处都是正确的:它可以更轻松地拍摄应用程序的快照,序列化它,实现撤消...

我写了更多implementation details of the centralized state architecture in my blog

还要查找有关基于集中状态概念的框架的信息,例如OmMercuryMorearty

答案 1 :(得分:2)

  

这种方法是否可取?我会有什么缺点和缺陷   当应用程序开始扩展时面对?

我总体而言,这可能不是一个好主意,因为它会对这个大型物体的可维护性造成重大问题,并在整个应用程序中引入副作用的可能性,使得难以正确测试。

基本上你没有得到任何封装,因为应用程序中任何地方的任何方法都可能改变了数据模型的任何部分。

  

当对象的一小部分被更新时,优先级会增加   角度巧妙地重新渲染三角洲或者它会考虑   对象变了,重新渲染整个屏幕? (这会导致穷人   性能),如果是这样的话有什么作用?

当角度出现摘要时,处理所有手表以确定已更改的内容,所有更改都将导致调用手表的处理程序。只要你意识到自己创造了多少DOM元素并且很好地管理这个数字性能并不是一个很大的问题,那么还有像bind-once这样的选项,以避免让观众过多。这成为一个问题(Chrome分析工具非常适合确定您是否需要处理这些问题并找到正确的性能目标)

  

现在,因为整个DOM被顺利翻译成一个JavaScript   对象应用程序必须继续操作此对象。我们要不要   拥有适合复杂JavaScript对象操作的工具   jQuery是DOM操纵器之王?

您仍然可以使用jQuery但是您希望在指令中执行任何DOM操作。这允许您在视图中应用它们,该视图实际上是DOM的驱动程序。这可以防止您的控制器与视图绑定并保持一切可测试。 Angular包含jQLite,它是jQuery的一个较小的衍生物,但如果你在angular之前包含jQuery,它将使用它而你可以在Angular中使用jQuery的全部功能。

  

数据已经整齐抽象,并且组织良好,以便随时可以   被序列化到服务器,firebase或本地导出到用户。   实现崩溃恢复将很容易,请将此功能视为   &#39;休眠&#39;桌面选项。

这是事实,但我认为如果你想出一个定义良好的保存对象来保持恢复状态所需的信息而不是将应用程序的所有部分的整个数据模型存储在一个中,那么我认为会更好。地点。随着时间的推移对模型的更改将导致保存的版本出现问题。

  

模特&amp;查看完全脱钩。例如,公司A可以编写模型   保持状态和几个明显的控制器来改变模型和   一些与用户交互的基本视图。现在这家公司A可以邀请   其他开发人员公开写自己的观点并请求   公司A用于更多控制器和REST方法。这将赋予权力   精益开发。

只要您在控制器中编写模型,这仍然是正确的,并且是一个优势。您还可以编写自定义指令来封装功能和模板,这样可以大大降低代码的复杂性。

  

如果我开始将此对象的版本控制到服务器并且我可以创建一个   以相同的方式回放给用户他看到了网站并且可以   继续工作没有麻烦。这将作为一个真正的后退按钮   对于单页应用程序。

我认为ngRoute或ui-router真的已经为你覆盖这个并且挂起来保存状态真是一条更好的路线(没有双关语意)。

这也是我的扩展意见,但我认为从视图到模型的绑定是使用Angular给我们带来的伟大事情之一。就最强大的部分而言,我认为它确实在指令中允许您扩展HTML的词汇并允许大量的代码重用。依赖注入也值得一提,但无论你如何排名,都是非常棒的功能。

答案 2 :(得分:2)

我认为这种方法没有任何优势。一个巨大的对象如何比应用程序模型(应用程序名称,版本等),用户模型(凭据,令牌,其他身份验证的东西),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提供对用户对象的访问权限,从而隐藏appState的结构。但是,控制器必须再次耦合到appState结构:

function UserWidgetCtrl($scope) {
   $scope.user = $scope.appState.auth;
} 

与此比较:

function UserWidgetCtrl($scope, UserService) {
   UserService.getUser().then(function(user) {
     $scope.user = user;
   })
}

在后一种情况下,控制器无法决定用户数据来自何处。在前一种情况下,appState层次结构中的任何更改都意味着必须更新控制器或视图。但是,您可以在幕后保留单个对象,但是应该通过专用服务来提取对单独部分(在这种情况下是用户)的访问。

不要忘记,随着对象结构的发展,你的$watch会变慢并消耗更多内存,特别是在打开对象相等标志的情况下。