vue2中的watch(侦听器)讲解,以及解决深度监听新值和旧值相同的两种方案(手写深拷贝和JSON.parse())。

news2024/12/27 13:33:05

目录

一:什么是watch?

二:watch的基础使用 

 1.最基本的使用

 2.简写形式

三:watch中的immediate和deep属性

 1.immediate属性

 2.deep属性

 3.解决深度监听新旧值相同的问题        

       1)使用序列化和反序列化。

        2)手写深拷贝算法


一:什么是watch?

        相信大家在开发项目中,有时候会遇到一些需求,是当一个数据改变之后进行一些操作,这个时候有些人会设置一个定时器,周期性的去循环访问,当发现数据发生了改变后执行操作。但是这种操作方式会导致系统资源的浪费,以及更新的不及时等。因此vue通过watch(侦听器)提供了一个更通用的方法来响应数据的变化,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

        核心原理就是对传入Vue options的watch对象里面的所有的属性,都添加一个自定义watcher,收集对应属性的依赖,然后当这个依赖项更新的时候,不仅通知它的渲染watcher(可能没有),也会通知当前的这个自定义watcher,从而叫到你定义的callback,完成函数内的逻辑。

二:watch的基础使用 

 1.最基本的使用

         下方代码是watch的一个基础使用,可以看到当第一次进入页面的时候,并没有触发,而点击按钮的时候,name的值发生了改变,被watch监听到,因此执行了下面的输出逻辑。

<template>
  <div class="home">
    <el-button @click="name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        name: '张三'
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      name: { //这里是要监听的变量名
        handler(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue)
          console.log('原来的值是:' + oldValue)
        }
      }
    }
  }
</script>
运行效果

 2.简写形式

        当只是如上所示,简单地进行监听的时候,为了方便,我们可以直接使用简写形式。

<template>
  <div class="home">
    <el-button @click="name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        name: '张三'
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      name(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
        console.log('改后的值是:' + newValue)
        console.log('原来的值是:' + oldValue)
      }
    }
  }
</script>

三:watch中的immediate和deep属性

 1.immediate属性

         在上述代码中,我们可以发现,当watch第一次绑定数据的时候并没有触发监听效果,这是watch中的一个特性,但同样的,我们可以使用immediate属性改变这种特性。

<template>
  <div class="home">
    <el-button @click="name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        name: '张三'
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      name: {
        handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue)
          console.log('原来的值是:' + oldValue)
        },
        immediate: true
      }
    }
  }
</script>
运行效果

 2.deep属性

         在使用watch的过程中,如果定义了一个对象,这时候我们会发现使用一般的watch监听,是无法监听对象内部的改变的,这时候我们需要使用depp属性进行一个深度监听。

        比如在以下代码中,因为watch所监听的是一个person对象,当点击按钮改变之后,会发现没有任何执行效果。也就是监听失败。

<template>
  <div class="home">
    <el-button @click="person.name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        person: {
          name:'张三'
        }
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      person: {
        handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue.name)
          console.log('原来的值是:' + oldValue.name)
        }
      }
    }
  }
</script>

         所以我们在上述代码中加入depp:true,形成下面的代码,这时候我们可以在控制台看到是能够成功执行的。

<template>
  <div class="home">
    <el-button @click="person.name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        person: {
          name:'张三'
        }
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      person: {
        handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue.name)
          console.log('原来的值是:' + oldValue.name)
        },
        deep:true//深度监听
      }
    }
  }
</script>
运行效果

 3.解决深度监听新旧值相同的问题        

        但是在上面的运行效果中,我们可以发现改变前和改编后的值都是一样的,这显然不符合我们的初衷,失去了oldValue的作用,当然我们可以把监听的字符串person变成’person.name‘,但是这样又不属于监听对象了,而是回到了最初的监听字符串。

        我查了一下vue的官方文档,对watch的解释如下

在变异 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变异之前值的副本。

        通过官方解释,我们有以下两种方式解决该问题:

       1)使用序列化和反序列化。

<template>
  <div class="home">
    <el-button @click="person.name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        person: {
          name:'张三'
        }
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      newPerson: {
        handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue.name)
          console.log('原来的值是:' + oldValue.name)
        },
        deep:true,//深度监听
      }
    },
    computed:{
    // 使用计算属性进行深拷贝
      newPerson(){
        return JSON.parse(JSON.stringify(this.person))
      }
    }
  }
</script>
运行效果

        这种方式优点是直接简单,对于程序员而言代码行数更少,但是缺点在于这种深拷贝方式容易带来性能的问题,并且会丢失原有的原型链,在如unidentified等特殊值时会报错,将NaN序列化成null等问题。所以通常而言,用第二种方法会更好一些

        2)手写深拷贝算法

