TypeScript 与 Vue基础语法使用

330 阅读1分钟

今天学习一下vue3和ts语法如何搭配使用。在开始之前首先装一个插件。TypeScript Vue Plugin (Volar)。

defineProps

defineProps是子组件用来接受父组件的传值的。首先来看第一种情况。

  1. 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>

image.png

  1. 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>

  1. 如果需要写默认值的话,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>

image.png

defineEmits

defineEmits是子组件用来给父组件传值的。

  1. 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>

  1. 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>

image.png

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>