vue3中ref和reactive响应式数据、ref模板引用(组合式和选项式区别)、组件ref的使用

目录

Ⅰ.ref

1.基本用法:ref响应式数据

2.ref模板引用

3.ref在v-for中的模板引用

​4.ref在组件上使用

​5.TS中ref数据标注类型

Ⅱ.reactive 

 1.基本用法:reactive响应式数据

 2.TS中reactive标注类型

Ⅲ.ref和reactive的使用场景和区别

Ⅳ.小结&常见疑问解答



Ⅰ.ref

1.基本用法:ref响应式数据

ref 可以对原始类型(基本数据类型)和对象(引用类型)的数据进行响应式处理。
ref 的核心作用是将一个普通的 JavaScript 数据转换成响应式的。它返回一个特殊的对象,这个对象包含一个名为 .value 的属性,我们在JS中就需要通过操作 .value 访问和修改这个ref的响应式数据,但是在模板中不需要加.value来访问这个ref响应式数据的。

基本概念解析
基本数据类型:number, string, boolean, undefined, null
响应式数据:Vue的响应式数据(Reactivity)是Vue.js框架最重要的特性之一,它是Vue实现数据绑定的核心机制。 在Vue中,当数据发生变化时,相关的DOM元素会自动更新,而不需要手动操作DOM。 这种自动更新的机制使得开发者在编写代码时可以更加专注于数据的状态和业务逻辑,而不必担心如何去更新视图。

 如果我们想让一个 名为count的变量 变成响应式的,就可以这样写:

import { ref } from 'vue';//count是基本数据类型,变为响应式
const count = ref(0);    

现在,count变量就不再是一个普通的数字了,而是一个包含 .value 属性的对象,它的值可以通过 count.value 来访问和修改。当我们修改 count.value 的值时,任何依赖于 count 的页面元素都会自动更新。
我们在JS中访问或修改该 count变量的值的时候,需要加上 .value来进行访问,但是在模板中访问 count 的时候,不需要加上 .value,直接写变量名count即可访问count的值

//在模板中,我们可以不加..value来访问count变量的值:
<template><div>{{ count }}</div>
</template>//在JS中,我们要加上.value来访问/修改count变量的值:
console.log(count.value); // 0
count.value = 1; 

我们还可以让 包含count属性的对象 变成一个响应式的数据:

//我们可以直接用 ref 包裹整个 对象//count是对象的属性
const data = ref({ count: 0 })    //对象也可以变成响应式的

这样一来,data 就变成了一个响应式对象,它的所有属性,包括 count,都会变成响应式的。我们在JS中访问或修改该 data对象的属性的时候,需要加上 .value来进行访问,并且在模板中访问 data.count 的时候,也需要加上 .value

//在模板中访问data对象属性count
<template>
<div>{{ data.value.count }}</div>
</template>//在JS中访问data对象对象count
console.log(data.value.count); // 0

假如我们直接将一个 ref 对象赋值给一个对象的属性,而 ref没有使用.value进行赋值,那么这个属性不会变成响应式的。

有一个名为 data 的对象,我们想让它的 name 属性变成响应式的,下面这种写法是错误的:

因为 data.name 现在存储的是 nameRef 对象本身,而不是它的值。
当我们修改改 nameRef.value 时,data.name 并不会随之改变,页面也不会更新。

const data = { name: 'John' };
const nameRef = ref('John');
data.name = nameRef;   //ref没有使用.value进行赋值,data.name是不会变成响应式的

正确的做法是将 nameRef.value 赋值给 data.name 或者可以直接用 ref 包裹整个 data 对象,以下是正确示例:

//使用nameRef.value进行赋值
data.name = nameRef.value;//或者直接用ref包括整个data对象
const data = ref({ name: 'John' });

ref 小结:ref 是 Vue 3 中创建响应式数据的基本方法,但它并不会直接将一个普通的 JavaScript 对象变成响应式的。我们需要通过 .value 来访问和修改响应式数据,或者直接用 ref 包裹整个对象,才能实现真正的响应式更新。

2.ref模板引用

如果我们需要直接访问组件中的底层DOM元素,可使用vue提供特殊的ref属性来进行访问

我们可以在元素上使用ref属性来设置需要访问的DOM元素

  • ref属性值是 字符串 的形式;
  • ref属性值还可以是用v-bind::ref形式绑定的函数,该函数的第一个参数是该元素;

