Vue3.x 版本的出现带来了许多令人眼前一亮的新特性,其中组合式 API(Composition API),一组附加的、基于功能的 API 被作为一种新的逻辑复用和代码组织的方式提供给了开发者,提供更加灵活的组合组件逻辑能力。
- 组合式 API:一组低侵入式的、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑
- 用一句通俗的话来说:
composition API
其实是用于解决功能、数据和业务逻辑分散的问题,使项目更益于模块化开发以及后期维护
先看一段简单的代码:
<template>
<h2>组合式APIh2>
<el-button type="primary" @click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
el-button>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script>
import {computed, reactive} from "vue"
export default {
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
script>
我们先来看下这段代码发生了啥?
import {computed, reactive} from "vue";
Component API
是以函数的形式展示组件属性,所以第一步就是导入我们需要的函数。在我们的例子中,我们用 reactive
创建响应属性,用 computed
创建计算属性。
export default {
setup() {
// ...
return {
state,
increment
}
}
还有一个 setup
函数, setup
函数是一个新的组件选项。作为在组件内使用 Composition API
的入口点,如果 setup
返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文,我们就可以在模板里使用对应的属性和方法。
组合式 API 的使用,官方参考文档和 API 文档有详细说明,这里不过多说明。为方便大家理解和把握,根据官方的 API 文档整理了全景图。
Vue 组合式API 可以分为五大块:
Object
、Array
的数据响应String
、Number
等computed
和 watch
:基于响应式数据的数据计算与监听onMounted
、onBeforeMount
等在 Vue2 中我们采用 Options API
来写上面的代码:
<template>
<h2>Option APIh2>
<el-button type="primary" @click="increment">
Count is: {{ count }}, double is: {{ double }}
el-button>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script>
export default {
data() {
return {
count: 0
}
},
computed: {
double() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
}
}
script>
那在 Vue2 中如果我们要复用这个逻辑,我们可以通过诸如 mixins
或作用域插槽的模式达成。
mixins
的方式<template>
<h2>Option API Mixinsh2>
<el-button type="primary" @click="increment">
Count is: {{ count }}, double is: {{ double }}
el-button>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script>
import CounterMixin from '@/mixins/counter'
export default {
mixins: [CounterMixin]
}
script>
mixins
存在的问题是:
property
来源不清晰。例如在阅读一个运用了多个 mixin
的组件代码时,很难看出某个 property
是从哪一个 mixin
中注入的。mixin
之间的 property
和方法可能有冲突。<template>
<h2>Option API Sloth2>
<Counter v-slot="{ count, double, increment }">
<el-button type="primary" @click="increment">
Count is: {{ count }}, double is: {{ double }}
el-button>
Counter>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script>
import Counter from '@/components/Counter'
export default {
components: {
Counter
}
}
script>
优点:有了scoped slots,我们就可以通过v-slot属性准确地知道我们可以访问哪些属性,这样就更容易理解代码了。
缺点:我们只能在模板中访问,而且只能在 Counter
组件作用域中使用。
useCounter.js
// 将这部分代码逻辑抽离出来
import {computed, reactive} from "vue"
export function useCounter() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
});
const increment = function() { state.count++ }
return {
state,
increment
}
}
CounterSetup.vue
<template>
<h2>组合式API script setup 语法糖h2>
<el-button type="primary" @click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
el-button>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script setup>
import { useCounter } from '@/libs/useCounter'
const { state, increment } = useCounter()
script>
是不是更加优雅了,相比较而言:
property
来源十分清晰,因为它们都是被组合逻辑函数返回的值。Vue
全局导出的 API,而不是依赖其微妙的 this
上下文除了方便逻辑提取与复用之外,Composition API 带给我们的实际上更多的是一种新的代码编写思维。
当要去理解一个组件时,我们更加关心的是“这个组件是要干什么” (即代码背后的意图) 而不是“这个组件用到了什么选项”。基于 Options API
撰写出来的代码自然采用了后者的表述方式,然而对前者的表述并不好。
Options API
选项的强行分离为展示背后的逻辑关注点设置了障碍。此外,在处理单个逻辑关注点时,我们必须不断地在选项代码块之间“跳转”,以找到与该关注点相关的部分。
比如上面的例子中,基于 Options API
的方式我们必须在 data
、computed
、methods
三个选项中跳转,来完成这段逻辑。而通过 Composition API
的方式我们把相同逻辑关注点的代码并列在一起,形成了一个独立的逻辑函数。
当然 Composition API
的引入也存在一定的弊端。
组合式API 在代码组织方面提供了更多的灵活性,但它也需要开发人员更多地自我克制来 “正确地完成它”,组合式API 会让没有经验的新手编写出“面条代码”。
在 Options API
中实际上形成了一种强制的约定:
props
里面设置接收参数data
里面设置变量computed
里面设置计算属性watch
里面设置监听属性methods
里面设置事件方法会发现 Options API
都约定了我们该在哪个位置做什么事,这在一定程度上也强制我们进行了代码分割。
现在用 Composition API
,不再这么约定了,所以代码组织非常灵活,如果作为一个新手,或者不深入思考的码农,那么在逻辑越来越复杂的情况下,setup
代码量越来越多,同样 setup
里面的 return
越来越复杂,势必会落入“面条代码”地步。
就是vue3新出的一个语法糖,使用方法就是在 script
标签的后面加上一个 setup
修饰。
代码如下:
<template>
<h2>组合式API script setup 语法糖h2>
<el-button type="primary" @click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
el-button>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script setup>
import { useCounter } from '@/libs/useCounter'
const { state, increment } = useCounter()
script>
优点:
当使用 的时候,任何在
声明的顶层的绑定 (包括变量,函数声明,以及
import
引入的内容) 都能在模板中直接使用:
<template>
<div @click="log">{{ msg }}div>
<div>{{ capitalize('hello') }}div>
template>
<script setup>
// 引入
import { capitalize } from '@/libs/helpers'
// 变量
const msg = 'Hello!'
// 函数
function log() {
console.log(msg)
}
script>
this
在 setup
中你应该避免使用 this
,因为它不会找到组件实例。setup
的调用发生在 data property
、computed property
或 methods
被解析之前,所以它们无法在 setup
中被获取。这使得 setup
在和其它 Options API
一起使用时可能会导致混淆。
Options API
)在一个vue3项目中,我们经常需要从 vue
,vue-router
等引入相应的函数方法进单个文件中。
比如:
import { ref, reactive, toRefs, watchEffect, computed, watch, onUnmounted,} from 'vue'
import { useRouter } from 'vue-router'
这时我们可以使用基于unplugin
项目的两个插件:
unplugin-auto-import
来实现vue函数的自动导入unplugin-vue-components
来实现vue组件库的自动按需导入yarn add unplugin-auto-import unplugin-vue-components -D
vite.config.js
中配置// vite.config.js
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()], // 这里使用ElementPlus组件库
imports: [
'vue',
'vue-router',
{
vuex: ['useStore']
}
]
}),
Components({
resolvers: [ElementPlusResolver()]
}),
]
})
配置完成后我们就可以直接在项目中这样使用
<template>
<el-button type="primary">{{ text }}el-button>
<br>
<div style="display: block;margin-top: 50px;">
<router-link to="/" style="margin-top: 50px">首页router-link>
div>
template>
<script setup>
const text = ref('我是一个 Element Plus 按钮') // ref 无需从vue中导入
script>
本文所有代码地址:
gitlab(需要注册):https://gitlab.explorexd.com/devs/vue3-demo
github:https://github.com/jiaxudonggit/vue3-demo
https://v3.cn.vuejs.org/guide/introduction.html
https://juejin.cn/post/7027443242928979982
https://juejin.cn/post/6844904191928827912
https://juejin.cn/post/7025837638540066824
https://element-plus.gitee.io/zh-CN/guide/installation.html
https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project