美文网首页
简述vue2.0响应式 原理

简述vue2.0响应式 原理

作者: 景元合 | 来源:发表于2020-03-08 15:42 被阅读0次

    前言

    关于vue2.0响应式原理,应该都清楚是使用了defineProperty的get()与set()属性,今天结合代码将其响应式原来重新梳理一遍。

    关于defineProperty

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
    Object.defineProperty(obj, prop, descriptor)
    参数

    • obj
      要在其上定义属性的对象。
    • prop
      要定义或修改的属性的名称。
    • descriptor
      将被定义或修改的属性描述符。
      其中descriptor有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。

    如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。

    对于object的数据劫持

    function updateView(){
        console.log('视图更新');
    }
    function defineReactive(target,key,value){
        Object.defineProperty(target,key,{
            get(){
                return value
            },
            set(newValue){
                if(newValue!==value){
                    updateView();
                    value=newValue
                }
            }
        })
    }
    function observe(target){
        if(typeof target!=='object'||target===null){
            return
        };
        for(let key in target){
            defineReactive(target,key,target[key])
        }
    }
    let data={name:'1'};
    observe(data);
    data.name=2;
    console.log(data.name);
    //视图更新
    //2
    

    以上代码是最简单的对于object的数据劫持,但是会发现当object里面还有对象时候是坚持不到变化的,这是因为此时只是对最外层数据进行了数据劫持,因此还需要在defineReactive内使用递归调用observe()方法,同时可以在set()方法内调用observe()方法

    function updateView(){
        console.log('视图更新');
    }
    function defineReactive(target,key,value){
        observe(value);
        Object.defineProperty(target,key,{
            get(){
                return value
            },
            set(newValue){
                if(newValue!==value){
                    observe(newValue)
                    updateView();
                    value=newValue
                }
            }
        })
    }
    function observe(target){
        if(typeof target!=='object'||target===null){
            return
        };
        for(let key in target){
            defineReactive(target,key,target[key])
        }
    }
    let data={name:'1',age:{count:2},money:{count:2}};
    observe(data);
    data.age.count=3;
    data.money={count:3}
    console.log(data.age.count);
    console.log(data.money.count)
    视图更新
    视图更新
    3
    3
    

    同时大家知道defineProperty没法对数组长度进行监控,因此需要重写Array的'push','pop','shift','unshift','unshift','splice','sort','reverse'进行重写

    let oldArrayPrototype=Array.prototype;
    let proto=Object.create(oldArrayPrototype);
    ['push','pop','shift','unshift','unshift','splice','sort','reverse'].forEach(method=>{
        proto[method]=function(){
            updateView();
            oldArrayPrototype[method].call(this,...arguments);
        }
    })
    function updateView(){
        console.log('视图更新');
    }
    function defineReactive(target,key,value){
        observe(value);
        Object.defineProperty(target,key,{
            get(){
                return value
            },
            set(newValue){
                if(newValue!==value){
                    observe(newValue)
                    updateView();
                    value=newValue
                }
            }
        })
    }
    function observe(target){
        if(typeof target!=='object'||target===null){
            return
        };
        if(Array.isArray(target)){
            Object.setPrototypeOf(target,proto);
        }
        for(let key in target){
            defineReactive(target,key,target[key])
        }
    }
    let data={name:'1',age:[1,2]};
    observe(data);
    data.age.push(2);
    console.log(data.age);
    //视图更新
    //[ [Getter/Setter], [Getter/Setter], 2 ]
    

    写在最后

    大家应该发现使用defineProperty存在的问题

    • 一上来会使用递归便利所有属性,如果嵌套很深,那效率会比较低。
    • 对于没有定义的数据,没法检测其变化
    • 无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应
      为了弥补defineProperty存在的问题,vue3.0使用proxy替代defineProperty,一会再写一篇关于vue3.0双向绑定的原理文章。

    相关文章

      网友评论

          本文标题:简述vue2.0响应式 原理

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