Henry Henry
  • JavaScript
  • TypeScript
  • Vue
  • ElementUI
  • React
  • HTML
  • CSS
  • 技术文档
  • GitHub 技巧
  • Nodejs
  • Chrome
  • VSCode
  • Other
  • Mac
  • Windows
  • Linux
  • Vim
  • VSCode
  • Chrome
  • iTerm
  • Mac
  • Obsidian
  • lazygit
  • Vim 技巧
  • 分类
  • 标签
  • 归档
  • 网站
  • 资源
  • Vue 资源
GitHub (opens new window)

Henry

小学生中的前端大佬
  • JavaScript
  • TypeScript
  • Vue
  • ElementUI
  • React
  • HTML
  • CSS
  • 技术文档
  • GitHub 技巧
  • Nodejs
  • Chrome
  • VSCode
  • Other
  • Mac
  • Windows
  • Linux
  • Vim
  • VSCode
  • Chrome
  • iTerm
  • Mac
  • Obsidian
  • lazygit
  • Vim 技巧
  • 分类
  • 标签
  • 归档
  • 网站
  • 资源
  • Vue 资源
GitHub (opens new window)
  • JavaScript

  • TypeScript

  • Vue

    • Vue 编码指南
    • vue-cli 启动本地服务局域网不能访问的原因分析
    • 解决 Vue 相同路由参数不同不会刷新的问题
    • 解决 vuex requires a Promise polyfill in this browser 问题
    • 关于父组件通过 v-on 接收子组件多个参数的一点研究
    • Vue $attrs 和 $listeners
    • Vue axios 发送 Form Data 数据格式请求
    • Vue 开发技巧
    • Vue 动态路由
    • Vue 集成 UEditor 富文本编辑器
    • Vue 修饰符
    • Vue 问题集合
    • Vue props 传多值的问题
    • vue-router 在 IE11 下手动更改 URL 的 hash 不会触发路由
    • vue-router 路由参数刷新消失的问题
    • 那些年被我们忽略的 vue 语法
    • vue 生命周期深入
      • 1. 生命周期钩子函数
      • 2. 单个组件的生命周期
      • 3. 父子组件的生命周期
      • 4. 兄弟组件的生命周期
      • 5. 宏 mixin 的生命周期
      • 参考资料:
    • vue 组件通信深入
    • vue 组件通信深入 Vuex
    • vue项目移动端、pc端适配方案
    • vuepress 如何引入 vuex
  • ElementUI

  • React

  • AntD

  • 前端
  • Vue
Henry
2018-03-15
目录

vue 生命周期深入

这篇博客将会从下面四个常见的应用诠释组件的生命周期, 以及各个生命周期应该干什么事

  1. 单组件的生命周期
  2. 父子组件的生命周期
  3. 兄弟组件的生命周期
  4. 宏 mixin 的生命周期

生命周期: Vue 实例从开始创建、初始化数据、编译模板、挂载 Dom→ 渲染、更新 → 渲染、卸载等一系列过程, 我们称这是 Vue 的生命周期, 各个阶段有相对应的事件钩子

# 1. 生命周期钩子函数

下面这张图是 vue 生命周期各个阶段的执行情况:

生命周期钩子函数 组件状态 最佳实践
beforeCreate 实例初始化之后, this 指向创建的实例, 不能访问到 data、computed、watch、methods 上的方法和数据 常用于初始化非响应式变量
created 实例创建完成, 可访问 data、computed、watch、methods 上的方法和数据, 未挂载到 DOM, 不能访问到 $el 属性, $ref 属性内容为空数组 常用于简单的 ajax 请求, 页面的初始化
beforeMount 在挂载开始之前被调用, beforeMount 之前, 会找到对应的 template, 并编译成 render 函数 -
mounted 实例挂载到 DOM 上, 此时可以通过 DOM API 获取到 DOM 节点, $ref 属性可以访问 常用于获取 VNode 信息和操作, ajax 请求
beforeupdate 响应式数据更新时调用, 发生在虚拟 DOM 打补丁之前 适合在更新之前访问现有的 DOM, 比如手动移除已添加的事件监听器
updated 虚拟 DOM 重新渲染和打补丁之后调用, 组件 DOM 已经更新, 可执行依赖于 DOM 的操作 避免在这个钩子函数中操作数据, 可能陷入死循环
beforeDestroy 实例销毁之前调用. 这一步, 实例仍然完全可用, this 仍能获取到实例 常用于销毁定时器、解绑全局事件、销毁插件对象等操作
destroyed 实例销毁后调用, 调用后, Vue 实例指示的所有东西都会解绑定, 所有的事件监听器会被移除, 所有的子实例也会被销毁 -

