八、Vue3 Composition API (二)
Lyk 2022/8/27 Vuecomputed函数组件生命周期函数Provide和inject使用watchwatchEffect自定义Hookscript setup语法糖写法definePropsdefineEmitsdefineExpose
# 1、computed
- 在前面我们讲解过计算属性computed:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理
- 在前面的Options API中,我们是使用computed选项来完成的;
- 在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
- 如何使用computed呢?
- 方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;
- 方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象;
# 2、setup中使用ref(获取元素及组件)
- 在setup中如何使用ref获取元素或者组件?
- 其实非常简单,我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可
- 这里我们用vue2,vue3的方式分别演示;如下图所示
# 3、生命周期钩子
- 我们前面说过 setup 可以用来替代 data 、 methods 、 computed 等等这些选项,也可以替代 生命周期钩子。
- 那么setup中如何使用生命周期函数呢?
- 可以使用直接导入的 onX 函数注册生命周期钩子
# 4、Provide函数和Inject函数
# 4.1.Provide函数
- 事实上我们之前还学习过Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项。
- 我们可以通过 provide来提供数据:
- 可以通过 provide 方法来定义每个 Property;
- provide可以传入两个参数:
- name:提供的属性名称;
- value:提供的属性值;
<script>
import { provide } from 'vue'
export default {
setup() {
let counter = 100
let info = {
name:'kobe',
age:45
}
provide("counter", counter)]
provide("info", info)
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 4.2.Inject函数
- 在 后代组件 中可以通过 inject 来注入需要的属性和对应的值:
- 可以通过 inject 来注入需要的内容;
- inject可以传入两个参数:
- 要 inject 的 property 的 name;
- 默认值;
<script>
import { inject } from 'vue'
export default {
setup() {
const conuter = inject("counter")
const info = inject("info")
}
}
</script>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4.3.数据的响应式
- 为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 和 reactive。
<script>
import { ref, reactive, provide } from 'vue'
export default {
setup() {
let counter = ref(100)
let info = reactive({
name:'kobe',
age:45
})
provide("counter", counter)]
provide("info", info)
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 5、侦听器Watch
# 5.1.侦听数据的变化
- 在前面的Options API中,我们可以通过watch选项来侦听data或者props的数据变化,当数据变化时执行某一些操作。
- 在Composition API中,我们可以使用watchEffect和watch来完成响应式数据的侦听;
- watchEffect:用于自动收集响应式数据的依赖;
- watch:需要手动指定侦听的数据源;
# 5.2.Watch的使用
- watch的API完全等同于组件watch选项的Property:
- watch需要侦听特定的数据源,并且执行其回调函数;
- 默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;
<script>
import { ref, watch } from 'vue'
export default {
setup() {
const name = ref('kobe')
watch(name,(newValue,oldValue) => {
console.log(newValue,oldValue)
})
return {
name
}
}
}
</script>
<template>
<div class='watch'>
<input type="text" v-model="name">
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 5.3.侦听多个数据源
- 侦听器还可以使用数组同时侦听多个源:
<script>
import { ref, watch } from 'vue'
export default {
setup() {
const name = ref('kobe')
const age = ref(45)
watch([name,age],(newValue,oldValue) => {
console.log(newValue,oldValue)
})
return {
name,
age
}
}
}
</script>
<template>
<div class='watch'>
<input type="text" v-model="name">
<input type="text" v-model="age">
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 5.4.watch的选项
- 如果我们希望侦听一个深层的侦听,那么依然需要设置 deep 为true:
- 也可以传入 immediate 立即执行;即刚开始数据没发生改变就侦听一次;
- 注意:
- 通过reactive处理的对象,默认是深层侦听的;
- 补充:如果你侦听的是一个原始对象:即没有通过ref或reactive处理;会报如下警告
- A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.(监视源只能是getter/effect函数、ref、reactive对象或这些类型的数组。)
<script>
import { reactive, watch } from 'vue'
export default {
setup() {
//深层侦听,和立即执行
const info = reactive({
name:'kobe',
age:45,
friends:[
{id:112,name:'james',age:38}
]
})
watch(() => ({...info}),(newValue,oldValue) => {//这里想要深层侦听,必须设置deep:true
console.log(newValue.friends[0].name,oldValue?.friends[0].name)
console.log(newValue,oldValue)
},{
deep:true
})
watch(info,(newValue,oldValue) => {//通过reactive处理的对象,默认是深层侦听的
console.log(newValue.friends[0].name,oldValue?.friends[0].name)
console.log(newValue,oldValue)
},{
immediate:true
})
return {
info
}
}
}
</script>
<template>
<div class='watch'>
<input type="text" v-model="info.friends[0].name">
</div>
</template>
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
27
28
29
30
31
32
33
34
35
36
37
38
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
27
28
29
30
31
32
33
34
35
36
37
38
# 5.5.watchEffect
- 当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect。
- 我们来看一个案例:
- 首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
- 其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const age = ref(8)
const name = ref('kobe')
watchEffect(() => {
console.log("watchEffect正在执行->", name.value, age.value)
})
function changeAge() {
age.value++
}
return {
age,
name,
changeAge
}
}
}
</script>
<template>
<h2>age:{{age}}</h2>
<button @click="changeAge">age+1</button>
</template>
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 5.6.watchEffect的停止侦听
- 如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。
- 比如在上面的案例中,我们age达到20的时候就停止侦听:
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const age = ref(8)
const name = ref('kobe')
const stopWatch = watchEffect(() => {
console.log("watchEffect正在执行->", name.value, age.value)
//if(age.value >= 20) {
// stopWatch()
//}
})
function changeAge() {
age.value++
if(age.value > 20) {
console.log('停止侦听')
stopWatch()//调用watchEffect的返回值,停止侦听
}
}
return {
age,
name,
changeAge
}
}
}
</script>
<template>
<h2>age:{{age}}</h2>
<button @click="changeAge">age+1</button>
</template>
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
27
28
29
30
31
32
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
27
28
29
30
31
32
# 6、自定义hook练习
- useCounter
- 我们先来对之前的counter逻辑进行抽取:
- useTitle
- 我们编写一个修改title(网站标题)的Hook:
- useScrollPosition
- 我们来完成一个监听界面滚动位置的Hook:
# 7、script setup语法
# 7.1.setup函数语法糖
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,当同时使用 SFC 与组合式 API 时则推荐该语法。- 更少的样板内容,更简洁的代码;
- 能够使用纯 Typescript 声明 prop 和抛出事件;
- 更好的运行时性能 ;
- 更好的 IDE 类型推断性能 ;
- 使用这个语法,需要将 setup attribute 添加到
<script>
代码块上:
<script setup>
console.log("Hello setup语法糖写法")
</script>
1
2
3
2
3
- 里面的代码会被编译成组件 setup() 函数的内容:
- 这意味着与普通的
<script>
只在组件被首次引入的时候执行一次不同; <script setup>
中的代码会在每次组件实例被创建的时候执行。
- 这意味着与普通的
# 7.2.顶层的绑定会被暴露给模板
- 当使用
<script setup>
的时候,任何在<script setup>
声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容)都能在模板中直接使用:
<script setup>
import Home from './Home.vue'
const message = '顶层的绑定直接暴露给模板template,可直接使用'
function btnClick() {
console.log('btnClick')
}
</script>
<template>
<div class="app">
<home></home>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 响应式数据需要通过ref、reactive来创建。
# 7.3.导入的组件直接使用
<script setup>
范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
import Home from './Home.vue'
</script>
<template>
<div class="app">
<home></home>
</div>
</template>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 7.4.defineProps() 和 defineEmits()
父子组件通信在setup函数语法糖写法中的用法
为了在声明 props 和 emits 选项时获得完整的类型推断支持,我们可以使用 defineProps 和 defineEmits API,
它们将自动地在
<script setup>
中可用,不需要另外import引入
# 7.5.defineExpose()
defineExpose也是自动地在
<script setup>
中可用,不需要另外import引入使用
<script setup>
的组件是默认关闭的:- 通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在
<script setup>
中声明的绑定;
- 通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在
通过 defineExpose 编译器宏来显式指定在
<script setup>
组件中要暴露出去的 property: