對於剛接觸vue一兩個月、才僅僅獨立做過一兩個vue項目的小白來說,以前一直自我感覺自己知道vue的生命周期,
直到前兩天去面試,面試官讓我說一下vue的生命周期。。。
其實我的心中是有那張圖的,但是因為學習、分析的年代久遠,心中有圖卻不知道怎么表述,成了個能發聲的啞巴,
最后也就只能按順序說出了那幾個鈎子函數,真是憋足了氣!
直到今天一鼓作氣二刷生命周期,我才恍然大悟,原來我之前的理解中,潛意識里一直是把那幾個鈎子函數當做了vue的生命周期!
簡直了,想想那天,面試官肯定在心里把我鄙視死了吧!
好了不扯了,直接說我今天看完后,自我感覺還很好的新的理解吧。
也許日后再深入接觸后,會發現今天的理解也很淺顯,但是至少今天比我前天好多了哈哈。
生命周期:就是從一個組件或者實例開始被初始化、創建開始到這個實例被銷毀或者結束的一個過程。
這個過程比如官網表述的:在過程中需要設置數據監聽、編譯模板、將實例掛載到 DOM 並在數據變化時更新 DOM 等
同時,在這個過程中vue給我們提供了很多的方法,也就是所說的生命周期鈎子函數。
在實例生命周期的不同階段借助這些鈎子函數,“用戶在不同階段添加自己的代碼”
由此:真正的生命周期是一個流程,而不是單單那幾個鈎子函數,鈎子函數只是用來在流程的不同階段幫助我們做更多的事情。
在我粗淺的理解下,我暫時把vue的生命周期統分成五大區塊(個人的划分)
一、創建(初始化)
二、查找與處理(找到組件並渲染)
三、掛載(插入)
四、更新(重新渲染並插入)
五、銷毀(卸載所有)
其中每一大塊又分幾個小的步驟,但是大體規律又是如出一轍:
- 本區塊開始前(一個區塊流程開始的鈎子告訴你)
- 本區塊開始中
- 本區塊開始后(一個區塊流程完畢的鈎子告訴你)
好像都是廢話哈哈,但我真的對這一點的感受很深刻。
接下來一個一個來
就像我們人從生下來到死這么一個過程,要有這個流程,先得開始:
一、創建部分
new Vue( 這句代碼,初始化一個vue實例,開始創建一個vue對象
生命周期開始,init event初始化事件,為當前實例做基礎配置;
創建之前,這里提供一個鈎子函數,beforeCreate 開始創建鈎子,
這個時候還啥也沒做呢,頁面一片空白,可以在頁面中先展示一個loading組件,給用戶一個友好體驗;
創建中,init injections(初始化注冊) & reactivity 創建過程中,data屬性被成功綁定,dom未生成;
創建之后,這里提供一個鈎子函數,created 創建完畢鈎子,
這個時候vue對象實例化完畢,dom樹依舊未生成,頁面還是一片空白,
但是,實例已完成以下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。
可以在這里ajax獲取數據賦給data屬性了,以便日后使用;
二、查找部分
也就是new Vue()括號里邊的參數開始被執行解析的過程:
new Vue({ el: '#app', router, store, template: '<App/>', components: { App } })
或
new Vue({ render (h) { return h('div', this.hi) } })
查找el屬性的對應內容,el對應的DOM 元素作為 Vue 實例的掛載目標
如果值可以被找到,那么實例將立即進入編譯過程
如果找不到,就去查是否在括號后邊掛載了$.mount()並有內容,用於手動開啟編譯
以上官網解說:“如果在實例化時存在el這個選項,實例將立即進入編譯過程,否則,需要顯式調用 vm.$mount()
手動開啟編譯。”
官網:https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-DOM
如果都沒找到,生命周期結束;
如果順利的都找到了,就繼續往下查找{}內的下一個屬性template
如果template對應的值當中有組件或者有html內容,那么也算查找成功,
如果為空,繼續查找render屬性值是否為空
緊接着,如果{}選項中存在渲染函數render,
那么template將被忽略,因為render渲染函數是字符串模板的代替方案,
render可以讓你發揮 JavaScript 最大的編程能力,而不用寫template的靜態模板
如果沒有或為空,template字符串模板將會替換掛載的元素,即el的屬性值
掛載元素el的內容都將被忽略,除非模板template的內容有分發插槽。
如果template和render都不存在,則生命周期結束。
查找階段匯總:
Vue 選項中的 render
函數若存在,則 Vue 構造函數不會從 template
選項或通過 el
選項指定的掛載元素中提取出的 HTML 模板編譯渲染函數。
至此,食材都准備好了,vue大廚開始開工吧!
此時,將template里的內容放到render函數中開始渲染處理,即執行render方法渲染template里的內容。
(這里是對vue的語法進行解析嗎?)
題外話:
el其實就是日后要掛載vue組件的一個目標點,如果連目標都找不到,還活着干嘛,干脆結束
template/render就是日后要做的事情,如果都不知道自己接下來要去做什么,還活着干嘛,干脆也結束
做人,又何嘗不是如此。
菜都准備好了總要上桌吧!
所有的dom結構都被渲染好了,vue語法也被解析成正常的html內容了,總要放到頁面展示了吧:
三、掛載部分
開始掛載之前,這里提供一個鈎子函數,beforeMount掛載前鈎子,
編譯template里的內容並在虛擬dom中執行,頁面上依舊沒有任何展示;
掛載中,要做的事就是創建vm$.el,並替換到el元素,
這一段我也不是太理解,大概感覺就是將#app的那段空的div換成剛才vue生成好的虛擬dom;
掛載完畢,這里提供一個鈎子函數,mounted掛載完畢鈎子,
el
被新創建的 vm.$el
替換,並掛載到實例上去
至此,所有的dom結構和數據都被展示到頁面當中,
這時可以做一些事情,比如關掉之前展示的loading;
注意: mounted
不會承諾所有的子組件也都一起被掛載。如果你希望等到整個視圖都渲染完畢,可以用 vm.$nextTick 替換掉 mounted
mounted: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been rendered }) }
nextTick: 在dom結構更新后使用這個方法獲取最新的dom結構
生命周期到這里,也算初步完成了他的使命,
如果是一個純靜態頁不做任何修改展示的話,根本沒必要用到生命周期的后兩區塊內容了。
但是如果在mounted之后再對實例中的data屬性做操作的話,就會走進生命周期的另一個階段:更新
四、更新部分
依舊是那個套路,觸發了更新的開關后,會給一個開始更新的回調:
更新之前,這里有一個鈎子函數,beforeUpdate開始更新前鈎子,
在這個鈎子里可以提供一個彈窗提示用戶確認跟新啥的。或者再展示一個loading;
也可以手動移除已添加的事件監聽器
更新中,vue實例要開始將舊數據替換為新數據,在虛擬dom中重新渲染,
虛擬dom開始改變,但是頁面這時沒有任何變化,因為只是改的虛擬dom,還並未真正修改dom結構;
更新完畢,這里有一個鈎子函數,updated更新后鈎子,
這時真正的dom結構被徹底替換,頁面展示上也會發生改變,
在這個鈎子里可以提供一個彈窗告訴用戶更新完畢。同時去掉loading彈層啥的;
注意: updated
不會承諾所有的子組件也都一起被重繪。如果你希望等到整個視圖都重繪完畢,可以用 vm.$nextTick 替換掉 updated
:
updated: function () { this.$nextTick(function () { // Code that will run only after the // entire view has been re-rendered }) }
如果執行了vm.$destory,就會進入到最后一個部分
五、銷毀部分
同上
開始銷毀vue實例之前,會有一個鈎子函數提示開發者組件要開始銷毀:beforeDestory開始銷毀鈎子,
在這個鈎子中我們可以提醒用戶是否刪除等,或者做一些開發者與業務有關的相關操作;
銷毀中,vue這時的主要目標就是卸載,就像人要洗澡前各種脫一樣(具體就不要想象了!),
他要卸載在身上的各種監聽、各種事件, 各種綁定以及各種子組件實例銷毀,感覺像毀滅一切
比如watchers(我沒用過沒有發言權)、子組件child components、事件event;
銷毀后,這時再次提供最后一個鈎子函數,destoryed銷毀完畢,
在這里我們可以提示用戶刪除完畢啥的,也可以清空我們自己的定時器或者做一些其他善后工作;
到這一步,此次整個vue實例的生命周期就徹底結束了。
以上,僅是自己了解vue后再刷生命周期圖的二次理解,僅代表個人的理解,
不一定完全正確,歡迎指正,最后也別全信。畢竟絕知此事要躬行。
最后,貼一張一刷時自己備注的官網生命周期圖,對照着圖再去理解我的總結應該更好。(查看清晰大圖:右鍵新標簽頁打開)
哇塞,認真學習了16分鍾,記錄草稿后,加上自己理解對樹講述了兩遍后,一口氣2000字的心得寫下來了。
真的是畫竹要先胸有成竹才行!
2018-06-09 14:07:16