Vue 响应式原理深度解析:Vue2 vs Vue3 核心差异 + ref/reactive 实战指南

引言:为什么响应式是 Vue 的 "灵魂"?

作为 Vue 开发者,你一定对这样的场景习以为常:

  • 修改data里的变量,页面 DOM 自动同步更新;
  • 表单输入时,绑定的响应式数据实时变化;
  • 数组push新元素后,列表自动渲染新增项。

这背后的 "魔法",正是 Vue 的响应式系统------ 它让数据与视图建立 "自动关联",开发者无需手动操作 DOM,只需关注数据逻辑,极大提升开发效率。

但你是否好奇:

  • 数据变化时,Vue 是如何 "感知" 到的?
  • 为什么 Vue2 中给对象新增属性需要Vue.set,而 Vue3 不需要?
  • refreactive到底该怎么选?它们的底层逻辑是什么?

本文将从 "原理拆解→版本对比→实战应用→避坑指南" 四个维度,彻底讲透 Vue 响应式的核心逻辑:先剖析 Vue2 的Object.defineProperty实现,再详解 Vue3 的Proxy升级方案,最后结合ref/reactive的实战场景,让你不仅 "会用",更能 "懂原理"。

1. 响应式的核心本质:数据劫持 + 依赖收集

在深入版本差异前,我们先明确响应式的核心目标:当数据发生变化时,自动触发依赖该数据的视图更新或逻辑执行

要实现这个目标,需要解决两个关键问题:

  1. 数据劫持:如何 "监听" 数据的读写操作(比如修改属性值、数组新增元素)?
  2. 依赖收集:如何记录 "哪些视图 / 逻辑依赖了这个数据"?

这就像一个 "订阅 - 发布" 系统:

  • 数据是 "发布者",视图 / 逻辑是 "订阅者";
  • 数据劫持负责 "监听发布者的动作"(比如数据被修改);
  • 依赖收集负责 "记录订阅者列表";
  • 当数据变化时,发布者通知所有订阅者执行更新。

Vue2 和 Vue3 的响应式系统,本质上都是围绕这两个核心问题展开,只是数据劫持的实现方式不同------ 这也是两者差异的根源。

2. Vue2 响应式原理:Object.defineProperty 的 "功与过"

Vue2 的响应式核心是Object.defineProperty(以下简称 "defineProperty"),这是 ES5 提供的 API,用于劫持对象的单个属性,实现对属性读写的监听。

2.1 核心原理:劫持属性 + Dep/Watcher 机制

2.1.1 第一步:用 defineProperty 劫持对象属性

Vue2 会遍历data中的所有对象属性,通过defineProperty重写gettersetter方法:

  • getter:当属性被访问时触发,用于 "收集依赖"(记录哪些地方用到了这个属性);
  • setter:当属性被修改时触发,用于 "派发更新"(通知所有依赖该属性的地方执行更新)。
2.1.2 第二步:Dep(依赖收集容器)

每个被劫持的属性,都会对应一个Dep实例(可以理解为 "订阅者列表"):

  • DepaddSub方法:将依赖(Watcher 实例)加入列表;
  • Depnotify方法:遍历列表,通知所有 Watcher 执行更新。
2.1.3 第三步:Watcher(依赖订阅者)

Watcher是 "依赖的具体载体",每个视图组件、计算属性、watch监听,都会对应一个Watcher实例:

  • 当组件渲染时,会触发属性的getter,将当前Watcher加入属性的Dep列表;
  • 当属性被修改时,触发setter,调用Dep.notify(),所有Watcher会执行update方法,触发组件重新渲染或watch回调。

2.2 Vue2 响应式完整流程

2.3 代码示例:手动实现 Vue2 响应式核心

javascript 复制代码
// 1. 依赖收集容器:Dep
class Dep {
  constructor() {
    this.subscribers = []; // 存储依赖(Watcher实例)
  }

  // 添加依赖
  addSub(watcher) {
    this.subscribers.push(watcher);
  }

  // 通知所有依赖更新
  notify() {
    this.subscribers.forEach(watcher => watcher.update());
  }
}

// 2. 订阅者:Watcher
class Watcher {
  constructor(updateFn) {
    this.updateFn = updateFn; // 依赖更新时执行的函数
  }

  // 执行更新
  update() {
    this.updateFn();
  }
}

