简体   繁体   English

AngularJS的单个对象模型

[英]Single Object Model for AngularJS

I am experimenting few best practices in AngularJS specially on designing model. 我正在AngularJS中尝试一些特别是设计模型的最佳实践。 One true power in my opinion in AngularJS is 我认为AngularJS的一个真正的力量是

'When model changes view gets updated & vice versa' '当模型更改视图得到更新时,反之亦然'

. That leads to the obvious fact 这导致了明显的事实

'At any given time the model is the single source of truth for application state' “在任何特定时间,模型都是应用程序状态的唯一真实来源”

Now, After reading various blog posts on designing the right model structure and I decided to use something like 'Single Object' approach. 现在,在阅读了关于设计正确模型结构的各种博客文章后,我决定使用类似“单一对象”的方法。 Meaning the whole app state is maintained in a single JavaScript object. 这意味着整个应用程序状态都保存在一个JavaScript对象中。

For example of a to-do application 例如待办事项

$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}]
  }
]        

}; };

This object will go HUGE depending on the application complexity. 根据应用程序的复杂性,此对象将变为巨大。 Regarding this I have the below worries and marked them as questions. 关于这一点,我有以下的担忧,并将其标记为问题。

  • Is such approach advisable? 这种方法是否可取? What are the downsides and pitfalls I will face when application starts to scale? 当应用程序开始扩展时,我将面临哪些缺点和缺陷?

  • When small portion of object is updated say priority is increased will angular smartly re-render the delta alone or will it consider the object got changed and re-render whole screen? 如果对象的一小部分被更新,则表示优先级增加,角度会单独智能地重新渲染增量,还是会考虑对象被更改并重新渲染整个屏幕? (This will lead to poor performance), If so what are the works around? (这将导致表现不佳),如果是这样的话,有什么作用?

  • Now since the whole DOM got smoothly translated into one JavaScript object the application has to keep manipulating this object. 现在,由于整个DOM被平滑地转换为一个JavaScript对象,因此应用程序必须继续操作此对象。 Do we have right tools for complex JavaScript object manipulation like jQuery was king of DOM manipulator? 我们是否有适合复杂JavaScript对象操作的工具,比如jQuery是DOM操纵器的王者?

With the above doubts I strongly find the below advantages. 有了上述疑虑,我强烈地发现了以下优点。

  • Data has got neatly abstracted & well organized so that anytime it can be serialized to server, firebase or local export to user. 数据已经整齐抽象和组织良好,以便随时可以将其序列化为服务器,firebase或本地导出到用户。

  • Implementing crash recovery will be easy, Think this feature as 'Hibernate' option in desktops. 实现崩溃恢复很容易,将此功能视为桌面中的“Hibernate”选项。

  • Model & View totally decoupled. 模型和视图完全分离。 For example, company A can write Model to maintain state and few obvious Controllers to change the model and some basic view to interact with users. 例如,公司A可以编写模型来维护状态,而很少有明显的控制器来更改模型和一些基本视图来与用户交互。 Now this company A can invite other developer to openly write their own views and requesting the company A for more controllers and REST methods. 现在,该公司A可以邀请其他开发人员公开编写自己的视图,并请求公司A获取更多控制器和REST方法。 This will empower LEAN development. 这将有助于精益开发。

  • What if I start versioning this object to server and I can make a playback to the user in the SAME way he saw the website and can continue to work without hassle. 如果我开始将此对象版本化到服务器并且我可以以相同的方式向用户播放他看到网站并且可以继续工作而不会有麻烦。 This will work as a true back button for single page apps. 这将作为单页应用程序的真正后退按钮。

In my day job we use the "state in a single object" pattern in a big enterprise AngularJS application. 在我的日常工作中,我们在大型企业AngularJS应用程序中使用“单个对象中的状态”模式。 So far I can only see benefits. 到目前为止,我只能看到好处。 Let me address your questions: 让我来解答你的问题:

Is such approach advisable? 这种方法是否可取? What are the downsides and pitfalls I will face when application starts to scale? 当应用程序开始扩展时,我将面临哪些缺点和缺陷?

I see two main benefits: 我看到两个主要好处:

