Vue生命周期小結(jié)

Vue框架已日臻成熟行瑞,生命周期也算是老生常談了。網(wǎng)路上也有很多對Vue生命周期的講解餐禁。
此處是補充上自己的理解血久,再次總結(jié)一下。

一帮非、什么是生命周期(LifeCycle)氧吐?

生命周期在計算機語言里绷旗,生命周期一般是指一個對象的創(chuàng)建(生)到銷毀(死)的階段。

二副砍、Vue的生命周期

2.1 生命周期圖解

對于Vue的生命周期衔肢,就是其組件的生命周期。具體可以看下圖(相對官方文檔的圖豁翎,已補充翻譯):

vue-lifecycle(該翻譯圖忘記來源角骤,有知道繪制作者提供下).png

從圖中可以直觀注意到,Vue的生命周期可以劃分為四個階段:

  • create 階段: vue實例被創(chuàng)建心剥;
  • mount 階段: vue實例被掛載到真是的DOM節(jié)點邦尊;
  • update 階段:當vue實例里面的data數(shù)據(jù)變化時,觸發(fā)組件的重新渲染优烧;
  • destroy 階段:vue實例被銷毀蝉揍。

2.2 階段分解

上面的四個階段,每個階段分為開始前和開始后畦娄,這樣就衍生出了9個方法:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed
    ——也就是所謂的鉤子函數(shù)又沾。

接下來我們一個階段一個階段來分析它們的觸發(fā)條件及表征。

2.2.0 實驗代碼準備

(1)一個最簡單的Vue實例化代碼如下:

<div id="app">
    <p>{{ message }}</p>
</div>
var vm = new Vue({
        el: '#app',
        data: {message: 'Hello Vue~'},
});

我們主要關(guān)心著這個實例化里的屬性:元素el什么時候有值(掛載上了熙卡?)杖刷、data什么時候有數(shù)據(jù)?驳癌、message又是什么時候有數(shù)據(jù)呢滑燃?
于是,我們在每個回調(diào)里面去打印下這三個值看看颓鲜。

(2)為了更直觀去理解Vue實例的變化表窘,我們引用了一段實驗代碼(該源碼來源于參考文章1):

<div id="app">
    <p>{{ message }}</p>
</div>

