Vue响应式原理
一、导引
1、MVVM模式
大家都知道 Vue 是 MVVM 模式,包括Model、View-Model、View。
模板:
<div>年龄:{
{age}}</div>
数据变化:
this.age ++;
当数据变化时,试图也会随之改变,这里就是view-model发挥了作用。
2、侵入式和非侵入式
侵入式就是没有调用任何函数,直接改变数据就能实现视图的变化。
非侵入式就是通过调用函数来改变数据就能实现视图的变化。
那为什么Vue可以实现非侵入式的效果的——如何实现数据响应式?
二、Object.defineProperty()的认识——响应式的核心
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
可以检测对象属性变化,实现数据劫持和数据代理
1、value、writable、enumerable
var obj = {
};
Object.defineProperty(obj, 'a', {
//给a属性定义值
value: 6,
//是否可写
writable: false,
//是否可以被枚举
enumerable: true
})
Object.defineProperty(obj, 'b', {
//给a属性定义值
value: 10,
//是否可以被枚举
enumerable: false
})
console.log('obj.a', obj.a);//6
obj.a = 10
console.log('obj.a', obj.a);//6:因为设置了不可写
for (const key in obj) {
console.log('可枚举', key);//只会输出a,因为b设置了不可被枚举
}
2、get、set
var obj = {
};
Object.defineProperty(obj, 'c', {
//getter:数据劫持
get() {
console.log("正在访问c属性")
},
//setter
set() {
console.log('正在改变c属性')
}
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//undefined
三、defineReactive函数(自定义函数)
解决obj.c为undefined的问题
因为getter函数(get)需要有一个返回值,这个返回值就是属性的值(get和value不能共存)
var obj = {
};
Object.defineProperty(obj, 'c', {
//getter:数据劫持
get() {
console.log("正在访问c属性");
return 7
},
//setter
set() {
console.log('正在改变c属性')
}
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//7
当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
var obj = {
};
Object.defineProperty(obj, 'c', {
//getter:数据劫持
get() {
console.log("正在访问c属性");
return 7
},
//setter
set(newValue) {
console.log('正在改变c属性', newValue)
}
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//7 因为get返回的依旧是7
最终解决方案:
var obj = {
};
var temp;
Object.defineProperty(obj, 'c', {
//getter:数据劫持
get() {
console.log("正在访问c属性");
return temp;
},
//setter
set(newValue) {
console.log('正在改变c属性', newValue)
temp = newValue
}
})
obj.c++;//自增有访问也有改变
obj.c = 10;//赋值只有改变
console.log(obj.c);//10
成功修改!!!
自定义defineReactive函数
//自定义defineReactive函数
function defineReactive(data, key, val) {
Object.defineProperty(data, key, {
//可枚举
enumerable: true,
//可以被delete
configurable: true,
//getter:数据劫持
get() {
console.log("正在访问" + key + "属性");
return val;
},
//setter
set(newValue) {
console.log("正在改变" + key + "属性", newValue)
if (val !== newValue) {
val = newValue
}
}
})
}
var obj = {
};
defineReactive(obj, 'c', 10)
console.log(obj.c);//10
三、递归侦测对象全部属性(Observer、observe以及defineReactive是一个循环、递归的关系)
通过以上定义的defineReactive函数无法实现多层对象的劫持。
//自定义defineReactive函数(data->要访问的对象,key->对象中的属性,val->要修改的属性值【闭包】)
function defineReactive(data, key, val) {
console.log("defineReactive", key);
//可以传两个参数
if (arguments.length === 2) {
val = data[key]
}
observe(val);
Object.defineProperty(data, key, {
//可枚举
enumerable: true,
//可以被delete
configurable: true,
//getter:数据劫持
get() {
console.log("正在访问" + key + "属性");
return val;
},
//setter
set(newValue) {
console.log("正在改变" + key + "属性", newValue)
if (val !== newValue) {
val = newValue
observe(newValue);
}
}
})
}
//设置属性能否被枚举
function def(data, key, value, enumerable) {
Object.defineProperty(obj, key, {
value,
enumerable,
writable: true,
configurable: true
})
}
//能将object转化为每个层级的属性都是响应式的——可以被侦测的object
class Observer {
constructor(value) {
//给实例添加__ob__属性,不可以被枚举,value为实例
def(value, '__ob__', this, false);
console.log("构造器", value)
this.walk(value)
}
//遍历
walk(value) {
for (const key in value) {
defineReactive(value, key)
}
}
}
//创建observe函数,首先判断
function observe(val) {
if (typeof val !== 'object') return;
var ob;
if (typeof val.__ob__ !== 'undefined') {
ob = val.__ob__;
} else {
ob = new Observer(val);
}
return ob
}
var obj = {
a: {
m: {
n: 5
}
},
b: 10
}
observe(obj);
console.log(obj.a.m.n);
以上就是Vue响应式原理(defineProperty、defineReactive、Observer)——对象object的内容,欢迎大家关注《前端面试问题》专栏。
我会将自己平时项目中常见的问题以及笔试面试的知识在CSDN与大家分享,一起进步,加油。