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

Vue2.x响应式原理解析(一)

时间:2023-03-31 17:27:39 vue.js

前言相信大家对Vue的数据响应式原理都不陌生。Vue2使用Object.defineProperty()来实现变化检测,而Vue3使用ES6提供的ProxyAPI来替代之前的defineProperty来实现这个功能。知道了它的响应式原理,那么如何实现数据拦截的方法呢?接下来我们就一步步实现自己的数据拦截库吧!基本概念MVVM框架MVVM是Model-View-ViewModel的简称。它本质上是MVC的改进版本。MVVM就是把View的状态和行为抽象在里面,让我们把ViewUI和业务逻辑分开。当然,ViewModel已经为我们做了这些事情。可以取出Model的数据,帮助处理View因为需要显示内容而涉及到的业务逻辑。MVVM框架三要素:数据响应、模板引擎及其渲染数据响应:监听数据变化并在视图中更新(数据变化可以在视图中响应,即数据响应)——Vue2.x版本:Object.defineProperty()-Vue3.x版本:代理模板引擎:提供描述视图的模板语法-插值:{{}}-指令:v-bind、v-on、v-model、v-for、v-if...rendering:如何将template转为html-template=>vnode=>dom实现数据检测1.基本方法定义//reactive.jsfunctiondefineReactive(obj,key,val){Object.defineProperty(obj,key,{get(){//每次获取到一个值就输出日志,方便调试console.log(`somedatawasget---key:${key},val:${val}`)returnval},set(newVal){if(newVal!==val){//每次赋值输出一个log,方便调试console.log(`newdatawasset---key:${key},val:${newVal}`)val=newVal}}})}至此我们基本实现了一个原始的数据拦截功能,我们来测试一下//reactive.jslettest={}defineReactive(test,'foo','firstblood')//取foo的值test.foo//设置foo的值test.foo='foo'上面我们定义了一个对象test,使用之前写的defineReactive函数对其进行处理,并尝试获取foo的值。此时,当我们运行nodereactive.js,控制台输出显示我们已经成功拦截了测试对象的取值和赋值!虽然现在的defineReactive简易版已经基本实现了对象拦截的操作,但是还是有很多不足,比如:我们需要手动处理对象的每一个属性(key)defineReactive(test,'foo','foo')defineReactive(test,'bar','bar')defineReactive(test,'baz','baz')当对象属性也是对象时,无法继续检测对象属性的属性lettest={foo:{id:1,name:'foo',}}defineReactive(test,'foo',{name:'newFoo'})test.foo.id//节点执行当前文件并输出'somedatawasget---key:foo,val:[objectObject]'//表示只有foo属性检测成功,当检测不到foo的id属性并赋值为对象时,检测无法继续lettest={}defineReactive(test,'foo',{name:'newFoo'})test.foo.name//节点执行当前文件并输出'somedatawasget---key:foo,val:[objectObject]'//表示t只有foo属性检测成功,foo的name属性如果对象新增/删除新属性检测不到lettest={}defineReactive(test,'foo','firstblood')//foo取值test.foo//节点执行并输出'somedatawasget---key:foo,val:firstblood'test.bar//nodeonlyoutputs'somedatawasget---key:foo,val:firstblood'执行后,未检测到bar的值。基于以上不足,我们需要继续完善我们的对象拦截操作2.完善defineReactive-遍历需要响应的对象//reactive.jsfunctionobserve(obj){//对传入的参数做类型判断if(typeofobj!=='object'||obj===null)return//objectReactive:遍历每个key,定义getter和setterObject.keys(obj).forEach(key=>{//调用之前已经写好的拦截方法defineReactive(obj,key,obj[key])})}通过observe方法,我们遍历对象的每一个属性,并设置拦截操作,这样我们只需要将需要拦截的对象交给observe,就可以自动拦截该对象的所有属性constmyData={foo:'foo',bar:'bar',baz:{name:'baz'}}observe(myData)//testmyData.foomyData.bar='newBar'myData.baz.namenode执行上面的代码和控制台输出证明当前对象属性自动拦截功能已经基本实现,但是嵌套对象还是有问题-解决nes的问题tedobjects当对象的属性值也是对象时,我们只需要将对象的属性值交给observe进行处理//reactive.jsfunctiondefineReactive(obj,key,val){observe(val)Object.defineProperty(obj,key,{//...})//...}测试并查看:constmyData={foo:'foo',bar:'bar',baz:{name:'baz'}}observe(myData)myData.baz.namenode执行后控制台输出如下,说明我们已经实现了嵌套对象数据访问解析赋值的检测是对象问题。如果给一个对象的属性赋值的时候,这个值是一个对象,那么我们就需要观察这个属性的值,让它也成为一个检测对象。//reactive.jsfunctiondefineReactive(obj,key,val){observe(val)Object.defineProperty(obj,key,{get(){//...},set(newVal){//...observe(newVal)//新值是对象//...}})//...}-解决添加/删除新属性的问题//reactive.js//添加一个新的set函数来处理functionset(obj,key,val){defineReactive(obj,key,val)}至此,我们实现了一个简单版的数据拦截库!完整版代码如下:/***将对象转换为响应式数据*@param{*}obj需要响应式的对象*@param{*}key属性*@param{*}val值*/functiondefineReactive(obj,key,val){//解决test.baz.a等对象嵌套问题observe(val)Object.defineProperty(obj,key,{get(){console.log(`get${key}:${val}`)returnval},set(newVal){if(newVal!==val){console.log(`set${key}:${newVal}`)val=newVal//赋值是一个对象情况(如test.foo={f1:666})observe(val)}}})}/***对象响应:遍历每个key,定义getter,setter*@param{*}数据需要响应对象*/functionobserve(data){if(typeofdata!=='object'||data===null){return}Object.keys(data).forEach(key=>{defineReactive(data,key,data[key])})}/***添加新属性*@param{*}obj*@param{*}key*@param{*}val*/function$set(obj,key,val){defineReactive(obj,key,val)}结语今天我们基本实现了一个简易版的数据拦截库,那么我们如何使用这个库来实现数据的响应式,让数据呢?关于变化驱动的视图响应?在Vue2.x中如何实现?篇幅有限,且听下一章分解~!