[vue2/vue3] 详细剖析watch、computed、watchEffect的区别,原理解读

news2024/11/17 5:15:53

前言:哈喽,大家好,我是前端菜鸟的自我修养!今天给大家分享【深入剖析watch、computed、watchEffect的区别】,并提供具体代码帮助大家深入理解,彻底掌握!原创不易,如果能帮助到带大家,欢迎 收藏+关注 哦 💕

🌈🌈文章目录

一、watch

1.使用语法

2.watch的实现原理 

二、computed

1.简单使用

2.computed的实现原理

三、watchEffect

1.简单使用

2.小结

3.watchEffect的实现原理

4.注意事项

1. 避免过度监听

2. 异步操作需谨慎处理

3. 避免无限循环

四、实际开发当中该怎么去选择

 1.watch

1.1 异步操作触发

1.2 深度监听

2.computed

2.1 派生数据 

2.2 性能优化

3.watchEffect

3.1 自动依赖追踪

3.2 动态数据处理

五、总结

在Vue中,数据响应式是一个核心概念,它使得当数据变化时,相关的视图会自动更新。为了更灵活地处理数据的变化,Vue提供了多种方式,其中包括watch、computed和watchEffect

一句话概括watch ​适用于需要有条件地监听数据变化的场景,computed​ 适用于创建派生数据和性能优化,而watchEffect 适用于自动追踪依赖的场景。在实际应用中,根据具体需求选择合适的API可以更好地发挥Vue的响应式能力。

一、watch

1.使用语法

watch是Vue中一个非常强大的特性,它允许你监听数据的变化并做出相应的反应。它有两种用法:一是监听一个具体的数据变化,二是监听多个数据的变化。

// 监听单个数据
watch('someData', (newVal, oldVal) => {
  // 做一些事情
});

// 监听多个数据
watch(['data1', 'data2'], ([newVal1, newVal2], [oldVal1, oldVal2]) => {
  // 做一些事情
});

2.watch的实现原理 

Vue中watch的实现主要依赖于Watcher这个核心类。当调用watch时,实际上是创建了一个Watcher实例,并将回调函数和需要监听的数据传递给这个实例。

// 简化版的watch实现
function watch(source, cb) {
  const watcher = new Watcher(source, cb);
}

Watcher类的构造函数接收两个参数,分别是需要监听的数据(可以是一个字符串,也可以是一个返回值的函数)回调函数。在构造函数中,会对数据进行求值,然后将这个Watcher实例添加到数据的依赖列表中。

class Watcher {
  constructor(source, cb) {
    this.getter = typeof source === 'function' ? source : () => this.vm[source];
    this.cb = cb;
    this.value = this.get();
  }

  get() {
    pushTarget(this); // 将当前Watcher实例压栈
    const value = this.getter.call(this.vm); // 触发数据的getter,将当前Watcher实例添加到依赖列表中
    popTarget(); // 将当前Watcher实例出栈
    return value;
  }

  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.cb(this.value, oldValue);
  }
}

 简单来说,watch的实现原理就是通过Watcher实例来监听数据的变化,当数据变化时,触发update方法执行回调函数

二、computed

1.简单使用

computed用于计算派生数据。它依赖于其他响应式数据,并且只有在相关数据发生变化时才会重新计算

computed(() => {
  return someData * 2;
});

2.computed的实现原理

computed的实现原理相对于watch更为复杂,它依赖于gettersetter的机制。在Vue中,computed的定义是一个包含get和set方法的对象

const computed = {
  get() {
    return someData * 2;
  },
  set(value) {
    someData = value / 2;
  }
};

在computed的实现中,当访问计算属性时,实际上是执行了get方法,而在数据变化时,会执行set方法。这里主要使用了Object.defineProperty这个JavaScript的特性。

function createComputedGetter() {
  return function computedGetter() {
    const value = getter.call(this); // 执行计算属性的get方法
    track(target, TrackOpTypes.GET, 'value'); // 添加依赖
    return value;
  };
}

function createComputedSetter() {
  return function computedSetter(newValue) {
    setter.call(this, newValue); // 执行计算属性的set方法
    trigger(target, TriggerOpTypes.SET, 'value'); // 触发更新
  };
}

function computed(getterOrOptions) {
  const getter = 
    typeof getterOrOptions === 'function'
      ? getterOrOptions
      : getterOrOptions.get;

  const setter = getterOrOptions.set;

  const cRef = new ComputedRefImpl(
    getter,
    setter,
    isFunction(getterOrOptions) || !getterOrOptions.get
  );

  return cRef;
}

class ComputedRefImpl {
  // 构造函数
  constructor(getter, setter, isReadonly) {
    // ...
    this.effect = effect(getter, {
      lazy: true,
      scheduler: () => {
        if (!this._dirty) {
          this._dirty = true;
          triggerRef(this);
        }
      },
    });
  }
  // ...
}

