vue.js采用数据劫持结合发布订阅者模式,通过Object.defineProperty()或Proxy劫持各个属性的setter和getter,当数据发生变化时向订阅者发布消息,并触发相应的Listen回调。Vue2.0整体思路的核心:通过Object.defineProperty()来劫持属性,达到监听数据变化的目的。Vue遍历数据对象的所有属性,并使用Object.defineProperty将所有这些属性转换为getter/setter。在访问和修改属性时通知更改。通过以下代码实现对属性的监控:vardata={name:'Pat'};observe(data);data.name='PatWu';//哈哈哈,监听值变了kindeng-->dmqfunctionobserve(data){if(!data||typeofdata!=='object'){return;}//遍历所有属性Object.keys(data).forEach(function(key){defineReactive(data,key,data[key]);});};functiondefineReactive(data,key,val){observe(val);//递归处理子属性Object.defineProperty(data,key,{enumerable:true,configurable:false,get:function(){console.log('Monitoredvalue:',val);returnval;},set:function(newVal){console.log('Monitoredvaluechange:',val,'-->',newVal);val=newVal;}});}在渲染过程中,会访问vm上的数据,此时会触发数据对象的getter。然后每个对象值的getter持有一个dep,当getter被触发时会调用dep.depend()方法,从而实现依赖的收集://...omitfunctiondefineReactive(data,key,val){观察(val);vardep=newDep();//实例化依赖集合对象Object.defineProperty(data,key,{get:function(){//依赖集合dep.depend();//Dep.target.addDep(this)returnval;}//...省略});}dep.target在渲染过程中已经被赋值为renderingwatcher,然后执行addDep方法,然后订阅当前watcher持有的数据在dep的subs中,这个目的是为哪些subs做准备可以在后续数据更改时得到通知。//watcher.jsfunctionaddDep(dep){//...省略dep.addSub(this)}收集依赖监听变化后如何通知订阅者,数据变化触发数据对象的setter,dep会在里面执行setter.notify(),然后调用订阅者的update方法实现数据更新渲染。//...省略functiondefineReactive(data,key,val){observe(val);vardep=newDep();//实例化依赖集合对象Object.defineProperty(data,key,{//...省略set:function(newVal){if(val===newVal)return;console.log('监听值变化:',val,'-->',newVal);val=newVal;dep.notify();//通知所有订阅者}});}functionDep(){this.subs=[];}Dep.prototype={addSub:function(sub){this.subs.push(sub);},notify:function(){this.subs.forEach(function(sub){sub.update();//触发更新渲染});}};Vue3.0实现Vue3.0和Vue2.0的区别只是数据劫持的方式由Object.defineProperty改为Proxy代理,其他代码不变。函数观察者(数据){constthat=this;for(varkeyindata){that.deps[key]=[];//初始化所有订阅者对象{msg:[subscriber],info:[]}}lethandler={get(target,property){returntarget[property];},set(target,key,value){letres=Reflect.set(target,key,value);varwatchers=that.deps[key];watchers.map(item=>{item.update();});返回资源;}}//通过Proxy劫持数据this.$data=newProxy(data,handler);}Object.definePropertyProxy的优缺点:1.Object.defineProperty的缺点不能监听数组:因为数组没有getters和setters,因为数组的长度是不确定的,如果太长,性能负担太大,只能监控属性,不能监控整个对象,需要遍历一个循环属性只能监控属性变化,不能监控属性删除。2.proxy的好处是可以监控数组,监控整个对象而不是属性。拦截方式有13种,功能强大很多,返回新的对象,而不是直接修改原对象;3.proxy的缺点是兼容性不好,不能用polyfill平滑;
