写在前头:
reactive定义的数据只能修改里面的属性,不能将整个数据替换,实在要替换请使用
 

Object.assign(obj1, obj2);

举个例子
这种写法无法直接改变obj1

let obj1 = reactive({
	name: '猫',
	age: '2',
});

obj1 = {
	name: '猪',
	age: '2',
}

正确的写法
 

let obj1 = reactive({
	name: '猫',
	age: '2',
});
Object.assign(obj1, {
	name: '猪',
	age: '2',
	});

或者逐个更改属性
 

let obj1 = reactive({
	name: '猫',
	age: '2',
});
obj1.name = '狗'
	obj1.age = '3'

言归正传:

Vue 3 中,refreactive 是管理响应式数据的两种主要方式。它们的使用场景、工作原理以及各自的优缺点有所不同。以下是它们的用法、区别、优缺点及推荐使用场景的总结。

1. ref 用法

ref 用于定义基本类型或复杂类型的响应式数据。它适合处理基本数据类型(如数字、字符串、布尔值),也可用于对象和数组。

用法示例
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0); // 基本数据类型
    const name = ref('Vue3'); // 字符串
    const user = ref({ name: 'Alice', age: 25 }); // 对象

    const increment = () => {
      count.value++;  // 通过 .value 访问或修改 ref 包裹的值
    };

    return { count, name, user, increment };
  }
};
优点
  • 适合基本数据类型:对于简单的数据类型(如数字、字符串、布尔值),ref 是最佳选择。
  • 可以封装任何类型的数据:不仅限于基本类型,还可以封装对象、数组等复杂类型。
  • 引用式包装:即便是对象或数组,在使用 .value 时也能获取和修改它们的内容。
缺点
  • 需要通过 .value 访问:必须通过 ref.value 的形式访问和更新数据,初学者有时容易忘记或感到不便。
使用场景
  • 简单的原始数据类型:数字、字符串、布尔值等。
  • 需要手动包装非响应式对象:比如希望将一个对象显式地封装为响应式对象。
  • HTML元素引用:通过 ref 还能获取 DOM 元素的引用,比如在模板中操作具体的 DOM。

2. reactive 用法

reactive 用于将对象或数组转换为响应式数据。它适合处理复杂数据结构(如对象、数组等)。

用法示例
import { reactive } from 'vue';

export default {
  setup() {
    const user = reactive({
      name: 'Alice',
      age: 25,
      hobbies: ['reading', 'coding']
    });

    const incrementAge = () => {
      user.age++;  // 直接修改对象属性,无需 .value
    };

    return { user, incrementAge };
  }
};
优点
  • 直观的语法:不需要 .value,可以像操作普通对象一样直接访问和修改属性,写法更加简洁。
  • 深度响应式reactive 会对对象的所有嵌套属性进行深度监听,无需手动处理嵌套数据。
缺点
  • 只能用于对象和数组reactive 不能用于基本数据类型,如数字、字符串等。
  • 不能直接解构reactive 对象不能直接解构,否则解构后的属性将失去响应式能力。
使用场景
  • 复杂的数据结构:适合对对象、数组、嵌套数据进行响应式管理。
  • 更符合面向对象的数据组织:需要管理对象之间的层级和属性关系时,reactive 更加自然。

3. refreactive 的区别

特性

ref

reactive

适用场景

基本数据类型、DOM引用、复杂类型封装

对象、数组、嵌套数据

访问方式

必须通过 .value 来访问和修改值

直接通过对象属性访问和修改

响应式行为

对于对象或数组,只有包裹的整体是响应式的,内部属性需要手动用 ref

深度响应式,对所有嵌套属性进行监听

能否解构

