vue - 本质是 MVVM 框架, 由 MVC 发展而来
React - 本质是前端组件化框架, 不是一个完整的 MVC 框架, 可以认为是 MVC 中的 V(View)
MVVM
MVVM 的出现促进了 GUI 前端开发与后端业务逻辑的分离, 极大地提高了前端开发效率. MVVM 的核心是 ViewModel 层, 它就像是一个中转站(value converter), 负责转换 Model 中的数据对象来让数据变得更容易管理和使用, 该层向上与视图层进行双向数据绑定, 向下与 Model 层通过接口请求进行数据交互, 起呈上启下作用. 如下图所示
mvvm1.PNG
MVVM 的设计思想: 关注 Model 的变化, 让 MVVM 框架去自动更新 DOM 的状态, 从而把发者从操作 DOM 的繁琐步骤中解脱出来!
Bug 很难被调试. 因为使用双向绑定的模式, 当你看到界面异常了, 有可能是你 View 的代码有 Bug, 也可能是 Model 的代码有问题. 数据绑定使得一个位置的 Bug 被快速传递到别的位置, 要定位原始出问题的地方就变得不那么容易了. 另外, 数据绑定的声明是指令式地写在 View 的模版当中的, 这些内容是没办法去打断点 debug 的.
一个大的模块中 model 也会很大, 虽然使用方便了也很容易保证了数据的一致性, 当时长期持有, 不释放内存就造成了花费更多的内存.
对于大型的图形应用程序, 视图状态较多, ViewModel 的构建和维护的成本都会比较高.
yuanli.jpeg
生命周期
Vue 生命周期
vue-life.PNG
React 为每个组件提供了生命周期钩子函数去响应不同的时刻, 组件的生命周期分为三个部分:(1)实例化;(2)存在期;(3)销毁 & 清理期. 具体周期如下图所示:
React v16.3 以前版本
reacct.16.3-down.PNG
React v16.3 以后版本
react-16.3-up.jpeg
数据流管理
Vue 组件数据流的问题
而 vue 的思想是响应式的, 也就是基于是数据可变的, 通过对每一个属性建立 Watcher 来监听, 当属性变化的时候, 响应式的更新对应的虚拟 dom.
vue-parent_child.PNG
React 数据是单向不可变的
react 是自上而下的单向组件数据流, 容器组件 & 展示组件 (也叫傻瓜组 s 件 & 聪明组件) 是最常用的 react 组件设计方案, 容器组件负责处理复杂的业务逻辑以及数据, 展示组件负责处理 UI 层, 通常我们会将展示组件抽出来进行复用或者组件库的封装, 容器组件自身通过 state 来管理状态, setState 更新状态, 从而更新 UI, 通过 props 将自身的 state 传递给展示组件实现通信.
这是当业务需求不复杂, 页面较简单时我们常用的数据流处理方式, 仅用 react 自身提供的 props 和 state 来管理足矣, 但是如果稍微增加一点复杂度呢, 比如当我们项目中遇到这些问题:
state-props.jpg
1, 如何实现跨组件通信, 状态同步以及状态共享?
react V16.3 以前, 通过状态提升至最近的共同父组件来实现.(虽然有官方提供的 context API, 但是旧版本存在一个问题: 看似跨组件, 实则还是逐级传递, 如果中间组件使用了 ShouldComponentUpdate 检测到当前 state 和 props 没有变化, return false, 那么 context 就会无法透传, 因此 context 没有被官方推荐使
react.PNG
react V16.3 版本以后, 新版本 context 解决了之前的问题, 可以轻松实现, 但依然存在一个问题, context 也是将底部子组件的状态控制交给到了顶级组件, 但是顶级组件状态更新的时候一定会触发所有子组件的 re-render, 那么也会带来损耗.(虽然我们可以通过一些手段来减少重绘, 比如在中间组件的 SCU 里进行一些判断, 但是当项目较大时, 我们需要花太多的精力去做这件事)
context.PNG
2, 如何避免组件臃肿?
当某个组件的业务逻辑非常复杂时, 我们会发现代码越写越多, 因为我们只能在组件内部去控制数据流, 没办法抽离, Model 和 View 都放在了 View 层, 整个组件显得臃肿不堪, 业务逻辑统统堆在一块, 难以维护.
3, 如何让状态变得可预知, 甚至可回溯?
当数据流混乱时, 我们一个执行动作可能会触发一系列的 setState, 我们如何能够让整个数据流变得可 "监控", 甚至可以更细致地去控制每一步数据或状态的变更?
4, 如何处理异步数据流?
react 自身并未提供多种处理异步数据流管理的方案, 仅用一个 setState 已经很难满足一些复杂的异步流场景;
组件通信
component-props.PNG
其实这部分两个比较相似. 在 Vue 中有三种方式可以实现组件通信:
父组件通过 props 向子组件传递数据或者回调, 虽然可以传递回调, 但是我们一般只传数据, 只需通过事件的机制来处理
子组件向父组件的通信子组件通过事件 向父组件发送消息
通过 V2.2.0 中新增的 provide/inject 来实现父组件向子组件注入数据, 可以跨越多个层级.
访问children 等不合编码规范的方式.
在 React 中, 组件是如何通信的呢?
父组件通过 props 可以向子组件传递数据或者回调
通过 context 进行跨层级的通信, 这其实和 provide/inject 起到的作用差不多.
React 不支持自定义事件, Vue 中子组件向父组件传递消息有两种方式: 事件和回调函数, 而且 Vue 更倾向于使用事件. 但是在 React 中我们都是使用回调函数的, 这可能是他们二者最大的区别.
模版上不同
vue - 使用模板(最初由 angular 提出)
React - 使用 JSX
条件判断
template.PNG
循环遍历生成
template2.PNG
JSX
template3.PNG
总结
模板语法上, 我更加倾向于 JSX
模板分离上, 我更加倾向于 vue
补充说明
JSX 语法(标签, JS 表达式, 判断, 循环, 事件绑定)
JSX 是语法糖, 需被解析成 JS 才能运行
JSX 是 React 引入的, 但不是 React 独有的, 是独立的标准, 可被其他项目使用
VDOM
React
React 在开发初期就引入虚拟 DOM 概念, 后来发现很好用, 但是这是一个无心插柳的结果, 但 React 的核心思想: 组件化, 一个 Component 拯救世界, 忘掉烦恼, 从此不再操心界面.
为什 Virtual Dom 快? JavaScript 很快, Dom 很慢 ~
vue:Vue 在 2.0 版本引入了 vdom. 其 vdom 是基于 snabbdom 库所做的修改. snabbdom 是一个开源的 vdom 库. snabbdom 的主要作用就是将传入的 JS 模拟的 DOM 结构转换成虚拟的 DOM 节点. 先通过其中的 h 函数 将 JS 模拟的 DOM 结构转换成虚拟 DOM 之后, 再通过其中的 patch 函数 将虚拟 DOM 转换成真实的 DOM 渲染到页面中. 为了保证页面的最小化渲染, snabbdom 引入了 Diff 算法 , 通过 Diff 算法找出前后两个虚拟 DOM 之间的差异, 只更新改变了的 DOM 节点, 而不重新渲染为改变的 DOM 节点.
- <!DOCTYPE html>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0"
- />
- <meta http-equiv="X-UA-Compatible" content="ie=edge" />
- <title>
- Document
- </title>
- </head>
- <body>
- <p id="container">
- </p>
- <button id="btn-change">
- change
- </button>
- <!-- 引入 snabbdom 库, 先不必纠结为什么这样引入, 以及每个文件的作用. -->
- <script src="https://cdn.bootCSS.com/snabbdom/0.7.1/snabbdom.js">
- </script>
- <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js">
- </script>
- <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js">
- </script>
- <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js">
- </script>
- <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js">
- </script>
- <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.js">
- </script>
- <script>
- // 定义 patch 函数
- var patch = snabbdom.init([snabbdom_class, snabbdom_props, snabbdom_style, snabbdom_eventlisteners])
- // 定义 h 函数
- var h = snabbdom.h;
- // 生成一个 vnode
- var vnode = h('ul#list', {},
- [h('li.item', {},
- ['Item 1']), h('li.item', {},
- ['Item 2']), ]) console.log(vnode);
- // 获取 container
- var container = document.getElementById('container');
- patch(container, vnode); // 初次渲染
- var btn = document.getElementById('btn-change');
- btn.onclick = function() {
- var newVnode = h('ul#list', {},
- [h('li.item', {},
- ['Item 1']), h('li.item', {},
- ['Item B']), h('li.item', {},
- ['Item 3']), ]) patch(vnode, newVnode); // 再次渲染
- vnode = newVnode; // 将修改后的 newVnode 赋值给 vnode
- }
- </script>
- </body>
- </HTML>
vue 中的模板解析和渲染的核心就是: 通过类似 snabbdom 的 h()和 patch()的函数, 先将模板解析成 vnode, 如果是初次渲染, 则通过 patch(container,vnode)将 vnode 渲染至页面, 如果是二次渲染, 则通过 patch(vnode,newVnode), 先通过 Diff 算法比较原 vnode 和 newVnode 的差异, 以最小的代价重新渲染页面.
组件化的区别
React 本身就是组件化, 没有组件化就不是 React
vue 也支持组件化, 不过是在 MVVM 上的扩展
对于组件化, 我更加倾向于 React , 做的彻底而清晰
共同点
都支持组件化
都是数据驱动试图
Chrome 开发工具
React 和 Vue 都有很好的 Chrome 扩展工具去帮助你找出 bug. 它们会检查你的应用, 让你看到 Vue 或者 React 中的变化. 你也可以看到应用中的状态, 并实时看到更新.
React 的开发工具: https://www.jianshu.com/p/06df38a956dc
Vue 的开发工具: https://www.jianshu.com/p/dab699ca2fd4
DEMO 展示
参考文章:
- https://cn.vuejs.org/v2/guide/comparison.html
- https://scotch.io/bar-talk/exciting-new-features-react-163-bye-componentwillreceiveprops-hello-new-context-api
来源: http://www.jianshu.com/p/2781cb61d2d0