美文网首页
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