先来说说一些API的用法

setup script

这个语法糖真的是太爽了,想想之前vue2的那些写法,真的让人很恼火,自由度非常不够,setup script体验过的都说好别急,咱们继续

 1<script setup lang="ts">
 2// 在这里写vue的逻辑
 3</script>
 4<template>
 5  <div>哈哈</div>
 6</template>

ref & reactive & 事件

总结三点:

  • ref:需要响应式的常量,但是使用或者赋值时需要xxx.value

  • reactive:需要响应式的对象或者数组,可直接使用或赋值

  • 事件:在setup script中,直接定义事件,不需要像vue2那样在method中定义

 1<script setup lang="ts">
 2import { refreactive } from 'vue'
 3const enum Name {
 4  CN = '林三心',
 5  EN = 'Sunshine_Lin'
 6}
 7const enum Gender {
 8  MAN = '男',
 9  WOMAN = '女'
10}
11// 常量
12const name = ref(Name.CN)
13// 对象、数组
14const user = reactive({
15  nameName.CN,
16  genderGender.MAN
17})
18const arr = reactive<{ namestring }[]>([{ name'嘻嘻嘻' }])
19// 事件直接定义
20const switchName = () => {
21  // 赋值需要name.value
22  name.value = name.value === Name.CN ? Name.EN : Name.CN
23}
24// 事件直接定义
25const switchGender = () => {
26  // 直接赋值
27  user.gender = user.gender === Gender.MAN ? Gender.WOMAN : Gender.MAN
28}
29const handleAdd = () => {
30  arr.push({ name'哈哈哈' })
31}
32</script>
33<template>
34  <button @click="switchName">
35    我叫 {{name}}
36  </button>
37  <button @click="switchGender">
38    我叫 {{user.name}},我是 {{user.gender}}
39  </button>
40  <button @click="handleAdd">新增</button>
41  <ul>
42    <li v-for="(item, index) in arr" :key="index">{{ item.name }}-{{ index }}</li>
43  </ul>
44</template>

computed & watch & watchEffect

  • computed:计算函数

  • watch:监听函数,可监听常量和引用变量,可传immediatedeep可监听对象也可只监听对象的某个属性

  • watchEffect:跟watch差不多,但是watchEffect不需要说明监听谁,用到谁就监听谁(详情见下)

 1<script setup lang="ts">
 2import { ref
 3         reactive
 4         computed
 5         watch
 6         watchEffect } from 'vue'
 7const enum Name {
 8  CN = '林三心',
 9  EN = 'Sunshine_Lin'
10}
11const enum Gender {
12  MAN = '男',
13  WOMAN = '女'
14}
15const name = ref(Name.CN)
16const user = reactive({
17  nameName.CN,
18  genderGender.MAN
19})
20const switchName = () => {
21  name.value = name.value === Name.CN ? Name.EN : Name.CN
22}
23const switchGender = () => {
24  user.gender = user.gender === Gender.MAN ? Gender.WOMAN : Gender.MAN
25}
26// computed计算出userText
27const userText = computed(() => {
28  return `我叫${user.name},我是${user.gender}的`
29})
30// 监听常量name
31watch(name, (next, pre) => {
32  console.log('name被修改了'nextpre)
33})
34// 监听user对象
35watch(user, (next) => {
36  console.log('user被修改了'next)
37}{
38  immediatefalse// 首次执行
39  deeptrue // 深度监听对象
40})
41// 可监听对象的某个属性,这里监听user.gender
42watch(() => user.gender, (next, pre) => {
43  console.log('user.gender被修改了'nextpre)
44})
45// 不需要说明监听谁
46// 用到user.gender就监听user.gender
47watchEffect(() => {
48  const gender = user.gender
49  console.log('user.gender被修改了'gender)
50})
51</script>
52<template>
53  <button @click="switchName">修改name</button>
54  <button @click="switchGender">修改user.gender</button>
55  <div>{{ userText }}</div>
56</template>

生命周期

