Vue3 Composition API

选项式API

下面代码我们用选项式API来实现一个简易的计数器:

1
2
3
4
5
6
7
8
9
10
11
12
export default {
data() {
return {
counter: 0
}
},
methods: {
counterAdd() {
this.counter++
}
}
}

使用data、methods等组件来编写组件确实很有效,然而当组件开始变多变大时,逻辑的关注点列表也开始增长,在选项式API中,组件的逻辑的数据和方法被分别保存在data和methods中,这样的碎片化开发导致组件难以阅读和理解。在组合式API中,可以把一个逻辑的数据和方法收集在一起。

setup函数

setup在组件被创建之前执行,它被作为组合式API的入口。

setup中应该避免使用this,因为它会找不到组件实例,setup发生在datapropertycomputedmethods之前,所以他们在setup中无法被获取

setup添加到组件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
props: {
user: {
type: String,
required: true
}
},
setup(props) {
console.log(props) // { user: '' }

return {} // 这里返回的任何内容都可以用于组件的其余部分
}
// 组件的“其余部分”
}

setup函数可以返回变量、函数,可以把Vue2中的datamethods中的数据和函数放在setup中。

1
2
3
4
5
6
7
8
9
10
11
function setup() {
let counter = 0;
const counterAdd = () => {
counter++
}

return {
counter,
counterAdd // 返回的函数,它的行为与将其定义在 methods 选项中的行为相同
}
}

这里定义了一个计数器,但是无法生效,因为counter变量是非响应式的,这意味着无论调用多少次counterAdd函数,counter的值始终为0。

参数

props

propssetup函数的第一个参数,props是响应式的,因此不能使用解构。

1
2
3
4
5
6
7
8
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}

如果需要解构,可以使用toRefs函数来完成:

1
2
3
4
5
6
7
import { toRefs } from 'vue'

setup(props) {
const { title } = toRefs(props)

console.log(title.value)
}

context

contextsetup的第二个参数,它是一个普通的对象,暴露了其他能在setup中能用的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
setup(props, context) {
// Attribute (非响应式对象,等同于 $attrs)
console.log(context.attrs)

// 插槽 (非响应式对象,等同于 $slots)
console.log(context.slots)

// 触发事件 (方法,等同于 $emit)
console.log(context.emit)

// 暴露公共 property (函数)
console.log(context.expose)
}
}

因为context是一个普通的对象,因此可以使用解构:

1
2
3
4
5
export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}

结合mustache使用

如果 setup 返回一个对象,那么该对象的值以及传递给 setupprops 参数中的值就都可以在模板中访问到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>{{ counter }}</div>
</template>

<script>
import { ref } from 'vue'

export default {
setup() {
let counter = ref(0);
const counterAdd = () => {
counter.value++
}
return {
counter,
counterAdd
}
}
}
</script>

setup中返回的refs在mustache中是会被自动解包的,因此不用在模板中使用.value

使用this

setup中的this与组件选项中的this是完全不同的,所以在setup中应该要避免使用this

ref响应式变量

在Vue3中,我们可以通过一个新的ref函数来创建响应式变量。

1
2
3
import { ref } from 'vue'

const counter = ref(0)

ref函数将接受的参数包裹在一个带有value值的对象中返回,然后使用value可以访问响应式变量的值。

1
2
3
4
5
6
7
8
9
import { ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1

这样我们可以把计数器应用改成使用响应式变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
import { ref } from 'vue'

function setup() {
let counter = ref(0);
const counterAdd = () => {
counter.value++
}

return {
counter,
counterAdd
}
}

setup中注册生命周期钩子

为了让组合式API和选项式API一样完整,还需要在setup中注册生命周期钩子函数,组合式API的生命周期钩子与选项式相同,只是加上了前缀on,如mounted变成了onMounted

将其添加到setup中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ref, onMounted} from 'vue'

function setup() {
let counter = ref(0);
const counterAdd = () => {
counter.value++
}

onMounted(counterAdd) // 在mounted时调用counterAdd

return {
counter,
counterAdd
}
}

watch响应式更改

Vue3中的watch和选项式API的watch很类似,它接受三个参数:

  1. 要监听的响应式变量
  2. callback
  3. 可选的配置选项

Vue3中watch的用法:

1
2
3
4
5
6
import { ref, watch } from 'vue'

const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})

以上写法等同于选项式API中的以下写法

1
2
3
4
5
6
7
8
9
10
11
12
export default {
data() {
return {
counter: 0
}
},
watch: {
counter(newValue, oldValue) {
console.log('The new counter value is: ' + this.counter)
}
}
}

watch应用到计数器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ref, onMounted, watch } from 'vue'

function setup() {
let counter = ref(0);
const counterAdd = () => {
counter.value++
}

watch(counter, (newValue, oldValue) => { console.log(`Counter added!The current value of counter is ${counter.value}`) })

return {
counter,
counterAdd
}
}

computed属性

refwatch一样,也可以从Vue中导入computed函数创建计算属性。

1
2
3
4
5
6
7
8
import { ref, computed } from 'vue'

const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)

counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2

我们给computed函数传递了一个函数,它是类似gettercallback,输出的是一个类似ref只读的响应式变量,一样使用value访问它的值。

实现代码复用

把组件里的数据和函数都放在setup里会使setup函数变得非常大,为了避免这样,我们要将组件的代码提取到一个独立的组合式函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// @/src/components/counter.js
import { ref, watch, onMounted } from 'vue'

export default function counterSetup() {
let counter = ref(0);
const counterAdd = () => {
counter.value++
}

onMounted(counterAdd)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})

return {
counter,
counterAdd
}
}

然后我们在组件中使用它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import counterSetup from '@/src/components/counter.js'

export default {
setup() {
const { repositories, getUserRepositories } = counterSetup()

return {
counter,
counterAdd
}
},
data() {
return {

}
},
computed: {

},
methods: {

}
}

生命周期钩子

选项式 API 组合式API
beforeCreate Not needed*
created Not needed*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered
activated onActivated
deactivated onDeactivated