前沿:
通过前面几节的学习,我们已经对vue有了初步的了解,大致了解了vue可以帮我们干什么,
那么接下来我们就来看看vue的生命周期和它常用的钩子函数,
1. 理解生命周期的含义
生命周期:就是一个组件从实例化创建并添加到DOM树开始 ,一直到组件被销毁的整个过程 。
生命周期函数(钩子函数): 就是在Vue生命周期的整个过程的不同阶段被调用的函数
2. 生命周期图
首先来看一下官网对于vue生命周期的图解, 官网上目前有8个生命周期函数,
还有两个我们之后再看. 先看看基本的8个钩子函数
3. 钩子函数的理解
通过打断点的方式,让我们好好理解生命周期的钩子函数, 你们也可以自己复制代码进行测试好好理解Vue的钩子函数
<script>
new Vue({
el: "#app",
data:{},
methods:{},
// 1. 在实例化之前被调用
beforCreate: function(){
// 这个方法的时候data还没有加载,所以此方法用不到
// 一般不会在这里处理什么事情
alert("组件实例化之前执行的函数");
debugger;
},
// 2. 实例化初始之后,被添加到DOM元素之前触发
created: function(){
// 可以在这里发送ajax,初始化操作
alert("组件实例化完毕,但页面还未显示");
debugger;
},
// 3. 在元素(虚拟DOM)已经准备好被添加到DOM,但是还没有添加时触发
beforeMount: function(){
// 要保证有el,或者vm.$mount() 否则这里不会执行
alert("组件挂载前,但页面还未显示,但是虚拟DOM已经配置");
debugger;
},
// 4. 会在元素创建后触发
mounted: function(){
// 如果有template属性模板,会用模板替换外部的el,只要有此属性,直接卸载外部el找中的内
// 这将没有任何意义了
// template只能有一个更元素,不能是文本节点,
// 真实的dom渲染完了,可以操作dom了
alert("组件挂载后,此方法执行后,页面显示");
debugger;
},
// 5. 在数据更新将要对DOM做一些修改时触发
beforeUpdate: function(){
// 当页面依赖的数据发生变化时才执行,一般用watch来替换,这个方法不好用
// 页面依赖的数据发生变化,数据已变化,页面还没有渲染
alert("组件更新前,但页面还未显示,但是虚拟DOM已经配置");
debugger;
},
// 6. 后在DOM的更新已经完成后触发
updated: function(){
// 重新渲染页面后执行, 可以操作DOM了
alert("组件更新后,此方法执行后,页面显示");
debugger;
},
// 7. 在组件即将被销毁并且从DOM上移出时触发
beforeDestroy: function(){
//没什么意义,死了就什么都干不了了
alert("组件销毁前");
debugger;
}
// 8. 组件被销毁后触发
destroyed: function(){
alert("组件销毁");
debugger;
}
})
</script>
实践是检验道理的唯一标准,
这里只能通过注释来介绍一下vue的钩子函数的意义。
想要详细了解还需要你亲自测试体会
生命周期钩子的 this
上下文指向调用它的 Vue 实例。
通过这张生命周期图,我们发现我们之前还有一些内容没有测试到,
诸如使用$mount
绑定DOM元素, 实例化时自定义template
模板等,那么接下来就好好测试这些,生命周期图所讲述的内容
4. 绑定节点
我们之前学到一直是使用vue配置对象里的el来绑定DOM节点
生命周期图告诉我们,如果我们没有el属性就会查找vue实例对象有没有通过$mount
方法来绑定DOM元素
其实就算你是用el
绑定了DOM元素,在Vue源码中也是会转为$mount
处理
<div id="app">
<div v-html="msg"></div>
</div>
<div id="haha"></div>
<script>
const vm = new Vue({
el:"#app",
data: {
msg:"hello"
},
})
vm.$mount("#haha")
</script>
同时我们还会发现,如果el
和$mount
都存在,绑定的元素有差异,那么以el
为主,
因为生命周期图告诉我们只有当el属性不存在的时候,才会查看$mount
方法
5. template模板编译
我们之前学习一直使用的都是没有template模板的, 根据生命周期图显示,如果我们没有template,模板,我们就会将el 属性对应的挂载点作为我们的模板
如果有template模板,我们就会用以后的模板替换之前的模板
<div id="app">
{{ msg }}
</div>
<script>
const vm = new Vue({
el:"#app",
template:"<div id='haha'>我是小当家</div>",
data: {
msg:"hello"
},
})
</script>
5.1 注意template模板里只能有一个根标签
所以下面的写法会报错,是错误的写法
const vm = new Vue({
template:`
<div id='haha'>
我是小当家
</div>
<span></span>
`,
data: {
msg:"hello"
},
})
5.2 改变数据绑定的位置
如果有template 模板,我们动态绑定的数据,就需要在模板中绑定
<div id="app">
{{ msg }}
</div>
<script>
const vm = new Vue({
el:"#app",
template:"<div id='haha'>我是小当家{{ msg }}</div>",
data: {
msg:"hello"
},
})
</script>
6. 关于mounted钩子函数中获取DOM元素的问题
6.1 正常在mounted钩子函数里获取DOM
<div id="app">
<div v-html="msg"></div>
</div>
<script>
const vm = new Vue({
el:"#app",
data: {
msg:"<h2 id='box'>hello</h2>"
},
mounted(){
console.log("mounted:");
console.log(box);
},
})
</script>
我们会发现获取DOM元素完全没有问题,
6.2 更改DOM节点内容
如果我们动态的修改了DOM节点,那么我们再获取看看
<div id="app">
<div v-html="msg"></div>
</div>
<script>
const vm = new Vue({
el:"#app",
data: {
msg:"<h2 id='box'>hello</h2>"
},
mounted(){
// 动态修改DOM 节点
this.msg = `<h2 id="box">你好</h2>`
// 获取DOM 节点
console.log(box);
},
})
</script>
这是我们就会发现我们获取的还是原先的DOM节点, 此时去获取节点内容就会有问题,
获取的DOM节点并不是更改后最新的DOM节点
6.3 解决动态DOM 节点的问题
我们可以使用$nextTice
来解决此类问题
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
$nextTick()
,是将回调函数延迟在下一次dom更新数据后 调用,简单的理解是:数据更新后,在dom重新渲染完毕,自动执行该函数,
通过$nextTick
方法来异步操作vue实例
示例:
<div id="app">
<div v-html="msg"></div>
</div>
<script>
const vm = new Vue({
el:"#app",
data: {
msg:"<h2 id='box'>hello</h2>"
},
mounted(){
// 动态修改DOM 节点
this.msg = `<h2 id="box">你好</h2>`
// 获取DOM 节点
console.log(box);
// 通过$nextTick异步处理方法来获取就会得到最新的值
this.$nextTick(() => {
console.log(box);
})
},
})
</script>
上面的示例,我们打印了2次box ,box是DOM元素的id,可以用来获取DOM元素, 但是此时两次获取的DOM元素展示的结果不一样
第一次获取box
虽然数据已经改变, 页面也准备重新渲染新的DOM
元素, 但是此时DOM
还没有更新完成,就获取box
,获取的就是原来的DOM
元素内容, $nextTick
方法会在DOM
元素更新完成以后才会触发回调函数,在回调函数中获取的box
才是更改后最新的DOM
元素
建议在组件学习后再回来看:
带有子组件的示例:
<div id="app">
<child ref="child"></child>
</div>
<template id="child">
<div>
<span v-for="a in arr">{{a}}</span>
</div>
</template>
<script>
var vm = new Vue({ // 根实例
el: '#app',
data: {
radio: 'home'
},
mounted() {
console.log(1); // 在执行父组件的mounted
// console.log(this.$refs.child.$el.innerHTML);
// 这里打印的是 1,2,3的数组
// 这里可以选择$nextTick方法,这个是在页面渲染完毕后执行
this.$nextTick(() => {
console.log(this.$refs.child.$el.innerHTML); // 这个时候才4,5,6
})
},
components: {
child: {
template: '#child',
data() {
return { arr: [1, 2, 3] }
},
mounted() {
console.log(2); // 先打印子组件的mounted
this.arr = [4, 5, 6]; // 说明这里mounted是异步的
}
}
}
});
</script>
显示结果
通过实例发现,是不是用$nextTick
方法在组件数据更新以后获取的DOM
元素的内容都不一样