- 组件data的数据一旦变化,立刻触发视图的更新
- 数据驱动视图的第一步
核心API-Object.defineProperty
1.1回顾Object.defineProperty
Object.defineProperty(obj, prop, descriptor)
obj:
要定义属性的对象。
prop
:要定义或修改的属性的名称或 Symbol 。
descriptor
:要定义或修改的属性描述符。
// 下面代码表示给对象obj1添加一个age属性
let age = 10;
Object.defineProperty(obj1,'age',{
get(){
console.log('get');
return age;
},
set(newVal){
console.log('set');
age = newVal
}
})
obj1.age = 20;
console.log(obj1.age);
1.2通过Object.defineProperty实现响应式
首先这里是用到了数据代理模式,将Vue配置对象中的data配置项中的数据,通过Object.defineProperty挂载到了vue身上,随后我们访问的数据就都是从Vue实例对象上获得的。具体实现看下面的代码:
基本代码
// 监测者函数,负责接受一个数据,并其进行监测
function Observer(data){
// 只监听对象和数组的属性
if(typeof data !== 'object' || data===null) return data;
// 如果data合法,就把他身上的所有属性都通过defineProperty重新定义,并且设置setter和getter方法
// for in 可遍历对象和数组
for(let key in data){
// 封装一个函数
defineReactive(data,key,data[key])
}
}
function defineReactive(data,key,val){
// 核心API
Object.defineProperty(data,key,{
get(){
return val;
},
set(newValue){
val = newValue;
console.log('视图更新');
}
})
}
深度监听
上述代码解决不了深度监听的问题,为了实现深度监听,需要修改一下代码
//调用核心API进行数据代理
function defineReactive(data,key,val){
//这里再调用一次Observer函数实现深度监听,
//也就是如果发现你传过来的val还是一个引用类型,那么继续向下监听,
//如果不是,Observer函数会自动返回
Observer(val)
// 核心API
Object.defineProperty(data,key,{
get(){
return val;
},
set(newValue){
val = newValue;
console.log('视图更新');
}
})
}
监听数组
但是目前这个代码还存在问题,那就是此时无法监听到数组的变化。为了监测到数组的变化,需要进一步修改代码
// 记录Array.prototype
let oldArrayPrototype = Array.prototype;
// Object.create()方法创建一个新对象,接受的参数是该对象的原型
let newArrayPrototype = Object.create(oldArrayPrototype);
// 把常用的方法都重写一遍
['push','pop','splice','shift','unshift'].forEach((func)=>{
newArrayPrototype[func] = function(){
console.log('视图更新');
oldArrayPrototype[func].call(this,...arguments)
}
})
// 监测者函数,负责接受一个数据,并其进行监测
function Observer(data){
// 只监听对象和数组的属性
if(typeof data !== 'object' || data===null) return data;
// 如果是数组,那么就修改其原型
if(Array.isArray(data)) data.__proto__ = newArrayPrototype;
// 如果data合法,就把他身上的所有属性都通过defineProperty重新定义,并且设置setter和getter方法
// for in 可遍历对象和数组
for(let key in data){
// 封装一个函数
defineReactive(data,key,data[key])
}
}
几个缺点
- 为了实现深度监听,如果数据对象比较复杂,那么监听创建的成本就比较高
- 对于数组需要进行额外的操作实现监听
- 新增属性和删除属性是监听不到的,需要通过vue.set、vue.delete