在vue中,data选项中定义的数据
数据改变,视图自动响应,我们把这个情况称之为: 响应式原理
底层代码实现响应式原理
vue2.0响应式原理的核心: es5的属性Object.defineProperty(对象,对象的key,描述符【控制对象属性】)
Object.defineProperty不支持ie8以及以下,所以vue就不支持了
对象拦截,数组劫持
如何实现响应式原理呢?
// 1. 初始化数据定义
const data={
name:'2003',
age:18,
sex:'nan',
car:{
price:2000
},
arr:[1,2,3,4]
}
// 2. render函数表示视图渲染了
function render(){
console.log('视图更新');
}
// 3. observer就是观察者,用于观察data的变化
function observer(data){
// data就是observer观察的内容
if(!data) return;//null
if(Array.isArray(data)){//数组的情况
data.__proto__=arrNewProto;
};
if(typeof data==='object'){//对象的情况
// 对data选项中的数据做拦截
for(let key in data){
reactive(data,key,data[key]);
}
}
}
// 4. reactive函数是对对象中的每一个属性做拦截
function reactive(obj,key,value){
//这里的value也有可能是一个对象————递归解决
observer(value);
// vue2.0响应式原理的核心: es5的属性Object.defineProperty(对象,对象的key,描述符【控制对象属性】)
// Object.defineProperty不支持ie8以及以下,所以vue就不支持了
Object.defineProperty(obj,key,{
get(){// 别名: getter
//一般是对 对象中属性名的属性值做初始化设置
return value;//每个属性对应一个初始值
},
set(val){// 别名: setter val就是新值
// 用于获取对象的属性的属性值修改后的新值
console.log('val',val);
if(val!=value){
observer(val);//传入的新值也有可能是对象,所以新值也需要做响应式拦截
render();//监听到了走render函数
}
}
})
}
//5. 对数组的方法进行劫持
//5.1数组的原生方法不能用?就拷贝一份数组的新的原型,上面就有数组的方法
const arrNewProto=Object.create(Array.prototype);
//5.2对哪些方法进行重写呢?
const arr=['push','splice','pop','shift','unshift'];
arr.forEach(item=>{
arrNewProto[item]=function(){//相当于给我们自己克隆出来的原型身上封装方法
//还需要调用原生的push
render();
Array.prototype[item].call(...arguments);//???为什么我这里不能加入第一个参数null
}
})
observer(data);//成功监听到data中name值得变化
// data.name='zhangsan';
// data.car.price=2000;
//data.arr.push(5);
一步步进行推导
1.问题:初始化数据data是一个对象,修改对象中的数据时,data.name=‘zhangsan’;
2.问题:当初始化数据data是一个二层结构时,监听不到二层结构?
解决方案:递归解决
3.问题:当set函数中传入的val值与原先的值相同时,那就不需要进行视图渲染
解决方案:传入的新值与旧值进行判断
4.问题:如果传入的新值是一个对象呢?
解决方案:observer(val);传入的新值也有可能是对象,所以新值也需要做响应式拦截
5.问题:传入的值也有可能是数组?
思考:数组的方法是原生的,不允许更改原生的方法,所以对数组进行劫持,就需要从写数组的方法
解决方案:对数组进行劫持
原生的方法监听不到,一执行自己写的push方法,就能监听到,render函数一运行,就证明你做了push操作