- vue - 本質(zhì)是 MVVM 框架幅垮,由 MVC 發(fā)展而來
- React - 本質(zhì)是前端組件化框架,不是一個完整的MVC框架蜗侈,可以認(rèn)為是MVC中的V(View)
MVVM
MVVM 的出現(xiàn)促進了 GUI 前端開發(fā)與后端業(yè)務(wù)邏輯的分離,極大地提高了前端開發(fā)效率。MVVM 的核心是 ViewModel 層沥阳,它就像是一個中轉(zhuǎn)站(value converter),負(fù)責(zé)轉(zhuǎn)換 Model 中的數(shù)據(jù)對象來讓數(shù)據(jù)變得更容易管理和使用自点,該層向上與視圖層進行雙向數(shù)據(jù)綁定桐罕,向下與 Model 層通過接口請求進行數(shù)據(jù)交互,起呈上啟下作用。如下圖所示
MVVM的設(shè)計思想:關(guān)注Model的變化功炮,讓MVVM框架去自動更新DOM的狀態(tài)溅潜,從而把發(fā)者從操作DOM的繁瑣步驟中解脫出來!
Bug很難被調(diào)試薪伏。因為使用雙向綁定的模式滚澜,當(dāng)你看到界面異常了,有可能是你View的代碼有Bug嫁怀,也可能是Model的代碼有問題博秫。數(shù)據(jù)綁定使得一個位置的Bug被快速傳遞到別的位置,要定位原始出問題的地方就變得不那么容易了眶掌。另外挡育,數(shù)據(jù)綁定的聲明是指令式地寫在View的模版當(dāng)中的,這些內(nèi)容是沒辦法去打斷點debug的朴爬。
一個大的模塊中model也會很大即寒,雖然使用方便了也很容易保證了數(shù)據(jù)的一致性,當(dāng)時長期持有召噩,不釋放內(nèi)存就造成了花費更多的內(nèi)存母赵。
對于大型的圖形應(yīng)用程序,視圖狀態(tài)較多具滴,ViewModel的構(gòu)建和維護的成本都會比較高凹嘲。
生命周期
Vue生命周期
React為每個組件提供了生命周期鉤子函數(shù)去響應(yīng)不同的時刻,組件的生命周期分為三個部分:(1)實例化构韵;(2)存在期周蹭;(3)銷毀&清理期。具體周期如下圖所示:
React v16.3以前版本
React v16.3以后版本
數(shù)據(jù)流管理
Vue組件數(shù)據(jù)流的問題
而vue的思想是響應(yīng)式的疲恢,也就是基于是數(shù)據(jù)可變的凶朗,通過對每一個屬性建立Watcher來監(jiān)聽,當(dāng)屬性變化的時候显拳,響應(yīng)式的更新對應(yīng)的虛擬dom棚愤。
React數(shù)據(jù)是單向不可變的
react是自上而下的單向組件數(shù)據(jù)流,容器組件&展示組件(也叫傻瓜組s件&聰明組件)是最常用的react組件設(shè)計方案杂数,容器組件負(fù)責(zé)處理復(fù)雜的業(yè)務(wù)邏輯以及數(shù)據(jù)宛畦,展示組件負(fù)責(zé)處理UI層,通常我們會將展示組件抽出來進行復(fù)用或者組件庫的封裝揍移,容器組件自身通過state來管理狀態(tài)次和,setState更新狀態(tài),從而更新UI羊精,通過props將自身的state傳遞給展示組件實現(xiàn)通信斯够。
這是當(dāng)業(yè)務(wù)需求不復(fù)雜,頁面較簡單時我們常用的數(shù)據(jù)流處理方式喧锦,僅用react自身提供的props和state來管理足矣读规,但是如果稍微增加一點復(fù)雜度呢,比如當(dāng)我們項目中遇到這些問題:
1燃少,如何實現(xiàn)跨組件通信束亏、狀態(tài)同步以及狀態(tài)共享?
react V16.3以前阵具,通過狀態(tài)提升至最近的共同父組件來實現(xiàn)碍遍。(雖然有官方提供的context API,但是舊版本存在一個問題:看似跨組件阳液,實則還是逐級傳遞怕敬,如果中間組件使用了ShouldComponentUpdate檢測到當(dāng)前state和props沒有變化,return false帘皿,那么context就會無法透傳东跪,因此context沒有被官方推薦使
react V16.3版本以后,新版本context解決了之前的問題鹰溜,可以輕松實現(xiàn)虽填,但依然存在一個問題,context也是將底部子組件的狀態(tài)控制交給到了頂級組件曹动,但是頂級組件狀態(tài)更新的時候一定會觸發(fā)所有子組件的re-render斋日,那么也會帶來損耗。(雖然我們可以通過一些手段來減少重繪墓陈,比如在中間組件的SCU里進行一些判斷恶守,但是當(dāng)項目較大時,我們需要花太多的精力去做這件事)
2贡必,如何避免組件臃腫熬的?
當(dāng)某個組件的業(yè)務(wù)邏輯非常復(fù)雜時,我們會發(fā)現(xiàn)代碼越寫越多赊级,因為我們只能在組件內(nèi)部去控制數(shù)據(jù)流押框,沒辦法抽離,Model和View都放在了View層理逊,整個組件顯得臃腫不堪橡伞,業(yè)務(wù)邏輯統(tǒng)統(tǒng)堆在一塊,難以維護晋被。
3兑徘,如何讓狀態(tài)變得可預(yù)知,甚至可回溯羡洛?
當(dāng)數(shù)據(jù)流混亂時挂脑,我們一個執(zhí)行動作可能會觸發(fā)一系列的setState,我們?nèi)绾文軌蜃屨麄€數(shù)據(jù)流變得可“監(jiān)控”,甚至可以更細(xì)致地去控制每一步數(shù)據(jù)或狀態(tài)的變更崭闲?
4肋联,如何處理異步數(shù)據(jù)流?
react自身并未提供多種處理異步數(shù)據(jù)流管理的方案刁俭,僅用一個setState已經(jīng)很難滿足一些復(fù)雜的異步流場景橄仍;
組件通信
其實這部分兩個比較相似。在Vue 中有三種方式可以實現(xiàn)組件通信:
- 父組件通過props向子組件傳遞數(shù)據(jù)或者回調(diào)牍戚,雖然可以傳遞回調(diào)侮繁,但是我們一般只傳數(shù)據(jù),只需通過事件的機制來處理
- 子組件向父組件的通信子組件通過事件 向父組件發(fā)送消息
- 通過 V2.2.0 中新增的 provide/inject 來實現(xiàn)父組件向子組件注入數(shù)據(jù)如孝,可以跨越多個層級宪哩。
- 訪問children等不合編碼規(guī)范的方式。
在React中第晰,組件是如何通信的呢斋射?
- 父組件通過 props 可以向子組件傳遞數(shù)據(jù)或者回調(diào)
- 通過 context 進行跨層級的通信,這其實和 provide/inject 起到的作用差不多但荤。
React不支持自定義事件罗岖,Vue中子組件向父組件傳遞消息有兩種方式:事件和回調(diào)函數(shù),而且Vue更傾向于使用事件腹躁。但是在 React 中我們都是使用回調(diào)函數(shù)的桑包,這可能是他們二者最大的區(qū)別。
模版上不同
- vue - 使用模板(最初由 angular 提出)
- React - 使用 JSX
條件判斷
循環(huán)遍歷生成
JSX
總結(jié)
- 模板語法上纺非,我更加傾向于 JSX
- 模板分離上哑了,我更加傾向于 vue
補充說明
- JSX 語法(標(biāo)簽、JS 表達(dá)式烧颖、判斷弱左、循環(huán)、事件綁定)
- JSX 是語法糖炕淮,需被解析成 JS 才能運行
- JSX 是 React 引入的拆火,但不是 React 獨有的, 是獨立的標(biāo)準(zhǔn)涂圆,可被其他項目使用
VDOM
React
React在開發(fā)初期就引入虛擬DOM概念们镜,后來發(fā)現(xiàn)很好用,但是這是一個無心插柳的結(jié)果润歉, 但React的核心思想:組件化模狭,一個Component拯救世界,忘掉煩惱踩衩,從此不再操心界面嚼鹉。
為什Virtual Dom快? Javascript很快, Dom很慢 ~
vue:Vue在2.0版本引入了vdom贩汉。其vdom是基于snabbdom 庫所做的修改。snabbdom是一個開源的vdom庫锚赤。snabbdom的主要作用就是將傳入的JS模擬的DOM結(jié)構(gòu)轉(zhuǎn)換成虛擬的DOM節(jié)點匹舞。先通過其中的 h函數(shù) 將JS模擬的DOM結(jié)構(gòu)轉(zhuǎn)換成虛擬DOM之后,再通過其中的 patch函數(shù) 將虛擬DOM轉(zhuǎn)換成真實的DOM渲染到頁面中宴树。為了保證頁面的最小化渲染策菜,snabbdom引入了Diff算法 晶疼,通過Diff算法找出前后兩個虛擬DOM之間的差異酒贬,只更新改變了的DOM節(jié)點,而不重新渲染為改變的DOM節(jié)點翠霍。
<!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庫锭吨,先不必糾結(jié)為什么這樣引入,以及每個文件的作用寒匙。 -->
<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;
//生成一個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()的函數(shù)零如,先將模板解析成vnode,如果是初次渲染锄弱,則通過patch(container,vnode)將vnode渲染至頁面考蕾,如果是二次渲染,則通過patch(vnode,newVnode)会宪,先通過Diff算法比較原vnode和newVnode的差異肖卧,以最小的代價重新渲染頁面。
組件化的區(qū)別
- React 本身就是組件化掸鹅,沒有組件化就不是 React
- vue 也支持組件化塞帐,不過是在 MVVM 上的擴展
- 對于組件化,我更加傾向于 React 巍沙,做的徹底而清晰
共同點
- 都支持組件化
- 都是數(shù)據(jù)驅(qū)動試圖
Chrome 開發(fā)工具
React和Vue都有很好的Chrome擴展工具去幫助你找出bug葵姥。它們會檢查你的應(yīng)用,讓你看到Vue或者React中的變化句携。你也可以看到應(yīng)用中的狀態(tài)榔幸,并實時看到更新。
- React的開發(fā)工具: http://www.reibang.com/p/06df38a956dc
- Vue的開發(fā)工具: http://www.reibang.com/p/dab699ca2fd4
DEMO展示
參考文章: