Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScript 对象,修改它则视图(View)会自动更新。这种设计让状态管理变得非常简单而直观
Vue的MVVM模型示意图
Vue数据响应原理图
实现MVVM通用方式
要实现mvvm的双向绑定,就必须要实现以下几点:
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
4、mvvm入口函数,整合以上三者
整体感知virtual DOM
virtual DOM分为三个步骤:
1.createElement(): 用 JavaScript对象(虚拟树) 描述 真实DOM对象(真实树)
2.diff(oldNode, newNode) : 对比新旧两个虚拟树的区别,收集差异
3.patch() : 将差异应用到真实DOM树
有的时候 第二步 可能与 第三步 合并成一步(Vue 中的patch就是这样)
Vue的实现原理总结
- 首先,在实例化的过程中,把一个普通 JavaScript 对象传给 Vue 实例的 data
选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。 - Dep 是一个依赖收集器。data 下的每一个属性都有一个唯一的 Dep 对象,在 get 中收集仅针对该属性的依赖,然后在 set 方法中触发所有收集的依赖。
- 在Watcher中对表达式求值,从而触发数据的get。在求值之前将当前Watch实例设置到全局,使用pushTarget(this)方法。
- 在get()中收集依赖,this.subs.push(sub),set的时候触发回调Dep.notify()。
- Compile中首先将template或el编译成render函数,render函数返回一个虚拟DOM对象(将模板转为 render 函数的时候,实际是先生成的抽象语法树(AST),再将抽象语法树转成的 render 函数)
- 当 vm._render 执行的时候,所依赖的变量就会被求值,并被收集为依赖。按照Vue中 watcher.js 的逻辑,当依赖的变量有变化时不仅仅回调函数被执行,实际上还要重新求值,即还要执行一遍
- 如果还没有 prevVnode 说明是首次渲染,直接创建真实DOM。如果已经有了 prevVnode 说明不是首次渲染,那么就采用 patch 算法进行必要的DOM操作。这就是Vue更新DOM的逻辑。
() => {
vm._update(vm._render(), hydrating)
}
这实际上就做到了 re-render,因为 vm._update 就是开头所说的虚拟DOM中的最后一步:patch
详细表述
图片来源: Vue2.1.7源码学习__HcySunYang