<template>
  <div class="home">
    <el-button @click="person.name='李四'">改变</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        person: {
          name: '张三'
        }
      }
    },
    //使用watch监听器,当不点击按钮的时候不会触发
    watch: {
      newPerson: {
        handler(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue.name)
          console.log('原来的值是:' + oldValue.name)
        },
        deep: true, //深度监听
      }
    },
    computed: {
      // 使用计算属性进行深拷贝
      newPerson() {
        // 使用自己手写的深克隆方法,效率更高
        return this.deepClone(this.person)
      }
    },
    methods: {
      //下面这个是手写深拷贝
      deepClone(source) {
        let target = null;
        if (!this.isArray(source) && !this.isObject(source)) {
          target = source;
        }
        if (this.isArray(source)) {
          target = [];
          for (let i = 0; i < source.length; i++) {
            target[i] = this.deepClone(source[i]);
          }
        }
        if (this.isObject(source)) {
          target = {};
          for (let key in source) {
            target[key] = this.deepClone(source[key]);
          }
        }
        return target;
      },

      isArray(value) {
        return Object.prototype.toString.call(value) === '[object Array]';
      },

      isObject(value) {
        return Object.prototype.toString.call(value) === '[object Object]';
      }
    }
  }
</script>

         这里运行效果和方案一的效果是一样的,所以不再附加运行截图。但是运行时间上可以看到,将点击事件的代码修改成方法,并且使用console.time()来计算时间,如下代码所示

<template>
  <div class="home">
    <!-- 这里的@click直接变成一个方法 -->
    <el-button @click="handleClick">改变</el-button>
  </div>
</template>


<!-- script修改部分如下 -->

<script>
  export default {
    ...
    watch: {
      newPerson: {
        handler(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
          console.log('改后的值是:' + newValue.name)
          console.log('原来的值是:' + oldValue.name)
          console.timeEnd('process')// 《---新增代码(关闭计时器)
        },
        deep: true, //深度监听
      }
    },

    ...

    },
    methods: {
      handleClick(){// 《---新增代码
        console.time('process')//打开计时器
        this.person.name='李四'
      },
      ....
    }
  }
</script>

添加上述代码后,再次执行两种方式的代码,执行时间如下

JSON.parse()的执行时间

手写深拷贝的执行时间

         通过上面两张图的对比,我们可以清楚地看到,使用JSON.parse()的执行时间是比手写深拷贝要久的,因此在大项目中,手写的执行效率往往更具有优势。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1415781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DevOps系列文章之 GitLabCI汇总

GitlabCI环境搭建 前提 先安装 docker Docker容器化安装 docker pull gitlab/gitlab-ee:12.4.0-ee.0 创建挂载目录 mkdir -p /srv/gitlab mkdir -p /srv/gitlab/config # 映射到 Glitlab 容器中的配置目录 mkdir -p /srv/gitlab/logs # 映射到 Glitlab 容器中的日志目录 m…

换个思维方式快速上手UML和 plantUML——类图

和大多数朋友一样&#xff0c;Jeffrey 在一开始的时候也十分的厌烦软件工程的一系列东西&#xff0c;对工程化工具十分厌恶&#xff0c;觉得它繁琐&#xff0c;需要记忆很多没有意思的东西。 但是之所以&#xff0c;肯定有是因为。对工程化工具的不理解和不认可主要是基于两个逻…

最新国内GPT4.0使用教程,AI绘画-Midjourney绘画V6 ALPHA绘画模型,GPT语音对话使用,DALL-E3文生图+思维导图一站式解决方案

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

围棋的气 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 围棋棋盘由纵横各19条线垂直相交组成&#xff0c;棋盘上一共19x19361个交点&#xff0c;对弈双方一方执白棋&#xff0c;一方执黑棋&#xff0c;落子时只能将棋子…

智慧文旅:未来旅游业的数字化转型

随着科技的快速发展&#xff0c;数字化转型已经成为各行各业的必然趋势。旅游业作为全球经济的重要组成部分&#xff0c;也正经历着前所未有的变革。智慧文旅作为数字化转型的重要领域&#xff0c;正逐渐改变着旅游业的传统模式&#xff0c;为游客带来更加便捷、个性化的旅游体…

锐龙笔记本Windows 11休眠无法唤醒问题的解决(6800h, 7840H/Hs)

锐龙笔记本运行Windows 11时经常会遇到休眠后无法唤醒的问题&#xff0c;表现为休眠后 按键盘或鼠标无反应&#xff0c;只能长按电源开关关机后再开机。网上有很多说法&#xff0c;比如显卡问题或其它问题。但是本质 上这个是电源管理软硬件不兼容导致的&#xff0c;解决办法如…

2024 年 eBPF 和网络趋势预测

本文地址&#xff1a;2024 年 eBPF 和网络趋势预测 | 深入浅出 eBPF 1. eBPF 1.1 eBPF 将继续呈指数增长1.2 eBPF 应用市场1.3 eBPF 在手机中得到更广泛的应用1.4 eBPF 滥用带来的风险2. 可观测 2.1 最受欢迎的可观测性2.2 降低可观测性开销2.3 上下文感知的 Kubernetes 工作负…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-自定义帖子管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

