拖了好久补完了其他知识,现在开始学习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这个可能会写的久一些。