前言
vue3目前已经发布了alpha版本,除了服务端渲染,其它工作已经全部完成。尤大大也升级了vue-loader,提供了一个可以使用.vue组件的测试模板。vue3最大的改变是加入了这个灵感来源于React Hook的Composition API(组成API),这个API将对vue编程产生了根本性变革,但是vue3还是兼容vue2的Options API。除此之外,还引入了一些不兼容修改,具体可查看Vue3已合并的RFC。本文并不会全面介绍vue3的新特效,只会着重与vue3的核心——Composition API。
小试牛刀
先看一下基础例程:
<template>
<button @click="increment">
Count is: {
{
state.count }}, double is: {
{
state.double }}
</button>
</template>
<script>
import {
reactive, computed } from 'vue'
export default {
setup(props, context) {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
</script>
从上面的基础例程可以看到,vue3的.vue组件大体还是和vue2一致,由template``、script和style组成,作出的改变有以下几点:
- 组件增加了setup选项,组件内所有的逻辑都在这个方法内组织,返回的变量或方法都可以在模板中使用。
- vue2中
data、computed等选项仍然支持,但使用setup时不建议再使用vue2中的data灯选项。 - 提供了
reactive、computed、watch、onMounted等抽离的接口代替vue2中data等选项。
为什么Composition API?
为什么使用Composition API代替Options API?难道将各种类型的代码分类写在对应的地方不比把所有的代码都写在一个setup函数中要好?要解答这两个问题,我们首先来思考一下Options API的局限。
代码组织
Options API是把代码分类写在几个Option中:
export default {
components: {
},
data() {
},
computed: {
},
watch: {
},
mounted() {
},
}
当组件比较简单只有一个逻辑的时候,稍微麻烦的是用户必须在几个Option之间跳来跳去,这个是小问题,写代码哪能不用鼠标呢😄?挑战往往在复杂的情况下才会出现。当一个vue组件内存在多个逻辑时会怎样呢?

图片中相同的颜色表示一种逻辑。
- 使用Options API时,相同的逻辑写在不同的地方,各个逻辑的代码交叉错乱,这对维护别人代码的开发者来说绝不是一件简单的事,理清楚这些代码都需要花费不少时间。
- 而使用Composition API时,相同的逻辑可以写在同一个地方,这些逻辑甚至可以使用函数抽离出来,各个逻辑之间界限分明,即便维护别人的代码也不会在“读代码”上花费太多时间(前提是你的前任会写代码)。
必须指出的是,Composition API提高了代码的上限,也降低了代码的下限。在使用Options API时,即便再菜的鸟也能保证各种代码按其种类进行划分。但使用Composition API时,由于其开放性,出现什么代码是无法想象的。但毫无疑问,Options API到Composition API是vue的一个巨大进步,vue从此可以从容面对大型项目。
逻辑抽取与复用
在vue2中要实现逻辑复用主要有两种方式:
- mixin
mixin的确能抽取逻辑并实现逻辑复用(我更多用它来定义接口),但这种方式着实不好,mixin更多是一种代码复用的手段:- 命名冲突。mixin的所有option如果与组件或其它mixin相同会被覆盖(这个问题可以使用Mixin Factory解决)。
- 没有运行时实例。顾名思义,mixin不过是把对应的option混入组件内,运行时不存在所抽取的逻辑实例。
- 松散的关系。Options API注定所抽取的逻辑的组织是松散,逻辑内部之间的关系也是松散的。
- 含蓄的属性增加。mixin加入的option是含蓄的,新手会迷惑于莫名其妙就存在的一个属性,尤其是在有多个mixin的时候,无法知道当前属性是哪个mixin的。
- Scoped slot
scoped slot也可以实现逻辑抽取,使用一个组件抽取逻辑,然后通过作用域插槽暴露给子组件。
// GenericSort.vue
<template>
<div>
<!-- 暴露逻辑的数据给子组件 -->
<slot :data="data"></slot>
</div>
</template>
<script>
export default {
// 在这里完成逻辑
data() {
}
}
</script>
使用的时候
<template>
<!--传入未排序的数据unSortdata-->
<GenericSort data="unSortdata">
<template v-slot="sortData">
<!-- 使用经过处理后的sortData数据 -->
<template>
</GenericSearch>
</template>
但这种方式还是有许多缺点:
- 增加缩进。一两个时没多大影响,但过多时使可读性变差。
- 一般需要增加配置,不灵活。需要在slot上增加配置,以应对更多的情况。
- 性能差。仅为了抽取逻辑,需要创建维护一个组件实例。
- Composition Function
在vue3中提供了一种叫Composition Function的方式,这种方式允许像函数般抽离逻辑:
<template>
<div>
<p> {
{
count}}</p>
<button @click="onClick" :disabled="state">Start</button>
</div>
</template>
<script>
import {
ref} from 'vue'
// 倒计时逻辑的Composition Function
const useCountdown = (initialCount) => {
const count = ref(initialCount)
const state = ref(false)
const start = (initCount) => {
state.value = true;
if (initCount > 0) {
count.value = initCount
}
if (!count.value) {
count.value = initialCount
}
const interval = setInterval(() => {
if (count.value === 0) {
clearInterval(interval)
state.value = false
} else {
count.value--
}
}, 1000)
}
return {
count,
start,
state
}
}
export default {
setup() {
// 直接使用倒计时逻辑
const {
count, start, state} = useCountdown(10)
const onClick = () => {
start()
}
return {
count, onClick, state}
}
}
</script>
vue3建议使用如React hook中一样使用use开头命名抽取的逻辑函数,如上代码抽取的逻辑几乎如函数一般,使用的时候也极其方便,完胜vue2中抽取逻辑的方法。
ts类型支持
vue2比较令人诟病的地方还是对ts的支持,对ts支持不好是vue2不适合大型项目的一个重要原因。其根本原因是Vue依赖单个this上下文来公开属性,并且vue中的this比在普通的javascript更具魔力(如methods对象下的单个method中的this并不指向methods,而是指向vue实例)。换句话说,尤大大在设计Option API时并没有考虑对ts引用的支持。那vue2中是怎样做到对ts的支持?
<script lang="ts">
import {
Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Prop(Number) readonly propA: number | undefined
@Prop({
default: 'default value' }) readonly propB!: string
@Prop([String, Boolean]) readonly propC: string | boolean | undefined
}
</script>
vue2对ts的支持主要是通过vue class component,还需引入vue-property-decorator包,该库完全依赖于vue-class-component包。咋一看,这不是支持了吗?下面聊聊这种方式的缺点:
- vue class component与js的vue组件差异太大,另外需要引入额外的库,学习成本大幅度增高。
- 依赖于装饰器语法。而目前装饰器目前还处于stage2阶段(可查看tc39 decorators),在实现细节上还存在许多不确定性,这使其成为一个相当危险的基础。
- 复杂性增高。采用Vue Class Component且需要使用额外的库,相比于简单的js vue组件,显然复杂化。
这些原因让人望而却步,vue2的ts项目数量不多也是可以让人理解的。相比与vue2,vue3对ts的支持则好得多:
- vue3中是在setup中进行编程,setup不依赖
this,大部分API大多使用普通的变量和函数,它们天然类型友好。 - 用Composition API编写的代码可以享受完整的类型推断,几乎不需要手动类型提示。这也意味着用提议的API编写的代码在TypeScript和普通JavaScript中看起来几乎相同。
- 这些接口已获得更好的IDE支持,即使非TypeScript用户也可以从键入中受益。
接口一览
Composition API是一系列接口的总称,下文将逐一介绍Composition API的各个接口。学习代码
setup
setup是vue新增的一个选项,它是组件内使用Composition API的入口。setup中是没有this上下文的(js 函数都有this,但是setup的this跟vue2的this不是一个东西,已经完全没用了,故为没有)。
- 执行时机
setup是在创建vue组件实例并完成props的初始化之后执行,也是在beforeCreate钩子之前执行,这意味着setup中无法使用其它option(如data)中的变量,而其它option可以使用setup中返回的变量。 - 与模板结合使用
如果setup返回一个对象,这个对象的所有属性会合并到template的渲染上下文中,也就是说可以在template中使用setup返回的对象的属性。
<template>
<div>{
{
count }} {
{
object.foo }}</div>
</template>
<script>
<

Vue3 引入了Composition API,彻底改变了编程方式。本文详细介绍了为何使用Composition API,包括代码组织、逻辑抽取与复用的改进,以及如何在模板中使用refs。此外,还探讨了setup函数、watch接口的增强以及模板refs的处理。文章指出,虽然Composition API提高了代码上限,但也可能降低代码下限,需要良好的编程习惯。最后,列举了生命周期钩子和Composition API的主要接口。
最低0.47元/天 解锁文章
2447

被折叠的 条评论
为什么被折叠?



