• Vue学习之计算属性


    模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象:

    const author = reactive({
      name: 'John Doe',
      books: [
        'Vue 2 - Advanced Guide',
        'Vue 3 - Basic Guide',
        'Vue 4 - The Mystery'
      ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    我们想根据 author 是否已有一些书籍来展示不同的信息:

    <p>Has published books:</p>
    <span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
    
    • 1
    • 2

    这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 author.books。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。

    因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

    vue

    <script setup>
    import { reactive, computed } from 'vue'
    //reactive用于对象或者数组,ref常用于字符串
    const author = reactive({
      name: 'John Doe',
      books: [
        'Vue 2 - Advanced Guide',
        'Vue 3 - Basic Guide',
        'Vue 4 - The Mystery'
      ]
    })
    
    // 一个计算属性 ref
    const publishedBooksMessage = computed(() => {
      return author.books.length > 0 ? 'Yes' : 'No'
    })
    </script>
    
    <template>
      <p>Has published books:</p>
      <span>{{ publishedBooksMessage }}</span>
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    我们在这里定义了一个计算属性 publishedBooksMessage。computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value。

    Vue 的计算属性会自动追踪响应式依赖。它会检测到 publishedBooksMessage 依赖于 author.books,所以当 author.books 改变时,任何依赖于 publishedBooksMessage 的绑定都会同时更新。

    也可参考:为计算属性标注类型

    计算属性缓存 vs 方法 ​

    你可能注意到我们在表达式中像这样调用一个函数也会获得和计算属性相同的结果:

    template
    <p>{{ calculateBooksMessage() }}</p>
    js
    // 组件中
    function calculateBooksMessage() {
      return author.books.length > 0 ? 'Yes' : 'No'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果,而不用重复执行 getter 函数。

    这也解释了为什么下面的计算属性永远不会更新,因为 Date.now() 并不是一个响应式依赖:

    js

    const now = computed(() => Date.now())
    
    • 1

    相比之下,方法调用总是会在重渲染发生时再次执行函数。

    为什么需要缓存呢?想象一下我们有一个非常耗性能的计算属性 list,需要循环一个巨大的数组并做许多计算逻辑,并且可能也有其他计算属性依赖于 list。没有缓存的话,我们会重复执行非常多次 list 的 getter,然而这实际上没有必要!如果你确定不需要缓存,那么也可以使用方法调用。

    可写计算属性 ​

    计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:

    <script setup>
    import { ref, computed } from 'vue'
    
    const firstName = ref('John')
    const lastName = ref('Doe')
    
    const fullName = computed({
      // getter
      get() {
        return firstName.value + ' ' + lastName.value
      },
      // setter
      set(newValue) {
        // 注意:我们这里使用的是解构赋值语法
        [firstName.value, lastName.value] = newValue.split(' ')
      }
    })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    现在当你再运行 fullName.value = ‘John Doe’ 时,setter 会被调用而 firstName 和 lastName 会随之更新。

    最佳实践 ​

    Getter 不应有副作用 ​

    计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要改变其他状态、在 getter 中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此 getter 的职责应该仅为计算和返回该值。在之后的指引中我们会讨论如何使用侦听器根据其他响应式状态的变更来创建副作用。

    避免直接修改计算属性值 ​

    从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算

    以下是几个升级的实例:
    (1)根据输入出版书籍数量筛选作者
    (2)根据输入姓名拆分姓和名分别显示
    (3)根据输入的身份证号动态获取出生日期

    <script setup>
    import { reactive,ref, computed } from 'vue'
    
    const author = reactive([{
      name: 'John Doe',
      books: [
        'Vue 2 - Advanced Guide',
        'Vue 3 - Basic Guide',
        'Vue 4 - The Mystery'
      ]
    },
    {
      name: 'Mike',
      books: [
       'Vue 2 - Advanced Guide',
      ]
    },
    {
      name: 'Mike2',
      books: [
        'Vue 2 - Advanced Guide',
        'Vue 3 - Basic Guide',
        'Vue 4 - The Mystery'
      ]
    }])
    const firstName=ref('张')
    const lasnAME=ref("三")
    
    
    //根据数据值计算
    const publishedBooksMessage = computed(() => {
      const returnMsg=ref('')
      for(var i=0;i<author.length;i++){
          returnMsg.value+=author[i].books.length==bookNumLimit.value?author[i].name+"、":''
      }
      returnMsg.value=returnMsg.value.substring(0,returnMsg.value.length-1)
      return returnMsg.value
    })
    //根据输入值获取姓和名
    const fullName1=computed({
      get(){
        return firstName.value+' '+lasnAME.value
      },
      set(newValue){
        if(newValue.length>=3){
            [firstName.value,lasnAME.value]=newValue.split(' ')
        }else{
          firstName.value=''
          lasnAME.value=''
        }
       
      }
    })
    const idCard=ref('')
    const year=ref('')
    const month=ref('')
    const day=ref('')
    //根据身份证号计算出生日期
    const birthDay=computed(() => {
      if(idCard.value.length==18){
         const  newValue=idCard.value.substring(6,14);
          year.value=newValue.substring(0,4)
          month.value=newValue.substring(4,6)
          day.value=newValue.substring(6,8)
      }else{
        return '';
      }
      return year.value+"/"+month.value+"/"+day.value
    
    })
    const selectValue=ref('')
    const bookNumLimit=ref(0)
    
    </script>
    
    <template>
      书籍数量:<input v-model="bookNumLimit"><p>已发书籍作者:</p>
      <span>{{ publishedBooksMessage }}</span><br/>
      输入姓名:<input  v-model="fullName1" val=""><br/>
      姓:<span>{{ firstName}}</span><br/>
      名:<span>{{ lasnAME}}</span>
    <br/>
    身份证号:<input  v-model="idCard" ><br/>
    出生年月:{{ birthDay}}
    </template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    实现效果:
    在这里插入图片描述
    在这里插入图片描述
    修改身份证号,出生年月随之修改
    在这里插入图片描述

  • 相关阅读:
    自我成长自学必备网站,终生学习平台
    大师傅敢死队风格
    Baumer工业相机堡盟工业相机如何通过NEOAPISDK实现双快门采集两张曝光时间非常短的图像(C++)
    LetCode之热题100.1——哈希(两数之和)
    【SLAM】视觉SLAM简介
    计算机竞赛 基于深度学习的人脸专注度检测计算系统 - opencv python cnn
    百度知道本地搭建环境无限制采集聚合【最新版】
    在Ubuntu上配置PPPoE服务:从安装到自动化启动的全指南
    nvide shortcuts table
    深入了解 Postman 中的变量
  • 原文地址:https://blog.csdn.net/qq_38527427/article/details/136261428