Vue2.0完整生命周期(包括路由守衛(wèi))

組件相關(guān)鉤子函數(shù): beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destoryed
還有兩個(gè)特殊的(使用keep-alive):activateddeactivated(不詳述)
v2.5.0+新增: errorCaptured (暫時(shí)還不知道咋用)

路由守衛(wèi):
全局&路由獨(dú)享:beforeEach、beforeResolve(v2.5.0+新增)、afterEach ;beforeEnter(路由獨(dú)享,類似beforeEach)
組件內(nèi):beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave

組件生命周期鉤子函數(shù)

beforeCreate

實(shí)例初始化之后

this指向創(chuàng)建的實(shí)例
數(shù)據(jù)觀測(cè)(data observer)和event/watcher配置尚未完成
不能訪問到methods、data、computed、watch上的方法和數(shù)據(jù)

created

實(shí)例創(chuàng)建完成,并已經(jīng)完成以下配置:數(shù)據(jù)觀測(cè)(data observer),屬性和方法的運(yùn)算, watch/event 事件回調(diào)
此時(shí)可以調(diào)用methods中定義的方法,修改data的數(shù)據(jù),并且可觸發(fā)響應(yīng)式變化、computed值重新計(jì)算,watch到變更等

還未掛載到DOM,不能訪問到$el屬性,$ref屬性內(nèi)容為空數(shù)組

new Vue({
  data () {
    return {
      a : 1
    }
  } ,
  created (){
    console.log( this.a )  // 1
  }
})

這個(gè)生命周期階段比較常用,比如ajax請(qǐng)求獲取初始化數(shù)據(jù)對(duì)實(shí)例進(jìn)行初始化預(yù)處理等操作;但要注意在結(jié)合vue-router使用時(shí),進(jìn)入created生命周期階段后是無法對(duì)掛載實(shí)例進(jìn)行攔截的,此時(shí)實(shí)例已經(jīng)創(chuàng)建完成;故如果需要某些數(shù)據(jù)獲取完成情況才允許進(jìn)入頁(yè)面的場(chǎng)景,建議在路由鉤子beforeRouteEnter中實(shí)現(xiàn)

beforeMount

在掛載開始之前被調(diào)用

注意:從vue生命周期圖可以看出
當(dāng)new Vue({...})的配置中沒有el屬性時(shí),生命周期暫停,等待vm.$mount(el)調(diào)用時(shí)才繼續(xù)


created->beforeMount

beforeMount之前,會(huì)找到對(duì)應(yīng)的template,并編譯成render函數(shù)
(這個(gè)步驟如果使用.vue文件和運(yùn)行時(shí)版本將會(huì)在構(gòu)建時(shí)提前完成)

template查找的優(yōu)先級(jí)順序:
** template參數(shù) > el 外部HTML**
如果指定了render函數(shù),則直接采用render函數(shù),即忽略template參數(shù)和el外部HTML

寫個(gè)栗子測(cè)試:

<div id="app">template outside</div>
...
import App from './App.vue';  // App是任一Vue組件對(duì)象

new Vue({
  el: '#app',
  // template: '<p>template inside</p>',   // part inside
  // render: h => h(App)     //  part render
})

需要Vue完整版本支持,注釋part inside和part render依次打開即可觀察到三次不同的結(jié)果

Vue的編譯過程暫略

mounted

el被新創(chuàng)建的$el替換 ---- 怎么理解?

這個(gè)可以理解為掛載前為實(shí)例尋找了一個(gè)暫時(shí)容身之處el,編譯完成($el創(chuàng)建完成)后替換這個(gè)容身之處完成實(shí)例的掛載
如:之前那個(gè)栗子中,將part render打開后觀察生成的DOM結(jié)構(gòu),<div id="app">template outside</div>這個(gè)節(jié)點(diǎn)即原el已經(jīng)被替換掉

實(shí)例掛載到DOM上,此時(shí)可以通過DOM API獲取到DOM節(jié)點(diǎn),$ref屬性可以訪問

