Vue 組件生命週期鉤子函數

例子是在 jsrun.net 平臺編寫,不支持移動端平臺,因此本文建議在 PC 端進行閱讀。

所謂生命週期鉤子函數(簡稱生命週期函數),指的是組件的建立更新銷燬三個階段所觸發執行的函數。根據每一個階段觸發的鉤子函數,咱們能夠相應的作一些操做,如獲取後端接口數據、監聽事件、執行事件、執行定時器、移除事件、清理定時器等等。html

生命週期根據上面的三個階段,本人總結爲:vue

  • 實例化期

    組件建立git

  • 存在期

    組件更新github

  • 銷燬期

    組件銷燬面試

後續會根據這三大週期,分別說明生命週期函數。segmentfault

爲了方便理解,本人在 http://jsrun.net 上編寫了例子。(一個跟 jsfiddle 差很少的網站,國內 jsfiddle 被牆了)後端

生命週期示意圖

首先看下官網的生命週期示意圖,這裏的生命週期函數都是針對瀏覽器端的,服務端目前只支持 beforeCreatecreated 這兩個生命週期函數。api

其中官網並無把 renderrenderError 函數概括爲生命週期鉤子函數。瀏覽器

其中 Has "el" option 對好比下:app

有 el

// 有el屬性的狀況下
new Vue({
  el: "#app",
  beforeCreate: function() {
    console.log("調用了beforeCreate");
  },
  created: function() {
    console.log("調用了created");
  },
  beforeMount: function() {
    console.log("調用了beforeMount");
  },
  mounted: function() {
    console.log("調用了mounted");
  }
});

// 輸出結果
// 調用了beforeCreate
// 調用了created
// 調用了beforeMount
// 調用了mounted

無 el

// 有el屬性的狀況下
const vm new Vue({
  beforeCreate: function() {
    console.log("調用了beforeCreate");
  },
  created: function() {
    console.log("調用了created");
  },
  beforeMount: function() {
    console.log("調用了beforeMount");
  },
  mounted: function() {
    console.log("調用了mounted");
  }
});

// 輸出結果
// 調用了beforeCreate
// 調用了created

無 el 時,若是須要掛載,能夠這樣處理:vm.$mount('#app')。效果同樣了,本質上沒區別,只是用法更靈活。

實例化期

實例化期會涉及到如下生命週期函數(執行順序自上而下):

  • beforeCreate
  • created
  • beforeMount
  • mouted

其中 beforeCreatecreated 中間會觸發 render 函數,若是有 template 會轉換爲 render 函數進行渲染。(固然若是組件的 定義了 render 函數,那麼 render 函數優先級更高)

詳細的例子請看 http://jsrun.net/LZyKp/edit

// 輸出請看 右下角 Console 命令行工具
new Vue({
  el: '#dynamic-component-demo',
  data: {
    num: 2,
  },
  beforeCreate(){
    console.log("beforeCreate",this.num,this.a);
    // 輸出爲 befoerCreate,,
    // this.num 數據還沒監測,this.a 方法未綁定
  },
  created(){
    console.log("created",this.num,this.a,this.$el);
    // 輸出爲 created, 2, function () { [native code] }
  },
  beforeMount(){
    console.log(this.$el.innerText);
    // 輸出 {{ num }},仍是原來的 DOM 內容
  },
  mounted(){
    console.log(this.$el.innerText);
    // 輸出 2,已是 vue 渲染的 DOM 內容
  },
  methods: {
    a(){}
  }
})

beforeCreate

在實例初始化以後,數據觀測 (data observer) 和 event/watcher 事件配置以前被調用。

created

在實例建立完成後被當即調用。在這一步,實例已完成如下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el 屬性目前不可見。

beforeMount

在掛載開始以前被調用:相關的 render 函數首次被調用。$el 屬性已經可見,但仍是原來的 DOM,並不是是新建立的。

mounted

el 被新建立的 vm.$el 替換,並掛載到實例上去以後調用該鉤子。若是 root 實例掛載了一個文檔內元素,當 mounted 被調用時 vm.$el 也在文檔內。

注意 mounted 不會承諾全部的子組件也都一塊兒被掛載。若是你但願等到整個視圖都渲染完畢,能夠用 vm.$nextTick 替換掉 mounted

mounted: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been rendered
  })
}

存在期

存在期會涉及到如下生命週期函數:

  • beforeUpdate
  • updated

Vue 須要改變數據纔會觸發組件從新渲染,纔會觸發上面的存在期鉤子函數。其中 beforeUpdateupdated 中間會觸發 render 函數。

例子請看 http://jsrun.net/8ZyKp/edit

// 須要點擊更新按鈕
// 連續點擊更新按鈕,都會是 2 秒後不點擊更新纔會輸出 「2 秒沒更新了」
// 輸出請看 右下角 Console 命令行工具
new Vue({
  el: '#dynamic-component-demo',
  data: {
    num: 2,
  },
  beforeUpdate(){
    clearTimeout(this.clearTimeout);
    this.clearTimeout = setTimeout(function(){
      console.log("2 秒沒更新了");
    },2000);
    console.log("beforeUpdate",this.num,this.$el.innerText);
    // 第一次點擊更新,輸出爲 beforeUpdate,3,點擊更新 2
  },
  updated(){
    console.log("updated",this.num,this.$el.innerText);
    // 第一次點擊更新,輸出爲 updated,3,點擊更新 3
  },
  methods: {
    updateComponent(){
      this.num++;
    }
  }
})

