前端八股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。

相关推荐
用户461169657282 分钟前
网站 HTTPS 不稳定?证书排查指南
前端
n876513 分钟前
Playwright Java WEB UI 自动化实现框架与逻辑
java·前端·ui
keyipatience15 分钟前
TypeScript学习(1)
前端·javascript·学习·typescript·html·css3
爱喝热水的呀哈喽29 分钟前
HEJIAN RESEARCH
开发语言·matlab
白露与泡影36 分钟前
后端 Java 面试核心八股汇总|高频面试题 + 解析,覆盖基础到进阶
java·开发语言·面试
xifangge202544 分钟前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
“AI国潮设计-小江”1 小时前
【AIGC实战】Python+SDXL批量生成「潮汕英歌舞冰淇淋蛋糕&红桃粿咖啡」IP,固定角色形象批量出甜品商业效果图
开发语言·人工智能·python·prompt·aigc
wangruofeng1 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
\光辉岁月/1 小时前
3.包装类
java·开发语言·包装类
honkun61 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js