vue
最大的特点之一就是数据驱动视图,也就是数据的变化,引起视图的变化。那么,第一步肯定要知道数据什么时候发生变化,这就叫做对数据的变化侦测
想要知道数据什么时候发生变化,我们可以使用js的Object.defineProperty
方法
let student = {
'age': 11,
}
我们可以直接通过student.age读取到学生的年龄是11,但是当学生年龄的值被修改了,我们如何才能主动知道值被修改了呢?
我们可以使用Object.defineProperty
方法来写上述例子
let student = {};
let val = 11;
Object.defineProperty(student, 'age', {
enumerable: true, // 是否可枚举
configurable: true, // 是否可配置
get(){
console.log('age属性被读取了')
return val
},
set(newVal){
console.log('age属性被修改了')
val = newVal
}
})
通过Object.defineProperty()
方法给student一个age属性,并把这个属性的读和写用get()
和set()
进行拦截,该属性进行读或写操作的时候就会分别触发get()
和set()
。所以,student这个对象已经变成了可观测的
以下的vue源码,就是用来将对象的所有属性变成可观测的
- 先定义一个observer类,目的是将object转成可观测object
- 给
value
新增一个__ob__
属性,作为标记,避免重复操作 - 判断数据类型,只有object类型才会调用walk方法,然后将每一个属性转换成
getter/setter
的形式来侦测变化 - 发现object子属性的值也是object形式的时候,通过new observer(val)递归子属性
// 源码位置:src/core/observer/index.js
/**
* Observer类会通过递归的方式把一个对象的所有属性都转化成可观测对象
*/
export class Observer {
constructor (value) {
this.value = value
// 给value新增一个__ob__属性,值为该value的Observer实例
// 相当于为value打上标记,表示它已经被转化成响应式了,避免重复操作
def(value,'__ob__',this)
if (Array.isArray(value)) {
// 当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])
}
}
}
/**
* 使一个对象转化成可观测对象
* @param { Object } obj 对象
* @param { String } key 对象的key
* @param { Any } val 对象的某个key的值
*/
function defineReactive (obj,key,val) {
// 如果只传了obj和key,那么val = obj[key]
if (arguments.length === 2) {
val = obj[key]
}
if(typeof val === 'object'){
new Observer(val)
}
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get(){
console.log(`${key}属性被读取了`);
return val;
},
set(newVal){
if(val === newVal){
return
}
console.log(`${key}属性被修改了`);
val = newVal;
}
})
}
因此,只要将一个object
传到observer
中,那么这个object
就会变成可观测的object
。
// 可观测的student
let student = new Observer({
'age': 11
})