浅谈vue的生命周期

生命周期

在使用vue的过程中,对vue的生命周期的理解是最基础的开始,也许你不需要全部理解,但对其中最主要的几个阶段却要知之甚深,这是你项目中密切相关的。

周期图

image

这是一张vue官网上面展示的生命周期图,对每一个过程都做了罗列。

周期说明

所有的生命周期钩子函数会自动绑定this上下文到实例中,因此可以访问数据,对属性和方法进行运算。不能使用箭头函数来定义一个生命周期方法
如(created: () => this.fetch()),箭头函数绑定了父上下文,因此this与vue的实例不同,会显示未定义。

  • 1、beforeCreate

在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

在这个阶段可以异步方式获取data数据,使用this.$nextTick或者setTimeout都行,当全执行完之后会执行里面的代码,获取需要的数据。

如果需要同步拿到数据,需要了解数据存放地点,在beforeCreate前,所有的options都会先存到vm.options中,在beforeCreate之后才会附到vm上,然后再触发created钩子,所以要想在这个时候拿到数据,那直接**this.options.data()["data"]**获取。

beforeCreate最好不要改动data里的数据,否则可能会出现无法监听的情况,模板中需要使用data进行渲染时,先给data默认的初始值,created之后再进行更改(如ajax)改成需要的值。

  • 2、created

组件实例创建完成,属性已绑定,但DOM还未生成,挂载阶段还没开始,$el属性还不存在。在这一步主要的工作为:调用数据,调用方法,调用异步函数。

在create函数里面是可以获取到data,调用Vue方法,可以获取到原本Html上直接加载出来的DOM,但是无法获取到通过挂载模板生成的DOM。比如v-for遍历的内容。在这一步通常会初始化某些属性值,比如通过异步函数获取数据更新data,然后再渲染成视图。

  • 3、beforeMount

在挂载开始之前被调用:render 函数或者模板首次被调用。

  • 4、mounted

el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。这个时候DOM会被渲染完成,初始的数据会被渲染完成,在这里才能获取到具体的DOM元素。

  • 5、beforeUpdate

数据更新时调用,发生在虚拟 DOM 重新渲染之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。 当我们更改Vue的任何数据,都会触发该函数。

  • 6、updated

由于数据更改导致的虚拟 DOM 重新渲染,在这之后会调用该钩子。 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。 该钩子在服务器端渲染期间不被调用。

  • 7、beforeDestroy

实例销毁之前调用。在这一步,实例仍然完全可用。

  • 8、destroyed

Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。

