Vue3 组合式 API 学习

创建项目

需要 Node.js 16.0 或更高版本,执行指令

1
npm init vue@latest

这一指令将会安装并执行 create-vue

setup

执行周期

执行时机比 beforeCreate() 还要早,所以也无法使用 this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
export default{
    setup(){
        console.log('setip', this)
    },
    beforeCreate(){
        console.log('beforeCreate')
    }
}
</script>

执行后,先打印 setup undefined 然后才是 beforeCreate

数据调用

如果想要在 template 中使用 setup() 函数里定义的数据或函数,必须要 return

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<script>
export default{
    setup(){
        // data
        const msg = 'hello vue3'
        // function
        const logMsg = () => {
            console.log(msg)
        }
        // return
        return{
            msg,
            logMsg
        }
    }
}
</script>

只有 return 之后,才可以在 template 里使用

1
2
3
4
<template>
<div>{{ msg }}</div>
<button @click="logMsg">button</button>
</template>

语法糖

每次都要返回属实麻烦,实际上可以直接通过在 script 标签加上 setup 这样就可以自动返回,上述就可以改为

1
2
3
4
5
6
<script setup>
const msg = 'hello vue3'
const logMsg = () => {
        console.log(msg)
    }
</script>

当然,实际上还是会 return 的,只是不用写出来了

响应式数据

reactive

reactive() 函数接收一个对象类型的数据,返回一个响应式的对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<script setup>
import {reactive} from 'vue'
const state = reactive({
  count: 100
})
const addCount = () => {
  state.count++
}
</script>

<template>
<div>
  <div>{{ state.count }}</div>
  <button @click="addCount">+1</button>
</div>
</template>

ref

ref() 接收简单类型或复杂类型,返回一个响应式对象。其本质是在原有传入数据的基础上,外层包了一个对象,包成了更复杂类型。其实也是再使用 reactive() 实现的响应式

所以,在 <script> 中访问数据,需要通过 .value 才行。而在 <template> 中访问数据直接用变量就行。上述例子用 ref() 重写如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script setup>
import {ref} from 'vue'
const c = ref(0)
const addC = () => {
  c.value++
}
</script>

<template>
<div>
  <div>{{ c }}</div>
  <button @click="addC">+1</button>
</div>
</template>

实际开发只使用 ref() 会比较灵活统一

computed

计算属性 computed() 与 Vue2 类似,只是变成了可以任意调用的函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script setup>
import { computed, ref} from 'vue'

const list = ref([1, 2, 3, 4, 5, 6, 7, 8])

const computedList = computed(() => {
    return list.value.filter(item => item > 2)
})
</script>

<template>
<div>{{ list }}</div>
<div>{{ computedList }}</div>
</template>

这样创建出来的属性是只读的,不是可写的,若需要可写,需要显式声明 get()set(),以下摘自官方示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script>
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0
</script>

摘自: https://cn.vuejs.org/api/reactivity-core.html#computed

watch

watch() 函数同样是侦听一个或多个数据的变化,数据变化时执行回调函数。不过多了两个额外参数 immediatedeep

  • 单个数据
1
2
3
watch(count, (newValue, oldValue) => {
    console.log('count changed', oldValue, newValue)
})
  • 多个数据
1
2
3
watch([count, name], ([newCount, newName], [oldCount. oldName]) => {
    console.log('count or name has changed', [newCount, newName], [oldCount. oldName])
})

示例,这里我用了简化的 pug,不过其实也很好懂啦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script setup>
import { ref, watch } from 'vue'

const count = ref(0)
const name = ref('John')

const addCount = () => count.value++
const changeName = () => name.value = 'Mike'

// 监视单个数据的变化
watch(count, (newValue, oldValue) =>{
    console.log('count changed', oldValue, newValue)
})

// 监听多个数据的变化
watch([count, name], (newArr, oldArr) => {
    console.log('count or name changed', oldArr, newArr)
})
</script>

<template lang="pug">
    div count: {{ count }}
    button(@click="addCount") +1
    div name: {{ name }}
    button(@click="changeName") change name
</template>

immediate

immediate 表示立即执行,即在进入页面后会立马执行一次,此时的 oldValueundefined

1
2
3
4
5
watch(count, (newValue, oldValue) =>{
    console.log('count changed', oldValue, newValue)
}, {
    immediate: true
})

deep

deep 是深度监视,因为默认 watch 进行浅层监视是无法监视到复杂类型的变化的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script setup>
import { ref, watch } from 'vue'

const userInfo = ref({
    name: 'John',
    age: 18
})

const changeUserInfo = () => {
    userInfo.value.age++
}

watch(userInfo, (newValue) => {
    console.log('userInfo changed', newValue)
}, {
    deep: true
})
</script>

<template lang="pug">
    div {{ userInfo }}
    button(@click="changeUserInfo") change userInfo
</template>

复杂类型单个属性侦听

使用 deep 将会对复杂类型所有属性进行侦听,也就是只要任一属性发生变化,都会执行函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script setup>
import { ref, watch } from 'vue'

const userInfo = ref({
    name: 'John',
    age: 18
})

const changeAge = () => {
    userInfo.value.age++
}
const changeName = () => {
    userInfo.value.name = 'Mike'
}

watch(() => userInfo.value.age, (newValue, oldValue) => {
    console.log('age changed', oldValue, newValue)
})
</script>

<template lang="pug">
    div {{ userInfo }}
    button(@click="changeAge") change age
    button(@click="changeName") change name
</template>

这样就只有 age 改变时候才会触发,且返回值是 age 的值

生命周期函数

Vue3 的生命周期对比

Option APIComposition API
beforeCreate/createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted

和组合式 API 的不同就是变成了函数调用,且可以调用多次

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<script setup>
import { onMounted } from 'vue';

// 组合式的 beforeCreate 和 created 直接写
const getList = () => {
    console.log('从后端获取数据')
}

// 执行,进入页面便请求
getList()
    
// 生命周期函数可以调用多次,会按顺序执行
onMounted(() => {
    console.log('逻辑一')
})
onMounted(() => {
    console.log('逻辑二')
})
</script>
This post is licensed under CC BY-NC-SA 4.0 by the author.