如果元素的 ref属性值 采用的是字符串形式:

  • 在组合式API的JS中,我们需要声明一个同名的ref变量,来获得该模板的引用;
  • 在选项式API的JS中,可通过this.$refs来访问模板的引用;

 组合式API:

<template><!-- 字符串形式的 ref -->账号输入框:<input type="text" ref="account"><button @click="accountInputStyle">改变账号输入框的样式</button><!-- 函数形式的 ref ,必须采用 v-bind 或 : 的形式来给ref绑定属性值 -->密码输入框:<input type="password" :ref="passwordFn"><button @click="passwordInputStyle">改变密码输入框的样式</button></template><script setup>
import { ref, reactive, computed, onMounted, nextTick } from 'vue'//定义响应式数据(同名ref变量)
// ref 变量名 和 对应DOM元素的ref属性值 相等
let account = ref(null)
let password = ref(null)const accountInputStyle = () => {//account是获取的输入框DOM元素account.value.style.padding = '15px'account.value.style.caretColor = 'red'account.value.className = 'rounded'account.value.focus()
}// 使用函数给ref绑定属性值,该函数的第一个参数为该元素
// 在页面渲染的时候会自动执行
// 函数式生命的 ref,不会在 this.$refs 中获取
const passwordFn = (el) => {// el是DOM元素,这里是密码输入框password.value = elconsole.log(password.value)
}
const passwordInputStyle = () => {//此处设置的 style 均为行内样式password.value.style.border = '4px solid green'password.value.style.padding = '15px'password.value.focus()
}onMounted(() => {});
</script><style scoped>
</style>

选项式API:

