vue是每一个前端开发人员都绕不过的一个技术,在国内的市场占有量也是非常的大,我们大部分人用着vue, 却不知道他内部其实经历了一些什么。每个生命周期又是什么时候开始执行的。我们今天来详细的看一看
首先,生命周期是个啥?
借用官网的一句话就是:每一个vue实例从创建到销毁的过程,就是这个vue实例的生命周期。在这个过程中,他经历了从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。那么这些过程中,具体vue做了些啥,我们今天来了解一下。
了解之前,我们先贴上一张官网的生命周期图,从图上,我们再一步一步来理解vue生命周期。
我们先简单的来解说这张图,然后再通过例子来详看
首先,从图上,我们可以看出,他的一个过程是
大致过程就是这样,下面我们来通过例子来看一看
<body>
<div id="app">
<p>{
{
message}}</p>
<button @click="changeMsg">改变</button>
</div>
</body>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'hello world'
},
methods: {
changeMsg () {
this.message = 'goodbye world'
}
},
beforeCreate() {
console.log('------初始化前------')
console.log(this.message)
console.log(this.$el)
},
created () {
console.log('------初始化完成------')
console.log(this.message)
console.log(this.$el)
},
beforeMount () {
console.log('------挂载前---------')
console.log(this.message)
console.log(this.$el)
},
mounted () {
console.log('------挂载完成---------')
console.log(this.message)
console.log(this.$el)
},
beforeUpdate () {
console.log('------更新前---------')
console.log(this.message)
console.log(this.$el)
},
updated() {
console.log('------更新后---------')
console.log(this.message)
console.log(this.$el)
}
})
</script>
从上面我们可以看出几点,
下面我们再看个例子
var vm = new Vue({
el: '#app',
data: {
message: 'hello world'
},
template: '我是模板内的{
{message}}',
methods: {
changeMsg () {
this.message = 'goodbye world'
}
},
beforeCreate() {
console.log('------初始化前------')
console.log(this.message)
console.log(this.$el)
},
created () {
console.log('------初始化完成------')
console.log(this.message)
console.log(this.$el)
},
beforeMount () {
console.log('------挂载前---------')
console.log(this.message)
console.log(this.$el)
},
mounted () {
console.log('------挂载完成---------')
console.log(this.message)
console.log(this.$el)
},
beforeUpdate () {
console.log('------更新前---------')
console.log(this.message)
console.log(this.$el)
},
updated() {
console.log('------更新后---------')
console.log(this.message)
console.log(this.$el)
}
})
我们在new Vue实例的时候直接传入了一个template,这时候我们再看输出
这么看是不是就很清晰了啊 ,在beforeMount的时候,$el还是#app, 但是在mounted的时候就变成模板的div了,是不是因为我们传了个template啊,所以,他直接将这个template转换成render函数啦。再渲染成真实dom后,用渲染出来的真实dom替换了原来的$el。
下面我们删除上面的template, 点击按钮更改下message,查看输出
哎。。。有没有看到一个很奇怪的东西啊,在beforeUpdate中输出的$el居然和updated里面输出的是一样的。这不对啊,以我们上面所说的逻辑的话,beforeUpdate内的$el应该是更新前的啊。这是怎么回事呢。这时候我们先来看一下mounted里面的。mounted里面我们看到p标签内依旧是hello world 对不对,其实这是因为,我是先点击了#app那个div的箭头,将这个div展开了以后,我再点击的按钮去更改了message,所以mounted里面还是原来的。那我现在如果先不展开mounted里面的div的话,我们来看看会怎么样
可以看到,初始输出,其实是这样的,我们看不到#app内的东西,需要点击箭头展开才能看到,现在,我不展开,然后我先点击按钮去改变message, 等beforUpdate和updated都执行完成后,我们再来一起展开,看下会怎么样
这是点击改变了message后的截图,然后我们现在展开div看看
看到没有,我们发现什么啦,怎么现在mounted里面的$el也变成更新后的啦。
呵呵,不要慌,其实啊,因为this.$el是一个对象,其实本质就是一个指针,当我们刚console.log输出的时候,其实并没有显示内容,而当我们点击箭头去展开这个div的时候,将指针指向了当前的$el,所以我们看到的才会都是改变后的$el。这也就是为什么之前mounted里面的$el是改变之前的值,而现在是改变之后的值了,因为之前那张图,我是先展开了mounted中的div,再去改变的message。下面我们再来验证下是不是这么回事
怎么验证,我们修改下代码
mounted () {
console.log('------挂载完成---------')
console.log(this.message)
console.log(this.$el.innerHTML)
console.log(this.$el)
},
beforeUpdate () {
console.log('------更新前---------')
console.log(this.message)
console.log(this.$el.innerHTML)
console.log(this.$el)
},
updated() {
console.log('------更新后---------')
console.log(this.message)
console.log(this.$el.innerHTML)
console.log(this.$el)
}
我们增加一个输出 this.$el.innerHTML, 再查看结果
这么看是不是就很明了啦,beforeUpdate里面的$el的内容,确实还是改变之前的,而我们之前看到的,只是因为我们后面展开时指针指向了当前值才导致的,是个视觉差而已。
后面两个销毁的,我就不举例说明了,没啥说的。下面我们再看一个问题,就是如果我们没有设置el时,会怎么样,我们在之前的生命周期图中,是说过,当没有找到el时, 说是不是会等待vm.$mount(el) 啊,这句话啥意思,我们来看一下
首先,我们看下,vue源码中,
在执行完,beforeCreate和created之后,是做了个判断,当存在el时,调用了 $mount方法,created之后的步骤,就是在这里面去走的。那如果没有el呢, 生命周期图中是说等待vm. $mount调用。那是不是只能等待我们手动去调用啊。
var vm = new Vue({
data: {
message: 'hello world'
},
// template: '我是模板内的{
{message}} ',
methods: {
changeMsg () {
this.message = 'goodbye world'
}
},
beforeCreate() {
console.log('------初始化前------')
console.log(this.message)
console.log(this.$el)
},
created () {
console.log('------初始化完成------')
console.log(this.message)
console.log(this.$el)
},
beforeMount () {
console.log('------挂载前---------')
console.log(this.message)
console.log(this.$el)
},
mounted () {
console.log('------挂载完成---------')
console.log(this.message)
console.log(this.$el.innerHTML)
console.log(this.$el)
},
beforeUpdate () {
console.log('------更新前---------')
console.log(this.message)
console.log(this.$el.innerHTML)
console.log(this.$el)
},
updated() {
console.log('------更新后---------')
console.log(this.message)
console.log(this.$el.innerHTML)
console.log(this.$el)
}
})
这个时候,我们删除了el属性,看看结果
是不是只走了前面两个生命周期啊,后面就没走了,这个时候其实就是在等$mount被调用了,那我们加个按钮,点击按钮,手动调用一下$mount看会怎样
没点击之前
点击后
可以看到,生命周期继续往下走了。
这时候不知道大家是不是想起来,看到有些vue项目的main.js里面是这样的
export default new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})
而有些vue项目中人家用的又是这样的
export default new Vue({
router,
store,
i18n,
render: h => h(App)
}).$mount('#app')
其实后者,就相当于是手动调用了$mount了。
好了,言尽于此,有没有看懂的朋友,请直接私信或者评论。