vue3 core之生命周期 引入篇

  • 1 文章简介
  • 2 生命周期主要内容
    • 2.1 引出生命周期
      • 2.1.1 例子引入
        • 2.1.1.1 不推荐的实现方法(定时器在vue外面)
        • 2.1.1.2 推荐使用的实现方式(定时器在vue里面)
      • 2.1.2 vue实例的执行过程
      • 2.1.3 挂载概念的引入
      • 2.1.4 生命周期函数
        • 2.1.4.1 生命周期回调函数
        • 2.1.4.2 生命周期钩子(程序员内常用语言)
      • 2.1.5 生命钩子扩展【了解】
        • onMounted()
        • onUpdated()
        • onUnmounted()
        • onBeforeMount()
        • onBeforeUpdate()
        • onBeforeUnmount()
        • onErrorCaptured()
  • 下期预告
    • 2.2 生命周期的挂载流程
    • 2.3 生命周期更新流程
    • 2.4 生命周期销毁流程
    • 2.5 生命周期总结
  • summary

1 文章简介


本文是我在学习vue3生命周期时的一些学习笔记,用于复习巩固和知识积累。文章内容和举例一步步推进!可以放心选择!

2 生命周期主要内容

2.1 引出生命周期

2.1.1 例子引入

首先,我们先通过一个例子来引入生命周期的概念。

例子:实现文本透明度的高低交替过渡变化

效果如图:

透明度的值1不能变 变化的东西交给vue

<h2 :style="{opacity:opacity}">abc<h2>

// 这个写法是错误的

// 需要注意的是,动态绑定样式得用冒号,引号里面的得写成对象(用{}包裹)

{opacity:opacity}前一个是css数据的属性,第二个是数据的名字。他俩重名,可以写成{opacity}

2.1.1.1 不推荐的实现方法(定时器在vue外面)

不推荐的实现方法(循环定时器在new vue外面 使用箭头函数 )
(这里的数字设定决定动画过渡效果的舒服与否)

代码如下:

<script>
Vue.config.productionTip = flase //阻止vue在启动时生成生产提示
new Vue({
    el:'#root',
    data:{
    opacity:1;
}
})
// 通过外在定时器实现
setInterval(() => {
    opacity -= 0.01 
},16)
</script>

但此时的代码并不能实现效果,因为我们触碰不到opacity,所以第一次修改(修改变量接收触碰 和 定时器对象变化范围限制)后的代码如下:

<script>
Vue.config.productionTip = flase //阻止vue在启动时生成生产提示

// 只能令一个vm等于vue
const vm = new Vue({
    el:'#root',
    data:{
    opacity:1;
}
})
// 通过外在定时器实现
setInterval(() => {
    // 然后实现触碰vue中的vm的透明度
    vm.opacity -= 0.01 
    // vm.opacity不能一直减小 必须要有限制
    if(vm.opacity <= 0)
       vm.opacity = 1
},16)
<script>

此时我们会发现,这样编写程序不是很好。

  • 首先是代码逻辑的不足

红色框里的部分是vue实例,绿色框是具体逻辑。最主要的是这个逻辑还在操作vue实例里面的数据

从功能上来看,二者是扭在一起的,但是从代码逻辑来看,二者是分开的。我们追求的是,当你把vue示例折叠起来时,所有你用到的数据、方法等等就都带走了,不至于说往外面甩逻辑。

注意:这里也不并不是说有了new vue之后外面就不能写东西了,并不是这样。只是说最好把逻辑能够写在vue示例里面。

  • 其次是vm使用的不足

这里创建的vm,其实收到它的意义并不大。

要对vue实例进行一些处理,尽可能在内部就消化了。如果想对vue进行全局的操作,那就使用Vue.。在真正开发时很少用到vm接收,避免麻烦和内存占用。

因此,我们对代码进行第二次修改,主要将定时器想办法写在vue实例里面。

2.1.1.2 推荐使用的实现方式(定时器在vue里面)

推荐使用的实现方式(循环定时器在vue里面 用普通函数包裹箭头函数)

new Vue({
    el:'#root',
    data:{
    opacity:1;
},
methods:{
    change(){
      setInterval(() => {
      this.opacity -= 0.01 
      if(this.opacity <= 0)
         this.opacity = 1
      },16)
    }
  },
})

但是你会发现,效果显示不出来。原因是change()函数没有被调用。

  • 接下来你可以能会犯一个非常可怕的错误。

错误代码实例:(错误为{{change()}})

<body>
    <div id="root">
      <h2 :style="{opacity}">欢迎学习Vue</h2>
    {{change()}}
    </div>
</body>
<script>
new Vue({
    el:'#root',
    data:{
    opacity:1;
},
methods:{
    change(){
      setInterval(() => {
      this.opacity -= 0.01 
      if(this.opacity <= 0)
         this.opacity = 1
      },16)
    }
  },
})
</script>
  • 你可能会想到js原生的window窗口事件
