这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天
组合式 API
组合式 API 介绍
Vue3 一个很重要的特性那便是从选项式 API -> 组合式 API的转变, 虽然选项式 API 使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效,但是当我们的组件开始变得更大时,逻辑关注点的列表也会增长,这会导致组件难以阅读和理解,从而使项目变得越来越难以维护(就是选项式 API 逻辑太分散了,项目做大后不利于维护)。
这次新增加的组合式 API 中,它将同一个功能的逻辑和数据放置在了一起,使同一个的功能代码更加聚合。
setup 函数简单介绍
因此在 Vue3.0 版本中组合式 API 便出现 setup 函数的写法
//undefined //beforeCreate
<script lang="ts">
export default {
setup() {
console.log(this);
},
beforeCreate() {
console.log("beforeCreate");
},
};
</script>
会发现最终输出的结果为undefined和beforeCreate,我们可以得出如下结论
-
setup函数在任何生命周期函数之前执行 -
setup函数中你应该避免使用this,因为它不会找到组件实例
<script lang="ts">
export default {
setup() {
let count = 1;
return {
count,
};
},
};
</script>
<template>
<h1>{{count}}</h1>
<button @click="count++">+1</button>
</template>
你会发现1顺利的在你屏幕上显示出来,但是你无论怎么按 button 你的数据都不会发生变化,我们可以得出如下结论
- setup 中函数定义变量或者对象要写在 return 中,才可以在模板中使用
- 我们创建的数据并不是响应式的
//与上方代码完全相同
<script setup lang="ts">
let count = 1;
</script>
<template>
<h1>{{ count }}</h1>
<button @click="count++">+1</button>
</template>
可以很明显的发现script setup 语法糖有着更少的样板内容,更简洁的代码,因此我们接下来的代码都会以script setup 语法糖方式来书写。
响应式组件 ref
我们还是使用上方累加的例子,如下我们把它改成响应式的
<script setup lang="ts">
import { ref } from 'vue';
let count = ref(1)
count.value++
</script>
<template>
<h1>{{ count }}</h1>
<button @click="count++">+1</button>
</template>
<style>
- ref 可以创建响应式数据,但不建议包含引用数据类型
- 在 TypeScript 中通过 value 属性才能读取或者修改数据。
- 在 template 模板中直接通过变量名称才能获取读取或者修改数据。
- 详情可见Vue3 官网-ref 的响应式变量
响应式组件 reactive
我们用 reactive 创建一个引用数据类型的响应式数据,并通过点击按钮改变它的值
<script setup lang="ts">
import { reactive } from "vue";
const person = reactive({
name: "suemor",
age: 18,
});
const changePerson = () => {
(person.name = "小杰"), (person.age = 28);
};
// const name = reactive('小黄') 无效写法
</script>
<template>
<button @click="changePerson">点我改变person属性</button>
<ul>
<li v-for="(item, index) in person" :key="index">{{item}}</li>
</ul>
</template>
- reactive 可以创建引用数据类型的响应式数据
- reactive 修改值并不需要通过
value - reactive 函数只能基于引用数据类型创建响应式数据,不能创建基本数据类型
- ref 在 Typescript 中使用时需要点上 value, 而 reactive 在 Typescript 中使用时不需要点上 value,在模板中使用时都不需要加 value
计算属性 computed
响应式 API 中 computed 和之前的 组合式 API 的 computed 选项用法类似,但需要先引入:import { computed } from "vue"
<script setup lang="ts">
import { computed, ref } from 'vue';
const num1 = ref(1)
const num2= ref(1)
const sum = computed(()=>num1.value + num2.value)
</script>
<template>
<h1>{{sum}}</h1>//2
</template>
<style>
- 需要先引入:
import { computed } from "vue" - 回调函数的返回值就是计算结果
监听状态 watch
watch 函数用于监听响应式数据的变化,下面的示例演示了如何使用watch
<script setup lang="ts">import {watch,reactive, ref } from 'vue';
const person = ref('')
watch(person,(oldValue,newValue)=>{
console.log(oldValue,newValue);
})
</script>
<template>
<input v-model="person" type="text" placeholder="请输入姓名">
</template>
<style>
- 需要先引入:
import { watch } from "vue" - 用于监听响应式数据的变化
defineProps 和 defineEmits
在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits 。因为我们使用了 Typescript,所以我们可以尝试使用纯类型声明来声明 props 和 emits
子组件
<template>
<h1>{{ props.msg }}</h1>
<button @click="handleClick">点击我调用父组件方法</button>
</template>
<script setup lang="ts">
//props
const props = withDefaults(
defineProps<{
msg?: string;
}>(),
{
msg: "我是默认值",
}
);
//emits
const emit = defineEmits<{
(e: "on-change", data: string): void;
}>();
const handleClick = () => emit("on-change", "父组件方法被调用了");
</script>
父组件
<script setup lang="ts">
import { ref } from "vue";
import TestPropsEmit from "./views/TestPropsEmit.vue";
const msg = ref("我是来自父组件的值");
const handleChange = (data: string) => {
console.log(data);
};
</script>
<template>
<TestPropsEmit :msg="msg" @on-change="handleChange" />
</template>
最终结果如下图
defineProps和defineEmits都是只在<script setup>中才能使用的编译器宏。他们不需要导入且会随着<script setup>处理过程一同被编译掉。defineProps和defineEmits在选项传入后,会提供恰当的类型推断。- 通过
withDefaults可以给props提供默认值