淺析React&Vue兩大流行框架優(yōu)缺點(diǎn)

  • vue - 本質(zhì)是 MVVM 框架,由 MVC 發(fā)展而來(lái)
  • React - 本質(zhì)是前端組件化框架,不是一個(gè)完整的MVC框架,可以認(rèn)為是MVC中的V(View)

MVVM

MVVM 的出現(xiàn)促進(jìn)了 GUI 前端開發(fā)與后端業(yè)務(wù)邏輯的分離,極大地提高了前端開發(fā)效率。MVVM 的核心是 ViewModel 層,它就像是一個(gè)中轉(zhuǎn)站(value converter),負(fù)責(zé)轉(zhuǎn)換 Model 中的數(shù)據(jù)對(duì)象來(lái)讓數(shù)據(jù)變得更容易管理和使用,該層向上與視圖層進(jìn)行雙向數(shù)據(jù)綁定,向下與 Model 層通過(guò)接口請(qǐng)求進(jìn)行數(shù)據(jù)交互,起呈上啟下作用。如下圖所示

mvvm1.png

MVVM的設(shè)計(jì)思想:關(guān)注Model的變化,讓MVVM框架去自動(dòng)更新DOM的狀態(tài),從而把發(fā)者從操作DOM的繁瑣步驟中解脫出來(lái)!

  • Bug很難被調(diào)試。因?yàn)槭褂秒p向綁定的模式,當(dāng)你看到界面異常了,有可能是你View的代碼有Bug,也可能是Model的代碼有問(wèn)題。數(shù)據(jù)綁定使得一個(gè)位置的Bug被快速傳遞到別的位置,要定位原始出問(wèn)題的地方就變得不那么容易了。另外,數(shù)據(jù)綁定的聲明是指令式地寫在View的模版當(dāng)中的,這些內(nèi)容是沒(méi)辦法去打斷點(diǎn)debug的。

  • 一個(gè)大的模塊中model也會(huì)很大,雖然使用方便了也很容易保證了數(shù)據(jù)的一致性,當(dāng)時(shí)長(zhǎng)期持有,不釋放內(nèi)存就造成了花費(fèi)更多的內(nèi)存。

  • 對(duì)于大型的圖形應(yīng)用程序,視圖狀態(tài)較多,ViewModel的構(gòu)建和維護(hù)的成本都會(huì)比較高。

yuanli.jpeg

生命周期

Vue生命周期

vue-life.png

React為每個(gè)組件提供了生命周期鉤子函數(shù)去響應(yīng)不同的時(shí)刻,組件的生命周期分為三個(gè)部分:(1)實(shí)例化;(2)存在期;(3)銷毀&清理期。具體周期如下圖所示:

React v16.3以前版本

reacct.16.3-down.png

React v16.3以后版本

react-16.3-up.jpeg

數(shù)據(jù)流管理

Vue組件數(shù)據(jù)流的問(wèn)題

而vue的思想是響應(yīng)式的,也就是基于是數(shù)據(jù)可變的,通過(guò)對(duì)每一個(gè)屬性建立Watcher來(lái)監(jiān)聽,當(dāng)屬性變化的時(shí)候,響應(yīng)式的更新對(duì)應(yīng)的虛擬dom。

vue-parent_child.png

React數(shù)據(jù)是單向不可變的

react是自上而下的單向組件數(shù)據(jù)流,容器組件&展示組件(也叫傻瓜組s件&聰明組件)是最常用的react組件設(shè)計(jì)方案,容器組件負(fù)責(zé)處理復(fù)雜的業(yè)務(wù)邏輯以及數(shù)據(jù),展示組件負(fù)責(zé)處理UI層,通常我們會(huì)將展示組件抽出來(lái)進(jìn)行復(fù)用或者組件庫(kù)的封裝,容器組件自身通過(guò)state來(lái)管理狀態(tài),setState更新狀態(tài),從而更新UI,通過(guò)props將自身的state傳遞給展示組件實(shí)現(xiàn)通信。

這是當(dāng)業(yè)務(wù)需求不復(fù)雜,頁(yè)面較簡(jiǎn)單時(shí)我們常用的數(shù)據(jù)流處理方式,僅用react自身提供的props和state來(lái)管理足矣,但是如果稍微增加一點(diǎn)復(fù)雜度呢,比如當(dāng)我們項(xiàng)目中遇到這些問(wèn)題:

state-props.jpg

1,如何實(shí)現(xiàn)跨組件通信、狀態(tài)同步以及狀態(tài)共享?

react V16.3以前,通過(guò)狀態(tài)提升至最近的共同父組件來(lái)實(shí)現(xiàn)。(雖然有官方提供的context API,但是舊版本存在一個(gè)問(wèn)題:看似跨組件,實(shí)則還是逐級(jí)傳遞,如果中間組件使用了ShouldComponentUpdate檢測(cè)到當(dāng)前state和props沒(méi)有變化,return false,那么context就會(huì)無(wú)法透?jìng)?,因此context沒(méi)有被官方推薦使

react.png

react V16.3版本以后,新版本context解決了之前的問(wèn)題,可以輕松實(shí)現(xiàn),但依然存在一個(gè)問(wèn)題,context也是將底部子組件的狀態(tài)控制交給到了頂級(jí)組件,但是頂級(jí)組件狀態(tài)更新的時(shí)候一定會(huì)觸發(fā)所有子組件的re-render,那么也會(huì)帶來(lái)?yè)p耗。(雖然我們可以通過(guò)一些手段來(lái)減少重繪,比如在中間組件的SCU里進(jìn)行一些判斷,但是當(dāng)項(xiàng)目較大時(shí),我們需要花太多的精力去做這件事)

context.png

2,如何避免組件臃腫?

當(dāng)某個(gè)組件的業(yè)務(wù)邏輯非常復(fù)雜時(shí),我們會(huì)發(fā)現(xiàn)代碼越寫越多,因?yàn)槲覀冎荒茉诮M件內(nèi)部去控制數(shù)據(jù)流,沒(méi)辦法抽離,Model和View都放在了View層,整個(gè)組件顯得臃腫不堪,業(yè)務(wù)邏輯統(tǒng)統(tǒng)堆在一塊,難以維護(hù)。

3,如何讓狀態(tài)變得可預(yù)知,甚至可回溯?

當(dāng)數(shù)據(jù)流混亂時(shí),我們一個(gè)執(zhí)行動(dòng)作可能會(huì)觸發(fā)一系列的setState,我們?nèi)绾文軌蜃屨麄€(gè)數(shù)據(jù)流變得可“監(jiān)控”,甚至可以更細(xì)致地去控制每一步數(shù)據(jù)或狀態(tài)的變更?

4,如何處理異步數(shù)據(jù)流?

react自身并未提供多種處理異步數(shù)據(jù)流管理的方案,僅用一個(gè)setState已經(jīng)很難滿足一些復(fù)雜的異步流場(chǎng)景;

組件通信

component-props.png

其實(shí)這部分兩個(gè)比較相似。在Vue 中有三種方式可以實(shí)現(xiàn)組件通信:

  • 父組件通過(guò)props向子組件傳遞數(shù)據(jù)或者回調(diào),雖然可以傳遞回調(diào),但是我們一般只傳數(shù)據(jù),只需通過(guò)事件的機(jī)制來(lái)處理
  • 子組件向父組件的通信子組件通過(guò)事件 向父組件發(fā)送消息
  • 通過(guò) V2.2.0 中新增的 provide/inject 來(lái)實(shí)現(xiàn)父組件向子組件注入數(shù)據(jù),可以跨越多個(gè)層級(jí)。
  • 訪問(wèn)parent/children等不合編碼規(guī)范的方式。

在React中,組件是如何通信的呢?

  • 父組件通過(guò) props 可以向子組件傳遞數(shù)據(jù)或者回調(diào)
  • 通過(guò) context 進(jìn)行跨層級(jí)的通信,這其實(shí)和 provide/inject 起到的作用差不多。

