Vue生命周期钩子和异步编程仅此一篇即刻入门

Vue生命周期钩子和异步编程

Vue生命周期分为4部分:

  1. 创建(Initialization)
    • beforeCreate
    • created
  2. 挂载(DOM Insertion)
    • onBeforeMount
    • onMounted
  3. 更新(Data Changes)
    • onBeforeUpdate
    • onUpdated
  4. 销毁(Teardown)
    • onBeforeUnmount
    • onUnmounted

但是,在组合式API中,因为使用setup()函数的缘故,创建这一生命周期钩子不再独立存在

setup()函数作为在beforeCreate之后、created之前运行的函数,取代了原先创建生命周期的功能

实现了Vue 3 组合式 API 设计的核心理念之一:将代码按逻辑功能而非生命周期阶段组织

作为新手,你肯定觉得了解这些有什么作用,完全不是我要处理的范畴,实际上,了解生命周期对于一个Vue的项目数据流向逻辑至关重要

案例1:浏览器控制台误判

<script setup>
const messages = ref([])
console.log('初始化:',JSON.parse(JSON.stringify(messages.value)))
onMounted(() => {
    messages.value.push(
        {content: '你无敌了孩子'}
    );
    console.log('挂载后:',JSON.parse(JSON.stringify(messages.value)))
})
</script>

你如果将JSON.parse(JSON.stringify(messages.value))修改为messages.value时,你会发现,这两个结果居然是一样的

这是因为浏览器中的console使用的proxy指向,这就导致了使用push实际上推送了数据但是没有修改索引,那么理所当然的二者显示的是同样的内容了

所以使用JSON.parse(JSON.stringify(messages.value))输出字符串作为快照保存是一种合理的手段

案例2:如果再多一个console.log()

<script setup>
const messages = ref([])
console.log('初始化:',JSON.parse(JSON.stringify(messages.value)))
onMounted(() => {
    messages.value.push(
        {content: '你无敌了孩子'}
    );
    console.log('挂载后:',JSON.parse(JSON.stringify(messages.value)))
})
console.log('New:',JSON.parse(JSON.stringify(messages.value)))
</script>

这一次,我们在onMounted后再加上一个console.log('New:',JSON.parse(JSON.stringify(messages.value)))会发生什么呢

让我们想一想,如果按照书写顺序执行完onMounted后,messages.value已经是{content: '你无敌了孩子'},那么理所当然新加的console输出内容也是一样的

但是,再看看Vue生命周期和setup()函数,你会发现onMounted应当在创建周期之后,而setup()函数正好是组合式API中跨越创建周期的函数,那么实际上console.log('New:',JSON.parse(JSON.stringify(messages.value)))是在onMounted之前就执行的

所以正确的结果应该是空数组[]

这里,我们需要明白setup()函数实际上就是一个同步函数,所有内部操作都是顺序执行,那你会问了:这个onMounted()按顺序执行不应该在console.log('New:',JSON.parse(JSON.stringify(messages.value)))执行吗?对的,确实在这个新加日志之前执行,但是onMounted()实际上是一个生命周期钩子注册函数,也就是说使用onMounted()只是声明我要使用这个钩子,但这个钩子只有到属于其生命周期的环节才执行

这一部分可以去了解回调函数的概念

最后的结果顺序其实是:‘初始化’、‘New’、‘挂载后’

但是说了这么多,好像没有看出来生命周期钩子跟异步编程有啥关系啊,你这上面的内容也没用到异步编程啊

有的,有的,兄弟,像这样的案例我还有10个!

案例3:顶层异步操作

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

async function pauseOperation() {
  await delay(2000); // 挂起2秒
}

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const messages = ref([{content:'你来打我啊'}])
console.log('初始化:', JSON.parse(JSON.stringify(messages.value)))
onMounted(() => {
  messages.value.push(
      {content: '你无敌了孩子'}
  );
  console.log('挂载后:', JSON.parse(JSON.stringify(messages.value)))
})
console.log('New:', JSON.parse(JSON.stringify(messages.value)))
await pauseOperation()
</script>

如果真这么做了,会报错[Vue warn]: Component <Anonymous>: setup function returned a promise, but no <Suspense> boundary was found in the parent component tree. A component with async setup() must be nested in a <Suspense> in order to be rendered.

这是提示你,你让同步函数setup()进行了Promise的异步操作,通常情况下是不被允许的,如果有需要,需要保证setup()执行内容被<Suspense>标签包括

关于异步setup()的使用请读者自行搜索

你肯定想问,这都啥跟啥啊

但其实这里就已经涉及到你应用设计的逻辑了,通常情况下,我们推荐是将组件渲染出来再向里面填充数据,这是我们就可以异步获取数据的同时不必阻塞主线程(渲染流程)

比如我们可以将onMounted修改为异步函数:如下

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

async function pauseOperation() {
  await delay(2000); // 挂起2秒
}

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const messages = ref([{content:'你来打我啊'}])
console.log('初始化:', JSON.parse(JSON.stringify(messages.value)))
onMounted(async () => {
  await pauseOperation()
  messages.value.push(
      {content: '你无敌了孩子'}
  );
  console.log('挂载后:', JSON.parse(JSON.stringify(messages.value)))
})
console.log('New:', JSON.parse(JSON.stringify(messages.value)))
</script>

这样,你就可以看到组件正常渲染,优先显示出'你来打我啊',经过pauseOperation()挂起2s后,追加'你无敌了孩子'的显示

关于生命周期钩子还有更多内容等待读者自己去尝试发掘,这里笔者只是给出一点概念性的小问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值