從源碼上來看,Vue對象的每一個生命週期鉤子前都幹了什麼?

1、從官方文檔查看的Vue的生命週期鉤子

上面的圖片是從Vue的官網裏面找到的,若是單從這張圖去查看生命週期幹了什麼,咱們只能知道:vue

  • beforeMount前會對給出的模板進行編譯(僅僅是在用Vue.js打包的時候會進行編譯,在用腳手架的時候,在打包過程當中會把模板編譯成爲渲染函數,這樣能夠避免每次建立一個Vue對象的時候進行編譯模板)
  • mounted後,Vue實例對象纔是完整的對象,這個轉改可以維持到beforeDestroy

可是咱們不知道的東西會有不少,我這裏就舉出幾個問題:算法

  1. 父子節點的生命週期執行順序是怎麼樣的?
  2. 每一個生命週期可以訪問到的數據是否相同?
  3. 咱們知道修改了data後可以觸發更新生命週期的鉤子,那麼究竟是怎樣進行觸發的?
  4. 咱們知道Vue的話是經過VNode來更新管理真實的DOM的,那麼在上面的生命週期何時進行渲染的?

爲何咱們要知道上面的狀況?由於咱們可能會遇到一些問題,須要知道Vue裏面的實現app

  • 通常狀況下,咱們在組件初始化完畢的時候,也就是在mounted回調函數裏面調用咱們初始化的執行代碼(很大狀況下是請求,也就是異步操做),可是若是如今有一個性能的需求,就是要求儘早地調用咱們初始化的代碼,那麼選擇哪一個生命週期呢?這得根據每一個生命週期乾的事情來決定
  • 對於一些全局同步的操做,好比解析路由來對應頁面顯示的內容等等這些操做,那麼咱們是想要它儘早地完成,以便全局使用。

在這裏的話,咱們能夠對我提出的第一個問題進行解決。如今場景是這樣的,咱們有一個父親組件(app.vue),還有一個子組件(index.vue),父親組件傳給子組件一個abc屬性(這個屬性綁定父親組件的abc數據),在建立頁面後,設定定時器,1s後更新abc屬性,兩秒後將父組件進行銷燬,那麼會出現什麼狀況呢?dom

咱們打印出了以上的信息,能夠進行回答上面提出的第一個問題:異步

  • 父組件老是先於子組件建立,可是慢於子組件掛載節點
  • 父組件老是在子組件進行更新完畢後再進行更新
  • 父組件銷燬的時候會進行遞歸銷燬子組件

以上三點都是很容易理解緣由的,這裏就不進行解釋了。咱們如今已經知道的父子組件的生命週期執行順序了,接下來咱們看一下每一個生命週期鉤子前幹了什麼?ide

2、源碼上查看的生命週期鉤子(鉤子以前)

0.選項的合併

​ 經過Vue的官方文檔,咱們有時候可以看到一個建立Vue實例的選項能夠有不一樣的寫法,那麼針對於不一樣的寫法,須要有一個工具來把這幾個不一樣的寫法進行規範化,這就是選項的合併的所要進行處理的內容。函數

1.beforeCreate

在這個鉤子函數前主要是對Vue實例進行屬性的初始化工具

  • 生命週期狀態、監聽器的初始化,這些與業務邏輯無關
  • 處理父組件對本組件的自定義事件的監聽,將其放到**_events**中
  • 對建立VNode函數進行綁定,在Vue實例對象中,是不會存在直接對DOM進行操做的,而是經過建立VNode,而後經過patch的方式對DOM節點進行操做,操做的工具方法什麼的跟原生同樣的,Vue工具類裏面包含對這些方法的封裝。
  • 對父組件傳進來的屬性(props)以及監聽事件(v-on)進行深度觀察

2.created

