從js運(yùn)行機(jī)制角度分析vue數(shù)據(jù)追蹤和react狀態(tài)管理的異同

前言

之前一直用react框架來寫前端工程瘦穆,最近單位一個項目因使用了vue框架栓撞,于是對照著react項目,選擇對應(yīng)的生命周期函數(shù)碗硬、router瓤湘、 redux/vuex、jsx/template恩尾、async await/axios弛说,三下五除二組裝起了vue項目,當(dāng)然翰意,也因為對vue整體沒有太多的了解木人,在有些復(fù)雜頁面的如組件的復(fù)用遇到了些問題,重點就是不知道數(shù)據(jù)如何復(fù)寫到頁面上冀偶,于是便有了這篇文章醒第。

一.js原生寫法對DOM節(jié)點上數(shù)據(jù)的改變

從最基本最原始js說起,當(dāng)html上有個dom節(jié)點p標(biāo)簽時候进鸠,我們可以通過js來改變p標(biāo)簽的內(nèi)容

//html代碼
<a id='a1'>a標(biāo)簽</a>
<p id='p2'>p標(biāo)簽</p>
<button id='btn3' onClick='btnClick'>按鈕</button >

//js代碼
btnClick(){
  $("#p1").html("新內(nèi)容");
}

我們知道稠曼,瀏覽器的刷新按鈕是對整個頁面進(jìn)行重新渲染,而上述代碼客年,我們通過jquery實現(xiàn)了對P標(biāo)簽內(nèi)容的改變霞幅,并且這個改變是局部的改變,并不會引起a標(biāo)簽和button按鈕量瓜,也不會引起整個頁面重新渲染
記住兩點司恳,瀏覽器刷新是整個頁面渲染jquery是局部渲染

二.Ajax/setTime等具有延遲操作特性函數(shù)對DOM節(jié)點上數(shù)據(jù)的改變

我們請求ajax獲取數(shù)據(jù)后绍傲,對頁面上p標(biāo)簽數(shù)據(jù)的改變跟上面js改變其實在本質(zhì)上并沒有什么改變扔傅,都是局部刷新。但往往我們一個頁面有多個ajax請求,這些請求都是異步的铅鲤,而我們知道JavaScript是單線程划提,這是怎么實現(xiàn)的?

瀏覽器線程

js運(yùn)作在瀏覽器中,是單線程的邢享,即js代碼始終在一個線程上執(zhí)行鹏往,這個線程稱為js引擎線程。
瀏覽器是多線程的骇塘,除了js引擎線程伊履,它還有:
UI渲染線程,用于渲染頁面
瀏覽器事件觸發(fā)線程款违,用于控制交互唐瀑,響應(yīng)用戶
http請求線程,用于處理請求插爹,ajax是委托給瀏覽器新開一個http線程
EventLoop輪詢的處理線程哄辣,處理線程用于輪詢消息隊列
……..
當(dāng)我們頁面運(yùn)行在頁面上,展現(xiàn)在我們眼前的是js引擎線程赠尾,當(dāng)我們操作時力穗,雖然處理代碼是寫在了js里面,但最終還是會觸發(fā)瀏覽器事件線程气嫁,并將該事件放在消息隊列里当窗,如果該操作包含一個網(wǎng)絡(luò)請求,則會調(diào)用http請求線程(異步)寸宵,獲取數(shù)據(jù)改變頁面數(shù)據(jù)則會調(diào)用UI渲染線程崖面。同理,setTime也是將操作放入消息隊列里梯影,改變數(shù)據(jù)巫员,調(diào)用UI渲染線程。
記住一點光酣,當(dāng)一個頁面運(yùn)行在瀏覽器上疏遏,至少有四五個線程協(xié)作才能完成數(shù)據(jù)變化頁面刷新

三.vue的數(shù)據(jù)追蹤