window.onload = () =>{
 vm.change()
}
// 同样可以实现效果 可是这样不就写回去了吗
  • 你也可能会想到写一个button,通过鼠标点击或者鼠标滑过实现效果,但是要求是你打开网页,效果会自己出来,而不是你人为控制。
  • 再说说为什么{{change()}}的调用是错误的

change调用之后确实开启了一个定时器,但定时器里面在干嘛,它在修改opacity。vue给你了一个承诺,只要你敢修改date里的数据,vue就会帮你重新解析模板,这不是又要调用change,change又会更改opacity,这样一来,直接给死循环了,这就会导致指数大爆炸式的定时器开启。

你把change写在methods里面只有两种做法,第一种是作为事件的回调去使用,在这里显然是不可以的,因为不符合要求;第二种是把change作模板里的插值语法,你亲自去调用。这种方式显然也不能实现。因此,就这个例子来说,change写在methods里面是不太好的。

下面,我们做出进一步修改。保留methods的结构,一会演示一个问题。

2.1.2 vue实例的执行过程

首先,我们要了解一下vue将代码转化为显示效果并显示到相应页面上的过程。

  • 读取需要显示的元素(比如<h2>标签内容)

  • 将读取的内容解析,转化为虚拟DOM

  • 将虚拟DOM转化为真实DOM

  • 最后将真实DOM放到页面

这个时候,我们应该能够想到,当vue的这个过程进行到最后一个过程即真实DOM准备就绪,下一步就是将真实DOM显示在页面上的时候,也正是这个时候,开启定时器。

当然,你会疑问怎么实现上述操作。vue在完成最后一步操作后,会调一个特殊的函数。这个函数里面就可以编写刚刚设想的逻辑。

2.1.3 挂载概念的引入

这个函数很特殊,特殊在哪呢?首先,它压根不写在methods里面,而是跟methods平级,它的函数名为onMounted(在vue2中叫mounted)。mount本身有挂载的意思,mounted则是挂载完毕的意思。在代码中则表示,vue找到了这段模板,开始解析变成虚拟DOM,然后转成真实DOM,然后放入页面当中,完成整个过程。这个“放入”说的官方一点,其实就是挂载

那么onMounted什么时候调用,我们现在应该能够猜到了——vue完成模板的解析并把初始的(一上来第一次解析的)真实的 DOM元素放入页面后(挂在完毕)调用onMounted,并且onMounted在整个vm的工作过程中,只会调用一次。

所以我们现在带着这个逻辑开始修改。

// vue完成模板的解析并把**初始的**(一上来第一次解析的)真实的 DOM元素放入页面后(挂在完毕)调用

new Vue({
    el:'#root',
    data:{
    opacity:1;
},

methods:{
},
onMounted(){
    setInterval(() => {
       this.opacity -= 0.01 
       if(this.opacity <= 0)
          this.opacity = 1
      },16)
    }
  }
})

此时,你再运行会发现,效果完美地实现了!

2.1.4 生命周期函数

所谓的生命周期就是指这些特殊的函数。

onMounted其实只是一个简单的函数,只不过vue在一个关键的时刻帮我们调用了它。即关键性的时刻做了关键的事。

其实整个生命周期中还调用了很多函数。vm也就是vue实例在执行的时候是分为很多步骤的,我们看到的目前只有onMounted,在调用onMounted之前还调用了几个函数(后面的文章会有介绍),这几个函数加上最重要的生命周期函数,就构成了生命周期函数。

为什么叫它生命周期函数呢,因为在整个vm干活的过程中,特殊的时间点帮你去调用的。

还有一个细节,我们会发现,直接使用this时它的指向直接就是vm,这是为什么呢?

  • 首先,定时器使用的是箭头函数,它得往外找,往外找就找到了onMounted函数。

  • 很给力的是,在你调onMounted函数的时候,vue就已经帮你把this的指向维护好了。

vm的生命周期,就是在关键的时刻调用关键的函数(生命周期函数)。

  • 生命周期又称生命周期回调函数、生命周期函数、生命周期钩子。

2.1.4.1 生命周期回调函数

  • 如何解释回调?
    你写的onMounted函数,你调用了吗?没有。但是它执行了吗?执行了。所以其实是回调在发挥作用。

2.1.4.2 生命周期钩子(程序员内常用语言)

你只需要准备好生命周期函数,剩余的调用就像钩子一样,vue在合适的时候帮你把它钩出来使用。

  • vue在关键时刻帮我们调用一些特殊名称的函数。

  • 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。

  • 生命周期函数的this指向是vm或组件实例对象。

注册周期钩子

举例来说,onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码:

<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  console.log(`the component is now mounted.`)
})
</script>

还有其他一些钩子,会在实例生命周期的不同阶段被调用,最常用的是 onMountedonUpdatedonUnmounted
当调用 onMounted 时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册。
例如,请不要这样做:

setTimeout(() => {
  onMounted(() => {
    // 异步注册时当前组件实例已丢失
    // 这将不会正常工作
  })
}, 100)

