@ngxrx/store
v0.0.9
Published
### 初衷 - rxjs配合依赖注入可以很方便并且灵活地实现状态服务,但是灵活的另一方面又会导致状态库代码不够规范,状态的处理逻辑和其他业务逻辑杂糅的情况 - redux风格的状态库可以让代码更规范健壮,然而过多的概念及代码量会增加开发者的心智负担
Downloads
5
Readme
基于rxjs的轻量级状态库
初衷
- rxjs配合依赖注入可以很方便并且灵活地实现状态服务,但是灵活的另一方面又会导致状态库代码不够规范,状态的处理逻辑和其他业务逻辑杂糅的情况
- redux风格的状态库可以让代码更规范健壮,然而过多的概念及代码量会增加开发者的心智负担
因此状态库的最佳实践应该是既考虑代码风格的约束又考虑开发的便利性
于是抽空实现了一个轻量级的状态库,并有一定的代码风格约束:基于注解的方式来简化redux风格的API,并采用自然的编程方式来处理状态流转,而不是约定的一些概念。
- action:不再需要像redux一样定义一堆的action及reducer,开发者只需要调用状态库里定义的方法,方法里可以直接对状态进行修改,并加上@Action() 注解即可。注解会去自动添加切面逻辑来派发状态更新通知。
- select:支持扁平化的获取层级嵌套的状态,并增加shareReplay来保证pipe里的函数只会计算一次,防止增加观察者的数量会重复计算损耗性能的问题。
- state$:Store类实现的是Observer接口,以BehaviorSubject的形式存储状态
- snapshot:状态快照,可以方便的获取当前状态
- 多store:不同于redux的单对象存储状态,我采用了多store的概念,这样可以有效的拆解状态,做到精准观察,并无需考虑action的命名冲突问题(redux定义action的时候需要考虑命名冲突问题),服务层或者组件可以方便的合并多个store的状态流来形成一个完整的状态。
npm install @ngxrx/store
定义store
import {Injectable} from '@angular/core';
import {Store} from '../../../projects/rx-store-lib/src/lib/store';
import {Action} from '../../../projects/rx-store-lib/src/lib/action';
import {of} from 'rxjs';
import {delay, tap} from 'rxjs/operators';
export interface TodoItem {
id: number;
content: string;
doneStatus: boolean;
}
interface TodoState {
todoList: TodoItem[];
user: {
id: number,
name: string,
};
}
@Injectable({
providedIn: 'root'
})
export class TodoStore extends Store<TodoState> {
constructor() {
super({todoList: [], user: {}});
}
@Action()
fetchTodoList() {
return of([
{
id: 1, content: 'todo1', doneStatus: false
},
{
id: 2, content: 'todo2', doneStatus: true
},
]).pipe(delay(1000), tap(list => this.snapshot.todoList = list));
}
@Action()
addTodo(content: string) {
const list = this.snapshot.todoList;
list.push({id: new Date().getTime() + Math.floor(Math.random() * 1000), content: content, doneStatus: false});
}
@Action()
checkItem(newTodo: TodoItem) {
const oldTodItem = this.snapshot.todoList.find(i => i.id === newTodo.id);
if (oldTodItem) {
oldTodItem.doneStatus = newTodo.doneStatus;
}
}
@Action()
initUser() {
setTimeout(() => {
this.snapshot.user = {
name: 'alvin',
id: 1
};
this.next();
}, 1000);
}
}
注入到组件
import {Component, OnDestroy, OnInit} from '@angular/core';
import {TodoItem, TodoStore} from './todo.store';
import {Observable} from 'rxjs';
import {takeWhile} from 'rxjs/operators';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.less']
})
export class TodoComponent implements OnInit, OnDestroy {
allItems: Observable<TodoItem[]>;
todoList$: Observable<TodoItem[]>;
doneList$: Observable<TodoItem[]>;
userName$: Observable<any>;
todoContent: string;
private alive = true;
constructor(private todoStore: TodoStore) {
this.allItems = this.todoStore.select(state => state.todoList).pipe(takeWhile(() => this.alive));
this.todoList$ = this.todoStore.select(state => state.todoList.filter(i => !i.doneStatus)).pipe(takeWhile(() => this.alive));
this.doneList$ = this.todoStore.select(state => state.todoList.filter(i => i.doneStatus)).pipe(takeWhile(() => this.alive));
this.userName$ = this.todoStore.select('user.name').pipe(takeWhile(() => this.alive));
}
ngOnInit() {
this.todoStore.fetchTodoList();
this.todoStore.initUser();
}
ngOnDestroy(): void {
this.alive = false;
}
addTodo(content) {
if (!content) {
return;
}
this.todoStore.addTodo(content);
this.todoContent = '';
}
}