beforeUpdate

數據更新時,虛擬 DOM 變化以前調用,這裏適合在更新以前訪問現有的 DOM,好比手動移除已添加的事件監聽器。

請不要在此函數中更改狀態,不然會觸發死循環。

updated

數據更新和虛擬 DOM 變化以後調用。

當這個鉤子被調用時,組件 DOM 已經更新,因此你如今能夠執行依賴於 DOM 的操做。然而在大多數狀況下,你應該避免在此期間更改狀態。若是要相應狀態改變,一般最好使用計算屬性watcher 取而代之。

和 mounted 同樣, updated 不會承諾全部的子組件也都一塊兒被重繪。若是你但願等到整個視圖都重繪完畢,能夠用 vm.$nextTick 替換掉 updated

updated: function () {
  this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been re-rendered
  })
}

請不要在此函數中更改狀態,不然會觸發死循環。

銷燬期

銷燬期會涉及到如下生命週期函數:

  • beforeDestroy
  • destroyed

例子請看 http://jsrun.net/QZyKp/edit

// 切換 tab,看右下角 console 輸出
Vue.component('tab-home', { 
    template: '<div>Home component</div>',
  beforeDestroy(){
    console.log("tab-home","beforeDestroy");
  },
  destroyed(){
    console.log("tab-home","destroyed");
  },
})
Vue.component('tab-posts', { 
    template: '<div>Posts component</div>',
  beforeDestroy(){
    console.log("tab-posts","beforeDestroy");
  },
  destroyed(){
    console.log("tab-posts","destroyed");
  },
})
Vue.component('tab-archive', { 
    template: '<div>Archive component</div>',
  beforeDestroy(){
    console.log("tab-archive","beforeDestroy");
  },
  destroyed(){
    console.log("tab-archive","destroyed");
  },
})

new Vue({
  el: '#dynamic-component-demo',
  data: {
    currentTab: 'Home',
    tabs: ['Home', 'Posts', 'Archive']
  },
  computed: {
    currentTabComponent: function () {
      return 'tab-' + this.currentTab.toLowerCase()
    }
  }
})

beforeDestroy

實例銷燬以前調用,在這一步,實例仍然徹底可用。通常在這裏移除事件監聽器、定時器等,避免內存泄漏

destroyed

Vue 實例銷燬後調用。調用後,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。

因此若是須要用到 Vue 實例指示的所用綁定的東西,須要在 beforeDestroy 中使用。這麼說,destroyed 函數能作的事,在 beforeDestroy 也能作,因此不必在 destroyed 函數中處理。

其餘不經常使用的生命週期函數

  • activated

    當組件激活的時候調用,能夠參考構建組件 - keep-alive

  • deactivated

    當組件停用的時候調用,能夠參考構建組件 - keep-alive

  • errorCaptured

    這個生命鉤子詳細請看官網,2.5.0 新增的,當捕獲一個來自子孫組件的錯誤時被調用。

使用注意

生命週期函數請不要使用 ES6 箭頭函數,不然 this 指向會有問題。

請看這個例子 http://jsrun.net/cZyKp/edit

// 輸出請看 右下角 Console 命令行工具
new Vue({
  el: '#dynamic-component-demo',
  data: {
    num: 2,
  },
  created: ()=>{
    console.log("created",this);
    // 輸出爲 created,[object Window]
    // this 指向不是 Vue 實例而是父級 this
  }
})

發散思考

既然每一個組件都有生命週期,那麼父子、兄弟組件之間執行順序有什麼區別呢?

那麼你首先須要瞭解到 template 何時轉變成虛擬 DOM,而虛擬 DOM 又是在何時構建完成,而後又是何時掛載到真實的 DOM 上面。

這裏不深刻說明 vue 原理,大概瞭解下 template 到虛擬 DOM 的過程:

template -> AST 樹 -> render 函數

實際上咱們關注點在 render 函數,能夠了解下運行時-編譯器-vs-只包含運行時。虛擬 DOM 就是在 render 函數中造成。若是你使用過 JSX,那麼能夠簡單的把 JSX 理解爲一個個的虛擬 DOM。固然 render 函數也會執行掛載到真實 DOM 的邏輯。

虛擬 DOM 就是一個樹,vue 會把全部組件映射爲虛擬 DOM 後渲染到真實 DOM 節點中,掛載不管如何必定是在最後執行的,因此全部組件 mounted 函數輸出的日誌必然是連在一塊兒的,updated 也同理。

若是你不理解這句話,能夠看這個例子 http://jsrun.net/7hyKp/edit

父子組件生命週期函數執行順序

render 函數纔是父子組件生命週期函數的分割線。

而 render 函數的順序以下。

實例化期:beforeMount -> render -> mounted

存在期:beforeUpdate -> render -> updated

詳細請看在線例子,http://jsrun.net/vhyKp/edit

兄弟組件生命週期執行順序

詳細請看在線例子,http://jsrun.net/NhyKp/edit

參考文章


學習和總結文章同步發佈於 https://github.com/xianshanna...,有興趣能夠關注一下,一塊兒學習和進步。
相關文章
相關標籤/搜索