例子是在 jsrun.net 平臺編寫,不支持移動端平臺,因此本文建議在 PC 端進行閱讀。
所謂生命週期鉤子函數(簡稱生命週期函數),指的是組件的建立、更新、銷燬三個階段所觸發執行的函數。根據每一個階段觸發的鉤子函數,咱們能夠相應的作一些操做,如獲取後端接口數據、監聽事件、執行事件、執行定時器、移除事件、清理定時器等等。html
生命週期根據上面的三個階段,本人總結爲:vue
組件建立git
組件更新github
組件銷燬面試
後續會根據這三大週期,分別說明生命週期函數。segmentfault
爲了方便理解,本人在 http://jsrun.net 上編寫了例子。(一個跟 jsfiddle 差很少的網站,國內 jsfiddle 被牆了)後端
首先看下官網的生命週期示意圖,這裏的生命週期函數都是針對瀏覽器端的,服務端目前只支持 beforeCreate
和 created
這兩個生命週期函數。api
其中官網並無把 render
、renderError
函數概括爲生命週期鉤子函數。瀏覽器
其中 Has "el" option
對好比下:app
有 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
中間會觸發 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(){} } })
在實例初始化以後,數據觀測 (data observer) 和 event/watcher 事件配置以前被調用。
在實例建立完成後被當即調用。在這一步,實例已完成如下的配置:數據觀測 (data observer),屬性和方法的運算,watch/event 事件回調。然而,掛載階段還沒開始,$el
屬性目前不可見。
在掛載開始以前被調用:相關的 render
函數首次被調用。$el
屬性已經可見,但仍是原來的 DOM,並不是是新建立的。
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 }) }
存在期會涉及到如下生命週期函數:
Vue 須要改變數據纔會觸發組件從新渲染,纔會觸發上面的存在期鉤子函數。其中 beforeUpdate
和 updated
中間會觸發 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++; } } })
數據更新時,虛擬 DOM 變化以前調用,這裏適合在更新以前訪問現有的 DOM,好比手動移除已添加的事件監聽器。
請不要在此函數中更改狀態,不然會觸發死循環。
數據更新和虛擬 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 }) }
請不要在此函數中更改狀態,不然會觸發死循環。
銷燬期會涉及到如下生命週期函數:
例子請看 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() } } })
實例銷燬以前調用,在這一步,實例仍然徹底可用。通常在這裏移除事件監聽器、定時器等,避免內存泄漏
Vue 實例銷燬後調用。調用後,Vue 實例指示的全部東西都會解綁定,全部的事件監聽器會被移除,全部的子實例也會被銷燬。
因此若是須要用到 Vue 實例指示的所用綁定的東西,須要在 beforeDestroy 中使用。這麼說,destroyed 函數能作的事,在 beforeDestroy 也能作,因此不必在 destroyed 函數中處理。
當組件激活的時候調用,能夠參考構建組件 - keep-alive
當組件停用的時候調用,能夠參考構建組件 - keep-alive
這個生命鉤子詳細請看官網,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 } })
既然每一個組件都有生命週期,那麼父子、兄弟組件之間執行順序有什麼區別呢?
那麼你首先須要瞭解到 template
何時轉變成虛擬 DOM,而虛擬 DOM 又是在何時構建完成,而後又是何時掛載到真實的 DOM 上面。
這裏不深刻說明 vue 原理,大概瞭解下 template 到虛擬 DOM 的過程:
template
-> AST 樹
-> render 函數
實際上咱們關注點在 render 函數,能夠了解下運行時-編譯器-vs-只包含運行時。虛擬 DOM 就是在 render 函數中造成。若是你使用過 JSX,那麼能夠簡單的把 JSX 理解爲一個個的虛擬 DOM。固然 render 函數也會執行掛載到真實 DOM 的邏輯。
虛擬 DOM 就是一個樹,vue 會把全部組件映射爲虛擬 DOM 後渲染到真實 DOM 節點中,掛載不管如何必定是在最後執行的,因此全部組件 mounted 函數輸出的日誌必然是連在一塊兒的,updated 也同理。
若是你不理解這句話,能夠看這個例子 http://jsrun.net/7hyKp/edit。
render 函數纔是父子組件生命週期函數的分割線。
而 render 函數的順序以下。
實例化期:beforeMount
-> render
-> mounted
存在期:beforeUpdate
-> render
-> updated
詳細請看在線例子,http://jsrun.net/vhyKp/edit。
詳細請看在線例子,http://jsrun.net/NhyKp/edit。
學習和總結文章同步發佈於 https://github.com/xianshanna...,有興趣能夠關注一下,一塊兒學習和進步。