ref 可以解构(解构后仍然需 .value

解构后失去响应式能力

语法简洁性

访问属性时需要使用 .value

操作对象时无需额外 .value


4. 优缺点对比

ref 的优缺点
  • 优点
  • 可以用于任何类型(基本数据类型、对象、数组)。
  • DOM 元素引用方便,常用于操作 template 中的元素。
  • 缺点
  • 必须使用 .value 访问和修改数据,稍显冗余。
  • 对于复杂对象或数组,只能将其整体变为响应式,内部属性需要手动处理。
    具体解释: 在 Vue 3 中,ref 可以将基本数据类型(如数字、字符串)以及复杂类型(如对象、数组)变为响应式。但当我们使用 ref 包装复杂对象或数组时,ref 只能将整个对象或数组视为一个整体变为响应式,如果你需要对该对象的内部属性进行精细的响应式处理,可能需要手动处理内部属性的响应式特性。

ref 对复杂对象或数组的处理

当你用 ref 来处理复杂对象(如对象、数组)时,虽然这个复杂对象是响应式的,但在修改对象内部的某些属性时,不会自动追踪响应。这意味着当我们需要对复杂对象的内部属性进行修改和响应式更新时,可能需要手动将这些属性声明为 ref 或者通过 .value 来显式更新。

  • 示例:ref 包裹对象时的响应式行为
import { ref } from 'vue';

export default {
  setup() {
    const user = ref({
      name: 'Alice',
      age: 25
    });

    const updateName = () => {
      user.value.name = 'Bob';  // 修改 user 对象内部属性
    };

    return { user, updateName };
  }
};

在上面的示例中,user 是一个对象,并被 ref 包裹。当我们通过 user.value.name = 'Bob' 修改内部属性时,Vue 能够正确追踪到变化,因为我们通过 .value 明确地修改了整个对象的值。

问题:无法直接响应复杂对象内部的深层嵌套属性

假如我们有一个更复杂的嵌套对象,直接修改嵌套属性并不会触发响应式更新。这种情况下,需要显式地通过 .valueref 来处理内部嵌套属性。

  • 示例:复杂嵌套对象需要手动处理的情况
import { ref } from 'vue';

export default {
  setup() {
    const user = ref({
      name: 'Alice',
      details: {
        age: 25,
        address: {
          city: 'New York'
        }
      }
    });

    const updateCity = () => {
      user.value.details.address.city = 'Los Angeles'; // 可能无法正确触发响应式更新
    };

    return { user, updateCity };
  }
};

在这个示例中,我们将 addressref 包裹,这样内部的 city 属性在被修改时,Vue 就能够自动追踪并更新视图。

总结

  • ref 用于包装复杂对象时,整个对象是响应式的,但对象内部的嵌套属性修改可能不会自动触发响应式更新。
  • 如果需要对复杂对象或数组的内部属性进行精细化的响应式处理,应该手动将这些属性用 ref 包裹,或者通过 .value 来显式更新嵌套属性的值。
  • 这种情况提醒我们在使用 ref 包裹复杂数据时,要意识到它的局限性,并根据需求选择 refreactive 来管理响应式数据。
  • 对于简单数据类型,ref 是最直接和方便的选择;但在处理复杂对象时,特别是对象的嵌套属性时,可能需要更复杂的手动处理。
reactive 的优缺点
  • 优点
  • 语法更加直观简洁,操作时不需要 .value
  • 深度响应式,适合管理嵌套的复杂数据。
  • 缺点
  • 仅限于对象和数组,无法处理基本数据类型。
  • 不能直接解构对象,否则解构后的属性将失去响应式能力。

5. 推荐的使用场景

使用 ref 的场景
  • 处理基本数据类型:如 numberbooleanstring,可以使用 ref 来确保这些类型是响应式的。
  • 简单的 DOM 引用:在 Vue 中通过 ref 访问 DOM 元素或组件实例。
  • 包装单个值:当你需要管理基本数据类型的响应式数据时,ref 是最合适的。
  • 复杂对象需要显式封装为响应式时:如果你希望将一个对象或数组显式地转换为响应式数据,而不使用 reactive 的深度响应式特性。
使用 reactive 的场景
  • 处理复杂对象或数组:当你需要管理包含多个属性的对象或数组时,reactive 是最佳选择,因为它能够自动处理嵌套的属性和深度响应。
  • 需要更自然的对象操作:对于那些需要频繁操作属性的对象,reactive 提供更符合直觉的语法,不需要使用 .value
  • 表单数据或大规模数据操作:当管理用户表单或需要管理复杂的数据结构(如多个嵌套属性对象)时,reactive 更加灵活高效。

6. 组合使用的场景

在一些场景下,你可以同时使用 refreactive,例如你需要处理一个对象的深度响应式属性,但其中的某些属性需要用 ref 封装以实现特定的逻辑。

示例:refreactive 组合使用

import { ref, reactive } from 'vue';

export default {
  setup() {
    const count = ref(0);  // 处理简单的基本数据类型
    const user = reactive({ name: 'Alice', age: ref(25) });  // 组合使用,user 是响应式对象,age 使用 ref 单独封装

    const incrementAge = () => {
      user.age.value++;  // 需要用 .value 修改 ref 类型的属性
    };

    return { count, user, incrementAge };
  }
};

总结

  • ref:适合处理基本类型(数字、字符串、布尔值等)和需要显式封装的对象/数组。使用 .value 访问和修改。
  • reactive:适合处理复杂对象和数组,并且会对所有嵌套属性进行深度响应式管理,语法上更符合直觉。