
在 Reactive 方面,有兩個非常知名的工程師,他們是來自 Netfix 的 RxJava 作者Ben Christensen 和 RxJS 作者 Ben Lesh,兩人在 Reactive 方面都做出了非常多的貢獻。在 2019 年初 Reactive 社群有兩個非常令人振奮的消息,RSocket 步入了大衆視野,RSocket 是二進制異步化通訊協定,完全相容 Reactive 語義,提供了多種通訊模型,而且是對等通訊;此外就是 Svelte 3.0 釋出,Svelte 作者 Rich Harris 做了知名的 “Rethinking Reactivity” 的演講。Svelte 通過編譯器的方式讓Reactive 使用更簡單,沒有 Virtual DOM 等性能損失,代碼更小性能更高。當然在 Reactive 社群,RxJS 還是最核心的底層架構,始終發揮着重大作用。這篇文章,我們就讨論一下如何基于 RxJS 銜接 RSocket 和 Svelte, 在前端開發中全面擁抱 Reactivity,那麼就從 Svelte 開始吧。
Svelte
Svelte 架構的核心理念是 Reactive,但是和其他前端架構不太一樣的是,Svelte 是通過靜态編譯實作 Reactive,同時并減少架構運作時的代碼量,這點你可以在 Svelte 作者 Rich Harris 的 《Svelte 3: Rethinking reactivity》 文章和演講中了解到。
Svelte 文法簡潔,幫助您編寫更少的樣闆代碼。雖然同樣是實作 Reactivity,對比RxJS,Svelte 的入門門檻非常低,你幾乎不需要了解和 Reactive 相關的知識,就可以編寫全響應式的 UI 應用。
Svelte 包含對 RxJS 的支援,如 RxJS 的 Observable 變量,另外展現在自定義Store 上,如和 RxJS 的 BehaviorSubject 的整合。Svelte 主要是關注在 UI 層面的 Reactivity,如 state 管理,事件處理等,但是涉及到邏輯處理,如背景互動的 fetch,WebSocket,流式資料等,可能 RxJS 會更友善,這方面也是 RxJS 非常擅長的。讓我們看一下典型的幾個例子:
RxJS 的 interval 應用
你可以通過 RxJS 的 interval 進行定時狀态更新,同時可以實作非常複雜的邏輯:
<script>
import { interval } from "rxjs";
import { map, take,startWith} from "rxjs/operators";
const counter = interval(1000).pipe(
map(i => i + 1),
startWith(0),
take(10)
);
</script>
<h2>Count to 10</h2>
{$counter}
RxJS BehaviorSubject對象
RxJS 的 BehaviorSubject 可以在 Svelte 中直接使用,和 Svelte 的狀态管理發揮着同樣的作用。
<script>
const store1 = new BehaviorSubject(0);
store1.set = store1.next;
</script>
<button on:click={()=>{store1.next(1)}}>increment 1</button>
<button on:click={()=>{$store1 = 2}}>increment 2</button>
{$store1}
RxJS的fromFetch
結合 RxJS 的 fromFetch ,可以非常容易地和後端進行互動,完全是響應式的。
<script>
const data = fromFetch('https://httpbin.org/ip', {
selector: response => response.json()
});
</script>
<h2>Your IP: </h2>
{$data.origin}
當然你可以可以結合 RxJS 的 WebSocket 特性完成和 WebSocket 的互動。詳細細節可以參考
https://rxjs-dev.firebaseapp.com/api/webSocket/webSocket。
RSocket
Svelte 和 RxJS 互動解決 UI 和基本的後端通訊是沒有問題的,如 HTTP REST API 和 WebSocket 等,那麼為何還需要 RSocket?讓我們先看一下 RSocket 的通訊模型:
- request/response: 這也是典型的 HTTP 請求模型,也适用于 RPC 場景,當然這個通訊是異步化的,也适用 Promise 模型。
- request/stream: 流式資料請求,如 Pub/Sub 模型,可以非常友善地後端的資料流,如來自 Kafka 消息等。
- fireAndForget: 不需要傳回确認的場景,如資料采集後送出後端的場景,這樣速度更快。
- Channel:雙向通訊,如 IM 聊天場景,可以同時實作消息的發送和接收。
标準的請求響應,如 HTTP REST API,可以使用 request/response;資料采集,我們可以使用 fireAndForget 做到性能極緻;如果是消息訂閱或者流式資料處理,你可以使用 request/stream;如果你要在 web 頁面中添加即時消息(IM)聊天場景,使用 Channel 即可。一句話,借助于 RSocket 這一協定,可以讓你實作各種通訊場景的需求。RSocket 完全是基于 Reactive 語義的,這樣和 RxJS 和 Svelte 可以無縫銜接,不需要額外的轉換操作。
此外 RSocket 還支援對等通訊,也就是通訊的雙方或者多方,同時可以為 client 或者 server。我們都知道 Svelte Store 都是針對元件通訊的,如果 Svelte Store 和RSocket 整合,則可以實作不同頁面之間的通訊,頁面之間的協作場景就可以非常容易完成。
目前 RSocket 對 JavaScript 支援主要包括三個方面:RSocket Browser,在浏覽器中連接配接後端 RSocket 服務;RSocket Node.js 可以快速建立後端 RSocket 服務;RSocket Deno 可以建立基于 Deno 的後端 RSocket 服務。
關于 Svelte 和 RSocket 通訊,可以參考
https://github.com/linux-china/svelte-rsocket-demo,詳細的結構圖如下:
RxJS 7
RxJS 起着銜接 Svelte 和 RSocket 的功能,主要是 RxJS 本身的強大功能。是以接下來我們介紹一下即将釋出的 RxJS 7.0 的新特性和功能。
RxJS 7.0 采用最新的 TS 版本(目前為 4.0.x),這樣可以使用 TypeScript 最新的特性,這樣代碼就整潔很多,當然可靠性和穩定性也提高很多。
RxJS toPromise 的調整
RxJS 的 Observable 是可以轉換為 Promise 對象的,但是 API 讓一些人有些模糊,主要的原因是 Observable 是流式的資料,是以在 RxJS 7.0 中使用 firstValueFrom, lastValueFrom 來替換 toPromise 函數,代碼如下:
import {firstValueFrom, lastValueFrom} from "rxjs";
let result = await firstValueFrom(observable);
基于 AsyncIterable 建構 Observable
AsyncIterable 在異步化越來越重要, 如熟知的 for await...of 語句,就是在異步可疊代對象上建立一個疊代循環。在 RxJS 7.0中,我們可以基于 AsyncIterable 建立 Observable 對象,然後就可以利用 Observable 強大的功能來處理異步可以疊代對象清單。
from(iterable).subscribe(x => console.log(x))
當然如果你想将Observable轉換為AsyncIterables,可以參考Ben Lesh的
https://github.com/benlesh/rxjs-for-await項目。
fromFetch 函數提示
fromFetch 做了提升,添加了一個 selector 函數,這樣可以支援直接從 response 提取對應的資訊,如轉換為文本或者 json 對象等,之前需要進行 map 轉換的,現在友善非常多。
fromFetch("http://httpbin.org/ip", {selector: response => response.json()});
RxJS 和 Deno
Deno 作為一個新的 JS 運作引擎,因其的安全性、内置 TypeScript 支援和分布式的 module 加載機制,越來越受到 JavaScript 開發者的關注。RxJS 是基于 TypeScript 編寫的,是以 Deno 的原生 TypeScript 支援沒有問題。
此外 Deno 的 API 設計遵循 Web APIs 規範,如 fetch 函數、WebSocket 類,這就讓 RxJS 的 fromFetch 和 WebSocket 可以在 Deno 使用,可以直接在 Deno 中使用 RxJS 網絡通訊相關的功能,此外 RSocket 也包括對 Deno 的支援,也可以使用 RSocket 進行網絡通訊。
在 Deno 中使用 RxJS fromFetch 樣例如下:
import {firstValueFrom} from "https://esm.sh/[email protected]?no-check";
import {fromFetch} from "https://esm.sh/[email protected]/fetch?no-check";
const ip = await firstValueFrom(fromFetch("http://httpbin.org/ip", {selector: response => response.json()}));
console.log(ip.origin);
RxJS 7.0 其他
如記憶體優化,使用記憶體更小,添加更多 operator 方法。當然大家也不用太擔心相容性的問題,目前 RxJS 7.0 隻是增加一些 API,隻會将一些 API 調整為廢棄 (Deprecated) 狀态,還不會删除掉,可能在 RxJS 8 中會進行删除,是以 RxJS 7.0 的相容性不用太擔心。
當然 RxJS 7.0 還有更多更新,詳細資訊請參考:
https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md總結
對比文法層級的 Promise(async/await),Reactive 确實複雜不少,可能需要花費你不少時間去掌握。但是也不是完全無法掌握的,Svelte 讓 Reactive UI 變得非常簡單,你幾乎不需要了解什麼是 Reactive;RSocket 基于 Reactive語義對通訊模型進行抽象化,通訊模型簡單很多且能覆寫多種業務場景;而 RxJS 強大的功能,可以很好地粘合多項技術,如 Svelte 和 RSocket,擁抱 Reatctivity 毫無壓力。
第十五屆 D2 前端技術論壇,将會邀請 RxJS 作者 Ben Lesh 進行《重構 RxJS 架構:我們如何讓其更小、更快》的主題演講,Ben 會就其 RxJS 的開發經驗和大家分享一下他的經驗,相信大家一定會有新的收獲。
關注「Alibaba F2E」
把握阿裡巴巴前端新動向