vue所有功能的實現都是圍繞其生命周期進行的,在生命周期的不同階段調用對應的鉤子函數可以實現組件數據管理和DOM渲染兩大重要功能。學習實例的生命周期,能幫助我們理解vue實例的運行機制,更好地利用鉤子函數完成我們的業務代碼。 1、即將創建:對應的鉤子函數為beforeCreate。此階段為實例初始化 ...
vue所有功能的實現都是圍繞其生命周期進行的,在生命周期的不同階段調用對應的鉤子函數可以實現組件數據管理和DOM渲染兩大重要功能。學習實例的生命周期,能幫助我們理解vue實例的運行機制,更好地利用鉤子函數完成我們的業務代碼。
1、即將創建:對應的鉤子函數為beforeCreate。此階段為實例初始化之後,此時的數據觀察和事件機制都未形成。
<template> <div class="router-page-wrap" style="background: #fc595d;"> this is usercenter <input type="text" v-model="message" ref="input"> {{message}} </div> </template> <script> var common = require('common'); module.exports = { data : function() { return { message : 'not update' } }, beforeCreate : function () { console.log('this is beforeCreate :', this.message, this.$refs.input); } } </script>
得到的結果是:
此時,實例中的data和el都是undefined。因此,在beforeCreate鉤子函數中不能使用data中的數據,也不能獲得DOM節點。
2、創建完畢:對應的鉤子函數為created。在這個階段vue實例已經創建,我們在同樣列印一下data和DOM元素。
上面代碼的基礎上我們添加created鉤子函數:
得到的結果是什麼呢??
此時,我們能夠讀取到數據data的值,但是DOM還沒有生成,所以和DOM相關的屬性還不存在,自然也就不能獲取DOM元素。在Vue2的源碼中也是這樣描述的:
首先,運行new Vue()
的時候,會進入代碼src/core/instance/index.js
的Vue構造方法中,並執行this._init()
方法。在_init
中,會對各個功能進行初始化,並執行beforeCreate
和created
兩個生命周期方法。核心代碼如下:
initLifecycle(vm) initEvents(vm) callHook(vm, 'beforeCreate') initState(vm) callHook(vm, 'created') initRender(vm)
可以看到,到當執行鉤子函數created時,complier還沒有將template解析成render方法,DOM自然不能獲取
3、即將掛載:對應的鉤子函數是beforemount。在上個階段我們知道DOM還沒生成,相關屬性還是undefined,那麼此階段為即將掛載,將發生什麼呢?
增加一下代碼:
列印一下,我們得到:
結果好像和上一個階段並沒有什麼區別......弄的我好失落....那到底beforeMount這個階段的意義是什麼呢?難道這是vue給我設的一個坑??等等~,先別跳!!讓我們問問源(zu)碼(zong)(︶.̮︶✽)
if (vm.$options.el) { vm.$mount(vm.$options.el) }
在vue2的源碼中在講述mount這一重要步驟時進行了一個關鍵的判斷,就是判斷vue的掛載節點是否存在!!那麼,掛載節點時如何存在了的呢?這一定就是在beforeMount這一階段完成的!讓我們馬上上代碼實驗一下剛纔的斷言。在這裡換一種創建vue的方式。
現在我們知道vue實例和DOM其實是分開的兩個概念,然而,vue實例的必須和DOM相關聯並改變DOM才能完成它的使命。這就需要將Vue掛載到DOM上。因此,vue提供了一個el參數來確定掛載的DOM節點。當我們根據vue構造函數new出一個Vue時,只要設定了這個el元素,那麼這個新的Vue一切操作將只對這個el及其子元素有效。
這時我們將得到列印的結果如下:
可以看到,這裡數據name的值還沒被渲染到DOM中,然而通過id已經能夠取得dom的根節點了,這個節點即掛載節點。
走到這裡,beforeMount的意義已經逐漸清晰了。在這一階段,我們雖然依然得不到具體的DOM元素,但vue掛載的根節點已經創建,下麵vue對DOM的操作將圍繞這個el繼續進行。在這個階段vue成功獲得了DOM王國國王--根元素el的信任,之後vue通過掌控“數據驅動”這台國家機器獲得了對DOM王國的絕對統治權,“挾天子以令諸侯”,vue的所有命令都將在對DOM每個元素的控制中得到精確執行。
beforeMount這個階段是過渡性的,一般一個項目只能用到一兩次,但它卻是意義非凡的!!在它之後vue將執行mount函數,帶有vue屬性的DOM及vue數據將全部被呈現,$refs將可以在DOM中使用並獲取元素。最重要的是,之後我們將迎來輝煌的mounted階段。beforeMount這個過程在Vue2的源碼中是這樣描述的:
可以看出,$mount函數的操作都基於beforeMount階段獲取的根元素el。($mount函數將根據el,template,render屬性調用render方法,即我們平時說的compile過程。compile會把我們寫的vue語言編譯成render(JSX),這一步一般都是構建工具幫我們完成的啦,啦啦啦~)。
根據源碼,_render方法執行完之後,會執行_mount方法,在這個過程中會首先new出形成一個watcher對象,會運行傳入的一個_render方法主要就是運行之前compile的render方法,形成vNode節點,也就是大名鼎鼎的虛擬DOM。拿到vNode後,傳入vm._update()
方法,進行DOM更新。(watcher和下麵將講到的update涉及到虛擬DOM原理,會在以後的文章中結合vue詳細說明,感覺自己挖了一個坑~~)
4、渲染完畢:對應的鉤子函數是mounted。mounted是平時我們使用最多的函數了,一般我們的非同步請求都寫在這裡。在這個階段,數據和DOM都已被渲染出來。
繼續增加代碼:
列印一下:
“千呼萬喚始出來”,我們終於得到了想要的結果!!
不過,這並不是最終的結果。(◐ˍ◑),因為更新即將到來~
5、即將更新渲染:對應的鉤子函數是beforeUpdate。vue遵循數據驅動DOM的原則,當我們修改vue實例的data時,vue會自動幫我們更新視圖。那麼當我們調用beforeMount函數時,會發生什麼呢?
為了說明數據變化對DOM的影響,我首先更新代碼,增加了一個method方法。到目前為止,代碼如下:
<template> <div class="router-page-wrap" style="background: #fc595d;"> this is usercenter <input type="text" v-model="message" ref="input" id="input"> <em ref="em">{{message}}</em> <button v-touch:tap="messageChange">changeMessage</button> </div> </template> <script> var common = require('common'); module.exports = { data : function() { return { message : '我的博客園' } }, methods : { messageChange : function () { this.message = 'emma的博客園' } }, beforeCreate : function () { console.log('this is beforeCreate :', this.message, this.$refs.input); }, created : function () { console.log('this is created :') console.log('message :' , this.message); console.log('DOM element:', this.$refs.input); }, beforeMount : function () { console.log('this is beforeMount :') console.log('message :' , this.message); console.log('DOM element:', this.$refs.input); }, mounted : function () { console.log('this is mounted :') console.log('message :' , this.message); console.log('DOM element:', this.$refs.input); }, beforeUpdate : function () { console.log('=即將更新渲染='); let name = this.$refs.em.innerHTML; console.log('name:'+name); } } </script>
運行之後:
可以看到,beforeUpdate函數在數據更新後並沒立即更新數據,但是DOM中的數據已經改變,這是Vue雙向數據綁定的作用,以後也會講到,哇塞,又一個坑~~
6、更新渲染後:對應的鉤子函數是updated。為了不使看到同-函數在不能階段的效果,我註釋掉beforeUpdate函數,添加update函數並綁定了剛纔的click事件。
我們得到預料中的結果:
現在DOM終於和我們更改過的內容同步了!
7、銷毀之前:對應的鉤子函數是beforeDestroy。到上一步vue已經成功的通過數據驅動DOM更新,當我們不在需要vue操縱DOM時,就需要銷毀Vue,也就是清除vue實例與DOM的關聯,調用destroy方法可以銷毀當前組件。在銷毀前,會觸發beforeDestroy鉤子函數。
8、銷毀之後:對應的鉤子函數是destroyed。在銷毀後,會觸發destroyed鉤子函數。
我們通過調用destroy函數觀察vue實例銷毀前後vue和DOM的變化。增加代碼如下:
調用destroy前,我們改變name,在視圖隨之改變,beforeDestroy獲取的DOM和我們更新後的結果一致。
調用destroy後,我們改變name,視圖不會改變,destroyed也不會獲取到DOM信息了。
銷毀之前,修改name的值,可以成功修改視圖顯示的內容為更新後的內容,調用實例的$destroy( )方法之後,vue實例與DOM的關係解綁,vue數據的任何變化都不會使DOM更新,說明實例成功被銷毀了~~
vue的生命周期的思想貫穿在組件開發的始終,通過熟悉其生命周期調用不同的鉤子函數,我們可以準確地控制數據流和其對DOM的影響;vue生命周期的思想是Vnode和MVVM的生動體現和繼承,以後我將繼續學習相關的知識,在一個更高的高度學習vue,我會在學習中繼續和大家不斷分享學習的點滴感悟,更希望獲得大家對我的文章積極提出問題和建議,我們共同討論,共同進步~~