vue生命周期
生命周期可以分为四个阶段,初始化阶段,模板编译阶段,挂载阶段,已挂载和卸载阶段。
- el :选择器
- $el:对应选择器的template模板(html代码)
- render:也是vue实例中的一项,其参数更接近vue解析器,按优先级,render参数 > vue实例对象中有template > 外部的HTML模板 后面两个也是将其编译成render函数;【注意】render选项参数比template更接近Vue解析器!所以优先级如下:
render函数选项 > template参数 > 外部HTML
- $mount :当Vue实例没有el属性时,则该实例尚没有挂载到某个dom中,假如需要延迟挂载,可以在之后手动调用vm.$mount()方法来挂载。例如:
new Vue({内部无el项}).$mount('#app')是延迟加载,同下面的语句相同
new Vue({el:"#app",***})
- computed计算属性可用于快速计算视图(View)中显示的属性。这些计算将被缓存,并且只在需要时更新.
- watch主要用于监控vue实例的变化,它监控的变量当然必须在data里面声明才可以,它可以监控一个变量,也可以是一个对象,一般用于监控路由、input输入框的值特殊处理等等,它比较适合的场景是一个数据影响多个数据.
- methods每当触发重新渲染时,方法的调用方式将总是再次执行函数。因此,函数必须是一个纯函数。它不能有副作用。输出只能依赖于传递给函数的值。
HTML
<div id="app">
<p>{{ message }}</p>
<h1>{{message + '这是在outer HTML中的'}}</h1>
</div>
js
var app = new Vue({
el: '#app',
data: {
message : "xuxiao is boy"
},
template:"<h1>{{message +'这是在template中的'}}</h1>",
// render: function(createElement) {
// return createElement('h1', 'this is createElement')
// }
})
最终结果
vue整个过程做了些什么
new Vue()创建实例
- vue项目都是通过组件化进行实现的,一个页面通过若干组件组成,构成一个组件树,
- 每一个组件都通过一个vue实例来进行管理,
- 每当要渲染一个组件前,都会为该组件创建一个vue实例来管理组件中的数据和事件方法。
- 通过new Vue()创建实例时,实际创建了一个vue实例的空壳,初始化生命周期和事件
注意:这时候实例中的data选项还没有数据,所以在beforecreated中无法访问到data中的数据
初始化阶段
beforecreate
- 在这个阶段,完成实例初始化,
- 初始化非响应式变量 this指向创建的实例;
- 可以在这加个loading事件;
- data computed watch methods上的方法和数据均不能访问,这个时候的Vue实例还什么都没有,
- $route对象是存在的,可以根据路由信息进行重定向之类的操作;
- 这个阶段的data对象未完成初始化,el也没有完成初始化;
- DOM还没有开始
注意:在beforecreated中千万不要去修改data中赋值的数据,最早也要在create里面去做(添加一些行为)
created 【创建完成】
在模板渲染成HTML前调用,即通常初始化一些属性值,然后再渲染成视图, 未挂载DOM;created时还没完成挂载,无法通过id等获得DOM元素;
- 实例创建完成 完成数据(data props computed)的初始化 导入依赖项。
- 可访问data computed watch methods上的方法和数据 ;
- 不能访问el【初始化还未完成】,ref为空数组 ;不能对元素进行操作;
- 可在这结束loading,
- 还做一些初始化,实现函数自执行,
- 可以对data数据进行操作,可进行一些请求,请求不易过多,避免白屏时间太长。
- el还是undefined,
- 而数据已经与data中的属性进行绑定(放在data中属性当值发生改变的同时,视图也会发生变化),
- 在这里可以在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
-
若在此阶段进行的 DOM 操作一定要放在 Vue.nextTick() 的回调函数中;因为created() 钩子函数执行的时候 DOM 其实并未进行任何渲染
Vue.nextTick( [callback, context] ):在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
模板编译阶段
beforemount & mount
在created后,beforemount前,会检查el选项,没有就会调用vm.$mount(),然后就会继续检查template,没有的话就绑定el选项的html,进入beforemount后,编译模板为虚拟的DOM,,开始render,将虚拟DOM渲染到页面上;
从created到beforeMount的过程中,
var app = new Vue({
el: '#app',
data: {
message : "xuxiao is boy"
},
template:"<h1>{{message +'这是在template中的'}}</h1>",
// render: function(createElement) {
// return createElement('h1', 'this is createElement')
// },
首先会判断vue实例中有没有el选项,如果有的话则进行下面的编译,但是如果没有el选项,则停止生命周期,直到vue实例上调用vm.$mount(el)。
如果有el,再判断是否有template参数,如果有,则把其当作模板编译成render函数,
如果没有,则把外部的html作为模板编译。template中的模板优先级高于outer HTML模板。
这是把outerHTML当作模板编译了
<div id="app">
<p>{{ message }}</p>
<h1>{{message + '这是在outer HTML中的'}}</h1>
</div>
如果把实例中render function选项的注释去掉,则直接用render function里的,得到网页如下
所以按优先级来说 render function>template>outerHTML
————————————————
在vue对象中还有一个render函数,它是以createElement作为参数,然后做渲染操作,而且我们可以直接嵌入JSX.
综合排名优先级:render函数选项 > template选项 > outer HTML.
挂载阶段
beforeMount【挂载之前】
在这个阶段,$el还只是我们在HTML里面写的节点。$el属性已存在,是虚拟dom,只是数据未挂载到模板中;有了el,编译了template|/outerHTML 能找到对应的template,并编译成render函数
- 载入前(完成了data和el数据初始化),
- 但是页面中的内容还是vue中的占位符,
- $el属性已存在,是虚拟dom,只是数据未挂载到模板中。
- data中的message信息没有被挂在到Bom节点中,
- 在这里可以在渲染前最后一次更改数据的机会,不会触发其他的钩子函数,一般可以在这里做初始数据的获取
mounted 【成功挂载】
完成创建vm,
完成el的双向绑定,完成挂载dom和渲染,可以在这个阶段对挂载的DOM进行操作;
ref 可在这发起后端请求,拿回数据,配合路由钩子做一些事情; 可对DOM 进行操作
载入后html已经渲染(ajax请求可以放在这个函数中),把vue实例中的data里的message挂载到BOM节点中去
beforeUpdate
更新前状态(view层的数据变化前,不是data中的数据改变前),重新渲染之前触发,然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染
- 只有view上面的数据变化才会触发beforeUpdate和updated,仅属于data中的数据改变是并不能触发;
- 数据更新之前 可在更新前访问现有的DOM,如手动移除添加的事件监听器;
- beforeUpdate是指view层的数据变化前【具体的可以用this.$el.innerHTML可以看出没有改变】,不是data中的数据改变前触发。因为Vue是数据驱动的
updated
数据已经更改完成,dom也重新render完成【具体的可以用this.$el.innerHTML可以看出已经改变了】
完成虚拟DOM的重新渲染和打补丁; 组件DOM 已完成更新; 可执行依赖的dom 操作 注意:不要在此函数中操作数据,会陷入死循环的。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher取而代之。
view层的数据更新后,data中的数据同beforeUpdate,都是更新完以后的。注意:会发现
beforeUpdate
和updated
钩子函数中的$el
一样,根据官方理解beforeUpdate
应该指向虚拟dom,所以才会一样,而dom中的真正内容不一样,但是beforeMount
和mouted
钩子函数中为什么又会有区别呢?感觉是设计的不足之处。**_
activated
- 在使用vue-router时有时需要使用来缓存组件状态,这个时候created钩子就不会被重复调用了,
- 如果我们的子组件需要在每次加载的时候进行某些操作,可以使用activated钩子触发
deactivated
keep-alive 组件被移除时使用
beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。
说明:
beforeDestroy(){
console.group('beforeDestroy 销毁前状态==========>>');
console.log("%c%s", "color:red", "el : "+this.$el); //[object HTMLDivElement]
console.log(this.$el);
console.log("%c%s", "color:red", "data : "+this.$data); //[object Object]
console.log("%c%s", "color:red", "message: "+this.message); //今天周二了!!!
},
destroyed
官方解释:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
说明:执行destroy方法后,对data的改变不会再触发周期函数,此时的vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
destroyed(){
console.group('destroyed 销毁完成状态==========>>');
console.log("%c%s", "color:red", "el : "+this.$el); //[object HTMLDivElement]
console.log(this.$el);
console.log("%c%s", "color:red", "data : "+this.$data); //[object Object]
console.log("%c%s", "color:red", "message: "+this.message); //今天周二了!!!
},
具体使用
- beforeCreate:这个时候data,watcher,methods统统没有,这个时候的Vue实例还什么都没有,但是$route对象是存在的,可以根据路由信息进行重定向之类的操作。
mouted和created
父子组件嵌套时触发钩子函数顺序
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
总结
- beforecreate:可以在这加个loading事件
- created :在这结束loading,还做一些初始化,实现函数自执行
- mounted : 在这发起后端请求,拿回数据,配合路由钩子做一些事情
- beforeDestory: 你确认删除vue实例了吗?
- destoryed :当前实例已被销毁,解绑相关指令和事件监听器
参考文章:
https://blog.csdn.net/wasbb_mm/article/details/83861456
https://juejin.im/post/5c9f4fe36fb9a05e3a343a2c#heading-16
https://www.jianshu.com/p/7026b73bb626
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。