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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章