《Vue3实战教程》5:响应式基础

如果您有疑问,请观看视频教程《Vue3实战教程》

响应式基础​

API 参考

本页和后面很多页面中都分别包含了选项式 API 和组合式 API 的示例代码。现在你选择的是 组合式 API。你可以使用左侧侧边栏顶部的“API 风格偏好”开关在 API 风格之间切换。

声明响应式状态​

ref()

在组合式 API 中,推荐使用 ref() 函数来声明响应式状态:

js

import { ref } from 'vue'const count = ref(0)

ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

js

const count = ref(0)console.log(count) // { value: 0 }
console.log(count.value) // 0count.value++
console.log(count.value) // 1

参考:为 refs 标注类型 

要在组件模板中访问 ref,请从组件的 setup() 函数中声明并返回它们:

js

import { ref } from 'vue'export default {// `setup` 是一个特殊的钩子,专门用于组合式 API。setup() {const count = ref(0)// 将 ref 暴露给模板return {count}}
}

template

<div>{{ count }}</div>

注意,在模板中使用 ref 时,我们需要附加 .value。为了方便起见,当在模板中使用时,ref 会自动解包 (有一些注意事项)。

你也可以直接在事件监听器中改变一个 ref:

template

<button @click="count++">{{ count }}
</button>

对于更复杂的逻辑,我们可以在同一作用域内声明更改 ref 的函数,并将它们作为方法与状态一起公开:

js

