Blazorの状態管理

Blazorの状態管理

世界で最も長いフォームに記入していると想像してください。住所から誕生日、最近訪問した7カ国のリストまで、詳細を入力するのに30分かかりました。[送信]ボタンをクリックすると、すぐに[接続が失われました]というメッセージが表示されます。

最后更新 2022/04/18 19:53
寒冰屋
预计阅读 10 分钟
分类
Blazor
标签
.NET Blazor 状態管理の状況

世界で最も長いフォームに記入していると想像してください。住所から誕生日、最近訪問した7カ国のリストまで、詳細を入力するのに30分かかりました。[送信]ボタンをクリックすると、すぐに[接続が失われました]というメッセージが表示されます。心配ないよね?“戻る”ボタンをクリックするだけで、“ああ、いいえ!テーブルは空。それは残酷に見え、二度とサイトを訪問しないことを約束します。

これはあなたのウェブサイト訪問者が望む経験ではない。したがって、Blazorアプリケーションで状態を管理する方法を理解することが重要です。状態を管理するために書かなければならないコードの量を最小限に抑えながら、状態を管理するか。“Yes,please!”

观看相关视频:Blazor Apps 中的状态管理

1ブレザーの定義

まず、Blazorアプリでの“状態”の意味を理解しましょう。最高のユーザーエクスペリエンスを得るためには、エンドユーザーの接続が一時的に中断されたり、ページをリフレッシュしたり、ナビゲートしたりしても、エンドユーザーに一貫したエクスペリエンスを提供することが重要です。経験の構成要素は以下のとおりです。

  • ユーザインタフェースUIを表すHTMLドキュメントオブジェクトモデルDOM
  • ページ上で入力および书き出し中のデータを表すフィールドおよび属性
  • ページコードの一部として実行しているサービスの

在没有任何特殊代码的情况下,根据Blazor 托管模型,将状态保存在两个位置。对于 Blazor WebAssembly(客户端)应用程序,状态保持在浏览器内存中,直到用户刷新或导航离开页面为止。在 Blazor Server 应用程序中,状态保存在分配给每个称为电路的每个客户端会话的特殊“存储桶”中。这些电路在断开连接后超时时可能会丢失状态,甚至在服务器处于内存压力下的活动连接过程中也可能消失。

2. リファレンス·アプリケーション

为了说明状态的细微差别,我从Blazor Health App开始:

从 Angular 到 Blazor:The Health App

Blazorでサンプルアプリケーションを構築します。Blazorは、ブラウザで動作するWebアプリケーションを構築するための. NETベースのフレームワークで、C#とRazorテンプレートを使用してクロスプラットフォームのHTML 5準拠Web Assemblyコードを生成します。

ナビゲーションのニュアンスを説明するために2ページを含むように拡張しました。関連するGitHubリポジトリから:

JeremyLikness/BlazorState

サンプルプロジェクトがいくつかある。問題はBlazor Web AssemblyプロジェクトとBlazor Serverプロジェクトでは異なります。

3. Blazor Web Assemblyの状態

Blazor Web Assembly(クライアントプロジェクト)では、状態はメモリに保存されます。つまり、ナビゲーションを更新または強制すると状態が破壊されます。実際の効果を確認するには、次の手順に従います。

  1. 设置BlazorState.Wasm为启动项目并运行它。
  2. フォーム情報を更新。
  3. [結果]に移動し、同じ結果が存在することを確認します。
  4. 导航回“主页”并强制刷新(通常为CTRL+F5)。请注意该窗体还原为默认值。
  5. 更新表单信息,然后通过添加/results到浏览器中的 URL 栏来手动导航,然后按ENTER。请注意,它也使用默认值。

悪い経験だ! Blazor Serverとは少し異なります。

4. Blazorサーバー内の状態

将启动项目更改为BlazorState.Server并运行该项目。请尝试执行与客户端版本相同的步骤,并注意保持了状态,因为该状态保存在服务器内存中。打开应用程序后,停止并重新启动 Web 服务器。您应该看到一个断开连接消息。服务器重新启动后,单击“重新加载”选项,请注意,尽管应用程序已恢复,但它会丢失所有状态。

今問題があります解決策を見てみましょう!

5. ソリューション·アーキテクチャ

以下解决方案使用一种旨在最大化重用性的体系结构方法。Blazor.ViewModel 项目托管该应用程序的界面、属性和业务逻辑。它是Model-View-ViewModel(MVVM)模式的.NET Standard 库实现,可以从 WPF,Xamarin 甚至 Blazor 的任何类型的.NET Core 项目轻松引用。最大程度的重用!

对于 UI 和用户体验逻辑,以及可共享资产(例如图像,样式表,JavaScript 代码甚至 Razor 视图组件),Blazor.Shared都利用Razor类库。该解决方案实现了 HealthModelBase 避免重复的 MVVM 代码的功能。它还将此处描述的所有状态管理解决方案实现为可轻松应用于 Blazor WebAssembly 和 Blazor Server 项目的服务和/或组件。由于“宿主”项目仅提供一些结构来引用共享组件和资源,因此这进一步使代码重用最大化。