在上述代码中,createComputedGetter和createComputedSetter用于创建计算属性的getter和setter。computed函数接收一个getter函数,并通过Object.defineProperty将getter和setter添加到计算属性的引用对象中。

当计算属性被访问时,会触发getter,此时会将当前计算属性添加到依赖列表中。当计算属性的依赖数据发生变化时,会触发setter,并通过triggerRef触发计算属性的更新。

三、watchEffect

1.简单使用

watchEffect()函数用于创建一个自动追踪依赖的响应式副作用。它会在初始化时会立即执行一次,并自动追踪在回调函数中使用的所有响应式数据,在这些数据发生变化时重新运行回调函数。

例如,在每当 todoId 的引用发生变化时使用侦听器来加载一个远程资源,如果用watch,是这么写:

<template>
  <div>Test</div>
</template>
 
<script setup>
  import { ref, watch } from 'vue'
  const todoId = ref(1)
  const data = ref(null)
 
  watch(
    todoId,
    async () => {
      const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
      data.value = await response.json()
      console.log(data.value)
    },
    { immediate: true }
  )
</script>

打印:

但是用了watchEffect()就可以简化为:

<template>
  <div>Test</div>
</template>
 
<script setup>
  import { ref, watchEffect } from 'vue'
  const todoId = ref(1)
  const data = ref(null)
 
  watchEffect(async () => {
    const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)
    data.value = await response.json()
    console.log(data.value)
  })
</script>

打印:

2.小结

两者都立即打印了data。但下面的例子中,回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect(),我们不再需要明确传递 todoId 作为源值。

从这个角度来看,watchEffect()的作用类似于Vue2中的computed,即依赖项发生变化,自己也跟着改变。但与computed不同,watchEffect()没有返回值,而是直接执行回调函数

3.watchEffect的实现原理

watchEffect是Vue3中引入的响应式API,它用于执行一个响应式函数,并在函数中响应式地追踪其依赖。与watch不同,watchEffect不需要显式地指定依赖,它会自动追踪函数内部的响应式数据,并在这些数据变化时触发函数重新执行

首先,watchEffect的核心是依赖追踪和触发。Vue3中的响应式系统使用ReactiveEffect类来表示一个响应式的函数。

class ReactiveEffect {
  constructor(fn, scheduler = null) {
    // ...
    this.deps = [];
    this.scheduler = scheduler;
  }

  run() {
    // 执行响应式函数
    this.active && this.getter();
  }

  stop() {
    // 停止追踪
    cleanupEffect(this);
  }
}

export function watchEffect(effect, options = {}) {
  // 创建ReactiveEffect实例
  const runner = effect;
  const job = () => {
    if (!runner.active) {
      return;
    }
    if (cleanup) {
      cleanup();
    }
    // 执行响应式函数
    return runner.run();
  };
  // 执行响应式函数
  job();
  // 返回停止函数
  return () => {
    stop(runner);
  };
}

在上述代码中,ReactiveEffect类表示一个响应式的函数。watchEffect函数接收一个响应式函数,并创建一个ReactiveEffect实例。在执行时,该实例会追踪函数内部的响应式数据,并在这些数据变化时触发函数重新执行。

watchEffect返回一个停止函数,用于停止对响应式数据的追踪

4.注意事项

1. 避免过度监听

由于watchEffect()会追踪使用到的所有响应式数据,因此要确保在回调函数中只使用必要的响应式数据,避免造成不必要的渲染开销。

2. 异步操作需谨慎处理

由于watchEffect()会立即执行回调函数,如果在回调函数中进行异步操作,需要谨慎处理,以免导致意外行为或副作用。

<script setup>
import { watchEffect } from 'vue'
 
// 它会自动停止
watchEffect(() => {})
 
// ...这个则不会!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>
3. 避免无限循环

当在watchEffect()的回调中修改响应式数据时,可能会导致无限循环。要避免此问题,可以使用watch()函数并设置 immediate: true选项,或者使用ref来存储临时数据。

四、实际开发当中该怎么去选择

 1.watch

watch主要用于监听特定的数据变化并执行回调函数。它可以监听数据的变化,并在满足一定条件时执行相应的操作。常见的使用场景包括:

1.1 异步操作触发

当某个数据发生变化后,需要进行异步操作,比如发起一个网络请求或执行一段耗时的操作。

watch(() => state.data, async (newData, oldData) => {
  // 异步操作
  await fetchData(newData);
});

1.2 深度监听

监听对象或数组的变化,并在深层次的数据变化时执行回调

watch(() => state.user.address.city, (newCity, oldCity) => {
  console.log(`City changed from ${oldCity} to ${newCity}`);
});