<script type="text/javascript">
    var vm = new Vue({
        el: '#app',
        data: {message: 'Hello Vue~'},
        beforeCreate: function(){
            console.group('beforeCreate 創(chuàng)建前 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 1 => undefined
            console.log('%c%s','color:red','data    :',this.$data) // 1 => undefined
            console.log('%c%s','color:red','message :',this.message) // 1 => undefined

        },
        created: function(){
            console.group('created 創(chuàng)建完畢 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 1 => undefined
            console.log('%c%s','color:red','data    :',this.$data) // 1 => {message: 'Hello Vue~'}
            console.log('%c%s','color:red','message :',this.message) // 1 => 'Hello Vue~'

        },
        beforeMount: function(){
            console.group('beforeMount 掛載前 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 1 => <div id="app"><p>{{ message }}</p></div>
            console.log('%c%s','color:red','data    :',this.$data) // 1 => {message: 'Hello Vue~'}
            console.log('%c%s','color:red','message :',this.message) // 1 => 'Hello Vue~'

        },
        mounted: function(){
            console.group('mounted 掛載結(jié)束 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 1 => <div id="app"><p>'Hello Vue~'</p></div>
            console.log('%c%s','color:red','data    :',this.$data) // 1 => {message: 'Hello Vue~'}
            console.log('%c%s','color:red','message :',this.message) // 1 => 'Hello Vue~'

        },
        beforeUpdate: function(){
            console.group('beforeUpdate 更新前 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 2 => <div id="app"><p>'Celine~'</p></div>
            console.log('%c%s','color:red','data    :',this.$data) // 2 => {message: 'Celine~'}
            console.log('%c%s','color:red','message :',this.message) // 2 => 'Celine~'

        },
        updated: function() {
            console.group('updated 更新完成 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 2 => <div id="app"><p>'Celine~'</p></div>
            console.log('%c%s','color:red','data    :',this.$data) // 2 => {message: 'Celine~'}
            console.log('%c%s','color:red','message :',this.message) // 2 => 'Celine~
            
        },
        beforeDestroy: function(){
            console.group('beforeDestroy 銷毀前 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 3 => <div id="app"><p>'Celine~'</p></div>
            console.log('%c%s','color:red','data    :',this.$data) // 3 => {message: 'Celine~'}
            console.log('%c%s','color:red','message :',this.message) // 3 => 'Celine~

        },
        destroyed: function(){
            console.group('destroyed 銷毀完成 =========')
            console.log('%c%s','color:red','el      :',this.$el) // 3 => <div id="app"><p>'Celine~'</p></div>
            console.log('%c%s','color:red','data    :',this.$data) // 3 => {message: 'Celine~'}
            console.log('%c%s','color:red','message :',this.message) // 3 => 'Celine~

        }
    })

    // 1 頁面直接刷新進來,執(zhí)行的鉤子有 beforeCreate / created / beforeMount / mounted 四個甜滨。
    // 2 在控制臺執(zhí)行  vm.message = "Celine~" 后乐严,執(zhí)行的鉤子有 beforeUpdate / updated 兩個。
    //   更新前和更新后的艳吠,打印數(shù)據(jù)均是新數(shù)據(jù) 麦备?? 這點和想象的不太一致昭娩。
    // 3 在控制臺執(zhí)行  vm.$destroy() 后,執(zhí)行的鉤子有 beforeDestroy / destroyed 兩個黍匾。
    //   銷毀前和銷毀后栏渺,數(shù)據(jù)依舊存在?锐涯? 這點也和想象的不太一致磕诊。不過這個時候再去改變message值,vue不會再響應(yīng)(也不會去執(zhí)行beforeUpdate / updated 鉤子)
    // 4  在控制臺執(zhí)行  vm.message = "Bye~" ,沒有任何鉤子有響應(yīng)霎终。

</script>

返回結(jié)果:


由此可見滞磺,當代碼運行時,會一次調(diào)用Vue 的 beforeCreate莱褒、created击困、beforeMount、mounted 四個方法广凸,直至完成組件的掛載阅茶。
update階段,要在數(shù)據(jù)發(fā)生改變時(比如更新message字段 vm.message = 'Hahahaha~')才出發(fā)谅海;destroy階段脸哀,要在調(diào)用vm.$destroy()后才觸發(fā)。

下面我們來分別分析每個階段:

2.2.1 create階段

從生命周期圖可以看出扭吁,在這個階段主要做兩件事:

  • 監(jiān)控Data數(shù)據(jù)
  • 初始化內(nèi)部事件

從控制臺打印數(shù)據(jù)撞蜂,可以看出:在beforeCreate時,因為啥動作都還沒有開始侥袜,所以vm.$el,vm.$data,vm.message都是undefined谅摄。
而在created時,因為已開始監(jiān)控Data數(shù)據(jù)系馆,所以data,message都有值送漠。至于初始化內(nèi)部事件是什么,我們此處暫不表由蘑,后續(xù)補充闽寡。

2.2.2 mount階段

在這個階段,做的事情就比較多了尼酿。

掛載前:

  1. 先判斷是否有el選項(我們的上方示例代碼中有該選項爷狈,即el: '#app',);
  2. 如果有el選項裳擎,接著判斷是否有template選項(此處我們的示例代碼中并沒有該選項)涎永;
  3. 如果有template選項,編譯該模板并導(dǎo)入到渲染函數(shù)鹿响;如果沒有template選項羡微,就把實例模板el指向的DOM的outerHTML(上方示例代碼即為<div id="app"><p>{{ message }}</p></div>)作為模板.

掛載后:

  1. 創(chuàng)建vm.$el,并用上面編譯好的模板(html內(nèi)容)替換el指向的DOM惶我。

從控制臺打印數(shù)據(jù)就可以看出:
掛載前妈倔,vm.$el<div id="app"><p>{{ message }}</p></div>,看起來似乎有值了绸贡,但又不太對勁——因為message的值沒有代入進入盯蝴。其實此處有點像虛擬DOM的效果:也就是我的vm.$el雖然不是完整的毅哗,但也先準備著。(所以捧挺,在其他版本的瀏覽器中虑绵,此處也可能是打印出undefined。)
掛載結(jié)束就不一樣了闽烙,三個數(shù)據(jù)項都就緒了翅睛。vm.$el也成為了完美的<div id="app"><p>Hello Vue~</p></div>

以上的結(jié)果是按照有el選項鸣峭,沒有template選項的情況執(zhí)行的宏所。接下來我們看看其他種情況下,會發(fā)生點什么摊溶?

(1)沒有el選項
操作:注釋掉 el: "#app"
結(jié)果:只進行了create階段爬骤,沒有進行mount階段。
原因分析:這個好理解莫换,因為沒有了el選項霞玄,就無從掛載起了。生命周期也就結(jié)束了拉岁。
如果此時想進行掛載坷剧,可以手動去調(diào)用vm.$mount(el)

(2)手動掛載
操作:new Vue({...})后面執(zhí)行 vm.$mount('#app')
結(jié)果:進行了create階段后喊暖,也進行了mount階段惫企。

(3)有template選項時
之前的實例代碼是沒有template選項的情況表現(xiàn),此處我們看下若有template選項陵叽,會發(fā)生點什么呢狞尔?

    var vm = new Vue({
        el: '#app',
        data: {message: 'Hello Vue~'},
        template: "<h1>我是模板標題1</h1>", 
        }

操作:new Vue({...})里新增 template選項,如上巩掺。
結(jié)果: vm.$el變成了template選項的內(nèi)容偏序;DOM節(jié)點#app也替換成template選項的內(nèi)容了。其實掛載后胖替,vm.$el是什么研儒,DOM節(jié)點#app也對應(yīng)是什么,它們是等價的独令。
分析: 此處驗證了前面生命周期圖:若有template選項端朵,就編譯它并導(dǎo)入到渲染函數(shù);若沒有template選項记焊,就取#app的outerHTML作為模板逸月。

(4)render()方法
Vue實例里還有render()方法可以提供模板,我們看下如果存在render()方法遍膜,會發(fā)生什么碗硬?

    var vm = new Vue({
        el: '#app',
        data: {message: 'Hello Vue~'},
        template: "<h1>我是模板標題1</h1>", 
                render(h){
            return h('h2','這是render出來的標題2');
        },
        }

操作:new Vue({...})里新增 render(h)方法,如上瓢颅。
結(jié)果: vm.$el變成了render返回的模板內(nèi)容恩尾。
分析: 也就是說,渲染模板的優(yōu)先級可以小結(jié)為:render()方法 > template選項 > el屬性的outerHTML挽懦。

以上就是掛載階段的一序列可能性變化翰意。接下來我們看下更新階段。

2.2.3 update階段


更新階段的前提是:“when data changes”也就是說當data選項里的數(shù)據(jù)有變化時觸發(fā)信柿。
讓數(shù)據(jù)改變有很多操作方式冀偶,此處我們簡單的在控制臺對message字段進行改寫。

// 在控制臺輸入:
vm.message = "Now update!!" //直接回車


當進行了數(shù)據(jù)更新渔嚷,就會觸發(fā)beforeUpdate方法和 updated方法进鸠。
此處在控制臺打印出來的數(shù)據(jù)和預(yù)想的有出入:beforeUpdate本應(yīng)該輸出的舊數(shù)據(jù)(message: Hello Vue~),但此處更新前后的數(shù)據(jù)卻顯示一樣形病。

在參考文章2里面客年,說這是打印出來的是虛擬DOM,都已更新漠吻,但真實DOM還沒有改變量瓜。但我個人覺得不一定是這樣。我嘗試過在各個鉤子函數(shù)補充打印出DOM元素(如下代碼)途乃,但結(jié)果更新前后也都是更新后的數(shù)據(jù)绍傲。

console.log('%c%s','color:red','#app DOM :',document.getElementById("app"))

我個人更傾向于是因為控制臺本身原因。在beforeUpdate時可能確實是舊數(shù)據(jù)耍共,只不過往下執(zhí)行updated時候烫饼,更新新數(shù)據(jù)時,也改寫了beforeUpdate部分的數(shù)據(jù)划提。(待進一步探討研究補充枫弟。

2.2.4 destroy階段

image.png

銷毀階段,需要執(zhí)行vm.$destroy()才會進入鹏往。
同樣的淡诗,我們在控制臺執(zhí)行銷毀方法,得到如下結(jié)果:

可以看出伊履,銷毀前后的數(shù)據(jù)是一樣的韩容,但實際上,銷毀后Vue實例會接觸所有綁定唐瀑,所有事件被移除群凶,子組件被銷毀。比如我們此處更新 data 數(shù)據(jù)項 vm.message哄辣,可以發(fā)現(xiàn)请梢,不會在觸發(fā)update階段了赠尾。

2.3 生命周期小結(jié)

我們對上面的分析結(jié)果做個小結(jié),此處的表格會多考慮兩個方法(當有<keep-alive></keep-alive>組件時毅弧,生命周期會多出現(xiàn)一個activate階段)气嫁。

方法名 特征 屬性變化
beforeCreate 組件實例創(chuàng)建前(或者說剛被創(chuàng)建),啥也沒有够坐。 $el寸宵、data 的值都為undefined。
created 組件實例創(chuàng)建完成元咙。屬性已綁定梯影,但DOM還未產(chǎn)生。 data有值了庶香,$el屬性還是undefined甲棍。
beforeMount 模板編譯/掛載前。 $el是虛擬DOM脉课。
mounted 模板編譯/掛載后救军。 “虛擬”的dom節(jié)點被真實的dom節(jié)點替換,并將其插入到dom樹中倘零。此時可以獲取到$el為真實的dom元素唱遭。
beforeUpdate 組件更新之前。 $el呈驶、data 的值都為新數(shù)據(jù)拷泽。
updated 組件更新之后。 $el袖瞻、data 的值都為新數(shù)據(jù)司致。
activated for kepp-alive,組件被激活時調(diào)用聋迎。 (待補充)
deactivated for kepp-alive脂矫,組件被移除時調(diào)用。 (待補充)
beforeDestroy 組件銷毀前嗲用霉晕。此時實例仍可用庭再。 $el、data 都有值牺堰。實例綁定的事件還存在拄轻。
destroyed 組件銷毀后調(diào)用。 $el伟葫、data 雖然都有值恨搓。但實例綁定的事件和子組件都沒有了。

3 了解生命周期的作用

我們?nèi)リP(guān)注聲明周期,是為了能更好的判斷在不同的生命周期鉤子函數(shù)里面做些什么操作和處理斧抱。比如:

  • beforeCreate - 加入loading事件
  • created - 結(jié)束loading
  • beforemount - 發(fā)起服務(wù)端請求常拓,取數(shù)據(jù)
  • mounted - 根據(jù)請求數(shù)據(jù),對頁面DOM做些什么操作
    ……

具體每個階段的做些什么夺姑,還是要根據(jù)實際場景來設(shè)定咯~

-----------------------HAPPY END--------------------------------


參考文獻:

  1. segmentFaul: Vue2.0 探索之路——生命周期和鉤子函數(shù)的一些理解 本文的主實例代碼來源
  2. 簡書:05墩邀、手把手教Vue--生命周期 本文掛載階段的分類案例來源
  3. cnblogs:Vue生命周期 聲明周期分析方法最初來源
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掌猛,一起剝皮案震驚了整個濱河市盏浙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荔茬,老刑警劉巖废膘,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異慕蔚,居然都是意外死亡丐黄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門孔飒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灌闺,“玉大人,你說我怎么就攤上這事坏瞄」鸲裕” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵鸠匀,是天一觀的道長蕉斜。 經(jīng)常有香客問我,道長缀棍,這世上最難降的妖魔是什么宅此? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮爬范,結(jié)果婚禮上父腕,老公的妹妹穿的比我還像新娘。我一直安慰自己青瀑,他們只是感情好受扳,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凉夯,像睡著了一般裁僧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蘸炸,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天躬络,我揣著相機與錄音,去河邊找鬼搭儒。 笑死穷当,一個胖子當著我的面吹牛提茁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馁菜,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼茴扁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了汪疮?” 一聲冷哼從身側(cè)響起峭火,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎智嚷,沒想到半個月后卖丸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡盏道,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年稍浆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猜嘱。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡衅枫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出朗伶,到底是詐尸還是另有隱情弦撩,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布腕让,位于F島的核電站孤钦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏纯丸。R本人自食惡果不足惜偏形,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望觉鼻。 院中可真熱鬧俊扭,春花似錦、人聲如沸坠陈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仇矾。三九已至庸蔼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贮匕,已是汗流浹背姐仅。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掏膏。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓劳翰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親馒疹。 傳聞我的和親對象是個殘疾皇子佳簸,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

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

  • Vue 實例 屬性和方法 每個 Vue 實例都會代理其 data 對象里所有的屬性:var data = { a:...
    云之外閱讀 2,204評論 0 6
  • 生命周期 在使用vue的過程中,對vue的生命周期的理解是最基礎(chǔ)的開始颖变,也許你不需要全部理解生均,但對其中最主要的幾個...
    刀刀小技閱讀 1,228評論 1 2
  • 業(yè)務(wù)邏輯處理時機 created:異步的數(shù)據(jù)獲取、初始化 mounted:掛載元素內(nèi)dom節(jié)點的獲取悼做,初始數(shù)據(jù)的渲...
    8d2855a6c5d0閱讀 280評論 0 0
  • 蠶 陳倉實驗小學(xué) 王成圓 前幾天我的好朋友送了我許多幼蠶疯特,蠶在...
    海底浪花閱讀 841評論 0 7
  • 寫了這么一段代碼:html: js: 功能很簡單,就是點擊div .add-div時肛走,觸發(fā)input #fileI...
    罌粟1995閱讀 26,270評論 1 1