computed与watch的区别

如果一个值依赖多个属性(多对一),用computed肯定是更加方便的。 如果一个值变化后会引起一系列操作,或者一个值变化会引起一系列值的变化(一对多),用watch更加方便一些。 watch 支持异步代码而computed 不支持

1.计算属性 computed

特点:

  • 支持缓存,只有依赖数据发生改变,才会重新进行计算;
  • 不支持异步,当 computed 内有异步操作时无效,无法监听数据的变化;
  • computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的。也就是基 于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值;
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性 是一个多对一或者一对一,一般用computed;
  • 如果 computed 属性值是函数,那么默认会走 get 方法,函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个 set 方法,当数据变化时,调用 set 方法;

使用例子:

<template>
  <div>
    <span>{{testName}}</span>
    <el-input v-model="firstText"></el-input>
    <el-input v-model="lastText"></el-input>
    <el-input v-model="mergeText1"></el-input>
    <el-input v-model="mergeText2"></el-input>
    <div>{{fullNameFun()}}</div>
  </div>
</template>
<script>
import { defineComponent, computed, ref } from 'vue'
export default defineComponent({
  setup() {
    let firstText = ref('hello')
    let lastText = ref('world')
    const mergeText1 = computed(() => firstText.value + ' '  + lastText.value)
    const mergeText2 = computed({
      // getter
      get() {
        // 回调函数 当需要读取当前属性值时执行,根据相关数据计算并返回当前属性的值
        return `${firstText.value} ${lastText.value}`
      },
      // setter
      set(val) {
        //监视当前属性值的变化,当属性值发生变化时执行,更新相关的属性数据,val就是fullName的最新属性值
        const names = val.split(' ')
        console.log(names)
        firstText.value = names[0]
        lastText.value = names[names.length - 1]
      },
    })
    
    function fullNameFun(){
      return firstText.value+ ' ' + lastText.value
    }
    
    return {
      firstText,
      lastText,
      mergeText1,
      mergeText2,
      fullNameFun
    }
  }
})
</script>

优点:

  • 当改变 ref 或者 reactive 响应式变量值时,整个应用会重新渲染,vue 会被数据重新渲染到 dom 中。这时,如果我们模板中使用了 methods 中的fullNameFun函数,或者使用了组合式return的函数。随着渲染,方法也会被调用。但是 如果computed中所依赖的变量没有发生改变,则不会进行重新的计算,从而性能开销比较小。当新的值需要大量计算才能得到,缓存的意义就非常大;
  • 如果 computed 所依赖的数据发生改变时,计算属性才会重新计算,并进行缓存;当改变其他数据时,computed 属性 并不会重新计算,从而提升性能;
  • 当拿到的值需要进行一定处理使用时,就可以使用 computed;