2.computed

computed用于创建一个计算属性,它依赖于其他响应式数据,并且只有在依赖数据发生变化时才重新计算。常见的使用场景包括:

2.1 派生数据 

根据现有的数据计算出一些派生的数据,而不必每次都重新计算

const fullName = computed(() => `${state.firstName} ${state.lastName}`);

2.2 性能优化

避免不必要的重复计算,提高性能。

const result = computed(() => {
  // 避免重复计算
  if (someCondition) {
    return heavyCalculation();
  } else {
    return defaultResult;
  }
});

3.watchEffect

watchEffect用于执行一个响应式函数,并在函数内部自动追踪依赖。它适用于不需要显式指定依赖,而是在函数内部自动追踪所有响应式数据变化的场景。常见的使用场景包括:

3.1 自动依赖追踪

函数内部的所有响应式数据都被自动追踪,无需显式指定

watchEffect(() => {
  console.log(`Count changed to ${state.count}`);
});

3.2 动态数据处理

处理动态变化的数据,无需手动管理依赖

watchEffect(() => {
  // 处理动态变化的数据
  handleDynamicData();
})

五、总结

watch ​适用于需要有条件地监听数据变化的场景,computed​ 适用于创建派生数据和性能优化,而watchEffect 适用于自动追踪依赖的场景。在实际应用中,根据具体需求选择合适的API可以更好地发挥Vue的响应式能力。

  好了,本文就到这里吧,点个关注再走嘛~ 

🚀 个人简介:7年开发经验,某大型国企前端负责人,信息系统项目管理师、CSDN优质创作者、阿里云专家博主,分享前端相关技术与工作常见问题~
💟 作    者:前端菜鸟的自我修养❣️
📝 专    栏:vue从基础到起飞
🌈 若有帮助,还请 关注➕点赞➕收藏  ,不行的话我再努努力💪💪💪  

  更多专栏订阅推荐:

👍 前端工程搭建
💕 前端常见问题汇总,避坑大全

📝 javascript深入研究

✍️ GIS地图与大数据可视化

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

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

相关文章

云卓SKYDROID-H30——科技改变未来

云卓H30采用高通处理器、搭载安卓嵌入式系统&#xff0c;拥有三个工作频率&#xff0c;让图像更清晰、延迟更低、距离远、抗干扰性强&#xff0c;支持多种接口&#xff0c;更有10.1寸高清工业级阳光可视屏&#xff0c;防尘耐磨&#xff0c;结构强度高&#xff0c;适用于各种严苛…

python自动化之schedule

目录 代码&#xff08;以每5秒1次为例&#xff09;: 每5分钟1次 每2小时1次 每天18:00执行 用到的库&#xff1a;schedule&#xff0c;time 实现的效果&#xff1a;按秒来运行任务&#xff0c;按分钟来运行任务&#xff0c;按小时来运行任务&#xff0c;按天来运行任务 代…

<电力行业> - 《第1课:电力行业的五大四小》

1 什么是电力行业的五大四小&#xff1f; 我们常说的电力行业的五大四小&#xff0c;指的是电力行业有实力的公司&#xff0c;分为&#xff1a;较强梯队的五大集团、较弱梯队的四小豪门。 五个实力雄厚的集团&#xff0c;分别是&#xff1a; 中国华能集团公司中国大唐集团公…

基于K线最短路径构造的非流动性因子

下载地址https://download.csdn.net/download/SuiZuoZhuLiu/89492221

如何实现电子签名签章功能?

随着技术的发展&#xff0c;传统的纸质合同签署方式逐渐暴露出效率低下、存储不便和安全性不足等问题。为了解决这些问题&#xff0c;电子签署服务为用户提供了一个安全、高效、环保的合同管理解决方案。 电子合同管理与签署平台的核心功能 1、用户管理&#xff1a;平台提供用…

【在线评论】不同视角下在线评论对客户满意度和推荐度的影响—推文分析—2024-07-01

今天的推文主题是【在线评论】&#xff0c;重点关注可以关注第四篇&#xff0c;很全面地分析了在线评论的信息多维性。 第一篇从客户的在线评论入手&#xff0c;将客户消费的动机为功利、享受、社会满足&#xff1b;第二篇是关于在线评论对消费者再次选择同一家酒店的机制探索…

强化学习原理入门-1绪论

1 绪论 1.1 这是一本什么书 强化学习算法&#xff1f; AlphaGo大胜世界围棋冠军李世石和柯洁事件&#xff0c;核心算法就用到了强化学习算法。 1.2 强化学习解决什么问题 案例1 非线性系统二级倒立摆 案例2 AlphaGo与柯洁的第二局棋 案例3 机器人学习站立 ...... 智能…

【程序大侠传】异步架构应用回调数据接收接口偶发NPE

