我们知道Vue可以实现数据双向绑定,Angular和Vue都是采用的MVVM 模式,意思就是当M(模型层)层数据进行修改时,VM层会监测到变化,并且通知V(视图层)层进行相应的修改,反之修改V层则会通知M层数据进行修改,实现了视图与模型层的相互解耦。其中Angular是采用的脏值检测实现的,Vue是采用的发布-订阅模式+数据劫持 实现的。
Vue是通过Object.defineProperty()
来实现对属性的劫持,达到监听数据变动的目的。当你把一个普通的 JavaScript 对象传给 Vue 实例的 data
选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
那么先看看Vue对数据的包装吧!使用 Object.getOwnPropertyDescriptor 查看数据属性描述。
var mydata={
name:'cc',
age:'2'
};
var app= new Vue({
el: '#app',
data: mydata
});
console.log(app.name===mydata.name);
console.log(Object.getOwnPropertyDescriptor(app,'name'));
console.log(Object.getOwnPropertyDescriptor(mydata,'name'));
控制台输出:
诶,可以看到确实Vue对data数据进行了处理,这个动作发生在 Vue生命周期的 beforeCreate 和 created 之间。(注意这里可以看到getter\setter的函数名不一样,这是因为将data对象进行数据劫持之后,要在vue实例对象上可访问,将根级响应式属性代理到了vue实例上)
可以试着使用一下Object.defineProperty ,在数据改变的时候得到响应。比如像这样:
var mydata={
name:'cc',
age:'2',
like:{
dog:'二狗子',
cat:'小哈'
}
};
//递归地设置
function DataProcess(data)
{
if (!data || typeof data !== 'object')
return;
Object.keys(data).forEach(function(item){
definePro(data,item,data[item])
}
);
}
function definePro(data,item,val)
{
DataProcess(val);
Object.defineProperty(data,item,{
enumerable: true,
configurable: true,
get: function getter () {
return val;
},
set: function setter (newVal) {
if(val!=newVal)
{
val=newVal;
console.log('数据发生了改变!新值为'+newVal);
}
}
});
}
DataProcess(mydata);
mydata.like.dog='eclipse'; //输出:数据发生了改变!新值为eclipse
mydata.like.dog='eclipse'; //这里无输出
实现过程:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
Observer是一个数据监听器,其实现核心方法就是前文所说的Object.defineProperty( )。如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行Object.defineProperty( )处理。
2.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
Compile在vue中这个动作发生在 Vue生命周期的 created 和 mounted之间
3.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:在自身实例化时往属性订阅器(dep)里面添加自己,自身必须有一个update()方法,待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退
Title
{{msg}}
具体可以参考下面几篇:
vue的双向绑定原理及实现
剖析Vue原理&实现双向绑定MVVM
其他参考:
关于Vue的MVVM
详解vue生命周期
深入响应式原理