响应式原理:
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
1.搞清楚什么是Object.defineProperty
Object.defineProperty():直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。
Object.defineProperty(object ,propertyname,descriptor )
接收三个参数
object (要修改的对象)
propertyname (属性名)
-
descriptor (属性描述)
1、value 属性的值,
2、writable 是否可操作属性值 ,
3、configurable 是否可修改配置
4、enumerable 是否可枚举
2.defineProperty的get与set
- getter :是一种获得属性值的方法
- setter:是一种设置属性值的方法。
var obj = {},book = '三国演义';
Object.defineProperty(obj,'book',{
get: function(){
//返回经过处理后的变量
return '<<'+book+'>>'
},
set: function(val){
//利用临时变量接收赋值
book = val
}
})
console.log(obj.book) ==> "<<三国演义>>"
obj.book = '西游记'
console.log(obj.book) ==> "<<西游记>>"
3.数据变化,vue如何视图更新的
数据更改 ---->get进行依赖收集,找到哪些组件数据更改----> Set触发Notify ----> 更改对应的虚拟DOM ----> 重新render
4.什么是观察者
一种实现一对多关系解耦的行为设计模式。
它主要涉及两个角色:观察目标、观察者。如图:
特点:观察者 要直接订阅 观察目标,观察目标 做出通知后,观察者 就要进行相应处
5.什么是依赖收集
也就是被依赖者要知道依赖者的存在
const obsObject = observable({
A: 1,
B: 2,
C: 3
});
autoRun(() => {
console.log(obsObject.A + obsObject.C);
console.log(obsObject.C - obsObject.A);
});
obsObject.B = 4; // 什么都没有发生
obsObject.A = 5;
// --> 8 observe函数的回调触发了
// --> -2 observe函数的回调触发了
obsObject.C = 6;
// --> 11 observe函数的回调触发了
// --> 1 observe函数的回调触发了
依赖收集:通过自然地使用变量,来完成依赖的收集,当变量改变时,根据收集的依赖判断是否需要触发回调。
Object.defineProperty实现方式:
//定义一个对象
const hero ={
name: '赵云',
hp: 3000,
sp: 150,
equipment: ['马', '矛']
};
//创建观察
class Observable {
constructor(obj) {
return this.walk(obj);
}
walk(obj) {
const keys = Object.keys(obj);
keys.forEach((key) => {
this.defineReactive(obj, key, obj[key]);
})
return obj;
}
defineReactive(obj, key, val) {
if (Array.isArray(obj[key])) {
// Array添加push的钩子
Object.defineProperty(obj[key], 'push', {
value() {
this[this.length] = arguments[0];
}
})
Object.defineProperty(obj, key, {
get() {
return val;
}
})
} else {
Object.defineProperty(obj, key, {
get() {
return val;
},
set(newVal) {
val = newVal;
}
})
}
}
}
通过defineReactive函数对数据对象“英雄”的各属性get与set设置了钩子,在get中响应依赖收集,在set中触发监听函数,至此该数据对象变得“可观察”。
观察者
Watcher(hero, 'health', () => {
return hero.hp > 2000 ? '强壮' : '良好';
});