前序 在这片浩瀚的代码江湖中&#xff0c;各大门派林立&#xff0c;各自修炼独门绝技&#xff0c;江湖中的侠士们分别担任着开发、测试、产品和运维的角色&#xff0c;共同守护着这片数字化的疆域。 开发门派&#xff1a;代码剑宗 代码剑宗的弟子们精通各种编程语言&#xff…

新款奔驰GLE350升级原厂空气悬挂系统有哪些功能

奔驰 GLE350 升级原厂空气悬挂带来了一系列显著的优势和功能&#xff1a; 1. 舒适性提升 • 能够根据不同的路况和驾驶模式自动调节悬挂硬度和高度&#xff0c;有效过滤路面颠簸&#xff0c;为驾乘者提供更加平稳、舒适的行驶体验。 2. 行驶高度调节 • 驾驶者可以手动或自…

网络攻防题录集

文章目录 第一章 网络攻防概述第二章 密码学第三章 网络协议脆弱性分析第四 自测题三第五章 自测题五第六章 自测题六第七章 自测题七第八章 自测题八第九章 自测题九第十章 自测题十第十一章 自测题十一第十二章 自测题十二第十三章 自测题十三 第一章 网络攻防概述 第一代安…

文心一言 VS 讯飞星火 VS chatgpt (293)-- 算法导论21.4 2题

二、证明&#xff1a;每个结点的秩最多为 ⌊lgm⌋ 。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 在数据结构和算法中&#xff0c;当我们谈论“结点的秩”时&#xff0c;这通常与某些特定的数据结构&#xff08;如B树或B树&#xff09;相关&#xff0c;其中每…

vant ( weapp ) - - - - - van-tabs组件选中下划线初始位置异常

这里写自定义目录标题 1. 当前效果展示2. 官方解释 & 方案 1. 当前效果展示 明显可以看到框内的光标位置偏移了&#xff0c;但当切换一次之后就会显示正常。 只有初次打开的时候&#xff0c;才会出现上述问题。 代码如下&#xff1a; <van-popup show"{{ makeSho…

【超级简单】植物大战僵尸杂交版V2.1,手机上最简单的安装方法~!

大家好&#xff0c;我是坤坤黑科技&#xff01;之前给大家分享了植物大战僵尸杂交版手机的安装方法&#xff0c;但是很多朋友还是因为操作难度大所以没有玩到。今天发现一个更加简单的在手机上玩植物大战僵尸杂交版的方法&#xff0c;直接安装就可以玩到最新的2.1版本~ 植物大…

【python】最新版抖音s逆向拿到数据,非常详细教程(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

SaaS增长:小型SaaS企业可以使用推荐奖励计划吗

在SaaS&#xff08;软件即服务&#xff09;行业的激烈竞争中&#xff0c;如何快速有效地增长用户数量是每个企业都面临的挑战。对于小型SaaS企业来说&#xff0c;资源有限&#xff0c;如何最大化利用现有资源实现用户增长成为了一个重要议题。在这样的背景下&#xff0c;推荐奖…

解决 npm intasll 安装报错 Error: EPERM: operation not permitted

Node.js安装及环境配置完成之后 npm install express -g 安装全局的模块报错提示没有权限operation not permitted mkdir 错误编号4048&#xff1a; 其原因是当前用户操作该目录权限不足&#xff0c;当以管理员身份运行cmd&#xff0c;再执行npm install express -g 是不会报权…

JELR-G150F可调漏电继电器 面板安装 30~300mA 约瑟JOSEF

一、应用 JELR系列漏电继电器&#xff08;以下简称继电器&#xff09;适用于交流电压为660V.至1140V电压系统中,频率为50Hz,电流15~4000A线路中做有无中性点漏电保护. 该继电器可与带分励脱扣器或失压脱扣器的断路器、交流接触器、磁力启动器等组成漏电保护装置&#xff0c;作…

Vue3 登录成功,浏览器存在toke,再次访问/login路由到/index 首页页面

文章目录 目录 文章目录 流程 小结 概要流程技术细节小结 概要 首先需要清楚知道浏览器localstorage和Session storage的区别 localStorage 和 sessionStorage 是 HTML5 提供的两种客户端存储数据的方法&#xff0c;它们在使用和生命周期上有一些区别&#xff1a; 1. 生命周期…

视频共享交换平台LntonCVS视频融合共享平台搭建智慧园区视频监控平台应用方案

智慧园区作为现代化城市发展的重要组成部分&#xff0c;不仅承载着产业升级的使命&#xff0c;更是智慧城市建设的重要体现。随着产业园区竞争的激烈化&#xff0c;越来越多的关注点集中在如何打造完善的智慧园区上。 在智慧园区规划的初期阶段&#xff0c;面临诸多挑战。如何选…