五、【Vue3】——计算属性与监视(computed&watch&watchEffect)

570 阅读4分钟

本文已参与【新人创作礼】活动,一起开启掘金创作之旅。

引言:Vue3中的计算属性computed和监视watch与Vue2中的配置功能一致,但监视watch在用的时候却存在两个小坑,而且Vue3出现了watchEffect函数,能够更智能化去监视一个对象,下面与小编一起学习探讨一下吧。

一、计算属性computed

Vue3中的computed与Vue2中的功能一致,分简单写法(默认只能get)与完成写法(既能get也能set),大家可根据以下代码进行验证,此处不过多赘述。

<template>
    <h1>一个人的信息</h1>
    姓:<input type="text" v-model="person.firstName">
    名:<input type="text" v-model="person.lastName">
    <h2>全名:{{person.fullName}}</h2>
    <br/>
    全名<input type="text" v-model="person.fullName">
</template>
<script>
    import {computed, reactive} from 'vue'
    export default {
        name: 'Demo',
        setup(){
            let person = reactive({
                firstName:'张',
                lastName:'三'
            })
            // 计算属性-简单:只能获取不能修改
            // person.fullName = computed(()=> {
            // 	return person.firstName + '-' + person.lastName
            // })
            // 计算属性-完整:既能get也能set
            person.fullName = computed({
                get () {
                    return person.firstName + '-' + person.lastName
                },
                set (value) {
                    const nameArr = value.split('-')
                    person.firstName = nameArr[0]
                    person.lastName = nameArr[1]
                }
            })
            return {
                person
            }
        }
    }
</script>

二、监视watch

1、先简单回顾一下Vue2的watch写法

Vue2中的watch为对象类型,每个属性可做监视,同时immediate、deep可对监视状态进行调整;

watch: {
    firstName : {
      immediate:true, // 在刷新页面时先执行一次监视
      deep:true, // 深层监听,为了发现对象内部值的变化,此处不考虑是否对象,只做展示
      handler (val) {
      this.fullName = val + ' ' + this.lastName
      }
    },
}

2、重点说下Vue3的watch用法

语法:watch(source,callback,object)

  • source:监视的属性。当监视属性为基础数据类型时,为String;当监视属性为对象中的某个属性时,为Function;当同时监视多个属性时,可使用数组形式。

  • callback:监视的回调。回调函数中有两个参数,newValue和oldValue。

  • object:最后参数为对象类型,里边可以传immediate、deep。

  1. 通过一个简单案例看一下watch最简单用法
<template>
    <h2>当前求和为:{{sum}}</h2>
    <button @click="sum++">点我+1</button>
    <hr>
    <h2>当前的信息为:{{msg}}</h2>
    <button @click="msg+='!'">修改信息</button>
    <hr>
    <h2>姓名:{{person.name}}</h2>
    <h2>年龄:{{person.age}}</h2>
    <h2>薪资:{{person.job.j1.salary}}K</h2>
    <button @click="person.name+='~'">修改姓名</button>
    <button @click="person.age++">增长年龄</button>
    <button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
    import {watch, reactive, ref} from 'vue'
    export default {
        name: 'Demo',
        setup(){
            let sum = ref(0)
            let msg = ref('你好啊')
            let person = reactive({
                name:'张三',
                age:18,
                job:{
                    j1:{
                        salary:20
                    }
                }
            })
            // 情况一:监视ref所定义的一个响应式数据
            watch(sum,(newValue,oldValue) => {
                console.log(newValue,oldValue)
            },{immediate:true})
            return {
                sum,
                msg,
                person
            }
        }
    }
</script>

因为开启了immediate,所以一进页面先监听一次,往后没点一次按钮数字加1,请看效果图:

image.png

image.png

  1. 情况二:监视ref所定义的多个响应式数据.此时属性放在一个数组中
watch([sum,msg],(newValue,oldValue) => {
    console.log('sum或msg变了',newValue,oldValue)
})

先点【点我+1】按钮,对应回调参数返回的数组第一个数据变化,又点【修改信息】,随之第二个属性变化。请看效果图:

image.png image.png 3. 情况三:监视reactive所定义的一个响应式数据的全部属性

watch(person,(newValue,oldValue)=>{
        console.log('person变化了',newValue,oldValue)
},{deep:false}) //此处的deep配置无效
  • 注意1:此处无法正确的获取oldValue。无论是点击【修改姓名】【增长年龄】【涨薪】均获取不到正确的oldValue

  • 注意2:强制开启了深度监视(deep配置无效)。job值还是能被监视到

image.png 4. 情况四:监视reactive所定义的一个响应式数据中的某个属性

watch(() => person.name , (newValue,oldValue) => {
        console.log('person的name变化了',newValue,oldValue)
})

此时的oldValue可以正常获取到,观察是否名称增加了 ~,但oldValue没有变化

image.png 5. 情况五:监视reactive所定义的一个响应式数据中的某些属性

watch([()=>person.name,()=>person.age],(newValue,oldValue)=> {
        console.log('person的name或age变化了',newValue,oldValue)
})

监视对账中的多个属性时,用数组加方法形式进行传值,同时回调里的新旧值还是在一个数组里呈现出来,请看效果图:

image.png 6. 特殊情况:监视reactive所定义的一个响应式数据中的一个属性,且这个属性也是对象类型

// 为防止大家遗忘,将person数据源放在此处
let person = reactive({
    name:'张三',
    age:18,
    job:{
        j1:{
            salary:20
        }
    }
})
// 监视person中的job也是一个对象类型
watch(()=>person.job,(newValue,oldValue)=>{
    console.log('person的job变化了',newValue,oldValue)
},{deep:true})
  • 注意1:监视person中的job也是一个对象类型

  • 注意2:此处无法正确的获取oldValue

  • 注意3:此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效 请看效果图:

image.png 如上图展示,点击【涨薪】按钮后,newValue和oldValue值一样,oldValue无法获取之前的20

将deep改为false后,将监视不到job值变化

watch(()=>person.job,(newValue,oldValue)=>{
        console.log('person的job变化了',newValue,oldValue)
},{deep:false})

一直点【涨薪】按钮,数据变化没有检测到,控制台并没有打印出数据

image.png

三、watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调。

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

  • watchEffect有点像computed:

    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
    console.log('sum.value',sum.value)
    console.log('person.job.j1.salary',person.job.j1.salary)
    console.log('watchEffect所指定的回调执行了')
})

image.png