本文小编为大家详细介绍“Vue响应式流程及原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue响应式流程及原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。执行init操作。包括且不限制initLifecycle
、initState
等执行mount。进行元素挂载compiler步骤在runtime-only版本中没有。compiler步骤对template属性进行编译,生成render函数。一般在项目中是在.vue
文件开发,通过vue-loader处理生成render函数。执行render。生成vnoderender例子,如下对应手写的render函数patch。新旧vnode经过diff后,渲染到真实dom上执行$mount
。实际执行mountComponent
这里会实例化一个WatcherWatcher中会执行get
方法,触发updateComponent
执行updateComponent
。执行vm._update(vm._render(), hydrating)
执行vm.render()
。render其实调用createElment
(h
函数)根据tag的不同,生成组件、原生VNode并返回执行vm.update()
。createElm()
到createChildren()
递归调用将VNode转化为真实的dom,并且最终渲染到页面这里以如下代码案例讲解更加清晰~没错,就是这么熟悉!就是一个初始化的Vue项目{{msg}}主要讲解组件跟普通元素的不同之处,主要有2点:如何生成VNode——创建组件VNodecreateComponent
如何patch——组件new Vue到patch流程createComponent
$vnode:占位符vnode。最终渲染vnode挂载的地方。所有的组件通过递归调用createComponent直至不再存在组件VNode,最终都会转化成普通的dom。_vnode:渲染vnode。(注意:这一步对应上免费云主机域名图render流程的紫色块的展开!!!)区分普通元素VNode普通VNode:tag是html的保留标签,如tag: 'div'
组件VNode:tag是以vue-component
开头,如tag: 'vue-component-1-App'
(注意:这一步对应上图patch流程的紫色块的展开!!!)相信你看完细粒度的Vue组件化过程可能已经晕头转向了,这里会用一个简化版的流程图进行回顾,加深理解案例代码这里会从Observer、Dep、Watcher三个对象进行讲解,分object
、array
两种依赖收集方式。一定要注意!数组
的依赖收集 跟对象的属性
是不一样的。对象属性经过深度遍历后,最终就是以一个基本类型的数据为单位收集依赖,但是数组仍然是一个引用类型。如果这里不懂,先想一个问题: 我们用this.msg = 'xxx'
能触发setter
派发更新,但是我们修改数组并不是用this.arr = xxx
,而是用this.arr.push(xxx)
等修改数组的方法。很显然,这时候并不是通过触发arr
的setter
去派发更新的。那是怎么做的呢?先带着这个问题继续往下看吧!三个核心对象:Observer
(蓝)、Dep
(绿)、Watcher
(紫)依赖收集准备阶段——Observer、Dep的实例化注意对象、数组的不同处理方式。这里以 核心代码 + 图 进行讲解接下来核心分析defineReactive
做了什么。注意childOb
,这是数组进行依赖收集的地方(也就是为什么我们this.arr.push(4)
能找到Watcher
进行派发更新)依赖收集触发阶段——Wather实例化、访问数据、触发依赖收集Dep.target相关讲解targetStack:栈结构,用来保存Watcher
pushTarget:往targetStack
中push
当前的Watcher
(排在前一个Watcher的后面),并把Dep.target
赋值给当前Watcher
popTarget:先把targetStack
最后一个元素弹出(.pop),再把Dep.target
赋值给最后一个Watcher
(也就是还原了前一个Watcher)通过上述实现,vue保证了全局唯一的Watcher
,准确赋值在Dep.target
中细节太多绕晕了?来个整体流程,从宏观角度再过一遍(computed部分可看完彩蛋后再回来重温一下)派发更新区分对象属性、数组方法进行讲解如果想要深入了解组件的异步更新,戳这里,了解Vue组件异步更新之nextTick。本文只针对派发更新流程,不会对异步更新DOM进行展开讲解~这里可以先想一下,以下操作会发生什么?this.msg = 'new val'
this.arr.push(4)
是的,毫无疑问都会先触发他们之中的get
,那再触发什么呢?我们接下来看对象属性修改触发set,派发更新。this.msg = 'new val'
数组调用方法。this.arr.push(4)
这里可以联合数组的依赖收集再看一遍,你就恍然大悟了。为什么对象的属性、数组的依赖收集方式不一样整个new Vue阶段、到依赖收集、派发更新的全部流程就到这里结束了。可以纵观流程图看出,Vue应用就是一个个Vue组件组成的,虽然整个组件化、响应式流程很多,但核心的路径一旦走通,你就会恍然大悟。案例代码我们先看流程图。图有点大~大家可以放大看看,每个核心步骤都附有文字说明根据案例概括一下,加深理解computed中的name
其实就是一个computed Watcher,这个Watcher在init
阶段生成当App组件render的阶段,render函数会访问到模版中的{{ name }}
,则会触发computed的求值,也就是执行上面代码computedGetter()
。执行watcher.evaluate()
。也就是执行wathcer.get
。上文依赖收集的第3点:依赖收集触发阶段有对get方法进行讲解,忘了的可以上去回顾一下执行watcher.depend()
代码中判断watcher.dirty
标志是什么?有什么用?只有computed的值发生改变(也就是其依赖的数据改变),watcher.dirty
才会被设为true只有watcher.dirty
为true
才会对computed进行 求值 或 重新求值总结:也就是组件每次render,如果computed的值没改变,直接返回value值(是不需要重新计算的),这也是computed的一个特点首先pushTarget
把Dep.target
从App组件的渲染Watcher改为name的computed Watcher其次执行cb:function() { return this.firstName + this.secondName }
执行cb的过程中,必然会访问到firstName
、secondName
,这时候就是我们熟悉的依赖收集阶段了。firstName、secondName都会把name这个computed watcher收集到自己的dep.subs[]
中最后popTarget
把name的computed Watcher弹出栈,并恢复Dep.target
为当前App组件的渲染Watcher遍历computed watcher的deps。其实就是firstName、secondName实例的Depdep.depend
也就是调用watcher.addDep
(把Dep收集进watcher.deps中),再由watcher.appDep调用dep.addSub
(把Watcher收集进dep.subs中)这样一来,就完成了firstName、secondName对App组件的渲染watcher进行收集结果如下。响应式数据中会存在两个Watcher至于为什么响应式数据要收集2个watcher?下文computed派发更新会讲解讲到这里,我以自己的理解讲解下文章开头引言的问题:为什么Watcher、Dep多对多且相互收集?这可能也是大家阅读Vue源码中一直存在的一个疑惑(包括我自己刚开始读也是这样)对的,当然是为了computed中的响应式数据收集渲染Watcher啦!!!还有!!!还记得前文中依赖收集的第3点——依赖收集触发阶段的代码讲解中我写了很多注释的cleanupDeps
吗?cleanupDeps
的作用就是清除掉当前没有使用到的响应式数据。怎么清除?我们往下看首先看个案例回答个问题,代码如下。当flag为true时,msg2
并没有渲染在页面中,那么此时我们点击按钮修改msg2
的值会不会、或者应不应该触发这个组件的重新渲染呢?答案肯定是不会、不应该。所以:cleanupDeps
就是为此而存在的那cleanupDeps
是怎么工作的呢?接着看下面代码到此,你是否已经懂得了watcher中为什么要收集自己观测的响应式数据对应的dep呢?派发相对来说比较简单了~跟响应式的派发更新基本一致,继续以案例来讲解吧!当我们修改firstName会发生什么?this.firstName = 'change'
首先触发firstName的set,最终会调用dep.notify()
。firstName的dep.subs中有2个watcher,分别执行对应watcher的notifycomputed watcher:将dirty属性置为true。渲染watcher会执行派发更新流程(如本文响应式流程——2.派发更新一致)nextTick阶段执行flushSchedulerQueue
,则会执行watcher.run()
watcher.run会执行watcher.get方法,也就是重新执行render、update的流程执行render又会访问到name的computed,从而又会执行computedGetter
此时的watcher.dirty在本步骤3已经置为true,又会执行watcher.evaluate()
进行computed的求值,执行watcher.depend()
……后续的流程就是派发更新的流程了~user Watcher的依赖收集相比computed会简单一点,这里不会赘述太多,只说核心区别,还有watch的常用配置immediate
、deep
、sync
user Watcher在init阶段会执行一次watcher.get()
,在这里会访问我们watch的响应式数据,从而进行依赖收集。回顾下computed,computed在这个阶段什么也没做。如果userWatcher设置的immediate: true
,则会在new Watcher后主动触发一次cb的执行deep
逻辑很简单,大概讲下:深度遍历这个对象,访问到该对象的所有属性,以此来触发所有属性的getter。这样,所有属性都会把当前的user Watcher收集到自己的dep中。因此,深层的属性值修改(触发set派发更新能通知到user Watcher),watch自然就能监测到数据改变~感兴趣的同学可以自己去看看源码中traverse
的实现。sync
。当前tick执行,以此能先于渲染Wathcer执行。不设置同步的watcher都会放到nextTick中执行。读到这里,这篇“Vue响应式流程及原理是什么”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注百云主机行业资讯频道。
这篇“el-upload多选文件上传报错如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“el-upload多选文件上传报错如何解决”文章吧…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。