一、计算属性(computed)

计算属性(Computed Properties)是 Vue 中一种特殊的响应式数据,它能基于已有的响应式数据动态计算出新的数据。

计算属性有以下特性:

  • 自动缓存:只有当它依赖的响应式数据发生变化时,才会重新计算。

  • 响应式更新:依赖的数据变化后,会自动触发计算属性重新计算。

  • 简化模板:在模板中使用计算属性可以减少复杂逻辑,让模板更清晰、易读。

简单来说:

计算属性是基于其他响应式数据而自动计算得到的值,且具有缓存和响应式的特性。

1、计算属性的基本用法

<script setup>
import { ref, computed } from 'vue'// 响应式数据
const firstName = ref('Tom')
const lastName = ref('Jerry')// 计算属性(根据响应式数据动态计算)
const fullName = computed(() => {return `${firstName.value} ${lastName.value}`
})
</script><template><div>{{ fullName }}</div> <!-- 显示:Tom Jerry -->
</template>

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

<script setup>
import { ref, computed } from 'vue'const firstName = ref('John')
const lastName = ref('Doe')const fullName = computed({// getterget() {return firstName.value + ' ' + lastName.value},// setterset(newValue) {// 注意:我们这里使用的是解构赋值语法[firstName.value, lastName.value] = newValue.split(' ')}
})
</script>

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

注意,computed()里面不接受任何的参数,我们看到里面有一个回调函数,这个回调函数本质上是getter函数

  • 之前版本(<3.4),这个 getter 函数没有参数。

  • 从 3.4 开始,这个 getter 函数可以接受一个参数:就是上一次计算属性的计算结果。

简单来说:

如果需要,可以通过访问计算属性的 getter 的第一个参数来获取计算属性返回的上一个值:

<script setup>
import { ref, computed } from 'vue'const count = ref(2)// 这个计算属性在 count 的值小于或等于 3 时,将返回 count 的值。
// 当 count 的值大于等于 4 时,将会返回满足我们条件的最后一个值
// 直到 count 的值再次小于或等于 3 为止。
const alwaysSmall = computed((previous) => {if (count.value <= 3) {return count.value}return previous
})
</script>

 如果你正在使用可写的计算属性的话:

<script setup>
import { ref, computed } from 'vue'const count = ref(2)const alwaysSmall = computed({get(previous) {if (count.value <= 3) {return count.value}return previous},set(newValue) {count.value = newValue * 2}
})
</script>

 2、计算属性与方法(methods)的详细区别

两者区别如下:

对比维度计算属性(computed)方法(methods)
缓存机制有缓存,仅数据变化才重新计算无缓存,每次调用都会执行
调用方式不需要括号调用,像属性一样使用需要括号调用,明确为函数
适用场景基于响应式数据的计算处理事件或显式调用的场景
性能开销性能较高(缓存优化)性能较低(频繁调用时)

计算属性与方法性能差异分析

假设模板多次渲染对比:

  • 计算属性

    <div>{{ doubleCount }}</div>
    <div>{{ doubleCount }}</div>
    <div>{{ doubleCount }}</div>
    
    • 只计算一次,缓存结果。

  • 方法调用

    <div>{{ getDoubleCount() }}</div>
    <div>{{ getDoubleCount() }}</div>
    <div>{{ getDoubleCount() }}</div>
    
    • 每次都调用一次,共调用3次,性能浪费。

因此,对于频繁使用但数据不频繁变化的场景,建议使用计算属性

3、计算属性什么时候不能用?

计算属性适用于:

  • 同步、快速的计算逻辑

  • 无副作用的计算(纯函数)。

计算属性不适合:

  • 异步逻辑(如请求数据)。

  • 执行副作用(修改其他数据、DOM 操作)。

 二、监听属性

在 Vue 中,监听属性(Watch) 是一种响应式机制,用于监测响应式数据的变化:

  • 当你想在数据发生变化时执行某些逻辑(如发送请求、更新数据或执行某些副作用)时,就可以使用监听属性。

  • 监听属性通过 Vue 提供的 watch()watchEffect() 函数实现。

简单来说:

监听属性让你能够对数据变化做出反应,执行一些副作用或异步操作。

1、监听属性的基本用法(watch)

<script setup>
import { ref, watch } from 'vue'const count = ref(0)// 监听 count 的变化
watch(count, (newValue, oldValue) => {console.log(`count变化了:从${oldValue}到${newValue}`)
})
</script><template><button @click="count++">增加 ({{ count }})</button>
</template>

