美文网首页
Vue.set,delete源码解析

Vue.set,delete源码解析

作者: miao8862 | 来源:发表于2021-04-26 22:02 被阅读0次

Vue.set

因为我们一开始可能不知道入口在哪,这时我们可以写个测试用例,用来查找入口

先创建一个测试文件

<!-- test/test4.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="../dist/vue.js"></script>
</head>
<body>
  <div id="setDemo">
    <h1>set/del</h1>
    <p>{{obj.foo}}</p>
  </div>
  <script>
  const app = new Vue({
    el: "#setDemo",
    data: {
      obj: {}
    },
    mounted() {
      setTimeout(() => {
        Vue.set(this.obj, 'foo', 'fooo')
      }, 1000);
    }
  })
</script>
</body>
</html>

在浏览器中打开这个测试文件,找到源码位置

  1. 在sources中,ctrl+p输入test4.html查找到文件,然后我们在Vue.set处打上断点,开始调试
    调试入口
  2. 因为this.obj是一个对象,所以会先走一遍代理,这步可以忽略


    image.png
  3. 第二次进入Vue.set时,才是真正进入set方法,发现set方法在src\core\observer\index.js文件中
    image.png

打开源码对应位置解读

// src\core\observer\index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
  // 判断目标是不是一个数组,且key是一个有效值
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 将数组的长度修改为 target.length 和 key 中的较大者,否则如果当要设置的元素的索引大于数组长度时 splice 无效
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  // 如果不是数组,就是对象
  // 这个判断本质上是判断key是否在target自身属性上,即obj.hasOwnProperty(key)
  // 即这个key不是新增的,本身就是响应式的,不需要做额外操作,直接返回
  if (hasOwn(target, key)) {
    target[key] = val
    return val
  }
  // 通过target是否包含__ob__属性,判断它本身是不是一个响应对象
  const ob = (target: any).__ob__
  // 判断target是不是Vue实例本身,或者是否为根数据对象,如果是发出警告,避免覆盖
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  // 如果不是,那么对它做响应式也没用
  if (!ob) {
    target[key] = val
    return val
  }
  // target是对象,才对新增的key做响应式处理
  defineReactive(ob.value, key, val)
  // 从而触发响应
  ob.dep.notify()
  return val
}

Vue.delete

del的方法src\core\observer\index.js文件中,且就在set方法下面,同理,你也可以写个测试用例验证,我这就不再累赘述了

// Vue.delete删除操作
export function del (target: Array<any> | Object, key: any) {
  // 判断是不是一个数组,且key有效
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 直接做删除操作
    target.splice(key, 1)
    return
  }
  // 判断target是不是Vue实例本身,或者是否为根数据对象,如果是发出警告,避免覆盖
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid deleting properties on a Vue instance or its root $data ' +
      '- just set it to null.'
    )
    return
  }
  // 如果为对象,key不在对象属性上,则不需要做删除操作
  if (!hasOwn(target, key)) {
    return
  }
  // 否则使用delete关键字作删除操作
  delete target[key]
  // 如果targett本身不是响应式的,就不需要做响应通知
  if (!ob) {
    return
  }
  // 否则,则做响应通知
  ob.dep.notify()
}

相关文章

网友评论

      本文标题:Vue.set,delete源码解析

      本文链接:https://www.haomeiwen.com/subject/mlfyrltx.html