[英]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.