Vue 生命周期
Vue 生命周期
- beforeCreate、created
- beforeMount、mounted
- beforeUpdate、updated
- beforeDestory、destory
过程分析
beforeCreate
- $el(DOM):undefined
- $data:undefined
- message: undefined
created (ajax 请求可在此阶段)
- $el(DOM):undefined
- $data:已存在
- message: 已存在
new Vue -> mount 阶段前
- 注意: 之间会有一个 init 的过程
- 这个过程最重要的是完成了
响应式
,依赖收集
响应式
通过defineProperty
来完成该功能
- 该方法其中的 get 来完成依赖收集,
- set 用来当 数值被修改的时候触发
首先,当 new Vue 后,会将传入的 data 里的数据,通过 Vue 的构造函数调用 observer,调用 defineProperty 绑定传入的对象
依赖收集
依赖收集的作用是,如果 data 里面一条数据,视图渲染中不需要,此时就没必要通知到所有地方更新,这就是依赖收集的作用
依赖收集采用订阅者模式,首先订阅者需要将所有观察者对象存放,然后还要有一个通知所有观察者的操作,对应观察者实现自己的更新操作
当对象被读的时候,比如把 render function 渲染(render function 后面讲),就会触发 get,进行一个依赖收集。然后当数据被改动的时候,会触发 set,此时通知到所有目标,就能实现响应式和依赖收集。
beforeMount
- $el(DOM):可以打印出来,但是根据文档上看,此时el并不存在,只是使用虚拟DOM占位,$el 真正被创建应该是在 mounted 阶段
<h1>{{ message }}</h1>
- $data:已存在
- message: 已存在
beforeMount 阶段前
- 注意:这里会首先判断对象是否有$el 选项,如果有就继续向下编译,如果没有,生命周期就会暂时停止。直到 vue 实例调用 mount 挂载。
- 注意:判断完$el之后会判断是否有template,如果有就将其编译成render function,如果没有,就会将外部HTML作为模板。所以判断之前要先判断$el,来找对应 template。
template 的编译
然后简单说一下 template 的编译,template 的编译可以分为三个部分 parse,optimize,generate。
- parse 将 template 中的字符串解析,然后得到 class,style 等数据,形成 AST 语法树
- optimize 是一个优化的处理,因为当视图更新的时候,会有一个 patch 的过程,其中会用到 diff 算法,对新老节点进行同层比较,diff 算法后面说。会将差异更新到视图上,静态节点是不需要根据数据变化而变化的,所以 optimize 这层处理会将静态节点编辑,比较的时候会将标记节点跳过,达到优化目的。
- generate 将 AST 语法树转换成 render function,render function 是为了转换成 vNode 节点
mounted
- $el(DOM):已真正存在
- $data:已存在
- message: 已存在
mounted 之前
- 会真正创建$el,在 mounted 之前修改数据都不会触发 update 生命周期,当在 mounted 地方修改数据,会触发 update 生命周期。
- 虽然此时,输出 mounted 阶段的 data,会发现已经是更新过的数据了,但是根据文档来理解,此时的阶段应该还是旧值,不然不会触发 update 周期
beforeUpdate
- 此阶段是虚拟 DOM 和 patch 之前的阶段,可以在此阶段改动 data 值
updated
- 此阶段是虚拟 DOM 和 patch 都完成的阶段,不要在此阶段更改 data,容易造成循环。
update 过程中的 patch 机制
diff 算法是能更快的去查找新老节点的区别。
新老节点比对,比如说老节点不存在或者新节点不存在,作相应的添加删除即可。
重点说 sameVnode、patchVnode 过程。
sameVnode
判断是否满足 sameVnode
首先是判断,只有当 key、tag、isComment(是否为注释节点),data 同时定义(或不定义),如果是 input 满足 type 相同的情况下,此种情况为 sameVnode。
patchVnode
符合 sameVnode 情况下触发。同层比较。时间复杂度从 O(n^3) -> O(n)
O(n^3):老的树结构每一个节点都要和新的树结构所有节点比较,这是 O(n^2),计算变化还要一遍,得出 O(n^3)
- 新老节点相同,则直接返回。
- 当新老节点都是静态的(optimize),而且 key 相同时,直接从老节点拿过来即可。
- 如果是文本节点的话,直接设置 text
- 非文本情况下,然后就是新节点不存在或者老节点不存在,相应的新节点添加或者删除
- 非文本情况下,重点说,新老节点都存在且不相同的情况下执行 updateChildren,分别定义新老节点 start 和 end 的位置。首先是,while 循环,新老节点的两个指向节点会向中间靠拢。两两比对,四种情况。
- start 都相同,start 分别后移
- end 都相同,end 分别前移
- 交叉情况,oldStart 与 newEnd 相同,把 oldStart 节点移动到 oldEnd 节点后面,oldStart 后移,newEnd 前移
- 交叉情况,newStart 与 oldEnd 相同,把 oldEnd 节点移动到 oldStart 节点前面,newStart 后移,oldEnd 前移
如果上述情况都不满足,那么会在 old 节点里面找有无对应节点,有的话放到指定位置,没的话新建一个,新 newStart 节点插入到 oldStart 老节点前面。同时 newStart 后移一位。
注意:移动的都是 elm,可以理解为 dom 移动,diff 比对过程中的节点不会移动。
- 最后一步,就是 while 循环结束以后,分两种情况
- oldStart > oldEnd,说明新节点多,须将新节点插入
- newStart > oldEnd,说明老节点多,须将老节点删除
beforeDestory
destory
最后就是销毁前和销毁后的状态了
- 感谢你的欣赏!