Composition API也叫组合式API,是Vue3.x的新特性。
通过创建 Vue 组件,我们可以将接口的可重复部分及其功能提取到可重用的代码段中。仅此一项就可以使我们的应用程序在可维护性和灵活性方面走得更远。然而,我们的经验已经证明,光靠这一点可能是不够的,尤其是当你的应用程序变得非常大的时候——想想几百个组件。在处理如此大的应用程序时,共享和重用代码变得尤为重要
通俗的讲:
没有Composition API之前vue相关业务的代码需要配置到option的特定的区域,中小型项目是没有问题的,但是在大型项目中会导致后期的维护性比较复杂,同时代码可复用性不高。Vue3.x中的composition-api就是为了解决这个问题而生的
compositon api提供了以下几个函数:
setuprefreactivewatchEffectwatchcomputedtoRefs- 生命周期的
hooks
# 一、setup组件选项
新的
setup组件选项在创建组件之前执行,一旦props被解析,并充当合成API的入口点
提示:
由于在执行
setup时尚未创建组件实例,因此在setup选项中没有this。这意味着,除了props之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
使用 setup 函数时,它将接受两个参数:
propscontext
让我们更深入地研究如何使用每个参数
# 1. Props
setup函数中的第一个参数是props。正如在一个标准组件中所期望的那样,setup函数中的props是响应式的,当传入新的prop时,它将被更新
// MyBook.vue
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
注意:
但是,因为
props是响应式的,你不能使用ES6解构,因为它会消除prop的响应性。
如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成此操作。
// MyBook.vue
import { toRefs } from 'vue'
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
# 2. 上下文
传递给
setup函数的第二个参数是context。context是一个普通的 JavaScript 对象,它暴露三个组件的 property
// MyBook.vue
export default {
setup(props, context) {
// Attribute (非响应式对象)
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
console.log(context.emit)
}
}
context是一个普通的 JavaScript 对象,也就是说,它不是响应式的,这意味着你可以安全地对context使用 ES6 解构
// MyBook.vue
export default {
setup(props, { attrs, slots, emit }) {
...
}
}
attrs和slots是有状态的对象,它们总是会随组件本身的更新而更新。这意味着你应该避免对它们进行解构,并始终以attrs.x或slots.x的方式引用 property。请注意,与props不同,attrs和slots是非响应式的。如果你打算根据attrs或slots更改应用副作用,那么应该在onUpdated生命周期钩子中执行此操作。
# 3. setup组件的 property
执行
setup时,组件实例尚未被创建。因此,你只能访问以下 property:
propsattrsslotsemit
换句话说,你将无法访问以下组件选项:
datacomputedmethods
# 4. ref reactive 以及setup结合模板使用
在看setup结合模板使用之前,我们首先得知道ref 和 reactive 方法。
如果 setup 返回一个对象则可以在模板中绑定对象中的属性和方法,但是要定义响应式数据的时候可以使用ref, reactive方法定义响应式的数据
# 错误写法:
<template>
{{msg}}
<br>
<button @click="updateMsg">改变etup中的msg</button>
<br>
</template>
<script>
export default {
data() {
return {
}
},
setup() {
let msg = "这是setup中的msg";
let updateMsg = () => {
alert("触发方法")
msg = "改变后的值"
}
return {
msg,
updateMsg
}
},
}
</script>
<style lang="scss">
.home {
position: relative;
}
</style>
# 正确写法一:
ref用来定义响应式的 字符串、 数值、 数组、
Bool类型
import {
ref
} from 'vue'
<template>
{{msg}}
<br>
<br>
<button @click="updateMsg">改变etup中的msg</button>
<br>
<br>
<ul>
<li v-for="(item,index) in list" :key="index">
{{item}}
</li>
</ul>
<br>
</template>
<script>
import {
ref
} from 'vue'
export default {
data() {
return {
}
},
setup() {
let msg = ref("这是setup中的msg");
let list = ref(["马总", "李总", "刘总"])
let updateMsg = () => {
alert("触发方法");
msg.value = "改变后的值"
}
return {
msg,
list,
updateMsg
}
},
}
</script>
<style lang="scss">
.home {
position: relative;
}
</style>
# 正确写法二:
reactive 用来定义响应式的对象
import {
reactive
} from 'vue'
<template>
{{msg}}
<br>
<br>
<button @click="updateMsg">改变setup中的msg</button>
<br>
<br>
<ul>
<li v-for="(item,index) in list" :key="index">
{{item}}
</li>
</ul>
<br>
{{setupData.title}}
<br>
<button @click="updateTitle">更新setup中的title</button>
<br>
<br>
</template>
<script>
import {
reactive,
ref
} from 'vue'
export default {
data() {
return {
}
},
setup() {
let msg = ref("这是setup中的msg");
let setupData = reactive({
title: "reactive定义响应式数据的title",
userinfo: {
username: "张三",
age: 20
}
})
let updateMsg = () => {
alert("触发方法");
msg.value = "改变后的值"
}
let updateTitle = () => {
alert("触发方法");
setupData.title = "我是改变后的title"
}
return {
msg,
setupData,
updateMsg,
updateTitle
}
},
}
</script>
<style lang="scss">
.home {
position: relative;
}
</style>
**说明:**要改变ref定义的属性名称需要通过 属性名称.value来修改,要改变reactive中定义的对象名称可以直接
# 5. 使用 this
在
setup()内部,this不会是该活跃实例的引用,因为setup()是在解析其它组件选项之前被调用的,所以setup()内部的this的行为与其它选项中的this完全不同。这在和其它选项式 API 一起使用setup()时可能会导致混淆
# 二、toRefs - 解构响应式对象数据
把一个响应式对象转换成普通对象,该普通对象的每个
property都是一个ref,和响应式对象property一一对应
<template>
<div>
<h1>解构响应式对象数据</h1>
<p>Username: {{username}}</p>
<p>Age: {{age}}</p>
</div>
</template>
<script>
import {
reactive,
toRefs
} from "vue";
export default {
name: "解构响应式对象数据",
setup() {
const user = reactive({
username: "张三",
age: 10000,
});
return {
...toRefs(user)
};
},
};
</script>
当想要从一个组合逻辑函数中返回响应式对象时,用 toRefs 是很有效的,该 API 让消费组件可以 解构 / 扩展(使用
…操作符)返回的对象,并不会丢失响应性:
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2,
})
// 对 state 的逻辑操作
// ....
// 返回时将属性都转为 ref
return toRefs(state)
}
export default {
setup() {
// 可以解构,不会丢失响应性
const { foo, bar } = useFeatureX()
return {
foo,
bar,
}
},
}
# 三、computed - 计算属性
# 四、readonly “深层”的只读代理
传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的
# 五、watchEffect
在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。
# 六、watch 、watch 与watchEffect区别
对比watchEffect,watch允许我们:
- 懒执行,也就是说仅在侦听的源变更时才执行回调;
- 更明确哪些状态的改变会触发侦听器重新运行;
- 访问侦听状态变化前后的值