一、全景图
来自大圣老师的开课吧网课中的截图
二、背景
react:专业
- react 诞生于2011年( FaxJS),
- 2013 年 7 月 3 日 v0.3.0
- 2016 年 3 月 30 日 v0.14.8
- 2016 年 4 月 9 日 v15.0.0
- 2017 年 9 月 27 日 v16.0.0,fiber正式诞生
- 2019 年 react 16.8发布 正式支持 hooks语法。
- 2020 年 10 月 22 日 v17
解决问题
React
用来解决什么问题,官方网站上这样说道: We build React to solve one problem:building large applications with data that changes over time.
vue:传奇
发端于2013年的个人项目,但如今已然成为世界三大前端框架之一,在中国大陆更是前端首选。(面试官问:为什么你学习用vue?答:因为爱国!)
- 2013年,在
Google
工作的尤雨溪,受到Angular
的启发,从中提取自己所喜欢的部分,开发出了一款轻量框架,最初命名为Seed
。 - 同年12月,这粒种子发芽了,更名为
Vue
(因为是个视图层库,而 vue 是 view 的法语),版本号是0.6.0。 - 2014.01.24,
Vue
正式对外发布,版本号是0.8.0。 - 发布于2014.02.25的0.9.0,有了自己的代号:
Animatrix
,这个名字来自动画版的《骇客帝国》,此后,重要的版本都会有自己的代号。 - 0.12.0发布于2015.06.13,代号
Dragon Ball
(龙珠),这一年,Vue
迎来了大爆发,Laravel
社区(一款流行的PHP
框架的社区)首次使用Vue
,Vue
在JS
社区也打响了知名度。 - 1.0.0
Evangelion
(新世纪福音战士)是Vue
历史上的第一个里程碑。同年,vue-router
(2015-08-18)、vuex
(2015-11-28)、vue-cli
(2015-12-27)相继发布,标志着Vue
从一个视图层库发展为一个渐进式框架。很多前端同学也是从这个版本开始成为Vue
的用户(vue1
纯纯的响应式)。 - 2016年10月1日 2.0.0
Ghost in the Shell
(攻壳机动队)是第二个重要的里程碑,它吸收了React
的Virtual Dom
方案,还支持服务端渲染。 - 2019年2月4日 在2.6发布之前的很长一段时间,Vue核心团队都在忙着vue-cli3.0的开发,积攒了不少需求,发布了
vue2
倒数第二个的版本(2020年发布) - 2020年09月18日 带着成为任何人都可以快速学习、易于接近的框架的使命
vue3
它来了,同年还推出了新时代打包工具vite
(未来可能会取代webpack
)
vue群众基础图 如下图:
三、技术思想
react整体上是函数式的思想,组件使用jsx语法,all in js,将html与css全都融入javaScript,jsx语法相对来说更加灵活。
vue的整体思想仍然是拥抱经典的html(结构)+css(表现)+js(行为)的形式,vue鼓励开发者使用template模板,并提供指令供开发者使用如v-if、v-show、v-for等指令,因此在开发vue应用的时候会有一种在写经典web应用(结构、表现、行为分离)的感觉。
一些老生常谈的 数据管理(props、data vs state)、组件写法等这里就不比较了。
1、key的异同为例
1、react
与vue
在列表加key
的问题上最终目的是一致的:更准确、更快地拿到oldVnode
中对应的vnode
节点,提高性能。但是它们实现的算法却有一定的差异。
react
React
在渲染数组时如果子组件没有提供key
,会默认将循环的index
作为key
来用作第一次渲染。
源码本质上就是暴力比较法:由于单链表fiber无法使用双指针算法,所以无法对算法使用双指针优化。总体上经历两轮遍历,第一轮遍历:处理更新的节点。第二轮遍历:处理剩下的不属于更新的节点。
为了降低算法复杂度,React
的diff
会预设限制:
- 只对同级元素进行
Diff
。如果一个DOM
节点在前后两次更新中跨越了层级,那么React
不会尝试复用他。 - 两个不同类型的元素会产生出不同的树。如果元素由
div
变为p
,React
会销毁div
及其子孙节点,并新建p及其子孙节点。 - 先判断
key
再判断type
,两者相同则为同一节点。更新>新增/删除
vue
vue
在diff
性能方面要强于react
,当我认真阅读了源码与书籍后,佩服至极,
力扣算法题--最长递增子序列
举个例子吧:以数组[2, 11, 6, 8, 1]
为例:最终输出的结果为[0, 2, 3]
,表示最强增长序列的索引分别是0,2,3;对应的值是2,6,8。换句话说,在这个数组中最长连续增长的值就是数组中的2,6,8三个元素。
那么vue3费了这么大的力气,使用这个方法的目的是什么呢? Vue2在DOM-Diff过程中,优先处理特殊场景的情况,即头头比对,头尾比对,尾头比对等。
而Vue3
在DOM-Diff
过程中,根据 newIndexToOldIndexMap
新老节点索引列表找到最长稳定序列,通过最长增长子序列的算法比对,找出新旧节点中不需要移动的节点,原地复用,仅对需要移动或已经patch
(新增删除节点等操作)节点进行操作,最大限度地提升替换效率,相比于Vue2版本是质的提升!
2、diff的宏观比较
react
在react
中如果某个组件的状态发生改变,react
会把此组件以及此组件的所有后代组件重新渲染,不过重新渲染并不代表会全部丢弃上一次的渲染结果,react
还是会通过diff
去比较两次的虚拟dom
最后patch
到真实的dom
上。虽然如此,如果组件树过大,diff
其实还是会有一部分的开销。react内部通过 fiber
优化 diff
算法,外部建议开发者使用 shouldComponentUpdate
pureComponent
来规避问题。
vue
vue2
的响应式是Object.defineProperty
实现的,并且重写getter``setter
等一系列操作实现观察者模式,一旦数据发生变化,不会像react
一样去比较整颗组件树,而是去更新数据状态变化了的组件实例。
3、生命周期
react 生命周期
old 15.x-16
触发阶段 | react核心生命周期 |
---|---|
挂载阶段 | constructor、componentWillMount、render、componentDidMount、 |
更新过程 | componentWillReceiveProps、shouldComponentUpdate、render、componentDidUpdate |
卸载阶段 | componentWillUnMount |
new 16+
触发阶段 | react核心生命周期 |
---|---|
挂载阶段 | constructor、getDerivedStateFormProps、render、componentDidMount |
更新过程 | getDerivedStateFormProps、getSnapshotBeforeUpdate、shouldComponentUpdate、render、componentDidUpdate |
卸载阶段 | componentWillUnmount |
其他:componentDidCatch
React生命周期的命名一直都是非常语义化(小声bb:真是又臭又长又难记)
vue生命周期
选项式 API | Hook inside setup |
---|---|
beforeCreate | |
created | |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount(vue2 beforeDestory) | onBeforeUnmount |
unmounted(vue2 destoryed) | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked(vue3状态跟踪) | onRenderTracked |
renderTriggered(vue3状态触发) | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
4、函数式编程
双方都做出了大规模的改动,虽然源码不同,但设计思想以及代码简易程度来看,确实都在进步。
react hooks
原先繁琐的 compomentDidUpdate生命周期 =》 useEffect,虽然不完全相同,但在大多数场景中,从开发者层面看待,我们更多是关心props或者state中的数据变化之后,会产生了什么后果(副作用),省去了开发者自己比较前后值的过程。(这应该是react借鉴vue watch)
vue3 组合式api
vue3组合式api借鉴了react hooks中的部分思想,不得不说,青出于蓝而胜于蓝,再加上框架自己也做出了很多优化工作,在性能上是react比不上的。
react | vue |
---|---|
useState、ref | reactive、ref |
useEffect | onMounted、watch、watchEffect、onUnmounted |
上面整理了比较像的部分,拿useEffect与watchEffect进行对比 |
function() {
useEffect(()=>{
// 当demo发生变化时触发
console.log(demo)
},[demo])
return (
<div>react</div>
)
}
import {reactive, watchEffect} from 'vue'
export default {
setup() {
const state = reactive({ count: 0, name: 'zs' })
watchEffect(() => {
console.log(state.count)
console.log(state.name)
})
return {
state
}
}
}
由于初始化时源码内部的响应式机制的缘故,新APIwatchEffect
甚至都不需要监听是谁发生了变化就可以触发副作用,因为监听的这个过程,全程都是vue3
源码中的 Proxy
完成的。
不仅如此 vue3更是推出了更贴近原生js的语法,点赞!!!!!!
<script setup>
import { reactive, ref} from "vue";
// 渐进式更新:ref api
let val = ref("");
let todos = reactive([
{
id: 0,
title: "吃饭",
done: false,
},
{
id: 1,
title: "睡觉",
done: false,
},
]);
function addTodo() {
todos.push({
id: todos.length,
title: val.value,
done: false,
});
val.value = "";
}
</script>
其实到这里,vue技术栈的同学一定暗自得意,蹦出一句vue yes!
可惜这么好的库也有存在的问题,vue2
响应式Object.defineProperty
无法监听数组下标和对象后新增的属性值变化已是老生常谈,vue3
采用了 Proxy Api
解决这些问题的同时,也带来了新的问题,如reactive
只能传对象(react useState简单复杂值都可以),而官方推荐的ref
却需要通过 .value
才能获取值,这着实让社区炸锅,为此vue
团队迫于压力不得不推出 toRefs
(感兴趣的同学可以了解一下,之前线下我和艺宝探讨过)。
5、事件处理(@Click vs onClick)
vue
vue
中使用 v-on
(简写为@
) 指令监听 DOM
事件,并在触发时运行一些 JavaScript
代码。通常使用v-on
接收一个需要调用的方法名称。
<div @click="helloShanghai">welcome</div>
<div @click="helloShanghai('hello')">welcome</div>
访问原生DOM事件,可以将$event显式传入method中
<div @click="helloShanghai('hello', $event)">welcome</div>
普通元素 addEventListener
,组件$on
react
React
元素的事件处理和 DOM
元素的很相似,但是有一点语法上的不同:
React
事件的命名采用小驼峰式,而不是纯小写。- 使用
JSX
语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
<div onClick={this.handleClick}>Click me</div>
<div onClick={this.handleClick.bind(this)}>Click me</div>
<div onClick={()=>{this.handleClick()})}>Click me</div>
<div onClick={this.handleClick()}>Click me</div><!-- 错误写法>
react16
将合成事件挂载到 document
上,17版本后为root
根元素。
6、组件化
Vue
鼓励写近似常规HTML
的模板。写起来很接近标准 HTML
元素,只是多了一些属性。
React
推荐你所有的模板通用JavaScript
的语法扩展——JSX
书写。
7、构建工具
React ==> Create React APP Vue ==> vue-cli
8、其他
当然还有其他的一些比较,诸如vue
的插槽(slot)与react
的props.children
等等。
实在累死了。。。
四、我的看法
1、react
在中后台项目中由于在处理复杂的业务逻辑或组件的复用问题比vue
优雅而被人认可,但这种优雅是要有成本代价的,它更需要团队技术整体比较给力,领头大佬的设计与把关能力要更优秀,因此开发成本更大。
2、vue
更友好更易上手的写法著称,渐进式的框架、更友好的api、更亲民的设计让开发成本大大下降而效率大大提升。
3、另一方面,vue
因为规定了很多精巧常用
的api
(语法糖),规范了开发者,同时也一定程度上限制了开发者的发散思维;而react
因为更少的常用``api
提高了开发者的创造力,同时也因为每个react
开发者对react
有不同的理解而产生不同的代码风格。
4、vue
与react
在发展长河中越发成熟,深思熟虑后觉得两者不管在移动端或大型中后台都是非常可行的,其实框架本无好坏之分,我们更应该思考的是团队想要用什么技术栈、自己喜欢与擅长什么技术栈。
大家又是如何思考的呢?
回忆起那些年学前端的往事种种,不禁想吟诗一首。
五、吟诗一首
技术变迁,迭起兴衰
我们见证了jquery 的鸟尽弓藏,
我们见证了react 的百炼成刚,
我们见证了vue 的初露锋芒,
我们见证了nodejs 的以柔克刚。
地阔天长,
我们唯有乘风破浪,
纵使技术千变万化,
我们也能驰骋疆场。
致那些为了目标不断学习、奋发图强的人儿,2021年9月7日。
六、全家桶
react | vue |
---|---|
react-router、react-router-dom | vue-router |
redux | vuex |
antd | elementUI、iview |
Create React APP | vue-cli、vite |
next.js | nuxt |
react-native、taro | uni-app、weex |
antd-mobile | mint-ui、vux、Vonic、cube-ui |
七、参考文档
为什么 React 现在要推行函数式组件,用 class 不好吗?