問題と解決策を解決したので、Blazorアプリでの状態管理に進みましょう!

6. サービスの登録

第一步可能并不那么明显,但是为了全面起见,我想介绍服务。要查看实际效果,请创建一个新的 Blazor 客户端应用程序并运行它。内置模板为几个页面提供了简单的导航。导航到Counter页面并增加计数器。现在,离开页面浏览并返回。计数器重置为零!这是因为计数器的状态保留在组件中,因此每次初始化组件时都会将其重置:

<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
private int currentCount = 0;
private void IncrementCount()
{
    currentCount++;
}

“インメモリ”(またはBlazorサーバー“インサーキット”)状態を維持するには、カウンター“サービス”を作成します。

public class CounterService
{
    public int Count { get; private set; }
    public void Increment()
    {
        Count += 1;
    }
}

Startup.cs以下位置注册服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<CounterService, CounterService>();
}

…然后删除@code块中的代码Counter.razor,注入计数器服务并直接进行数据绑定:

@inject CounterService Svc
<h1>Counter</h1>
<p>Current count: @Svc.Count</p>
<button class="btn btn-primary" @onclick="Svc.Increment">Click me</button>

コンポーネントが破棄/再作成されると、サービスはメモリに残り、ナビゲーション中でも一貫したカウントを維持します。これが維持の第一歩です。リファレンスアプリケーションは、この方法でマスタービューモデルを登録します。

7. ブラウザのキャッシュ

维护状态的一种方法是使用HTML5 Web Storage来利用浏览器缓存。该 API 非常简单。BlazorState.Shared中的stateManagement.js文件定义了一个简单的,可全局访问的界面。它使用localStorage JavaScript API,但您可以选择使用sessionStorage

window.stateManager = {
    save: function (key, str) {
        localStorage[key] = str;
    },
    load: function (key) {
        return localStorage[key];
    }
};

它包含在 Blazor WebAssembly 项目的index.html和 Blazor Server 项目的_Host.cshtml的根目录中。包括共享资产(shared assets)就像使用路径一样简单:

<script src="_content/BlazorState.Shared/stateManagement.js"></script>

Blazor 的组件模型使创建用于管理状态更改的“包装器”组件变得简单。这是在StorageHelper.razor中实现的。首先,using 语句引用视图模型,JavaScript 互操作性和 JSON 序列化程序。实现被注入。

@using Microsoft.JSInterop; @using System.Text.Json; @inject IJSRuntime
JsRuntime @inject IHealthModel Model

テンプレートは単にサブコンポーネントをラップし、状態がロードされるとレンダリングします。

@if (hasLoaded) { @ChildContent } else {
<p>Loading...</p>
}

コンポーネントを初期化した後、コードはキャッシュからビュー·モデルをロードしようとします。

string vm;
try
{
    vm = await JsRuntime.InvokeAsync<string>("stateManager.load", nameof(HealthModel));
}
catch(InvalidOperationException)
{
    return;
}

在 Blazor 服务器中,组件已在服务器上预渲染。JavaScript 不可用,因此 interop 调用将抛出InvalidOperationException。这是第一次被发现。第二个调用是从客户端进行的,并且如果缓存了 viewmodel,它将成功。从高速缓存加载视图模型的 JSON 后,将对其进行反序列化,并将属性移至全局视图模型实例。

var viewModel = JsonSerializer.Deserialize<HealthModel>(vm);
if (viewModel != null)
{
    isDeserializing = true;
    Model.AgeYears = viewModel.AgeYears;
    Model.HeightInches = viewModel.HeightInches;
    Model.IsFemale = viewModel.IsFemale;
    Model.IsImperial = viewModel.IsImperial;
    Model.WeightPounds = viewModel.WeightPounds;
    isDeserializing = false;
}

isDeserializing标志对于避免无限循环很重要,正如您在下一个注册属性更改通知的代码中所看到的:

Model.PropertyChanged += async (o, e) =>
{
    if (isDeserializing)
    {
        return;
    }
    var vmStr = JsonSerializer.Serialize(((HealthModel)Model));
    await JsRuntime.InvokeAsync<object>(
        "stateManager.save", nameof(HealthModel), vmStr);
};
hasLoaded = true;

如果视图模型上的属性发生更改,则视图模型将被序列化并存储在缓存中。当由于初始加载而触发了属性更改时,将跳过此操作(因此会出现该isDeserializing标志,否则它将在尝试反序列化时进行序列化)。现在该组件可以使用了!Blazor.ServerLocalBlazor.WasmLocal都是的帮助类,它在App.razor中的实现方式相同:

<BlazorState.Shared.StorageHelper>
  <Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
      <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
      <LayoutView Layout="@typeof(MainLayout)">
        <p>Sorry, there's nothing at this address.</p>
      </LayoutView>
    </NotFound>
  </Router>
</BlazorState.Shared.StorageHelper>

