本文已参与「新人创作礼」活动,一起开启掘金创作之路
1. 了解组合式API
1.1 选项式API特点
讲解 vue3 的组合式 API 之前,先来看看传统的选项式 API 的特点
一个非常明显的优点就是学习成本较低,因为框架已经规定死了
- 数据在 data 中
- 自定义方法及事件处理函数在 methods 中
- 计算属性在 computed 中
- 侦听器在 watch 中
- 钩子函数中可能还分布了一些初始化代码
- 。。。。。。。。。。。
但是当组件中功能越来越多,逻辑越来越复杂时,选项式 API 的确定就爆露出来了,那就是一个功能点的相关代码太分散了
这种死板的按照数据特点进行划分的方法非常不适合大项目的开发
下面是一张经典的 vue2 代码组织图,这是一个大型组件的示例,其中逻辑关注点按颜色进行分组,这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块
1.2 组合式API诞生
够将同一个逻辑关注点相关代码收集在一起,也就是写到一个地方,不再分散到各个选项中,如同一个功能用到的
- 数据:原来data中或者prop中的数据
- 方法:原来 methods 中的方法
- 计算属性和侦听器中代码
- 钩子函数中的代码
- 。。。。
这就需要很多新的语法特性的支持,如在这个集中的地方,如何创建原来 data 中的响应式数据,如何注册钩子函数,如何在这个地方注册计算属性和侦听器等
这也是我们接下来要学的
2. setup 函数
组合式 API 在 setup 中编写,换句话说,setup 是组合式 API 的入口
-
setup选项在组件创建之前执行(在beforeCreate钩子函数不调用之前),所以无法在 setup 函数中使用 this 引用组件实例 -
setup的调用发生在dataproperty、computedproperty 或methods被解析之前,所以它们无法在setup中被获取 -
setup返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板 -
setup 函数接受两个参数
- props:父组件传入的数据
- context:一个普通 JavaScript 对象,暴露了其它可能在
setup中有用的值
setup 函数在beforeCreate钩子函数之前执行
<template>
<h1>hello</h1>
</template>
<script>
export default {
name: 'App',
setup() {
console.log('setup函数执行')
},
beforeCreate() {
console.log('beforeCreate钩子函数执行了')
},
}
</script>
<style>
</style>
结果
setup函数执行
App.vue?91a0:12 beforeCreate钩子函数执行了
3.响应式变量
setup 函数中声明的变量默认不是响应式的,这与在 data 选项中声明的变量不同,不同类型的变量想要转换为响应式的,方法也不太相同
我们这里先聊聊简单数据类型
3.1 简单数据类型
setup 函数中声明的变量默认不是响应式的,这与在 data 选项中声明的变量不同
<template>
<h1 @click="change">{{ msg }}</h1>
</template>
<script>
export default {
name: 'App',
setup() {
const msg = 'hello' // msg 就是一个普通变量,在单击事件中也无法修改其值
return { msg }
},
methods: {
change() {
this.msg = 'world'
},
},
}
</script>
<style>
</style>
在 devtools中其也是不可以修改状态
通过 ref 函数可以将简单数据类型的变量定义为响应式数据
① 从 vue 中引入 ref 函数
② 使用 ref 将变量 msg 的值包裹起来
③ 在 setup 函数之外,使用 this.msg 访问变量
④ 在模板中使用变量
3.2 change 方法写在哪里
上面将 change 方法写道 methods 中
按照 vue3 中组合式 API 的原则,相同的逻辑关注点应该组合在一起,而不是分散在不同的选项中
所以 msg 及更改其值的方法应该组合在 setup 中
- 在 setup 中访问其中定义的变量,不要使用 this,但要使用变量.value 方式为其赋值
- setup 函数中要返回 msg 变量和 change 函数,这样才可以在模板中及其他地方使用
4. 钩子函数
如果希望页面加载时就请求接口获取数据,就需要在钩子函数中编写代码
组合式 API 上的生命周期钩子与选项式 API 的名称相同,但前缀为 on:即 mounted 看起来会像 onMounte
这些函数接受一个回调,当钩子被组件调用时,该回调将被执行
setup创建实例前onBeforeMount挂载DOM前onMounted挂载DOM后onBeforeUpdate更新组件前onUpdated更新组件后onBeforeUnmount卸载销毁前onUnmounted卸载销毁后
setup 函数中要使用钩子函数,首先需要从 vue 中导出需要的钩子函数
4. 侦听器
4.1 侦听器实现
在 setup 函数中使用侦听器
- 从 vue 中导入 watch 函数
- 侦听器中监听响应式数据
修改上面的案例,类型id 值来自于父组件,那么就需要获取 props 中传入的 类型id,然后侦听这个 id 值的变化,已动态的更新书记列表
父组件
<template>
<div class="nav">
<a
href="#"
@click.prevent="change(item.id)"
v-for="item in categoryList"
:key="item.id"
>
{{ item.title }}
</a>
</div>
<book-list :categoryId="categoryId"></book-list>
</template>
<script>
import axios from 'axios'
import { ref, onMounted } from 'vue'
import BookList from './components/BookList.vue'
export default {
name: 'App',
setup() {
const categoryId = ref(1)
const categoryList = ref([])
const getCategory = async () => {
const res = await axios.get('http://127.0.0.1:3000/api/book/category')
categoryList.value = res.data.data
}
onMounted(getCategory)
// 单击类别切换小说列表
const change = (id) => {
categoryId.value = id
}
return {
categoryList,
change,
categoryId,
}
},
components: {
BookList,
},
}
</script>
<style scoped>
.nav a {
margin: 10px 20px;
}
</style>>
子组件
4.2 toRefs 和 toRef
首先说 toRefs 函数,这个了解了,toRef 函数自然就清楚了
toRefs是函数,转换响应式对象中所有属性为单独响应式数据,对象成为普通对象,并且值是关联的,如何理解呢?
1、响应式对象中结构出来的属性不是响应式的
下面创建一个响应式对象
这里为什么使用 reactive 创建响应式对象,而不使用 ref 呢?
创建响应式对象的推荐方式为
- 简单数据类型,使用 ref,如字符串、数字、布尔值等
- 复杂数据类型,使用 reactive,如对象、数组
如果使用 ref 创建响应式对象,则下面结构代码应为
const {name,age}=obj.value不如上面代码直观
从对象中结构出来的 name 和 age 两个属性都不是响应式的
2、使用 toRefs 函数将结构出来的属性都转换为响应式的
我们上面案例中的 props 自然也是一个响应式对象,但是从其中结构出来的 categoryId 不是响应式的,所以需要使用 toRefs 函数将其属性都转换为响应式的,就可以根据 categoryId 的变化切换书籍信息了
3、toRef 函数将响应式对象中的单个属性转换为响应式的
toRefs 函数用于将响应式对象中的所有属性都转换为响应式的,这对于 props 对象来说非常合适,毕竟这个对象中的所有属性一定都需要是响应式的
但某些情况,可能响应式对象中只有某一个属性需要转换为响应式的,就可以使用 toRef 函数