Vue2、3 和 React 原理对比

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的响应式差别

Vue 响应式对比
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 对象,包含了 tagpropschildren 三个属性。

1)减少 JavaScript 操作真实 DOM 的带来的性能消耗;

2)抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是小程序,也可以是各种 GUI。

diff 算法

diff 算法是进行虚拟节点 Element 的对比,并返回一个 patchs 对象,用来存储两个节点不同的地方,最后用 patchs 记录的消息去局部更新 Dom。

遍历patches,然后得到每个真实 DOM 和其对应的 patch,然后在真实 DOM 上进行更新。

vdom + diff

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 的信息,生成一棵新的树。这棵新树每生成一个新的节点,都会将控制权交回给主线程,去检查有没有优先级更高的任务需要执行。如果没有,则继续构建树的过程。

tree-shaking

 webpack 2 或 Rollup 打包工具进行“tree-shaking”,将用不到的代码排除出最终的包。