# $set、$delete原理

# $set原理

$set内部调用的是Vue.set (opens new window)方法,它是在initGlobalAPI (opens new window)时,给Vue添加的全局静态方法。我们将源码进行简化,方便理解。

  1. target是数组,我们调用target.splice方法来修改数组。注意,此时数组对象在observe (opens new window)阶段,splice方法已经被重写了。此时调用splice时,会触发数组的dep.notify,通知页面更新。
  2. target是一个对象
    1. 如果对象中本来就有这个key(并且不是对象原型上的key),直接设置值并返回即可。如果对象是响应式,设置值时,视图默认就会更新。
    2. 获取对象的observe实例: _ ob_ 。
      1. 注意,在observe阶段,每个对象都有一个dep,并且对象上都有一个 _ ob_ 属性,指向其对象的observe实例。
      2. 如果_ ob_ 不存在,说明它不是一个响应式对象。直接设置值并返回即可。
    3. 给对象通过defineReactive方法,添加响应式时,并通过dep.notify()通知视图更新。
export function set (target: Array<any> | Object, key: any, val: any): any {
  // 1、target是数组,且key是一个有效的下标
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    // 在索引key位置,通过splice方法添加数据(数组的splice方法已经被重写了)
    target.splice(key, 1, val)
    return val
  }
  // 如果target中已经存在该key,直接返回。如果对象是响应式时,视图默认就会更新
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  // 获取target的Observer实例对象:__ob__
  const ob = (target: any).__ob__;
  if (!ob) {  // 说明target不是一个响应式对象,直接设置值即可,页面也不会更新
    target[key] = val
    return val
  }
  // 2) target是一个对象,调用defineReactive,将新添加的key变为响应式对象
  defineReactive(ob.value, key, val)
  // ob.dep,即targe对象自己的那个dep实例,触发更新,重新渲染页面
  ob.dep.notify()
  return val
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# $delete原理

$set内部调用的是Vue.del (opens new window)方法,它是在initGlobalAPI (opens new window)时,给Vue添加的全局静态方法。整个过程与$set类型,可参考代码注释。

function del (target: Array<any> | Object, key: any) {
  // 1、target是数组,且key是一个有效的下标
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 在索引key位置,通过splice方法删除数据(数组的splice方法已经被重写了)
    target.splice(key, 1)
    return
  }
  const ob = (target: any).__ob__
  // 如果对象没有这个属性,直接返回
  if (!hasOwn(target, key)) {
    return
  }
  // 如果是对象,直接删除target.key,并调用dep.notify更新视图
  delete target[key]
  if (!ob) {
    return
  }
  ob.dep.notify()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19