Vue的生命周期
Vue的生命周期指的是vm对象从创建到最终销毁的整个过程,在这个过程中不同的时间节点上调用不同的钩子函数(在不同时刻被自动调用的函数)
- 虚拟DOM在内存中就绪时:去调用一个a函数
- 虚拟DOM转换成真实DOM渲染到页面时:去调用一个b函数
- Vue的data发生改变时:去调用一个更新阶段的钩子函数
- Vue实例被销毁时:去调用一个销毁阶段的钩子函数
Vue 的生命周期可以被划分为初始阶段、挂载阶段、更新阶段、销毁阶段, 每个阶段会调用两个钩子函数beforeXxx()、xxxed()
- 初始阶段: beforeCreate() 创建前, created() 创建后
- 挂载阶段: beforeMount() 挂载前, mounted() 挂载后
- 更新阶段: beforeUpdate() 更新前, updated() 更新后
- 销毁阶段: beforeDestroy() 销毁前, destroyed() 销毁后
- 8个钩子函数直接写在Vue构造函数的options对象当中
研究Vue的生命周期主要是研究在不同的时刻Vue做了哪些不同的事儿,根据不同需求编写对应的钩子函数等待Vue自动调用
- 如在vm被销毁之前需要将绑定到元素上的自定义事件全部解绑,解绑的代码可以写到beforeDestroy()这个函数中
初始阶段(初次渲染)
第一步: 创建Vue实例vm, 此时Vue实例已经完成了创建
第二步: 初始化事件对象和生命周期
第三步: 调用beforeCreate()钩子函数,此时数据代理和数据监测还未创建,无法通过vm去访问data对象的属性
第四步: 调用created()钩子函数, 此时数据代理和数据监测已经创建完毕,可以通过vm访问data对象的属性
第五步: 编译模板语句生成虚拟DOM, 此时虽然生成了虚拟Dom但还没有渲染到页面上
- el必须要存在,如果不存在需要手动调用vm.$mount(el)进行手动挂载
- el和template同时存在优先选择template, 如果没有template才会选择el
初始阶段适合做什么
- beforeCreate:可以在此时加一些loading效果
- created:结束loading效果, 发送一些网络请求获取数据, 添加定时器等操作
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
m(){
console.log('m....')
}
},
beforeCreate() {
// 创建前表示数据代理和数据监测还未创建,此时还无法访问data当中的数据以及methods当中的方法
console.log('beforeCreate', this.counter)
// 调用methods报错,方法不存在
//this.m()
// 打一个断点
debugger
},
created() {
// 创建后表示数据代理和数据监测创建完毕,可以访问data中的数据了。
console.log('created', this.counter)
// 可以访问methods的m方法
this.m()
}
})
测试el和template配置项
- el有,template也有,最终编译template模板语句
- el有,template没有,最终编译el模板语句
- el没有的时候,需要手动调用vm.$mount(el)进行手动挂载(保证el必须存在),然后流程才能继续, 此时如果有template有,最终编译template模板语句
- el没有的时候,需要手动调用vm.$mount(el)进行手动挂载(保证el必须存在),然后流程才能继续, 此时如果没有template,最终编译el模板语句
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
<script>
// el和template同时存在优先选择template,
const vm = new Vue({
el : '#app',
template : `
<h1>{{s}}</h1>
`,
data : {
msg : '测试el和template配置项',
s : 'template配置项!!!!'
}
})
// el必须要存在,如果不存在需要手动调用vm.$mount(el)进行手动挂载
//vm.$mount('#app')
</script>
</body>
挂载阶段(初次渲染)
第一步: 调用beforeMount()钩子函数(此时有虚拟Dom但还没有渲染到页面),在函数中操作页面元素并没有效果,因为虚拟DOM已经确定了,等到其渲染时就会覆盖
第二步: 给vm追加$el属性用它来代替”el”,这个属性代表了真实的DOM元素(将虚拟Dom渲染到页面生成真实DOM)
第三步: 调用mounted()钩子函数,在函数中操作页面的DOM会有效果,因为操作的是真实DOM
mounted: 适合操作页面的DOM元素
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
beforeMount() {
// 挂载前
console.log('beforeMount')
},
mounted() {
// 挂载后
console.log('mounted')
// $el属性代表了真实的DOM元素
console.log(this.$el)
console.log(this.$el instanceof HTMLElement)
}
})
更新阶段(data发生变化)
更新阶段开始的标志是data发生变化时需要重新渲染页面
第一步: 调用beforeUpdate()钩子函数(此时只是更新了内存中的data数据发生,页面还未更新)
第二步: 进行diff算法新旧DOM比对, 虚拟DOM重新渲染和修补
第三步: 调用updated()钩子函数(此时内存中的数据和页面都已更新)
更新阶段适合做什么
- beforeUpdate:适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
- updated:适合在页面更新后对数据做统一处理
<div id="app">
<h1>{{msg}}</h1>
<h3>计数器:{{counter}}</h3>
<h3 v-text="counter"></h3>
<button @click="add">点我加1</button>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
add(){
this.counter++
},
},
// 更新阶段
beforeUpdate() {
// 更新前,只是更新了内存中的数据,还没有更新页面
console.log(this.counter)
},
updated() {
// 更新后
console.log(this.counter)
},
})
</script>
销毁阶段(调用方法)
销毁阶段开始的标志是手动调用vm.$destroy()方法
第一步(解绑前): 调用beforeDestroy()钩子函数,此时Vue实例还在, vm对象上所有的东西还没有开始解绑(子组件,监视器,自定义事件监听器,v-指令)
- 虽然vm上的监视器,子组件,v-指令,自定义的和内置的事件监听器还没有解绑, 但是它们都已经不能用了,和destroyed()效果相同
- 修改data也不会重新渲染页面了,因为当前已经处于销毁阶段了,渲染阶段是更新阶段的事情
第二步(解绑后): 调用destroyed()钩子函数,此时Vue实例还在但是vm对象上所有的东西都已经解绑完成了
销毁阶段适合做什么: beforeDestroy方法适合做销毁前的准备工作,例如可以在这里清除定时器
<body>
<div id="app">
<h1>{{msg}}</h1>
<h3>计数器:{{counter}}</h3>
<h3 v-text="counter"></h3>
<!--高版本的Vue会把内置的事件监听器也会卸载-->
<button @click="add">点我加1</button>
<button @click="destroy">点我销毁</button>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue生命周期',
counter : 1
},
methods: {
add(){
console.log('add....')
this.counter++
},
destroy(){
// 执行销毁阶段,销毁vm
this.$destroy()
},
},
watch : {
counter(){
console.log('counter被监视一次')
}
},
// 销毁阶段
beforeDestroy() {
// 销毁前
console.log('beforeDestroy')
console.log(this)
// 修改data也不会重新渲染页面了
this.counter = 1000
},
destroyed() {
// 销毁后
console.log('destroyed')
console.log(this)
},
})
</script>
</body>