基于js的前端頁面救军,包含三個部分html财异、style、script唱遭,而基于vue的頁面也是包含這個三個部分戳寸,template對應(yīng)html,script和style拷泽;基于js的頁面只有一些window.onload可以被歸類為生命周期函數(shù)疫鹊,而基于vue的頁面極大的豐富了生命周期函數(shù)袖瞻,其中的watch和computed就用于來追蹤數(shù)據(jù)的變化。
下面我們就來分析vue是怎么追蹤數(shù)據(jù)的
1.在data()函數(shù)中定義數(shù)據(jù)初始值
2.在template模板中用{{}}對某個控件(一個實例拆吆,如p標(biāo)簽)實現(xiàn)數(shù)據(jù)綁定聋迎。這里要介紹ES5中一個神奇的方法 Object.defineProperty,他有三個參數(shù)枣耀,實例對象霉晕、需要定義的屬性或方法的名字、目標(biāo)屬性所擁有的特性捞奕。在第三個參數(shù)中牺堰,vue.js悄悄的給實例添加了setter和getter方法。這個函數(shù)對數(shù)據(jù)實現(xiàn)的攔截颅围,當(dāng)我們給數(shù)據(jù)賦值的時候伟葫,setter方法會實現(xiàn)2個操作:1.給數(shù)據(jù)賦值 2.發(fā)送一個事件給瀏覽器,告訴瀏覽器的UI渲染線程院促,我需要渲染頁面了筏养。個人認(rèn)為這是vue數(shù)據(jù)追蹤最核心的地方。
3.vue也可以用watch和computed來追蹤數(shù)據(jù)的變化常拓。watch用來監(jiān)測當(dāng)A數(shù)據(jù)變化時候撼玄,其它非A數(shù)據(jù)有沒有什么變化(一對一,一對多墩邀,A是前者,監(jiān)測前者的變化來改變后者)盏浙,computed用來監(jiān)測當(dāng)非B數(shù)據(jù)或者多個非B數(shù)據(jù)變化時眉睹,B數(shù)據(jù)有什么變化(一對一,多對一废膘,B是后者竹海,監(jiān)測前者的變化來改變前后者)。一般我們程序中要computed即可丐黄,很少會使用到watch斋配。這里在程序中我們對每一個綁定的數(shù)據(jù)通過computed來追蹤變化。

//templa代碼
<a id='a1'>{{a1}}</a>
<p id='p2'>{{p2}}</p>
<button id='btn3' onClick='btnClick3'>按鈕3</button >
<button id='btn4' onClick='btnClick4'>服務(wù)器取值</button >

//script中代碼
data(){
  a1:'a標(biāo)簽';
  p2:'p標(biāo)簽';
}
watch:{
  p2:function(){
    this.a1='改變了';
  }
},
computed(){
  p2:function(){
    return vuex中.state.p2;
  }
},
methods:{
  btnClick3(){
    this.p2='新內(nèi)容';
  }
  btnClick4(){
    發(fā)送服務(wù)器請求getdata();
  }
}
//vuex中代碼
state:{
 p2:'';
}
mutations:{
  getdata(state,res){
    const data=服務(wù)器返回數(shù)據(jù);
    state.p2=data;
  }
}

點擊頁面按鈕3進(jìn)行賦值的時候灌闺,會默認(rèn)調(diào)用p標(biāo)簽的setter方法完成1.更新數(shù)據(jù)2.發(fā)送事件給瀏覽器艰争,調(diào)用UI渲染線程局部渲染頁面
點擊服務(wù)器請求按鈕,獲取數(shù)據(jù)后桂对,computed中p2:function(){}也是一個賦值行為甩卓,從vuex中獲取數(shù)據(jù)后,跟按鈕3完成同樣的更新數(shù)據(jù)及局部渲染頁面蕉斜;此外逾柿,watch監(jiān)聽到了p2的變化缀棍,也同步改變了a1的值,渲染了頁面机错。這里實際上是局部渲染了兩次頁面爬范。
記住一點,vue是追蹤每一條通過{{}}綁定的數(shù)據(jù)弱匪,每次賦值后局部渲染青瀑。

react狀態(tài)管理

與vue數(shù)據(jù)追蹤不同的是,react采用的時候狀態(tài)變化來引起頁面的變化痢法。
我們先剔除狀態(tài)里的函數(shù)狱窘,單純討論數(shù)據(jù)部分。
初學(xué)react财搁,要比vue難理解的多蘸炸,什么一組確定的狀態(tài)對應(yīng)一個確定的頁面,什么更新組件的狀態(tài)然后根據(jù)新的狀態(tài)重新渲染用戶界面尖奔,感覺跟大學(xué)里辯證唯物唯心主義一樣繞人搭儒。
先看代碼

//render代碼
<a id='a1'>{a1}</a>
<p id='p2'>{p2}</p>
<button id='btn3' onClick='btnClick3'>按鈕3</button >
<button id='btn4' onClick='btnClick4'>服務(wù)器取值</button >

