使用 Angular 2 构建 Redux 应用程序 - 第 1 部分
介绍
状态管理一直是前端框架中一个持续存在的问题。前端框架的关键原则是多层次的状态变异,这使得标准 MVC(模型-视图-控制器)方法无效。这种多层状态变异在 Angular 1 中很明显,其中管理应用程序状态的逻辑分布在指令、控制器和服务之间,每个级别都有自己的状态变异逻辑。分段的应用程序状态很容易导致不一致,并且难以测试。
随着前端应用程序变得越来越复杂,对用户输入的反应也越来越强烈,这些问题也变得越来越明显,越来越难以规避。然而,前端开发越来越流行的特点(例如强调函数式编程)催生了一种名为Redux的新型状态管理模型。Redux 将状态集中到一个实体中,让开发人员可以在应用程序的任何位置访问最新状态。
尽管 Redux 是由 React 社区发起的,但ngrx/store等第三方库和rxJS等扩展使得 Redux 成为同样适合在 Angular 2 应用程序中使用的概念。
在本指南中,我将介绍 Redux 的核心概念以及它们如何促进 Angular 2 应用程序。
Redux 的主要概念
Redux 包含三个主要部分
- 总店
- Reducers
- 操作
每个部分在应用程序状态的变异中都扮演着不同的角色。用于处理异步请求(如 API 调用)的中间件将在第二部分中介绍。
主店
存储将整个应用程序状态组合成一个实体,充当Web 应用程序的数据库。存储分为不同的状态,代表应用程序中不同类型的数据。状态是不可变的,但可以通过明确定义的操作进行更改。因此,状态变异受到限制和集中,简化了调试过程并使代码更易于理解。
Reducers
如果 store 是应用程序的数据库,那么 Reducer 就是表。Reducer表示应用程序中以特定方式组成的切片或结构。Reducer 是一个纯函数,它定义在分派操作时状态的切片将如何变化。它接受两个参数,即先前的状态和操作,并返回新状态。
export interface Reducer<T> {
(state: T, action: Action): T;
}
例子
export const itemsReducer: ActionReducer<number> = (state = [], action: Action) => {
switch (action.type) {
case ADD_ITEM: //adding an item
const item:Item = action.payload;
//KEEPING THE STATE IMMUTABLE:
// We don't perform actions that alter the state such as
// array.push, array.shift and so on.
// Instead, we concatenate the item, preserving the state.
return [ ...state, item ];
}
case REMOVE_ITEM://removing the item
// Again, we don't remove the item, we just filter the new state so
// that it won't contain the item we're removing
return state.filter(item => {
return item.id !== action.payload.id;
});
}
}
操作
操作表示从应用程序发送到存储的信息的有效负载,通常由用户交互触发。每个 Reducer 都有一组操作类型,用于定义应如何更改状态。操作由类型和有效负载组成:
export interface Action {
type: string;
payload?: any;
}
例子:
//making an action for adding an item
dispatch({type: ADD_ITEM, payload: {id: 1, name: 'An item' , category: 'miscellaneous'}})
概述
当一个动作被分派时,reducer 会接受它并根据动作类型应用有效负载,然后输出新状态。
存储包含整个状态,reducer 返回状态片段,而操作是预定义的、用户触发的事件,用于传达状态的给定片段应如何更改。如果操作需要异步请求,则使用中间件。reducer 获取其先前的状态,对其应用新操作,然后将其返回。
投影数据
正如您所知,Store 是一个表示应用程序状态的树状结构,由表示状态不同部分的 Reducer 组成。在 Angular 2 的ngrx/store中,store 是一个可观察对象,因此可以响应式地访问应用程序的状态。可观察的 store 还允许我们使用 RxJS 的运算符混合多个状态的值。
//getting a single slice of the state
store.select('items')
//combining multiple slices
Observable.combineLatest(
store.select('items'),
store.select('categories'),
(items, categories) => {
})
Redux 实践
现在您已经了解了主要概念,您可能想知道它们如何在 Angular 2 应用程序中结合在一起。为了说明 Redux 的工作原理,我们将构建一个简单的财务会计工具,该工具将使用 Redux 架构跟踪您的交易。它将具有从虚拟账户中增加或扣除资金的操作。稍后,我们将添加一种查看您当前余额和一些其他统计数据的方法。
设置
我们将使用angular-cli来设置项目:
ng new financials_app
cd financials_app
ngrx/store是专为 Angular 2 应用程序构建的状态容器。它将为 Redux 架构提供实用程序和构建块。
npm install @ngrx/core @ngrx/store --save
或者,您还可以安装Bootstrap以使应用程序看起来美观且结构良好:
npm install bootstrap@next
将以下行添加到angular-cli.json。
在app.scripts数组中,作为对象属性:
"scripts": [
"../node_modules/jquery/dist/jquery.js",
"../node_modules/tether/dist/js/tether.js",
"../node_modules/bootstrap/dist/js/bootstrap.js"
],
在app.styles数组中:
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.css",
"styles.css"
]
你的第一个 Reducer
在 Redux 应用程序中要做的第一件事是定义您的商店并开始将 Reducer 附加到它。对于应用程序的第一次迭代,我们将添加财务运营的状态。
在您的 Angular 2 应用中,创建一个新目录,其中将包含您的应用程序的 Reducer:
cd src/app
mkdir common
首先我们来做一个财务运作的模型:
//src/app/common/operation.model.ts
export class Operation {
id: number;
amount: number;
reason: string;
constructor() {}
}
定义 Reducer 函数
接下来,创建一个文件,其中包含财务操作的操作和简化器的定义:
//src/app/common/operations.ts
import {ActionReducer, Action, State} from '@ngrx/store';
import {Operation} from "./operation.model";
//definitions of the actions that alter the state
export const ADD_OPERATION = 'Add an operation';
export const REMOVE_OPERATION = 'Remove an operation';
export const INCREMENT_OPERATION = 'Increment an operation';
export const DECREMENT_OPERATION = 'Decrement an operation';
//the initial state of the operations
const initialState:State = [];
//the operationsReducer function: a pure function that is responsible for maintaining the
//financial operations state of your store
export const operationsReducer: ActionReducer = (state = initialState, action: Action) => {
switch (action.type) {
//In Redux, you cannot mutate the state. In this case using .push(), .pop(),
// .shift() or .unshift() is against the convention.
case ADD_OPERATION: //Action type
const operation:Operation = action.payload;//the contents of an operation
return [ ...state, operation ];
case INCREMENT_OPERATION:
const operation = ++action.payload.amount;
return state.map(item => {
return item.id === action.payload.id ? Object.assign({}, item, op
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~