前言
之前一直用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都會重排兩次刻盐,開銷很大。