React不支持自定義事件,Vue中子組件向父組件傳遞消息有兩種方式:事件和回調(diào)函數(shù),而且Vue更傾向于使用事件。但是在 React 中我們都是使用回調(diào)函數(shù)的,這可能是他們二者最大的區(qū)別。

模版上不同

  • vue - 使用模板(最初由 angular 提出)
  • React - 使用 JSX

條件判斷

template.png

循環(huán)遍歷生成

template2.png

JSX

template3.png

總結(jié)

  • 模板語(yǔ)法上,我更加傾向于 JSX
  • 模板分離上,我更加傾向于 vue

補(bǔ)充說(shuō)明

  • JSX 語(yǔ)法(標(biāo)簽、JS 表達(dá)式、判斷、循環(huán)、事件綁定)
  • JSX 是語(yǔ)法糖,需被解析成 JS 才能運(yùn)行
  • JSX 是 React 引入的,但不是 React 獨(dú)有的, 是獨(dú)立的標(biāo)準(zhǔn),可被其他項(xiàng)目使用

VDOM

React

React在開發(fā)初期就引入虛擬DOM概念,后來(lái)發(fā)現(xiàn)很好用,但是這是一個(gè)無(wú)心插柳的結(jié)果, 但React的核心思想:組件化,一個(gè)Component拯救世界,忘掉煩惱,從此不再操心界面。

為什Virtual Dom快? Javascript很快, Dom很慢 ~

vue:Vue在2.0版本引入了vdom。其vdom是基于snabbdom 庫(kù)所做的修改。snabbdom是一個(gè)開源的vdom庫(kù)。snabbdom的主要作用就是將傳入的JS模擬的DOM結(jié)構(gòu)轉(zhuǎn)換成虛擬的DOM節(jié)點(diǎn)。先通過(guò)其中的 h函數(shù) 將JS模擬的DOM結(jié)構(gòu)轉(zhuǎn)換成虛擬DOM之后,再通過(guò)其中的 patch函數(shù) 將虛擬DOM轉(zhuǎn)換成真實(shí)的DOM渲染到頁(yè)面中。為了保證頁(yè)面的最小化渲染,snabbdom引入了Diff算法 ,通過(guò)Diff算法找出前后兩個(gè)虛擬DOM之間的差異,只更新改變了的DOM節(jié)點(diǎn),而不重新渲染為改變的DOM節(jié)點(diǎn)。

<!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庫(kù),先不必糾結(jié)為什么這樣引入,以及每個(gè)文件的作用。 --> 
  <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函數(shù)
    var patch = snabbdom.init([
        snabbdom_class,
        snabbdom_props,
        snabbdom_style,
        snabbdom_eventlisteners
    ])       
    //定義h函數(shù)
    var h = snabbdom.h;      
    //生成一個(gè)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中的模板解析和渲染的核心就是:通過(guò)類似snabbdom的h()和patch()的函數(shù),先將模板解析成vnode,如果是初次渲染,則通過(guò)patch(container,vnode)將vnode渲染至頁(yè)面,如果是二次渲染,則通過(guò)patch(vnode,newVnode),先通過(guò)Diff算法比較原vnode和newVnode的差異,以最小的代價(jià)重新渲染頁(yè)面。

組件化的區(qū)別

  • React 本身就是組件化,沒(méi)有組件化就不是 React
  • vue 也支持組件化,不過(guò)是在 MVVM 上的擴(kuò)展
  • 對(duì)于組件化,我更加傾向于 React ,做的徹底而清晰

共同點(diǎn)

  • 都支持組件化
  • 都是數(shù)據(jù)驅(qū)動(dòng)試圖

Chrome 開發(fā)工具

React和Vue都有很好的Chrome擴(kuò)展工具去幫助你找出bug。它們會(huì)檢查你的應(yīng)用,讓你看到Vue或者React中的變化。你也可以看到應(yīng)用中的狀態(tài),并實(shí)時(shí)看到更新。

DEMO展示

參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容