var vm = new Vue({
    el: '#vue_det',
    data: {
        info: "This Test"
    },
    beforeCreate: function() {
        console.group('beforeCreate 创建前状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el); //undefined
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data)); //undefined 
        console.log("%c%s", "color:red", "info: " + this.info)
    },
    created: function() {
        console.group('created 创建完毕状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el); //undefined
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data)); //已被初始化 
        console.log("%c%s", "color:red", "info: " + this.info); //已被初始化
    },
    beforeMount: function() {
        console.group('beforeMount 挂载前状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el); //已被初始化
        console.log(this.$el);
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data)); //已被初始化  
        console.log("%c%s", "color:red", "info: " + this.info); //已被初始化  
    },
    mounted: function() {
        console.group('mounted 挂载结束状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el); //已被初始化
        console.log(this.$el);
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data)); //已被初始化
        console.log("%c%s", "color:red", "info: " + this.info); //已被初始化 
    },
    beforeUpdate: function() {
        console.group('beforeUpdate 更新前状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el);
        console.log(this.$el);
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
        console.log("%c%s", "color:red", "info: " + this.info);
    },
    updated: function() {
        console.group('updated 更新完成状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el);
        console.log(this.$el);
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
        console.log("%c%s", "color:red", "info: " + this.info);
    },
    beforeDestroy: function() {
        console.group('beforeDestroy 销毁前状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el);
        console.log(this.$el);
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
        console.log("%c%s", "color:red", "info: " + this.info);
    },
    destroyed: function() {
        console.group('destroyed 销毁完成状态===============》');
        console.log("%c%s", "color:red", "el     : " + this.$el);
        console.log(this.$el);
        console.log("%c%s", "color:red", "data   : " + JSON.stringify(this.$data));
        console.log("%c%s", "color:red", "info: " + this.info)
    }
})

第一次运行,输出结果如下:


image

当在控制输入vm.info = "rewrite"对info进行更新时:

image

在beforeUpdate和updated里面 info的值都变为了'rewrite'

created和mounted区别

通过上面的说明可以理解两者的不同。通常created使用的次数多,而mounted通常是在html渲染完成后去获取或者更新某些DOM元素时使用,在mounted中通常会对插件或者组件进行使用。

var vm = new Vue({
    el: '#vue_det',
    template: "<ul><li id='name'>{{name}}</li><li>{{age}}</li></ul>",
    created: function() {
        this.name = "js"
        this.age = "12"
        var x = document.getElementById("name") //第一个命令台错误,这个时候模板页面还没有渲染完成
        console.log(x.innerHTML); // Cannot read property 'innerHTML' of null
    },
    mounted: function() {
        var x = document.getElementById("name") //第二个命令台输出的结果
        console.log(x.innerHTML); //js
    }
})

在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素,而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点,故输出了结果“js”。

el选项的有无对生命周期过程的影响

系统会判断对象中有没有el选项,有el选项,则继续编译过程,没有el选项,则停止编译,也意味着暂时停止了生命周期,直到vm.$mount(el)

var vm = new Vue({
    el: '#vue_det',
    data: {
        info: "This Test"
    },
    beforeCreate: function() {
        console.log('调用了beforeCreat钩子函数')
    },
    created: function() {
        console.log('调用了created钩子函数')
    },
    beforeMount: function() {
        console.log('调用了beforeMount钩子函数')
    },
    mounted: function() {
        console.log('调用了mounted钩子函数')
    }
})
//控制台输出
/*
*调用了beforeCreat钩子函数
*调用了created钩子函数
*调用了beforeMount钩子函数
*调用了mounted钩子函数
*/

在el选项填写且正确的时候,生命周期将正常进行。

下面我们去掉el选项:

var vm = new Vue({
    data: {
        info: "This Test"
    },
    beforeCreate: function() {
        console.log('调用了beforeCreat钩子函数')
    },
    created: function() {
        console.log('调用了created钩子函数')
    },
    beforeMount: function() {
        console.log('调用了beforeMount钩子函数')
    },
    mounted: function() {
        console.log('调用了mounted钩子函数')
    }
})
//控制台输出
/*
*调用了beforeCreat钩子函数
*调用了created钩子函数
*/

可以看到,生命周期的钩子函数执行到created就结束了。

而当我们不加el选项,但是手动执行vm.$mount(el)方法的话,也能够使暂停的生命周期进行下去,如下所示:

var vm = new Vue({
    data: {
        info: "This Test"
    },
    beforeCreate: function() {
        console.log('调用了beforeCreat钩子函数')
    },
    created: function() {
        console.log('调用了created钩子函数')
    },
    beforeMount: function() {
        console.log('调用了beforeMount钩子函数')
    },
    mounted: function() {
        console.log('调用了mounted钩子函数')
    }
})
vm.$mount('#vue_det');
//控制台输出
/*
*调用了beforeCreat钩子函数
*调用了created钩子函数
*调用了beforeMount钩子函数
*调用了mounted钩子函数
*/

可以看到,这个时候虽然对象中没有el参数,但通过$mount(el)动态添加的方式,也能够使生命周期顺利进行。

template参数选项的有无对生命周期的影响

  • 1、如果Vue实例对象中有template参数选项,则将其作为模板编译成render函数
  • 2、如果没有template参数选项,则将外部的HTML作为模板编译(template),也就是说,template参数选项的优先级要比外部的HTML高
  • 3、如果1,2条件都不具备,则报错
<div id="vue_det">
    {{info}} 这是在outer HTML中的
</div>
<script>
    var vm = new Vue({
        el: '#vue_det',
        template: "<h1>{{info +'这是在template中的'}}</h1>",
        data: {
            info: "This Test"
        },
    })
</script>

执行上面的代码,输入页面显示为 This Test 这是在template中的

render选项参数比template更接近Vue解析器!所以综合排列如下:render函数选项 > template参数 > 外部HTML;Vue的编译实际上是指Vue把模板编译成 render 函数的过程

Vue.nextTick对异步函数的结果进行操作

Vue.nextTick()官方解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

什么时候需要用的Vue.nextTick()

在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,因为在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。

所以解释起来就是:在某个动作有可能改变DOM元素结构的时候,对DOM一系列的js操作都要放进Vue.nextTick()的回调函数中。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容