使用Redux和ngrx构建更好的Angular2应用(一)

来源:互联网 发布:linux源码包安装步骤 编辑:程序博客网 时间:2024/06/17 05:07

我的angular2项目
http://git.oschina.net/zt_zhong/CodeBe
原文地址:http://onehungrymind.com/build-better-angular-2-application-redux-ngrx/

Angular2状态管理的演变

Angular2的状态管理是以单例开始的,如果你使用的是单个的控制器来管理整个应用的所有状态的话。
如果是一个SPA,一个控制器是否是正确的?我们从最原始的地方开始,在指令或者路由中,将我们的视图和控制器分组成更小的,独立的单元。这是一个巨大的进步,但仍然存在在我们的应用程序中管理复杂状态的问题。这种情况并不罕见,就是状态会穿插在我们应用的控制器,服务,路由,指令,偶尔出现在模版中。可变状态本身并不是邪恶的,但是共享的可变状态是导致灾难的关键。
输入图片说明

就像Angular这样的现代Web框架永久地改变了以jQuery为中心的应用开发方法,React从根本上改变了我们在使用现代Web框架时处理状态管理的方式。Redux是这种转变的前沿和中心,因为它引入了一种优雅而又深刻简单的管理应用程序状态的方法。值得一提的是,Redux(大R)是一个图书馆,但更重要的是它是一个完全框架无关的设计模式(小r),并且巧妙地与Angular相似。redux的优点在于它可以用几句话来表达。可以归结为三个方面。

单状态树

输入图片说明

redux的基本前提是应用程序的整个状态都表示在一个名为store或应用程序存储的JavaScript对象中,该对象可以使用名为reducers的特殊函数来执行。同样重要的是,状态是不可变的,而reducers是唯一能够改变它们的方法。如上图所示,Store是应用的中心。

状态的合并和不可变性使得理解和预测应用程序的行为变得可能。

事件流出

输入图片说明

在redux中,用户事件被捕获并发射到reducer进行处理。在Angular 1.x中,这是一个非常常见的反模式,看到庞大的控制器,大量的逻辑专门用于管理本地状态。通过移动控制组件状态的逻辑到reducers中,对我们组件的负担变得可以忽略不计。在Angular2中,你经常会看到一个控制器什么都不做,只是将事件捕获然后通过发射一个output变量到它的父控制器。

在上图中,你将看到2个事件流。一个是从子组件发送到其父组件,然后发送到reducer的事件。第二个流是发送到服务以执行异步操作的事件,然后将其结果发送到reducer。最终都通向reducer。

事件流入

输入图片说明

当事件流出时,状态从父组件向下流向其子组件。Angular2通过在子组件声明input属性让父组件传递状态给子组件变得非常简单。这在变化检测方面有一些严重的影响,我们会进一步研究。

@ngrx/store

输入图片说明

Angular2中引入的可观察对象和异步管道的语法糖使得Angular2应用的状态迁移变得非常简单。我的好友Rob Wormald 使用Rxjs创建了一个非常酷的Redux的实现—@ngrx/store。它提供了Redux拥有的功能,并结合了可观察对象,是一个非常强大的技术栈。

简单的应用

输入图片说明

我们将构建一个简单的列表-详情REST应用程序,其中列出了一组记录(items),然后我们可以选择一条记录并进行编辑或创建一条新的记录。为了说明@ ngrx / store如何与异步操作配合使用,我们将使用json-server提供使用REST API来使用Angular 2 http服务。如果要查看应用程序的简化版本,可以查看simple-data-flow分支以跳过HTTP调用。

抓紧时间上车,马上要发车了。

源码

奠定基础

在本课程中,我们将介绍很多课题,所有我们尽全力来做好每一步。总是有一个新概念的初始阶段,你必须先奠定基础,才能开始阐述具体的组件。在本节中,我们将构建一个足够用来解释redux和ngrx的Angular上下文空间。不要被细节打断,因为我们将不止一次来回顾一切,以便加强我们的理解。

创建Reducers

为了方便我们的列表-详情页应用,我们需要管理一组记录(items)以及当前选中的记录(selectedItem)。我们将使用@ngrx/store提供给我们的store,来存储应用的状态。

为了管理应用的状态,我们需要创建items和selectedItem的reducer。一个典型的reducer只不过是一个接收一个状态并执行一个动作的函数。我们的ngrx reducer有点不同,因为第二个参数是一个将要执行的动作类型的对象和该动作的有效载荷。我们可以设置状态的默认值,以确保所有内容正确的初始化。

// The "items" reducer performs actions on our list of itemsexport const items = (state: any = [], {type, payload}) => {  switch (type) {    default:      return state;  }};

我们将构建我们的reducer来处理具体的操作,但现在我们只是简单的在switch语句返回当前状态。上面的代码和下面的代码几乎是一样的,除了一个是items的reducer,一个是selectedItem的reducer。当创建reducers时,一步步来更容易理解底层的设计模式。

// The "selectedItem" reducer handles the currently selected itemexport const selectedItem = (state: any = null, {type, payload}) => {  switch (type) {    default:      return state;  }};

对于应用的store,创建一个接口可以帮助我们理解reducers如何填充到我们的应用中。在AppStore接口,你可以看到我们正在处理一个单一的对象,它包含一个items数组和一个名称为selectedItem的Item对象属性。

export interface AppStore {  items: Item[];  selectedItem: Item;}

如果我们需要添加额外的功能,AppStore只需要添加一个新的键值对以适应新的模型。

注入Store

现在我们的reducer已经被定义了,我们需要通过将它们添加到我们的AppStore中,然后将它们注入到我们的应用程序中。第一步是将items,selectedItem,providStore导入到我们的应用中。
provideStore是@ngrx/store中导出的一个函数,它在我们的应用程序生命周期中为我们提供应用程序的状态。

我们通过调用provideStore来初始化我们的store,传入一个包含了items和selectedItem的reducer的对象给它。注意,这里传入的对象要和AppStore接口匹配。

然后,当我们调用bootstrap来初始化我们的应用时,我们将它定义为应用程序的依赖,以便我们可以在整个应用程序中使用store。

import {bootstrap} from 'angular2/platform/browser';import {App} from './src/app';import {provideStore} from '@ngrx/store';import {ItemsService, items, selectedItem} from './src/items';bootstrap(App, [  ItemsService, // The actions that consume our store  provideStore({items, selectedItem}) // The store that defines our app state]).catch(err => console.error(err));

你可能已经注意到我们也在导入和注入ItemsService; 我们将会定义它,因为它将成为我们store的主要消费者。