简单的描述一下 Vue 2.x 的运行机制(需要注意分析的是 Runtime + Compiler 的 Vue.js)。
初始化流程:
init
过程会初始化生命周期,初始化事件中心,初始化渲染、执行beforeCreate
周期函数、初始化 data
、props
、computed
、watcher
、执行created
周期函数等。$mount
方法对Vue实例进行挂载(挂载的核心过程包括模板编译、渲染以及更新三个过程)。render
方法而是定义了template
,那么需要经历编译阶段。需要先将template
字符串编译成 render function
,template
字符串编译步骤如下 :
parse
正则解析template
字符串形成 AST(抽象语法树,是源代码的抽象语法结构的树状表现形式)optimize
标记静态节点跳过 DIFF 算法(DIFF 算法是逐层进行比对,只有同层级的节点进行比对,因此时间的复杂度只有 O(n)。generate
将 AST 转化成render function
字符串render function
后,调用$mount
的mountComponent
方法,先执行beforeMount
钩子函数,然后核心是实例化一个渲染Watcher
,在它的回调函数(初始化的时候执行,以及组件实例中监测到数据发生变化时执行)中调用updateComponent
方法(此方法调用render
方法生成虚拟 Node,最终调用update
方法更新 DOM)。render
方法将render function
渲染成虚拟的Node(真正的 DOM 元素是非常庞大的,因为浏览器的标准就把 DOM 设计的非常复杂。如果频繁的去做 DOM 更新,会产生一定的性能问题,而 Virtual DOM 就是用一个原生的 JavaScript 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多,而且修改属性也很轻松,还可以做到跨平台兼容),render
方法的第一个参数是createElement
(或者说是h
函数),这个在官方文档也有说明。update
方法,update
方法又会调用pacth
方法把虚拟 DOM 转换成真正的 DOM 节点。需要注意在图中忽略了新建真实 DOM 的情况(如果没有旧的虚拟 Node,那么可以直接通过createElm
创建真实 DOM 节点),这里重点分析在已有虚拟 Node 的情况下,会通过sameVnode
判断当前需要更新的 Node节点是否和旧的 Node 节点相同(例如我们设置的key
属性发生了变化,那么节点显然不同),如果节点不同那么将旧节点采用新节点替换即可,如果相同且存在子节点,需要调用patchVNode
方法执行 DIFF 算法更新 DOM,从而提升 DOM 操作的性能。需要注意在初始化阶段,没有详细描述数据的响应式过程,这个在响应式流程里做说明。
响应式流程:
init
的时候会利用Object.defineProperty
方法(不兼容 IE8)监听Vue实例的响应式数据的变化从而实现数据劫持能力(利用了 JavaScript 对象的访问器属性get
和set
,在未来的 Vue3 中会使用 ES6 的Proxy
来优化响应式原理)。在初始化流程中的编译阶段,当render function
被渲染的时候,会读取Vue实例中和视图相关的响应式数据,此时会触发getter
函数进行依赖收集(将观察者Watcher
对象存放到当前闭包的订阅者Dep
的subs
中),此时的数据劫持功能和观察者模式就实现了一个 MVVM 模式中的 Binder,之后就是正常的渲染和更新流程。setter
函数,setter
会通知初始化依赖收集中的Dep
中的和视图相应的Watcher
,告知需要重新渲染视图,Wather
就会再次通过update
方法来更新视图。可以发现只要视图中添加监听事件,自动变更对应的数据变化时,就可以实现数据和视图的双向绑定了。
原文:https://www.cnblogs.com/0314dxj/p/13953602.html