前端面经-VUE3篇--vue3基础知识(一)插值表达式、ref、reactive

 一、计算属性(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 中,监听属性(Watcher) 是一种响应式机制,用于监测响应式数据的变化:

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

  • 监听属性通过 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.mzph.cn/pingmian/79124.shtml

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

相关文章

数据结构6 · BinaryTree二叉树模板

代码函数功能顺序如下&#xff1a; 1&#xff1a;destroy&#xff1a;递归删除树 2&#xff1a;copy&#xff1a;复制二叉树 3&#xff1a;preOrder&#xff1a;递归前序遍历 4&#xff1a;inOrder&#xff1a;递归中序遍历 5&#xff1a;postOrder&#xff1a;递归后续遍…

C++/SDL进阶游戏开发 —— 双人塔防游戏(代号:村庄保卫战 13)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 十…

强化学习之基于无模型的算法之时序差分法

2、时序差分法(TD) 核心思想 TD 方法通过 引导值估计来学习最优策略。它利用当前的估计值和下一个时间步的信息来更新价值函数&#xff0c; 这种方法被称为“引导”&#xff08;bootstrapping&#xff09;。而不需要像蒙特卡罗方法那样等待一个完整的 episode 结束才进行更新&…

AE/PR模板 100个现代文字标题动态排版效果动画 Motion Titles

Motion Titles是一个令人惊艳的AE/PR模板&#xff0c;提供了100个现代文字标题的动态排版效果动画。这些动画效果能够为你的项目增添视觉冲击力和专业感&#xff0c;为文字标题注入活力和动感。该模板适用于Adobe After Effects CC或更高版本以及Adobe Premiere Pro 2020或更高…

【AI提示词】二八法则专家

提示说明 精通二八法则&#xff08;帕累托法则&#xff09;的广泛应用&#xff0c;擅长将其应用于商业、管理、个人发展等领域&#xff0c;深入理解其在不同场景中的具体表现和实际意义。 提示词 # Role: 二八法则专家## Profile - language: 中文 - description: 精通二八法…

前端八股 CSS 1

盒子模型 进行布局时将所有元素表示为一个个盒子box padding margin border content content&#xff1a;盒子内容 待显示的文本和图像 padding&#xff1a;内边距&#xff0c;内容和border之间的空间&#xff0c;不能为负数&#xff0c;受bkc影响 border:边框&#xff0c…

组件通信-$attrs

概述&#xff1a;$attrs用于实现当前组件的父组件&#xff0c;向当前组件的子组件通信&#xff08;爷→孙&#xff09;。 具体说明&#xff1a;$attrs是一个对象&#xff0c;包含所有父组件传入的标签属性。 注意&#xff1a;$attrs会自动排除props中声明的属性(可以认为声明过…

jdk开启https详细步骤

要在 JDK 中启用 HTTPS&#xff0c;您可以按照以下详细步骤进行操作&#xff1a; 生成密钥库和证书&#xff1a; 首先&#xff0c;您需要生成一个密钥库&#xff08;keystore&#xff09;和证书&#xff0c;可以使用 keytool 工具来生成。以下是使用 keytool 生成密钥库和证书的…

文章四《深度学习核心概念与框架入门》

文章4&#xff1a;深度学习核心概念与框架入门——从大脑神经元到手写数字识别的奇幻之旅 引言&#xff1a;给大脑装个"GPU加速器"&#xff1f; 想象一下&#xff0c;你的大脑如果能像智能手机的GPU一样快速处理信息会怎样&#xff1f;这正是深度学习的终极目标&…

关于CSDN创作的常用模板内容

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 好文评论新文推送 &#x1f4c3;文章前言 &…

linux的信号量初识

Linux下的信号量(Semaphore)深度解析 在多线程或多进程并发编程的领域中&#xff0c;确保对共享资源的安全访问和协调不同执行单元的同步至关重要。信号量&#xff08;Semaphore&#xff09;作为经典的同步原语之一&#xff0c;在 Linux 系统中扮演着核心角色。本文将深入探讨…

《Android 应用开发基础教程》——第十一章:Android 中的图片加载与缓存(Glide 使用详解)

目录 第十一章&#xff1a;Android 中的图片加载与缓存&#xff08;Glide 使用详解&#xff09; &#x1f539; 11.1 Glide 简介 &#x1f538; 11.2 添加 Glide 依赖 &#x1f538; 11.3 基本用法 ✦ 加载网络图片到 ImageView&#xff1a; ✦ 加载本地资源 / 文件 / UR…

AE模板 300个故障干扰损坏字幕条标题动画视频转场预设

这个AE模板提供了300个故障干扰损坏字幕条标题动画视频转场预设&#xff0c;让您的视频具有炫酷的故障效果。无论是预告片、宣传片还是其他类型的视频&#xff0c;这个模板都能带给您令人惊叹的故障运动标题效果。该模板无需任何外置插件或脚本&#xff0c;只需一键点击即可应用…

在 Python 中,以双下划线开头和结尾的函数(如 `__str__`、`__sub__` 等)

在 Python 中&#xff0c;以双下划线开头和结尾的函数&#xff08;如 __str__、__sub__ 等&#xff09;被称为特殊方法&#xff08;Special Methods&#xff09;或魔术方法&#xff08;Magic Methods&#xff09;。它们确实是 Python 内置的&#xff0c;用于定义类的行为&#…

git问题记录-如何切换历史提交分支,且保留本地修改

问题记录 我在本地编写了代码&#xff0c;突然想查看之前提交的代码&#xff0c;并且想保留当前所在分支所做的修改 通过git stash对本地的代码进行暂存 使用git checkout <commit-hash>切换到之前的提交记录。 查看完之后我想切换回来&#xff0c;恢复暂存的本地代码…

Github开通第三方平台OAuth登录及Java对接步骤

调研起因&#xff1a; 准备搞AI Agent海外项目&#xff0c;有相当一部分用户群体是程序员&#xff0c;所以当然要接入Github这个全球最大的同性交友网站了&#xff0c;让用户使用Github账号一键完成注册或登录。 本教程基于Web H5界面进行对接&#xff0c;同时也提供了spring-…

期刊、出版社、索引数据库

image 1、研究人员向期刊或者会议投稿&#xff0c;交注册费和相应的审稿费等相关费用[1]&#xff1b; 2、会议组织者和期刊联系出版社&#xff0c;交出版费用&#xff1b; 3、出版社将论文更新到自己的数据库中&#xff0c;然后将数据库卖给全世界各大高校或企业&#xff1b; 4…

Transformer 模型及深度学习技术应用

近年来&#xff0c;随着卷积神经网络&#xff08;CNN&#xff09;等深度学习技术的飞速发展&#xff0c;人工智能迎来了第三次发展浪潮&#xff0c;AI技术在各行各业中的应用日益广泛。 注意力机制&#xff1a;理解其在现代深度学习中的关键作用&#xff1b; Transformer模型…

zynq7035的arm一秒钟最多可以支持触发多少次中断

一、概述 1.关于zynq7035的ARM处理器一秒能够支持多少次中断触发&#xff0c;需要综合来考虑。需要确定ARM处理器的参数&#xff0c;目前zynq7000系列&#xff0c;使用的双核Cortex-A9处理器。其中主频大概在500MHZ~1GHZ左右&#xff0c;不同的用户配置的主频可能稍微有差别。 …

数据结构与算法:图论——最短路径

最短路径 先给出一些leetcode算法题&#xff0c;以后遇见了相关题目再往上增加 最短路径的4个常用算法是Floyd、Bellman-Ford、SPFA、Dijkstra。不同应用场景下&#xff0c;应有选择地使用它们&#xff1a; 图的规模小&#xff0c;用Floyd。若边的权值有负数&#xff0c;需要…