从源码理解vue的响应式一

285 阅读1分钟

1.如何追踪变化,现在稍微会写vue的都知道2.0的时候是利用Object.defineProperty

function defineReactive(obj, key, val) {  
    Object.defineProperty(obj, key, {    
        enumerable: true,    
        configurable: true,    
        get: function reactiveGetter() {      
            return val;    
        },    
        set: function reactiveSetter(newVal) {      
            if (newVal === val) {        
                return;          
            }      
            val = newVal;    
        }  
    }
)}

2.那么这样写了离响应式还是很远的,这时候我们可以这样想,如果有对应的实例使用当前对象,那么就被收集到当前的依赖中

function defineReactive(obj, key, val) {  
    let dep = [];  
    Object.defineProperty(obj, key, {    
        enumerable: true,    
        configurable: true,    
        get: function reactiveGetter() {      
            dep.push(this.$vm);      
            return val;    
        },    
        set: function reactiveSetter(newVal) {      
            if (newVal === val) {        
                return;      
        }      
        // 某种触发当前的dep      
        for(let i = 0;i < dep.length;i++) {        
            dep[i].notify(); // 假设notify是可以让对应实例重新渲染的函数      
        }      
        val = newVal;    
      }  
    }
)}

3.思考到这里我们可以发现我们只是想到了最简单的一种情况,大概是这样

export default {  
    data () {    
        return {      
            a: 1,      
            b: '1'    
        }  
    }
}

4.如果a属性也是对象怎么办,那一般大家会想到的是递归

class Observe {  
    constructor(value) {    
        this.value = value;    
        if (!Array.isArray(value)) {      
            this.walk(value);    
        }  
    }  
    walk(obj) {    
        const keys = Object.keys(obj);    
        for(let i = 0;i < keys.length;i++) {      
            defineReactive(obj, keys[i], obj[keys[i]]);    
        }  
    }
}
function defineReactive(obj, key, val) {  
    if (typeof obj[key] === 'object') {    
          new Observe(val);  
    }  
    let dep = [];  
    Object.defineProperty(obj, key, {    
        enumerable: true,    
        configurable: true,    
        get: function reactiveGetter() {      
            dep.push(this.$vm);      
            return val;    
        },    
        set: function reactiveSetter(newVal) {      
            if (newVal === val) {        
                return;      
            }      
            // 某种触发当前的dep                 
            for(let i = 0;i < dep.length;i++) {        
                dep[i].notify(); 
                // 假设notify是可以让对应实例重新渲染的函数      
            }      
            val = newVal;    
        }  
    }
)}

5.总结

响应式的目的是当数据发生变化时,页面可以立马渲染更新;在vue2.0中实现了通过Object.defineProperty将属性的读取和更新转换为getter和setter的形式来追踪变化,当读取数据的时候会走到getter,当改变数据的时候会走到setter,observer的作用是将一个对象的所有属性都设置为响应式的,每个属性都有自己的依赖集合dep,当一个数据被多个实例读取的时候,这多个实例都会被push到这个集合中,当数据变化的时候,setter中会循环处理dep依次通知所有的实例更新

6.备注

从上面简单的实现过程中,我们可以发现,如果属性是没有提前在data中定义的,那么是做不到响应式的,应对方式$set