vue2中$set的原理

在 Vue2 中,$set 是一个核心 API,用于解决对象或数组新增属性时无法触发响应式更新的问题。要理解其原理,需要先了解 Vue2 响应式系统的底层实现。

1. Vue2 响应式的核心:Object.defineProperty

Vue2 的响应式系统基于 Object.defineProperty 实现,其核心逻辑是:

  • 对数据对象的已有属性 进行劫持(拦截 getset 操作)
  • 当属性被访问时(get),收集依赖(Watcher)
  • 当属性被修改时(set),触发依赖更新(通知视图重新渲染)

但这种方式存在天然限制:只能劫持对象已存在的属性。对于新增属性或删除属性,默认无法触发响应式更新。

2. $set 的作用

$set 的设计目的就是解决上述限制,它能让新增的属性也具备响应式能力,同时触发视图更新。

js 复制代码
// 响应式对象
const vm = new Vue({
  data() {
    return {
      obj: { name: 'foo' }, // 已有属性 name 是响应式的
      arr: ['a', 'b']
    }
  }
})

// 直接新增属性,不会触发更新
vm.obj.age = 20; // 非响应式

// 使用 $set 新增属性,会触发更新
this.$set(vm.obj, 'age', 20); // 响应式

// 数组新增元素(Vue 对数组方法做了特殊处理,但直接通过索引修改仍需 $set)
this.$set(vm.arr, 2, 'c'); // 响应式

3. $set 的实现原理

$set 源码(简化版)的核心逻辑如下:

js 复制代码
function set(target, key, value) {
  // 1. 处理数组:利用重写的 splice 方法触发更新
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key);
    target.splice(key, 1, value);    // splice 已被 Vue 重写,会触发更新
    return value;
  }

  // 2.处理对象:如果属性已存在,直接赋值(会触发 set 拦截)
  if (key in target && !(key in Object.prototype)) {
    target[key] = value;
    return value;
  }

  // 获取响应式对象的 Observer 实例
  const ob = target.__ob__;

  // 3. 非响应式对象(如普通对象),直接赋值
  if (!ob) {
    target[key] = value;
    return value;
  }

  // 4.为新属性添加响应式劫持(调用 defineReactive)
  defineReactive(ob.value, key, value);
  
  // 触发依赖更新(通知视图渲染)
  ob.dep.notify();
  
  return value;
}

defineReactive 是 Vue2 响应式系统的核心函数,它的主要作用是将对象的属性转换为响应式属性,实现对属性读写的拦截,从而在数据变化时自动更新视图

  • 递归响应式 :如果属性值是对象(或数组),会通过 observe 函数递归调用 defineReactive,实现深层响应式。
  • 依赖收集 :借助 Dep 类管理依赖,Dep.target 指向当前正在渲染的组件对应的 Watcher,读取属性时会将 Watcher 加入依赖列表。
  • 触发更新 :当属性被修改时,set 函数会调用 dep.notify(),遍历依赖列表并触发所有 Watcher 的更新逻辑(最终调用组件的 render 方法重新渲染)。

核心步骤拆解:

  1. 处理数组 :由于 Vue 对数组的 splicepush 等方法进行了重写(拦截),调用这些方法会自动触发更新。因此对于数组,$set 本质是通过 splice(key, 1, value) 实现的。

  2. 处理对象

    • 若属性已存在,直接赋值(会触发该属性原有的 set 拦截器)。
    • 若属性不存在,通过 defineReactive 为新属性添加响应式劫持(即重新调用 Object.defineProperty 拦截 getset)。
    • 手动触发依赖更新(ob.dep.notify()),确保视图同步刷新。

$set 本质是 "手动为新属性添加响应式劫持,并强制触发更新" 的封装。

总结

$set 是 Vue2 为弥补 Object.defineProperty 缺陷而设计的 API,其核心原理是:

  • 对数组:通过重写的 splice 方法触发响应式更新。
  • 对对象:为新属性手动添加响应式劫持(defineReactive),并触发依赖更新。

在 Vue3 中,由于响应式系统改用 Proxy 实现(支持监听新增 / 删除属性),因此不再需要 $set 这个 API。

相关推荐
西西小飞龙5 小时前
Less/Sass Mixins vs. Extend
前端·less·sass
syjy25 小时前
(含下载)BeTheme WordPress主题使用教程
前端·wordpress·wordpress建站
Misnice5 小时前
shadcn如何使用
前端·reactjs
h_jQuery5 小时前
vue使用gm-crypto对数据进行sm4加密处理
前端·javascript·vue.js
阿赛工作室5 小时前
Vue中onBeforeUnmount不触发的解决方案
前端·javascript·vue.js
码王吴彦祖5 小时前
顶象 AC 纯算法迁移实战:从补环境到纯算的完整拆解
java·前端·算法
小叶lr6 小时前
jenkins打包前端样式丢失/与本地不一致问题
运维·前端·jenkins
浩星6 小时前
electron系列1:Electron不是玩具,为什么桌面应用需要它?
前端·javascript·electron
ZC跨境爬虫6 小时前
Scrapy工作空间搭建与目录结构解析:从初始化到基础配置全流程
前端·爬虫·python·scrapy·自动化
小村儿6 小时前
连载04-最重要的Skill---一起吃透 Claude Code,告别 AI coding 迷茫
前端·后端·ai编程