Vue2.0生命周期(組件鈎子函數與路由守衛)


組件相關鈎子函數: beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destoryed

還有兩個特殊的(使用keep-alive):activateddeactivated(不詳述)
v2.5.0+新增: errorCaptured (暫時還不知道咋用)

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

組件生命周期鈎子函數

beforeCreate

實例初始化之后

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

created

實例創建完成,並已經完成以下配置:數據觀測(data observer),屬性和方法的運算, watch/event 事件回調
此時可以調用methods中定義的方法,修改data的數據,並且可觸發響應式變化、computed值重新計算,watch到變更等

還未掛載到DOM,不能訪問到屬性,ref屬性內容為空數組

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

這個生命周期階段比較常用,比如ajax請求獲取初始化數據對實例進行初始化預處理等操作;但要注意在結合vue-router使用時,進入created生命周期階段后是無法對掛載實例進行攔截的,此時實例已經創建完成;故如果需要某些數據獲取完成情況才允許進入頁面的場景,建議在路由鈎子beforeRouteEnter中實現

beforeMount

在掛載開始之前被調用

注意:從vue生命周期圖可以看出
當new Vue({...})的配置中沒有el屬性時,生命周期暫停,等待vm.$mount(el)調用時才繼續
created->beforeMount

beforeMount之前,會找到對應的template,並編譯成render函數
(這個步驟如果使用.vue文件和運行時版本將會在構建時提前完成)

template查找的優先級順序:
template參數 > el 外部HTML
如果指定了render函數,則直接采用render函數,即忽略template參數和el外部HTML

寫個栗子測試:

<div id="app">template outside</div> ... import App from './App.vue'; // App是任一Vue組件對象 new Vue({ el: '#app', // template: '<p>template inside</p>', // part inside // render: h => h(App) // part render })

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

Vue的編譯過程暫略

mounted

el被新創建的$el替換 ---- 怎么理解?

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

實例掛載到DOM上,此時可以通過DOM API獲取到DOM節點,$ref屬性可以訪問

雖然經常觀察到先進入子組件mounted,但根據Vue官方文檔:

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

在這個階段改變data上的值,相關的computed屬性可以立刻更新;但需要進入到下一次DOM更新,才能看到DOM上數據更新

栗子:

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觸發還要靠后 - 這個待會說到路由相關鈎子時再展開

beforeUpdate

這里的更新對象是模板,即需要虛擬 DOM 重新渲染和打補丁,beforeUpdate發生在以上兩個流程之前,此時新的虛擬DOM已經生成

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

如:

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

在一些參考文章中看到:
在這個鈎子函數中,可以對狀態進行進一步更改,不會再次觸發重渲染流程 --- 這個說法有問題
如果對狀態進行變更會導致重新進入beforeUpdate(這里變更的狀態同樣要在模板中使用,如果變更沒有在模板中使用的data,才不會再次觸發重渲染流程)
而且若變更操作不是基礎類型的簡單賦值,還會引起死循環(不斷重新進入beforeUpdate)!

看看這個栗子,依次把注釋打開測試

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沒有在模板中使用,變更不會引起重渲染流程 // this.a = 3; // 會再次進入一次重渲染流程,第二次進入時發現a仍是3,值沒有變更,不會再次重渲染 // this.a ++; // 會引起死循環,每次進入重渲染流程時,a的值都會變更 }, updated() { console.log(document.getElementById('testa').innerHTML) } }) 

應該避免在這個鈎子函數中操作數據

updated

由於數據更改導致的虛擬 DOM 重新渲染和打補丁,在這之后會調用該鈎子。

當這個鈎子被調用時,組件 DOM 已經更新,可以執行依賴於 DOM 的操作

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

同樣,應該避免在這個鈎子函數中操作數據

beforeDestroy

實例銷毀之前調用。在這一步,實例仍然完全可用,this仍能獲取到實例

一般在這一步中進行:銷毀定時器、解綁全局事件、銷毀插件對象等操作

destroyed

Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀

注意:vue2.0之后主動調用$destroy()不會移除dom節點,作者不推薦直接destroy這種做法,具體參考https://github.com/vuejs/vue/...,如果實在需要這樣用可以在這個生命周期鈎子中手動移除dom節點

總結

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

路由守衛 —— 路由級別的(全局&路由獨享)

router.beforeEach

全局前置守衛
當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是異步解析執行,此時導航在所有守衛 resolve 完之前一直處於 等待中

如何使用:

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

一般在這個守衛方法中進行全局攔截,比如必須滿足某種條件(用戶登錄等)才能進入路由的情況

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

  • next(): 進行管道中的下一個鈎子。如果全部鈎子執行完了,則導航的狀態就是 confirmed (確認的)
  • next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了(可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址 —— 也就是說並不是單純的中斷,還會檢查URL的變更以保證不會錯誤的進入到next路由
  • next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。可傳遞的參數與router.push中選項一致
  • next(error): (v2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調

router.beforeResolve (v 2.5.0+)

全局解析守衛

和beforeEach類似,區別是在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用
即在 beforeEach 和 組件內beforeRouteEnter 之后

參數和beforeEach一致,也需要調用next對導航確認

router.afterEach

全局后置鈎子
在所有路由跳轉結束的時候調用
這些鈎子不會接受 next 函數也不會改變導航本身

beforeEnter

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

路由守衛 —— 組件內

beforeRouteEnter

在渲染該組件的對應路由被確認前調用,用法和參數與beforeEach類似,next需要被主動調用
注意:

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

beforeRouteUpdate (v 2.2+)

在當前路由改變,並且該組件被復用時調用,可以通過this訪問實例, next需要被主動調用,不能傳回調

  • 對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,組件實例會被復用,該守衛會被調用
  • 當前路由query變更時,該守衛會被調用
  • vue-router推薦的數據獲取方法二中,結合beforeRouteEnter使用,在路由參數變更時可以重新獲取數據,獲取成功再調用next(),參考:https://router.vuejs.org/zh-c...

之前在手機瀏覽器中好像發現這個守衛的bug?@TODO 待確認

beforeRouteLeave

導航離開該組件的對應路由時調用,可以訪問組件實例 this,next需要被主動調用,不能傳回調

總結

結合並擴展Vue-router官方文檔的說明:

  • 導航行為被觸發,此時導航未被確認。
  • 在失活的組件里調用離開守衛 beforeRouteLeave。
  • 調用全局的 beforeEach 守衛。
  • 在重用的組件里調用 beforeRouteUpdate 守衛 (2.2+)。
  • 在路由配置里調用 beforeEnter。
  • 解析異步路由組件(如果有)。
  • 在被激活的組件里調用 beforeRouteEnter。
  • 調用全局的 beforeResolve 守衛 (2.5+),標示解析階段完成。
  • 導航被確認。
  • 調用全局的 afterEach 鈎子。
  • 非重用組件,開始組件實例的生命周期

    • beforeCreate&created
    • beforeMount&mounted
  • 觸發 DOM 更新。
  • 用創建好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數。
  • 導航完成

路由守衛與組件生命周期-首次


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM