前沿:
通过前面几节的学习,我们已经对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); // 先打印子组件的mountedthis.arr = [4, 5, 6]; // 说明这里mounted是异步的}}}});
</script>
显示结果
通过实例发现,是不是用$nextTick
方法在组件数据更新以后获取的DOM
元素的内容都不一样