Vue 和 React 和共通之处在于:
- 数据驱动视图
- 组件化
- Virtual DOM
不同之处在于:
- 核心思想
- 写法
- Diff 算法
- 响应式原理
数据驱动视图
数据变化的时候,相应的视图会得到更新,开发者只需要关注数据的变化而不用再去手动的操作DOM
Vue 数据驱动是通过 MVVM 这种框架来实现的,MVVM框架主要包含3个部分:Model、View和 ViewModel
- Model:指的是数据部分,对应到前端就是 JavaScript 对象
- View:指的是视图部分,对应前端就是 DOM
- ViewModel:就是连接视图与数据的中间件(getter/setter)
再来看 React, 需要先了解以下概念
- pending:当前所有等待更新的
state队列
- isBatchingUpdates:React中用于标识当前是否处理批量更新状态,默认false
- dirtyComponent:当前所有待更新state的
组件队列
React 通过setState
实现数据驱动视图,通过setState
来引发一次组件的更新过程从而实现页面的重新渲染(除非shouldComponentUpdate返回false):
setState()
首先将接收的第一个参数state存储在pending队列中(state)- 判断当前React是否处于批量更新状态,是的话就将需要更新state的组件添加到dirtyComponents中(组件)
- 不是的话,它会遍历dirtyComponents的所有组件,调用updateComponent方法更新每个dirty组件(开启批量更新事务)
响应式原理
Vue
- Vue 依赖收集,自动优化,数据可变
- Vue 递归监听 data 的所有属性,直接修改
- 当数据发生改变,自动找到引用数据的组件,进行重新渲染
React
- React 基于状态机,手动优化,数据不可变,需要 setState 驱动新的 State 替换老的 State
- 当数据改变时,以组件为根目录,默认全部重新渲染
组件化
- 组件是独立和可复用的代码组织单元,它能够大幅提高应用开发效率、测试性、复用性,使开发者使用小型、独立和通常可复用的组件构建大型应用
- 调试方便,可维护性高,使得整个应用的耦合度降低
Vue 和 React 通过将页面拆分成一个一个小的可复用单元来提高代码的复用率和开发效率
- React 推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js
- Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 HTML、CSS、JS 写在同一个文件(vue也支持JSX写法)
虚拟DOM
虚拟 DOM(Virtual DOM)本质上是JS 和 DOM 之间的一个映射缓存,它在形态上表现为一个能够描述 DOM 结构及其属性信息的 JS 对象,它主要存储在内存中。主要来说:
- 虚拟dom是一个js对象,存储在内存之中。
- 虚拟dom能够描述真实dom(存在一个对应关系)
- 当数据变化的时候,生成新的DOM,对比新旧虚拟 DOM 的差异,将差异更新到真实 DOM 上(也就是Diff过程)
- 减少直接操作 DOM(框架给我们提供了屏蔽底层 DOM 书写的方式,减少频繁的整更新 DOM ,同时也使得数据驱动视图)
- 为函数式UI编程提供可能
- 可以跨平台,渲染到DOM(web)之外的平台。比如ReactNative,Weex
Vue 和 React 通用流程:vue template/react jsx -> render函数 -> 生成VNode -> 当有变化时,新老VNode Diff -> Diff算法对比,并真正去更新真实 DOM
两者对Diff算法的优化基本上思路是相同的(Diff 算法借助元素的 Key 判断元素是新增、删除、修改,从而减少不必要的元素重渲染):
- tag不同认为是不同节点
- 只比较同一层级,不跨级比较
- 同一层级的节点用key唯一标识,tag和key都相同则认为是同一节点
DOM 的更新策略不同:
- React 会自顶向下全 Diff
当状态发生改变时,组件树就会自顶向下的全 Diff, 重新 render 页面, 重新生成新的虚拟 DOM TREE, 新旧 DOM TREE 进行比较, 进行 patch 打补丁方式,局部更新 DOM - Vue 会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树
把这些 data 属性 全部转为 getter/setter。同时 watcher 实例对象会在组件渲染时,将属性记录为dep,当 dep 项中的 setter 被调用时,通知 watcher 重新计算,使得关联组件更新
核心思想
Vue 的核心思想是尽可能的降低前端开发的门槛,是一个灵活易用的渐进式双向绑定的MVVM框架。它的整体思想仍然是拥抱经典的 HTML (结构)+ CSS (表现)+ JS (行为)的形式,通过 getter/setter 以及一些函数的劫持,能精确知道数据变化,不需要特别的优化就能达到很好的性能
React 的特色在于函数式编程、数据不可变以及单向数据流的理念。函数式编程最大的好处是其稳定性(无副作用)和可测试性(输入相同,输出一定相同),所以通常大家说的React适合大型应用,根本原因还是在于其函数式编程
核心思想不同导致的一些差异
主要是写法和API差异
Vue 推崇 template模板、options API,这样更契合传统 Web 开发者的直觉和习惯,但也不可避免的引入了更多概念和API,比如template模板中需要理解slot、filter、指令等概念和api,options API中需要理解watch、computed(依赖收集)等概念和api
而 React 推崇 JSX、 HOC、 all in JS,其本身由于 FP 的特性,本质上核心只有一个Virtual DOM + Diff算法,所以API非常少
组件通信方法
Vue:
- 父->子
- props: 父组件使用props属性向子组件传递数据
- slot
- $refs
- $children
- 子->父
- 事件形式:子$emit,父监听
- $parent
- 兄弟组件
- 中央总线new Bus(),发布/订阅模式,其实此方法也可以用在跨级、父子间通讯
- 借助同一父组件通过props传递数据,也就是利用父组件实现中转传递(会增加子组件和父组件之间的耦合度)
- 跨级组件
- 层层组件传递props
- provide/inject
React:
- 父->子
- props: 父组件使用props属性向子组件传递数据
- ref: 获取整个子组件对象,可以调用子组件中的函数
- 子->父
- 利用回调函数:父组件使用props属性向子组件传递一个函数,子组件携带自己的数据并通过调用该函数向父组件传递数据
- 兄弟组件
- 自定义事件机制:发布/订阅模式,通过向事件对象上添加监听器和触发事件来实现组件之间的通信
- 借助同一父组件通过props传递数据,也就是利用父组件实现中转传递(会增加子组件和父组件之间的耦合度)
- 跨级组件
- 层层组件传递props
- 使用context:生产/消费者模式
事件机制
Vue:
- 原生事件使用标准 Web 事件
- Vue组件自定义事件机制,是父子组件通信基础
React:
- 自身实现了一套自己的事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等
- 所有事件并没有绑定到具体的dom节点上而是绑定在了document 上,然后由统一的事件处理程序来处理,同时也是基于浏览器的事件机制(冒泡),所有节点的事件都会在 document 上触发