Vue生命周期
生命周期示意图
Vue3
组件创建阶段
-
new vue
new一个vue的实例对象;此时会进入组件的创建过程(该组件在代码中被注册并使用时,就代表着其被new了一个新的实例对象)。
-
Init Events & Lifecycle
初始化组件的事件和生命周期函数;当执行完这一步之后,组件的生命周期函数就已经全部初始化好了,等待着依次去调用。
-
beforeCreate (服务端渲染可用)
(初始化一个空的 Vue 实例),组件的props,data和methods以及页面DOM结构,都还没有初始化
在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。
-
Init injections & reactivity
这个阶段中,正在初始化props,computed, data和methods中的数据以及方法。
在creted之前就会行初始化 computed 和 watch
-
created(服务端渲染可用)
Vue 实例初始化完成,
props data methods
都已初始化完成,可调用。但尚未开始渲染模板。在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且
$el
property 目前尚不可用。 -
判断是否有el,template,然后进行编译
正在解析模板结构,把data上的数据拿到,并且解析执行模板结构汇总的指令;当所有指令被解析完毕,那么模板页面就被渲染到内存中了;当模板编译完成,我们的模板页面,还没有挂载到页面上,只是存在于内存中,用户看不到页面;
优先级顺序:el < template < render
el对应的HTML元素是写在网页上的。
//1、el,template,render(渲染函数)都是vue对象对应的HTML元素(DOM对象)
//2、优先级顺序:el < template < render
//3、el对应的HTML元素是写在网页上的。
HTML代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<span>我今年{{age}}岁了</span>
</div>
</body>
</html>
<script type="text/javascript" src="js/vue.min.js" ></script>
//1、只有el
let vm = new Vue({
el:"#app",
data:{
age:12
}
});
2、只有template,是不行的,因为,vue对象不知道把template放在何处;
3、只有render(渲染)函数,也是不行的,因为,vue对象不知道把render后的结果放在何处;
4、既有el又有template,就会用template里的内容替换el的outHTML。
let vm = new Vue({
el:"#app",
template:"<div><p>我template出来的,年龄{{age}}</p></div>",
data:{
age:12
}
});
查看elements:
发现 id为app的div没有了
5、既有el又有template,又有render函数。就会使用render函数的内容,因为它的优先级高。
// 但是此时,“Mustache”语法 (双大括号)没法使用,
// 因为,vue只是把render函数的返回值放在HTML里,而不进行再次的绑定
// render函数就是让你发挥 JavaScript 最大的编程能力。
let vm = new Vue({
el:"#app",
template:"<div><p>我template出来的,年龄{{age}}</p></div>",
data:{
age:12
},
render:function(createElement){
// return createElement('h1', '我是render出来的HTML,年龄{{age}}');//不能使用“Mustache”语法 (双大括号)
return createElement('h1', '我是render出来的HTML,年龄'+this.age);
}
});
查看elements:
发现 id为app的div没有了,template属性的值也没有起作用,只显示了render函数的返回值。
-
beforeMount
编译模板,调用
render
函数生成 vdom ,但还没有开始渲染 DOM -
创建并替换
这一步把正在内存中渲染好的模板结构替换掉el属性指定的DOM元素
-
mounted
渲染 DOM 完成,页面更新。组件创建完成,开始进入运行阶段。
注意
mounted
不会保证所有的子组件也都被挂载完成。如果你希望等到整个视图都渲染完毕再执行某些操作,可以在mounted
内部使用 vm.$nextTick:
组件运行阶段
-
beforeUpdate
data中的数据,已经是最新的数据了。但是,页面上渲染的数据,还是之前的旧数据
在数据发生改变后,DOM 被更新之前被调用。这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。
-
virtual DOM re-render and patch
正在根据最新的data数据,重新渲染内存中的的DOM结构;并把渲染好的模板结构替换到页面上
-
updated
在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。
注意,尽量不要在
updated
中继续修改数据,否则可能会触发死循环。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。注意,
updated
不会保证所有的子组件也都被重新渲染完毕。如果你希望等到整个视图都渲染完毕,可以在updated
里使用 vm.$nextTick: -
activated/onActivated
被
keep-alive
缓存的组件激活时调用。 -
deactived/onDeactivated
被
keep-alive
缓存的组件停用时调用。
组件销毁阶段
-
beforeDestory/beforeUnmount
组件进入销毁阶段。组件即将被销毁,但是还没有真正开始销毁,此时组件还是正常可用的;data、methods等数据或方法,依旧可以被正常访问。
卸载组件实例后调用,在这个阶段,实例仍然是完全正常的。
移除、解绑一些全局事件、自定义事件,可以在此时操作。
-
销毁过程
销毁组件的数据侦听器,子组件,事件监听
-
destoryed/unmounted
卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
2.5.0+新增errorCaptured
-
类型:
(err: Error, vm: Component, info: string) => ?boolean
-
详细:
在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回
false
以阻止该错误继续向上传播。
当捕获一个来自子孙组件的错误时被调用
如何正确的操作 DOM
mounted
和 updated
都不会保证所有子组件都挂载完成,如果想等待所有视图都渲染完成,需要使用 $nextTick
mounted() {
this.$nextTick(function () {
// 仅在整个视图都被渲染之后才会运行的代码
})
}
ajax 放在哪个生命周期合适?
一般有两个选择:created
和 mounted
,建议选择后者 mounted
。
执行速度
- 从理论上来说,放在
created
确实会快一些 - 但 ajax 是网络请求,其时间是主要的影响因素。从
created
到mounted
是 JS 执行,速度非常快。 - 所以,两者在执行速度上不会有肉眼可见的差距
代码的阅读和理解
- 放在
created
却会带来一些沟通和理解成本,从代码的执行上来看,它会一边执行组件渲染,一边触发网络请求,并行 - 放在
mounted
就是等待 DOM 渲染完成再执行网络请求,串行,好理解
所以,综合来看,更建议选择 mounted
。
Composition API 生命周期有何不同
setup
代替了beforeCreate
和created
- 生命周期换成了函数的形式,如
mounted
->onMounted
参考 https://v3.cn.vuejs.org/api/composition-api.html#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90
import { onUpdated, onMounted } from 'vue'
export default {
setup() {
onMounted(() => {
console.log('mounted')
})
onUpdated(() => {
console.log('updated')
})
}
}
computed 初始化在什么时候?
props,methods,data和computed,watch的初始化都是在beforeCreated和created之间完成的
watch,immediate为true触发时机
immediate如果为true, 也是发生在beforeCreate 之后,created之前
父子组件的生命周期顺序
加载渲染过程 :
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程:
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程:
父beforeUpdate->父updated
销毁过程:
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
父子组件及mixin的生命周期执行顺序:
mixin的beforeCreate > 父beforeCreate > mixin的created > 父created > mixin的beforeMount > 父beforeMount > 子beforeCreate > 子created > 子beforeMount > 子mounted > mixin的mounted >父mounted
<template>
<div>
<div ref="div">父组件</div>
<input type="text" v-model="msg">
<div>{{newMsg}}</div>
<hr>
<child :msg="msg"></child>
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: 'ParentDemo',
components: {
Child
},
data() {
return {
msg: '父组件的数据'
}
},
beforeCreate() {
console.log('父beforeCreate', this.msg)
},
created() {
console.log('父created', this.msg)
},
beforeMount() {
console.log('父beforeMount', this.$refs.div)
},
mounted() {
console.log('父mounted', this.$refs.div)
},
beforeUpdate() {
console.log('父beforeUpdate', this.$refs.div.innerHTML)
},
updated() {
console.log('父updated', this.$refs.div.innerHTML)
},
activated() {
console.log('父activated')
},
deactivated() {
console.log('父deactivated')
},
beforeDestroy() {
console.log('父beforeDestroy')
},
destroyed() {
console.log('父destroyed')
},
errorCaptured() {
console.log('父errorCaptured')
},
computed: {
newMsg() {
return this.msg + 'computed'
}
},
watch: {
msg: {
handler(newVal, oldVal) {
console.log('父watch', newVal, oldVal)
},
immediate: true
}
}
}
</script>
Child.vue
<template>
<div>
<div>子组件</div>
<p>父组件传递过来的数据:{{msg}}</p>
</div>
</template>
<script>
export default {
name: 'ChildDemo',
props: ['msg'],
beforeCreate() {
// 这里获取不到this.msg,获取会报错
console.log('子beforeCreate')
},
created() {
console.log('子created', this.msg)
},
beforeMount() {
console.log('子beforeMount')
},
mounted() {
console.log('子mounted')
},
beforeUpdate() {
console.log('子beforeUpdate')
},
updated() {
console.log('子updated')
},
activated() {
console.log('子activated')
},
deactivated() {
console.log('子deactivated')
},
beforeDestroy() {
console.log('子beforeDestroy')
},
destroyed() {
console.log('子destroyed')
},
errorCaptured() {
console.log('子errorCaptured')
}
}
</script>
上源码
beforeCreate->created之间做的事
- inject
- state (props,methods,computed,watched)
- provide
vue/src/core/instance
init.ts
inject.ts
state.ts
inject.ts
参考文章
-
vue 自省知识点——生命周期
-
Vue中的computed是在生命周期的哪个阶段执行的?
-
Vue组件的生命周期
-
vue中el属性…
-
vue2,最重要的还是官网啊啊啊
-
vue3官网相关解释