先来说说一些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 { ref, reactive } 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 name: Name.CN,
16 gender: Gender.MAN
17})
18const arr = reactive<{ name: string }[]>([{ 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:监听函数,可监听常量和引用变量,可传
immediate
和deep
。可监听对象也可只监听对象的某个属性 -
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 name: Name.CN,
18 gender: Gender.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被修改了', next, pre)
33})
34// 监听user对象
35watch(user, (next) => {
36 console.log('user被修改了', next)
37}, {
38 immediate: false, // 首次执行
39 deep: true // 深度监听对象
40})
41// 可监听对象的某个属性,这里监听user.gender
42watch(() => user.gender, (next, pre) => {
43 console.log('user.gender被修改了', next, pre)
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 { defineProps, defineEmits } from 'vue'
3// 注册父传子的props
4const { msg } = defineProps({
5 msg: {
6 type: String,
7 required: true
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({ msg, change })
13</script>
1<!-- 父组件 -->
2<script setup>
3import ChildView from './ChildView.vue'
4import { ref, onMounted } 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 loadingComponent: LoadingComponent,
5 // 加载失败时要使用的组件
6 errorComponent: ErrorComponent,
7 // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
8 delay: 1000,
9 // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
10 // 默认值:Infinity(即永不超时,单位 ms)
11 timeout: 3000
12})
13// 使用时,可控制显隐
14<Suspense v-if="show" >
15 <AsyncPopup />
16</Suspense>
Hooks
自定义hook——useUser
我们要知道Hooks
的好处,其实它就是一个函数,好处有:
-
复用性高
-
减少代码量,提高可维护性
-
相同逻辑可放一起
就比如刚刚我们修改user的那个例子,假如很多页面都需要这个逻辑,那岂不是每个地方都需要写一遍?
所以最好的办法就是把这部分逻辑封装成Hook
注意:规范点,hook都要以
use
开头
1// useUser.ts
2import { reactive, computed } 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 name: Name.CN,
14 gender: Gender.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的节点下