在這個鉤子函數以前主要是對數據進行初始化而且進行監聽操做:性能

  • Injectionprovide選項進行初始化
  • 對於用戶定義的屬性進行初始化,如下是按順序來初始化,若是後面的屬性名與前面相同的話,會報錯:
    • props:傳進來的屬性是父組件過來的,這個屬性不須要在子組件裏面進行觀察(由於父組件已經觀察它了)
    • method:對設定的方法掛載到vm實例中,而且使用bind方法綁定函數上下文爲vm對象,這也就是爲何咱們使用箭頭函數的方式的時候,也是可以經過this訪問vm實例。
    • data:在選項的合併的時候將data處理成一個可執行的函數,這裏的話,是將這個函數進行執行,獲得組件的數據,而且將全部數據進行深度觀測。
    • computed:建立一個lazy型的觀察者,這個觀察者是不會主動去觸發的(若是計算屬性是函數的話,每次須要拿到值的時候進行調用函數,若是是普通值的話,則會直接獲取普通值)。這個計算屬性是不能直接賦值操做的(getter方法被設置成一個空函數,因此怎麼進行賦值也沒有用),因此是個lazy型的觀察者。computed的值只有在獲取的時候纔會執行而後拿到最新的值,因此它是一個懶求值。
    • watch:對傳進來觀察的對象的路徑進行解析,拿到對應的屬性。建立一個觀察者,對這個屬性進行觀察,一旦屬性發生變化的時候,會觸發這個觀察者,調用傳進來的函數。

3.beforeMount

在掛載前的生命鉤子前會進行元素綁定以及渲染函數的操做:this

  • vm.$el屬性賦值
  • 渲染函數若是爲空,進行建立空的方法

4.mounted

在本生命週期前,會進行渲染函數觀察者的設定,當屬性改變的時候,會直接反映到頁面數據中。渲染函數觀察者爲何要在數據觀察者設定後進行建立的話,這個等到後面的講述Vue的觀察者篇章的時候再說

這裏要注意到一點,在建立觀察者的時候,咱們能夠經過傳給觀察者一個before方法,這樣執行觀察者的函數前調用before方法。渲染函數觀察者會傳進一個before方法,這個方法只有一句話,就是將本組件的聲明週期改成beforeUpdate,也就是說在修改數據的時候,反映到頁面以前,組件會先進入到beforeUpdate生命週期。

5.beforeUpdate

上面一點也說明了,在beforeUpdate前是用戶的各項操做,只要修改了某個屬性值,那麼就會觸發這個生命週期鉤子。

6.updated

觸發beforeUpdate後,這時候是在清空調用觀察者隊列(queue是存放如今時間全部須要進行回調的觀察者的一個隊列,固然渲染函數觀察者也在裏面)。整個隊列執行完畢後會進行調用這個updated生命週期函數,在調用這個生命週期函數的時候,頁面從新渲染已經結束了,因此在updated生命週期鉤子函數前會作如下事情:

  • 打補丁(patch函數),這個函數首先是使用diff算法實現對dom操做的最小化,而後diff算法完成後,使用方法來針對於VNode修改dom節點內容。
  • 從新綁定修改後的dom節點,大家能夠在綁定vm對象的dom節點的屬性上看到**__vue__這個屬性,這個就是指向綁定的vm**。

7.beforeDestroy

僅僅是判斷是否正在被destroy,使用變量鎖(單線程下變量便可做爲鎖,無需使用原子操做)防止重複操做。

8.destroyed

這裏主要是對事件、觀察者的卸載,在這個生命週期鉤子函數前會執行如下事情:

  • 觀察者的卸載(computedwatchrender)三個觀察者進行卸載。
  • 對根數據的觀察者的卸載。
  • VNode進行卸載,VNode的卸載會引起dom節點的刪除。

9.destroyed以後

這裏對於組件的產生的事件、監聽的事情進行卸載,以及將對應的dom節點的**__vue__**屬性設空,最後對父親節點的解綁。

3、回答問題

  • 問題1在前面已經回答了,就不重複了

  • 問題2:每一個生命週期可以訪問到的數據是否相同?

    回答是不,在beforeCreate根本就拿不到數據。

    可是在正常運行的聲明周期函數中(beforeMountupdated),數據是一致的。這是在修改數據的時候,在一個事件循環中先觀察者放到隊列中,而後觸發觀察者回調隊列的執行。不過在一個vm對象中,渲染函數觀察者老是在隊列最後執行的,並且執行beforeUpdate是在執行渲染函數觀察者回調函數以前執行的。換句話說,觸發beforeUpdate以前,$watch定義的方法已經執行完畢,該更新的數據已經更新了。

  • 咱們知道修改了data後可以觸發更新生命週期的鉤子,那麼究竟是怎樣進行觸發的?

    在上面已經說明地很清楚了,若是有不懂的地方能夠再去看一下整個流程。

  • 咱們知道Vue的話是經過VNode來更新管理真實的DOM的,那麼在上面的生命週期何時進行渲染的?

    beforeUpdateupdated中間。

相關文章
相關標籤/搜索