今天学习一下vue3和ts语法如何搭配使用。在开始之前首先装一个插件。TypeScript Vue Plugin (Volar)。
defineProps
defineProps是子组件用来接受父组件的传值的。首先来看第一种情况。
- defineProps配合Vue默认语法进行类型校验。
APP.vue
<template>
App
<hr />
<Child :age="18" name="尼古拉是"></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
</script>
<style lang="scss" scoped></style>
Child.vue
<template>
<div>
Child
<p>age: {{ age }}</p>
<p>name: {{ name }}</p>
</div>
</template>
<script setup lang="ts">
defineProps({
age: {
type: Number,
required: true,
},
name: {
type: String,
required: true,
},
});
</script>
<style lang="scss" scoped></style>
- defineProps配合TS的泛型定义props类型校验
Child.vue
<template>
<div>
Child
<p>age: {{ age }}</p>
<p>name: {{ name }}</p>
</div>
</template>
<script setup lang="ts">
interface IProps {
age: number;
name?: string;
}
defineProps<IProps>();
</script>
<style lang="scss" scoped></style>
- 如果需要写默认值的话,props可以通过解构来指定默认值。
App.vue
<template>
App
<hr />
<Child :age="18"></Child>
</template>
<script setup lang="ts">
import Child from "./Child.vue";
</script>
<style lang="scss" scoped></style>
Child.vue
<template>
<div>
Child
<p>age: {{ age }}</p>
<p>name: {{ name }}</p>
</div>
</template>
<script setup lang="ts">
interface IProps {
age: number;
name?: string;
}
const { age, name = "赵四" } = defineProps<IProps>();
</script>
<style lang="scss" scoped></style>
defineEmits
defineEmits是子组件用来给父组件传值的。
- defineEmits的基本使用
App.vue
<template>
<div>
App
<p>age: {{ age }}</p>
<p>name: {{ name }}</p>
<hr />
<Child
:age="age"
:name="name"
@handleChangeAge="handleChangeAge"
@handleChangeName="handleChangeName"
></Child>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
const age = ref(18);
const name = ref("尼古拉是");
const handleChangeAge = (num: number) => {
age.value += num;
};
const handleChangeName = (newName: string) => {
name.value = newName;
};
</script>
<style lang="scss" scoped></style>
Child.vue
<template>
<div>
Child
<p>age: {{ age }}</p>
<p>name: {{ name }}</p>
<button @click="changeAge">ChangeAge</button>
<button @click="changeName">ChangeName</button>
</div>
</template>
<script setup lang="ts">
interface IProps {
age: number;
name?: string;
}
defineProps<IProps>();
// defineEmits生成emits触发
const emits = defineEmits(["handleChangeAge", "handleChangeName"]);
const changeAge = () => {
emits("handleChangeAge", 1);
};
const changeName = () => {
emits("handleChangeName", "赵四");
};
</script>
<style lang="scss" scoped></style>
- defineEmits的配合ts使用
Child.vue
<template>
<div>
Child
<p>age: {{ age }}</p>
<p>name: {{ name }}</p>
<button @click="changeAge">ChangeAge</button>
<button @click="changeName">ChangeName</button>
</div>
</template>
<script setup lang="ts">
interface IProps {
age: number;
name?: string;
}
defineProps<IProps>();
// const emits = defineEmits(['handleChangeAge', 'handleChangeName'])
// 改造成 TS 的写法
const emits = defineEmits<{
// 第一个参数是自定义事件名
// 第二个参数是参数的类型
(e: "handleChangeAge", num: number): void;
(e: "handleChangeName", newName: string): void;
}>();
const changeAge = () => {
emits("handleChangeAge", 1);
};
const changeName = () => {
emits("handleChangeName", "赵四");
};
</script>
<style lang="scss" scoped></style>
ref
通过泛型指定value的值类型,如果是简单值,该类型可以省略,如果是复杂类型,可以通过泛型来指定初始值的类型
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { Ref, ref } from "vue";
// 泛型参数规定了初始值的类型
// const money = ref<number>(10);
// 需求:根据后端返回的数据渲染列表
/* type TList = {
id: number;
name: string;
}; */
interface TList {
id: number;
name: string;
}
// 第一种定义方式(建议)
const list = ref<TList[]>([]);
// 第二种定义方式
// const list: Ref<TList[]> = ref([]);
setTimeout(() => {
list.value = [
{
id: 1,
name: "卢布",
},
{
id: 2,
name: "张飞",
},
];
}, 1000);
</script>
<style scoped></style>
computed
通过泛型可以指定computed计算属性的类型,通常可以省略。
<template>
<div>
{{ r.charAt(0) }}
</div>
</template>
<script setup lang="ts">
import { computed, reactive } from "vue";
const data = reactive({
firstName: "吕",
lastName: "布",
});
const r = computed<string>(() => {
return data.firstName + "" + data.lastName;
});
</script>
<style scoped></style>
如何拿到事件对象的类型
<template>
<div
style="background-color: pink; height: 300px"
@mousemove="handleMousemove"
>
x: {{ data.x }} y: {{ data.y }}
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
// 如何给事件对象指定类型
// 需求:移动到div,把移动的坐标显示到桌面
const data = ref({
x: 0,
y: 0,
});
const handleMousemove = (e: MouseEvent) => {
data.value.x = e.pageX;
data.value.y = e.pageY;
};
</script>
<style scoped></style>
如何获取到DOM
<template>
<div>
<!-- 2 用 imgRef 和 img 标签的 ref 属性进行绑定 -->
<img src="https://pinia.vuejs.org/logo.svg" ref="imgRef" />
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
// 1 创建一个ref引用
// 值可能是HTMLImageElement 或 null 类型
const imgRef = ref<HTMLImageElement | null>(null);
// 需求:期望页面渲染完毕拿到 img 的src
onMounted(() => {
// 3
// imgRef.value 就是DOM
// ! 表示非空断言(排除掉 null 或 undefined 的情况)
// ? 可选链,表示 ?前面的东西存在(不为 null 或 undefined) 才往后面继续获取
console.log(imgRef.value?.src);
});
</script>
<style scoped></style>
如何获取组件实例
App.vue
<script setup lang="ts">
import { ref } from "vue";
import Child from "./Child.vue";
// 父组件点击按钮,调用 Child 实例组件内部的方法
// #1
// typeof Child // 表示获取 Child 构造函数的类型
// InstanceType<typeof Child> // 拿到 Child 构造函数实例的类型
const childCmp = ref<InstanceType<typeof Child> | null>(null);
const handleClick = () => {
// #3
// childCmp.value 就表示 Child 组件的实例
// 第一报错
// 第二没提示
childCmp.value?.logHello();
};
</script>
<template>
<div>
App
<button @click="handleClick">click</button>
<hr />
<!-- #2 和 Child 组件的 ref 属性进行绑定 -->
<Child ref="childCmp" />
</div>
</template>
Child.vue
<script setup lang="ts">
const logHello = () => {
console.log("我本以为吕布已经天下无敌了,没想到有人比他还勇猛!");
};
defineExpose({
// 暴露出去,供外界使用
logHello,
});
</script>
<template>
<div>Child</div>
</template>