Vue 与 React 全方位差异对比
前言
作为国内前端最火的两个框架,Vue 和 React 一直被比较着。而作为一名成熟的前端当然是二者都要会!下面就梳理一下 Vue 和 React 这两个框架的差异性,只有做到心中有数,开发中进行切换才会做到游刃有余。而且面试中也不惧被提问:“既然你都使用过这两个框架,那么能说说它们之间的区别吗?”
基础对比
- 关于组件:Vue 组件是一个
.vue
后缀文件,包含模板、样式和脚本代码,和 HTML 文件格式一样,很好理解;而 React 组件是一个.jsx
或.tsx
后缀的文件,用的是其特有的 JSX 语法,模板和样式都可以写到 JS 里。 - 关于插值:Vue template 中使用
{{}}
,React JSX 中使用{}
。 - 关于属性:Vue template 使用
v-bind:xxx
动态绑定属性,或者简写为:xxx
;而 React JSX 中,属性值如果是 JS 变量,依然用插值的语法{xxx}
。 - 关于状态和响应式:
Vue3 提供了很多响应式 API 和工具,而 React 只有一个
useState
Vue 是双向数据绑定,修改数据自动更新视图;而 React 单向数据流,需要手动 setState 触发视图更新。 - 注意,这里单向/双向数据流指的的是
VM
层,即是视图层数据层。而父子组件之间的数据流都是单向的,即不允许子组件直接修改父组件传递过来的数据。 - 关于 Props:都可以通过 props 进行父子组件数据传递,只是 Vue props 要声明,React 不用声明可能直接使用。
- 关于 DOM 事件:Vue 提供
v-on
指令,也可以简写为@
来监听 DOM 事件,用法:v-on:click="handler"
或@click="handler"
。React 必须使用驼峰写法,如onClick={handler}
。 - 关于自定义事件:在父子组件中传递调用自定义事件时,Vue 通过
@add="add"
添加绑定事件;React 中还是当属性传递,插值语法绑定事件add={add}
。但是 Vue 子组件需要通过emit
,React 子组件可以直接拿到 props 里的事件进行调用。 - 关于样式:Vue 可以直接用
class
,React 则需要使用className
,因为 React 完全是 JS 代码,class
是 JS 的关键字,没法直接使用。动态样式 Vue 有很多自己的写法,而 React 还是用插值语法。 - 关于条件渲染:Vue template 使用
v-if
和v-else
等指令实现判断逻辑条件渲染;React JSX 依然使用插值语法{}
,只不过里面是使用 JS 表达式,如{ flag ? <Aaa/> : <Bbb/> }
。 - 关于列表渲染:Vue template 中循环渲染使用
v-for
指令;而 React 还是使用{xxx}
表达式。其中主要用到 JS 数组的map
方法。 - 关于生命周期:Vue 和 React 都定义了很多生命周期,这里主要举三个,组件挂载、更新、卸载生命周期:Vue 分别对应
onMounted()
、onUpdated()
、onUnmounted()
;React 组件只需要一个useEffect
hook 即可,挂载后执行useEffect
,在useEffect
hook 里返回一个函数可以用于在组件卸载时触发调用,useEffect
第二个参数是依赖项数组,可以处理更新的情况。 - 关于 watch/computed:Vue 中 使用
watch
监听某个数据,React 还是通过useEffct
第二个参数(依赖项数组)实现。Vue 使用 computed 对计算结果进行缓存,React 中使用useMemo
缓存结果。 - 关于插槽:插槽,用于定义和显示子组件的内容。插槽的概念是 Vue 提出来的,什么具名插槽、作用域插槽···,其实插槽编译后就是一个普通对象;React 是通过 props 里的 children 拿到子组件。
- 关于数据管理:Vue2 用 VueX,Vue3 用 Pinia;React 可选择的就更多,比如 Redux、dva、jotai、zustand等。
- 关于 api:Vue API 概念更多,React API 少
响应式原理
- Vue2 响应式的特点就是依赖收集,数据可变,自动派发更新,初始化时通过 Object.defineProperty 递归劫持 data 所有属性添加 getter/setter,触发 getter 的时候进行依赖收集,修改时触发 setter 自动派发更新找到引用组件重新渲染。
- Vue3 响应式使用原生 Proxy 重构了响应式,一是 proxy 不存在 Vue2 响应式存在的缺陷,二是性能更好,不仅支持更多的数据结构,而且不再一开始递归劫持对象属性,而是代理第一层对象本身。运行时才递归,用到才代理,用 effect 副作用来代替 Vue2 里的 watcher,用一个依赖管理中心 trackMap 来统一管理依赖代替 Vue2 中的 Dep,这样也不需要维护特别多的依赖关系,性能上取得很大进步。
- 相比 Vue 的自动化,React 则是基于状态,单向数据流,数据不可变,需要手动 setState 来更新,而且当数据改变时会以组件根为目录,默认全部重新渲染整个组件树,只能额外用 pureComponent/shouldComponentUpdate/useMemo/useCallback 等方法来进行控制。
- Vue 通过数据劫持和代理来监测数据变化,能够精准地检测到具体数据的变化,触发相应的更新,因此更新粒度非常小。而 React 推崇函数式编程,这种方式无法感知数据变化,不知道何时应该刷新。即便是手动调用
setState
触发更新,它也无法确定哪些组件需要刷新,而是渲染整个虚拟DOM,基本上就是无差别刷新。这导致了性能问题,因此需要不断通过其他方法来避免不必要的刷新,或者优化这种无差别刷新的性能。
Diff 算法
- Vue2 是同层比较新老 vnode,新的不存在老的存在就删除,新的存在老的不存在就创建,子节点采用双指针头对尾两端对比的方式,全量diff,然后移动节点时通过 splice 进行数组操作。
- Vue3 是采用 Map 数据结构以及动静结合的方式,在编译阶段提前标记静态节点,Diff 过程中直接跳过有静态标记的节点,并且子节点对比会使用一个 source 数组来记录节点位置及最长递增子序列算法优化了对比流程,快速 Diff,需要处理的边际条件会更少。
- React 是递归同层比较,标识差异点保存到 Diff 队列保存,得到 patch 树,再统一操作批量更新 DOM。Diff 总共就是移动、删除、增加三个操作,如果结构发生改变就直接卸载重新创建,如果没有则将节点在新集合中的位置和老集合中的 lastIndex 进行比较是否需要移动,如果遍历过程中发现新集合没有,但老集合有就删除。
总结
React JSX 语法非常简洁,只要记住下面两条就够了:
{xxx}
大括号里面是 JS 的变量或者表达式,可实现一切动态的功能,包括判断和循环onXxx
是 DOM 事件的写法
而 Vue template 定义了更多的规则,例如:
:xxx
动态属性@xxx
事件:class
和:style
的多种写法v-if
v-for
等多种指令- 还有更多,如
v-model
slot
等...
Vue 在更新时性能优化方面需要的心智负担会少一点,特别是 Vue3,而 React 如果不注意,容易导致一些组件无用的 Diff,但实际项目中真正能遇到这种性能瓶颈的也是极少数。
转载自:https://juejin.cn/post/7395147410212765705