『引言』
在传统的组件中,随着业务复杂度越来越高,代码量不断的增加,会导致整个代码的逻辑都不易阅读和理解。
Vue3 组合式 API(Composition API) 主要用在大型组件中提高代码逻辑的可复用性。
Vue3 使用组合式 API 的地方为 setup。Vue3 中使用 setup 定义响应式的数据和方法。
在 setup 中,我们可以按逻辑对部分代码进行分组,然后提取逻辑片段并与其它组件共享代码。因此,组合式 API(Composition API) 编写的代码更有条理。
『Composition API 的优势』
『Options API 存在的问题』
使用传统 Options API 中,新增或者修改一个需求,就需要分别在data,methods,computed里修改。
功能不能独立出来,如果功能特别多就会显得很乱。
看下面演示动画:
由上面的演示动画可以看出,所有功能都拆散了。
通过上面这个演示动画可以看到,data中保存着n个功能的数据,methods中保存着n个功能的方法,看起来十分的混乱。
『Composition API 的优势』
主要表现在,我们能够整洁的组织我们的代码,函数,让相关功能的代码更加有条理的组织在一起。
看下面演示:
根据以上动画,我们能很直观的看到 Composition API 的两大优点:代码逻辑的复用性和代码的整洁、清晰性。
『Options API 和 Composition API 举例对比』
通过不同方式实现如下效果:
通过上面的各个动画可以清晰明了的知道Composition API 的优势。
『Options API 实现』
//App.vue
<template>
<DemoA />
<DemoB />
</template>
<script>
import DemoA from './components/DemoA.vue'
import DemoB from './components/DemoB.vue'
export default {
name: 'App',
components: {
DemoA,
DemoB
}
}
</script>
//components/DemoA.vue
<template>
<h1>{{ message }}</h1>
</template>
<script>
export default{
name: 'DemoA',
data () {
return {
message: '我叫wnxx'
}
}
mounted (){
console.log("我叫wnxx")
}
</script>
//components/DemoB.vue
<template>
<h1>{{ message }}</h1>
</template>
<script>
export default{
name: 'DemoB',
data () {
return {
message: '我叫pupu'
}
}
mounted (){
console.log("我叫pupu")
}
</script>
『Composition API 实现』
//App.vue
<template>
<DemoA />
<DemoB />
</template>
<script>
import DemoA from './components/DemoA.vue'
import DemoB from './components/DemoB.vue'
export default {
name: 'App',
components: {
DemoA,
DemoB
}
}
</script>
//components/DemoA.vue
<template>
<h1>{{ message }}</h1>
</template>
<script setup>
import useMessage from '../hooks/useMessage'
let message = useMessage('wnxx')
</script>
//components/DemoB.vue
<template>
<h1>{{ message }}</h1>
</template>
<script setup>
import useMessage from '../hooks/useMessage'
let message = useMessage('pupu')
</script>
//hooks/useMessage.js
import { onMounted, ref } from "vue"
export default function (val) {
onMounted(() => {
console.log(`我叫${val}`)
})
return ref(`我叫${val}`)
}
Composition API 实现方式可以明显的看出一个优点那就是hooks函数,后面会有详细的介绍。
『setup 组件』
『setup特性及注意点⚠️』
setup函数是Composition API(组合API)的入口。- 在
setup函数中定义的变量和方法,最后都是需要 return出去的,不然无法在模板中使用。 - setup函数执行于
beforeCreate和created之前,也就是说setup函数里面无法使用data和methods方法中的数据。 - 解决了在vue2中,因为data和methods方法相距太远,导致组件之间无法复用的问题。
- 在
script setup模式下,组件不需要注册,直接挂载。 - 在模板中也可以自动获得自定义的指令。
setup函数只能是同步的不能是异步的。
『setup的执行时机』
beforeCreate() {
console.log('beforeCreate执行了');
},
setup() {
console.log('setup执行了');
return {};
},
- 可以通过
console.log看到setup是在beforeCreate生命周期之前执行的(只执行一次),setup执行在创建实例之前,beforeCreate之前执行一次, 此时组件对象还没有创建,this是undefined - 不能通过this来访问data/computed/methods/props
- 其实所有的composition API相关回调函数中也都不可以
『setup的返回值』
- 一般都返回一个对象: 为模板提供数据,也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与methods中的方法合并成功组件对象的方法
- 如果有
重名, setup优先
🌟🌟注意: 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
🌟🌟注意: setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise,
模板看不到return对象中的属性数据
『setup的参数』
setup() 函数接收两个参数 props 和 context。
第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。
第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象。里面有如下三个值:
可以通过 es6 语法解构 setup(props, {attrs, slots, emit})
1. attrs:值为对象,包含组件外部传递过来,没有在props配置中声明的属性的对象,相当于this.$attrs。
2. slots:包含所有传入的插槽内容的对象,相当于this.$slots。
3. emit:用来分发自定义事件的函数,相当于this.$emit。
🌟🌟注意🌟🌟: 在 setup 中你应该避免使用 this,因为它不会找到组件实例。
setup 的调用发生在 data property、computed property 或 methods 被解析之前,
所以它们无法在 setup 中被获取。
『attrs和props示例』
//父组件
<template>
<h1>父组件</h1>
<child :msg="msg" msg2='wnxx'></child>
</template>
<script setup>
import { ref } from 'vue'
// 引入子组件
import Child from './components/Child.vue'
export default {
name: 'App',
components: { Child },
const msg = ref('hello,setup')
}
</script>
//子组件
<template>
<h2>子组件</h2>
<h3>msg:{{ msg }}</h3>
</template>
<script>
export default {
name: 'Child',
props: ['msg'],
setup(props, {attrs, slots, emit}) {
console.log('props:', props)
//msg: "hello,setup"
console.log('attrs:', attrs)
//msg2: "wnxx"
return {}
}
}
</script>
『emit示例』
//父组件
<template>
<h1>父组件</h1>
<child @show="show"></child>
</template>
<script setup>
// 引入子组件
import Child from './components/Child.vue'
export default {
name: 'App',
components: { Child },
const show = () => {
console.log('name:', 'wnxx')
}
}
</script>
//子组件
<template>
<h2>子组件</h2>
<button @click='emitBtn'>按钮</button>
</template>
<script>
export default {
name: 'Child',
setup(props, { emit }) {
const emitBtn = () => {
emit('show')
}
return {
emitBtn
}
}
}
</script>
『setup的使用』
『setup() 来写组合式 API 时』
内部定义的属性和方法,必须使用 return 暴露到上下文,外部才能够使用,否则就会报错。
写法为:
<template>
<h1>{{ message }}</h1>
</template>
<script>
export default {
setup(){
import useMessage from '../hooks/useMessage'
let message = useMessage('wnxx')
return {
message
}
}
}
</script>
『使用 script setup 语法糖时』
不需要 return 和 setup函数,只需要全部定义到 script setup 内。
写法为:
<template>
<h1>{{ message }}</h1>
</template>
<script setup>
import useMessage from '../hooks/useMessage'
let message = useMessage('wnxx')
</script>
『setup 函数示例』
简单做一个小案例,完成如下效果:
『第一种方式』
<template>
<p>你喜欢的歌手是:{{selected}}</p>
<button v-for="(item, key) in data.singers"
:key="key"
@click="selectSinges(key)">{{item}}
</button>
</template>
<script lang="ts">
import { reactive, ref} from "vue"
export default {
name: "SingesSelect",
setup() {
const data = reactive({
singers: ["周杰伦", "汪苏泷", "李荣浩"]
})
const selected = ref("")
const selectSinges = (params: number): void => {
selected.value = data.singers[params]
}
return {
data,
selected,
selectSinges
}
}
}
『第二种方式』
<template>
<div>
<p>你喜欢的歌手是:{{ selected }}</p>
<button v-for="(item, key) in data.singers"
:key="key"
@click="selectSinges(key)">{{item}}
</button>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
const data = reactive({
singers: ["周杰伦", "汪苏泷", "李荣浩"]
})
const selected = ref("")
const selectSinges = (params: number): void => {
selected.value = data.singers[params]
}
</script>
这两种方式只是在写法上有一点的区别,一种使用setup()来写,另一种使用script setup语法糖,总体来说差别不是很大。