前言
作为前端的主流框架,Vue和React都各有自己的特点与优势,下面就听我来一一道来。
Vue的优势包括:
-
模板和渲染函数的弹性选择
-
简单的语法及项目创建
-
更快的渲染速度和更小的体积
React的优势包括:
-
更适用于大型应用和更好的可测试性
-
同时适用于Web端和原生App
-
更大的生态圈带来的更多支持和工具
共同点:
-
利用虚拟DOM实现快速渲染
-
轻量级
-
响应式组件
-
服务器端渲染
-
易于集成路由工具,打包工具以及状态管理工具
-
优秀的支持和社区
上面是将两者的优势各自列举了,那么他们在具体使用方面究竟有何不同呢?
模板 vs JSX
Vue要使用HTML模板template,而React则是完全相反,要求开发者借助JSX在JavaScript中创建DOM。
生命周期
Vue中的组件与React组件有着相似的生命周期方法。但有个很大区别就是:Vue没有shouldComponentUpdate,因为Vue的reactivity系统不需要。详细的理由见下文的渲染机制。
beforeCreate() //创建组件之前
created() //创建组件之后
beforeMount() //组件挂载之前
mounted() //组件挂载之后
beforeUpdate() //更新之前
updated() //更新之后
beforeDestroy() //销毁组件之前
destroyed() // 销毁组件后
construtor() //创建组件
componentWillMount() //组件挂载之前
componentDidMount() // 组件挂载之后
componentWillReceiveProps() // 父组件发生render的时候子组件调用该函数
shouldComponentUpdate() // 组件挂载之后每次调用setState后都会调用该函数判断是否需要重新渲染组件,默认返回true
componentDidUpdate() // 更新
render() //渲染,react中的核心函数
componentWillUnmount() //组件被卸载的时候调用,一般在componentDidMount注册的事件需要在这里删除
渲染机制
在渲染机制上,两个框架都使用了虚拟DOM(Virtual DOM)。它的诞生是基于这么一个概念:改变真实的DOM状态远比改变一个JavaScript对象的花销要大得多。Virtual DOM是一个映射真实DOM的JavaScript对象,如果需要改变任何元素的状态,那么是先在Virtual DOM上进行改变,而不是直接改变真实的DOM。当有变化产生时,一个新的Virtual DOM对象会被创建并计算新旧Virtual DOM之间的差别,之后这些差别会应用在真实的DOM上。
在Vue里,初始化时遍历所有数据属性,并将其转换为getter和setter。Vue添加这些getter和setter以在访问或修改属性时启用依赖关系跟踪和更改通知。同时每个组件会有一个对应的 watcher 对象,这个对象的职责就是在当前组件被渲染的时候,记录数据上面的哪些属性被用到了。当消息值被改变时,其setter被触发。这个set方法会设置新值,但也将执行第二个任务,通知Vue值已更改,以及依赖该页面的任何部分可能需要重新渲染。如果消息作为prop传递给任何子组件,Vue知道它们依赖于此,它们也将被自动重新渲染。这就是为什么Vue组件不需要shouldComponentUpdate 方法的原因。【简而言之,就是Vue不需要全部重新渲染】
- new Vue,执行初始化
- 挂载$mount方法,通过自定义Render方法、template、el等生成Render函数
- 通过Watcher监听数据的变化
- 当数据发生变化时,Render函数执行生成VNode对象
- 通过patch方法,对比新旧VNode对象,通过DOM Diff算法,添加、修改、删除真正的DOM元素
Vue虚拟DOM的diff算法:https://blog.csdn.net/github_36546211/article/details/78023747
https://blog.csdn.net/m6i37jk/article/details/78140159
在React里,也是通过虚拟DOM(Virtual DOM),经由diff算法,对两棵DOM树(虚拟DOM和真实DOM)从根节点开始比较,一旦发现有DOM节点不同,则将后面的子节点全部重新渲染。可以发现,在react的diff算法下,会大大减少dom树的节点遍历,因此可以实现极速渲染。但在比较不同层次的两个节点的时候可能会有两种情况:1)节点类型不同,如:div和span;2)节点类型相同,但属性不同,如:style。对于第一种情况,直接销毁节点后,新建节点;而对于第二种情况,只需要更改节点的属性即可。在比较同一层次节点如列表节点,我们对列表节点进行增删改的时候,为了高效的更改节点,我们就必须去识别每一个节点,如果每个节点都有唯一的标识key,那么就可以直接找到需要更改的节点进行操作,而非遍历每个节点,直到找到目标节点,这也是为什么我们需要在对列表节点添加唯一的key的原因。
React虚拟DOM的diff算法:http://www.infoq.com/cn/articles/react-dom-diff/
状态管理 vs 对象属性
如果你对React熟悉,你就会知道应用中的状态是(React)关键的概念。也有一些配套框架被设计为管理一个大的state对象,如Redux。此外,state对象在React应用中是不可变的,意味着它不能被直接改变(这也许不一定正确)。在React中你需要使用setState()方法去更新状态。React的状态是不可变的,要想改变状态就需要使用setState方法。
在Vue中,state对象并不是必须的,数据由data属性在Vue对象中进行管理。而在Vue中,则不需要使用如setState()之类的方法去改变它的状态,在Vue对象中,data参数就是应用中数据的保存者。可以使用配套的状态管理Vuex
数据流和数据绑定
vue是双向绑定。
Vue.js 最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统。所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据。这是通过设置属性访问器实现的。在vue中,与数据绑定有关的有 插值表达式、指令系统、*Class和Style、事件处理器和表单空间、ajax请求和计算属性
注: 关于vue的数据双向绑定和单向数据流
- Vue 的依赖追踪是【原理上不支持双向绑定,v-model 只是通过监听 DOM 事件实现的语法糖】
- vue的依赖追踪是通过 Object.defineProperty 把data对象的属性全部转为 getter/setter来实现的;当改变数据的某个属性值时,会触发set函数,获取该属性值的时候会触发get函数,通过这个特性来实现改变数据时改变视图;也就是说只有当数据改变时才会触发视图的改变,反过来在操作视图时,只能通过DOM事件来改变数据,再由此来改变视图,以此来实现双向绑定
- 双向绑定是在同一个组件内,将数据和视图绑定起来,和父子组件之间的通信并无什么关联;
- 组件之间的通信采用单向数据流是为了组件间更好的解耦,在开发中可能有多个子组件依赖于父组件的某个数据,假如子组件可以修改父组件数据的话,一个子组件变化会引发所有依赖这个数据的子组件发生变化,所以vue不推荐子组件修改父组件的数据,直接修改props会抛出警告。
react是单向数据流
react中通过将state(Model层)与View层数据进行双向绑定达数据的实时更新变化,具体来说就是在View层直接写JS代码Model层中的数据拿过来渲染,一旦像表单操作、触发事件、ajax请求等触发数据变化,则进行双同步。
- React是单向数据流,数据主要从父节点传递到子节点(通过props)。如果顶层(父级)的某个props改变了,React会重渲染所有的子节点。
- react中实现组件有两种实现方式,一种是createClass方法,另一种是通过ES2015的思想类继承React.Component来实现
- 在React应用中,按钮、表单、对话框、整个屏幕的内容等,这些通常都被表示为组件。
- React推崇的是函数式编程和单向数据流:给定原始界面(或数据),施加一个变化,就能推导出另外一个状态(界面或者数据的更新)
- 组件可以将UI切分成一些的独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件。组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的React元素。
父子组件间通信
为了数据的可靠性,所以父子组件间的通信都是单向的。
在React里面,只能由父组件通过props传向子组件,props是只可读的状态,不可以修改。而对于自身的状态state,子组件可以更改自身的state。
在 Vue 中,父子组件也由props传递数据,两者之间的关系可以概述为:props 向下,events 向上。父组件通过 props 向下传递数据给子组件,子组件通过 events 发送消息给父组件。
性能
如果做过实际的开发,就会有明显的感觉,不论是在开发调试还是打包发布的时候,都明显感觉Vue的性能要比React要好很多,至于具体的原因,笔者还没有深入研究。
【注意】参考博客:https://vuejs.org/v2/guide/comparison.html