vue3的生命周期、computed和watch、watcheffect

1,756 阅读5分钟

一、前言

大家好,我是地霊殿__三無,兜兜转转,总算略有空闲,可以静下来学习学习vue3的相关知识了,今天整点hook函数,在学习之余记录一下,加深印象。

二、生命周期

vue3和vue2的生命周期基本相同,不一样的地方在于vue3用setup代替了beforeCreate和created。

且vue3的生命周期都是按需加载的,且是在原有的命名基础前增加on,setup除外。

vue2vue3
beforeCreatesetup
createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered

整体的生命周期还是可以沿用vue2.x的思想的,将beforeCreate和created修改为setup即可。

image.png

写法也与vue2.x有所不同,需要引入。

在setup函数或者setup标签里填写,下面用setup标签的写法。

<script setup lang="ts">

import { onBeforeMount, onMounted } from 'vue'
onBeforeMount(() => {
  console.log('------onBeforeMount', 1)
})
onMounted(() => {
  console.log('------onMounted', 2)
})
</script>

三、监听函数

1、watch

惰性监听,作用和vue2.x提供的this.$watch相同。

vue3的watch监听的对象得是响应式的对象,即reactive或者ref定义的变量。

监听单一源

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

// 直接侦听一个ref
const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

数组形式监听多个源,试了一下,好像没发现监听前的值,几个都是修改后的值了。

const state = reactive({ count: 0 })
const state2 = reactive({ count: 3 })
watch([state, state2], ([stateNow, state2Now], [prevState, prevState2]) => {
  console.log('改变后的值', stateNow, state2Now)
  console.log('改变后的值', prevState, prevState2)
  console.log('监听对象', state, state2)
})

image.png

2、watcheffect

为什么有了watch,还需要有watcheffect呢,因为watch是惰性的,watch监听一个或者多个 特定 的响应性属性,仅当这些依赖项更改时才会触发,而且需要immediate为true,才会在组件创建后开始监听;而watchEffect是监听函数里的 任何 依赖项,默认就是在创建组件之后就开始监听,当监听函数里的任何依赖项发生更改时,就会触发。

watcheffect注重的是过程!!!

2.1 常规使用

我们想监听下state的count值,顺便看一下几个监听函数(在上一小节已经写了)的触发顺序。

import { onBeforeMount, onMounted, reactive, watch, watchEffect } from 'vue'
watchEffect(() => {
  console.log('顺序2', 'watchEffect监听', state.count)
})

可以看到,watch监听单个源的,触发的最快,其次是watchEffect,最后才是watch监听多个源。

image.png

2.2 在watcheffect中使用异步函数会来副作用,如何应对

在watchEffect里使用异步函数,经常会发生以下的场景:

监听的依赖项发生变化了,watchEffect被触发,然后调用了异步函数,结果异步函数还没触发,依赖项又变化了,那异步函数又被执行了一次,就结果而言,我们应该是只需要最新的那次就可以了,结果这里白白浪费了一次异步函数的执行资源。

这就需要我们的onInvalidate参数了。

onInvalidate 执行时间:

  • 副作用即将重新执行时(监听对象改变时)
  • 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)

首先我们利用定时器创建一个异步函数change

let timer: NodeJS.Timeout|undefined
const change = async (Num: number) => {
  timer = await setTimeout(() => {
    console.log(Num)
  }, 1000)
}

然后完善一下我们的watchEffect函数,添加一个onInvalidate,执行时机写在上面了,我们可以在onInvalidate函数内,去判断异步函数执行没,没执行,就手动取消上一次的定时器,这样只会执行最后一次的定时器。

watchEffect(onInvalidate => {
  console.log('顺序2', 'watchEffect监听', state.count)
  const tmp = change(state.count)
  onInvalidate(() => {
    console.log('执行了onInvalidate')
    if(timer) {
      clearTimeout(timer)
    }
  })
})

2.3 watchEffect的第二个参数---极少用到

副作用刷新时机 flush 一般使用post

presyncpost
更新时机组件更新前执行强制效果始终同步触发组件更新后执行

写法如下:

watchEffect(onInvalidate => {
  console.log('顺序2', 'watchEffect监听', state.count)
  const tmp = change(state.count)
  console.log(tmp)
  onInvalidate(() => {
    // tmp.cancel()
    console.log('执行了onInvalidate')
    if(timer) {
      clearTimeout(timer)
    }
  })
}, {
  flush: 'post',
  onTrigger () {
    console.log('触发ontrigger')
  }
})

即使是post,onTrigger的触发时间也是最前的,之后是onInvalidate,才到watchEffect的监听逻辑

image.png

2.4 如何手动停止调用

比如我们监听对象的值大于10之后,我们就不打算监听了,那就得手动调用一次

写法如下

change函数里添加逻辑,大于10就调用stop

const change = async (Num: number) => {
  timer = await setTimeout(() => {
    console.log(Num)
    if(Num > 10) {
      console.log('调用stop函数')
      stop()
    }
  }, 1000)
}

stop函数就是我们的watchEffect

const stop = watchEffect(onInvalidate => {
  和上述的函数内容一致,就不重复混字数了  
)

调用之后,watchEffect就失效了,不会再监听了(组件销毁时,也是这种逻辑,不过是自动调用而已)

3、computed

computed我们是相当熟悉了,就简单过一下。

ref和computed在import之后,我们用一段代码来讲讲触发过程。

demoCount就是我们的计算值,它的值是count变量的值+10,一开始,当settimeout运行后,对demoCount进行赋值操作,这并不能直接修改demoCount的值,而是会触发set方法,此时的count = 100 - 30 = 70,然后是触发get方法,返回了80 (=70+10)这一结果。

const count = ref(0)
const demoCount = computed({
  get: () => {
    return count.value + 10
  },
  set: (param) => {
    count.value = param - 30
  }
})
setTimeout(() => {
  demoCount.value = 100
})

四、小结

本次对vue3的生命周期和各类监听api进行了一次小结,我个人觉得,用的多的还是watch,其次是computed,最后是watchEffect。不过多了解一些总归是没毛病的。

ps: 我是地霊殿__三無,最近没状态,兜兜转转才水出一篇。

Snipaste_2022-07-19_15-30-26.jpg