import { ref } from 'vue'export default {setup() {const count = ref(0)function increment() {// 在 JavaScript 中需要 .valuecount.value++}// 不要忘记同时暴露 increment 函数return {count,increment}}
}

然后,暴露的方法可以被用作事件监听器:

template

<button @click="increment">{{ count }}
</button>

这里是 Codepen 上的例子,没有使用任何构建工具。

<script setup>

在 setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用单文件组件 (SFC) 来避免这种情况。我们可以使用 <script setup> 来大幅度地简化代码:

vue

<script setup>
import { ref } from 'vue'const count = ref(0)function increment() {count.value++
}
</script><template><button @click="increment">{{ count }}</button>
</template>

在演练场中尝试一下

<script setup> 中的顶层的导入、声明的变量和函数可在同一组件的模板中直接使用。你可以理解为模板是在同一作用域内声明的一个 JavaScript 函数——它自然可以访问与它一起声明的所有内容。

TIP

在指南的后续章节中,我们基本上都会在组合式 API 示例中使用单文件组件 + <script setup> 的语法,因为大多数 Vue 开发者都会这样使用。

如果你没有使用单文件组件,你仍然可以在 setup() 选项中使用组合式 API。

为什么要使用 ref?​

你可能会好奇:为什么我们需要使用带有 .value 的 ref,而不是普通的变量?为了解释这一点,我们需要简单地讨论一下 Vue 的响应式系统是如何工作的。

当你在模板中使用了一个 ref,然后改变了这个 ref 的值时,Vue 会自动检测到这个变化,并且相应地更新 DOM。这是通过一个基于依赖追踪的响应式系统实现的。当一个组件首次渲染时,Vue 会追踪在渲染过程中使用的每一个 ref。然后,当一个 ref 被修改时,它会触发追踪它的组件的一次重新渲染。

在标准的 JavaScript 中,检测普通变量的访问或修改是行不通的。然而,我们可以通过 getter 和 setter 方法来拦截对象属性的 get 和 set 操作。

该 .value 属性给予了 Vue 一个机会来检测 ref 何时被访问或修改。在其内部,Vue 在它的 getter 中执行追踪,在它的 setter 中执行触发。从概念上讲,你可以将 ref 看作是一个像这样的对象:

js

// 伪代码,不是真正的实现
const myRef = {_value: 0,get value() {track()return this._value},set value(newValue) {this._value = newValuetrigger()}
}

另一个 ref 的好处是,与普通变量不同,你可以将 ref 传递给函数,同时保留对最新值和响应式连接的访问。当将复杂的逻辑重构为可重用的代码时,这将非常有用。

该响应性系统在深入响应式原理章节中有更详细的讨论。

深层响应性​

Ref 可以持有任何类型的值,包括深层嵌套的对象、数组或者 JavaScript 内置的数据结构,比如 Map

Ref 会使它的值具有深层响应性。这意味着即使改变嵌套对象或数组时,变化也会被检测到:

js

import { ref } from 'vue'const obj = ref({nested: { count: 0 },arr: ['foo', 'bar']
})function mutateDeeply() {// 以下都会按照期望工作obj.value.nested.count++obj.value.arr.push('baz')
}

非原始值将通过 reactive() 转换为响应式代理,该函数将在后面讨论。

也可以通过 shallow ref 来放弃深层响应性。对于浅层 ref,只有 .value 的访问会被追踪。浅层 ref 可以用于避免对大型数据的响应性开销来优化性能、或者有外部库管理其内部状态的情况。

  • 减少大型不可变数据的响应性开销
  • 与外部状态系统集成

DOM 更新时机​

当你修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。Vue 会在“next tick”更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。

要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API:

js

import { nextTick } from 'vue'async function increment() {count.value++await nextTick()// 现在 DOM 已经更新了
}

reactive()

还有另一种声明响应式状态的方式,即使用 reactive() API。与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:

js

import { reactive } from 'vue'const state = reactive({ count: 0 })

参考:为 reactive() 标注类型 

在模板中使用:

template

<button @click="state.count++">{{ state.count }}
</button>

响应式对象是 JavaScript 代理,其行为就和普通对象一样。不同的是,Vue 能够拦截对响应式对象所有属性的访问和修改,以便进行依赖追踪和触发更新。

reactive() 将深层地转换对象:当访问嵌套对象时,它们也会被 reactive() 包装。当 ref 的值是一个对象时,ref() 也会在内部调用它。与浅层 ref 类似,这里也有一个 shallowReactive() API 可以选择退出深层响应性。

Reactive Proxy vs. Original​

值得注意的是,reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的:

js

const raw = {}
const proxy = reactive(raw)// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false

只有代理对象是响应式的,更改原始对象不会触发更新。因此,使用 Vue 的响应式系统的最佳实践是仅使用你声明对象的代理版本

为保证访问代理的一致性,对同一个原始对象调用 reactive() 会总是返回同样的代理对象,而对一个已存在的代理对象调用 reactive() 会返回其本身:

js

// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy) // true// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy) // true

这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理:

js

const proxy = reactive({})const raw = {}
proxy.nested = rawconsole.log(proxy.nested === raw) // false

reactive() 的局限性​

reactive() API 有一些局限性:

  1. 有限的值类型:它只能用于对象类型 (对象、数组和如 MapSet 这样的集合类型)。它不能持有如 stringnumber 或 boolean 这样的原始类型。

  2. 不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

    js
    let state = reactive({ count: 0 })// 上面的 ({ count: 0 }) 引用将不再被追踪
    // (响应性连接已丢失!)
    state = reactive({ count: 1 })
  3. 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:

    js
    const state = reactive({ count: 0 })// 当解构时,count 已经与 state.count 断开连接
    let { count } = state
    // 不会影响原始的 state
    count++// 该函数接收到的是一个普通的数字
    // 并且无法追踪 state.count 的变化
    // 我们必须传入整个对象以保持响应性
    callSomeFunction(state.count)

由于这些限制,我们建议使用 ref() 作为声明响应式状态的主要 API。

额外的 ref 解包细节​

作为 reactive 对象的属性​

一个 ref 会在作为响应式对象的属性被访问或修改时自动解包。换句话说,它的行为就像一个普通的属性:

js

const count = ref(0)
const state = reactive({count
})console.log(state.count) // 0state.count = 1
console.log(count.value) // 1

如果将一个新的 ref 赋值给一个关联了已有 ref 的属性,那么它会替换掉旧的 ref:

js

const otherCount = ref(2)state.count = otherCount
console.log(state.count) // 2
// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1

只有当嵌套在一个深层响应式对象内时,才会发生 ref 解包。当其作为浅层响应式对象的属性被访问时不会解包。

数组和集合的注意事项​

与 reactive 对象不同的是,当 ref 作为响应式数组或原生集合类型 (如 Map) 中的元素被访问时,它不会被解包:

js

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

在模板中解包的注意事项​

在模板渲染上下文中,只有顶级的 ref 属性才会被解包。

在下面的例子中,count 和 object 是顶级属性,但 object.id 不是:

js

const count = ref(0)
const object = { id: ref(1) }

因此,这个表达式按预期工作:

template

{{ count + 1 }}

...但这个不会

template

{{ object.id + 1 }}

渲染的结果将是 [object Object]1,因为在计算表达式时 object.id 没有被解包,仍然是一个 ref 对象。为了解决这个问题,我们可以将 id 解构为一个顶级属性:

js

const { id } = object

template

{{ id + 1 }}

现在渲染的结果将是 2

另一个需要注意的点是,如果 ref 是文本插值的最终计算值 (即 {{ }} 标签),那么它将被解包,因此以下内容将渲染为 1

template

{{ object.id }}

该特性仅仅是文本插值的一个便利特性,等价于 {{ object.id.value }}

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

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

相关文章

EasyExcel停更,FastExcel接力

11月6日消息&#xff0c;阿里巴巴旗下的Java Excel工具库EasyExcel近日宣布&#xff0c;将停止更新&#xff0c;未来将逐步进入维护模式&#xff0c;将继续修复Bug&#xff0c;但不再主动新增功能。 EasyExcel以其快速、简洁和解决大文件内存溢出的能力而著称&#xff0c;官方…

深入解读数据资产化实践指南(2024年)

本指南主要介绍了数据资产化的概念、目标和意义&#xff0c;以及实施数据资产化的过程。指南详细阐述了数据资产化的内涵&#xff0c;包括数据资产的定义、数据资产化的目标与意义&#xff0c;并介绍了数据资产化的过程包括业务数据化、数据资源化、数据产品化和数据资本化。 …

广州大学计算机组成原理课程设计

一.课设性质&#xff0c;目的&#xff0c;任务 《计算机组成与系统结构课程设计》是计算机学院各专业集中实践性环节之一&#xff0c;是学习完《计算机组成与系统结构》课程后进行的一次全面的综合练习。其目的是综合运用所学计算机原理知识&#xff0c;设计并实现一台模型计算…

MimicBrush:智能图像编辑新宠,能否革新你的创意设计?

一、介绍 MimicBrush 是一款由阿里巴巴和香港大学联合研发的图像编辑工具&#xff0c;它通过模仿参考图像&#xff0c;对目标图像选定区域进行自动局部编辑。以下是关于 MimicBrush 的详细介绍&#xff1a; 1.技术特点 智能匹配 &#xff1a;利用尖端 AI 技术&#xff0c;Mi…

QT用Enigmavb 打包成单独exe

QT用这个工具打包成单个exe&#xff0c;然后再用winrar打包成zip可以发给别人 在之前需要用QT的release打包 之前的文章QTrelease打包【非单个exe】 Enigmavb 打包流程&#xff1a; 安装过程&#xff1a; next-》i accept -》选择安装位置 -》next -》Create a desktop ic…

f(f(x))=x^2 -11x+36, 求f(6)的值,

偶然看到的一个题目&#xff0c;一时兴起&#xff0c;做了一下。题目如下 简单粗暴的思路是待定系数法&#xff0c;盲猜f(x)是个2次函数&#xff0c;令f(x)ax^2bxc ,带入原式&#xff0c;发现矛盾&#xff08;计算略&#xff09;就想放弃了。 忽然看到如果带入6 的话&#xf…

华为浏览器(HuaweiBrowser),简约高效上网更轻松

华为浏览器是一款由华为公司自主研发的网页浏览工具&#xff0c;凭借其独特的设计理念和优质的用户体验&#xff0c;正在吸引越来越多的用户关注。这款基于Chromium技术打造的浏览器不仅继承了Chrome的高性能特质&#xff0c;更融入了华为自身的创新元素&#xff0c;为用户打造…

Ubuntu下ESP32-IDF开发环境搭建

Ubuntu下ESP32-IDF开发环境搭建 文章目录 Ubuntu下ESP32-IDF开发环境搭建一、前言二、软件安装三、开发环境搭建3.1 ESP-IDF安装&#xff1a;3.2 安装编译工具&#xff1a; 四、编译并烧录代码五、ESP32代码编辑工具 一、前言 ​ 开发ESP32&#xff0c;我们首先就要安装开发环…

修炼内功之函数栈帧的创建与销毁

修炼内功之函数栈帧的创建与销毁 一 前置知识&#xff08;1&#xff09;栈&#xff08;2&#xff09;相关寄存器和汇编指令 二 函数栈帧三 代码演示函数栈帧的创建&#xff08;1&#xff09;代码演示&#xff08;2&#xff09;函数栈帧逐帧分析 四 对开篇问题的解答 相信来CSDN…

学习threejs,THREE.PlaneGeometry 二维平面几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️HREE.PlaneGeometry 二维平…

SpringCloud 入门(4)—— 网关

上一篇&#xff1a;SpringCloud 入门&#xff08;3&#xff09;—— Nacos配置中心-CSDN博客 Spring Cloud Gateway 作为 Spring Cloud 生态系统的一部分&#xff0c;主要在微服务架构中充当 API 网关的角色。它提供了统一的入口点来处理所有的 HTTP 请求&#xff0c;并将这些请…

石岩基督教福音堂

周末娃&#xff0c;娃的阿婆和我一起去石岩基督教福音堂做礼拜。 这是我第一次进入石岩的教堂。教堂很高。应该有3,4层楼高。 这周末做礼拜的人很多一楼人满了&#xff0c;阿婆去二楼做礼拜&#xff0c;娃和我去三楼的儿童室。很多家长楼下做礼拜&#xff0c;小朋友被安排三楼…

论文《Vertical Federated Learning: Concepts, Advances, and Challenges》阅读

论文《Vertical Federated Learning: Concepts, Advances, and Challenges》阅读 论文概况纵向联邦VFL框架介绍问题定义VFL 训练协议 对通信效率的优化对性能的优化自监督方案&#xff08;Self-Supervised Approaches&#xff09;半监督方案&#xff08;Semi-Supervised Approa…

如何用gpt来分析链接里面的内容(比如分析论文链接)和分析包含多个文件中的一块代码

如何用gpt来分析链接里面的内容&#xff0c;方法如下 这里使用gpt4里面有一个网路的功能 点击搜索框下面这个地球的形状即可启动搜索网页模式 然后即可提出问题在搜索框里&#xff1a;发现正确识别和分析了链接里面的内容 链接如下&#xff1a;https://arxiv.org/pdf/2009.1…

本地部署webrtc应用怎么把http协议改成https协议?

环境&#xff1a; WSL2 Ubuntu22.04 webrtc视频聊天应用 问题描述&#xff1a; 本地部署webrtc应用怎么把http协议改成https协议&#xff1f; http协议在安卓手机浏览器上用不了麦克风本&#xff0c;来地应用webrtc 本来是http协议&#xff0c;在安卓手机上浏览器不支持使…

面向未来的教育技术:智能成绩管理系统的开发

3.1 可行性研究 成绩管理系统开发实现分析需要从不同的角度来进行分析可行性&#xff0c;比如从时间角度&#xff0c;经济角度&#xff0c;甚至操作角度。从不同的角度分析可行性会让成绩管理系统开发具体化&#xff0c;进而达到辩证开发的正确性。 3.1.1 经济可行性 从经济方面…

zabbix监控山石系列Hillstone监控模版(适用于zabbix7及以上)

监控项&#xff1a; 触发器&#xff1a; 监控数据如下&#xff1a;

UE5 渲染管线 学习笔记

兰伯特 SSS为散射的意思 带Bias的可以根据距离自动切换mip的卷积值 而带Level的值mipmaps的定值 #define A8_SAMPLE_MASK .a 这样应该就很好理解了 这个只采样a通道 带Level的参考上面的 朝左上和右下进行模糊 带Bias参考上面 随机数 4D 3D 2D 1D

ArcGIS Pro 3.4新功能3:空间统计新特性,基于森林和增强分类与回归,过滤空间自相关

目录 应用 1&#xff1a;它是相关性还是托布勒第一定律&#xff1f; 应用 2&#xff1a;将空间带入非空间模型 结论 在 ArcGIS Pro 3.4 中&#xff0c;我们在新的空间组件实用程序&#xff08;Moran 特征向量&#xff09;工具集中发布了一个新工具 - 从字段过滤空间自相关。…

【微信小程序】2|轮播图 | 我的咖啡店-综合实训

轮播图 引言 在微信小程序中&#xff0c;轮播图是一种常见的用户界面元素&#xff0c;用于展示广告、产品图片等。本文将通过“我的咖啡店”小程序的轮播图实现&#xff0c;详细介绍如何在微信小程序中创建和管理轮播图。 轮播图数据准备 首先&#xff0c;在home.js文件中&a…