Vue3+vite+Ts+pinia—第四章 computed计算属性

1,272 阅读4分钟

4.1 computed概述

        计算属性是 Vue 实例的一个选项,可以用来定义一个属性,该属性的值是经过计算后的结果。它的特点是:

        1、依赖于其他的响应式属性

        2、会进行缓存,只有在依赖发生变化时,才会重新计算

        3、计算属性可以定义为一个函数,也可以定义为一个对象,包含 getter 和 setter

4.2 computed使用场景

        这里举一个例子,例如页面上需要显示一个人的姓名,它是由firstName和lastName两个字段组合而成,我们的第一反应肯定是firstName+lastName就完事了。如以下代码:

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{firstName}}-{{lastName}}</span> <br>
  </div>
</template>
<script lang="ts" setup name="Person">
  import {ref} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')
</script>  

image.png

        如果这里添加一个需求,firstName首字母需要大写,这里只有两种解决方法,要么就是给firstName变量赋值的时候先进行处理,要么直接在html模板里处理,其实两种方式都不太好。

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{firstName.slice(0,1).toUpperCase() + firstName.slice(1)}}-{{ lastName }}</span> <br>
  </div>
</template>

        这样虽然实现了需求,但是违背了一个原则,Vue官方文档标明,尽可能让模板简化,也就是尽量不要涉及业务代码。

        计算属性针对这些情况是非常友好的,我们可以使用一个计算属性fullName,将firstName与lastName整合,然后让模板读取,也可以把业务需求在计算属性里一并处理。

4.3 Vue2与Vue3语法对比

        1、Vue2

            在Vue2中,computed是一个对象,里面包含各个计算属性,它们都是一个函数。

computed: {
  fullName() {
    return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value
  }
}

        2、Vue3

            在Vue3里使用computed要先import引入,而且computed里面传入一个回调函数。

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
  </div>
</template>
<script lang="ts" setup>
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  let fullName = computed(() => {
    return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value
  })
</script>  

image.png

4.4 computed缓存特性

        computed属性无论使用多少次,它只会计算一次,只要它依赖的变量没有发生变化,它就不会重新计算。

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    全名:<span>{{fullName}}</span> <br>
    全名:<span>{{fullName}}</span> <br>
    全名:<span>{{fullName}}</span> <br>
    全名:<span>{{fullName}}</span> <br>
    全名:<span>{{fullName}}</span> <br>
    全名:<span>{{fullName}}</span> <br>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // fullName是一个计算属性,且是只读的
  let fullName = computed(()=>{
    console.log('执行computed计算')
    return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value
  })
</script>  

image.png

            如果不使用computed计算属性,使用的是一个函数来计算返回,是没有缓存的,每使用一次就计算一次:

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName2()}}</span> <br>
    全名:<span>{{fullName2()}}</span> <br>
    全名:<span>{{fullName2()}}</span> <br>
    全名:<span>{{fullName2()}}</span> <br>
    全名:<span>{{fullName2()}}</span> <br>
    全名:<span>{{fullName2()}}</span> <br>
    全名:<span>{{fullName2()}}</span> <br>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  function fullName2() {
    console.log('执行函数计算')
    return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value
  }
</script>  

image.png

4.5 computed的get与set

        computed计算属性如果传入回调函数,那么它是一个只读属性,不能修改。我们可以做一个尝试,写一个方法直接给fullName进行赋值:

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    <button @click="changeFullName">将全名改为li-si</button> <br>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // fullName是一个计算属性,且是只读的
  let fullName = computed(()=>{
    return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value
  })

  function changeFullName(){
    fullName.value = 'li-si'
  }
</script>

image.png

        其实在vscode的代码编辑器已经有非常友好的提示:你不能给它赋值,因为它是一个只读属性。在浏览器控制台也一样会提示:这个计算属性的value值是一个只读属性。

image.png

        如果我们想定义一个可读可写的computed属性,那就不要传回调函数,而是传一个对象,里面包含get和set两个方法:

        1、get方法:get方法比较简单,它其实就是显示在模板的内容,也就是我们刚才写回调函数return的内容。

        2、set方法:

            set方法在你修改这个计算属性的时候它就会执行,它会把最新的值接收回来。我们知道,想让computed重新计算,实际上是让它所依赖的变量发生改变,那么我们只需要在set方法里把它依赖的变量重新赋值,即可重新计算最新值。

            例如刚才的例子,我们在set方法拿到最新值之后,将fullName所依赖的firstName和lastName进行赋值,就可以更新fullName属性了。

<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    <button @click="changeFullName">将全名改为li-si</button> <br>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // fullName是一个计算属性,可读可写
  let fullName = computed({
    // 当fullName被读取时,get调用
    get(){
      return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value
    },
    // 当fullName被修改时,set调用,且会收到修改的值
    set(val){
      const [str1,str2] = val.split('-')
      firstName.value = str1
      lastName.value = str2
    }
  })

  function changeFullName(){
    fullName.value = 'li-si'
  }
</script>

0c3e4d5b-bdac-45a0-a105-cd20fc535051.gif

4.6 代码示例