
为什么会出现,解决了啥痛点?
新特性的出现肯定是为了解决和优化以往设计中的某个痛点,组合式API也一样。
我们都知道,代码的可重用性会增强我们应用的可维护性和灵活性,所以几乎每个前端语言框架都有组件化开发的思想,vue也不例外。
依据vue2的设计思想,使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效,但当一个组件开始变得更大时,逻辑关注点的列表也会增长,这会导致组件难以阅读和理解,就像下面这样
- 这种碎片化使得理解和维护复杂组件变得困难,选项的分离掩盖了潜在的逻辑问题,在处理单个逻辑关注点的时候得不断跳转相关代码选项块
- 为了能够将同一个逻辑关注点相关代码收集在一起,组合式API应运而生。
怎么使用组合式API
知道了它是怎么来的,我们还得学会怎么去使用
vue3是通过setup组件选项去使用组合式API的
setup调用时间
setup的调用发生在dataproperty、computedproperty 或methods被解析之前,所以它们无法在setup中被获取。- 执行setup时,组件实例尚未被创建,所以this在setup中无效,因为它不会找到组件实例。
- setup位于生命周期图顶部
参数
setup选项是一个接收props和context的函数,setup返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
setup(props,context) {
return {} // 这里返回的任何内容都可以用于组件的其余部分
}
props
需要注意的是,props是响应式的,即传入新的prop时,它将被更新,所以不能对props使用ES6结构,这样会消除props的响应性。需要解构可以在
setup函数中使用toRefs函数来完成import { toRefs } from 'vue' setup(props) { const { title } = toRefs(props) console.log(title.value) }如果
title是可选的 prop,则传入的props中可能没有title。在这种情况下,toRefs将不会为title创建一个 ref 。你需要使用toRef替代它:import { toRef } from 'vue' setup(props) { const title = toRef(props, 'title') console.log(title.value) }
context
context是一个普通的 JavaScript 对象,它不是响应式的,可以安全地对context使用 ES6 解构。export default { setup(props, { attrs, slots, emit, expose }) { ... } }attrs和slots是有状态的对象,它们总是会随组件本身的更新而更新。注意,是组件本身!这意味着我们应该避免对attrs和slots进行解构,并始终以attrs.x的形式去使用property,但由于property是非响应式的,如果想通过attrs的更改去做事情,我们应该要在onBeforeUpdate生命周期钩子执行操作我们需要知道attrs,slots,emit,expose都是些啥
attrs
<Child @testAttrsItem="testAttrsItem" />
// 其实就是父组件传递的函数集合
slot
<TestSlots >
<template v-slot:testSlot1>
<div>
我是插槽1
</div>
</template>
<template v-slot:testSlot2>
<div>
我是插槽2
</div>
</template>
</TestSlots>
// 其实就是插槽的集合
emit
// 子组件
export default defineComponent({
setup(props, { emit }) {
const emitFun = () => {
console.log("我是抛出去的函数");
};
const clickEmit = () => {
emit("emitFun", "param1");
};
return {
clickEmit
}
},
});
</script>
<template>
<div>
<button @click="clickEmit">点击抛出</button>
</div>
</template>
// 父组件
const monitorChildEmitFun=(param: string)=>{
console.log(param)
}
<TestEmit @emitFun="monitorChildEmitFun" />
expose
- expose函数接受一个对象参数,其中定义的property将可以被外部组件实例访问,未在其中定义变量将在父组件中访问不到
// 子组件
setup(props, { expose }) {
const count = ref(0)
const increment = () => ++count.value
expose({
increment
})
return () => h('div', count.value)
}
// 父组件
const child=ref(null)
const handleClick=()=>{
console.log(child.value)
child.value.increment()
}
<button @click="handleClick">点我</button>
<TestExpose ref="child" />
模板
- setup返回的对象中的property以及传递给setup的props属性可以在模板中访问的到
- 但需要注意的是,从
setup返回的 refs 在模板中访问时是被自动浅解包的,因此不应在模板中使用.value。
使用渲染函数
setup可以返回一个渲染函数,该函数可以直接使用在同一作用域中声明的响应式状态
setup(props, context) { const readersNumber = ref(0); const book = reactive({ title: "Vue 3 Guide" }); // 请注意这里我们需要显式使用 ref 的 value return () => h("div", [readersNumber.value, book.title]); }, // 父组件直接照常引入该组件进行渲染 // h本质上是createElement函数的别名返回一个渲染函数将阻止我们返回任何其它的东西,如果我们想要将这个组件的方法通过模板ref暴露给父组件就不能了,我们可以通过expose方法解决这个问题
