vue2响应式的精髓是defineProperty属性。
基础知识
获取属性标签:getOwnPropertyDescriptor
var obj={a:1,b:2};
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//输出:{value: 1, writable: true, enumerable: true, configurable: true}
设置属性标签:defineProperty
var obj={a:1,b:2};
Object.defineProperty(obj,'b',{
writable:false,//不可被重写
enumerable:false,//不可被for..in..循环遍历
configurable:false //不可被delete
});
console.log(obj.b,obj.a);
//输出:2 1
obj.b=2222;obj.a=1111;
console.log(obj.b,obj.a);
//输出:2 1111
for(var item in obj){
console.log(item);
}
//输出:a
console.log(delete obj.a); //输出:true
console.log(obj); //输出:{b: 2}
console.log(delete obj.b);//输出:false
console.log(obj);//输出:{b: 2}
简易设置:seal和freeze
var obj1={a:1,b:2};
Object.freeze(obj1);
console.log(Object.getOwnPropertyDescriptor(obj1,'a'));
//输出:{value: 1, writable: false, enumerable: true, configurable: false}
var obj2={a:1,b:2};
Object.seal(obj2);
console.log(Object.getOwnPropertyDescriptor(obj2,'a'));
//输出:{value: 1, writable: true, enumerable: true, configurable: false}
get和set
var obj={a:1,b:2};
Object.defineProperty(obj,'b',{
set:function(newVal){
console.log('this newVal is'+newVal);
},
get:function(){
console.log('you get b');
}
});
console.log(obj);
//输出:{a: 1}
console.log(obj.b);
//输出:you get b
//输出:undefined //由此看出get()方法必须return一个值
设置obj.b的值
var obj={a:1,b:2};
var _value=obj.b;
Object.defineProperty(obj,'b',{
set:function(newVal){
console.log('this newVal is '+newVal);
_value=newVal;
},
get:function(){
console.log('you get b');
return _value;
}
});
obj.b=22222;
console.log(obj.b);
//输出:this newVal is 22222
//输出:you get b
//输出:22222
vue2的简单实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
function Vue(){
this.$data={a:1};
this.el=document.getElementById('app');
this.virtualdom='';
this.observe(this.$data);
this.render();
}
Vue.prototype.observe=function (obj) {
var self=this;
var value;
for(var key in obj){
value=obj[key];
if(typeof value==='object'){
this.observe(value);
}else{
Object.defineProperty(this.$data,key,{
get:function () {
//此时vue进行依赖收集
return value;
},
set:function (newVal) {
// 此时vue触发收集的依赖更新
value=newVal;
self.render();
}
});
}
}
};
Vue.prototype.render=function () {
//此时vue读取视图模板,生成AST语法树
this.virtualdom='i am '+this.$data.a;
this.el.innerHTML=this.virtualdom;
};
var vm=new Vue();
setTimeout(function () {
console.log('change');
console.log(vm.$data);
vm.$data.a=444444;
},2000);
</script>
</body>
</html>
vue 3的原理
与vue2的区别,在数据监视方面,改用Proxy。
Proxy对象用于定义基本操作的自定义行为,和defineProperty类似,功能几乎一样,用法上有不同。
defineProperty:对原有对象进行劫持,本身是定义属性标签,使用其做数据双项绑定改变了原数据的属性标签,污染了原对象
Proxy:代理原对象,生成一个新的对象;不用监听某一具体属性,省去了vue2的for..in循环;省去了vue2的_value变量
var obj2={a:11,b:22};
var obj3=new Proxy(obj2,{
get:function (target,key,receiver) {
console.log(target,key,receiver);
//输出:{a: 11, b: 22} "a" Proxy {a: 11, b: 22}
//输出:{a: 11, b: 22} "b" Proxy {a: 11, b: 22}
return Reflect.get(target,key);
},
set:function (target,key,value,receiver) {//value:新值
console.log(target,key,value,receiver);
//输出:{a: 3333, b: 22} "a" Proxy {a: 3333, b: 22}
//输出:{a: 3333, b: 22} "b" Proxy {a: 3333, b: 22}
return Reflect.set(target,key,value);
}
});
console.log(obj3.a);//输出:11
obj3.a=3333;
console.log(obj3.a);//输出:3333
console.log(obj3.b);//输出:22
vue3的数据绑定简易实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div id="app"></div>
<!--<script type="text/javascript" src="js/test.js"></script>-->
<script type="text/javascript">
function Vue(){
this.$data={a:1};
this.el=document.getElementById('app');
this.virtualdom='';
this.observe(this.$data);
this.render();
}
Vue.prototype.observe=function () {
var self=this;
this.$data=new Proxy(this.$data,{
get:function(target,key){
return target[key];
},
set:function (target,key,newVal) {
target[key]=newVal;
self.render();
}
});
};
Vue.prototype.render=function () {
//此时vue读取视图模板,生成AST语法树
this.virtualdom='i am '+this.$data.a;
this.el.innerHTML=this.virtualdom;
};
var vm=new Vue();
setTimeout(function () {
console.log('change');
console.log(vm.$data);
vm.$data.a=444444;
},2000);
</script>
</body>
</html>
virtual dom虚拟层,并不正式存在