Angular 进阶部分 3.1 RxJS响应式编程 数据结构

Data Architecture in Angular 4

在应用里,获取数据的方式有很多种:
• AJAX HTTP Requests
• Websockets
• Indexdb
• LocalStorage
• Service Workers
• etc.

形成了如何有效处理多种来源方式问题。

多年来,MVC应用里面的一种常用数据结构,Models包含主要逻辑,View展示数据,controller负责把两者连接在一起。
问题是,MVC模式没有把直接转换到web应用的这一步做的很好。
所以出现了其他数据结构:
- MVW: Model-View-Whatever, 数据双向绑定,也是Angualr1.x用的模式。
整个应用共享数据结构,一个部分的改变会传递到整个应用。
- Flux:单向数据流(unidirectional data flow),store保存数据,view渲染store里的数据,动作改变store里的数据。
数据流是单向的。
- Observables:数据流(streams of data),从流里订阅数据,然后操作数据(perform operations to react to changes),RxJs是最流行的JS流库。

Falcor 是一个强大框架用来绑定客户端和服务端的数据。
让客户端和服务端通过只通过一个json资源进行连接,
客户端不需要重复向服务端发送多条请求。
需要在数据端(服务端?)设置好要发送的json,然后客户端接收即可。

(github地址)[https://github.com/Netflix/falcor]

在Angular4,数据结构可以随项目定制。

服务端 Part 1: services

Underscore.js 是一个库提供JS数据结构的操作,比如array和object。

Angular会使用RxJS,也就是流 stream。
流有如下特点

  • promise 只能处理一个函数,streams 能处理多个。
  • Imperative code pull data 命令式代码提取数据
    reactive streams“push”data 响应式编程,流是推入数据,数据订阅,然后流会推送新的改变的订阅。
  • RxJS是函数性的,比如map,reduce, filter这些函数来操作。
  • 流是是可随意组合的,composable, 流就像管道一样布满数据,你可以订阅任意一截管道,或者把它们组合成新的管道。

example 聊天机器人101

可以和3个机器人聊天,机器人会有简单的反馈机制。
需要3个top-levle父组件,3个模型,3个服务。

组件
3个父组件:
状态栏,聊天主题,聊天框
模型
User:用户
Message:信息
Thread: 主题,信息库以及聊天数据
服务
每一个模型都有对应的服务。
服务负责
1, 提供应用订阅的数据流,
2, 数据流操作

总之,
- 服务负责维护流,发出模型。
- 组件负责订阅流和渲染最近的数据流

比如聊天主题组件从主题服务监听最近的主题。
会深入运用Angular 4 和RxJs

模型搭建 User, Thread, Message

这里写图片描述
User,
包含id, name , avatarsrc

export class User { 
  id: string;
  constructor(
    public name: string, 
    public avatarSrc: string) {
      this.id = uuid(); 
    }
}

public name: string,有两个用处:
1,name成为User clsss的全局变量
2,放在constructor里,会给新实例默认配置。

Thread
p279 p302
创建主题模型,一些用户在此交互信息。
注意在这,导入了message数据从message模型,
lastMessage: Message;用于预览。

Message
message模型的constructor可以让模型实例化更为自由

constructor(obj?: any) {
this.id     = obj && obj.id     || uuid();
this.isRead = obj && obj.isRead || false;
this.sentAt = obj && obj.sentAt || new Date(); 
this.author = obj && obj.author || null;
this.text   = obj && obj.text   || null;
this.thread = obj && obj.thread || null;

表明创建新数据实例时,可以输入数据或者不输入(用默认值)

let msg1 = new Message();

let msg2 = new Message({ 
  text: "Hello Nate Murray!"
})

用户服务 UserService

p281, p304
需要让应用接收到目前用户。所以只需要一个流。
新建用户服务。UserService

import { Injectable } from '@angular/core';
import { Subject, BehaviorSubject } from 'rxjs';
import { User } from './user.model';

@Injectable 可以把其他依赖注入到这个constructor。
便于测试,让Angular更好处理对象的生命周期。

创建流

p281 p304

currentUser: Subject<User> = new BehaviorSubject<User>(null);

定义一个currentUse流,它是Subject流的实例变量,
subject流是一个读写流,继承自Observable and Observer

new BehaviorSubject<User>(null);
BehaviourSubject用来保存最新数值,保存User,第一个值时null。
BehaviourSubject用来保存最新数值。因为响应式,信息会被立刻发布。
信息有可能丢失。
所以把BehaviourSubject用来保存最新数值。就可以保证数据不会丢失。

创建用户

有两种增加用户方法
1. 把用户直接加到流

UsersService.currentUser.subscribe((newUser) => {
  console.log('New User is: ', newUser.name);
})

let u = new User('Nate', 'anImgSrc');
UsersService.currentUser.next(u);

使用了next方法往currentUser流里推入数据

  1. 创建一个setCurrentUser(newUser: User)方法
  public setCurrentUser(newUser: User): void {
    this.currentUser.next(newUser);
  }

注意这里同样使用next方法推入,但是currentUser只用了一次。
两种方法依据情况使用。

也有其他方法,在MessageService里会涉及。

MessagesService

需要5条流,3个数据管理,2个动作。

数据管理流
- newMessages 把每个新消息发出一次
- messages 发出一系列目前消息
- updates 操作message

the newMessages stream

只会把新消息发出一次的newMessages流
newMessages: Subject<Message> = new Subject<Message>();

把信息添加到newMessages流

  addMessage(message: Message): void {
    this.newMessages.next(message);
  }

同主题其他用户的信息
接受一个主题和用户,
造出主题下的所有信息
把主题下的本用户信息筛选掉,返回其他用户的信息。
返回的是新流

  messagesForThreadUser(thread: Thread, user: User): Observable<Message> {
    return this.newMessages
      .filter((message: Message) => {
               // 通过thread.id造出本主题下的信息
        return (message.thread.id === thread.id) &&
               // 排除本用户的信息
               (message.author.id !== user.id);
      });
  }

the Messages stream

Messages流 保存了一些列messages,
messages: Observable<Message[]>;

操作
p287 p310
维护messages的状态
用update流来操作这个messages流
update流的函数会接受一条流,并返回一条流,

update流接收函数,具体的函数放在constructor
updates: Subject<any> = new Subject<any>();

  constructor() {
    this.messages = this.updates
      // watch the updates and accumulate operations on the messages
      .scan((messages: Message[],
             operation: IMessagesOperation) => {
               return operation(messages);
             },
            initialMessages)

这里用了新的流函数:scan,
scan函数 返回累计值,
Rx.Observable.prototype.scan(accumulator, [seed])
接受参数:
accumulator (Function):
acc: Any - 传入数据
currentValue: Any - 目前数据
index: Number - 目前序号 index
source: Observable - the current observable instance
[seed] (Any): The initial accumulator value.

当使用this.updates.scan是,会生成新的流,一条订阅于update的流。
输入:需要累积的message和
需要的操作(IMessagesOperation)

返回新的message流

流共享

默认流是不能共享,如果一个订阅者读取一个数据,那个读取数据就消失了。
要设置共享,
两个操作: publishReplay and refCount

  • publishReplay 用于在不同订阅者间分享一个订阅,然后回复n条数据。
  • refCount 让return更容易发表。

把messages添加到message流

p289 p312
创建一个让流接受messages添加到列表。

创建一个create流,
create: Subject<Message> = new Subject<Message>();
在constructor里配置create流

this.create
  .map( function(message: Message): IMessagesOperation {
    return (messages: Message[]) => {
      return messages.concat(message);
    };
  })
  .subscribe(this.updates);

表示每一个输入的message,通过IMessagesOperatio把message添加到列表。
然后把这个更updata流挂钩。.subscribe(this.updates);

这里写图片描述

view端

chat-threads 组件导入模型和服务

    <chat-thread
         *ngFor="let thread of threads | async"
         [thread]="thread">
    </chat-thread>

NgFor 遍历threads,把thread传递到chat-thread组件
加入async,可以使用RxJS Observable。

在chat-thread组件里
OnInit 能够坚挺某个具体的事件周期。
ngOnInit 导入因为thread属性不能用于constructor

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值