1. 程式人生 > 其它 >Blazor和Vue對比學習(基礎1.8):Blazor中實現計算屬性和資料監聽

Blazor和Vue對比學習(基礎1.8):Blazor中實現計算屬性和資料監聽

1.7章《傳遞UI片斷》,需要做幾個案例,這部分暫停消化幾天。我們先把基礎部分相對簡單的最後兩章學習了。

計算屬性和資料監聽是Vue當中的概念,本質上都是監聽資料的變化,然後做出響應。兩者的區別,在於響應方式的不同。

1、計算屬性,如【const result = computed(()={return a + b})】。知名見意,是一個計算表示式,表示式中使用到的響應式變數都是它監聽的物件,只要其中有任何變數發生變化,結果都會重新計算。可以理解為EXCEL裡的計算,比如某個單元格的公式為“=A2+A1”。A2A1發生變化是,結果也隨之改變。

2、資料監聽,如【watch(a,(newValue,oldValue)=>{console.log(newValue,oldValue)})

】,其中a為被監聽物件(必須是響應式),當a發生變化時,執行後面的回撥函式,newValue是被監聽物件的新值,oldValue為舊值,這兩個值在回撥的方法體裡面,可以使用。

3computedwatch,都會因被監聽物件的改變,自動做出響應。不同的是,computed是為了返回一個計算結果(這個結果是ref響應式的);而watch是為了執行一個回撥函式,做一些事情。所以,理論上watch也可以返回一個計算結果,實現computed的功能。

Blazor中沒有計算屬性和資料監聽的概念,但也能實現相應的功能,只是本人技術有限,會有些差異限制。我們直接通過案例來進行對比:

一、Vue的計算屬性

computedBlazor的實現

//Vue=====================================
//Vue使用computed來實現一個表示式的計算,引數是一個回撥,在方法體裡return出計算表示式。computed的返回值是一個ComputedRefImpl型別物件,使用方式和ref響應式物件一樣
//實際上computed的值是可寫的,如下寫法:computed({get(){},set(newValue){}}),在set裡寫修改邏輯。但非常不推薦這麼做,一違反這個API的設計初衷,二真沒必要。
<template>
    <h1>a:{{a}}</h1>
    <h1>b:{{b}}</h1>
    <h1>result:{{result}}</h1>
    <button @click="
a++">a++</button> <button @click="b++">b++</button> <button @click="show">show</button> </template> <script setup> import {ref,computed} from 'vue' const a = ref(1) const b = ref(2) const result = computed(()=>{ console.log('執行計算'); //監測計算的執行 return a.value+b.value }) //result是ComputedRefImpl型別物件,邏輯層也需要像ref一樣 //在檢視層,result也和ref物件一樣,會自動解包,可以直接使用 function show(){ console.log(result); } </script> //Blazor==================================== //計算屬性,本質上就是表示式,我們知道code裡,表示式結果天然就是響應式的。所以我們可以定義一個變數,並根據其它變數來計算, <h1>a:{{a}}</h1> <h1>b:{{b}}</h1> <h1>result1:{{result1}}</h1> <h1>Result2:{{Result2}}</h1> <button @onclick="a++">a++</button> <button @onclick="b++">b++</button> @code{ private int a = 1; private int b = 2; private int result1 = a + b; //欄位方式 public int Result2 => a + b; //屬性方式(只讀屬性的簡寫) //完整隻讀屬性 private int result3; public int Result3{ get{ Console.WriteLine("執行計算"); //監測計算的執行 return a + b; } }} //Vue和Blazor的區別 //難道在Blazor裡,我們就這麼簡單了實現了computed?too young,too native //我們分別在Vue和Blazor的檢視層裡,多次讀取計算值,看看區別 //Vue中執行以下程式碼時,【console.log('執行計算')】只輸出一次,計算結果被快取,如果a和b不變,無論讀取多少次,計算都不會重複執行。如果計算很複雜,將大大提升效能 <h1>result:{{result}}</h1> <h1>result:{{result}}</h1> <h1>result:{{result}}</h1> //Blazor中執行以下程式碼時,【Console.WriteLine("執行計算");】輸出三次,每次讀取,都要執行一次計算。如果存在多次渲染、且計算複雜的情況,就需要特別注意 <h1>Result2:{{Result2}}</h1> <h1>Result2:{{Result2}}</h1> <h1>Result2:{{Result2}}</h1>