1) DEBUGGING. 1)调试。 When the application scales, the toughest question to answer is what is happening to my application right now? 当应用程序扩展时,最难回答的问题是我的应用程序现在发生了什么? When I have all the application state in one object, I can just print it on the console at any point in time while the application is running . 当我在一个对象中拥有所有应用程序状态时,我可以在应用程序运行时的任何时间点在控制台上打印它。

That means it's much easier to understand what is happening when an error occurs, and you can even do it with the application on production, without the need to use debugging tools. 这意味着更容易理解发生错误时发生的事情,甚至可以在生产中使用应用程序,而无需使用调试工具。

Write your code using mostly pure functions that process the state object (or parts of it), inject those functions and the state in the console, and you'll have the best debugging tool available. 使用大多数处理状态对象(或部分对象)的纯函数编写代码,在控制台中注入这些函数和状态,并且您将拥有最好的调试工具。 :) :)

2) SIMPLICITY. 2)简单。 When you use a single object, you know very clearly on your application what changes state and what reads state. 当你使用一个对象,你很清楚的知道你的应用程序有什么变化状态,什么读取状态。 And they are completely separate pieces of code. 它们是完全独立的代码片段。 Let me illustrate with an example: 让我用一个例子来说明:

Suppose you have a "checkout" screen, with a summary of the checkout, the freight options, and the payment options. 假设您有一个“结账”屏幕,其中包含结账摘要,运费选项和付款选项。 If we implement Summary , Freight and PaymentOptions with separate and internal state, that means that every time the user changes one of the components, you have to explicitly change the others . 如果我们实现具有单独和内部状态的SummaryFreightPaymentOptions ,这意味着每次用户更改其中一个组件时,您必须明确更改其他组件

So, if the user changes the Freight option, you have to call the Summary in some way, to tell it to update its values. 因此,如果用户更改了Freight选项,则必须以某种方式调用Summary ,以告知其更新其值。 The same have to happen if the user selects a PaymentOption with a discount. 如果用户选择具有折扣的PaymentOption ,则必须发生相同的情况。 Can you see the spaghetti code building? 你能看到意大利面条代码建设吗?

When you use a centralized state object things get easier, because each component only interacts with the state object . 当您使用集中状态对象时,事情会变得更容易,因为每个组件只与状态对象交互 Summary is just a pure function of the state. Summary只是一个国家的纯粹功能。 When the user selects a new freight or payment option, the state is updated. 当用户选择新的运费或付款选项时,状态会更新。 Then, the Summary is automatically updated, because the state has just changed. 然后, Summary会自动更新,因为状态刚刚更改。

When small portion of object is updated say priority is increased will angular smartly re-render the delta alone or will it consider the object got changed and re-render whole screen? 如果对象的一小部分被更新,则表示优先级增加,角度会单独智能地重新渲染增量,还是会考虑对象被更改并重新渲染整个屏幕? (This will lead to poor performance), If so what are the works around? (这将导致表现不佳),如果是这样的话,有什么作用?

We encountered some performance problems when using this architecture with angular. 使用带有角度的架构时,我们遇到了一些性能问题。 Angular dirty checking works very well when you use watchers on objects, and not so well when you use expensive functions. 当您在对象上使用观察者时,角度脏检查非常有效,而当您使用昂贵的功能时则不太好。 So, what we usually do when finding a performance bottleneck is saving a function result in a "cache" object set on $scope . 因此,我们在查找性能瓶颈时通常会将函数结果保存在$scope上设置的“缓存”对象中。 Every time the state changes we calculate the function again and save it to the cache. 每次状态改变时,我们再次计算函数并将其保存到缓存中。 Then reference this cache on the view. 然后在视图上引用此缓存。

Now since the whole DOM got smoothly translated into one JavaScript object the application has to keep manipulating this object. 现在,由于整个DOM被平滑地转换为一个JavaScript对象,因此应用程序必须继续操作此对象。 Do we have right tools for complex JavaScript object manipulation like jQuery was king of DOM manipulator? 我们是否有适合复杂JavaScript对象操作的工具,比如jQuery是DOM操纵器的王者?

Yes! 是! :) Since we have a big object as our state, each and every library written to manipulate objects can be used here. :)由于我们有一个大对象作为我们的状态,因此可以在这里使用为操作对象而编写的每个库。

