Vue3.0 | 快速上手Compisition API新特性(下)

992 阅读5分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

⏳前言

上一篇文章Vue3.0 | 快速上手Compisition API新特性(上)讲到了setupreactiveref等新特性......这篇文章将学习Vue3.0的生命周期watchcomputed做一个笔记。

⏰ Vue3生命周期

Vue3中的生命周期钩子函数被命名为了onXXX的形式,并且只能同步地(synchronously)在setup函数中执行。下面介绍几个常用的生命周期钩子函数:

Vue2和Vue3.0中生命周期的映射关系

  • beforeCreate -> use setup()
  • created -> use setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeUnmount -> onBeforeUnmount
  • unmounted -> onUnmounted
  • errorCaptured -> onErrorCaptured
  • renderTracked -> onRenderTracked
  • renderTriggered -> onRenderTriggered
  • activated -> onActivated
  • deactivated -> onDeactivated

beforeCreate、created

在Vue3中,beforeCreate和created与setup几乎同时执行,因此在Vue3中setup代替了beforeCreatecreated,也就是说它们如今都不需要额外声明,只需要将它们其中的代码都在setup中编写即可。

onMounted

在页面被加载完成时执行,这个时候组件实例已经挂载完成,通常在这个时候可以做一些依赖 DOM 的初始化操作,例如我们可以在登陆页面加载完成后自动聚焦登录框:

onMounted() {
    if (this.loginForm.username === '') {
      this.$refs.username.focus()
    } else if (this.loginForm.password === '') {
      this.$refs.password.focus()
    }
}

onUnmounted

onBeforeUnmount在组件被卸载前执行,而onMount在组件被卸载完成时执行,通常在这个时候可以清理自身组件的一些方法、逻辑、递归销毁子组件,例如我们在组件被销毁时需要主动移除组件的事件监听器:

onMounted(() => {
  document.addEventListener(‘click’,onMounts);
})
onUnmounted(() => {
  document.removeEventListener(‘click’,onMounts);
})

onUpdated

在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用,如果需要监听特定数据的改变并执行某些逻辑,最好不要使用 onUpdated 钩子函数而使用computed 或 watch,因为任何数据的变化导致的组件更新都会执行 onUpdated 钩子函数。

注意: 不要在onUpdated钩子中做更新数据的操作,会导致无限套娃的...

export default {
  name: "App",
  setup() {
    onUpdated(() => {
      console.log('updated')
    })
  }
};

onRenderTrigger

在数据更新时执行,接收一个参数event,其中包含数据更新前的值oldValue和更新后的值newValue

image.png

🔍 监听器watch

Vue3官方文档指出vue3版本的watch与2.0版本watch的功能是相等的(exact equivalent)

watch函数接收两个参数,第一个参数为需要监听的数据,也可以直接是一个ref。 第二个参数是一个回调函数callback,它有两个参数:数据更改后的值newValueoldValue ,只要监听对象发生改变,这个回调函数就会执行

watch(() => data.count, (count, preCount) => {
  console.log('newValue', count);
  console.log('oldValue', preCount);
})

image.png

监听多个对象

watchEffect

watchEffect接收传入的一个函数作为参数,并且会立即执行它,watchEffect会自动响应式追踪其依赖,并在其依赖变更时重新运行该函数。

export default {
  name: "App",
  setup() {
    const count = ref(0)

    setTimeout(() => {
      count.value = 1
    }, 2000)
    
    watchEffect(() => {
      console.log(count.value);
    })
  }
};

运行后可以看到watchEffect被立刻执行,打印出count的原始值0,过了2s后watchEffect再次执行并打印出count修改后的值1 image.png 当watchEffect在组件地setup函数或者是生命周期函数中调用,监听器watcher会自动连接到组件地生命周期,并且自动地在组件卸载时停止

watch和watchEffect的区别:

  1. 懒执行:watchEffect在传入回调时会立即执行,且在依赖发生变化时重新运行该函数,而watch的回调函数只有在监听对象发生改变时执行
  2. watch可以监听到数据修改前的值和修改后的值
  3. watchEffect自动收集依赖源,依赖源改变时自动执行自身,而watch必须显示指定(return)依赖源

小问题:watchEffect和onBeforeUpdate 谁先执行?

<script>
import { ref } from "@vue/reactivity";
import { onBeforeMount, onBeforeUpdate, onMounted, onUpdated, watch, watchEffect } from "vue";
export default {
  name: "App",
  setup() {
    const count = ref(0)
    console.log('setup');

    watch(() => count.value, () => {
      console.log('watch');
    })
    watchEffect(() => {
      const a = count.value
      console.log('watchEffect');
    })
    onMounted(() => {
      console.log('onMounted');
    })
    onBeforeMount(() => {
      console.log('before mount');
    })
    onBeforeUpdate(() => {
      console.log('before updated');
    })
    onUpdated(() => {
      console.log('updated');
    })

    setTimeout(() => {
      count.value = 1
    }, 2000)
    
    return {
      count
    }
  }
};
</script>

控制台打印顺序如下图:

image.png 可以看到,watchEffect会比onBeforeCreate先执行,这是因为watchEffect的第二个参数默认为{ flush: 'pre' },我们可以在第二个参数中传入配置选项{ flush: 'post' },这样一来onBeforeUpdate会先于watchEffect执行,但注意:onUpdated始终会在watchEffct后执行

onInvalidate 消除副作用

首先我们需要搞清楚副作用effecteffect functiononInvalidate 有些函数会产生一些副作用effect(例如ajax请求,DOM操作,操作localStorage,setTimeOut...),它们可能需要在失效时被及时清除

它会在watchEffect即将要执行时,或是在watchEffect被停止时执行,原因是onInvalidate需要在副作用执行之前清除它。在state已经被改变后,effect function执行前。

那么onInvalidate要在effect之前执行呢?原因是在副作用中可能会有await关键字,如果onInvalidate在它之后执行的话,副作用先于它执行,那么清除器并没有起作用,也就是清除器需要先行一步把副作用清除掉---用于拦截副作用

image.png

📱 Computed

计算属性computed在vue2.0是options,到了vue3.0后变成了API

接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式ref对象。

watchEffect函数返回的是一个停止函数stop

const stop = watchEffect(() => {
  console.log(count.value)
})
stop()    // 停止监听
<template>
  {{ sayHiToJay }}
</template>

export default {
  name: "App",
  setup() {
    const greetings = 'hi '
    const SayHiToJay = computed(() => greetings + 'Jay')
    
    return {
      SayHiToJay
    };
  }
};

image.png

或者,接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: val => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0

结言

这篇文章用于记录Vue3.0的学习过程,如果偏差错误欢迎提出指正!🎁