注意这并不意味着对onMounted 的调用必须放在 setup()<script setup> 内的词法上下文中。onMounted() 也可以在一个外部函数中调用,只要调用栈是同步的,且最终起源自 setup() 就可以。

2.1.5 生命钩子扩展【了解】

注意:所有罗列在本页的 API 都应该在组件的 setup() 阶段被同步调用。

onMounted()

注册一个回调函数,在组件挂载完成后执行。

  • 类型
function onMounted(callback: () => void): void
  • 详细信息

组件在以下情况下被视为已挂载:

  • 其所有同步子组件都已经被挂载 (不包含异步组件或 <Suspense> 树内的组件)。

  • 其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。

这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用,或是在服务端渲染应用中用于确保 DOM 相关代码仅在客户端执行。

这个钩子在服务器端渲染期间不会被调用。

  • 示例

通过模板引用访问一个元素:

<script setup>
import { ref, onMounted } from 'vue'

const el = ref()

onMounted(() => {
  el.value // <div>
})
</script>

<template>
  <div ref="el"></div>
</template>

onUpdated()

注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

  • 类型
function onUpdated(callback: () => void): void
  • 详细信息

父组件的更新钩子将在其子组件的更新钩子之后调用。

这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。

这个钩子在服务器端渲染期间不会被调用。

不要在 updated 钩子中更改组件的状态,这可能会导致无限的更新循环!

  • 示例

访问更新后的 DOM

<script setup>
import { ref, onUpdated } from 'vue'

const count = ref(0)

onUpdated(() => {
  // 文本内容应该与当前的 `count.value` 一致
  console.log(document.getElementById('count').textContent)
})
</script>

<template>
  <button id="count" @click="count++">{{ count }}</button>
</template>

onUnmounted()

注册一个回调函数,在组件实例被卸载之后调用。

  • 类型
function onUnmounted(callback: () => void): void
  • 详细信息

一个组件在以下情况下被视为已卸载:

  • 其所有子组件都已经被卸载。

  • 所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止。

可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。

这个钩子在服务器端渲染期间不会被调用。

  • 示例
<script setup>
import { onMounted, onUnmounted } from 'vue'

let intervalId
onMounted(() => {
  intervalId = setInterval(() => {
    // ...
  })
})

onUnmounted(() => clearInterval(intervalId))
</script>

onBeforeMount()

注册一个钩子,在组件被挂载之前被调用。

  • 类型
function onBeforeMount(callback: () => void): void
  • 详细信息

当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。

这个钩子在服务器端渲染期间不会被调用。

onBeforeUpdate()

注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

  • 类型
function onBeforeUpdate(callback: () => void): void
  • 详细信息

这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。

这个钩子在服务器端渲染期间不会被调用。

onBeforeUnmount()

注册一个钩子,在组件实例被卸载之前调用。

  • 类型
function onBeforeUnmount(callback: () => void): void
  • 详细信息

当这个钩子被调用时,组件实例依然还保有全部的功能。

这个钩子在服务器端渲染期间不会被调用。

onErrorCaptured()

注册一个钩子,在捕获了后代组件传递的错误时调用。

  • 类型
function onErrorCaptured(callback: ErrorCapturedHook): void

type ErrorCapturedHook = (
  err: unknown,
  instance: ComponentPublicInstance | null,
  info: string
) => boolean | void
  • 详细信息

错误可以从以下几个来源中捕获:

  • 组件渲染
  • 事件处理器
  • 生命周期钩子
  • setup() 函数
  • 侦听器
  • 自定义指令钩子
  • 过渡钩子

这个钩子带有三个实参:错误对象、触发该错误的组件实例,以及一个说明错误来源类型的信息字符串。

你可以在 errorCaptured() 中更改组件状态来为用户显示一个错误状态。注意不要让错误状态再次渲染导致本次错误的内容,否则组件会陷入无限循环。

这个钩子可以通过返回 false 来阻止错误继续向上传递。请看下方的传递细节介绍。

错误传递规则

默认情况下,所有的错误都会被发送到应用级的 app.config.errorHandler (前提是这个函数已经定义),这样这些错误都能在一个统一的地方报告给分析服务。

  • 如果组件的继承链或组件链上存在多个 errorCaptured 钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生 DOM 事件的冒泡机制。

  • 如果 errorCaptured 钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到 app.config.errorHandler

  • errorCaptured 钩子可以通过返回 false 来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的 errorCaptured 钩子或 app.config.errorHandler 因这个错误而被调用。

下期预告

2.2 生命周期的挂载流程


下期将对此图进行详细说明和解释,一起期待一下吧!

2.3 生命周期更新流程

2.4 生命周期销毁流程

2.5 生命周期总结

summary

本文是一些关于vue3中生命周期的引入部分,包含相关基础知识和实例分析,语言比较浅显易懂,都是由本人结合学习视频和相关资料归纳总结得出。难免会有不足和错误的地方,希望大家发现后及时告知我,我会第一时间做出更改并更新。后续还会对生命周期的知识进行整理!

更多推荐

vue3 生命周期函数 引入篇