1. 程式人生 > >Blazui 常見問題:我更新了資料,為什麼介面沒重新整理?

Blazui 常見問題:我更新了資料,為什麼介面沒重新整理?

首發於:http://www.blazor.group:8000/topic/reply?tpid=9

開門見山,不介紹,不廢話
建議食用本文前先食用 https://www.cnblogs.com/wzxinchen/p/12082136.html

正常情況下,Blazor 的介面是怎樣重新整理的?

Blazor 繫結(繫結就是重新整理)機制有以下幾種

  1. 首次載入時的自動繫結
  2. 呼叫 StateHasChanged 強制重新繫結(重新繫結即重新整理)
  3. 註冊事件自動重新整理

對於第三點,註冊事件是指類似於以下程式碼

<BMenu OnClick="ShowMenu"></BMenu>

注意這程式碼是瞎寫的,僅僅為了示例
在上面程式碼中,註冊了 OnClick 事件,處理程式為 ShowMenu
OnClick 既然是事件,那它總得有個方法簽名,來規定 ShowMenu 的方法簽名是什麼
一般來說,有兩種方法簽名:

  1. 直接就是一個委託
  2. EventCallBack結構體

對於第一種情況,OnClick 事件的定義往往是

[Parameter]
public Action OnClick {get;set;}

對於第二種情況,OnClick 事件的定義往往是

[Parameter]
public EventCallBack OnClick {get;set;}

這兩種情況在呼叫方註冊的方法是一樣的,那麼這兩種情況有什麼不一樣的?

不同點很小,第一種情況沒有什麼好說的,第二種情況,EventCallBack 內部呼叫了 StateHasChanged。也就是說,如果事件定義是第二種,那麼在方法執行完後會自動重新整理一次,如果是第一種,則需要手動重新整理

預設情況下,Blazor 的重新整理機制有怎樣的問題?

  1. 通過第一段我們知道,如果我們註冊了一個事件,而且這個事件是 EventCallBack,那麼在方法呼叫完成之後會自動重新整理
    如果我們註冊的不是 OnClick 事件,而是 OnMouseMove 事件呢?那麼 StateHasChanged 事件會被不停得呼叫,會給服務端造成極大壓力
  2. 我們在編寫頁面的時候,往往會在不同的地點手動呼叫 StateHasChanged 方法來重新整理介面,假設 A 方法呼叫了 B 方法,B 又呼叫 C,並且 A 是型別為 EventCallBack 事件處理程式,然後 B、C裡面最開始都呼叫了 StateHasChanged,這會重新整理多少次?你的伺服器資源會被吃光。

Blazor 如何解決這個問題?

Blazor 的 ComponentBase 類中,提供了 ShouldRender 這個方法,當這個方法返回為 false 時,不會執行渲染,即使你呼叫了 StateHasChanged,仍然不會渲染,這相當於讓你來決定,什麼時候才是真的需要渲染,什麼時候呼叫 StateHasChanged 才會生效。

你的 Blazui 元件介面為什麼沒重新整理?

基於 Blazor 的解決辦法,當元件第一次渲染完成之後,ShouldRender 會返回為 false,然後後面呼叫都返回 false,對於任意一個元件,若出現沒重新整理的情況下,請考慮這個因素。
那麼,如何讓 Blazui 元件進行重新整理呢?方法很簡單,呼叫該元件的 MarkAsRequireRender 方法,標記該元件需要重新整理。注意這個方法只是標記為需要重新整理,如果不是自動呼叫的 StateHasChanged 方法,那麼你需要手動調一次才重新整理,這個方法 Blazui 封裝為 Refresh 方法,內部直接調的 StateHasChanged 方法

我呼叫了 MarkAsRequireRender 方法,為什麼仍然沒重新整理?

到了這步,仍然沒解決,可能會有些頭疼

因為要重新整理的元件的父元件沒重新整理

這裡涉及到一個很重要的概念,Blazor 中,一個頁面中的所有元件,是一顆樹
可以簡單理解為二叉樹或是N叉樹,每一個節點,就是一個元件,這個節點下的直屬節點,是這個元件的子元件

<A>
  <B>
     <C>
     </C>
  </B>
</A>

A 是 B 的父元件,B 是 C 的父元件,要刷 C,你必須刷 B,注意這裡的刷 B 並不會重新整理 B 本身,而是刷的 B 的直屬子元件,現在,你應該可以理解,我要重新整理 B,我又該刷誰?當然是刷 B 的父元件
為什麼會這樣?因為你如果重新整理 B,那麼 B 所需要的引數根本不會更新,這樣一來也根本無法重新整理資料,它的主要目的,是更新 B 的直屬子元件的所有引數,這樣才能重新整理 B 的直屬子元件

因為當前頁面所屬元件沒重新整理

Blazor 中,一切皆元件,當前頁面它仍然是個元件,按照第一點講到的,你大概可以理解這種情況該怎麼辦,很簡單,設定當前頁面為需要重新整理即可
若當前頁面沒有繼承 BComponentBase 這個類,則不需要考慮這個情況。

你的非同步呼叫先後順序錯誤

這種情況是比較坑的,不容易發現。
考慮下面的程式碼

public void Test(){
    Task.Factory.StartNew(()=>{
        //從資料庫拉資料
    });
    
}

Test 是 OnClick 事件的處理方法,很簡單的一個情況,假設它不存在我們之前說的所有情況,這樣寫,仍然有概率不重新整理,注意,是有概率,只是這概率特高。
當這個方法執行完並且開始執行 StateHasChanged 時,非同步任務裡面的拉取資料的程式碼執行完了嗎?不一定,天知道。
通常情況是沒執行完,那麼就是說,當重新整理時,資料壓根就沒拉到,這樣的話,你的介面當然不會更新。

public async Task Test(){
    await Task.Factory.StartNew(()=>{
        //從資料庫拉資料
    });
    
}

改成這樣即