本文已参与「新人创作礼」活动,一起开启掘金创作之路。
生命周期
简单介绍下生命周期,
生命周期就是每一个Vue实例从创建到卸载的过程,涵盖了从创建实例,初始化数据,编译模板、挂载dom、更新dom、卸载实例等过程。
初始化阶段
1. **new Vue() ** 创建Vue实例
2. init event & lifeCycle 初始化事件和生命周期
初始化事件,就是初始化一些绑定事件,比如on,emit等 初始化生命周期的一些状态,比如isMounted,is等,我们熟知的用来创建dom的函数createElement()也在这里初始化。
beforeCreate钩子 参考上下两个周期,我们应该是拿不到el,template,data,methods的
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.10/vue.min.js"></script>
<body>
<div id="app">
<p>Hello Vue!</p>
</div>
</body>
<script>
new Vue({
el: "#app",
template: "<div>我是Vue<div>",
data: {
msg: "Hello Vue!",
},
methods: {
showMsg: function() {
console.log(this.msg);
},
},
// init event & lifeCycle 初始化事件和生命周期
beforeCreate() {
console.log("template", this.template); // template undefined
console.log("el", this.el); // el undefined
console.log("data", this.msg); // data undefined
this.showMsg(); // TypeError: this.showMsg is not a function
},
});
</script>
3.init injections and reactivity 初始化data、method以及初始化数据劫持以及绑定组件的watcher,以便检测组件数据的变化。
created 在这里仍然拿不到el,template,但是已经能拿到data和methods
// init injections and reactivity 初始化数据和方法
created() {
console.log("template", this.template); // template undefined
console.log("el", this.el); // el undefined
console.log("data", this.msg); // data Hello Vue!
this.showMsg(); // Hello Vue!
},
编译模板阶段
这个阶段的目标就是编译模板生成dom保存到内存中,也就是生成了虚拟dom 这里有三个对象都可能作为模板取生成dom: el、template、render函数
el是在页面中已存在的dom节点上选择一个作为Vue的挂载元素; template是在vue中定义的字符串模板,他将会替换挂载元素; render函数是字符串模板的代替模式,依然会替换掉挂载元素;
编译阶段的流程几个重点:
- 如果不存在el,必须显示调用$mount()才能手动开始编译,否则无法进入到编译阶段;
- 如果存在el,并且不存在template,render函数,就会使用el编译成template模板,转化成render函数生成虚拟dom;
- 如果存在el,并且有template, 并且实例中不存在render函数,template会替换el绑定的元素,转化成render函数,通过createElement生成虚拟dom存在内存;
- 如果三者皆存在,则直接通过render函数生成虚拟dom;
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.10/vue.min.js"></script>
<body>
<div id="app">我是el-{{msg}}</div>
</body>
<script>
new Vue({
el: "#app",
data: {
msg: "Hello Vue!",
},
template: "<div>我是template-{{msg}}</div>",
render: function (createElement) {
return createElement("div", '我是render-'+this.msg);
},
methods: {
showMsg: function () {
console.log(this.msg); // Hello Vue!
},
},
// init event & lifeCycle 初始化事件和生命周期
beforeCreate() {
console.log("----初始化前----");
console.log("el:", this.$el); // el undefined
console.log("data:", this.msg); // data undefined
},
// init injections and reactivity 初始化数据和方法
created() {
console.log("----初始化后----");
console.log("el:", this.$el); // el undefined
console.log("data:", this.msg); // data Hello Vue!
},
// 编译阶段
beforeMount() {
console.log("----挂载前----");
console.log("el", this.$el); // el undefined
},
});
</script>
展示一下三者都有的结果。
beforeMount: 这个钩子刚刚已经用过了,在这里数据的变量占位符还在,还可以在这里最后一次可以修改未渲染前的数据变量。
挂载阶段
1. Create vm.$el and replace "el" with it
渲染真实dom到页面上,dom中的变量占位符将渲染成真实数据,渲染成功后,真实dom也将替换掉$el原来的值;
<body>
<div id="app">
<p>
我是el-{{msg}}
</p>
</div>
</body>
<script>
new Vue({
el: "#app",
data: {
msg: "Hello Vue!",
},
template: "<div>我是template-{{msg}}</div>",
// render: function (createElement) {
// return createElement("div", '我是render-'+this.msg);
// },
methods: {
showMsg: function () {
console.log(this.msg); // Hello Vue!
},
},
// 编译阶段
beforeMount() {
console.log("----挂载前----");
console.log("el", this.$el); // el undefined
},
mounted() {
console.log("----挂载后----");
console.log("el", this.$el); // el <div>我是template-Hello Vue!</div>
}
});
</script>
结果可以发现,我们是用template的内容渲染的,可以看到在mounted中,el已经替换成了template的内容。
mounted钩子,在这里页面已经渲染成功,$el被页面上渲染的元素替换,可以通过document去更改数据,触发更新。
更新阶段
当我们数据发生改变的时候,会生成一个虚拟dom,然后会用新的虚拟dom和原来的虚拟dom进行diff比较,算出需要修改的范围,然后去更新dom,再通过render去渲染到页面上
beforeUpdate钩子:更新前 数据发生改变后会立刻触发beforeUpdate,生成虚拟dom,所以在这里我们拿到data中的数据已经是改变的,但是这时还没有进行diff和render,所以还没又渲染到页面上。 updated钩子:更新后 数据已经渲染到页面上,这时虚拟dom和页面上的dom都已经更改。
销毁阶段
beforeDestroy钩子,这时还没有进行销毁,实例的数据,方法都能拿到,在这里我们可以去关闭一些定时器之类的,操作一些实例的函数之类。 destroyed钩子,这时页面已经销毁了,组件的watcher解绑,data、methods、filter以及el都逐个销毁。