注意:

  1. created 阶段的 ajax 请求与 mounted 请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态
  2. mounted 不会承诺所有的子组件也都一起被挂载. 如果你希望等到整个视图都渲染完毕, 可以用 vm.$nextTick (opens new window)
  3. vue2.0 之后主动调用 $destroy() 不会移除 dom 节点,作者不推荐直接 destroy 这种做法,如果实在需要这样用可以在这个生命周期钩子中手动移除 dom 节点

# 2. 单个组件的生命周期

现根据实际代码执行情况分析:

<template>
  <div>
    <h3>单组件</h3>
    <el-button @click="dataVar += 1"> 更新 {{dataVar}}</el-button>
    <el-button @click="handleDestroy"> 销毁 </el-button>
  </div>
</template>
1
2
3
4
5
6
7
export default {
  data() {
    return {
      dataVar: 1
    }
  },
  beforeCreate() {
    this.compName = 'single'
    console.log(`--${this.compName}--beforeCreate`)
  },
  created() {
    console.log(`--${this.compName}--created`)
  },
  beforeMount() {
    console.log(`--${this.compName}--beforeMount`)
  },
  mounted() {
    console.log(`--${this.compName}--mounted`)
  },
  beforeUpdate() {
    console.log(`--${this.compName}--beforeUpdate`)
  },
  updated() {
    console.log(`--${this.compName}--updated`)
  },
  beforeDestroy() {
    console.log(`--${this.compName}--beforeDestroy`)
  },
  destroyed() {
    console.log(`--${this.compName}--destroyed`)
  },
  methods: {
    handleDestroy() {
      this.$destroy()
    }
  }
}
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

初始化组件时, 打印:

singlePrint1

当 data 中的值变化时, 打印:

singlePrint2

当组件销毁时, 打印:

singlePrint3

从打印结果可以看出:

  1. 初始化组件时,仅执行了 beforeCreate/Created/beforeMount/mounted 四个钩子函数
  2. 当改变 data 中定义的变量(响应式变量)时,会执行 beforeUpdate/updated 钩子函数
  3. 当切换组件(当前组件未缓存)时,会执行 beforeDestory/destroyed 钩子函数
  4. 初始化和销毁时的生命钩子函数均只会执行一次,beforeUpdate/updated 可多次执行

# 3. 父子组件的生命周期

将单组件作为基础组件(由于 props 在 beforeCreate() 中未初始化), 需要做如下更改:

props: {
    compName: {
      type: String,
      default: 'single'
    }
  },
  beforeCreate() {
    // this.compName = 'single'
    // console.log(`--${this.compName}--beforeCreate`)
    console.log(`  --data 未初始化 --beforeCreate`)
  },
1
2
3
4
5
6
7
8
9
10
11

父组件代码如下:

<template>
  <div class="complex">
    <h3>复杂组件</h3>
    <lifecycle-single compName="child"></lifecycle-single>
  </div>
</template>
1
2
3
4
5
6
const COMPONENT_NAME = 'complex'

import LifecycleSingle from './LifeCycleSingle'

export default {
  beforeCreate() {
    console.log(`--${COMPONENT_NAME}--beforeCreate`)
  },
  created() {
    console.log(`--${COMPONENT_NAME}--created`)
  },
  beforeMount() {
    console.log(`--${COMPONENT_NAME}--beforeMount`)
  },
  mounted() {
    console.log(`--${COMPONENT_NAME}--mounted`)
  },
  beforeUpdate() {
    console.log(`--${COMPONENT_NAME}--beforeUpdate`)
  },
  updated() {
    console.log(`--${COMPONENT_NAME}--updated`)
  },
  beforeDestroy() {
    console.log(`--${COMPONENT_NAME}--beforeDestroy`)
  },
  destroyed() {
    console.log(`--${COMPONENT_NAME}--destroyed`)
  },
  components: {
    LifecycleSingle
  }
}
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