ラップルータを使用すると、状態管理はアプリケーション内のすべてのページとコンポーネントを追加のコードを記述することなく処理できます。ブラウザの開発ツールを開き、アプリケーションの“ローカルストレージ”に移動して、フォームの更新時に値がどのように変化するかを確認できます。

重要的是要注意,用户可以访问其本地缓存,因此,如果您要存储敏感值,则应对它们进行加密。Microsoft.ASpNetCore.ProtectedBrowserStorage包提供了一个示例。

8. サーバー側の管理

处理状态的另一种方法是调用 API 并将其持久保存在服务器上。它的持久性取决于您:选择范围从 SQL,NoSQL 到简单的缓存(例如 Redis)。BlazorState.WasmRemote.Server是 ASP.NET 托管的 Blazor WebAssembly 应用程序。该StateController公开一个 API,它存储和检索使用的远程 IP 地址作为密钥对视图模型。这样做是为了保持演示简单。具有身份验证的生产应用程序可能会锁定用户和/或会话。

Blazor.Shared中的StateService处理 API 调用。构造函数接受全局 viewmodel 实例,提供 API 端点 URL 的IStateServiceConfig实例和HttpClient的实例。注入HttpClient而不是创建新实例非常重要,因为 Blazor WebAssembly 需要专门配置为在浏览器沙箱中运行的版本。构造函数从视图模型注册属性更改通知。

在初始化期间由页面组件调用InitAsync以加载视图模型状态。

public async Task InitAsync()
{
    _initializing = true;
    var vmJson = await _client.GetStringAsync(_config.Url);
    var vm = JsonSerializer.Deserialize<HealthModel>(vmJson, _options);
    _model.AgeYears = vm.AgeYears;
    _model.HeightInches = vm.HeightInches;
    _model.IsFemale = vm.IsFemale;
    _model.IsMetric = vm.IsMetric;
    _model.WeightPounds = vm.WeightPounds;
    _initializing = false;
}

このコードはクライアント側のキャッシュメソッドによく似ていますが、ローカルキャッシュではなくAPIコールからモデルを取得します。プロパティ変更ハンドラは、モデルをシリアル化してサーバーにパブリッシュします。

private async void Model_PropertyChanged(object sender,
    System.ComponentModel.PropertyChangedEventArgs e)
{
    if (_initializing || _config == null)
    {
        return;
    }
    var vm = JsonSerializer.Serialize(_model);
    var content = new StringContent(vm);
    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    await _client.PostAsync(_config.Url, content);
}

设置BlazorState.WasmRemote.Server为启动项目并运行它以查看实际效果。您可能需要更新在.Client项目中IStateServiceConfigStartup.cs实现中的正确 URL(因为端口可能不同)。在运行解决方案的情况下,打开“网络”选项卡,并在更新表单时记下调用。

このサービスはBlazor Web Assembly用にデモされているが、Blazor Serverと同じである。

9. 結論:結論

Blazor 对您如何管理状态没有意见。服务和组件模型使实现项目范围的解决方案变得容易。这篇文章着重于 Model-View-ViewModel 模式的实现,并注册了属性更改通知以处理本地或通过 API 的序列化状态。如果您使用诸如Redux之类的不同方法,则相同的方法将起作用。重要的步骤是在属性发生变化时更新商店,并在组件初始化时从状态管理解决方案加载。剩下的就是浏览器的历史记录!

查看有关ASP.NET Core Blazor 状态管理的官方文档。

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2024/11/06

なぜ私のブログはBlazorに戻るのか?

ブログサイトの開発は苦労し、MVC、Vue、Goなどの10近くのバージョンを試してきましたが、Blazorに戻り、静的SSRを使用して、速度が急上昇し、正常にオンラインになりました。

继续阅读
同分类 / 同标签 2024/02/29

Winformでもデータを表示できます。

winform開発の過程では、しばしばデータ表示機能を行う必要がありますが、以前はグリッドコントロールを使用していましたが、今日は例を通じて、Winform Blazorハイブリッドでant Design Blazorテーブルコンポーネントを使用してデータ表示を行う方法を紹介します。

继续阅读
同分类 / 同标签 2024/02/29

Winformのインターフェースも良く見えますか?

先日、winformでBlazorハイブリッドを使用することを紹介しましたが、Blazor UIを使用するとwinformプログラムがより良く見えるようになると述べました。次に、winform Blazorハイブリッドで描画する例を示したいと思います。

继续阅读
同分类 / 同标签 2024/01/07

“記事タイトルURLエイリアスジェネレータ”を公開

Code Factoryは、ウェブサイトツール、クロスプラットフォームのデスクトップおよびモバイルアプリケーションを提供するウェブマスターのための新しいオープンソースプロジェクトです。ウェブマスターは最終的により効率的で便利な体験を提供することを約束します。今日、ウェブマスターは記事タイトルURLエイリアスを簡単に作成し、SEOとユーザーエクスペリエンスを向上させるのに役立つ記事タイトルURLエイリアスジェネレータを導入しました。もっと実用的なツールを発見してください!

继续阅读