【vue源码】$delete实现原理

990 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

$set的作用

vm.$delete(target, key)中接收两个参数

  • targer 目标值
  • key 将要删除的属性 对于在初始化data时已经设置的值,当我们想要删除数据中的某一个值并触发视图更新,例如:person:{name:'张三',age:18}中我们想要删除age属性并触发视图更新,使用Object的delete并不能触发视图更新,需要借助$delete方法去删除。

前期准备和资料

  • 本文基于vue2.6.14版本进行解读
  • demo地址传送门(为了方便查看源码执行过程,请在debugger下进行调试查看,在demo关键代码处已打debugger)
  • demo相关代码

解读

前端相关代码

<div id="app">
    <h2>数组的处理</h2>
    <p v-for="num in numList">{{num}}</p>
    <button v-on:click="del">删除</button>
    <h2>删除属性</h2>
    <p v-if="person.name">{{person.name}}</p>
    <p v-if="person.age">{{person.age}}</p>
    <button v-on:click="delAge">删除age</button>
</div>
<script>
    let vue = new Vue({
        el: '#app',
        data: {
            numList:[0,1,2,3],
            person:{
                name: '张三',
                age: 18
            }
        },
        methods:{
            del(){
                this.$delete(this.numList, this.numList.length - 1)
            },
            delAge(){
                this.$delete(this.person, 'age')
            }
        },
    });
</script>

image.png

$delete的原理是在vue的原型上添加$delete的方法,大概原理同vue.$set类似,也是针对以下几种情况情况分别进行处理

  • 当key不是target的自身属性时,则直接return返回
  • 当target是数组的时候,同vue.$set原理一样借助vue内部拦截处理后数组的splice方法进行赋值,调用splice方法,可以触发界面的更新。
  • 对于删除target中已有的key属性,通过delete删除target中对应的属性,并通过notify通知触发数据变化更新界面
function del (target, key) { 
    debugger
    if (isUndef(target) || isPrimitive(target)
    ) {
      warn(("无法删除未定义、null或基元值的被动属性 " + ((target))));
    }
    // 对数组类型的数据,通过splice删除对应的数据会自动触发视图更新
    if (Array.isArray(target) && isValidArrayIndex(key)) {
      target.splice(key, 1);
      return
    }
    // 不能对vue实例和vue实例的根数据进行处理,并给出对应的提示
    var ob = (target).__ob__;
    if (target._isVue || (ob && ob.vmCount)) {
      warn(
        'Avoid deleting properties on a Vue instance or its root $data ' +
        '- just set it to null.'
      );
      return
    }
    // 如果key不是target上的属性,则直接return返回
    if (!hasOwn(target, key)) {
      return
    }
    delete target[key];
    // 判断target是否是响应式数据,如果不是,则直接return返回,否则的话,继续发送变化通知
    if (!ob) {
      return
    }
    ob.dep.notify();
  }

下图是对数组进行处理的代码执行

image.png 下图是对删除age属性处理的代码执行

image.png