当前位置: 首页 > Web前端 > vue.js

【重学前端框架】Vue中视图更新的原理(一)

时间:2023-03-31 18:50:25 vue.js

前言Vue2.0中响应式处理(数据变化和视图变化)的核心是Object.definedProperty。数据中的变量是响应式变量,但是对于对象类型的变量,在添加新属性或删除属性时,视图无法响应值的变化;对于数组类型的变量,通过数组下标修改属性值,并不是视图不能响应值的变化。通过push、pop、splice等方法修改数组的值,视图可以响应值的变化。为什么会有这样的规定?先从Object.definedProperty的使用规则说起。Object.definedProperty的使用规则1.重新定义{}类型对象属性的规则writeget:function(){console.log('get');returnval;},set:function(newVal){//设置的时候可以添加相应的操作console.log('set:',val);val+=newVal;}});}letobj={name:'成龙大哥',say:':其实我之前是拒绝拍这个游戏广告的,'};Object.keys(obj).forEach(k=>{defineReactive(obj,k,obj[k]);});obj.say='后来试了一下,哇,好热情好好玩';//会触发setobj.age=20//不会触发setconsole.log(obj.name+obj.say,obj);不会为新的对象属性触发set方法。2.重新定义array[]类型对象的属性规则(){console.log('get');returnval;},set:function(newVal){//设置时,可以添加相应的操作console.log('set:',val);val+=newVal;}});}让arr=[1,2,3,4,5];arr.forEach((v,i)=>{defineReactive(arr,i,v);});arr[0]='哦,娜娜娜';//触发setarr.push(6)//不会触发setconsole.log(arr)letarr2=[{name:'1'},{name:'2'},{name:'3'}];arr2.forEach((v,i)=>{defineReactive(arr2,i,v);});arr2.forEach((v,i)=>{v.status=true//不会触发set});arr2.forEach((v,i)=>{v.name=true//不会触发set});arr2[0]={name:2}//将触发集合arr2.splice(1,1,{name:111})//会触发setarr2.push({name:'tt'})//不会触发setarr2.length=2//不会触发set初始化会通过defineProperty设置data中的值是响应式,当给变量赋值时,会触发变量对应的set方法,从而调用视图更新函数。这里要说的是,Object.definedProperty通过数组下标修改值后会触发相应的set方法,通过push、pop等方法修改数组的值后不会触发set方法。但是,考虑到性能问题,Vue定义了一套数组类型数据变化的触发响应规则。通过数组下标修改数组的值不会触发视图更新,但是通过push、pop、shift、unshift等方法修改数组的值可以触发视图更新。Vue官方声明不会触发视图更新:1.对于数组对象2.对于{}对象,Vue对数组的处理默认是Object.definedProperty。至于操作数组的方法,默认是在数组中使用splice来修改数组。设置值时可以触发set(splice(1,1,value),相当于通过数组下标修改属性值)。但是因为Vue已经做了处理,下面的mutation方法可以修改数组的值来触发视图的更新。vue源代码如下:vararrayProto=Array.prototype;vararrayMethods=Object.create(arrayProto);varmethodsToPatch=['push','pop','shift','unshift','splice','sort','reverse'];/***拦截变异方法并发出事件*/methodsToPatch.forEach(function(method){//缓存原始方法varoriginal=arrayProto[method];def(arrayMethods,method,functionmutator(){varargs=[],len=arguments.length;while(len--)args[len]=arguments[len];varresult=original.apply(this,args);varob=this.__ob__;varinserted;switch(方法){case'push':case'unshift':inserted=args;breakcase'splice':inserted=args.slice(2);break}if(inserted){ob.observeArray(inserted);}//notifychangeob.dep.notify();returnresult});});/***定义一个属性。*/functiondef(obj,key,val,enumerable){Object.defineProperty(obj,key,{值:val,enumerable:!!enumerable,writable:true,configurable:true});}即在vue中根据原生Array的原型对象新建了一个对象,并重新定义了数组中的以下方法:'push','pop','shift','unshift','splice','sort','reverse'通过defineProperty把上面的方法定义定义成响应式的,同时对push,unshift做特殊的补充,以及对数组加值时的拼接方法进行处理,直接在Vue中操作上述方法修改数组值,视图即可响应。Object.defineProperty作为vue2.0响应式的核心,在vue3.0中将被Proxy取代。因为前者有一定的局限性,前者无法监控对象属性的增删改查,也无法监控通过native方法对数组的修改。后者提供更多类型数据的拦截和对象更新。很好的支持。参考资料:记一个问答题思考:为什么Vue检测不到数组变化Vue3.0计划