Appearance
数据、视图、Vue
vue 想必大家都应该很熟悉了,最常提起的特点便是:数据驱动视图,我们只需要改动数据,我们的页面就会随之变化,所以,我们可以得出以下结论:
View = Render ( Data )
View 代表页面
Render 代表 Vue
Data 即是数据
Object.defineProperty
看过一些文章的小伙伴可能对于这个方法或多或少知道一点,vue 就是使用这个方法对于数据进行观测的,对于每个数据,都会有其对应的 getter 和 setter ,这样我们就会知道数据何时发生了变化,并去更新相对应的视图。
看这段代码:
js
let Ned = {
money:"100"
}
Object.defineProperty(Ned ,'money',{
get(){
console.log('Ned 被读取了一次')
},
set(newVal){
console.log('Ned 被设置了一次,新数据为:',newVal)
}
})
Ned.money // Ned 被读取了一次
Ned.money="1亿" //Ned 被设置了一次,新数据为:1亿
从上面的案例可以看出,我们读取数据时会进入 get 函数中;我们设置数据时会进入 set 函数中,这样数据就变得可观测了,读取和设置数据的时候我们都会知道。
看源码(目录:src/core/observer/index.js
):
js
// Observer观察者类,对每个对象设置getter和setter,进行依赖收集和发送更新
export class Observer {
value: any;
constructor (value: any) {
this.value = value
/**
* 给value增加一个属性'__ob__',值为该value的Observer的实例
* 这样是相当于在value上打一个补丁,避免重复操作
* 方法在util/lang.js
*/
def(value, '__ob__', this)
if (Array.isArray(value)) {
// 数组逻辑
} else {
// 操作对象的逻辑
this.walk(value)
}
}
/**
* 遍历对象上的每个属性
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
//在对象上定义反应属性
export function defineReactive (
obj: Object, //要响应的对象
key: string, // 响应对象的键
val: any, // 对象的值
) {
const dep = new Dep() //创建一个依赖管理器
// 递归,针对子对象设置geter和setter,并返回子对象的Observer实例
let childOb = observe(val)
Object.defineProperty(obj, key, {
enumerable: true, //表示能否通过for in 循环属性
configurable: true, //是否可以删除或重新定义属性
// 在这里可以知道获取了值
get: function reactiveGetter () {
dep.depend()//收集依赖,往下面看会明白
return val
},
// 在这里可以知道更改了值
set: function reactiveSetter (newVal) {
dep.notify() // 通知所有依赖这个对象观察者进行更新
val=newVal
}
})
}
/*
* 给值创建观察者实例
* 如果观察成功就返回新的观察者实例
* 如果已经观察过了,就返回现有的
*/
function observe (value: any, asRootData: ?boolean): Observer | void {
// 如果不是对象,就不必设置getter好和setter
if (!isObject(value) {
return
}
let ob: Observer | void
//通过‘__ob__’,判断是否有Observer实例,如果已经打过标记了,就直接拿出Observer的实例对象
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
/**
* 确保value纯对象,且没有被是否Observer过
*/
shouldObserve && //是否Observer过,通过toggleObserving来修改
!isServerRendering() && // 是否是服务端渲染
(Array.isArray(value) || isPlainObject(value)) && //isPlainObject判断类型是否是object
Object.isExtensible(value) && //isExtensible判断对象是否可以扩展
!value._isVue // 避免vue实例被观察
) {
ob = new Observer(value)
}
return ob
}
从上面的源码可以看到,通过new Observer ( obj )
我们可以使对象变得可观测,那么下一步我们就要知道既然知道了数据什么时候变化,那该怎么去更新视图呢?该更新哪些视图呢,这就要先提到依赖收集了。