手撸简化版VUE响应式实现源码

  • 响应式原理
    Vue 遍历data对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
    官方地址

手撸简化版VUE响应式实现源码

模拟vue中的data数据

    const data = {
        name: '路灯',
        age: 18,
        son: {
            name: "小灯",
            age: 1
        },
        arr: [1, 2, 3]
    }
  1. 遍历data,使用Object.defineProperty设置每一个属性

    function observer(data){
        if(typeof data === 'object'){
            for(key in data){
                defineReactive(data, key, data[key])
            }
        }
    }
    
    function defineReactive(obj, key, value){
        Object.defineProperty(obj, key, {
            get(){
                return value;
            },
            set(newVal){
                value = newVal;
                render();
            }
        })
        
    }
    
    function render(){
        console.log('渲染了')
    }
    
    observer(data);

此时,如果修改data中的name,age都会打印'渲染了'(用render函数模拟VUE更新视图)。初步实现了,更改数据,视图会响应式更新。但是修改son.name不会渲染,所以需要递归遍历data所有子对象。

  1. 递归遍历data所有子对象
    function observer(data){
        if(Array.isArray(data)){    
            return ;
        }
        if(typeof data === 'object'){
            ...
        }
    }
    
    function defineReactive(obj, key, value){
        observer(obj[key]);
        Object.defineProperty(obj, key, {
            ...
        })
        
    }

VUE并不对数组属性进行监听,尤雨溪觉得:性能代价和获得的用户体验收益不成正比。所以出现了通过索引修改数组视图并不会更新现象。但是VUE重写了一些数组方法,调用这些方法修改数组会更新视图。

  1. 数组变异方法

如果属性是个数组,那么就让这个数组的原型等于一个新对象(arrayPrototype)。这个新原型对象里的push,pop等方法会触发渲染。

    function observer(data){
        if(Array.isArray(data)){
            data.__proto__ = arrayPrototype;
            return ;
        }

重写数组7种方法,这7种方法会触发渲染。

    const arrayPrototype = Object.create(Array.prototype);
    ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reserve'].forEach(method => {
        arrayPrototype[method] = function(){
            Array.prototype[method].apply(this, arguments);
            render();
        }
    })

经过上面,本来数组对象的隐式原型(_proto_)是 Array.prototype,现在是一个新对象,新对象的原型是Array.prototype。这个新原型对象重写了数组7个方法。

总结

以上在VUE源码的基础上简化,留其思想,以供学习。

VUE源码位置:src/core/observer

本文源码:

    const data = {
        name: '路灯',
        age: 18,
        son: {
            name: "小灯",
            age: 1
        },
        arr: [1, 2, 3]
    }
    
    const arrayPrototype = Object.create(Array.prototype);
    ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reserve'].forEach(method => {
        arrayPrototype[method] = function(){
            Array.prototype[method].apply(this, arguments);
            render();
        }
    })
    
    function observer(data){
        if(Array.isArray(data)){
            data.__proto__ = arrayPrototype;
            return ;
        }
        if(typeof data === 'object'){
            for(key in data){
                defineReactive(data, key, data[key])
            }
        }
    }
    
    function defineReactive(obj, key, value){
        observer(obj[key]);
        Object.defineProperty(obj, key, {
            get(){
                return value;
            },
            set(newVal){
                if(value == newVal) return;
                value = newVal;
                render();
            }
        })
        
    }
    
    function render(){
        console.log('渲染了')
    }
    
    observer(data);

你可能感兴趣的:(手撸简化版VUE响应式实现源码)