Vue 組件生命週期鉤子函數

Vue 組件生命週期鉤子函數

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

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

  • 實例化期

    組件創建

  • 存在期

    組件更新

  • 銷燬期

    組件銷燬

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

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

生命週期示意圖

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

其中官網並沒有把 renderrenderError 函數歸納爲生命週期鉤子函數。

其中 Has "el" option 對比如下:

有 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
  }
})

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章