通过地图带你深入了解vue的反应原理
前言
如果自己实现数据驱动模式,如何解决几个问题:
你通过什么方式知道我的数据已经改变了?什么用于同步更新视图?数据劫持——obvserver
我们需要知道数据的获取和变化,数据劫持是最基本的手段。在Obeserver中,我们可以看到如下代码:
Object.defineProperty(obj,key,{enumerable: true,configurable: true,get : function reactiveGetter(){//.},Set :函数反应设置器(newval) {//.}})通过Object.defineProperty的方法,我们可以在数据发生变化或获取时插入一些自定义操作。同样,vue也根据这种方法收集和分发更新。
绑定和更新视图——观察器
从初始化开始,当我们渲染视图时,我们将生成一个观察器,它监视视图中的参数变化并更新视图。代码如下:
//新观察器(VM,updatecomponent,noop,{before () {if (VM。_ ismounted!虚拟机。_ is desired){ call hook(VM,' beforeupdate')},true/* isrenderwatcher */)当然,我们可以保留我们的疑虑:
观察器如何更新视图数据,以及它如何与观察器的特定绑定和更新过程相链接,我们将在后续的依赖项集合中进行解释。
让我们先来谈谈响应系统中涉及的设计模式。
发布-订阅模式
在发布-订阅模式下,发布者和订阅者之间还有一个发布通道。一方面,它从发布者那里接收事件,另一方面,它向订阅者发布事件。订阅者需要从事件通道订阅事件
为了避免发布者和订阅者之间的依赖
vue的响应过程
Vue的响应系统借鉴了数据劫持和发布-订阅模式。
Vue使用Dep作为中介,解耦了观察者和观察者之间的关系,使他们的功能更加清晰。
如何完成依赖项收集和订阅更新?
依赖项收集过程
取决于收集过程
例如
div id=' App ' { message } } { { message 1 } }输入类型=' text ' v-model=' message ' div @ click=' change message ' change message/div/div var App=new vue({ El : ' # App },data: {message:' 1 ',message1:' 2 ',},methods : { change message(){ this。message=' 2'},watch 3360 { message : function(val){ this。消息1=val}
如何理解这个依赖项收集过程?关键在于观察者代码:
get () {pushTarget(this)让value const vm=this . vmtry { value=this.getter . call(VM,由VM调用的this . getter有两种)} catch (e) {//省略}最后{if (this。deep){ traverse(value)} pop target()this。clean deps()}返回值},一个是键值的getter方法,另一个是expOrFn,比如mounted
如何防止重复采集
让我们思考一下什么是重复收集。
我想到一种情况:dep数组中有许多相同的观察器。
例如,renderWatch很容易被重复收集,因为我们在html模板的数据中重用了一个变量。那他怎么变重了?
1.将只收集watch执行get时触发的提取操作
object . definepreproperty(obj,key,{enumerable: true,configurable: true,get : function reactiveGetter(){ const value=getter?吸气器。致电: valif (Dep.target) {dep。depend ()//.}返回值},设置:函数reactive setter (newval) {//.离开notify ()}})仅依赖于dep。目标存在时。Dep.target值仅在观察器执行get方法时存在。
2.当dep依赖时,将判断手表的id
depend(){ if(DeP . target){ DeP . target . AdDeP(this)} } AdDeP(DeP :){ const id=DeP . idif(!this . NewDepds . has(id)){ this . NewDepds . add(id)this . NewDeps . push(dep)if(!这个。德佩德斯。has (id)) {dep。addsub (this)}}我们会发现在dep的依赖过程中,会有一个newDepIds来记录已经存入的dep的ID,当dep已经存储了一个观察器时,就不再执行依赖集合操作了。
调度更新过程
收集过程结束后,听听更新过程。
订阅更新的流程
旧例
div id=' App ' { message } } { { message 1 } }输入类型=' text ' v-model=' message ' div @ click=' change message ' change message/div/div var App=new vue({ El : ' # App },data: {message:' 1 ',message1:' 2 ',},methods : { change message(){ this。message=' 3'},watch 3360 { message 3360 function(val){ this。消息1=val}
当点击事件被触发时,订阅更新过程被触发。
订阅更新流程图:
当renderWatch执行更新时,返回并调用beforeUpdate life钩子,然后执行patch方法来更改视图。
如何防止重复更新
如何防止重复更新?renderWatch将由许多部门收集,如果视图被渲染多次,这将导致性能问题。
事实上,问题在于——queueWatcher
queueWatcher中有两种操作:重复数据删除和异步更新。
函数queueWatcher(watcher){ const id=watcher . idif(has[id]==null){ has[id]=true queue . push(watcher)//.if(!等待){等待=真//.next tick(flushcscheduler queue)} } }事实上,queueWatcher非常简单。所有手表被收集成一个阵列,然后复制。
这至少可以避免renderWatch的频繁更新。
例如,在上面的例子中,message和message1都有一个renderWatch,但是它们只执行一次。
异步更新还可以确保在事件结束后触发视图层的更新,还可以防止renderWatch被重复更新
结局
文章讲述了响应过程的原因,但代码细节并不深刻。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:通过地图带你深入了解vue的反应原理是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。