All the benefits you mentioned are true: it makes it easier to take snapshots of the application, serialize it, implement undos... 您提到的所有好处都是正确的:它可以更轻松地拍摄应用程序的快照,序列化它,实现撤消...

I've written some more implementation details of the centralized state architecture in my blog . 在博客中写了更多关于集中式状态架构的实现细节

Also look for information regarding frameworks that are based on the centralized state idea, like Om , Mercury and Morearty . 还要查找有关基于集中状态概念的框架的信息,如OmMercuryMorearty

Is such approach advisable? 这种方法是否可取? What are the downsides and pitfalls I will face when application starts to scale? 当应用程序开始扩展时,我将面临哪些缺点和缺陷?

I'd say overall this is probably not a great idea since it creates major problems with maintainability of this massive object and introduces the possibility of side effects throughout the entire application making it hard to properly test. 我总体而言,这可能不是一个好主意,因为它会对这个大型物体的可维护性产生重大问题,并且在整个应用程序中引入副作用的可能性使得难以正确测试。

Essentially you get no sort of encapsulation since any method anywhere in the app might have changed any part of the data model. 基本上你没有得到任何封装,因为应用程序中任何地方的任何方法都可能改变了数据模型的任何部分。

When small portion of object is updated say priority is increased will angular smartly re-render the delta alone or will it consider the object got changed and re-render whole screen? 如果对象的一小部分被更新,则表示优先级增加,角度会单独智能地重新渲染增量,还是会考虑对象被更改并重新渲染整个屏幕? (This will lead to poor performance), If so what are the works around? (这将导致表现不佳),如果是这样的话,有什么作用?

When a digest occurs in angular all the watches are processed to determine what has changed, all changes will cause the handlers for the watches to be called. 当角度出现摘要时,处理所有手表以确定更改的内容,所有更改都将导致调用手表的处理程序。 So long as you are conscious of how many DOM elements you're creating and do a good job of managing that number performance isn't a huge issue, there are also options like bind-once to avoid having too many watchers if that becomes an issue (Chrome profiling tools are great for figuring out if you need to work on these problems and to find the correct targets for performance) 只要你意识到你正在创建多少个DOM元素并且很好地管理这个数字性能并不是一个大问题,那么还有像bind-once这样的选项可以避免拥有太多的观察者,如果它变成了问题(Chrome分析工具非常适合确定您是否需要处理这些问题并找到正确的性能目标)

Now since the whole DOM got smoothly translated into one JavaScript object the application has to keep manipulating this object. 现在,由于整个DOM被平滑地转换为一个JavaScript对象,因此应用程序必须继续操作此对象。 Do we have right tools for complex JavaScript object manipulation like jQuery was king of DOM manipulator? 我们是否有适合复杂JavaScript对象操作的工具,比如jQuery是DOM操纵器的王者?

You can still use jQuery but you would want to do any DOM manipulations within directives. 您仍然可以使用jQuery但是您希望在指令中进行任何DOM操作。 This allows you to apply them within the view which is really the driver of the DOM. 这允许您在视图中应用它们,该视图实际上是DOM的驱动程序。 This keeps your controllers from being tied to the views and keeps everything testable. 这可以防止您的控制器与视图绑定并保持一切可测试。 Angular includes jQLite a smaller derivative of jQuery but if you include jQuery before angular it will use that instead and you can use the full power of jQuery within Angular. Angular包含jQLite jQuery的较小衍生物,但是如果你在angular之前包含jQuery,它将使用它而你可以在Angular中使用jQuery的全部功能。

Data has got neatly abstracted & well organized so that anytime it can be serialized to server, firebase or local export to user. 数据已经整齐抽象和组织良好,以便随时可以将其序列化为服务器,firebase或本地导出到用户。 Implementing crash recovery will be easy, Think this feature as 'Hibernate' option in desktops. 实现崩溃恢复很容易,将此功能视为桌面中的“Hibernate”选项。

This is true but I think it's better if you come up with a well defined save object that persists the information you need to restore state rather than storing the entire data model for all the parts of the app in one place. 这是事实,但我认为如果你提出一个定义良好的保存对象,它可以保留恢复状态所需的信息,而不是在一个地方存储应用程序的所有部分的整个数据模型。 Changes to the model over time will cause problems with the saved versions. 随着时间的推移对模型的更改将导致保存的版本出现问题。

