计算属性computed
computed函数接收两个参数(get函数与set函数),调用computed函数可创建一个ComputedRefImpl对象(内含value、_value、_dirty
等属性与函数)。
computed的参数get函数体内放置依赖对象与被读时要返回的值或计算式的代码。
参数set函数接收写入值,函数体内放置value属性被改写时的较验、加工、生效代码。
computed函数创建对象如下:
<template>
<h1>{{wow}}</h1>
<h1>{{obj}}</h1>
<button @click="ee">改变依赖</button>
</template>
<script setup>
import { reactive,ref ,computed} from 'vue';
let obj =ref({eee:9})
let wow=computed(()=>{return obj.value.eee+1})
function ee(){obj.value.eee=98, console.log(wow,obj)}
</script>
杂谈响应原理
响应式对象能使DOM响应更新,是因为响应式对象会收集依赖自已的DOM名单,当自已改变时,会依名单通知DOM重新读取自已。
一、computed的get
1、computed函数的书写规范:computed({get与set对象})
- 参数是对象,注意要用{}括起:computed({get(){},set(){}})
- 只有get:通常我们只用到get,可以省略set:computed({get(){}})。
- 只有get时可以匿名,匿名要去掉{}:setcomputed(()=>{})
2、computed内部原理
- a、名单收集:
computed会将所有读取computed的DOM对象与computed对象收入到名单列表里。 - b、名单并入响应式对象的依赖:
computed会将名单及自已并入给get内部的响应式对象的依赖名单内。 - c、响应式对象双重告知:
当get内部的响应式对象发生改变时,会依名单告知所有依赖(告知读取computed的DOM对象及告知computed对象)。(注意computed不是响应式对象,computed虽有名单但并不会告知DOM对象)。 - d、computed收到告知处理:
当computed收到告知时,会将_dirty属性赋值为true。
computed初始化时为_dirty=true,computed被读取后_ dirty=fales。DOM初始渲染时会读取computed,因此DOM渲染完成后_ dirty=fales。 e、DOM对象收到告知处理:
DOM对象收到告知,而是先检查_dirty属性,当_dirty=fales
时读取value属性值;当_dirty=true
时会运行get函数,get函数返回值赋给value属性值,将_dirty=fales
、读取value属性值。
因此_ dirty属性直接读取value缓存值还是运行get函数后再读取value值,当有大量读取时,能大大节省计算资源。
如下强行使_dirty=true,以致于每次读取时都会运行get函数。<template> <h1 > {{wow}} </h1> <button @click="ee">测试_dirty=fales不会计算</button> <button @click="ef">测试_dirty=true会计算</button> </template> <script setup> import { reactive,ref ,computed} from 'vue'; let obj =ref({eee:1}) let wow=computed(()=>{console.log('我计算了'); {return obj.value.eee}}) function ee(){console.log(wow.value)} function ef(){wow._dirty=true,console.log(wow.value)} </script>
3、接受告知与被读取非同步
通常computed接受依赖告知时,dom也会收到告知对computed进行读取,但computed接受告知与被读取并不是同步的。
这是因为DOM更新机制(在一个作用域中,DOM更新总是在最后执行),依赖向computed发出告知后,还会执行作用域的其它代码,直到所有代码执行完毕才会执行DOM的computed读取。
多次告知一次读取: 因告知与被读取非同步,当一个作用域中有多次告知,但DOM的computed读取只在最后读取一次。
每告必读的实现: 如果要每次告知必须被读取一次,可在作用域内触发告知之后放置立即读取computed的代码。
如下:在wocao.na++发出告知后立即放置console.log(mabi.value)立即读取代码。使原本计算一次变成了计算两次。
<template>
<span>{{mabi}}</span><br>
<button @click="pp">侦听源一</button>
</template>
<script setup>
import { reactive,computed } from 'vue'
var eee=0
const wocao = reactive({name:0,na:2})
const mabi = computed(() => {console.log('计算一次');if(wocao.na>0){eee++; return wocao.name+eee}})
const pp=function(){wocao.na++,console.log(mabi.value),wocao.na++}
</script>
通常一次读取与每告必读的计算结果是一样的,但如果计算式内有自增减的普通变量,每次读取都会返回不同值,所在每告必读只在特别的情况下才使用。
4、简单计算与条件计算
简单计算
直接返回计算值的计算,如()=>{return a},return前不加判断语句。
return依赖: 跟在return后的计算式中的响应式对象称为return依赖。
get依赖: 出现在get里,但在return之外,并不参与计算的响应式对象称为get依赖。
无依赖return: 跟在return后的计算式中无响应式对象,称为无依赖return。无依赖return不会触发告知,需由get依赖触发告知。
如下:<template> <h1 >wow值: {{wow}} </h1> <button @click="ee">改变return依赖</button> <button @click="ef">改变return的普通变量</button> <button @click="ed">改变get依赖</button> </template> <script setup> import { reactive,ref ,computed} from 'vue'; let obj =ref('return依赖') let ob =ref('get依赖') var uu='return后的普通变量' let wow=computed(()=>{console.log(ob.value,'我计算了');return obj.value+'+'+uu}) function ee(){obj.value=obj.value+0} function ef(){uu+=0} function ed(){ob.value+=0} </script>
条件计算
在计算式前加入判断条件的计算叫条件计算,如()=>{if(uu){return a}}。
条件计算除了return依赖、get依赖外还有一种新的依赖,叫条件依赖。
条件依赖: 条件判断语句内的响应式对象称条件依赖。响应式对象更改会触发双重告知执行get。
多分支计算: 如采用if(){}else{}或if(){}else if(){}else{}语句会产生多分支计算,执行get时依条件执行其中一支。
条件组: 每种条件与其后的计算称为条件组。分为有依赖条件组(条件中有条件依赖)、无依赖条件组(条件中无条件依赖)。
当前条件组: 最后一次执行get依条件执行的那一支为当前条件组
顶级依赖: get依赖、条件依赖都是顶级依赖,顶级依赖改变时,会触发双重告知执行get。
次级依赖: return依赖为次级依赖。只有当前条件组的次级依赖才会触发告知。
如下,非当前条件组的依赖变化不会触发告知:<template> <h1 >wowo值: {{wowo}} </h1> <button @click="ef">改变当前条件计算组</button> <button @click="ee">次级依赖变化</button> </template> <script setup> import { reactive,ref ,computed} from 'vue'; let obj =ref(1) var uu=ref(true) let wowo=computed(()=>{console.log('计算了一次');if(uu.value){return obj.value}else{return'条件为假计算组'}}) function ee(){obj.value++,console.log(obj.value,wowo._dirty)} function ef(){uu.value=!uu.value} </script>
条件组切换: 有依赖条件组条件改变时,会触发执行get,能自动的切换到符合条件的条件组。无依赖条件组的条件改变时,不会触发执行get,需通过其内的次级依赖或外部的get依赖才能切换到符合条件的条件组。
如下是无依赖条件组利用其内的次级依赖和外部get依赖切换条件组的实例:<template> <h1 >wowo值: {{wowo}} </h1> <button @click="ef">点击:条件{{obb}}</button><br> <h4 >当前条件组为{{ui}},请点击改变下面{{ui}}条件组的次级依赖</h4> <button @click="er">true条件组的次级依赖</button> <button @click="ee">false条件组的次级依赖</button><br> 请点击改变下面的顶级依赖触发告知<br> <button @click="eer">我是get依赖</button> </template> <script setup> import { reactive,ref ,computed} from 'vue'; let obj =ref(1) let ob =ref(false) let oob =ref(false) let obb =ref('未改变') var uu=true var ui=uu let wowo=computed(()=>{ui=uu;oob.value;if(uu){return obj.value}else{return ob.value}}) function ef(){uu=!uu,obb.value='已改变'} function er(){obj.value++,obb.value='未改变'} function ee(){ob.value=!ob.value,obb.value='未改变'} function eer(){oob.value=!oob.value,obb.value='未改变'} </script>
5、依赖不能自增减
get内的依赖自增减,会导致计算再运行一次。后台会提示“计算仍然是脏的”
二、computed的set
computed是通过计算返回值赋给value属性,value值为computed被读取值,绕开计算直接赋值value会使计算失去意义,因此默认value属性只读,直接赋值value后台会警示“computed只读”。
1、set开启可写并拦截
set函数可开启value属性可写,并对写操作(赋值)进行拦截。
书写规范:set(newV){}
对value属性写入操作会向set函数传递一个参数,此参数为value属性写入的值。
2、set的功能
a、写操作生效
set开启了value属性可写,但任何对value属性的写操作(赋值)都会被set拦截。要使value属性写操作生效,需在set内对拦截值进行处理,以间接的方式使value属性等于要改写的值。这种使写操作生效的方法称为写操作生效。
使写操作生效的方法有两种,分别为_value属性全等法与依赖传导法。
_value
属性全等法: 利用_value属性与value属性全等特性,给_value赋value写操作值间接实现value写操作生效。
代码如下:set(newV){this._value=newV}
因computed不是响应对象,value属性值改变时不会告知DOM更新读取,因此要在写操作后使用绑定key属性来更新DOM依赖传导法: 利用依赖来触发get计算返回值给value属性的方法,曲线实现写操作生效。
如果get的return后不是一个计算式而是单一的一个响应式对象,代码如下:set(newV){依赖=newV}
;如果return后是计算式,需要对计算式进行反算,代码如下:set(newV){依赖=newV反算式}
两种方式实例如下:<template> <span :key="woca">{{mabi}}</span><br> <button @click="pp">通过_value使写生效</button> <button @click="pf">通过依赖使写生效</button> </template> <script setup> import { ref,computed } from 'vue' const wocao = ref(1) const woca = ref(true) const mabi = computed({ get() {console.log('我计算了') ;return wocao.value+1 }, set(newV) {if(newV==8){this._value=newV}else if(newV==9){wocao.value=newV-1} }}) const pp=function(){mabi.value=8,woca.value=!woca.value} const pf=function(){mabi.value=9} </script>
b、写较验与写加工
可以在set内对写的值进行较验或计算后,再通过_value属性全等法与依赖传导法返回写操作值。
c、_ value
_ value在不开启可写(无set)的情况下也可间接改变value的值。即不用在set函数内也能通过_value属性更改value属性值。
三、computed与普通函数的区别
在DOM中可以使用普通函数摸拟computed函数,实现computed函数的get功能(无法摸拟set功能),方法是在普通函数内书写get内的条件计算代码部分。
- 方法可以使用条件依赖与赖,但不能使用get依赖
- 方法被读取时没有缓存 每次读取都会进行运算。
- 方法读取时要的名称后加()小括号
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。