虾米带你轻松搞定Vuejs 系列

(十三)Vue.js 运行机制整体概览

今天成都雨夹雪,早上骑车还是很冷的,忙里偷闲整理一下Vue的笔记,从今天开始开始整理深入Vue内部的运行机制的笔记。

整体预览

结合前十二篇笔记介绍的Vue项目入门的构建等等。使自己有个整体的知识体系,今天主要先从整体上了解整个Vue内部整体上的一个认知,就算是热热身吧。

首先:先把vue框架的内部流程图抽象化一下(个人梳理,可能有不同见解,往谅解)

虾米带你轻松搞定Vuejs 系列

这里通过梳理内部整体的流程图,我会在整体上有个大概的认知;下面,为了加深理解,我决定每个模块拆开分析一下。

 

初始化及挂在机制

虾米带你轻松搞定Vuejs 系列

在 new Vue()之后,Vue会调用_init 函数进行初始化,这里就是初始化的过程,它会初始化生命周期、事件、 props、 methods、 data、 computed 与 watch 等。其中最重要的是通过 Object.defineProperty 设置 setter 与 getter 函数,用来实现响应式以及依赖收集,后面会详细讲到,这里只要有一个印象即可。

初始化之后调用 $mount 会挂载组件,如果是运行时编译,即不存在 render function 但是存在 template 的情况,需要进行编译步骤。

编译

compile编译可以分成 parse、optimize 与 generate 三个阶段,最终需要得到 render function。

 

虾米带你轻松搞定Vuejs 系列

 

虾米带你轻松搞定Vuejs 系列

从代码中也可以看出来这三个阶段的顺序

虾米带你轻松搞定Vuejs 系列

 

parse

parse 会用正则等方式解析 template 模板中的指令、class、style等数据,形成AST(抽象语法树)。

 

optimize

optimize 的主要作用是标记 static 静态节点,这是 Vue 在编译过程中的一处优化,后面当 update 更新界面时,会有一个 patch 的过程, diff 算法会直接跳过静态节点,从而减少了比较的过程,优化了 patch 的性能。

 

generate

generate 是将 AST 转化成 render function 字符串的过程,得到结果是 render 的字符串以及 staticRenderFns 字符串。

 

在经历过 parse、optimize 与 generate 这三个阶段以后,组件中就会存在渲染 VNode 所需的 render function 了。

 

 

响应式

虾米带你轻松搞定Vuejs 系列

这里的 getter 跟setter 就不说了很简单,在init的时候通过 Object.definePropery 进行了绑定,它的作用就是当被设置的对象被读取是执行getter 方法,而在被复制的时候就会执行setter 方法。

 

当 render function 被渲染的时候,因为会读取所需对象的值,所以会触发 getter 函数进行依赖收集,依赖收集的目的是将观察者 Watcher 对象存放到当前闭包中的订阅者 Dep 的 subs 中。形成如下所示的这样一个关系。

虾米带你轻松搞定Vuejs 系列

 

虾米带你轻松搞定Vuejs 系列

 

虾米带你轻松搞定Vuejs 系列

 

虾米带你轻松搞定Vuejs 系列

在修改对象的值的时候,会触发对应的 setter, setter 通知之前依赖收集得到的 Dep 中的每一个 Watcher,告诉它们自己的值改变了,需要重新渲染视图。这时候这些 Watcher 就会开始调用 update 来更新视图,当然这中间还有一个 patch 的过程以及使用队列来异步更新的策略,这个稍微研究一下,比较多后面再研究吧。

 

Virtual DOM

我们知道,render function 会被转化成 VNode 节点。Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。

 

Updata View

虾米带你轻松搞定Vuejs 系列

在修改一个对象值的时候,会通过 setter -> Watcher -> update 的流程来修改对应的视图,那么最终是如何更新视图的呢?

 

当数据变化后,执行 render function 就可以得到一个新的 VNode 节点,如果想要得到新的视图,最简单粗暴的方法就是直接解析这个新的 VNode 节点,然后用 innerHTML 直接全部渲染到真实 DOM 中。但是其实只对其中的一小块内容进行了修改,这样做似乎有些浪费。

 

那么我们为什么不能只修改那些改变了的地方呢?这个时候就要介绍我们的patch了。我们会将新的 VNode 与旧的 VNode 一起传入 patch 进行比较,经过 diff 算法得出它们的差异。最后我们只需要将这些差异的对应 DOM 进行修改即可。

 

总结

回过头再来看看这张整体内部运行流程图,是不是大脑中已经有一个大概的脉络了呢。今天确实太冷了,先到这里吧。未完待续.......