vue框架和react框架雖然都是虛擬dom,組件化開發,其中的不同點還是蠻多的。首先vue的雙向綁定而react則是單項數據流。react使用了jsx語法,讓編程更組件話。除了這些他們的生命周期還是不一樣的
總歸他們都經歷了 初始化階段,運行階段,銷毀階段。其中運行階段又分渲染和數據更新兩部分
先說下vue的生命周期相對比較簡單:
初始化階段:
1:new Vue(...) : 實例化、組件通過new Vue() 創建出來之后會初始化事件和生命周期,然后就會執行beforeCreate鈎子函數,這個時候,數據還沒有掛載呢,只是一個空殼,無法訪問到數據和真實的dom,一般不做操作
2:beforeCreate,created :掛載數據,綁定事件等等,然后執行created函數,這個時候已經可以使用到數據,也可以更改數據,在這里更改數據不會觸發updated函數,在這里可以在渲染前倒數第二次更改數據的機會,不會觸發其他的鈎子函數,一般可以在這里做初始數據的獲取
運行階段:
1:beforemounted:接下來開始找實例或者組件對應的模板,編譯模板為虛擬dom放入到render函數中准備渲染,然后執行beforeMount鈎子函數,在這個函數中虛擬dom已經創建完成,馬上就要渲染,在這里也可以更改數據,不會觸發updated,在這里可以在渲染前最后一次更改數據的機會,不會觸發其他的鈎子函數,一般可以在這里做初始數據的獲取
2:render ,mounted,beforeupdate,update : 接下來開始render,渲染出真實dom,然后執行mounted鈎子函數,此時,組件已經出現在頁面中,數據、真實dom都已經處理好了,事件都已經掛載好了,可以在這里操作真實dom。
到這里還不算結束,如果在程序運行中,沒有更新數據則無視,一旦有數據更新了,就會走以下兩個鈎子:beforeupdate,update
當組件或實例的數據更改之后,會立即執行beforeUpdate,然后vue的虛擬dom機制會重新構建虛擬dom與上一次的虛擬dom樹利用diff算法進行對比之后重新渲染,當更新完成后,執行updated,數據已經更改完成,dom也重新render完成,可以操作更新后的虛擬dom
銷毀階段:
1:beforeDestroy:當經過某種途徑調用$destroy方法后,立即執行beforeDestroy,一般在這里做一些善后工作,例如清除計時器、清除非指令綁定的事件等等
2:destroyed:組件的數據綁定、監聽...去掉后只剩下dom空殼,這個時候,執行destroyed,在這里做善后工作也可以
總結起來無外乎就七個當然真實的流程要更細致復雜,有興趣的可以自己研究源碼,實際項目中用到這些完全夠了:
newVue()實例化 => beforeCreate,created =>beforemounted,mounted =>(if data change){ beforeupdate,update } => beforedestroyed,destroyed
這里有幾點要注意:
1:Vue的編譯實際上是指Vue把模板編譯成 render 函數的過程
2:render選項參數比template更接近Vue解析器!所以綜合排列如下:
<div> <header> <h1>I'm a template!</h1> </header> <p v-if="message"> {{ message }} </p> <p v-else> No message. </p> </div> 會被渲染成: function anonymous() { with(this){return _c('div',[_m(0),(message)?_c('p',[_v(_s(message))]):_c('p',[_v("No message.")])])} }
4:在Vue中,數據更改會導致虛擬 DOM 重新渲染,並先后調用beforeUpdate鈎子函數和updated鈎子函數
重渲染(調用這兩個鈎子函數)的前提是被更改的數據已經被寫入模板中!!(這點很重要)
var vm = new Vue({ el: '#app', data: { number: 1 }, template: '<div id="app"><p></p></div>', beforeUpdate: function () { console.log('調用了beforeUpdate鈎子函數')//控制台沒有打印 }, updated: function () { console.log('調用了updated鈎子函數')//控制台沒有打印 } }) vm.number = 2
// 在模板中使用number這個數據 template: '<div id="app"><p> {{ number }} </p></div>',//添加{{number}}
beforeUpdate: function () { console.log('調用了beforeUpdate鈎子函數')//此時被打印出來了 }, updated: function () { console.log('調用了updated鈎子函數')//此時被打印出來了
}
也就是說:只有Vue實例中的數據被“寫入”到我們的模板中,它的改變才可以被Vue追蹤,重渲染從而調用 beforeUpdate鈎子函數和updated鈎子函數
5:
constructor(props) {
super(props);//如果有父元素繼承父元素的props
this.state = {
content:null,
}
}
執行階段:
1:componentWillMount():這個函數是在render前執行一次,如果在這個函數中調用setState改變某些狀態機,react會等待setState完成后再渲染組件
需要注意的是:如果有子組件,子組件也有此鈎子函數,並且會等待父級的函數調用完成后才會調用子組件的componentWillMount
2:render():主要用來渲染掛載組件。也是組件的最核心函數,和其它鈎子函數不一樣(上一個鈎子只執行一次)render函數是比較頻繁被調用的:(1)初始化加載頁面(2)狀態機改變setState ( 3 ) 接收到新的props(父組件更新)
需要注意的是render鈎子內不要改變狀態機state
3:componentDidMount():render之后就會調用,它也和componentWillMount一樣只調用一次。它的主要作用就是渲染掛組件可以使用refs了。(如果需要操作dom時)
需要注意的是子組件也有componentDidMount,不過和willMount不同的是子組件會在父組件調用前調用didMount
如果在該函數內修改state,render就會被再次調用。
如果不想多次渲染render,可以選擇將setState放在willMount內。
它比較適合數據初始化后,再異步的請求后端接口或其它數據需要再次更新頁面響應式的時候,請求的接口可以直接放在這里
數據更新監測階段:(如果有數據改變的話):
1:componetWillReceiveProps():上面的是第一遍流程,當后期有新的props傳入,react會調用willReceiveProps鈎子,換句話說,父組件內state值改變了,就會重新渲染render,父組件一render,子組件的props就更新了,也就調用了此鈎子
它的作用和didMount相似,也可以渲染掛在組件使用refs
需要注意的是:react初次渲染時,該函數並不會被觸發,因此有時該函數需要和componentWillMount或componentDidMount組合使用;
使用該函數一定要加nextProps參數:componentWillReceiveProps(nextProps),首次使用了解的可以先打印結果(console.log(nextProps))
2:shouldComponentUpdate() : 每次執行setstate都會執行該函數,也就是說只要一改變狀態,接收到新的state或props 就會調用此更新機制:它有默認的返回值 true,有兩個參數:新的props,新的state:sholdComponentUpdate(nextprops,nextstate)
它的作用是:如果有些變化不需要重新render組件,可以在該函數中阻攔。
注意:該方法在初始化渲染的時候不會調用,在使用 forceUpdate 方法的時候也不會
3:componentWillUpdate():每次執行setstate后,如果setState跟新改變了props,state也就是說通過setstate改變為新的prop,state值,它就會被調用,注意是在重新render前,所以它作用也是在獲取新的prop,state后,為下次render做准備工作
注意:不能再該函數中通過this.setstate再次改變狀態機,如果需要,則在componentWillReceiveProps函數中改變
4:componentDidUpdate():重新渲染后調用,在初始化渲染的時候該方法不會被調用
作用:使用該方法可以在組件更新之后如果還想操作DOM 元素的話可以在此寫入操作
最后是銷毀階段,這里只有一個componentWillUnmount()
在該方法中執行任何必要的清理,比如無效的定時器,或者清除在 componentDidMount 中創建的 DOM 元素。
1:當一個頁面中存在子父組件時,要注意componentWillMount和componentDidMount的使用,如果需要先加載父組件(獲取網路數據),父組件傳值給子組件,再加載子組件(獲取網路數據),那么不能同時在子父組件中使用componentDidMount獲取網路數據,因為會先執行子組件的componentDidMount,會由於未得到父組件的傳值而報錯;解決方案:(1)父組件:componentWillMount,子組件:componentDidMount;(2)父組件:componentDidMount,子組件:componentWillMount;
他們的真確順序應該是:父componentWillMount => 子componentDidMount=>父componentDidMount=>子componentWillMount
2:當一個頁面中如要實現兩個組件的聯動效果,(比如:頁面中包含componentA和componentB,單擊componentA,componentB內容對應變化,componentA向componentB通過redux傳參,那么componentB首次會通過componentDidMount接收,然后再通過componentWillReceiveProps接收)
需要額外注意的是:react17版本以后:有些生命周期基本棄用了(過時):不建議使用因為強制使用它們還是會有效
1:componentWillMount():建議在constructor()內初始化state,如果是訂閱類建議在componentDidMount()內進行
2:componentWillReceiveProps():如果執行數據提取以響應props的更改,請改為componentDidupdate()生命周期,如果是為了在props更改后重置某些state,請考慮組件完全受控或使用key使組件不受控來代替
3:componentWillUpdate():不能在此調用setState(),通常情況下此方法替換成componentDidupdate()。如果想要讀取dom信息(如保存滾動位置)可將邏輯移至getSnapshotBeforeUpdate()中
另外還有個生命周期是不常用的:shouldcomponentUpdate:只作為性能優化的方式存在,不要在這里做阻止渲染等操作會出現bug
注意,你不能此方法中調用 this.setState()
;在 UNSAFE_componentWillUpdate()
返回之前,你也不應該執行任何其他操作(例如,dispatch Redux 的 action)觸發對 React 組件的更新
通常,此方法可以替換為 componentDidUpdate()
。如果你在此方法中讀取 DOM 信息(例如,為了保存滾動位置),則可以將此邏輯移至 getSnapshotBeforeUpdate()
中。