力扣516. 最长回文子序列

动态规划 思路&#xff1a; 字符串最长回文子序列问题可以转换为原字符串 s 和逆串 s 的最长公共子序列长度问题&#xff0c;具体推断过程可以参考 力扣1312. 让字符串成为回文串的最少插入次数问题变成了求两个字符串最长公共子序列长度问题&#xff0c;具体思路可以参考 力扣…

每次请求sessionid变化【SpringBoot+Vue】

引言&#xff1a;花了一晚上的时间&#xff0c;终于把问题解决了&#xff0c;一开始后端做完后,用apifox所有接口测试都是可以的,但当前端跑起来后发现接收不到后端的数据。 当我写完前后端&#xff0c;主页面和获取当前页面信息接口后&#xff0c;配置了cros注解 CrossOrigin…

数据湖技术之发展现状篇

一. 大数据处理架构&#xff1a; 大数据处理架构的发展过程具体可以分为三个主要阶段&#xff1a;批处理架构、混合处理架构&#xff08;Lambda、Kappa架构&#xff09;、湖仓一体。首先是随着Hadoop生态相关技术的大量应用&#xff0c;批处理架构应运而生&#xff0c;借助离线…

QEMU源码全解析41 —— Machine(11)

接前一篇文章&#xff1a;QEMU源码全解析40 —— Machine&#xff08;10&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 时间过去了几…

MCU启动文件小解一下

GD32启动文件分析 启动文件的一些指令.s启动文件分析栈空间分配堆空间管理中断向量表定义堆空间定义Reset_Handler复位程序HardFault_Handler_main文件分析用户堆栈初始化 GD32启动文件主要做了以下工作&#xff1a; 初始化SP_initial_sp , PCReset_Handler指针&#xff0c;设置…

眼底增强型疾病感知蒸馏模型 FDDM:无需配对,fundus 指导 OCT 分类

眼底增强型疾病感知蒸馏模型 FDDM&#xff1a;fundus 指导 OCT 分类 核心思想设计思路训练和推理 效果总结子问题: 疾病特定特征的提取与蒸馏子问题: 类间关系的理解与建模 核心思想 论文&#xff1a;https://arxiv.org/pdf/2308.00291.pdf 代码&#xff1a;https://github.c…

C#调用SqlSugar操作达梦数据库报错“无效的表或视图名”

安装达梦数据库后&#xff0c;使用SqlSugar连接测试数据库并基于DBFirst方式创建数据库表对应的类&#xff0c;主要代码如下&#xff1a; SqlSugarClient db new SqlSugarClient(new ConnectionConfig(){DbType DbType.Dm,ConnectionString "Serverlocalhost; User Id…

Redis3-秒杀活动

秒杀 准备工作 我是参照下面这位大佬的i骄傲成下载的 csdn友情链接 Jmeter模拟多线程的压力测试工具 秒杀代码&#xff1a; package com.aaa.controller;import io.netty.util.internal.StringUtil; import org.apache.commons.lang.StringUtils; import org.springfram…

YoloV8改进策略:BackBone改进|DCNv4最新实践|高效涨点|多种改进教程|完整论文翻译

摘要 涨点效果:在我自己的数据集上,mAP50 由0.986涨到了0.993,mAP50-95由0.737涨到0.77,涨点明显! DCNv4是可变形卷积的第四版,速度和v3相比有了大幅度的提升,但是环境搭建有一定的难度,对新手不太友好。如果在使用过程遇到编译的问题,请严格按照我写的环境配置。 …

【GitHub项目推荐--不错的 TypeScript 学习项目】【转载】

在线白板工具 Excalidraw 标星 33k&#xff0c;是一款非常轻量的在线白板工具&#xff0c;可以直接在浏览器打开&#xff0c;轻松绘制具有手绘风格的图形。 如下图所示&#xff0c;Excalidraw 支持最常用的图形元素&#xff1a;方框、圆、菱形、线&#xff0c;可以方便的使用…

51-17 视频理解串讲— MViT 论文精读

继TimeSformer模型之后&#xff0c;咱们再介绍两篇来自Facebook AI的论文&#xff0c;即Multiscale Vision Transformers以及改进版MViTv2: Improved Multiscale Vision Transformers for Classification and Detection。 由于本司大模型组最近组织阅读的论文较多&#xff0c;…

文献翻译 || Ubuntu安装zotero文献管理软件,提高文献阅读效率

文章目录 前言安装方式选择apt方式snap方式 zotero的简单使用文献导入中文翻译插件下载并安装使用体验 前言 虽然在win下有很多文献管理软件和好用的文献翻译软件&#xff0c;但是如果平常有使用Ubuntu进行开发的需求&#xff0c;实际上很不愿意为了好好看文献专门切到Windows…