Vue3 组合式API
一、选项式 API 的缺点
在 Vue2 中,我们通过选项式 API 编写组件,分属于不同类别的内容需要填入对象的选项 API 之中,如下:
1 |
|
当页面中只有一到两个模块时,这种做法是合适的。
当页面中存在大量模块时,这种做法便开始暴露出问题:同一个模块的不同类别的代码需要分散于各个选项之中,同一个选项之中会被散乱放置分属于不同模块的代码。维护一个组件往往需要在多个选项之间来回跳转,并在各个选项中辨认属于该模块的代码。组件的阅读和维护因为选项 API 的代码组织方式成为难题。
二、组合式 API
为了解决选项式 API 的问题,Vue3 新增了组合式 API,它将不再需要将代码分门别类放置于各个选项之中,而是可以由程序员自行按模块进行划分。
三、setup
1. 说明
- setup 是新增的选项式 API
- setup 是组合式 API 的入口,使用组合式 API 的代码应该在 setup 选项中编写
- setup 将在 beforeCreate 之前调用,因此无法访问组件实例(this)
- setup 应该返回一个对象,对象中的属性和方法可以被组件的其它部分使用
- setup 可以返回渲染函数
2. 参数 - props
(1) props
setup 可以接收的第一个参数是 props,它包含外部传入的 prop,它是响应式的,会在 prop 更新时更新。
(2) 获取 prop
如果希望获取 prop 的值,可以通过 props.prop名
获取。
如果希望获取 prop 且不丢失响应性,应该:
通过 toRefs 获取一个 “普通” 对象,该对象中包含每个 prop 对应的 ref 对象
1
2
3
4setup(props) {
let myProps = toRefs(props)
···
}通过 toRef 获取指定 prop 对应的 ref 对象
1
2
3
4setup(props) {
let prop名 = toRef(props, 'prop名')
···
}
3. 参数 - context
setup 可以接收的第二个参数是 context,它是非响应式的,包含了组件的其它信息(attrs、slots、emit、expose)。
四、生命周期钩子
在组合式 API 中,如果要使用生命周期钩子,应该从 vue 处导入名为 on + 钩子名
的函数,调用并传入函数。
名称对照表如下:
选项式 API | Hook inside setup |
---|---|
beforeCreate |
Not needed |
created |
Not needed |
beforeMount |
onBeforeMount |
mounted |
onMounted |
beforeUpdate |
onBeforeUpdate |
updated |
onUpdated |
beforeUnmount |
onBeforeUnmount |
unmounted |
onUnmounted |
errorCaptured |
onErrorCaptured |
renderTracked |
onRenderTracked |
renderTriggered |
onRenderTriggered |
activated |
onActivated |
deactivated |
onDeactivated |
值得注意的是,在组合式 API 中没有
beforeCreate
和created
对应的钩子函数,这是因为 setup 的调用时间与它们相近,因此应该将这两个生命周期中应发生的事情写到 setup 中。
五、provide/inject
可以将 provide/inject 看作可跨级的 prop,它用于向后代组件传递信息,provide 用于发送,inject 用于接收。
在组合式 API 中,provide 和 inject 需要显示导入,使用方式如下:
祖先组件:
1
2
3
4
5
6
7
8
9import { provide } from 'vue'
···
setup() {
provide(名, 值)
provide(名, 值)
···
}后代组件:
1
2
3
4
5
6
7import { inject } from 'vue'
···
setup() {
let ··· = inject(名, 可选的默认值)
}默认值是可选的,如果没有获取到对应的值,则默认值会生效
在 Vue2 中,provide/inject 是没有响应性的;
在 Vue3 中,可以通过传递 “响应式数据” 来为 provide/inject 增加响应性。
六、响应式 ref 和 模板 ref
1. 什么是响应式 ref 和 模板 ref ?
在 Vue3 中,存在响应式 ref 和模板 ref 这两个概念,其中:
响应式 ref:根据值生成一个 ref 对象,它是响应式的
1
2
3
4let refMsg = ref("Hello")
// ref对象的改变会自动触发视图的更新
resMsg.value = "Hello World!"模板 ref:模板 ref 是一种访问模板元素的方式
1
2
3
4
5<p ref="test">123</p>
···
this.$refs.test
2. 重名问题
你可能会产生这样的疑惑,一个是寻找模板元素,一个是根据值生成响应化对象,为什么要取相同的名字?
事实上,
- 如果使用选项式 API,(一般情况下)无需使用响应式 ref,只使用模板 ref 即可
- 如果使用组合式 API,响应式 ref 和模板 ref 的概念是统一的
3. 组合式 API 中的响应式 ref 和模板 ref
(1) 响应式 ref 的使用:
1 |
|
(2) 模板 ref 的使用
1 |
|
如果模板中 “元素的 ref 名” 与 setup 中 return 的 “ref 对象名” 相同,则两者会进行绑定,即 模板ref == ref对象
。如果要访问元素,既可以通过 $refs.元素名
访问,也可以通过 ref对象.value
访问。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<template>
<p ref="myP">123</p>
</template>
<script>
import {ref} from "vue";
export default {
name: "TemplateRef",
setup() {
let myP = ref(null)
return {
myP
}
},
mounted() {
setInterval(() => {
// 在选项式API中访问ref,无需添加.value
console.log(this.myP)
console.log(this.$refs.myP)
console.log(this.myP === this.$refs.myP)
}, 1000)
}
}
</script>
由于模板 ref 会在模板渲染后被赋值,因此其初始值并不重要,一般设为 null
七、watchEffect
1. watchEffect()
1 |
|
watchEffect
是 Vue3 中新增的专用于组合式 API 的函数- 调用
watchEffect
时需要传入一个函数,函数中可以使用若干响应式数据 - 它会立即执行一次函数
- 它会自动 “追踪” 函数所使用的响应式数据,当它们变化时重新运行函数
1
2
3
4
5
6
7
8
9
10
11
let amount = ref(0)
let price = ref(10)
watchEffect(() => {
console.log("总价是:" + amount.value * price.value)
}) // "总价是:0"[初始化执行第一次]
amount++ // "总价是:10"[响应式对象改变,自动执行]
price++ // "总价是:11"[响应式对象改变,自动执行]
2. 回撤操作
watchEffect 传入的函数允许接收一个参数,该参数是一个回调函数,它将会在 watchEffect 下一次执行前被调用,可以用于回撤操作。
1
2
3
4
5
6
7
watchEffect((onCleanup) => {
// do something
onCleanup(() => {
// 回撤之前的操作
})
})
3. 停止监听
默认情况下,
watchEffect
将会随组件生命周期结束而停止监听如果希望手动监听,可以接收
watchEffect
的返回值并调用1
2
3
4
5const stop = watchEffect(() => {
...
})
stop()
4. watch 与 watchEffect
watch
需要明确指定要跟踪的响应式数据,并且只会跟踪它们watchEffect
可以无需指定要跟踪的响应式数据,只需要说明希望做的事情,并在事情中使用响应式数据,Vue 会自动跟踪它们watch
将监听的对象和希望做的事情分离,而watchEffect
将它们耦合