count 的值改变时,watch 会自动触发,执行回调函数。

监听多个数据: 

const firstName = ref('Tom')
const lastName = ref('Jerry')// 同时监听 firstName 和 lastName
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {console.log(`名字变化了:${oldFirst} ${oldLast} => ${newFirst} ${newLast}`)
})

2、监听属性的参数与选项(高级用法)

🔹 监听属性的函数签名:watch(source, callback, options)

  • source: 需要监听的响应式数据,可以是单个或多个。可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:

  • callback: 数据变化时执行的回调函数。

  • options: 可选参数,控制监听器行为。

🔹 常用的监听选项(options):

选项含义默认值
immediate是否立即执行一次监听回调false
deep是否深度监听对象内部属性变化false
flush控制监听器回调的执行时机'pre'

 深度监听(deep)到底是什么?

  • 默认情况下,Vue 的监听器只能监听对象引用本身的变化(比如替换对象)。

  • 使用 deep: true 时,能监听对象或数组内部属性或元素的变化。

const user = ref({ name: 'Tom', age: 18 })// 默认浅监听(只能监听整个对象变化)
watch(user, () => {console.log('浅监听:user变化了')
})user.value.age = 19 // ❌不会触发浅监听(对象引用未变)
user.value = { name: 'Jerry', age: 19 } // ✔️触发// 深度监听(对象内部属性变化也会触发)
watch(user, () => {console.log('深监听:user变化了')
}, { deep: true })user.value.age = 20 // ✔️触发深度监听

 监听属性的执行时机(flush)

flush 控制监听回调的执行时机:

flush 值含义使用场景
pre默认值,组件更新之前执行大多数情况
post组件更新之后执行需要访问更新后的DOM时
sync同步触发,数据变化立即执行非常特殊情况

3、watch vs watchEffect 的区别

在 Vue 中,watch()watchEffect() 都用于响应式地执行一些副作用操作(如发起请求、改变 DOM),但二者的追踪数据依赖方式不同:

特性watch()watchEffect()
如何追踪依赖手动显式指定要监听的数据(明确)自动追踪回调中访问的数据(隐式)
首次执行默认不立即执行,需手动开启自动立即执行一次
控制粒度精确控制监听的数据项,控制更细自动追踪所有访问的响应式数据,更灵活
适用场景明确知道监听什么数据变化数据依赖较多或复杂,更希望自动追踪

 举个简单例子,监听单个明确的数据:

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()},{ immediate: true }
)

 特点:

  • todoId 被显式声明为监听的源。

  • 回调函数只在明确的源数据(todoId)改变时触发。

  • 必须用 { immediate: true } 来立即执行一次,否则首次不会执行。

1、 watchEffect() 如何简化上面的例子?

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()
})

特点:

  • 自动立即执行一次,无需手动指定 { immediate: true }

  • 无需手动指定监听源,回调函数内的所有响应式数据访问(这里是 todoId.value)会自动被 Vue 跟踪。

  • 一旦被跟踪的数据变化(例如:todoId.value 改变),回调会自动再次执行。

 2、watchEffect() 的依赖跟踪原理(关键):

watchEffect() 会自动追踪回调函数在同步执行时访问的所有响应式数据。
但有个重要提示:

如果回调是异步函数,那么只有在第一个 await 之前访问的数据才会被追踪!

watchEffect(async () => {console.log(todoId.value) // ✅ 被追踪,因为在 await 之前访问const response = await fetch('...')console.log(someOtherRef.value) // ❌ 不被追踪,因为在 await 之后访问
})

原因是:

  • Vue 只能追踪同步执行阶段访问的数据。

  • 在异步操作完成后的回调内访问的数据不会被 Vue 追踪。

3、watchEffect() 在实际场景中的优势:

优势一:自动跟踪多个数据源(不必手动指定):

假如你有多个响应式数据:

const firstName = ref('Tom')
const lastName = ref('Jerry')watchEffect(() => {console.log(`Name: ${firstName.value} ${lastName.value}`)
})Vue 自动监控 firstName 和 lastName。无论哪个改变,都会触发回调函数。使用 watch() 则必须手动指定数据源:
watch([firstName, lastName], () => {console.log(`Name: ${firstName.value} ${lastName.value}`)
})
优势二:更精细地跟踪对象属性(比深监听高效):

