# 一、vue 生命周期

# 1、概念认知

生命周期是指一个组件从创建 —> 运行 —> 销毁的整个阶段。它强调的是一个 时间段

也可以说: 挂载 ---> 更新 ---> 销毁

生命周期函数是 vue 框架提供的内置函数,会伴随组件的生命周期,自动按次序执行。它强调的是一个 时间点

# 2、生命周期示意图

  • 示意图中的"template" option

即示意图的走左侧分支,整体替换 🤔

<div id="app">
  <!-- <h3>title名称</h3>
      <p>忽见面前一双玉色蝴蝶,大如团扇,一上一下,迎风翩跹,十分有趣。</p> -->
</div>

<script type="text/JavaScript">
  new Vue({
    el: '#app',
    // 只能有一个🚩根元素
    template:`
      <div>
        <h3>title名称</h3>
        <p>忽见面前一双玉色蝴蝶,大如团扇,一上一下,迎风翩跹,十分有趣。</p>
      </div>
    `
  })
</script>

# 3、重点关注 ✨

  为了更好的演示 vue 的生命周期过程,我们可以使用debugger进行断点调试。

以前我们还接触过一个东西:#region (代码折叠)

<script>
  new Vue({
    el: '#app',
    mounted() {
      console.log('mounted阶段的vue示例:' + this)
      debugger // 进入页面后刷新一下即可
    }
  })
</script>

  通常我们重点关注生命周期的几个重要阶段即可:

钩子 阶段 解释
created 怀孕 (拥有模板解析的所要的东西)
mounted 出生 (虚拟 DOM 变为 真实 DOM)
beforeDestroy 交代后事 (vm.$destroy()触发,善后工作)

# 二、$nextTick(cb) 钩子

  $nextTick() 方法并不会触发 Vue.js 组件的生命周期钩子函数,它仅仅是在当前执行栈执行完毕之后,下一次 DOM 更新循环结束之后执行的回调函数

提示

  简单的说就是:我们可以使用$nextTick(cb)钩子让cb回调函数在生命周期触发 updated 函数后执行。

# 1、使用示例

  组件的 $nextTick(cb) 方法,会把 cd 回调推迟到下一个 DOM 更新周期之后执行

即:组件的 DOM 更新完成后,再执行 cb 回调函数。这样可以保证 cb 回调函数可以操作到最新的 DOM 元素。

位置:

  它可以在组件的任何生命周期内(created、mounted、updated 和 destroyed 等)、任何组件的方法中使用

需求:

  在组件的数据发生更改时,我们想要立即操作更新后的 DOM(然后等待 DOM 更新触发回调)

<template>
  <h2 ref="title">当前计数:{{ counter }}</h2>
  <button @click="increment">+1</button>
</template>

<script>
  export default {
    methods: {
      increment() {
        // 数据改变
        this.counter += 20

        // DOM操作
        nextTick(() => {
          const title = this.$refs[title].textContent
          console.log(title)
        })
      }
    }
  }
</script>

# 2、原理分析 ✨

  vue 为什么会给我们提供一个$nextTick(cb)呢?

答案

  首先我们要明白 Vue 中更改响应式状态时,DOM 更新 (opens new window)并不是同步生效的,而是异步执行的。

  也就是说,它和 react 中的setState()一样,会将更改状态的任务缓存在一个队列中,最后一次性更新 DOM,以此来减小 DOM 渲染开销。

  而$nextTick(cb)可以在状态改变后立即使用,以等待 DOM 更新完成。它是一个待执行的回调,可以保证其操作的是已经更新后的 DOM 。

简单的说,$nextTick(cb) 的使用往往 DOM 操作

  Vue 在内部会对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

实现原理

  我们可以采用下面的两种方式来还原$nextTick(cb)的实现:

  • Promise(微任务)
updateMessage() {
  this.message = '已更新'
  console.log(this.$el.textContent) // => '未更新'

  // new Promise(resolve => resolve()).then(() => {
  Promise.resolve().then(() => {
    console.log(this.$el.textContent) // => '已更新'
  })
}
  • setTimeout(宏任务)
