【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的指向也就不会乱套了
相关文章
|
3天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
4天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
4天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
3天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
3天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
|
JavaScript 测试技术 容器
Vue2+VueRouter2+webpack 构建项目
1). 安装Node环境和npm包管理工具 检测版本 node -v npm -v 图1.png 2). 安装vue-cli(vue脚手架) npm install -g vue-cli --registry=https://registry.
1048 0
|
4天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
5天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
5天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
10天前
|
JavaScript
Vue基础知识总结 4:vue组件化开发
Vue基础知识总结 4:vue组件化开发