雖然經(jīng)常觀察到先進(jìn)入子組件mounted,但根據(jù)Vue官方文檔:

注意 `mounted` 不會(huì)承諾所有的子組件也都一起被掛載。如果你希望等到整個(gè)視圖都渲染
完畢,可以用 [vm.$nextTick](https://cn.vuejs.org/v2/api/#vm-nextTick) 

在這個(gè)階段改變data上的值,相關(guān)的computed屬性可以立刻更新;但需要進(jìn)入到下一次DOM更新,才能看到DOM上數(shù)據(jù)更新

栗子:

new Vue({
  el: '#app',
  template: '<p id="testa">{{a}}</p>',
  router,
  data ()
  {
    return {
      a : 0
    }
  },
  mounted() {
    this.a ++;
    console.log(this.b);   //  2
    console.log(document.getElementById('testa').innerHTML)  //  0
  },
  computed :{
    b (){
      return this.a+1;
    }
  }
})

beforeRouteEnter的next的勾子比mounted觸發(fā)還要靠后 - 這個(gè)待會(huì)說到路由相關(guān)鉤子時(shí)再展開

beforeUpdate

這里的更新對(duì)象是模板,即需要虛擬 DOM 重新渲染和打補(bǔ)丁,beforeUpdate發(fā)生在以上兩個(gè)流程之前,此時(shí)新的虛擬DOM已經(jīng)生成

如果發(fā)生變更的數(shù)據(jù)在模板中并沒有使用(包括直接和間接,間接:比如某個(gè)依賴該數(shù)據(jù)的計(jì)算屬性在模板中使用了),則不會(huì)觸發(fā)更新流程!!!

如:

new Vue({
  el: '#app',
  template: '<p id="testa">{{a}}</p>',
  router,
  data ()
  {
    return {
      a : 0,
      b :  1
    }
  },
  mounted (){
    this.b ++;   //   b并沒有在模板中使用
  },
  beforeUpdate (){
    debugger;  //  不會(huì)進(jìn)入這個(gè)斷點(diǎn)
  }
})

在一些參考文章中看到:
在這個(gè)鉤子函數(shù)中,可以對(duì)狀態(tài)進(jìn)行進(jìn)一步更改,不會(huì)再次觸發(fā)重渲染流程 --- 這個(gè)說法有問題
如果對(duì)狀態(tài)進(jìn)行變更會(huì)導(dǎo)致重新進(jìn)入beforeUpdate(這里變更的狀態(tài)同樣要在模板中使用,如果變更沒有在模板中使用的data,才不會(huì)再次觸發(fā)重渲染流程)
而且若變更操作不是基礎(chǔ)類型的簡(jiǎn)單賦值,還會(huì)引起死循環(huán)(不斷重新進(jìn)入beforeUpdate)!

看看這個(gè)栗子,依次把注釋打開測(cè)試

new Vue({
  el: '#app',
  template: '<p id="testa">{{a}}</p>',
  router,
  data ()
  {
    return {
      a : 0,
      c:  0
    }
  },
  beforeUpdate() {
    console.log(document.getElementById('testa').innerHTML)
    // this.c = 1;   //  this.c沒有在模板中使用,變更不會(huì)引起重渲染流程
    // this.a = 3;  //  會(huì)再次進(jìn)入一次重渲染流程,第二次進(jìn)入時(shí)發(fā)現(xiàn)a仍是3,值沒有變更,不會(huì)再次重渲染
    // this.a ++;   //  會(huì)引起死循環(huán),每次進(jìn)入重渲染流程時(shí),a的值都會(huì)變更
  },
  updated() {
    console.log(document.getElementById('testa').innerHTML)
  }
})

應(yīng)該避免在這個(gè)鉤子函數(shù)中操作數(shù)據(jù)

updated

由于數(shù)據(jù)更改導(dǎo)致的虛擬 DOM 重新渲染和打補(bǔ)丁,在這之后會(huì)調(diào)用該鉤子。

當(dāng)這個(gè)鉤子被調(diào)用時(shí),組件 DOM 已經(jīng)更新,可以執(zhí)行依賴于 DOM 的操作

注意 `updated` 不會(huì)承諾所有的子組件也都一起被重繪。如果你希望等到整個(gè)視圖都重繪完畢,
可以用 [vm.$nextTick](https://cn.vuejs.org/v2/api/#vm-nextTick) 

同樣,應(yīng)該避免在這個(gè)鉤子函數(shù)中操作數(shù)據(jù)

beforeDestroy

實(shí)例銷毀之前調(diào)用。在這一步,實(shí)例仍然完全可用,this仍能獲取到實(shí)例

一般在這一步中進(jìn)行:銷毀定時(shí)器、解綁全局事件、銷毀插件對(duì)象等操作

destroyed

Vue 實(shí)例銷毀后調(diào)用。調(diào)用后,Vue 實(shí)例指示的所有東西都會(huì)解綁定,所有的事件監(jiān)聽器會(huì)被移除,所有的子實(shí)例也會(huì)被銷毀

注意:vue2.0之后主動(dòng)調(diào)用$destroy()不會(huì)移除dom節(jié)點(diǎn),作者不推薦直接destroy這種做法,具體參考https://github.com/vuejs/vue/issues/5256,如果實(shí)在需要這樣用可以在這個(gè)生命周期鉤子中手動(dòng)移除dom節(jié)點(diǎn)

總結(jié)

參考官方文檔中的生命周期說明圖

路由守衛(wèi) —— 路由級(jí)別的(全局&路由獨(dú)享)

router.beforeEach

全局前置守衛(wèi)
當(dāng)一個(gè)導(dǎo)航觸發(fā)時(shí),全局前置守衛(wèi)按照創(chuàng)建順序調(diào)用。守衛(wèi)是異步解析執(zhí)行,此時(shí)導(dǎo)航在所有守衛(wèi) resolve 完之前一直處于 等待中

如何使用:

router.beforeEach((to, from, next) => {
  console.log('全局前置守衛(wèi):beforeEach -- next需要調(diào)用')
  next()
})

一般在這個(gè)守衛(wèi)方法中進(jìn)行全局?jǐn)r截,比如必須滿足某種條件(用戶登錄等)才能進(jìn)入路由的情況

參數(shù)to和from都是路由對(duì)象Route
next是個(gè)Function,有以下幾種用法(from api文檔)

  • next(): 進(jìn)行管道中的下一個(gè)鉤子。如果全部鉤子執(zhí)行完了,則導(dǎo)航的狀態(tài)就是 confirmed (確認(rèn)的)
  • next(false): 中斷當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了(可能是用戶手動(dòng)或者瀏覽器后退按鈕),那么 URL 地址會(huì)重置到 from 路由對(duì)應(yīng)的地址 —— 也就是說并不是單純的中斷,還會(huì)檢查URL的變更以保證不會(huì)錯(cuò)誤的進(jìn)入到next路由
  • next('/') 或者 next({ path: '/' }): 跳轉(zhuǎn)到一個(gè)不同的地址。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個(gè)新的導(dǎo)航。可傳遞的參數(shù)與router.push中選項(xiàng)一致
  • next(error): (v2.4.0+) 如果傳入 next 的參數(shù)是一個(gè) Error 實(shí)例,則導(dǎo)航會(huì)被終止且該錯(cuò)誤會(huì)被傳遞給 router.onError() 注冊(cè)過的回調(diào)

router.beforeResolve (v 2.5.0+)

全局解析守衛(wèi)

和beforeEach類似,區(qū)別是在導(dǎo)航被確認(rèn)之前,同時(shí)在所有組件內(nèi)守衛(wèi)和異步路由組件被解析之后,解析守衛(wèi)就被調(diào)用
即在 beforeEach 和 組件內(nèi)beforeRouteEnter 之后

參數(shù)和beforeEach一致,也需要調(diào)用next對(duì)導(dǎo)航確認(rèn)

router.afterEach

全局后置鉤子
在所有路由跳轉(zhuǎn)結(jié)束的時(shí)候調(diào)用
這些鉤子不會(huì)接受 next 函數(shù)也不會(huì)改變導(dǎo)航本身

beforeEnter

可直接定義在路由配置上,和beforeEach方法參數(shù)、用法相同

路由守衛(wèi) —— 組件內(nèi)

beforeRouteEnter

在渲染該組件的對(duì)應(yīng)路由被確認(rèn)前調(diào)用,用法和參數(shù)與beforeEach類似,next需要被主動(dòng)調(diào)用
注意:

  • 此時(shí)組件實(shí)例還未被創(chuàng)建,不能訪問this
  • 可以通過傳一個(gè)回調(diào)給 next來訪問組件實(shí)例。在導(dǎo)航被確認(rèn)的時(shí)候執(zhí)行回調(diào),并且把組件實(shí)例作為回調(diào)方法的參數(shù)
beforeRouteEnter (to, from, next) {
  // 這里還無法訪問到組件實(shí)例,this === undefined
  next( vm => {
    // 通過 `vm` 訪問組件實(shí)例
  })
}
  • 可以在這個(gè)守衛(wèi)中請(qǐng)求服務(wù)端獲取數(shù)據(jù),當(dāng)成功獲取并能進(jìn)入路由時(shí),調(diào)用next并在回調(diào)中通過 vm訪問組件實(shí)例進(jìn)行賦值等操作
  • beforeRouteEnter觸發(fā)在導(dǎo)航確認(rèn)、組件實(shí)例創(chuàng)建之前:beforeCreate之前;而next中函數(shù)的調(diào)用在mounted之后:為了確保能對(duì)組件實(shí)例的完整訪問

beforeRouteUpdate (v 2.2+)

在當(dāng)前路由改變,并且該組件被復(fù)用時(shí)調(diào)用,可以通過this訪問實(shí)例, next需要被主動(dòng)調(diào)用,不能傳回調(diào)

  • 對(duì)于一個(gè)帶有動(dòng)態(tài)參數(shù)的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉(zhuǎn)的時(shí)候,組件實(shí)例會(huì)被復(fù)用,該守衛(wèi)會(huì)被調(diào)用
  • 當(dāng)前路由query變更時(shí),該守衛(wèi)會(huì)被調(diào)用
  • vue-router推薦的數(shù)據(jù)獲取方法二中,結(jié)合beforeRouteEnter使用,在路由參數(shù)變更時(shí)可以重新獲取數(shù)據(jù),獲取成功再調(diào)用next(),參考:https://router.vuejs.org/zh-cn/advanced/data-fetching.html

之前在手機(jī)瀏覽器中好像發(fā)現(xiàn)這個(gè)守衛(wèi)的bug?@TODO 待確認(rèn)

beforeRouteLeave

導(dǎo)航離開該組件的對(duì)應(yīng)路由時(shí)調(diào)用,可以訪問組件實(shí)例 thisnext需要被主動(dòng)調(diào)用,不能傳回調(diào)

總結(jié)

結(jié)合并擴(kuò)展Vue-router官方文檔的說明:

  • 導(dǎo)航行為被觸發(fā),此時(shí)導(dǎo)航未被確認(rèn)。
  • 在失活的組件里調(diào)用離開守衛(wèi) beforeRouteLeave。
  • 調(diào)用全局的 beforeEach 守衛(wèi)。
  • 在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)。
  • 在路由配置里調(diào)用 beforeEnter。
  • 解析異步路由組件(如果有)。
  • 在被激活的組件里調(diào)用 beforeRouteEnter。
  • 調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+),標(biāo)示解析階段完成。
  • 導(dǎo)航被確認(rèn)
  • 調(diào)用全局的 afterEach 鉤子。
  • 非重用組件,開始組件實(shí)例的生命周期
    • beforeCreate&created
    • beforeMount&mounted
  • 觸發(fā) DOM 更新。
  • 用創(chuàng)建好的實(shí)例調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù)。
  • 導(dǎo)航完成
路由守衛(wèi)與組件生命周期-首次
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容