// 3. 响应式处理函数:劫持对象属性
function observe(obj) {
  // 只处理对象类型
  if (typeof obj !== 'object' || obj === null) return;

  // 遍历对象所有属性
  Object.keys(obj).forEach(key => {
    let value = obj[key];
    const dep = new Dep(); // 每个属性对应一个Dep

    // 重写getter/setter
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get() {
        // 访问属性时,收集依赖(此时Watcher已被激活)
        Dep.target && dep.addSub(Dep.target);
        return value;
      },
      set(newValue) {
        if (newValue === value) return;
        value = newValue;
        // 修改属性时,通知依赖更新
        dep.notify();
      }
    });

    // 递归处理嵌套对象
    observe(value);
  });
}

// 4. 测试:模拟Vue2响应式
const data = { name: 'Vue2', age: 8 };
observe(data);

// 创建Watcher(模拟组件渲染逻辑)
Dep.target = new Watcher(() => {
  console.log(`视图更新:name=${data.name}, age=${data.age}`);
});
// 首次访问属性,触发getter,收集依赖
console.log(data.name); // 触发getter,Dep收集Watcher
Dep.target = null;

// 修改属性,触发setter,通知更新
data.name = 'Vue2响应式'; // 输出:视图更新:name=Vue2响应式, age=8
data.age = 9; // 输出:视图更新:name=Vue2响应式, age=9

2.4 Vue2 响应式的 "致命缺陷"(实战痛点)

虽然defineProperty实现了响应式核心,但在实际开发中,它的设计限制带来了诸多痛点:

2.4.1 1. 无法监听对象新增 / 删除属性

defineProperty只能劫持已存在的属性 ,如果给对象新增属性(比如data.user = { name: '张三' }),或删除属性(delete data.name),不会触发getter/setter,导致响应式失效。

javascript 复制代码
// Vue2中新增属性,视图不更新
const vm = new Vue({
  data() {
    return { user: { age: 20 } };
  }
});

vm.user.name = '张三'; // 新增属性,视图不更新
delete vm.user.age; // 删除属性,视图不更新

// 解决方案:使用Vue.set/Vue.delete
this.$set(this.user, 'name', '张三');
this.$delete(this.user, 'age');
2.4.2 2. 无法监听数组的 "部分操作"

Vue2 对数组的pushpopshiftunshiftsplicesortreverse7 种方法进行了 "重写",可以触发响应式。但以下场景仍无法监听:

  • 直接修改数组索引:arr[0] = 10
  • 直接修改数组长度:arr.length = 0
javascript 复制代码
// Vue2中数组操作的坑
const vm = new Vue({
  data() {
    return { list: [1, 2, 3] };
  }
});

vm.list[0] = 10; // 直接修改索引,视图不更新
vm.list.length = 0; // 直接修改长度,视图不更新

// 解决方案:用重写的方法或Vue.set
vm.list.splice(0, 1, 10); // 可用
this.$set(vm.list, 0, 10); // 可用
2.4.3 3. 嵌套对象需要递归劫持,性能开销大

Vue2 需要遍历对象的所有属性,并且对嵌套对象进行递归劫持(比如data.user.address.city)。如果对象层级过深、属性过多,会导致初始化时的性能损耗。

2.4.4 4. 只能劫持对象,无法劫持原始值

defineProperty只能作用于对象的属性 ,无法直接劫持numberstringboolean等原始值。Vue2 通过将原始值包装在data对象中(比如data: { count: 0 })间接实现响应式。

3. Vue3 响应式原理:Proxy 的 "全方位升级"

为了解决 Vue2 的痛点,Vue3 放弃了defineProperty,转而采用 ES6 的Proxy(代理)API,配合Reflect(反射),实现了更强大、更灵活的响应式系统。

3.1 核心原理:代理对象 + effect 依赖收集

3.1.1 第一步:Proxy 代理整个对象

Proxy的核心优势是直接代理整个对象,而非单个属性:

  • 无需遍历对象属性(初始化性能更优);
  • 能监听对象的所有操作(新增属性、删除属性、数组索引修改等);
  • 支持嵌套对象的自动代理(访问嵌套属性时才递归代理,懒加载模式)。

Proxy的常用拦截器(针对响应式):

  • get(target, key):拦截属性访问(对应 Vue2 的getter,用于收集依赖);
  • set(target, key, value):拦截属性修改 / 新增(对应 Vue2 的setter,用于派发更新);
  • deleteProperty(target, key):拦截属性删除(Vue2 无法监听)。
3.1.2 第二步:Reflect 反射 API