假如你有复杂对象:

const user = ref({name: 'Tom',age: 20,address: { city: 'Shanghai', street: 'Main St' }
})watchEffect(() => {console.log(`User city: ${user.value.address.city}`)
})watchEffect() 只监听了对象的部分属性 (address.city),高效、精准。如果用深监听 (watch(user, ..., { deep: true })),会监听所有属性的变化,性能可能较差。

4、什么时候用 watch()?什么时候用 watchEffect()

场景推荐方式理由
明确知道监听的数据源✅ 使用 watch()明确指定,粒度精准
多个数据源或依赖复杂✅ 使用 watchEffect()自动跟踪,代码更简洁、更灵活
动态数据请求或复杂副作用watchEffect()自动监听,省去手动指定烦恼

4、监听属性的常见使用场景

场景示例
数据变化请求API表单值变化时重新获取数据
数据变化存储数据自动保存用户输入
执行副作用数据变化时更新DOM或执行动画

5、监听属性的注意事项 

  • 避免无限循环:

    watch(count, (val) => {count.value++ // ⚠️ 无限循环,不要这样做
    })
    
  • 不要监听非响应式数据(监听无效):

    const plain = { name: 'Tom' }
    watch(plain, () => {}) // ❌ 无效
    
  • 使用深监听时注意性能问题(深监听成本较高):

    watch(obj, () => {}, { deep: true }) // 谨慎使用
    

    注意,你不能直接侦听响应式对象的属性值,例如、

const obj = reactive({ count: 0 })// 错误,因为 watch() 得到的参数是一个 number
watch(obj.count, (count) => {console.log(`Count is: ${count}`)
})

 这里需要用一个返回该属性的 getter 函数:

// 提供一个 getter 函数
watch(() => obj.count,(count) => {console.log(`Count is: ${count}`)}
)

6、副作用清理

在 Vue 中,所谓的副作用通常指:

  • 异步请求(如 API 请求)

  • 定时器 (setTimeoutsetInterval)

  • DOM 操作、监听事件

  • 其他非纯函数的逻辑

这些操作不是立即完成的,可能会在未来某个时刻继续执行

 为什么需要副作用清理?

以 API 请求为例:假设我们有一个监听器监听 id:
watch(id, (newId) => {fetch(`/api/${newId}`).then(() => {console.log('请求完成,当前ID:', newId)})
})

可能的问题:

当你快速修改 id

  • 请求 1 (/api/1) 发出后,还未完成。

  • 请求 2 (/api/2) 立即发出。

  • 如果请求 2 的响应比请求 1 快,那么请求 1 的响应(较慢)回来时,结果是过时的,但还是会被处理。

我们想要的:

  • 当数据变化时,上一个异步请求应被取消或忽略,不再执行后续逻辑。

为了解决这个问题,Vue 提供了副作用清理机制

副作用清理函数 (onCleanup())

Vue 3.0 开始,Vue 提供了一个清理机制,称为 onCleanup