<template><!-- 字符串形式的 ref -->账号输入框:<input type="text" ref="account"><button @click="accountInputStyle">改变账号输入框的样式</button><!-- 函数形式的 ref ,必须采用 v-bind 或 : 的形式来给ref绑定属性值 -->密码输入框:<input type="password" :ref="passwordFn"><button @click="passwordInputStyle">改变密码输入框的样式</button></template><script>
export default {data: () => ({accountEl: null,passwordEl: null}),methods: {changeAccountInputStyle() {this.accountEl = this.$refs.account // 获取账号输入框的 DOMconsole.log(this.accountEl)this.accountEl.style = "padding: 15px"this.accountEl.className = "rounded"this.accountEl.focus()},passwordRef(el) { this.passwordEl = el  // el 元素是密码输入框},changePasswordInputStyle() {console.log(this.passwordEl) console.log(this.$refs) // 函数式声明的 ref,不会在this.$refs中获取this.passwordEl.style = "padding: 15px"this.passwordEl.className = "rounded"this.passwordEl.focus()},}
}
</script><style>
</style>

3.ref在v-for中的模板引用

当在v-for中使用模板引用时:(注意:需要 v3.2.25 及以上的版本;)

  • 如果 ref 值是 字符串 形式,在元素被渲染后包含对应整个 列表的所有元素【数组】
  • 如果 ref 值是 函数 形式,则会每渲染一个列表元素就会执行对应的函数【不推荐使用】;

 组合式API:

<template><ul><li v-for="b in books" :key="b.id" ref="bookList">{{ b.name }}</li></ul>
</template><script setup>
import { onMounted, ref } from "vue";// 书本
let books = ref([{ id: 1, name: "海底两万里" },{ id: 2, name: "骆驼祥子" },{ id: 3, name: "老人与海" },{ id: 4, name: "安徒生童话" },
]);let bookList = ref(null);onMounted(() => {console.log(bookList.value); // 获取引用的 DOM 对象并打印,发现是数组bookList.value[2].className = "error";
});
</script><style>
.error {border: 1px solid red;
}
</style>

选项式API:

<template><ul><!-- 如果 ref 值是字符串形式,在元素被渲染后包含对应整个列表的所有元素【数组】 --><li v-for="b in books" :key="b.id" ref="bookList">{{ b.name }}</li></ul><button @click="changeBookListStyle">点我查看 bookList</button><hr /><!-- 如果ref值是函数形式,则会每渲染一个列表元素则会执行对应的函数【不推荐使用】 --><ul><li v-for="s in students" :key="s.id" :ref="studentsRef">{{ s.name }}</li></ul>
</template><script>
export default {data: () => ({books: [{ id: 1, name: "红楼梦" },{ id: 2, name: "三国演义" },{ id: 3, name: "水浒传" },{ id: 4, name: "西游记" },],students: [{ id: 1, name: "Jack" },{ id: 2, name: "Annie" },{ id: 3, name: "Tom" },],}),methods: {changeBookListStyle() {console.log(this.$refs.bookList);this.$refs.bookList[2].style = "color: red";},studentsRef(el) {console.log(el);},},
};
</script>

两种运行效果:


4.ref在组件上使用

模板引用也可以被用在一个子组件上:这种情况下引用中获得的值是组件实例

  • 如果子组件使用的是组合式API<script setup>,那么该子组件默认是私有的,则父组件无法访问该子组件,除非子组件在其中通过defineExpose宏采用对象形式显示暴露特定的数据或函数
  • 如果子组件使用的选项式API默认情况下父组件可以随意访问该子组件的数据和函数,除非在子组件使用expose选项来暴露特定的数据或函数,那么父组件就只能访问在expose种暴露的数据或函数,没有暴露的就不能访问了。expose值为字符串数组

 组合式API:

  • 父组件:
    <template><h3>登录页面</h3><hr><!-- 组件上的 ref 的值为该组件的实例 --><Vue1 ref="login_vue"></Vue1><hr><button @click="showSonData">查看子组件的信息</button>
    </template><script setup>
    //组合式API中,默认情况下,子组件中的数据、函数等等都是私有的,不能访问
    //如果 子组件 通过 defineExpose 宏采用对象形式显式暴露特定的数据或函数等等import Vue1 from '@/components/src/test.vue'    //导入子组件
    import { ref, onMounted } from 'vue'
    const login_vue = ref(null)
    const showSonData = () => {console.log(login_vue.value.account)console.log(login_vue.value.password)login_vue.value.toLogin()
    }
    onMounted(() => {});
    </script>
  • 子组件:
    <template>账号:<input type="text" v-model="account"><br>密码:<input type="text" v-model="password"><hr><button @click="toLogin">登录</button>
    </template><script setup>
    import { ref } from 'vue'
    const account = ref('admin')
    const password = ref('123456')
    const toLogin = () => {alert('登录中……')
    }
    // 使用 defineExpose 将指定数据、函数等暴露出去
    defineExpose({account,toLogin
    });
    </script>

 效果展示:


选项式API:

  • 父组件:
<template><h3>登录页面</h3><hr><!-- 组件上的 ref 的值为该组件的实例 --><Vue1 ref="loginVue"></Vue1><hr><button @click="showSonData">查看子组件的信息</button>
</template><script>
//选项式API中,默认情况下,父组件可以随意访问子组件的数据和函数、计算属性等等
//如果 子组件 增加 expose 选项之后,就只能访问 expose 暴露的属性和函数等等import Vue1 from '@/components/src/test1.vue'
export default {name: 'App',components: { Vue1 },data: () => ({login_vue: null}),methods: {showSonData () {console.log(this.login_vue.account)console.log(this.login_vue.password)this.login_vue.toLogin()}},mounted () {// 打印出来的式子组件的ref对象this.login_vue = this.$refs.loginVueconsole.log(this.login_vue)}
}
</script>

子组件:

<template>账号:<input type="text" v-model="account"><br>密码:<input type="text" v-model="password"><hr><button @click="toLogin">登录</button>
</template><script>
export default {name: 'Vue1',data: () => ({account: 'admin',password: '123456'}),methods: {toLogin () {alert('登录中……')}},//向外暴露属性函数,增加这个选项之后,父组件只能访问该组件暴露的属性或方法等等expose: ['account', 'password']
}
</script><style scoped lang='scss'>
</style>

效果展示:

5.TS中ref数据标注类型

import { ref, Ref } from 'vue'
const ref1: Ref<number> = ref(0);

Ⅱ.reactive 

 1.基本用法:reactive响应式数据

reactive 不能基本类型的数据变为响应式,只适用于引用类型的数据(对象)。

reactive响应式数据由于是 proxy 代理的对象数据,可以直接获取到数据,不必添加 .value,即不论是在模板中还是JS中不需要加.value即可访问或修改reactive响应式数据的值。

const data = reactive({ num: 0});    //只能是引用类型,不能是基本类型
console.log(data.num); // 0

 2.TS中reactive标注类型

传给 reactive 函数的对象类型是什么,就给返回值对应的什么类型即可。

注意:如果这个对象当中又包含了 ref,这个时候 ref 是不需要添加对应的类型的,vue 会自动将其解包。

import { reactive } from 'vue'
const data: { num: number } = reactive({ num: 0});//如果对象中本身就包含了ref时:
import { reactive } from 'vue'
const reactive1: { num: number } = reactive({ num: ref(0)});

Ⅲ.ref和reactive的使用场景和区别

  1. 如果需要一个响应式原始值,那么使用 ref() 是正确的选择,要注意是原始值
  2. 如果需要一个响应式对象,层级不深,那么使用 ref 也可以
  3. 如果需要一个响应式可变对象,并且对象层级较深,需要深度跟踪,那么使用 reactive

Ⅳ.小结&常见疑问解答

1. 为什么在模板中访问 ref 数据不需要加上 .value
在模板中,Vue 会自动解包 ref 数据,所以我们可以直接使用 count 而不是 count.value,但是在 JavaScript 代码中,我们需要使用 .value 来访问 ref 数据的值。

2. 如何在组件之间传递 ref 数据?
可以通过 props 传递 ref 数据。在子组件中,可以使用 defineProps 来接收 ref 数据,并使用 .value 来访问它的值。

3. 如何监听 ref 数据的变化?
可以使用 watch 函数来监听 ref 数据的变化。watch 函数接收两个参数:第一个参数是要监听的数据,第二个参数是一个回调函数,当数据发生变化时,回调函数会被执行。

4. ref 可以用于哪些类型的数据?
ref 可以用于任何类型的 JavaScript 数据,包括基本类型(例如数字、字符串、布尔值)、对象、数组等等。(所以也可以使用ref贯穿始终...)


参考文章

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

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

相关文章

javascript实现雪花飘落效果

本文实现雪花飘落效果的 JavaScript 网页设计案例&#xff0c;代码实现如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, init…

项目准备(flask+pyhon+MachineLearning)- 3

目录 1.商品信息 2. 商品销售预测 2.1 机器学习 2.2 预测功能 3. 模型评估 1.商品信息 app.route(/products) def products():"""商品分析页面"""data load_data()# 计算当前期间和上期间current_period data[data[成交时间] > data[成…

FPGA开发,使用Deepseek V3还是R1(3):系统级与RTL级

以下都是Deepseek生成的答案 FPGA开发&#xff0c;使用Deepseek V3还是R1&#xff08;1&#xff09;&#xff1a;应用场景 FPGA开发&#xff0c;使用Deepseek V3还是R1&#xff08;2&#xff09;&#xff1a;V3和R1的区别 FPGA开发&#xff0c;使用Deepseek V3还是R1&#x…

实现 Leaflet 多类型点位标记与聚合功能的实战经验分享

在现代的地理信息系统&#xff08;GIS&#xff09;应用中&#xff0c;地图功能是不可或缺的一部分。无论是展示商业网点、旅游景点还是公共服务设施&#xff0c;地图都能以直观的方式呈现数据。然而&#xff0c;当数据量较大时&#xff0c;地图上可能会出现大量的标记点&#x…

企微审批中MySQL字段TEXT类型被截断的排查与修复实践

在MySQL中&#xff0c;TEXT类型字段常用于存储较大的文本数据&#xff0c;但在一些应用场景中&#xff0c;当文本内容较大时&#xff0c;TEXT类型字段可能无法满足需求&#xff0c;导致数据截断或插入失败。为了避免这种问题&#xff0c;了解不同文本类型&#xff08;如TEXT、M…

【常见BUG】Spring Boot 和 Springfox(Swagger)版本兼容问题

???欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老…

HTTP 协议的发展历程:从 HTTP/1.0 到 HTTP/2.0

HTTP 协议的发展历程&#xff1a;从 HTTP/1.0 到 HTTP/2.0 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是 Web 的基础协议&#xff0c;用于客户端和服务器之间的通信。从 HTTP/1.0 到 HTTP/2.0&#xff0c;HTTP 协议经历了多次重大改…

apload-lab打靶场

1.提示显示所以关闭js 上传<?php phpinfo(); ?>的png形式 抓包&#xff0c;将png改为php 然后放包上传成功 2.提示说检查数据类型 抓包 将数据类型改成 image/jpeg 上传成功 3.提示 可以用phtml&#xff0c;php5&#xff0c;php3 4.先上传.htaccess文件&#xff0…

金融支付行业技术侧重点

1. 合规问题 第三方支付系统的平稳运营&#xff0c;严格遵循《非银行支付机构监督管理条例》的各项条款是基础与前提&#xff0c;其中第十八条的规定堪称重中之重&#xff0c;是支付机构必须牢牢把握的关键准则。 第十八条明确指出&#xff0c;非银行支付机构需构建起必要且独…

Cherry Studio + 火山引擎 构建个人AI智能知识库

&#x1f349;在信息化时代&#xff0c;个人知识库的构建对于提高工作效率、知识管理和信息提取尤为重要。尤其是当这些知识库能结合人工智能来智能化地整理、分类和管理数据时&#xff0c;效果更为显著。我最近尝试通过 Cherry Studio 和 火山引擎 来搭建个人智能知识库&#…

LeetCode 2 - 两数相加

LeetCode 2 - 两数相加 是一道经典链表操作问题&#xff0c;经常作为面试中基础题的变体被考察。掌握多种解法及其变体&#xff0c;并熟悉其核心思路和模板代码&#xff0c;可以快速备战相关链表或大数计算问题。 题目描述 给定两个非空链表&#xff0c;它们代表两个非负整数&…

Qt之QStateMachine等待

在项目中经常需要等待&#xff0c;我们模拟0-30的数&#xff0c;假如我们其中5&#xff0c; 25的数需要进行等待&#xff0c;等待用户处理完自己事情后&#xff0c;按下按钮继续&#xff0c;找Qt的项目中有一个 QStateMachineqstatemmachine类提供了一个分层有限状态机。 QSta…

elpis全栈课程学习之elpis-core学习总结

elpis全栈课程学习之elpis-core学习总结 核心原理 elpis-core是全栈框架elpis的服务端内核&#xff0c;主要应用于服务端接口的开发以及页面的SSR渲染&#xff0c;elpis-core基于约定优于配置的原理&#xff0c;通过一系列的loader来加载对应的文件&#xff0c;大大节约用户的…

ChatGPT与DeepSeek:开源与闭源的AI模型之争

目录 一、模型架构与技术原理 二、性能能力与应用场景 三、用户体验与部署灵活性 四、成本与商业模式 五、未来展望与市场影响 六、总结 随着人工智能技术的飞速发展&#xff0c;ChatGPT和DeepSeek作为两大领先的AI语言模型&#xff0c;成为了行业内外关注的焦点。它们在…

在笔记本电脑上用DeepSeek搭建个人知识库

最近DeepSeek爆火&#xff0c;试用DeepSeek的企业和个人越来越多。最常见的应用场景就是知识库和知识问答。所以本人也试用了一下&#xff0c;在笔记本电脑上部署DeepSeek并使用开源工具搭建一套知识库&#xff0c;实现完全在本地环境下使用本地文档搭建个人知识库。操作过程共…

DeepSeek蒸馏TinyLSTM实操指南

一、硬件准备 阶段推荐配置最低要求训练阶段NVIDIA A100 80GB 4RTX 3090 24GB 1量化阶段Intel Xeon Gold 6248R CPUi7-12700K + 64GB RAM部署阶段Jetson Xavier NX开发套件Raspberry Pi 4B 8GB二、软件环境搭建 # 创建Python虚拟环境 conda create -n distil python=3.9 conda…

Linux ls 命令

Linux ls&#xff08;英文全拼&#xff1a; list directory contents&#xff09;命令用于显示指定工作目录下之内容&#xff08;列出目前工作目录所含的文件及子目录)。 语法 ls [-alrtAFR] [name...] 参数 : -a 显示所有文件及目录 (. 开头的隐藏文件也会列出)-d 只列出目…

LeetCode 热题 100 53. 最大子数组和

LeetCode 热题 100 | 53. 最大子数组和 大家好&#xff0c;今天我们来解决一道经典的算法题——最大子数组和。这道题在 LeetCode 上被标记为中等难度&#xff0c;要求我们找出一个具有最大和的连续子数组&#xff0c;并返回其最大和。下面我将详细讲解解题思路&#xff0c;并…

【计算机网络入门】初学计算机网络(九)

目录 1.令牌传递协议 2. 局域网&IEEE802 2.1 局域网基本概念和体系结构 3. 以太网&IEEE802.3 3.1 MAC层标准 3.1.1 以太网V2标准 ​编辑 3.2 单播广播 3.3 冲突域广播域 4. 虚拟局域网VLAN 1.令牌传递协议 先回顾一下令牌环网技术&#xff0c;多个主机形成…

Java 大视界 -- Java 大数据中的时间序列数据异常检测算法对比与实践(103)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…