Reflect是 ES6 提供的内置对象,用于 "反射" 对象的操作,与Proxy配合使用:

  • 替代Object的方法(比如Reflect.get(target, key)等价于target[key]);
  • 统一返回操作结果(比如Reflect.set返回boolean,表示修改是否成功);
  • 避免直接操作目标对象,更安全、更规范。
3.1.3 第三步:effect(依赖收集核心)

Vue3 用effect函数替代了 Vue2 的Watcher,核心作用是:

  • 执行一个函数(比如组件渲染函数、watch回调);
  • 在函数执行过程中,收集依赖(记录哪些响应式数据被访问);
  • 当响应式数据变化时,重新执行这个函数。

3.2 Vue3 响应式完整流程

3.3 代码示例:手动实现 Vue3 响应式核心

javascript 复制代码
// 1. 依赖收集:存储target(目标对象)→ key(属性)→ effects(依赖集合)
const targetMap = new WeakMap();

// 2. 收集依赖:将effect加入对应key的依赖集合
function track(target, key) {
  // 没有当前effect,直接返回
  if (!activeEffect) return;

  // 目标对象的依赖映射(target → Map(key → Set(effects)))
  let depsMap = targetMap.get(target);
  if (!depsMap) targetMap.set(target, (depsMap = new Map()));

  // 属性的依赖集合(key → Set(effects))
  let dep = depsMap.get(key);
  if (!dep) depsMap.set(key, (dep = new Set()));

  // 将当前effect加入依赖集合
  dep.add(activeEffect);
}

// 3. 派发更新:执行所有依赖的effect
function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;

  const dep = depsMap.get(key);
  if (dep) {
    // 执行所有effect
    dep.forEach(effect => effect());
  }
}

// 4. 全局变量:存储当前活跃的effect
let activeEffect = null;

// 5. 依赖函数:effect
function effect(fn) {
  // 包装effect函数
  const effectFn = () => {
    activeEffect = effectFn; // 激活当前effect
    fn(); // 执行回调,触发属性访问,收集依赖
    activeEffect = null; // 重置
  };

  effectFn(); // 首次执行
  return effectFn;
}

// 6. 响应式核心:reactive(基于Proxy)
function reactive(target) {
  // 只处理对象/数组类型
  if (typeof target !== 'object' || target === null) return target;

  // 创建Proxy代理
  return new Proxy(target, {
    // 拦截属性访问
    get(target, key, receiver) {
      const result = Reflect.get(target, key, receiver);
      track(target, key); // 收集依赖
      // 递归代理嵌套对象(懒加载)
      return reactive(result);
    },
    // 拦截属性修改/新增
    set(target, key, value, receiver) {
      const oldValue = Reflect.get(target, key, receiver);
      const success = Reflect.set(target, key, value, receiver);
      // 只有值变化时才触发更新
      if (success && oldValue !== value) {
        trigger(target, key); // 派发更新
      }
      return success;
    },
    // 拦截属性删除
    deleteProperty(target, key) {
      const success = Reflect.deleteProperty(target, key);
      if (success) {
        trigger(target, key); // 派发更新
      }
      return success;
    }
  });
}

// 7. 测试:模拟Vue3响应式
const data = reactive({ name: 'Vue3', age: 5, list: [1, 2, 3] });

// 创建effect(模拟组件渲染)
effect(() => {
  console.log(`视图更新:name=${data.name}, age=${data.age}, list=[${data.list}]`);
});

// 1. 修改已存在属性
data.name = 'Vue3响应式'; // 输出:视图更新:name=Vue3响应式, age=5, list=[1,2,3]

// 2. 新增属性(Vue2不支持)
data.gender = 'male'; // 输出:视图更新:name=Vue3响应式, age=5, list=[1,2,3](gender默认undefined)

// 3. 删除属性(Vue2不支持)
delete data.age; // 输出:视图更新:name=Vue3响应式, age=undefined, list=[1,2,3]

// 4. 修改数组索引(Vue2不支持)
data.list[0] = 10; // 输出:视图更新:name=Vue3响应式, age=undefined, list=[10,2,3]

// 5. 修改数组长度(Vue2不支持)
data.list.length = 2; // 输出:视图更新:name=Vue3响应式, age=undefined, list=[10,2]

3.4 Vue3 响应式的 "核心优势"(解决 Vue2 痛点)

3.4.1 1. 支持对象新增 / 删除属性

