Vue源码解析 1.0 变化侦测

181 阅读2分钟

拖了好久补完了其他知识,现在开始学习Vue的底层,冲冲冲。

变化侦测

Object.defineProperty

Vue就是使用Object.defineProperty完成数据的监听。但Object.defineProperty不支持IE8以下的阅览器这就是为什么Vue不支持IE8.0以下的了。

Vue3.0使用ES6的proxy替代Object.defineProperty,这下IE10以下都不支持了。8说了,先读完2.0的,再去读3.0的吧。

学习源码就是站在巨人的肩膀上学习他们的思维方式。

Object.defineProperty

对象中属性描述符有两个类型数据描述符存取描述符,这里就不细说了。一般定义对象属性都是数据描述符,这里使用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;
            console.log('更新数据啦')
            val = newVal
        }
    });
}


var obj = {
        a: 1
}
defineReactive(obj, 'a', obj.a)
obj.a = 2

上面这个defineReactive就为obj的a属性改变成了存取描述符

读取obj.a时会触发get属性

修改obj.a时会触发set属性(注:只有修改时会触发set属性,这个问题之后会讲到)

observer

这样一个一个加太慢了,再写一个批量添加存取描述符observer函数吧

function observer(data) {
    if (!data || (typeof data !== 'object')) {
        return
    }
    Object.keys(data).forEach(key => {
        defineReactive(data, key, data[key])
    })
}
var obj = {
        a: 1,
        b: 2,
        c: 3
}
observer(obj)

好了这样就都修改成上存取描述符

知道原理了就开始写Vue吧。这里用ES6的class写,比较清晰。

class Vue {
        constructor(options) {
            this._data = options.data || {}
            this.observer(this._data)
        }
        defineReactive(obj, key, val) {
            /* 使用递归实现所有都绑定上存取描述符 */
            if (typeof val === 'object') {
                this.observer(val)
            }
            Object.defineProperty(obj, key, {
                enumerable: true,   //可枚举性
                configurable: true, //可配置性
                get: function reactiveGetter() {
                    return val;
                },
                set: function reactiveSetter(newVal) {
                    if (newVal === val) return;
                    console.log('更新数据啦')
                    val = newVal
                }
            });
        }
        observer(data) {
            if (!data || (typeof data !== 'object')) {
                return
            }
            Object.keys(data).forEach(key => {
                this.defineReactive(data, key, data[key])
            })
        }
        /* 获取变量类型 */
        GetType(val) {
            return Object.prototype.toString.call(val).slice(8, -1).toLowerCase()
        }
    }
    let vm = new Vue({
        data: {
            a: 1,
            b: {
                c: 233
            }
        }
    })
    vm._data.a = 2
    vm._data.b.c = 555

好了,这样基本的变化侦测就实现了。

今天先这样,明天写Dep观察Watcher订阅Watcher这个可能会写的久一些。

最近杭州天气变冷了