【Vue】深究计算和侦听属性的原理

简介: 【Vue】深究计算和侦听属性的原理

hello,我是小索奇,精心制作的Vue系列教程持续更新哈,涵盖大量的经验和示例,由浅入深进行讲解,想要学习&巩固&避坑就一起学习吧~

计算和侦听属性

计算属性

重点概要

定义:要用的属性不存在,需要通过已有属性计算得来

原理:底层借助了Objcet.defineproperty()方法提供的getter和setter来计算属性计算属性会自动找到getter并调用,拿到返回值放到VM身上

关于get函数

  • get初次读取时会执行一次
  • 当依赖的数据发生改变时会被再次调用
  • 与methods实现相比,内部有缓存机制,当我们再次调用get时,直接走缓存了,效率更高,调试时也直接显示computed

那么我们需要改值的时候呢?

  • 计算属性最终会出现在VM上,利用this进行读取使用即可
  • 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变
  • 如果计算属性确定不考虑修改,可以使用计算属性的简写形式

代码案例

<body>
    <div id="root">
        <input type="text" v-model="firstName"> <br/><br/>
        <input type="text" v-model="lastName" > <br/><br/>
        <h1>{{name}}</h1>
    </div>
   <script>
    Vue.config.productionTip = false
       const vm = new Vue({
            el:'#root',
            data:{
                firstName:'即兴',
                lastName:'小索奇'
            },
            computed:{
                name:{
                    get(){
       return this.firstName + '-' + this.lastName
                    set(value){
                        console.log(VM)
                        const arr = value.split('-')
                        this.firstName = arr[0]
                        this.lastName = arr[1]
                    }
                }
            }
        })
   </script>
</body>

image-20230819013138164

<body>
    <div id="root">
            <input type="text" v-model="firstName"> <br/><br/>
         <input type="text" v-model="lastName"> <br/><br/>
            <h1>{{name}}</h1>
    </div>
   <script>
    Vue.config.productionTip = false
       const vm = new Vue({
            el:'#root',
            data:{
                firstName:'即兴',
                lastName:'小索奇'
            },
            computed:{
                name:{
                    get(){
                        return this.firstName + '-' + this.lastName
                    },
                    set(value){
                        // console.log(VM)
                        const arr = value.split('-')
                        this.firstName = arr[0]
                        this.lastName = arr [1]
                    }
                }
            }
        })
   </script>
</body>

计算属性简写

  • 在实际应用过程中计算属性一般是仅读取展示,不修改的
  • 当你确定只读不更改,那么才可以用简写函数

执行流程:执行完name函数,往VM身上放name这个属性,它的值是函数返回的结果

 computed:{
          name(){
             console.log('简写形式,等同于get ')
             return this.firstName + '-' + this.lastName
           }, 
         }

调用时用name即可不用加括号name()-> x

拓展

还可以方法调用,为什么不用呢?看下例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script type="text/JS" src="./js/vue.js"></script>
</head>
<body>
    <div id="root">
        <input type="text" v-bind:value="firstName"> <br><br>
        <input type="text" :value="lastName" ><br><br>
        <button> 
            <h1>{{name()}}</h1>
        </button>
    </div>
   <script>
    Vue.config.productionTip = false
        new Vue({
            el:'#root',
            data:{
                firstName:'即兴',
                lastName:'小索奇'
            },
            methods:{
                name(){
                    return this.firstName + this.lastName
                }
            }
        })
   </script>
</body>
</html>

为什么不用?

因为这样更新频率极高,每一次更改值都会重新调用方法解析模板;效率低下

侦听属性

  • 侦听属性称为监视属性也是一样的

Vue.js中,侦听属性通常是通过"监视器"(Watchers)来实现的监视器允许你在Vue实例中监听一个特定的数据属性,然后在属性值发生变化时执行相应的逻辑这可以用来在属性变化时执行异步操作、更新其他数据,或者触发其他行为

当被监视的属性变化时,回调函数自动调用

侦听属性的定义方式有两种:声明式和编程式下面详细解释这两种方式以及如何使用侦听属性:

1. 侦听属性:

watch选项来对一个或多个侦听属性每个侦听属性都对应一个属性名,以及一个处理函数,当这个属性发生变化时,处理函数会被触发

<body>
  <div id="root">
    <h2>今天天气很{{ info }}</h2>
    <button @click="changeWeather">切换天气</button>
  </div>
</body>
<script type="text/JS">
  Vue.config.productionTip = false;
  const vm = new Vue({
    el: '#root',
    data: {
      isHot: true,
    },
    computed: {
      info() {
        return this.isHot ? '炎热' : '凉爽';
      },
    },
    methods: {
      changeWeather() {
        this.isHot = !this.isHot;
      },
    },
  });
  watch:{
  isHot:{
   immediate:true, 
   handler(newValue,oldValue){
   console.log('isHot被修改了',newValue,oldValue)
    }
   }
  } 
</script>

2. 侦听属性:

还可以通过$watch方法在Vue实例中编程式地设置侦听属性 ,调用VM的方法$watch

  vm.$watch('isHot', {
  // 初始化立即执行handler
    immediate: true,
    // 同理,都能接受新旧值进行处理
    handler(newValue, oldValue) {
      console.log('isHot被修改了', newValue, oldValue);
    },
  });

无论是声明式还是编程式的侦听属性,你都可以在侦听属性的处理函数中执行任何你需要的操作,比如发起网络请求、更新其他数据属性、触发方法等

需要注意的是,尽管侦听属性是强大的工具,但过多的侦听属性可能会导致代码变得复杂且难以维护在一些情况下,你可以使用计算属性(Computed Properties)来取代侦听属性,以提高代码的可读性和性能

使用watch选项或$watch方法都可以设置侦听属性,从而在属性变化时触发处理函数

深度监视

简单概括就是,深度监视是指对一个对象的所有嵌套属性进行观察和监听

  1. 使用方法:在 watch 选项中,为要监视的对象设置 deep: true
  2. 工作原理:当设置为 deep: true 时,Vue 会递归地遍历对象的所有子属性,确保无论如何修改对象的任何嵌套属性,都会触发回调函数
  3. 注意事项:因为深度监视需要递归地监听对象的所有子属性,所以它可能会对性能产生影响,特别是当对象结构复杂、嵌套层次深时
  4. 使用场景:当你需要响应对象内部属性的变化,而不仅仅是对象本身的引用变化时,使用深度监视

一个小案例

   <body>
  <div id="root">
   <h2>今天天气很{{info}}</h2>
   <button @click="changeWeather">切换天气</button>
   <hr/>
   <h3>a的值是:{{numbers.a}}</h3>
   <button @click="numbers.a++">点我让a+1</button>
   <h3>b的值是:{{numbers.b}}</h3>
   <button @click="numbers.b++">点我让b+1</button>
   <button @click="numbers = {a:666,b:888}">改变numbers对象</button>
   {{numbers.c.d.e}}
  </div>
 </body>
 <script type="text/JS">
  Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示
  const vm = new Vue({
   el:'#root',
   data:{
    isHot:true,
    numbers:{
     a:1,
     b:1,
     c:{
      d:{
       e:100
      }
     }
    }
   },
   computed:{
    info(){
     return this.isHot ? '炎热' : '凉爽'
    }
   },
   methods: {
    changeWeather(){
     this.isHot = !this.isHot
    }
   },
   watch:{
    isHot:{
     handler(newValue,oldValue){
      console.log('isHot被修改了',newValue,oldValue)
     }
    },
    //监视多级结构中某个属性的变化
    /* 'numbers.a':{
     handler(){
      console.log('a被改变了')
     }
    } */
    //监视多级结构中所有属性的变化
    numbers:{
     deep:true,
     handler(){
      console.log('numbers改变了')
     }
    }
   }
  })
 </script>
</html>

注意:当彻底替换掉numbers的时候,是没有d的,打开调试页面,所以点击彻底替换之后d会报错

deep:true这一项不开启的话,当numbers中的项(如a、b改变)number不会变化,当我们开启的话,就代表监视多级结构中属性的变化

侦听属性简写

  • 当配置项中只有handler时才可以简写,要是需要其它配置的话(比如deep)则不能够简写(一般默认写这个即可)
watch: {
  isHot(newValue, oldValue) {
    console.log('isHot被修改了', newValue, oldValue)  
  }
}

使用VM方法调用

//正常写法
  /* vm.$watch('isHot',{
   immediate:true, //初始化时让handler调用一下
   deep:true,//深度监视
   handler(newValue,oldValue){
    console.log('isHot被修改了',newValue,oldValue)
   }
  }) */
  //简写
  /* vm.$watch('isHot',(newValue,oldValue)=>{
   console.log('isHot被修改了',newValue,oldValue,this)
  }) */

对比侦听属性 & 计算属性

计算属性主要适用于:

  • 需要对数据进行转换或格式化后再显示
  • 需要使用多个数据计算得出一个结果进行显示
  • 需要缓存结果,避免每次重复计算
  • 浮动面板或列表中需要对每个元素做重复计算

侦听属性主要适用于:

  • 需要在数据变化时触发副作用,比如调用接口、导航路由等(后期会提到)
  • 需要进行异步操作或开销较大的处理(比如使用定时器)
  • 需要参数化或复用监听逻辑
  • 当值的变化需要改变多个状态时

简单来说,如果单纯显示或格式化数据,使用计算属性可以简化代码并获得性能优势

如果数据变化需要触发复杂逻辑或副作用,监听属性会更合适

如果一个业务逻辑这两种都能实现的话,优先考虑计算属性

拓展

下列代码的this输出什么?

   watch:{
            setTimeout(()=>{
                this.name = this.firstName + '-' + this.lastName
            },1000)
        }

这里的this指向Vue实例

Q:如果把setTimeOut剪头函数改为普通函数即:setTimeout(function(){}),那么this指向是哪里

A:指向window!如果定时器是JS引擎调用的,箭头函数没有自己的this指向,那么就会往外找,从而找到VM

具体来说普通函数定义的方法,它的 this 会绑定当前 Vue 实例

tips

  • computed能完成的功能,watch都可以完成
  • watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作,上面定时器例子就阐明了这一点

两个重要的小原则:

  • 被Vue管理的函数,最好写成普通函数,这样this的指向才是VM 或 组件实例对象,可以更好的调用VM
  • 不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,this的指向也就不会乱套了
相关文章
|
25天前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
1天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
25天前
|
JavaScript 前端开发 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
27天前
|
JavaScript 前端开发 开发者
vue 数据驱动视图
总之,Vue 数据驱动视图是一种先进的理念和技术,它为前端开发带来了巨大的便利和优势。通过理解和应用这一特性,开发者能够构建出更加动态、高效、用户体验良好的前端应用。在不断发展的前端领域中,数据驱动视图将继续发挥重要作用,推动着应用界面的不断创新和进化。
|
28天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
32 1
vue学习第一章
|
28天前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
26 1
vue学习第三章
|
28天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
35 1
vue学习第四章
|
28天前
|
JavaScript 前端开发 算法
vue学习第7章(循环)
欢迎来到瑞雨溪的博客,一名热爱JavaScript和Vue的大一学生。本文介绍了Vue中的v-for指令,包括遍历数组和对象、使用key以及数组的响应式方法等内容,并附有综合练习实例。关注我,将持续更新更多优质文章!🎉🎉🎉
24 1
vue学习第7章(循环)
|
28天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
29 1
vue学习第九章(v-model)
|
28天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
39 1
vue学习第十章(组件开发)