Vue数据双向绑定原理(vue2向vue3的过渡)
众所周知,Vue的两大重要概念:
数据驱动
组件系统
接下来我们浅析数据双向绑定的原理
一、vue2
1、认识defineProperty
vue2中的双向绑定是基于defineProperty的get操作与set操作,那么我们简单认识下defineProperty,
作用: 就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性。
那么我们先来看下Object.getOwnPropertyDescriptor(),有定义方法就会有获取方法,对这就是与defineProperty相对的方法,它可以获取属性值。
var a ={
b:1,
c:2
}
console.log(Object.getOwnPropertyDescriptor(a,'b')); //查看获取b属性
这就是打印出来的结果,configurable的意思是可便利,enumerable的意思是可枚举,writable的意思是可写。
2、使用defineProperty实现简单的私有变量
知道了属性内部的属性值,我们可以使用defineProperty来实现一个私有变量。
var a ={
b:1,
c:2
}
Object.defineProperty(a,'b',{
writable:false //不可写
})
console.log(Object.getOwnPropertyDescriptor(a,'b')); //查看获取b属性
发现,改变不了a.b的值。
另外,还可以使用一个很快捷的方法实现私有变量
var a ={
b:1,
c:2
}
Object.freeze(a,'b'); //冻结,可理解为变为私有属性
// Object.seal(a,'b'); //configurable置为false
console.log(Object.getOwnPropertyDescriptor(a,'b')); //查看获取b属性
同样,writable为false。如果使用seal()只能使configurable为false。
3、简单认识defineProperty的get、set的方法
我们先不要创建全局变量,跟get方法return值,你会发现值为undefined
于是我们改了改
var a ={
b:1,
c:2
}
var _value=a.b; //b值赋值需要先赋给全局变量,不然使用不了
Object.defineProperty(a,'b',{
get:function(){
console.log('get');
return _value; // get方法必须ruturn值,值为全局变量。
},
set:function(newValue){
console.log(newValue)
_value=newValue; // 赋值
}
})
取到了。
4、使用defineProperty简单实现Vue双向绑定
test.js
// 使用defineProperty
function vue () {
this.$data={
a:1 // 数组的话不会更新
};
this.el=document.getElementById('app');
this._html="";
this.observe(this.$data);
this.render();
};
vue.prototype.observe=function(obj){
var value;
var self=this;
for(var key in obj){
value =obj[key];
if(typeof value === 'object'){
this.observe(value)
}else{
Object.defineProperty(this.$data,key,{
get:function(){
return value;
},
set:function(newvalue){
value=newvalue;
self.render();
}
})
}
}
}
vue.prototype.render=function(){
this._html='I am '+ this.$data.a;
this.el.innerHTML=this._html;
}
// ***************************************
//数组改变更新,装饰者模式
// var arraypro=Array.prototype;
// var arrayob=Object.create(arraypro);
// var arr = ["push","pop","shift"];
// // arr里的方法,既能保持原有的方法,又能触发更新。
// arr.forEach(function(method,index){
// arrayob[method]=function(){
// var ret=arraypro[method].apply(this,arguments);
// console.log('更新');
// return ret;
// }
// })
// var arr=[];
// arr.__proto__=arrayob;
// arr.push(1);
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue双向绑定原理</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="test.js"></script>
<script>
var vm = new vue();
setTimeout(function(){
console.log('changes');
console.log(vm.$data);
vm.$data.a=222;
},2000)
</script>
</html>
这样我们就简单实现了数据双向绑定。
二、vue3
1、简单认识Proxy
就是一种机制,用来拦截外界对目标对象的访问,可以对这些访问进行过滤或者改写,所以Proxy更像是目标对象的代理器。
var ob= {
a:1,
b:2
}
// 新创建一个Proxy对象直接替换ob对象
ob=new Proxy(ob,{
get:function(target,key,receive){ // target指的是原始对象,key指的是属性值,receive指的是这个Proxy对象
console.log(target,key,receive)
return target[key]; //get方法 依然需要return
},
set:function(target,key,newvalue,receive){
console.log(target,key,newvalue,receive) // newvalue指新创建的值
target[key]=newvalue;
}
});
这里需要注意的是:第一次使用proxy时,在new的时候把原对象替换掉。就会触发get方法。
看到上方的代码,我们可以总结Proxy的几个优点:
defineProperty只能监听某个属性,不能对全对象监听;
所以可以省去for in 提升效率;
可以监听数组,不用再去单独的对数组做异性操作。
2、使用Proxy简单实现数据双向绑定
test1.js
// 使用Proxy
function vue () {
this.$data={
a:1
};
this.el=document.getElementById('app');
this._html="";
this.observe(this.$data);
this.render();
};
vue.prototype.observe=function(obj){
var self=this;
this.$data=new Proxy(this.$data,{
get:function(target,key){
return target[key];
},
set:function(target,key,newvalue){
target[key]=newvalue;
self.render();
}
})
}
vue.prototype.render=function(){
this._html='I am '+ this.$data.a;
this.el.innerHTML=this._html;
}
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue双向绑定原理</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="test1.js"></script>
<script>
var vm = new vue();
setTimeout(function(){
console.log('changes');
console.log(vm.$data);
vm.$data.a=222;
},2000)
</script>
</html>
3、Proxy还可以做什么呢?
(1)、校验类型
function createValidator(target,validator){
return new Proxy(target,{
_validator:validator,
set:function(target,key,value,proxy){
if (target.hasOwnProperty(key)) {
var validator1=this._validator[key];
if(validator1(value)){
return Reflect.set(target,key,value,proxy)
}
else{
throw Error('type error')
}
}
}
})
}
var personvalidator={
name(val){
return typeof val==='string'
},
age(val){
return typeof val==='number'&&val>18
}
}
class Person {
constructor(name,age) {
this.name=name;
this.age=age;
return createValidator(this,personvalidator);
}
}
var tss=new Person('maomin',22);
(2)、真正私有变量
var api = {
_secret: 'xxxx',
_otherSec: 'bbb',
ver: 'v0.0.1'
};
api = new Proxy(api, {
get: function (target, key) {
// 以 _ 下划线开头的都认为是 私有的
if (key.startsWith('_')) {
console.log('私有变量不能被访问');
return false;
}
return target[key];
},
set: function (target, key, value) {
if (key.startsWith('_')) {
console.log('私有变量不能被修改');
return false;
}
target[key] = value;
},
has: function (target, key) {
return key.startsWith('_') ? false : (key in target);
}
});
api._secret; // 私有变量不能被访问
console.log(api.ver); // v0.0.1
api._otherSec = 3; // 私有变量不能被修改
console.log('_secret' in api); //false
console.log('ver' in api); //true
作者:Vam的金豆之路
主要领域:前端开发
我的微信:maomin9761
微信公众号:前端历劫之路