vue全部功能的實現都是圍繞其生命週期進行的,在生命週期的不一樣階段調用對應的鉤子函數能夠實現組件數據管理和DOM渲染兩大重要功能。學習實例的生命週期,能幫助咱們理解vue實例的運行機制,更好地利用鉤子函數完成咱們的業務代碼。vue
一、即將建立:對應的鉤子函數爲beforeCreate。此階段爲實例初始化以後,此時的數據觀察和事件機制都未造成。dom
<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節點。函數
二、建立完畢:對應的鉤子函數爲created。在這個階段vue實例已經建立,咱們在一樣打印一下data和DOM元素。工具
上面代碼的基礎上咱們添加created鉤子函數:學習
獲得的結果是什麼呢??ui
此時,咱們可以讀取到數據data的值,可是DOM尚未生成,因此和DOM相關的屬性還不存在,天然也就不能獲取DOM元素。在Vue2的源碼中也是這樣描述的:this
首先,運行new Vue()
的時候,會進入代碼src/core/instance/index.js
的Vue構造方法中,並執行this._init()
方法。在_init
中,會對各個功能進行初始化,並執行beforeCreate
和created
兩個生命週期方法。核心代碼以下:spa
initLifecycle(vm) initEvents(vm) callHook(vm, 'beforeCreate') initState(vm) callHook(vm, 'created') initRender(vm)
能夠看到,到當執行鉤子函數created時,complier尚未將template解析成render方法,DOM天然不能獲取3d
三、即將掛載:對應的鉤子函數是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詳細說明,感受本身挖了一個坑~~)
四、渲染完畢:對應的鉤子函數是mounted。mounted是平時咱們使用最多的函數了,通常咱們的異步請求都寫在這裏。在這個階段,數據和DOM都已被渲染出來。
繼續增長代碼:
打印一下:
「千呼萬喚始出來」,咱們終於獲得了想要的結果!!
不過,這並非最終的結果。(◐ˍ◑),由於更新即將到來~
五、即將更新渲染:對應的鉤子函數是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雙向數據綁定的做用,之後也會講到,哇塞,又一個坑~~
六、更新渲染後:對應的鉤子函數是updated。爲了避免使看到同-函數在不能階段的效果,我註釋掉beforeUpdate函數,添加update函數並綁定了剛纔的click事件。
咱們獲得預料中的結果:
如今DOM終於和咱們更改過的內容同步了!
七、銷燬以前:對應的鉤子函數是beforeDestroy。到上一步vue已經成功的經過數據驅動DOM更新,當咱們不在須要vue操縱DOM時,就須要銷燬Vue,也就是清除vue實例與DOM的關聯,調用destroy方法能夠銷燬當前組件。在銷燬前,會觸發beforeDestroy鉤子函數。
八、銷燬以後:對應的鉤子函數是destroyed。在銷燬後,會觸發destroyed鉤子函數。
咱們經過調用destroy函數觀察vue實例銷燬先後vue和DOM的變化。增長代碼以下:
調用destroy前,咱們改變name,在視圖隨之改變,beforeDestroy獲取的DOM和咱們更新後的結果一致。
調用destroy後,咱們改變name,視圖不會改變,destroyed也不會獲取到DOM信息了。
銷燬以前,修改name的值,能夠成功修改視圖顯示的內容爲更新後的內容,調用實例的$destroy( )方法以後,vue實例與DOM的關係解綁,vue數據的任何變化都不會使DOM更新,說明實例成功被銷燬了~~