Model & View totally decoupled. 模型和视图完全分离。 For example, company A can write Model to maintain state and few obvious Controllers to change the model and some basic view to interact with users. 例如,公司A可以编写模型来维护状态,而很少有明显的控制器来更改模型和一些基本视图来与用户交互。 Now this company A can invite other developer to openly write their own views and requesting the company A for more controllers and REST methods. 现在,该公司A可以邀请其他开发人员公开编写自己的视图,并请求公司A获取更多控制器和REST方法。 This will empower LEAN development. 这将有助于精益开发。

So long as you write your models in your controllers this still remains true and is an advantage. 只要您在控制器中编写模型,这仍然是正确的,并且是一个优势。 You can also write custom directives to encapsulate functionality and templates so you can greatly reduce the complexity of your code. 您还可以编写自定义指令来封装功能和模板,这样可以大大降低代码的复杂性。

What if I start version control this object to server and I can make a playback to the user in the SAME way he saw the website and can continue to work without hassle. 如果我开始将此对象的版本控制到服务器并且我可以以他看到网站的相同方式向用户播放并且可以继续工作而不会有麻烦。 This will work as a true back button for single page apps. 这将作为单页应用程序的真正后退按钮。

I think ngRoute or ui-router really already cover this for you and hooking into them to save the state is really a better route (no pun intended). 我认为ngRoute或ui-router真的已经为你覆盖了这个并且挂起来保存状态真是一条更好的路线(没有双关语意)。

Also just my extended opinion here, but I think binding from the view to the model is one of the great things using Angular gives us. 也只是我在这里的扩展意见,但我认为从视图到模型的绑定是使用Angular给我们带来的伟大事情之一。 In terms of the most powerful part I think it's really in directives which allow you to extend the vocabulary of HTML and allow for massive code re-use. 就最强大的部分而言,我认为它实际上是在指令中允许您扩展HTML的词汇并允许大量的代码重用。 Dependency injection also deserves an honorable mention, but yeah all great features no matter how you rank 'em. 依赖注入也值得一提,但无论你如何排名他们,都是很棒的功能。

I don't see any advantages in this approach. 我认为这种方法没有任何优势。 How is one huge object more abstracted away than, say, application model (app name, version, etc), user model (credentials, tokens, other auth stuff), toDoList model (single object with name and collection of tasks). 一个巨大的对象如何比应用程序模型(应用程序名称,版本等),用户模型(凭据,令牌,其他身份验证的东西),toDoList模型(具有名称和任务集合的单个对象)更抽象。

Now regarding decoupling of view and model. 现在关于视图和模型的分离。 Let's say you need a widget to display current user's name. 假设你需要一个小部件来显示当前用户的名字。 In Single Object approach, your view would look something like this: 在单对象方法中,您的视图看起来像这样:

<div class="user" ng-controller="UserWidgetCtrl">
   <span>{{appState.auth.userName}}</span>
</div>

Compare with this: 与此比较:

<div class="user" ng-controller="UserWidgetCtrl">
  <span>{{user.userName}}</span>
</div>

Of course, you might argue, that it is up to UserWidgetController to provide access to user object, thus hidding structure of appState . 当然,你可能会认为,这是达UserWidgetController提供访问用户对象,这样的hidding结构appState But then again controller must be coupled to appState structure: 但是再一次,控制器必须耦合到appState结构:

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

Compare with this: 与此比较:

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

In latter case, the controller does not get to decide, where user data comes from. 在后一种情况下,控制器无法决定用户数据来自何处。 In former case, any change in appState hierarchy means that either controllers or view will have to be updated. 在前一种情况下, appState层次结构中的任何更改appState意味着必须更新控制器或视图。 You could however keep single object behind the scenes, but access to separate parts (user, in this case) should be abstracted by dedicated services. 但是,您可以在幕后保留单个对象,但是应该通过专用服务来抽取对单独部分(在这种情况下是用户)的访问。

And don't forget, as your object structure goes, your $watch 'es will get slower and consume more memory, especially with object equality flag turned on. 并且不要忘记,随着对象结构的发展,你的$watch会变慢并消耗更多内存,特别是在打开对象相等标志的情况下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM