前端八股3---ref和reactive

目录

  • 一、核心区别速览
  • 二、代码演示
  • [1. 数据类型不同](#1. 数据类型不同)
  • [2. 访问方式不同](#2. 访问方式不同)
  • [3. 重新赋值的区别(重要!)](#3. 重新赋值的区别(重要!))
  • [4. 解构的区别](#4. 解构的区别)
  • 三、底层实现原理
  • [reactive 的实现](#reactive 的实现)
  • [ref 的实现(简化版)](#ref 的实现(简化版))
  • 四、使用场景建议
  • [什么时候用 reactive?](#什么时候用 reactive?)
  • [什么时候用 ref?](#什么时候用 ref?)
  • 五、常见误区与注意点
  • [误区1:reactive 的对象可以直接赋值](#误区1:reactive 的对象可以直接赋值)
  • [误区2:ref 在模板中需要 .value](#误区2:ref 在模板中需要 .value)
  • [误区3:ref 传入对象和 reactive 的关系](#误区3:ref 传入对象和 reactive 的关系)
  • 六、面试回答模板(满分答案)

一、核心区别速览

对比维度 reactive ref
数据类型 只能代理对象/数组 可以代理任意类型(基本类型 + 对象)
底层实现 直接使用 Proxy 对象用 reactive,基本类型用 getter/setter
访问方式 直接访问 state.count 需要通过 .value count.value
模板中使用 直接使用 自动解包,不需要 .value
重新赋值 ❌ 会丢失响应式 ✅ 保持响应式
解构 ❌ 会丢失响应式 ✅ 用 toRefs 解构

二、代码演示

1. 数据类型不同

javascript 复制代码
import { reactive, ref } from 'vue';

// reactive:只能用于对象/数组
const state = reactive({ count: 0 });     // ✅
const arr = reactive([1, 2, 3]);          // ✅
const num = reactive(0);                  // ❌ 警告:reactive 不能用于基本类型

// ref:可以用于任意类型
const count = ref(0);                     // ✅ 基本类型
const user = ref({ name: '张三' });        // ✅ 对象
const list = ref([1, 2, 3]);              // ✅ 数组

2. 访问方式不同

javascript 复制代码
const state = reactive({ count: 0 });
state.count++;  // 直接访问

const count = ref(0);
count.value++;  // 需要通过 .value

// 但在模板中,ref 会自动解包
// <template>
//   {{ count }}  ← 不需要 .value
// </template>

3. 重新赋值的区别(重要!)

javascript 复制代码
// ❌ reactive:重新赋值会丢失响应式
let state = reactive({ count: 0 });
state = { count: 1 };  // 丢失响应式!state 不再是代理对象

// ✅ ref:重新赋值保持响应式
let count = ref(0);
count.value = 1;       // 响应式还在
count = ref(10);       // 重新赋值整个 ref,仍然响应式

4. 解构的区别

javascript 复制代码
// ❌ reactive:解构会丢失响应式
const state = reactive({ count: 0, name: '张三' });
const { count, name } = state;
count++;  // 不会触发视图更新,只是普通变量

// ✅ 解决方案:使用 toRefs
const { count, name } = toRefs(state);
count.value++;  // 保持响应式

// ✅ ref:单独使用没问题
const count = ref(0);
let myCount = count;  // 直接赋值引用,保持响应式
myCount.value++;      // 会触发更新

三、底层实现原理

reactive 的实现

javascript 复制代码
function reactive(target) {
  // 只处理对象类型
  if (!isObject(target)) {
    console.warn('reactive 只能用于对象');
    return target;
  }
  
  return new Proxy(target, {
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver);
      // 依赖收集
      track(target, key);
      // 懒代理:如果返回值是对象,递归代理
      if (isObject(res)) {
        return reactive(res);
      }
      return res;
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      // 触发更新
      trigger(target, key);
      return result;
    }
  });
}

ref 的实现(简化版)

javascript 复制代码
function ref(value) {
  // 创建一个对象,带上 .value 属性
  const refObject = {
    _value: value,
    get value() {
      // 收集依赖
      track(refObject, 'value');
      return this._value;
    },
    set value(newVal) {
      this._value = newVal;
      // 触发更新
      trigger(refObject, 'value');
    }
  };
  
  // 如果传入的是对象,用 reactive 包装
  if (isObject(value)) {
    refObject._value = reactive(value);
  }
  
  return refObject;
}

关键点:

  • ref 本质是创建了一个有 .value 属性的对象

  • 基本类型:通过 getter/setter 实现响应式

  • 对象类型:内部调用 reactive 处理


四、使用场景建议

什么时候用 reactive?

javascript 复制代码
// ✅ 适合:数据结构明确、属性会变化的对象
const state = reactive({
  user: { name: '张三', age: 18 },
  list: [1, 2, 3]
});

// ✅ 适合:表单数据
const form = reactive({
  username: '',
  password: '',
  email: ''
});

什么时候用 ref?

javascript 复制代码
// ✅ 适合:基本类型数据
const count = ref(0);
const message = ref('');
const isLoading = ref(false);

// ✅ 适合:需要整体替换的数据
const user = ref(null);
// 后来获取数据后整体赋值
user.value = { name: '张三', age: 18 };

// ✅ 适合:不确定类型的数据
const data = ref(null);  // 可能是对象、数组、基本类型

五、常见误区与注意点

误区1:reactive 的对象可以直接赋值

javascript 复制代码
// ❌ 错误
let state = reactive({ count: 0 });
state = { count: 1 };  // 丢失响应式

// ✅ 正确:修改属性,不要重新赋值
state.count = 1;

// ✅ 或者用 Object.assign
Object.assign(state, { count: 1 });

误区2:ref 在模板中需要 .value

javascript 复制代码
// ❌ 错误理解
// <template>{{ count.value }}</template>  ← 不需要!

// ✅ 正确
// <template>{{ count }}</template>  ← 自动解包

误区3:ref 传入对象和 reactive 的关系

javascript 复制代码
const objRef = ref({ name: '张三' });
// objRef.value 实际上是一个 reactive 代理对象

console.log(objRef.value === reactive({ name: '张三' })); // false
// 但效果相同,都是响应式的

六、面试回答模板(满分答案)

问:reactive 和 ref 的区别是什么?

回答:

主要有以下几点区别:

  1. 数据类型reactive 只能代理对象和数组,ref 可以代理任意类型(包括基本类型)。

  2. 底层实现reactive 直接使用 Proxy 实现响应式;ref 内部创建一个带有 .value 属性的对象,基本类型通过 getter/setter 实现,对象类型则内部调用 reactive

  3. 访问方式reactive 的数据可以直接访问,ref 需要通过 .value 访问(模板中自动解包,不需要 .value)。

  4. 重新赋值reactive 重新赋值整个对象会丢失响应式,ref 重新赋值 .value 或整个 ref 都能保持响应式。

  5. 解构reactive 直接解构会丢失响应式,需要配合 toRefs 使用;ref 解构后仍保持响应式。

使用建议 :对象/表单数据用 reactive,基本类型和需要整体替换的数据用 ref

相关推荐
SWAGGY..2 小时前
【C++初阶】:(7)STL简介
开发语言·c++
落魄江湖行2 小时前
基础篇三 Nuxt4 组件进阶:插槽与事件传递
前端·nuxt4
kerli2 小时前
Compose 组件:LazyColumn 核心参数与 key/contentType 详解
android·前端
好运的阿财2 小时前
“锟斤拷”问题——程序中用powershell执行命令出现中文乱码的解决办法
linux·前端·人工智能·机器学习·架构·编辑器·vim
dog2502 小时前
卡瓦列里积分赏析
开发语言·php
流星蝴蝶没有剑2 小时前
CoPaw Agent 对接 Python 客户端开发指南:实现流式响应与实时打印
开发语言·python
踩着两条虫2 小时前
VTJ.PRO AI + 低代码实战:接入高德地图
前端·vue.js·ai编程
绝世唐门三哥2 小时前
React性能优化:memo、useMemo和useCallback全解析
前端·react.js·memo
咬_咬2 小时前
go语言学习(数组与切片)
开发语言·学习·golang·数组·切片