几分钟内使用 ReactiveX.Net 构建实时 React 图表
如何轻松地将来自跨平台 .NET 后端的异步实时数据流添加到您的 React Web 应用程序
引言
如果您曾经需要处理异步数据流,您可能已经使用过或至少听说过 ReactiveX,这是一个用于反应式编程的库,它提供了强大的 API 来将数据流转换为可观察序列,可以订阅和操作这些序列。 但它与常规事件驱动方法的区别在于,它能够从多个其他可观察序列中组合新的数据流,您可以灵活地组合、过滤或转换这些数据流。
我将演示如何利用它和我自己的库 dotNetify-React 来使异步实时 Web 应用程序的实现变得非常简单。 这是我们将要构建的内容的输出
它是一个 Web 浏览器上的实时图表,由异步数据流提供数据,该数据流本身由两个不同的数据流组成:一个用于正弦波信号,另一个用于其振幅。
以下步骤使用 Facebook 的 create-react-app 样板来生成 Web 应用程序,并使用 .NET Core SDK 运行后端。 您需要先安装它们。
如果您只想拉取源代码,请访问 Github dotnetify-react-demo。 还有一个在 Visual Studio 2017 上运行的版本,位于 dotnetify-react-demo-vs2017。
前端
我们将从创建应用程序外壳并安装所需的库开始
create-react-app livechart cd livechart npm install dotnetify --save npm install chart.js@1.1.1 --save npm install react-chartjs@0.8.0 --save npm install concurrently --save-dev
(我坚持使用旧版本的图表库,因为自那时以来 API 已经改变,我对它们还不熟悉。)
添加组件 /src/LiveChart.jsx,它将呈现图表
import React from 'react';
import dotnetify from 'dotnetify';
import { Bar } from 'react-chartjs';
export default class LiveChart extends React.Component {
constructor(props) {
super(props);
dotnetify.react.connect("LiveChart", this);
this.state = {};
this.chartData = {
labels: Array(10).fill(""),
datasets: [{
data: Array(10),
fillColor: 'rgba(75, 192, 192, 0.2)',
strokeColor: 'rgba(75, 192, 192, 1)'
}]
};
this.chartOptions = {
responsive: true,
scaleOverride: true,
scaleSteps: 5,
scaleStepWidth: 10
};
this.updateChart = value => {
this.chartData.datasets[0].data.shift();
this.chartData.datasets[0].data.push(value);
};
}
render() {
return (
<Bar data={this.chartData} options={this.chartOptions}>
{this.updateChart(this.state.NextValue)}
</Bar>
);
}
}
此组件最初将使用来自 *react-chartjs* 的条形图组件以及一个空数据集来呈现图表。 一旦通过 *dotNetify* 建立与后端的连接,该组件将收到对 `this.state.NextValue` 的实时更新,这反过来会导致图表使用新的数据集值重新呈现。
接下来,替换默认的 /src/App.js 以呈现我们的组件
import React, { Component } from 'react';
import LiveChart from './LiveChart';
export default class App extends Component {
render() {
return <LiveChart />
}
}
后端
有了前端之后,我们现在添加 .NET Core 后端部分。 首先创建一个默认的 ASP.NET Core Web 项目并安装所需的包
dotnet new web
dotnet add package DotNetify.SignalR --version 2.1.0-pre
dotnet add package System.Reactive
dotnet restore
打开 *package.json* 并添加以下行,以将 Node 开发服务器未处理的请求重定向到 .NET Core 服务器
"proxy": "https://:5000/",
仍然在 *package.json* 中,修改调用 `react-scripts` 的行,以使用 `concurrently` 库启动 Node 和 .NET Core 服务器
"start": "concurrently \"react-scripts start\"
\"dotnet run\" --kill-others",
接下来,添加类 LiveChart.cs,它将为前端组件提供实时更新
using System;
using System.Reactive.Linq;
namespace livechart
{
public class LiveChart : DotNetify.BaseVM
{
private IDisposable _subscription;
public int NextValue { get; set; }
public LiveChart()
{
var sine = Observable
.Interval(TimeSpan.FromMilliseconds(100))
.Select(t => Math.Sin(2 * Math.PI * .06 * t));
var amp = Observable
.Interval(TimeSpan.FromMilliseconds(100))
.Select(a => a % 50 + 1);
_subscription = Observable
.Zip(sine, amp, (s, a) => (int) Math.Abs( s * a))
.Subscribe(value =>
{
NextValue = value;
Changed(nameof(NextValue));
PushUpdates();
});
}
public override void Dispose() => _subscription.Dispose();
}
}
这个类背后的想法是生成一个由两个其他流组成的数据流:一个用于正弦波信号,另一个用于数字迭代以产生波动的幅度。 为了创建流,我们使用 *Rx* API `Observable.Interval` 在时间间隔内发出一个整数序列,然后将其进一步投影到所需的序列中。 然后将这两个流与 `Observable.Zip` 组合成一个单一的流,我们的类实例会订阅该流。
当新数据可用时,我们使用 *dotNetify* API `Changed` 和 `PushUpdates` 将数据发送到前端组件以更新其本地状态。 实际的通信是通过 *SignalR* 完成的,它将在可用时使用 `WebSocket`。 但我们不必担心它,因为它已经被抽象掉了。
接下来,在 *Startup.cs* 中配置 *dotNetify* 和 *SignalR*
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using DotNetify;
namespace livechart
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddSignalR();
services.AddDotNetify();
}
public void Configure(IApplicationBuilder app)
{
app.UseWebSockets();
app.UseSignalR();
app.UseDotNetify();
app.Run(async (context) =>
{
await context.Response.WriteAsync("LiveChart server");
});
}
}
}
最后,构建并运行应用程序
dotnet build
npm start
摘要
就是这样。 您可以在几分钟内快速构建一个异步实时 Web 应用程序。 尽管这是一个有些牵强的例子,但我希望它仍然可以用来展示这种技术是多么强大。
在现实世界的场景中,Web 客户端可能正在等待多个后端微服务,这些微服务的异步输出需要链接在一起才能生成最终结果。 使用 *ReactiveX* 和 *dotNetify* 的组合将大大降低代码的复杂性并节省您的时间和精力。