2. 侦听属性 watch (vue3中还有watcheffect详细讲解,在另外一篇文章中传送门

特点:

  • 完全等同于vue2 中的watch
  • 不支持缓存,数据变化,直接会触发相应的操作;
  • watch 支持异步操作;
  • 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
    当一个属性发生变化时,需要执行对应的操作,一对多;
  • 监听数据必须是 data 中声明过或者组合式api中声明的响应式值或者父组件传递过来的 props 中的数据。当数据变化时触发其他操作,函数有两个参数:
  • immediate:组件加载立即触发回调函数执行;
  • deep: 深度监听;为了发现对象内部值的变化,复杂类型的数据时使用,例如:数组中的对象内容的改变,注意:监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到;

注:当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的,这是和 computed 最大的区别。

2.1 一般用法,监听单个变量或多个数据源或者一个函数返回值

注:监听一个函数的返回值,当函数里面中所用到的变量发生变化都会触发回调

// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)
// 直接侦听一个 reactive
const count = ref(0)
watch(state , (newValue, oldValue) => {
  /* ... */
})

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

// 监听多个数据源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

// watch 可以监听一个函数的返回值
watch(() => {
   return otherName.firstName + otherName.lastName
},
   value => {
   // 当otherName中的 firstName或者lastName发生变化时,都会进入这个函数
   console.log(`我叫${value}`)
  }
)

2.2 监听复杂数据(深度监听 deep)

不使用 deep 时,当我们改变 obj.a 的值时,watch 不能监听到数据变化,默认情况下,watch 只监听属性引用的变化,也就是只监听了一层,但改对象内部的属性是监听不到的。
immerdiate 属性: 通过声明 immediate 选项为 true,可以立即执行一次 watch里面的函数。

import { watch, ref, reactive } from 'vue'

export default {
  setup() {
   let obj= reactive({
        text:'hello'
    })
    watch(obj, (newValue, oldValue) => {
      // 回调函数
    }, {
      immediate: true,
      deep: true
    })
    return {
      obj
    }
  }
}

通过使用 deep: true 进行深入观察,我们监听 obj,会把 obj 下面的属性层层遍历,都加上监听事件,这样做性能开销也会变大,只要修改 obj 中任意属性值,都会触发回调,那么如何优化性能呢?
可以直接对用对象 . 属性的方法拿到属性,也就是上面说道的侦听一个 getter

import { watch, ref, reactive } from 'vue-router'

export default {
  setup() {
   let obj= reactive({
        text:'hello'
    })
    watch(()=>obj.text, (newValue, oldValue) => {
      // 回调函数
    }, {
      immediate: true,
      deep: true
    })
    return {
      obj
    }
  }
}

注意事项:

  • watch 中的函数名称必须是所依赖 data 中的属性名称;(vue2)
  • watch 中的函数是不需要调用的,只要函数所依赖的属性发生了改变 那么相对应的函数就会执行;
  • watch 中的函数会有2个参数 一个是新值,一个是旧值;
  • watch 默认情况下无法监听对象的改变,如果需要进行监听则需要进行深度监听 深度监听需要配置 handler(vue2中才需要) 函数以及 deep 为true。(因为它只会监听对象的地址是否发生了改变,而值是不会监听的);(vue2) 在vue3中有些许区别。在另外一篇文章中再说
  • watch 默认情况下第一次的时候不会去做监听,如果需要在第一次加载的时候也需要去做监听的话需要设置 immediate:true;

vue2中数组响应式原理:
1 重新定义原生数组方法push unshift shift pop splice sort reverse 因为这些方法可以修改原数组。
2 拿到原生数组方法 Object.create(Array.prototype)
3 AOP拦截,再执行重写数组方法前,先执行原生数组方法

watch 在特殊情况下是无法监听到数组的变化

所以,vue2中对数组的解决方案:

  • 通过下标来更改数组中的数据;
  • 通过 length 来改变数组的长度;

通过 Vue 实例方法 set 进行设置 $set( target, propertyName/index, value)
参数: target {Object | Array}propertyName/index {string | number}value {any}

this.$set(this.arr,0,100);

通过 splice 来数组清空 $delete( target, propertyName/index )
参数:target {Object | Array}propertyName/index {string | number}

this.$delete(this.arr,0)

vue2中深度监听对应的函数名必须为 handler ,否则无效果,因为 watche r里面对应的是对 handler 的调用

划重点:在vue3中利用的是ES6的proxy,对数据响应式进行一个数据的代理,可以监控到数组的变化。vue3中如果想要让一个对象变为响应式数据,可以使用reactiveref。因此$set在vue3中废弃

3. 方法 methods

methods 跟前面的都不一样,我们通常在这里写入方法,只要调用就会重新执行一次,相应的有一些触发条件,在某些时候 methodscomputed 看不出来具体的差别,但是一旦在运算量比较复杂的页面中,就会体现出不一样。
注意:computed 是具有缓存的,这就意味着只要计算属性的依赖没有进行相应的数据更新,那么 computed 会直接从缓存中获取值,多次访问都会返回之前的计算结果。

总结

在 computed 和 watch 方面,一个是计算,一个是观察,在语义上是有区别的。
计算是通过变量计算来得出数据,而观察是观察一个特定的值,根据被观察者的变动进行相应的变化,在特定的场景下不能相互混用,所以还是需要注意 api 运用的合理性和语义性。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容