React和Vue是目前前端最主流的两大框架,最近面试总是被问及React和Vue的异同对比,这次就大概梳理一下。
GitHub上star对比截图
设计思想
React官网介绍React是一个用于构建用户界面的 JavaScript 库。React推荐JSX + inline style, 也就是把HTML和CSS全都写进JavaScript了,即 ”all in js“,HTML和css都可以放到js中。React主张函数编程,推荐使用纯函数,数据不可变,单向数据流,但是可以手动编写onChange等事件处理函数实现双向数据流。结合JSX轻松实现渲染模板
Vue官网介绍Vue是一套用于构建用户界面的渐进式框架,与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue保留了html、css、js分离的写法,使得现有的前端开发者在开发的时候能保持原有的习惯,更接近常用的web开发方式,模板就是普通的html,数据绑定使用mustache风格,样式直接使用css。Vue认为数据是可变的,使用v-model实现双向数据流。
构建与调试
* 构建工具
React创建新的单页应用的最佳方式: Create React App
npx create-react-app my-app
cd my-app
npm start
Vue官方构建工具: vue-cli
npm install -g @vue/cli
vue create my-app
npm run serve
# OR
yarn global add @vue/cli
vue create my-app
npm run serve
* 调试
React Developer Tools用来调试react
Redux DevTools用来调试redux
Vue.js devtools用来调试vue,vuex等
JSX vs 模板
React没有模板,是通过类组件的render方法的返回值或者函数式组件的返回值来渲染虚拟DOM,通过原生js来实现条件渲染、循环渲染、插值等。也可以使用JSX语法糖来更简单地去描述树状结构;
Vue则是使用模板,模板更贴近HTML与js代码、css代码分离开,使用mustache进行数据绑定,使用指令来实现条件渲染、循环渲染等
数据变化监听
* 数据流对比
React是单向数据流,且是不允许子组件修改父组件传来的props,组件的state是允许修改的,可以通过setState(异步的)来进行更改,不允许通过this.state这种方式直接更改组件的状态。vue2.x中也不允许子组件修改父组件传递的props,组件与DOM之间可以通过v-model双向绑定
* 监听数据变化方式对比
React组件在调用setState后,默认情况下由于shouldComponentUpdate()里的返回值是true所以全部组件都会重新渲染,造成很多不必要的渲染,可以通过在组件的shouldComponentUpdate()生命周期方法中来进行渲染优化或者直接使用官方的PureComponent(shouldComponentUpdate()中进行状态和属性的浅比较来决定是否需要渲染)
Vue2.x中通过Object.defineProperty()把data对象中所有属性添加到vm上。为每一个添加到vm上的属性,都指定一个getter/setter。在getter/setter内部去操作(读/写)data中对应的属性。Vue3中通过new Proxy()和Reflect来为数据指定一个getter/setter。当设置数据的时候会触发对应的setter方法,从而进一步触发vm的watcher方法,然后数据更改,vm则会进一步触发视图更新操作。vue能够更加精确的知道数据变化,只重新渲染与变化数据绑定的DOM
事件
React通过属性来绑定事件,React类组件需要自己手动处理事件this指向的问题
class Demo extends React.Component{
/*
通过onXxx属性指定事件处理函数(注意大小写)
a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
*/
constructor(props){
super(props);
//初始化状态
this.state = {
number: 22
}
//解决this指向问题
this.showInfo3 = this.showInfo3.bind(this)
}
showInfo1 = ()=> {
console.log(this.state);
}
showInfo2 = (e, number)=> {
console.log(number);
console.log(this.state);
}
showInfo3() {
console.log(this.state);
}
showInfo4(number) {
return (e) => {
console.log(number);
console.log(this.state);
}
}
render(){
return(
<div>
<button onClick={this.showInfo1}>点我提示信息(不传参)</button>
<button onClick={this.showInfo3}>点我提示信息(不传参)</button>
<button onClick={(e) => {this.showInfo2(e,66)}}>点我提示信息(传参)</button>
<button onClick={this.showInfo4(88)}>点我提示信息(传参)</button>
</div>
)
}
}
Vue中的事件处理函数都放到methods配置对象中,并把methods中的每一个事件处理函数都放到vm身上并把函数的this绑定到vm身上,通过指令来实现事件绑定
<button @click="showInfo1">点我提示信息(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息(传参)</button>
const vm = new Vue({
el:'#root',
methods:{
showInfo1(event){
console.log(this); //此处的this是vm
},
showInfo2(event,number){
console.log(this); //此处的this是vm
console.log(number); //66
}
}
})
组件通信方式
React组件通信的几种方式:
- 父子组件通信:props
- 兄弟组件(非嵌套组件):消息订阅-发布(pubs-sub)、集中式管理(redux、dva)
- 祖孙组件(跨级组件):消息订阅-发布(pubs-sub)、集中式管理(redux、dva)、Context(生产者-消费者模式,用的少)
Vue组件通信的几种方式:
- 父子组件通信:props与自定义事件
- 兄弟组件(非嵌套组件):全局事件总线、消息订阅-发布(pubs-sub)、集中式管理(vuex、redux)
- 祖孙组件(跨级组件):全局事件总线、消息订阅-发布(pubs-sub)、集中式管理(vuex、redux)、provide/inject
生态对比
小程序
移动端App
其他对比
- React认为数据不可变,所以一个嵌套比较深的状态数据obj:{a:{b:{c: '2'}}},修改obj.a.b.c = '3'是不会进行页面渲染的,只有this.setState({obj:{a:{b:{c: '3'}}}})才会进行页面渲染。Vue认为数据是可变的,但是只有data中初始化的数据才是响应式的,直接data中某个对象数据添加新数据是无法实现响应式的,得使用Vue.set(),对于数组,Vue不允许直接利用索引来修改数组可以使用Vue.set()或array.splice()
- React使用高阶组件HoC来实现组件组合,vue则使用mixins来实现,由于vue默默帮我们做了这么多事,所以我们自己如果直接把组件的声明包装一下,返回一个HoC,那么这个被包装的组件就无法正常工作。React最早也是使用mixins的,不过后来他们觉得这种方式对组件侵入太强会导致很多问题,就弃用了mixins转而使用HoC。
- React使用props、render props与children props来实现类似Vue中的具名插槽、作用域插槽和默认插槽
- diff算法对比,React和Vue都只进行同层比较,且都是用key来优化diff算法。React中认为只要新旧虚拟DOM的标签名和key一样就是同一个虚拟节点,而Vue在标签名和key相同的基础上还要对比属性;Vue基于snabbdom库,使用双向链表,边对比,边更新DOM,React主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。
Redux 与 Vuex对比
- Redux认为数据是不可变的,所以reducer中必须是纯函数,不允许修改传入的参数prevState,所以只能返回一个基于prevState的一个全新的state;
- Redux中的action的值必须是一个一般对象,这也就注定Redux不能实现异步action(值为函数),但是可以使用中间件来实现异步action,常用的有redux-thunk、 redux-promise、redux-saga等;
- Redux使用combineReducers(reducers)来实现模块化编码;
- 使用react-redux的connect方法来创建容器组件把redux中的state、action传给UI组件的props中并实现监测redux中状态的改变并且重新渲染组件;
- Vuex认为数据是可变的,所以不用像reducer那样的纯函数; Vue.use(Vuex)后创建完store,则vm上就添加了$store属性
- Vuex中mutations是同步的,actions用来实现一些异步操作,当state中的数据需要经过加工后再使用时,可以使用getters加工;
- mapState帮助我们映射
state
中的数据为计算属性;mapGetters用于帮助我们映射getters
中的数据为计算属性;mapActions用于帮助我们生成触发actions
的方法,即:包含$store.dispatch(xxx)
的函数;mapMutations用于帮助我们生成触发mutations
的方法,即:包含$store.commit(xxx)
的函数; - 使用modules和namespaced来实现模块化