Blazor和Vue对比学习(基础-8):Blazor中实现计算属性和数据监听
1.7章《传递UI片断》,需要做几个案例,这部分暂停消化几天。我们先把基础部分相对简单的最后两章学习了。
计算属性和数据监听是Vue当中的概念,本质上都是监听数据的变化,然后做出响应。两者的区别,在于响应方式的不同。
1、计算属性,如【const result = computed(()={return a + b})】。知名见意,是一个计算表达式,表达式中使用到的响应式变量都是它监听的对象,只要其中有任何变量发生变化,结果都会重新计算。可以理解为EXCEL里的计算,比如某个单元格的公式为“=A2+A1”。A2或A1发生变化是,结果也随之改变。
2、数据监听,如【watch(a,(newValue,oldValue)=>{console.log(newValue,oldValue)})】,其中a为被监听对象(必须是响应式),当a发生变化时,执行后面的回调函数,newValue是被监听对象的新值,oldValue为旧值,这两个值在回调的方法体里面,可以使用。
3、computed和watch,都会因被监听对象的改变,自动做出响应。不同的是,computed是为了返回一个计算结果(这个结果是ref响应式的);而watch是为了执行一个回调函数,做一些事情。所以,理论上watch也可以返回一个计算结果,实现computed的功能。
Blazor中没有计算属性和数据监听的概念,但也能实现相应的功能,只是本人技术有限,会有些差异限制。我们直接通过案例来进行对比:
一、Vue的计算属性computed和Blazor的实现
//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.value); } </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的数据监听watch和Blazor的实现
//先说结论:虽然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吗。【或许社区已经实现了,只是我还不知道!】
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战