updateMessage() {
  this.message = '已更新'
  console.log(this.$el.textContent) // => '修改数据'  '未更新'

  setTimeout(() => {
    console.log(this.$el.textContent) // => '修改数据'  '已更新'
  }, 0);
}

  所以,其实$nextTick(cb)有两种使用方式:

<script>
export default {
  mounted() {
    // 1、常规使用方式
    this.$nextTick(() => {
      // 在这里访问更新后的 DOM
      const myDiv = this.$refs[xxx]
    })

    // 2、在浏览器支持 Promise 的前提下, $nextTick() 返回一个 Promise 对象,所以你还可以这样写
    this.$nextTick().then(() => {
      const myDiv = this.$refs[xxx]
    })
    // 或者使用async/await
  }
}
</script>

思考

可以用周期函数 updated(),来代替 $nextTick() 吗?

# 3、应用:输入框

输入框的按需展示(焦点事件 focus、blur)

<div id="app">
  <!-- 显示、隐藏 -->
  <input type="text" v-if="isInput" @blur="buttonShow" ref="inputDOM" />

  <button v-else @click="inputShow">展示输入框</button>
</div>

<script type="text/JavaScript">
  new Vue({
    el: '#app',
    data() {
      return {
        isInput: false
      }
    },
    methods: {
      // 显示input框
      inputShow() {
        this.isInput = true

        // input框显示后✨才能获取焦点
        this.$nextTick(() => {
          this.$refs.inputDOM.focus()
        })
      },

      //显示button按钮
      buttonShow() {
        this.isInput = false
      }
    }
  });
</script>

# 4、应用:弹窗

  当我们使用 Dialog 对话框嵌套 Form 表单时,就会出现一个典型的应用:表单的数据回显

el-from 与 el-dialog(编辑/修改) 的数据回显

数据回显解决案例 (opens new window)

# 注意事项 1:

resetFields() 方法用于 formData 重置为初始值并移除校验结果
clearValidate() 方法用于清除表单验证状态

  • 如果在<el-form>标签上利用 ref="dialogFrom"属性使用了 resetFields()方法,那么和存在:rules="rules" 一样,每一个 <el-form-item>标签 ✍ 上都要加上 prop 属性

# 注意事项 2:

:before-close 是 Dialog 关闭前的回调函数,可以通过返回值控制 Dialog 是否关闭
@close 是 Dialog 关闭后的回调函数,它是在 Dialog 关闭之后执行

  在处理 Dialog 对话框的关闭弹窗时的this.$refs['dialogFrom'].resetFields()让表单回到原点的操作:

  • 如果采用:before-close,它仅当用户通过点击关闭图标或遮罩关闭 Dialog 时起效
  • 如果采用 ✍@close 则对所有的关闭弹窗操作有效。

知识回顾

  getFieldsValue() 方法在 Element UI 2.0 版本及之后的版本中已被删除(但我们可以访问表单组件实例的 model 属性代替this.$refs.dialogFrom.model

# 三、activated / deactivated 钩子

  Vue.js 中组件的生命周期钩子函数 activated 和 deactivated ,处于 mounted 和 destroyed 钩子函数之间,用于处理组件被激活和停用时的逻辑。

  为什么 activated 和 deactivated 没有在生命周期使示意图中显示?

答案

  因为,这两个钩子函数只有在使用了 <keep-alive> 组件进行缓存时才会被调用,而普通的组件切换不会触发这两个钩子函数。

<script>
  export default {
    // 离开Cart组件时,它会被缓存(同时自动触发组件的 deactivated生命周期函数)
    deactivated() {
      console.log('Cart组件被缓存')
    },

    // 回到Cart组件时,它会被激活(同时自动触发组件的 activated生命周期函数)
    activated() {
      console.log('Cart组件被激活')
    }
  }
</script>
更新于 : 8/7/2024, 2:16:31 PM