在Vue 3中,可以使用reactive函数将普通JavaScript对象转换为响应式对象,这样当对象的属性发生变化时,就会自动更新相应的UI。
但是请注意以下情况可能会丢失数据的响应式:
- import { reactive } from 'vue';
-
- // 定义一个响应式变量
- const data = reactive ({
- name:"",
- age:""
- });
-
- // 请求接口
- axios.get('/api/data')
- .then(res => {
- // 直接赋值
- data = res.data;
- })
- .catch(err => console.log(err));
-
-
解决方法如下:
-
- 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));
-
-
-
- import { ref } from 'vue';
-
- // 定义一个响应式变量
- const data = ref ({
- name:"",
- age:""
- });
-
- // 请求接口
- axios.get('/api/data')
- .then(res => {
- // 更新响应式变量的值
- data.value = res.data;
- })
- .catch(err => console.log(err));
-
-
-
上述代码中,data变量通过ref函数定义为响应式变量,它的值是一个对象。当请求接口完成时,将响应的数据赋值给data.value,就会自动更新相应的UI。
-
- import { reactive } from 'vue';
-
- // 定义一个响应式变量
- const data = reactive ({
- dataObj:{
- name:"",
- age:""
- }
- });
-
- // 请求接口
- axios.get('/api/data')
- .then(res => {
- // 嵌套一层 dataArr
- data.dataObj= res.data;
- })
- .catch(err => console.log(err));
-
-
使用reactive函数将data转换为响应式对象。这样在后续更新data对象的dataObj属性时,,就会自动更新相应的UI。
单独拿出来一个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));
在Vue中,使用reactive定义变量时,需要注意解构赋值的情况。如果在解构赋值中使用reactive定义的变量,会导致数据丢失,因为解构赋值会创建一个新的引用,而不是原始对象。因此,我们应该避免在解构赋值中使用reactive定义的变量,或者使用拷贝或者toRefs来避免数据丢失。
-
- import { reactive } from 'vue';
-
- // 定义一个响应式变量
- const data = reactive ({
- name:"码农键盘上的梦",
- age:"99"
- })
-
- // 解构了 响应式也丢了
- let { name } = data; //解构赋值
-
以下是几种解决方法:
reactive定义的变量,而不是使用解构赋值;toRefs方法将响应式对象转化为普通对象的响应式属性;-
- import { reactive, toRefs } from 'vue'
-
-
- // 定义一个响应式变量
- const data = reactive ({
- name:"码农键盘上的梦",
- age:"99"
- })
-
- // 使用toRefs解决
- const { name, age} = toRefs(data)
-
这种方法使用toRefs方法将响应式对象转化为普通对象的响应式属性是较为常用的方法。
- import { reactive, toRefs } from 'vue'
-
-
- // 定义一个响应式变量
- const data = reactive ({
- name:"码农键盘上的梦",
- age:"99"
- })
-
- // 使用拷贝解决
- const { name:nameCopy , age:ageCopy } = { ...data }
- console.log(nameCopy , ageCopy)
-
1.ref 定义数据(包括对象)时,都会变成 RefImpl(Ref 引用对象) 类的实例,无论是修改还是重新赋值都会调用 setter,都会经过 reactive 方法处理为响应式对象。
2.但是 reactive 定义数据(必须是对象),是直接调用 reactive 方法处理成响应式对象。如果重新赋值,就会丢失原来响应式对象的引用地址,变成一个新的引用地址,这个新的引用地址指向的对象是没有经过 reactive 方法处理的,所以是一个普通对象,而不是响应式对象。解构同理。
reactive() API 有一些局限性:
有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。
不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:
- let state = reactive({ count: 0 })
-
- // 上面的 ({ count: 0 }) 引用将不再被追踪
- // (响应性连接已丢失!)
- state = reactive({ count: 1 })
对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
js- const state = reactive({ count: 0 })
-
- // 当解构时,count 已经与 state.count 断开连接
- let { count } = state
- // 不会影响原始的 state
- count++
-
- // 该函数接收到的是一个普通的数字
- // 并且无法追踪 state.count 的变化
- // 我们必须传入整个对象以保持响应性
- callSomeFunction(state.count)
由于这些限制,我们建议使用 ref() 作为声明响应式状态的主要 API。
注:未经允许,不可转载!