//class中代碼
state:{
  a1:'a標(biāo)簽';
  p2:'p標(biāo)簽';
}
btnClick3(){
  const p='新內(nèi)容';
  this.setState({p2:p});
}
btnClick4(){
  發(fā)送服務(wù)器請求getdata();
}
componentWillReceiveProps(nextProps):function{
  if(nextProps.p!=this.pros.p2) const a='改變了';
  const p=nextProps.p;
  this.setState({p2: p,a1:a});
}
//redux中代碼
  state: {
    p:"",
  },
 effects: {
    *getdata({payload}, {call, put}){
      const p=服務(wù)器返回數(shù)據(jù);
      yield put({type: 'savedata',payload: {p}});
    },
}

在react中,state的作用有點類似vue中data(不考慮state里的函數(shù))提茁,componentWillReceiveProps作用相當(dāng)于vue的computed淹禾,我們可以發(fā)現(xiàn),不管是在按鈕3本頁面直接賦值還是從服務(wù)器取值用componentWillReceiveProps接受新數(shù)據(jù)后茴扁,都需要顯式的調(diào)用用this.setState铃岔,它完成2個動作:1.更新state里的數(shù)據(jù) 2.通知瀏覽器調(diào)用UI渲染線程來渲染頁面。這里跟vue中最大的區(qū)別就是峭火,vue是通過{{}}自動的給p標(biāo)簽實例添加setter毁习,setter會自動的告訴瀏覽器數(shù)據(jù)變化了,而react需要手動的調(diào)用this.setState來通知瀏覽器我們要改變數(shù)據(jù)了卖丸。此外纺且,vue是每條數(shù)據(jù)單獨實現(xiàn)局部自動更新,而react數(shù)據(jù)改變后稍浆,手動整體更新载碌。

五.總結(jié)

vue和react說到底還是js,既然是js衅枫,刷新頁面數(shù)據(jù)必然少不了瀏覽器重繪(repaint)和重排(reflow)嫁艇,事實上,vue的setter和react的setState給瀏覽器發(fā)送事件調(diào)用UI渲染線程的時候就是告訴瀏覽器我要重繪還是重排弦撩。

vue逐條更新和react整體更新到底誰效率更高裳仆?
重排是告訴瀏覽器我要重新布局,而重繪是根據(jù)已經(jīng)布置好的布局實現(xiàn)樣式外觀等改變孤钦,所以說重排的消耗要比重繪多的多歧斟。
react是整體更新纯丸,不管你什么數(shù)據(jù)變了,我都是不計代價的觸發(fā)一次重排
vue是逐條更新静袖,理論上只是根據(jù)數(shù)據(jù)的變化觉鼻,每次觸發(fā)一下重繪即可,可能10次重繪都趕不上react的一次重排队橙,看上去似乎vue效率更高坠陈。但是!vue有個v-if屬性捐康,這個是很討厭的東西仇矾,它會根據(jù)值來影藏這個顯示那個,實際上調(diào)用了兩次display:block/none解总,而給display賦值必然會引起重排贮匕,在復(fù)雜頁面中,我們必然會使用v-if花枫,每個v-if都會重排兩次刻盐,開銷很大。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劳翰,一起剝皮案震驚了整個濱河市敦锌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌佳簸,老刑警劉巖乙墙,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異生均,居然都是意外死亡伶丐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門疯特,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肛走,你說我怎么就攤上這事漓雅。” “怎么了朽色?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵邻吞,是天一觀的道長。 經(jīng)常有香客問我葫男,道長抱冷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任梢褐,我火速辦了婚禮旺遮,結(jié)果婚禮上赵讯,老公的妹妹穿的比我還像新娘。我一直安慰自己耿眉,他們只是感情好边翼,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸣剪,像睡著了一般组底。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筐骇,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天债鸡,我揣著相機(jī)與錄音,去河邊找鬼铛纬。 笑死厌均,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饺鹃。 我是一名探鬼主播莫秆,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼悔详!你這毒婦竟也來了镊屎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤茄螃,失蹤者是張志新(化名)和其女友劉穎缝驳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體归苍,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡用狱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拼弃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夏伊。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吻氧,靈堂內(nèi)的尸體忽然破棺而出溺忧,到底是詐尸還是另有隱情,我是刑警寧澤盯孙,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布鲁森,位于F島的核電站,受9級特大地震影響振惰,放射性物質(zhì)發(fā)生泄漏歌溉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一骑晶、第九天 我趴在偏房一處隱蔽的房頂上張望痛垛。 院中可真熱鬧草慧,春花似錦、人聲如沸榜晦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乾胶。三九已至抖剿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間识窿,已是汗流浹背斩郎。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留喻频,地道東北人缩宜。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像甥温,于是被迫代替她去往敵國和親锻煌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容