# vue生命周期
# 生命周期钩子汇总
- 根组件实例:8个 (beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed)
- 组件实例:8个 (beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed)
- 全局路由钩子:2个 (beforeEach、afterEach)
- 组件路由钩子:3个 (beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave)
- 指令的周期: 5个 (bind、inserted、update、componentUpdated、unbind)
- beforeRouteEnter的next所对应的周期
- nextTick所对应的周期
# 组件实例周期图
# 组件生命周期钩子
实例:
<template>
<el-row>
<el-col :span="2">
<el-input v-model="message"></el-input>
</el-col>
</el-row>
</template>
<script>
export default {
data () {
return {
message: '你好呀~'
}
},
beforeCreate: function () {
console.group('------beforeCreate创建前状态------')
console.log('el :' + this.$el) // undefined
console.log('data :' + this.$data) // undefined
console.log('message:' + this.message)
},
created: function () {
console.group('------created创建完毕状态------')
console.log('el :' + this.$el) // undefined
console.log('data :' + this.$data) // 已被初始化
console.log('message:' + this.message) // 已被初始化
},
beforeMount: function () {
console.group('------beforeMount挂载前状态------')
console.log('el :' + this.$el) // 已被初始化
console.log(this.$el)
console.log('data :' + this.$data) // 已被初始化
console.log('message:' + this.message) // 已被初始化
},
mounted: function () {
console.group('------mounted 挂载结束状态------')
console.log('el :' + this.$el) // 已被初始化
console.log(this.$el)
console.log('data :' + this.$data) // 已被初始化
console.log('message:' + this.message) // 已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
updated: function () {
console.group('updated 更新完成状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
destroyed: function () {
console.group('destroyed 销毁完成状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
结果:
# 路由和指令钩子
组件内:组件路由钩子和指令
<template>
<el-row>
<el-col :span="2">
<el-input v-model="message"></el-input>
</el-col>
<el-col :span="2" :offset="1">
<el-button v-order @click="$router.push('/hello')">切换路由</el-button>
</el-col>
</el-row>
</template>
<script>
export default {
data () {
return {
message: '你好呀~'
}
},
beforeRouteEnter (to, from, next) {
console.log('组件路由勾子:beforeRouteEnter')
next(vm => {
console.log('组件路由勾子beforeRouteEnter的next')
})
},
beforeRouteLeave (to, from, next) {
console.log(this) // 可以访问vue实例
console.log('组件路由勾子:beforeRouteLeave')
next()
},
beforeCreate: function () {
console.group('------beforeCreate创建前状态------')
console.log('el :' + this.$el) // undefined
console.log('data :' + this.$data) // undefined
console.log('message:' + this.message)
},
created: function () {
console.group('------created创建完毕状态------')
console.log('el :' + this.$el) // undefined
console.log('data :' + this.$data) // 已被初始化
console.log('message:' + this.message) // 已被初始化
},
beforeMount: function () {
console.group('------beforeMount挂载前状态------')
console.log('el :' + this.$el) // 已被初始化
console.log(this.$el)
console.log('data :' + this.$data) // 已被初始化
console.log('message:' + this.message) // 已被初始化
},
mounted: function () {
console.group('------mounted 挂载结束状态------')
console.log('el :' + this.$el) // 已被初始化
console.log(this.$el)
console.log('data :' + this.$data) // 已被初始化
console.log('message:' + this.message) // 已被初始化
},
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
updated: function () {
console.group('updated 更新完成状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
destroyed: function () {
console.group('destroyed 销毁完成状态===============》')
console.log('el :' + this.$el)
console.log(this.$el)
console.log('data :' + this.$data)
console.log('message:' + this.message)
},
directives: {
order: {
bind (el, binding, vnode) {
console.log('指令binding')
},
inserted (el, binding, vnode) {
console.log('指令inserted')
}
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
main.js:全局路由钩子
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import router from './router'
Vue.config.productionTip = false
Vue.use(ElementUI)
router.beforeEach((to, from, next) => {
console.log('路由全局勾子:beforeEach')
next()
})
router.afterEach((to, from) => {
console.log('路由全局勾子:afterEach')
})
/* eslint-disable no-new */
new Vue({
beforeCreate () {
console.log('根组件:beforeCreate')
},
created () {
console.log('根组件:created')
},
beforeMount () {
console.log('根组件:beforeMount')
},
mounted () {
console.log('根组件:mounted')
},
el: '#app',
router,
components: { App },
template: '<App/>'
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
结果:
# 非异步生命周期执行顺序
没有异步请求,正常进入路由加载组件,vue生命周期触发顺序
// main.js
import Vue from 'vue';
import App from './App';
import router from './router';
Vue.config.productionTip = false;
new Vue({
beforeCreate() {
console.log('根组件:beforeCreate');
},
created() {
console.log('根组件:created');
},
beforeMount() {
console.log('根组件:beforeMount');
},
mounted() {
console.log('根组件:mounted');
},
el: '#app',
router,
template: '<App/>',
components: { App },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Hello from '@/components/Hello';
import Test from '@/components/Test';
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
},{
path: '/test',
name: 'Test',
component: Test,
beforeEnter: (to, from, next) => {
console.log('路由独享的钩子:beforeEnter');
next();
},
},
],
});
router.beforeEach((to, from, next) => {
console.log('路由全局钩子1:beforeEach');
next();
});
router.beforeEach((to, from, next) => {
console.log('路由全局钩子2:beforeEach');
next();
});
router.afterEach((to, from) => {
console.log('路由全局钩子3:afterEach');
});
export default router;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app',
beforeRouteEnter (to, from, next) {
console.log('App组件路由钩子:beforeRouteEnter')
next(vm => {
console.log('App组件路由钩子beforeRouteEnter的next')
})
},
beforeCreate () {
console.log('App组件:beforeCreate')
},
created () {
this.$nextTick(() => {
console.log('App组件:nextTick')
})
console.log('App组件:created')
},
beforeMount () {
console.log('App组件:beforeMount')
},
mounted () {
console.log('App组件:mounted')
},
directives: {
foo: {
bind (el, binding, vnode) {
console.log('App组件:指令binding')
},
inserted (el, binding, vnode) {
console.log('App组件:指令inserted')
}
}
}
};
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Test.vue
<template>
<h1 v-foo @click = "$router.push('/')">test go hello</h1>
</template>
<script>
export default {
beforeRouteEnter (to, from, next) {
console.log('路由子组件路由钩子:beforeRouteEnter')
next(vm => {
console.log('路由子组件路由钩子beforeRouteEnter的next')
})
},
beforeCreate () {
console.log('路由子组件:beforeCreate')
},
created () {
this.$nextTick(() => {
console.log('路由子组件:nextTick')
})
console.log('路由子组件:created')
},
beforeMount () {
console.log('路由子组件:beforeMount')
},
mounted () {
console.log('路由子组件:mounted')
},
directives: {
foo: {
bind (el, binding, vnode) {
console.log('路由子组件:指令binding')
},
inserted (el, binding, vnode) {
console.log('路由子组件:指令inserted')
}
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
进入test路由:
# 实际执行的顺序
- 路由钩子 (beforeEach、beforeEnter、beforeRouteEnter、afterEach)
- 根组件 (beforeCreate、created、beforeMount)
- APP组件 (beforeCreate、created、beforeMount)
- 路由子组件 (beforeCreate、created、beforeMount)
- 路由子组件指令 (bind、inserted)
- 路由子组件 mounted
- App组件 mounted
- 根组件 mounted
- 路由子组件路由钩子beforeRouteEnter的next的回调
- App组件 nextTick
- 路由子组件 nextTick
# 异步请求生命周期
在钩子中发送异步请求,进入路由加载组件,vue生命周期触发顺序
修改路由配置文件,在beforeEach中发送异步请求,阻塞路由
// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Hello from '@/components/Hello';
import Test from '@/components/Test';
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello,
},{
path: '/test',
name: 'Test',
component: Test,
beforeEnter: (to, from, next) => {
console.log('路由独享的钩子:beforeEnter');
next();
},
},
],
});
router.beforeEach((to, from, next) => {
console.log('路由全局钩子1:beforeEach');
$.get('https://www.renrenche.com', () => {
console.log('路由全局钩子4:异步回调beforeEach');
next();
});
});
router.beforeEach((to, from, next) => {
console.log('路由全局钩子2:beforeEach');
next();
});
router.afterEach((to, from) => {
console.log('路由全局钩子3:afterEach');
});
export default router;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
结果:
# 实际执行的顺序
- 路由全局钩子 (beforeEach)
- 根组件 (beforeCreate、created、beforeMount)
- APP组件 (beforeCreate、created、beforeMount、mounted)
- 根组件 mounted
- App组件 nextTick
- 路由钩子(beforeEach异步回调结果、其他beforeEach、beforeEnter、beforeRouteEnter、afterEach)
- 路由子组件 (beforeCreate、created、beforeMount)
- 路由子组件指令 (bind、inserted)
- 路由子组件 mounted
- 路由子组件路由钩子beforeRouteEnter的next的回调
- 路由子组件 nextTick
# 总结
- 路由钩子执行周期非常早,甚至在根实例的渲染之前
具体的顺序 router.beforeEach > beforeEnter > beforeRouteEnter > router.afterEach
指令的绑定在组件mounted之前,组件的beforeMount之后
beforeRouteEnter的执行顺序是如此靠前,而其中next的回调勾子的函数,执行则非常靠后,在mounted之后
我们通常是在beforeRouteEnter中加载一些首屏用数据,待数据收到后,再调用next勾子,通过回调的参数vm将数据绑定到实例上