二、Vue的資料監聽watchBlazor的實現

//先說結論:雖然Blazor能夠實現部分資料監控的功能,但存在很多缺漏。這不像計算屬性,雖然存在效能問題,但功能是完整的
//Blazor對watch的實現,應該算是失敗了

//Vue=====================================
//先簡單彙總一下Vue中watch的基本用法
<template>
    <h1>a:{{a}}</h1>
    <h1>b:{{b}}</h1>
    <button @click="a++">a++</button>
    <button @click="b++">b++</button>
    <button @click="people.look.height++">people物件身高++</button>
</template>

<script setup>
import {ref, reactive,watch,watchEffect} from 'vue'
const a = ref(1)
const b = ref(1)
const people = reactive({name:"functionMC",look:{height:170,weight:130}})

//監視一個屬性
watch(a,(newValue,oldValue)=>{
    console.log(`a值被修改了,新值為${newValue},舊值為${oldValue}`)
})
//監視一個回調錶達式,其中newValue和oldValue是表示式的結果
watch(()=>a.value+b.value,(newValue,oldValue)=>{
    console.log(`a+b值被修改了,新值為${newValue},舊值為${oldValue}`)
})
//監視多個屬性,其中newValue和oldValue都是返回形如[a,b]的陣列
watch([a,b],(newValue,oldValue)=>{
    console.log(`a或b值被修改了,新值為${newValue},舊值為${oldValue}`)
})
//Vue3的watch預設開啟深度監視,可以監視物件的深層次改變,newValue和oldValue相等,都是新值
watch(people,(newValue,oldValue)=>{
    console.log(`people的身高修改了,新值為${newValue.look.height},舊值為${oldValue.look.height}`)
})
//watchEffect是另外一個監聽API,會自動監聽回撥中使用到的所有響應式資料,而且【在元件初次載入時會先執行一次回撥】,而watch是懶載入,元件初次載入時,並不會呼叫回撥
watchEffect(()=>{
    const result = a.value+b.value
    const height = people.look.height
    console.log(`a+b=${result},people的身高為${height}`)
})

//watch和watchEffect的監聽回撥時機,是在DOM更新之前,所以回撥中拿到的DOM是舊狀態,如果要拿到更新後的新狀態,需要配置一下
//watch(source, callback, {flush: 'post'})
//watchEffect(callback, {flush: 'post'})

</script>


//Blazor===================================
<h1>a:@a</h1>
<h1>b:@b</h1>
<h1>身高:@People.Look.Height</h1>
<button @onclick="@(()=>A++)">A++</button>
<button @onclick="@(()=>B++)">B++</button>
<button @onclick="@(()=>C++)">C++</button>
<button @onclick="@(()=>People.Look.Height++)">people物件身高++</button>

@code {

    //監視單個屬性時,直接在set裡執行回撥
    private int a;
    public int A
    {
        get { return a; }
        set { 
                Console.WriteLine($"A值被修改了,新值為{value},舊值為{a}"); 
                a = value; }
    }

    //監視多個屬性時,在多個屬性的set裡執行一個委託
    //action委託定義回撥
    private Action action = () =>
    {
        Console.WriteLine("B值或C值被修改了,無法獲得新舊值");
    };

    private int b;
    public int B
    {
        get { return b; }
        set { b = value; action.Invoke(); } //值被修改時,執行委託
    }

    private int c;
    public int C
    {
        get { return c; }
        set { c = value; action.Invoke(); } //值被修改時,執行委託
    }

    //引用型別,無法監視到值得改變
    private People people = new People
    {
        Name = "functionMC",
        Age = 18,
        Look = new Look { Height = 170, Weight = 130 }
    };
    public People People
    {
        get { return people; }
        set 
        { 
            people = value; 
            Console.WriteLine("無法監視值得改變");
        }
     }

    //使用生命週期函式執行回撥
    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);
        Console.WriteLine("有數值被改了,但不知道是哪一個");
    }

}

三、總結:

1、Blazor通過get,可以實現computed的功能,但不具備cumputed的快取能力,在執行復雜計算且在模板中多次渲染時,需要特別注意。

2、Blazor通過set,可以實現watch的部分功能,但實現不完整,直接的說,無法實現watch。

3、Blazor有沒有可能實現完整的computed和watch?是有可能的!比如祖傳的MVVM實現了屬性變化通知,不就是watch嗎。【或許社群已經實現了,只是我還不知道!】