本文是我在学习vue3生命周期时的一些学习笔记,用于复习巩固和知识积累。文章内容和举例一步步推进!可以放心选择!
首先,我们先通过一个例子来引入生命周期的概念。
例子:实现文本透明度的高低交替过渡变化
透明度的值1不能变 变化的东西交给vue
<h2 :style="{opacity:opacity}">abc<h2>
// 这个写法是错误的
// 需要注意的是,动态绑定样式得用冒号,引号里面的得写成对象(用{}
包裹)
{opacity:opacity}
前一个是css数据的属性,第二个是数据的名字。他俩重名,可以写成{opacity}
。
不推荐的实现方法(循环定时器在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,其实收到它的意义并不大。
要对vue实例进行一些处理,尽可能在内部就消化了。如果想对vue进行全局的操作,那就使用Vue.
。在真正开发时很少用到vm接收,避免麻烦和内存占用。
因此,我们对代码进行第二次修改,主要将定时器想办法写在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>
window.onload = () =>{
vm.change()
}
// 同样可以实现效果 可是这样不就写回去了吗
{{change()}}
的调用是错误的change调用之后确实开启了一个定时器,但定时器里面在干嘛,它在修改opacity。vue给你了一个承诺,只要你敢修改date里的数据,vue就会帮你重新解析模板,这不是又要调用change,change又会更改opacity,这样一来,直接给死循环了,这就会导致指数大爆炸式的定时器开启。
你把change写在methods里面只有两种做法,第一种是作为事件的回调去使用,在这里显然是不可以的,因为不符合要求;第二种是把change作模板里的插值语法,你亲自去调用。这种方式显然也不能实现。因此,就这个例子来说,change写在methods里面是不太好的。
下面,我们做出进一步修改。保留methods的结构,一会演示一个问题。
首先,我们要了解一下vue将代码转化为显示效果并显示到相应页面上的过程。
读取需要显示的元素(比如标签内容)
将读取的内容解析,转化为虚拟DOM
将虚拟DOM转化为真实DOM
最后将真实DOM放到页面
这个时候,我们应该能够想到,当vue的这个过程进行到最后一个过程即真实DOM准备就绪,下一步就是将真实DOM显示在页面上的时候,也正是这个时候,开启定时器。
当然,你会疑问怎么实现上述操作。vue在完成最后一步操作后,会调一个特殊的函数。这个函数里面就可以编写刚刚设想的逻辑。
这个函数很特殊,特殊在哪呢?首先,它压根不写在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)
}
}
})
此时,你再运行会发现,效果完美地实现了!
所谓的生命周期就是指这些特殊的函数。
onMounted其实只是一个简单的函数,只不过vue在一个关键的时刻帮我们调用了它。即关键性的时刻做了关键的事。
其实整个生命周期中还调用了很多函数。vm也就是vue实例在执行的时候是分为很多步骤的,我们看到的目前只有onMounted,在调用onMounted之前还调用了几个函数(后面的文章会有介绍),这几个函数加上最重要的生命周期函数,就构成了生命周期函数。
为什么叫它生命周期函数呢,因为在整个vm干活的过程中,特殊的时间点帮你去调用的。
还有一个细节,我们会发现,直接使用this时它的指向直接就是vm,这是为什么呢?
首先,定时器使用的是箭头函数,它得往外找,往外找就找到了onMounted函数。
很给力的是,在你调onMounted函数的时候,vue就已经帮你把this的指向维护好了。
vm的生命周期,就是在关键的时刻调用关键的函数(生命周期函数)。
你只需要准备好生命周期函数,剩余的调用就像钩子一样,vue在合适的时候帮你把它钩出来使用。
vue在关键时刻帮我们调用一些特殊名称的函数。
生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
生命周期函数的this指向是vm或组件实例对象。
注册周期钩子
举例来说,onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码:
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
console.log(`the component is now mounted.`)
})
</script>
还有其他一些钩子,会在实例生命周期的不同阶段被调用,最常用的是 onMounted
、onUpdated
和 onUnmounted
。
当调用 onMounted
时,Vue 会自动将回调函数注册到当前正被初始化的组件实例上。这意味着这些钩子应当在组件初始化时被同步注册。
例如,请不要这样做:
setTimeout(() => {
onMounted(() => {
// 异步注册时当前组件实例已丢失
// 这将不会正常工作
})
}, 100)
注意这并不意味着对onMounted
的调用必须放在 setup()
或 内的词法上下文中。
onMounted()
也可以在一个外部函数中调用,只要调用栈是同步的,且最终起源自 setup()
就可以。
注意:所有罗列在本页的 API 都应该在组件的 setup() 阶段被同步调用。
注册一个回调函数,在组件挂载完成后执行。
function onMounted(callback: () => void): void
组件在以下情况下被视为已挂载:
其所有同步子组件都已经被挂载 (不包含异步组件或
树内的组件)。
其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。
这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用,或是在服务端渲染应用中用于确保 DOM 相关代码仅在客户端执行。
这个钩子在服务器端渲染期间不会被调用。
通过模板引用访问一个元素:
<script setup>
import { ref, onMounted } from 'vue'
const el = ref()
onMounted(() => {
el.value //
})
</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中生命周期的引入部分,包含相关基础知识和实例分析,语言比较浅显易懂,都是由本人结合学习视频和相关资料归纳总结得出。难免会有不足和错误的地方,希望大家发现后及时告知我,我会第一时间做出更改并更新。后续还会对生命周期的知识进行整理!