Vue 是通过 响应式 和 vdom + diff 来进行数据监听和重新渲染,改动数据后,会主动触发渲染。
数据流双向 + 视图渲染单向
React 只通过 vdom + diff 来实现重新渲染,需要 setState 和 hooks 来手动触发渲染。
单向数据流,自上而下
MVVM
Modal(Plain Javascript Object) + View(Dom) + ViewModal(Dom listeners + Data bindings)
VM 层完成视图和数据的监听。其中的 DOM Listeners 和 Data Bindings 是实现双向绑定的关键。
DOM Listeners 监听页面所有 View 层中的 DOM 元素,当发生变化时,Model 层的数据随之变化。Data Bindings 会监听 Model 层的数据,当数据发生变化时,View 层的 DOM 元素也随之变化。
- 完整版:同时包含编译器和运行时的版本。
- 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
- 运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
当使用vue-loader
的时候,*.vue
文件内部的模板会在构建时预编译成 JavaScript。最终打好的包是不需要编译器的。运行时版本相比完整版体积要小大约 30%。
编译流程
– Parse 模板字符串 -> AST(Abstract Syntax Tree) 抽象语法树
– Transform 转换模板标记 譬如 v-bind v-if v-for v-html 的转换
– Generate AST -> 渲染函数

响应式
主动通知
数据模型仅仅是普通的 JavaScript 对象。修改数据时,视图会进行更新。
property 的变化( getter/setter 操作) =》通知 watcher =》组件实例的渲染

Vue2和Vue3的响应式差别

Object.defineProperty() Vue2
1) 不能监听数组、对象的变化,需要使用 Vue.set 修改属性;
2)递归操作,数据结构复杂并且量大时,性能损耗较大;
3)数组响应式操作,单独书写;
4)所有响应式 data 需要创建组件的时候都进行初始化;
5)异步更新时,使用 Vue.nextTick。
proxy Vue3
1)支持数组代理,不需要单独写监听代码;
2)只代理对象,不用代理属性,不需要递归,使用的是拦截功能;
3)运行时递归,不需要编译时进行遍历;
4)Reflect
方法进行返回。
虚拟 DOM
JavaScript 生成名为 Virtual Dom 的 DOM 副本。是一个普通的 JavaScript 对象,包含了 tag
、props
、children
三个属性。
1)减少 JavaScript 操作真实 DOM 的带来的性能消耗;
2)抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是小程序,也可以是各种 GUI。
diff 算法
diff 算法是进行虚拟节点 Element 的对比,并返回一个 patchs 对象,用来存储两个节点不同的地方,最后用 patchs 记录的消息去局部更新 Dom。
遍历patches,然后得到每个真实 DOM 和其对应的 patch,然后在真实 DOM 上进行更新。

Vue3 虚拟DOM
最长递增子序列 + 双端对比
1)双端对比,减少diff次数(web端多用于展示列表等数据的增删改查,比较适合双端预判);
2)静态判断,静态 dom 不用 diff;
3)Block 概念,模板层做静态分析,v-if 和 v-for 切分出 block,block 内部节点的位置是不变的,更新时只遍历动态节点。
React16 虚拟DOM
fiber tree
链表形式,可以随时中断。
时间切片:
1)任务可以切开;
2)diff 可以中断;(渲染、动画的时候可以中断 diff,保障流畅性)
fiber 树在首次渲染的时候会一次性生成。在后续需要 Diff 的时候,会根据已有树和最新 Virtual DOM 的信息,生成一棵新的树。这棵新树每生成一个新的节点,都会将控制权交回给主线程,去检查有没有优先级更高的任务需要执行。如果没有,则继续构建树的过程。