Proxyset拦截器能监听属性新增,deleteProperty能监听属性删除,无需Vue.set/Vue.delete,自然支持响应式。

3.4.2 2. 完美支持数组所有操作

无论是修改数组索引、长度,还是使用数组方法,Proxy都能拦截,彻底解决 Vue2 的数组监听痛点。

3.4.3 3. 嵌套对象懒加载代理

Vue3 不会在初始化时递归劫持所有嵌套对象,而是在访问嵌套属性时才进行代理 (比如data.user.address,只有访问address时才代理它),初始化性能大幅提升。

3.4.4 4. 支持更多数据类型

除了对象 / 数组,Proxy还能代理MapSet等集合类型(Vue2 不支持),响应式覆盖范围更广。

3.4.5 5. 更简洁的代码结构

无需遍历对象属性,Proxy直接代理整个对象,代码更简洁,维护成本更低。

4. Vue3 响应式数据创建:ref vs reactive(实战篇)

Vue3 提供了两种创建响应式数据的 API:refreactive。很多开发者会混淆它们的用法,其实核心区别在于处理的数据类型不同

4.1 reactive:引用类型的 "响应式包装器"

4.1.1 核心定义

reactive用于将引用类型数据 (对象、数组、MapSet等)转为响应式对象,返回一个Proxy代理实例。

4.1.2 典型使用场景
  • 复杂对象(如用户信息、表单数据);
  • 数组(如列表数据、选项数组);
  • 集合类型(如Map存储键值对数据)。
4.1.3 代码示例(实战级)
html 复制代码
<template>
  <div class="user-info">
    <h3>用户信息</h3>
    <p>姓名:{{ user.name }}</p>
    <p>年龄:{{ user.age }}</p>
    <button @click="updateUser">修改用户</button>

    <h3>爱好列表</h3>
    <ul>
      <li v-for="(hobby, index) in hobbies" :key="index">{{ hobby }}</li>
    </ul>
    <button @click="addHobby">新增爱好</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue';

// 1. 响应式对象(用户信息)
const user = reactive({
  name: '张三',
  age: 25
});

// 2. 响应式数组(爱好列表)
const hobbies = reactive(['篮球', '编程']);

// 修改用户信息
const updateUser = () => {
  user.name = '李四'; // 直接修改属性,响应式生效
  user.age += 1;
};

// 新增爱好
const addHobby = () => {
  hobbies.push('阅读'); // 数组方法,响应式生效
  hobbies[0] = '足球'; // 直接修改索引,响应式生效
};
</script>
4.1.4 关键注意事项
  • ❌ 不能直接替换reactive对象(会丢失响应式):

    javascript 复制代码
    const user = reactive({ name: '张三' });
    user = { name: '李四' }; // 错误:替换后不再是Proxy代理对象,响应式失效
  • ✅ 应修改对象属性而非替换对象:

    javascript 复制代码
    user.name = '李四'; // 正确
    Object.assign(user, { name: '李四' }); // 正确

4.2 ref:基本类型的 "响应式载体"

4.2.1 核心定义

ref用于将基本类型数据numberstringboolean等)转为响应式数据,返回一个 "响应式对象",该对象包含一个value属性,通过value访问 / 修改原始值。

4.2.2 底层原理

ref的底层是reactive!它会创建一个{ value: 原始值 }的响应式对象(用reactive包装),所以修改value时会触发响应式更新。

4.2.3 典型使用场景
  • 基本类型数据(如计数器、开关状态);
  • 单个引用类型数据(如单个用户、单个商品,可简化代码);
  • 组件间传递的基本类型 props(需用ref保持响应式)。
4.2.4 代码示例(实战级)
html 复制代码
<template>
  <div class="counter">
    <h3>计数器:{{ count }}</h3>
    <button @click="increment">+1</button>
    <button @click="decrement">-1</button>

    <h3>开关状态:{{ isOpen ? '开启' : '关闭' }}</h3>
    <button @click="toggleOpen">切换状态</button>

    <h3>单个商品:{{ product.name }}(价格:{{ product.price }}元)</h3>
    <button @click="discount">打8折</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

// 1. 基本类型:数字
const count = ref(0);

// 2. 基本类型:布尔值
const isOpen = ref(true);

// 3. 引用类型:单个对象(用ref也可,简化代码)
const product = ref({
  name: 'Vue3实战教程',
  price: 99
});

// 计数器+1
const increment = () => {
  count.value += 1; // script中必须通过.value修改
};