看一下vue2的生命周期在vue3中的表现

  • beforeCreate -> 没了

  • created -> 没了

  • beforeMount -> onBeforeMount

  • mounted -> onMounted

  • beforeUpdate -> onBeforeUpdate

  • updated -> onUpdated

  • beforeUnmount -> onBeforeUnmount

  • unmounted -> onUnmounted

  • activated -> onActivated

  • deactivated -> onDeactivated

  • errorCaptured -> onErrorCaptured

 1<script setup lang="ts">
 2import {
 3  onBeforeMount,
 4  onMounted,
 5  onUpdated,
 6  onBeforeUpdate,
 7  onBeforeUnmount,
 8  onUnmounted,
 9  onActivated,
10  onDeactivated,
11  onErrorCaptured
12} from 'vue'
13onBeforeMount(() => {
14  console.log('挂载前')
15})
16onMounted(() => {
17  console.log('挂载')
18})
19onBeforeUpdate(() => {
20  console.log('更新前')
21})
22onUpdated(() => {
23  console.log('更新')
24})
25onBeforeUnmount(() => {
26  console.log('销毁前')
27})
28onUnmounted(() => {
29  console.log('销毁')
30})
31onActivated(() => {
32  console.log('kee-alive激活本组件')
33})
34onDeactivated(() => {
35  console.log('kee-alive隐藏本组件')
36})
37onErrorCaptured(() => {
38  console.log('错误捕获')
39})
40</script>

defineProps & defineEmits

还记得之前vue2的父传子,子改父,的做法吗?使用porps、this.$emit

到了vue3,这种写法改了

我们来看看父组件

 1<script setup lang="ts">
 2import { ref } from 'vue'
 3import Dialog from './Dialog.vue'
 4const msg = ref('我是msg')
 5const changeMsg = (val: string) => {
 6  msg.value = val
 7}
 8</script>
 9<template>
10// 传进子组件
11<Dialog :msg="msg" @changeMsg="changeMsg" />
12</template>

再来看看子组件

 1<script setup lang="ts">
 2import { definePropsdefineEmits } from 'vue'
 3// 注册父传子的props
 4const { msg } = defineProps({
 5  msg: {
 6    typeString,
 7    requiredtrue
 8  }
 9})
10// 注册父传子的事件
11const emits = defineEmits(['changeMsg'])
12const handleClick = () => {
13  // 修改父组件的值
14  emits('changeMsg''修改msg')
15}
16</script>
17<template>
18  <div @click="handleClick">{{ msg }}</div>
19</template>

defineExpose