watch(id, (newId, oldId, onCleanup) => {const controller = new AbortController()fetch(`/api/${newId}`, { signal: controller.signal }).then((res) => res.json()).then((data) => {console.log('请求结果:', data)})onCleanup(() => {controller.abort() // 取消上一个请求})
})

含义解释:

  • 每次监听的数据 (id) 变化时:

    • 先调用上一次注册的 onCleanup 清理函数

    • 然后再执行新一次监听回调。

  • 因此,上一次的异步请求会自动终止,避免过时请求的结果被错误处理。

 watchEffect() 中的副作用清理

watchEffect((onCleanup) => {const timer = setInterval(() => {console.log('定时执行')}, 1000)onCleanup(() => {clearInterval(timer) // 清理定时器})
})

原理相同:

  • 每次响应式数据变化重新执行副作用之前,先调用清理函数。

  • 确保副作用(定时器、请求等)不重叠,避免内存泄漏或数据错乱。

从 Vue 3.5 版本开始,引入了新 API:onWatcherCleanup(): 

import { watch, onWatcherCleanup } from 'vue'watch(id, (newId) => {const controller = new AbortController()fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {console.log('请求完成:', newId)})onWatcherCleanup(() => {controller.abort() // 取消上一个请求})
})

特点:

  • 不再需要第三个参数 onCleanup

  • 可以独立地在监听器或 watchEffect() 回调函数内调用清理函数。

 使用限制:

  • 必须在同步阶段调用,不可放在 await 之后。

  • 因此,必须在异步操作之前注册。

正确用法(同步调用):watch(id, (newId) => {const controller = new AbortController()onWatcherCleanup(() => controller.abort()) // 同步调用,正确!fetch(`/api/${newId}`, { signal: controller.signal })
})❌ 错误用法(异步调用):watch(id, async (newId) => {const controller = new AbortController()await someAsyncOperation()onWatcherCleanup(() => controller.abort()) // ❌ 错误!异步调用

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[预备知识] 5. 优化理论(一)

优化理论 梯度下降&#xff08;Gradient Descent&#xff09; 数学原理与可视化 梯度下降是优化领域的基石算法&#xff0c;其核心思想是沿负梯度方向迭代更新参数。数学表达式为&#xff1a; θ t 1 θ t − α ∇ θ J ( θ t ) \theta_{t1} \theta_t - \alpha \nabla…

大模型微调Fine-tuning:从概念到实践的全面解析

目录 引言 一、什么是大模型微调&#xff1f; 1.1 预训练与微调的区别 1.2 微调的技术演进 二、为什么需要微调&#xff1f; 2.1 解决大模型的固有局限 2.2 微调的优势 三、主流微调方法 3.1 全参数微调 3.2 参数高效微调&#xff08;PEFT&#xff09; 四、微调实践指…

Docker 使用下 (二)

Docker 使用下 &#xff08;二&#xff09; 文章目录 Docker 使用下 &#xff08;二&#xff09;前言一、初识Docker1.1 、Docker概述1.2 、Docker的历史1.3 、Docker解决了什么问题1.4 、Docker 的优点1.5 、Docker的架构图 二、镜像三、容器四、数据卷4.1、数据卷的概念4.2 、…

洛谷P12238 [蓝桥杯 2023 国 Java A] 单词分类

[Problem Discription] \color{blue}{\texttt{[Problem Discription]}} [Problem Discription] Copy from luogu. [Analysis] \color{blue}{\texttt{[Analysis]}} [Analysis] 既然都是字符串前缀的问题了&#xff0c;那当然首先就应该想到 Trie \text{Trie} Trie 树。 我们可…

pta作业中有启发性的程序题

1 【知识点】&#xff1a;多态 函数接口定义&#xff1a; 以Student为基类&#xff0c;构建GroupA, GroupB和GroupC三个类 裁判测试程序样例&#xff1a; #include<iostream> #include <string> using namespace std;/* 请在这里填写答案 */int main() {const …

Scrapy框架之CrawlSpider爬虫 实战 详解

CrawlSpider 是 Scrapy 框架中一个非常实用的爬虫基类&#xff0c;它继承自 Spider 类&#xff0c;主要用于实现基于规则的网页爬取。相较于普通的 Spider 类&#xff0c;CrawlSpider 可以根据预定义的规则自动跟进页面中的链接&#xff0c;从而实现更高效、更灵活的爬取。 Scr…

Glide 如何加载远程 Base64 图片

最近有个需求&#xff0c;后端给出的图片地址并不是正常的 URL&#xff0c;而且需要一个接口去请求&#xff0c;但是返回的是 base64 数据流。这里不关心为啥要这么多&#xff0c;原因有很多&#xff0c;可能是系统的问题&#xff0c;也可能是能力问题。当然作为我们 Android 程…

004-nlohmann/json 快速认识-C++开源库108杰

了解 nlohmann/json 的特点&#xff1b;理解编程中 “数据战场”划分的概念&#xff1b;迅速上手多种方式构建一个JSON对象&#xff1b; 1 特点与安装 nlohmann/json 是一个在 github 长期霸占 “JSON” 热搜版第1的CJSON处理库。它的最大优点是与 C 标准库的容器数据&#xf…

#基础Machine Learning 算法(上)

机器学习算法的分类 机器学习算法大致可以分为三类&#xff1a; 监督学习算法 (Supervised Algorithms&#xff09;:在监督学习训练过程中&#xff0c;可以由训练数据集学到或建立一个模式&#xff08;函数 / learning model&#xff09;&#xff0c;并依此模式推测新的实例。…

正弦波、方波、三角波和锯齿波信号发生器——Multisim电路仿真

目录 Multisim使用教程说明链接 一、正弦波信号发生电路 1.1正弦波发生电路 电路组成 工作原理 振荡频率 1.2 正弦波发生电路仿真分析 工程文件链接 二、方波信号发生电路 2.1 方波发生电路可调频率 工作原理 详细过程 2.2 方波发生电路可调频率/可调占空比 调节占空比 方波产生…

【AND-OR-~OR锁存器设计】2022-8-31

缘由锁存器11111111111-硬件开发-CSDN问答 重置1&#xff0c;不论输入什么&#xff0c;输出都为0&#xff1b; 重置0&#xff0c;输入1就锁住1 此时输入再次变为0&#xff0c;输出不变&#xff0c;为锁住。

力扣-字符串-468 检查ip

思路 考察字符串的使用&#xff0c;还有对所有边界条件的检查 spilt&#xff08;“\.”&#xff09;&#xff0c;toCharArray&#xff0c;Integer.parseInt() 代码 class Solution {boolean checkIpv4Segment(String str){if(str.length() 0 || str.length() > 4) retur…

BC8 十六进制转十进制

题目&#xff1a;BC8 十六进制转十进制 描述 BoBo写了一个十六进制整数ABCDEF&#xff0c;他问KiKi对应的十进制整数是多少。 输入描述&#xff1a; 无 输出描述&#xff1a; 十六进制整数ABCDEF对应的十进制整数&#xff0c;所占域宽为15。 备注&#xff1a; printf可以使用…

ARM子程序和栈

微处理器中的栈由栈指针指向存储器中的栈顶来实现&#xff0c;当数据项入栈时&#xff0c;栈 指针向上移动&#xff0c;当数据项出栈时&#xff0c;栈指针向下移动。 实现栈时需要做出两个决定&#xff1a;一是当数据项进栈时是向低位地址方向向上生 长&#xff08;图a和图b&a…

jwt身份验证和基本的利用方式

前言 &#xff1a; 什么是jwt&#xff08;json web token&#xff09;&#xff1f; 看看英文单词的意思就是 json形式的token 他的基本的特征 &#xff1a; 类似于这样的 他有2个点 分割 解码的时候会有三个部分 头部 payload 对称密钥 这个就是对称加密 头部&am…

n8n工作流自动化平台的实操:利用本地嵌入模型,完成文件内容的向量化及入库

1.成果展示 1.1n8n的工作流 牵涉节点&#xff1a;FTP、Code、Milvus Vector Store、Embeddings OpenAI、Default Data Loader、Recursive Character Text Splitter 12.向量库的结果 2.实操过程 2.1发布本地嵌入模型服务 将bge-m3嵌入模型&#xff0c;发布成满足open api接口…

MATLAB人工大猩猩部队GTO优化CNN-LSTM多变量时间序列预测

本博客来源于CSDN机器鱼&#xff0c;未同意任何人转载。 更多内容&#xff0c;欢迎点击本专栏目录&#xff0c;查看更多内容。 目录 0 引言 1 数据准备 2 CNN-LSTM模型搭建 3 GTO超参数优化 3.1 GTO函数极值寻优 3.2 GTO优化CNN-LSTM超参数 3.3 主程序 4 结语 0 引言…

git项目迁移,包括所有的提交记录和分支 gitlab迁移到gitblit

之前git都是全新项目上传&#xff0c;没有迁移过&#xff0c;因为迁移的话要考虑已有项目上的分支都要迁移过去&#xff0c;提交记录能迁移就好&#xff1b;分支如果按照全新项目上传的方式需要新git手动创建好老git已有分支&#xff0c;在手动一个一个克隆老项目分支代码依次提…

Photo-SLAM论文理解、环境搭建、代码理解与实测效果

前言&#xff1a;第一个解耦式Photo-SLAM&#xff0c;亮点和效果。 参考&#xff1a;https://zhuanlan.zhihu.com/p/715311759 全网最细PhotoSLAM的conda环境配置教程&#xff0c;拒绝环境污染&#xff01;&#xff01;-CSDN博客 1. 环境搭建 硬件&#xff1a;RTX 4090D wi…

如何使用VSCode编写C、C++和Python程序

一、首先准备好前期工作。如下载安装Python、VSCode、一些插件等。写代码之前需要先创建文件夹和文件。 二、将不同语言写的代码放在不同的文件夹中&#xff0c;注意命名时不要使用中文。 三、打开VSCode&#xff0c;点击“文件”->“打开文件夹”->“daimalainxi”->…