浅析React&Vue两大流行框架

vue - 本质是 MVVM 框架,由 MVC 发展而来

React - 本质是前端组件化框架,不是一个完整的MVC框架,可以认为是MVC中的V(View)

MVVM

一、MVVM模式

MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对View 和 ViewModel 的双向数据绑定,这使得ViewModel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。

MVVM 的出现促进了 GUI 前端开发与后端业务逻辑的分离,极大地提高了前端开发效率。MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。如下图所示

MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把发者从操作DOM的繁琐步骤中解脱出来!

Bug很难被调试。因为使用双向绑定的模式,当你看到界面异常了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的。

一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存。

对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。

生活中的发布-订阅模式

上面事发布-订阅模式的一个比较正式的解释,可能这个解释不大好理解。所以我们通过实际生活中的例子来理解。

比如看中了一套房子,等到去了售楼处的说以后才被告知房子已经售罄了。但是售楼小姐告知,将来会有尾盘推出。具体什么时候推出,目前没人知道。

但是买家又不想频繁的跑,于是就把自己的电话号码登记在售楼处,在登记的花名册上有很多类似的买家。售楼小姐答应买家,新的房源一出来就一一通知买家。

所以上面就是一个发布订阅模式的简单例子。购房者(订阅者)订阅房源信息,售楼处(发布者)发布新房源消息给购房者(订阅者),购房者(订阅者)接收到消息后作出相应的反应。

通过Object.defindProperty 里面的get set方法,把这些属性加上监听,set的时候

发布订阅模式 dep订阅者 和 watcher(set是观察者,发生变化时候让dep 调watcher 去更新视图)

dep 是订阅者 用来管理watcher (观察者),是一个数组存放若干依赖 ;其实就是watcher; 一个watcher 对应一个属性;界面中出现一个变量就有一个watcher;重复使用的 两个name,deps 里就有两个watcher;dep里有个通知的方法,通知所有依赖去做更新;

watcher 用来做调用更新的对象 方法里面将 dep.targer = this  将当前watcher 实例指向dep 静态属性target 

依赖里面管理了很多 wacher  data里有几个属性就有几个依赖,每个依赖里可能有多个watcher

每一个属性都有一个依赖

页面

compile编译 : 1.parse(使用正则解析template中的vue指令v-xxx变量等等形成AST语法树);2. optimize(标记一些静态节点,用作后面的性能优化,在diff的时候直接略过);3. gererate (把第一步生产的AST转化为渲染函数render function)


生命周期

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><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><metahttp-equiv="X-UA-Compatible"content="ie=edge"/><title>Document</title></head><body><pid="container"></p><buttonid="btn-change">change</button><!-- 引入snabbdom库,先不必纠结为什么这样引入,以及每个文件的作用。 --><scriptsrc="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script><scriptsrc="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script><scriptsrc="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script><scriptsrc="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script><scriptsrc="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js"></script><scriptsrc="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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356