初始化组件时, 打印:

nestPrint1

当子组件 data 中的值变化时, 打印:

nestPrint2

当父组件 data 中的值变化时, 打印:

nestPrint3

当 props 改变时, 打印:

nestPrint4

当子组件销毁时, 打印:

nestPrint5

当父组件销毁时, 打印:

nestPrint6

从打印结果可以看出:

  1. 仅当子组件完成挂载后,父组件才会挂载
  2. 当子组件完成挂载后,父组件会主动执行一次 beforeUpdate/updated 钩子函数(仅首次)
  3. 父子组件在 data 变化中是分别监控的,但是在更新 props 中的数据是关联的(可实践)
  4. 销毁父组件时,先将子组件销毁后才会销毁父组件

# 4. 兄弟组件的生命周期

在上面的基础上, 复杂组件做如下更改

<template>
  <div class="complex">
    <h3>复杂组件</h3>
    <lifecycle-single compName="cihld1"></lifecycle-single>
    <lifecycle-single compName="child2"></lifecycle-single>
    <el-button @click="dataVar += 1">complex 更新 {{dataVar}}</el-button>
    <el-button @click="handleDestroy">complex 销毁 </el-button>
  </div>
</template>
1
2
3
4
5
6
7
8
9

初始化组件时, 打印:

siblingPrint1

当 child1 更新和销毁时, 打印:

siblingPrint2

当 child2 更新和销毁时, 打印:

siblingPrint3

当父组件销毁时, 打印:

siblingPrint4

从打印结果可以看出:

  1. 组件的初始化(mounted 之前)分开进行,挂载是从上到下依次进行
  2. 当没有数据关联时,兄弟组件之间的更新和销毁是互不关联的

# 5. 宏 mixin 的生命周期

在上面的基础上, 添加一个 mixin.js 文件, 内容如下:

const COMPONENT_NAME = 'lifecycleMixin'
export default {
  name: COMPONENT_NAME,
  beforeCreate() {
    console.log(`--${COMPONENT_NAME}--beforeCreate`)
  },
  created() {
    console.log(`--${COMPONENT_NAME}--created`)
  },
  beforeMount() {
    console.log(`--${COMPONENT_NAME}--beforeMount`)
  },
  mounted() {
    console.log(`--${COMPONENT_NAME}--mounted`)
  },
  beforeUpdate() {
    console.log(`--${COMPONENT_NAME}--beforeUpdate`)
  },
  updated() {
    console.log(`--${COMPONENT_NAME}--updated`)
  },
  beforeDestroy() {
    console.log(`--${COMPONENT_NAME}--beforeDestroy`)
  },
  destroyed() {
    console.log(`--${COMPONENT_NAME}--destroyed`)
  }
}
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

同样的, 复杂组件做如下更改:

import lifecycleMixin from './mixin'

export default {
  mixins: [lifecycleMixin]
  // ...
}
1
2
3
4
5
6

组件初始化时, 打印:

mixinPrint1

组件销毁时, 打印:

mixinPrint2

从打印结果可以看出:

mixin 中的生命周期与引入该组件的生命周期是仅仅关联的, 且 mixin 的生命周期优先执行

在此仅将 demo 中的核心部分列出, 完整的代码请查看 demo 源码 (opens new window)

# 参考资料:

  1. vue 官网教程 (opens new window)
  2. vue 官网 API (opens new window)
  3. Vue2.0 生命周期(组件钩子函数与路由守卫) (opens new window)
编辑 (opens new window)
#Vue
上次更新: 5/27/2023, 1:02:05 PM
那些年被我们忽略的 vue 语法
vue 组件通信深入

← 那些年被我们忽略的 vue 语法 vue 组件通信深入→

最近更新
01
version 1.15
07-01
02
version 1.14
06-27
03
version 1.13
06-27
更多文章>
Theme by Vdoing | Copyright © 2017-2023 HenryTSZ | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式