当前位置: 首页 > news >正文

vue3中使用reactive定义的变量响应式丢失问题(大坑!!!)

前言

        在Vue 3中,可以使用reactive函数将普通JavaScript对象转换为响应式对象,这样当对象的属性发生变化时,就会自动更新相应的UI。

但是请注意以下情况可能会丢失数据的响应式:

响应式丢失的情况:

1、对使用reactive 函数定义的变量直接赋值

<script setup>
​import { reactive } from 'vue';// 定义一个响应式变量
const data = reactive ({name:"",age:""
});// 请求接口
axios.get('/api/data').then(res => {// 直接赋值data = res.data;}).catch(err => console.log(err));​</script>

解决方法如下:

1、逐个属性进行赋值(不推荐!!!)
​<script setup>
​import { reactive } from 'vue';// 定义一个响应式变量
const data = reactive ({name:"",age:""
}});// 请求接口
axios.get('/api/data').then(res => {// 逐个属性赋值 不推荐data.name= res.data.name;data.age= res.data.age;}).catch(err => console.log(err));​</script>
2、改用ref(最简单) 简单数据类型使用ref()来进行定义。
​<script setup>
​import { ref } from 'vue';// 定义一个响应式变量
const data = ref ({name:"",age:""
});// 请求接口
axios.get('/api/data').then(res => {// 更新响应式变量的值data.value = res.data;}).catch(err => console.log(err));​</script>
​

        上述代码中,data变量通过ref函数定义为响应式变量,它的值是一个对象。当请求接口完成时,将响应的数据赋值给data.value,就会自动更新相应的UI。

3.直接在reactive中嵌套一层
​<script setup>
​import { reactive } from 'vue';// 定义一个响应式变量
const data = reactive ({dataObj:{name:"",age:""}
});// 请求接口
axios.get('/api/data').then(res => {// 嵌套一层 dataArrdata.dataObj= res.data;}).catch(err => console.log(err));​</script>

       使用reactive函数将data转换为响应式对象。这样在后续更新data对象的dataObj属性时,,就会自动更新相应的UI。

4、如果有ts类型限制可以写类(TS对reactive里对象进行限制)

单独拿出来一个ts文件,比如user.ts

//1.定义限制userData的接口
export interface userInfo{name:string,age:number
}
//写类
export class data{//定义userData并且做TS限制和赋初始值userData:userInfo = {name:"",age:""}
}

在对应的.vue文件中引入该类。

//1.引入刚写好ts类文件
import {userInfo,data} from "@/type/user.ts"
//2.重点来了,我实例化出来data,然后用一个变量User接收。
let User=reactive(new data());
/*
//实例化出来以后相当于这样的结构:
User={userInfo:{name:"",age:""}
}
*/
//3.获取接口数据
axios.get('/api/data').then(res => {// 更新响应式变量的值User.userData=res.data;//将返回的结果赋值给data,这样也不会丢失响应式,并且userData也受了TS的限制。}).catch(err => console.log(err));

2、解构赋值引起响应式数据丢失

在Vue中,使用reactive定义变量时,需要注意解构赋值的情况。如果在解构赋值中使用reactive定义的变量,会导致数据丢失,因为解构赋值会创建一个新的引用,而不是原始对象。因此,我们应该避免在解构赋值中使用reactive定义的变量,或者使用拷贝或者toRefs来避免数据丢失。

​<script setup>
import { reactive } from 'vue';// 定义一个响应式变量
const data = reactive ({name:"码农键盘上的梦",age:"99"
})// 解构了  响应式也丢了
let { name } = data; //解构赋值</script>

以下是几种解决方法:

1.直接访问reactive定义的变量,而不是使用解构赋值;
2.使用toRefs方法将响应式对象转化为普通对象的响应式属性;
​<script setup>
import { reactive, toRefs } from 'vue'// 定义一个响应式变量
const data = reactive ({name:"码农键盘上的梦",age:"99"
})// 使用toRefs解决
const { name, age} = toRefs(data)</script>

这种方法使用toRefs方法将响应式对象转化为普通对象的响应式属性是较为常用的方法。

3.在解构赋值时使用拷贝来避免数据丢失;
<script setup>
import { reactive, toRefs } from 'vue'// 定义一个响应式变量
const data = reactive ({name:"码农键盘上的梦",age:"99"
})// 使用拷贝解决
const { name:nameCopy , age:ageCopy } = { ...data }
console.log(nameCopy , ageCopy)</script>

3、原理:

        1.ref 定义数据(包括对象)时,都会变成 RefImpl(Ref 引用对象) 类的实例,无论是修改还是重新赋值都会调用 setter,都会经过 reactive 方法处理为响应式对象。

        2.但是 reactive 定义数据(必须是对象),是直接调用 reactive 方法处理成响应式对象。如果重新赋值,就会丢失原来响应式对象的引用地址,变成一个新的引用地址,这个新的引用地址指向的对象是没有经过 reactive 方法处理的,所以是一个普通对象,而不是响应式对象。解构同理。

附:官方文档对reactive的解读

reactive() API 有一些局限性:

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

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

    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。


注:未经允许,不可转载!

http://www.lryc.cn/news/234521.html

相关文章:

  • Windows Server 2012 R2系统服务器远程桌面服务多用户登录配置分享
  • mysql之搭建MHA架构实现高可用
  • Databend 与海外某电信签约:共创海外电信数据仓库新纪元
  • scala解析命令行参数
  • 盘点60个Python各行各业管理系统源码Python爱好者不容错过
  • SpringSecurity6 | 自动配置(下)
  • 6、传统CV之均值滤波
  • 快速搭建本地的chatgpt
  • 分布式下多节点WebSocket消息收发
  • LeetCode算法题解(动态规划)|LeetCode509. 斐波那契数、LeetCode70. 爬楼梯、LeetCode746. 使用最小花费爬楼梯
  • 【图像处理】:Otsu算法最大类间方差法(大津算法:附源码)
  • 【uni-app】设置背景颜色相关
  • 工厂模式-C++实现
  • 安装应用与免安装应用差异对比
  • FiscoBcos使用Go调用合约
  • 自然语言处理(NLP)-spacy简介以及安装指南(语言库zh_core_web_sm)
  • CTF-PWN-tips
  • 《Effective C++》条款21
  • 决策树,sql考题,30个经典sql题目
  • 【ES6.0】- 扩展运算符(...)
  • 关于Java中的深拷贝与浅拷贝
  • 13.真刀实枪做项目---博客系统(页面设计)
  • VScode 配置用户片段
  • Fedora 项目近日发布了 Fedora Linux 39
  • Uniapp连接iBeacon设备——实现无线定位与互动体验(理论篇)
  • GCD:异步同步?串行并发?一文轻松拿捏!
  • 学习c#的第十七天
  • 龙芯 操作系统选择和安装
  • 【开源】基于JAVA的智能停车场管理系统
  • 使用IDEA 将Eclipse java工程转为maven格式