// 计数器-1
const decrement = () => {
  count.value -= 1;
};

// 切换开关
const toggleOpen = () => {
  isOpen.value = !isOpen.value;
};

// 商品打折
const discount = () => {
  product.value.price *= 0.8; // 引用类型需通过.value访问属性
};
</script>
4.2.5 关键注意事项
  • ✅ 模板中无需写value:Vue3 会自动解包ref对象,模板中直接访问变量名即可(如{``{ count }}等价于{``{ count.value }});
  • ❌ script 中必须写valueref返回的是响应式对象,必须通过value访问 / 修改原始值;
  • ✅ 引用类型用ref也可:ref支持包装引用类型,底层会转为reactive,用法上与reactive的区别仅在于需要多一层value

4.3 ref vs reactive:核心区别与选择指南

特性 ref reactive
支持数据类型 基本类型 + 引用类型 仅引用类型
访问方式 script 中需.value,模板自动解包 直接访问属性,无需.value
底层实现 基于reactive(包装为{ value: T } 基于Proxy直接代理对象
替换整个对象 支持(count.value = 10product.value = { ... } 不支持(直接替换会丢失响应式)
适用场景 基本类型、单个引用类型 复杂对象、数组、集合类型
选择原则(一句话总结):
  • 基本类型用 refnumberstringboolean等);
  • 引用类型优先用 reactive (对象、数组、Map等);
  • 若需替换整个引用类型对象 (如product = { name: '新商品' }),用ref更方便。

4.4 实用工具:toRef、toRefs、unref

Vue3 提供了三个常用工具函数,用于解决refreactive的转换问题:

4.4.1 toRef:为 reactive 对象的单个属性创建 ref

用于 "提取"reactive对象的单个属性,保持响应式关联:

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

const user = reactive({ name: '张三', age: 25 });
const nameRef = toRef(user, 'name');

nameRef.value = '李四'; // 修改nameRef,user.name也会变化
console.log(user.name); // 输出:李四
4.4.2 toRefs:将 reactive 对象转为 ref 对象集合

用于 "解构"reactive对象,避免解构后丢失响应式:

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

const user = reactive({ name: '张三', age: 25 });
// 直接解构会丢失响应式
const { name, age } = user; // 非响应式
// 用toRefs解构,保持响应式
const { name: nameRef, age: ageRef } = toRefs(user);

nameRef.value = '李四';
console.log(user.name); // 输出:李四(响应式关联)
4.4.3 unref:自动解包 ref 对象

相当于isRef(val) ? val.value : val,简化ref对象的访问:

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

const count = ref(0);
console.log(unref(count)); // 输出:0(等价于count.value)

const num = 10;
console.log(unref(num)); // 输出:10(非ref直接返回原值)

5. Vue2 vs Vue3 响应式:全方位对比(表格)

对比维度 Vue2(Object.defineProperty) Vue3(Proxy + Reflect)
核心 API Object.defineProperty Proxy + Reflect
劫持粒度 单个属性 整个对象
对象新增属性 不支持(需 Vue.set) 原生支持
对象删除属性 不支持(需 Vue.delete) 原生支持
数组索引修改 不支持 原生支持
数组长度修改 不支持 原生支持
嵌套对象处理 初始化递归劫持(性能差) 访问时懒加载代理(性能优)
支持数据类型 对象、数组(有限支持) 对象、数组、Map、Set 等
原始值处理 需包装在对象中 用 ref 包装(底层 reactive)
依赖收集核心 Dep + Watcher effect + targetMap

6. 避坑指南:响应式开发常见错误与解决方案

6.1 坑 1:解构 reactive 对象导致响应式丢失

javascript 复制代码
// 错误:解构reactive对象,属性变为非响应式
const user = reactive({ name: '张三', age: 25 });
const { name, age } = user;
name = '李四'; // 视图不更新

// 解决方案:用toRefs解构
const { name: nameRef, age: ageRef } = toRefs(user);
nameRef.value = '李四'; // 视图更新

6.2 坑 2:直接替换 reactive 对象导致响应式丢失

javascript 复制代码
// 错误:替换reactive对象,失去Proxy代理
const user = reactive({ name: '张三' });
user = { name: '李四' }; // 不再是响应式对象

// 解决方案1:修改属性而非替换对象
user.name = '李四';

// 解决方案2:用ref包装(需.value替换)
const user = ref({ name: '张三' });
user.value = { name: '李四' }; // 响应式生效

6.3 坑 3:ref 在 script 中忘记写.value

javascript 复制代码
// 错误:script中直接修改ref变量,不生效
const count = ref(0);
count = 1; // 直接赋值,修改的是变量引用,而非.value

// 正确:通过.value修改
count.value = 1;

6.4 坑 4:认为 ref 只能用于基本类型

javascript 复制代码
// 误区:ref只能处理基本类型
// 正确:ref支持包装引用类型,底层是reactive
const product = ref({ name: 'Vue3', price: 99 });
product.value.price = 88; // 响应式生效

6.5 坑 5:Proxy 无法监听原始值(需用 ref)

javascript 复制代码
// 错误:直接用Proxy代理原始值,无效
const count = 0;
const reactiveCount = new Proxy(count, {}); // Proxy不支持原始值

// 正确:用ref包装原始值
const count = ref(0); // 底层转为reactive({ value: 0 })

7. 总结:响应式的本质与最佳实践

7.1 核心本质

Vue 的响应式系统,本质是 **"数据劫持 + 依赖收集"** 的订阅 - 发布模式:

  • Vue2 用Object.defineProperty劫持单个属性,实现简单但限制多;
  • Vue3 用Proxy代理整个对象,配合Reflecteffect,解决了 Vue2 的所有痛点,功能更强大、性能更优。

7.2 最佳实践

  1. 数据类型决定 API 选择 :基本类型用ref,引用类型优先用reactive
  2. 避免解构 reactive 对象 :如需解构,用toRefs保持响应式;
  3. 不替换 reactive 对象 :修改属性而非替换整个对象,或用ref包装;
  4. script 中牢记 ref 的.value:模板自动解包,但 script 中必须显式访问;
  5. 复杂场景用工具函数toReftoRefsunref简化转换逻辑。

7.3 未来展望

Vue3 的响应式系统基于 ES6 的Proxy,虽然存在 "不支持 IE 浏览器" 的兼容性问题(Vue3 已放弃 IE 支持),但在现代浏览器中,它的优势远大于限制。随着前端技术的发展,Proxy的灵活性和强大功能,将成为响应式系统的主流实现方案。

掌握响应式原理,不仅能让你在开发中避开各种 "坑",更能让你理解 Vue 的设计思想 ------"数据驱动视图" 的核心,正是通过响应式系统,让开发者从繁琐的 DOM 操作中解放出来,专注于业务逻辑。

如果本文对你有帮助,欢迎点赞、收藏、转发!如有疑问或补充,欢迎在评论区交流~

附录:Vue3 响应式 API 速查表

API 作用 适用场景
reactive 将引用类型转为响应式对象 复杂对象、数组、集合类型
ref 将基本类型 / 引用类型转为响应式数据 基本类型、单个引用类型(需替换)
toRef 提取 reactive 对象的单个属性为 ref 单独使用 reactive 对象的某个属性
toRefs 将 reactive 对象转为 ref 对象集合 解构 reactive 对象,保持响应式
unref 自动解包 ref 对象(ref→原始值) 简化 ref 对象的访问
isRef 判断是否为 ref 对象 类型判断
isReactive 判断是否为 reactive 响应式对象 类型判断
shallowRef 浅响应式(只监听.value 的替换) 无需深度响应的引用类型
shallowReactive 浅响应式(只监听顶层属性) 无需深度响应的复杂对象
相关推荐
编程修仙2 小时前
第三篇 Vue路由
前端·javascript·vue.js
比老马还六2 小时前
Bipes项目二次开发/硬件编程-设备连接(七)
前端·javascript
掘金一周2 小时前
前端一行代码生成数千页PDF,dompdf.js新增分页功能| 掘金一周 12.25
前端·javascript·后端
张就是我1065922 小时前
漏洞复现指南:利用 phpinfo() 绕过 HttpOnly Cookie 保护
前端
Kagol2 小时前
🎉TinyVue v3.27.0 正式发布:增加 Space 新组件,ColorPicker 组件支持线性渐变
前端·vue.js·typescript
潍坊老登2 小时前
大前端框架汇总/产品交互参考UE
前端
方安乐2 小时前
获取URL参数如何避免XSS攻击
前端·xss
十二AI编程3 小时前
MiniMax M2.1 实测,多语言编程能力表现出色!
前端
鹿野素材屋3 小时前
技术闲聊:为什么网游会在固定时间点,刷出固定的道具?
前端·网络·unity