这个API主要主要作用是:将子组件的东西暴露给父组件,好让父组件可以使用

 1<!-- 子组件 -->
 2<script setup>
 3import { ref } from 'vue'
 4const msg = ref('hello vue3!')
 5function change(
 6
 7{
 8  msg.value = 'hi vue3!'
 9  console.log(msg.value)
10}
11// 属性或方法必须暴露出去,父组件才能使用
12defineExpose({ msgchange })
13</script>
 1<!-- 父组件 -->
 2<script setup>
 3import ChildView from './ChildView.vue'
 4import { refonMounted } from 'vue'
 5const child = ref(null)
 6onMounted(() => {
 7  console.log(child.value.msg// hello vue3!
 8  child.value.change() // hi vue3!
 9})
10</script>
11<template>
12  <ChildView ref="child"></ChildView>
13</template>

全局API

需要注意几个点:

  • 1、vue3已经没有filter这个全局方法了

  • 2、vue.component -> app.component

  • 3、vue.directive -> app.directive

  • 4、之前Vue.xxx现在都改成app.xxx

 1// 全局自定义指令
 2app.directive('focus', {
 3  mounted(el) {
 4    el.focus()
 5  }
 6})
 7// 全局自定义组件
 8import CustomComp from './components/CustomComp.vue'
 9app.component('CustomComp'CustomComp)

自定义指令

还记得vue2中的自定义指令吗?

 1Vue.directive('xxx', {
 2  // 指令绑定到指定节点,只执行一次
 3  bind() {},
 4  // 指定节点插入dom
 5  inserted() { },
 6  // 节点VNode更新时,可能刚更新,没完全更新
 7  update() {},
 8  // VNode完全更新
 9  componentUpdated() {},
10  // 指令从指定节点解绑,只执行一次
11  unbind() {}
12})

再来看看vue3的,比较贴近vue本身的生命周期

 1app.directive('xxx', {
 2    // 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用
 3    created() {},
 4    // 当指令第一次绑定到元素并且在挂载父组件之前调用
 5    beforeMount() {},
 6    // 在绑定元素的父组件被挂载后调用
 7    mounted() {},
 8    // 在更新包含组件的 VNode 之前调用
 9    beforeUpdate() {},
10    // 在包含组件的 VNode 及其子组件的 VNode 更新后调用
11    updated() {},
12    // 在卸载绑定元素的父组件之前调用
13    beforeUnmount() {},
14    // 当指令与元素解除绑定且父组件已卸载时, 只调用一次
15    unmounted() {},
16});

defineAysncCompoment & Suspense

这个API用来加载异步组件,异步组件的好处我想大家也知道,就是用不到他就不加载,用到了才会加载,这大大降低了首屏加载时间,是优化的重要手段

defineAysncCompoment需配合vue3的内置全局组件Suspense使用,需要用Suspense包住异步组件

 1const AsyncPopup = defineAsyncComponent({ 
 2  loader: () => import("./LoginPopup.vue"),
 3   // 加载异步组件时要使用的组件
 4  loadingComponentLoadingComponent,
 5   // 加载失败时要使用的组件
 6  errorComponentErrorComponent
 7  // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
 8  delay1000
 9  // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
10  // 默认值:Infinity(即永不超时,单位 ms)
11  timeout3000 
12})
13// 使用时,可控制显隐
14<Suspense v-if="show" >
15  <AsyncPopup />
16</Suspense>

Hooks

自定义hook——useUser

我们要知道Hooks的好处,其实它就是一个函数,好处有:

  • 复用性高

  • 减少代码量,提高可维护性

  • 相同逻辑可放一起

就比如刚刚我们修改user的那个例子,假如很多页面都需要这个逻辑,那岂不是每个地方都需要写一遍?

所以最好的办法就是把这部分逻辑封装成Hook

注意:规范点,hook都要以use开头

 1// useUser.ts
 2import { reactivecomputed } from 'vue'
 3const enum Name {
 4  CN = '林三心',
 5  EN = 'Sunshine_Lin',
 6}
 7const enum Gender {
 8  MAN = '男',
 9  WOMAN = '女',
10}
11const useUser = () => {
12  const user = reactive({
13    nameName.CN,
14    genderGender.MAN,
15  })
16  const userText = computed(() => `我叫${user.name},我是${user.gender}的`)
17  const switchGender = () => {
18    user.gender = user.gender === Gender.MAN ? Gender.WOMAN : Gender.MAN
19  }
20  return {
21    switchGender,
22    userText,
23  }
24}
25export default useUser

现在我们可以在页面中使用这个自定义hook

 1<script setup lang="ts">
 2import useUser from './useUser'
 3const {
 4  userText,
 5  switchGender
 6} = useUser()
 7</script>
 8<template>
 9  <div @click="switchGender">
10    {userText }}
11  </div>
12</template>

useRouter & useRoute

这是vue3提供给我们的两个路由hook

  • useRouter:用来执行路由跳转

  • useRoute:用来获取路由参数

 1<script setup>
 2import { useRouter } from 'vue-router'
 3const router = useRouter()
 4function onClick(
 5
 6{
 7  // 跳转
 8  router.push({
 9    path'/about',
10    query: {
11      msg'hello vue3!'
12    }
13  })
14}
15</script>
 1<script setup>
 2import { useRoute } from 'vue-router'
 3const route = useRoute()
 4console.log(route.query.msg) 
 5// hello vue3!
 6</script>

组件

介绍一些vue3内置的组件

Fragment

回想一下,在vue2中,只允许存在一个根节点,但是在vue3中,允许存在多个根节点,其实是因为vue3做了处理:

vue3检测到你有多个根节点时,会为你最外层补上一个Fragment,所以其实根节点还是一个

vue3支持多个根节点,大家要记得哦~

 1
 2<template>
 3  <div>哈哈</div>
 4  <div>嘻嘻</div>
 5</template>

Teleport

Teleport是传送门组件,他可以将你的组件,挂载到任意一个节点之下,只要你指定一个选择器,可以是id、class

例如:将Dialog组件挂载到id为app的节点下

 1// Dialog.vue
 2<template>
 3  <div>我是林三心我是林三心我是林三心我是林
 4    三心我是林三心我是林三心我是林三心我是
 5    林三心我是林三心我是林三心我是林三心
 6    我是林三心</div>
 7</template>
 1<script setup lang="ts">
 2import Dialog from './Dialog.vue'
 3</script>
 4<template>
 5  // 将Dialog组件挂载到id为app的节点下
 6  <Teleport to="#app">
 7    <Dialog />
 8  </Teleport>
 9</template>

我们可以看到,Dialog组件被挂到了id为app的节点下

个人笔记记录 2021 ~ 2025