vue3+ts开发干货笔记

news2025/1/23 14:56:28

总结一下在vue3中ts的使用。当篇记录部分来自于vue官网,记录一下,算是加深印象吧。
纯干笔记,不断补充,想到什么写什么,水平有限,欢迎评论指正!

类型标注

props

<script setup lang="ts">
const props = defineProps({
  foo: { type: String, required: true },
  bar: Number
})

props.foo // string
props.bar // number | undefined
</script>

可以通过更直接的泛型定义

<script setup lang="ts">
const props = defineProps<{
  foo: string
  bar?: number
}>()
</script>

也可以将 props 的类型移入一个单独的接口中

<script setup lang="ts">
interface Props {
  foo: string
  bar?: number
}

const props = defineProps<Props>()
</script>

把定义单独放到另一个文件中,引入使用

<script setup lang="ts">
import type { Props } from './foo'

const props = defineProps<Props>()
</script>

设置默认值— 通过 withDefaults 编译器宏

export interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

复杂类型的props

<script setup lang="ts">
interface Book {
  title: string
  author: string
  year: number
}

const props = defineProps<{
  book: Book
}>()
</script>

也可以借助PropType 工具类型

import type { PropType } from 'vue'

const props = defineProps({
  book: Object as PropType<Book>
})

没有使用 < script setup > 时,使用 defineComponent()

import { defineComponent } from 'vue'

export default defineComponent({
  props: {
    message: String
  },
  setup(props) {
    props.message // <-- 类型:string
  }
})
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

export default defineComponent({
  props: {
    book: Object as PropType<Book>
  }
})

emits

<script setup lang="ts">
// 运行时
const emit = defineEmits(['change', 'update'])

// 基于选项
const emit = defineEmits({
  change: (id: number) => {
    // 返回 `true` 或 `false`
    // 表明验证通过或失败
  },
  update: (value: string) => {
    // 返回 `true` 或 `false`
    // 表明验证通过或失败
  }
})

// 基于类型
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

vue3.3+: 可选的、更简洁的语法

const emit = defineEmits<{
  change: [id: number]
  update: [value: string]
}>()

若没有使用 < script setup >,defineComponent() 也可以根据 emits 选项推导暴露在 setup 上下文中的 emit 函数的类型:

import { defineComponent } from 'vue'

export default defineComponent({
  emits: ['change'],
  setup(props, { emit }) {
    emit('change') // <-- 类型检查 / 自动补全
  }
})

ref

ref 会根据初始化时的值推导其类型:

import { ref } from 'vue'

// 推导出的类型:Ref<number>
const year = ref(2020)

// => TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2020'

或者在调用 ref() 时传入一个泛型参数,来覆盖默认的推导行为

// 得到的类型:Ref<string | number>
const year = ref<string | number>('2020')

year.value = 2020 // 成功!

另外也可以通过引入 Ref 来指定类型

import { ref } from 'vue'
import type { Ref } from 'vue'

const year: Ref<string | number> = ref('2020')

year.value = 2020 // 成功!

当指定了一个泛型参数但没有给出初始值,那么最后得到的就将是一个包含 undefined 的联合类型:

// 推导得到的类型:Ref<number | undefined>
const n = ref<number>()

reactive

reactive() 也会隐式地从它的参数中推导类型:

import { reactive } from 'vue'

// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })

要显式地标注一个 reactive 变量的类型,可以使用接口:

import { reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue 3 指引' })

TIP
不推荐使用 reactive() 的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。

computed

computed() 会自动从其计算函数的返回值上推导出类型:

import { ref, computed } from 'vue'

const count = ref(0)

// 推导得到的类型:ComputedRef<number>
const double = computed(() => count.value * 2)

// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')

还可以通过泛型参数显式指定类型:

const double = computed<number>(() => {
  // 若返回值不是 number 类型则会报错
})

事件处理函数

<script setup lang="ts">
function handleChange(event) {
  // `event` 隐式地标注为 `any` 类型
  console.log(event.target.value)
}
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 “strict”: true 或 “noImplicitAny”: true 时报出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,你在访问 event 上的属性时可能需要使用类型断言:

function handleChange(event: Event) {
  console.log((event.target as HTMLInputElement).value)
}

provide/inject

provide 和 inject 通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型:

import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'

const key = Symbol() as InjectionKey<string>

provide(key, 'foo') // 若提供的是非字符串值会导致错误

const foo = inject(key) // foo 的类型:string | undefined

建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入。

当使用字符串注入 key 时,注入值的类型是 unknown,需要通过泛型参数显式声明:

const foo = inject<string>('foo') // 类型:string | undefined

注意注入的值仍然可以是 undefined,因为无法保证提供者一定会在运行时 provide 这个值。

当提供了一个默认值后,这个 undefined 类型就可以被移除:

const foo = inject<string>('foo', 'bar') // 类型:string

如果你确定该值将始终被提供,则还可以强制转换该值:

const foo = inject('foo') as string

模版引用

模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:

<script setup lang="ts">
import { ref, onMounted } from 'vue'

const el = ref<HTMLInputElement | null>(null)

onMounted(() => {
  el.value?.focus()
})
</script>

<template>
  <input ref="el" />
</template>

可以通过类似于 MDN 的页面来获取正确的 DOM 接口。

注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null。

组件模版引用

有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法:

<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'

const isContentShown = ref(false)
const open = () => (isContentShown.value = true)

defineExpose({
  open
})
</script>

为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:

<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'

const modal = ref<InstanceType<typeof MyModal> | null>(null)

const openModal = () => {
  modal.value?.open()
}
</script>

注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用这种技巧,需要开启 Volar 的 Takeover 模式。

如果组件的具体类型无法获得,或者你并不关心组件的具体类型,那么可以使用 ComponentPublicInstance。这只会包含所有组件都共享的属性,比如 $el。

import { ref } from 'vue'
import type { ComponentPublicInstance } from 'vue'

const child = ref<ComponentPublicInstance | null>(null)

tsconfig.json

tsconfig.json是 TypeScript 项目的配置文件,放在项目的根目录下。指定了编译项目所需的根目录下的文件以及编译选项。官网参考

配置解析

{
  /* 根选项 */
  "include": ["./src/**/*"], // 指定被编译文件所在的目录
  "exclude": [], // 指定不需要被编译的目录
  //使用小技巧:在填写路径时 ** 表示任意目录, * 表示任意文件。

  /* 项目选项 */
  "compilerOptions": {
    "target": "ES6", // 目标语言的版本
    "module": "commonjs", // 生成代码的模板标准
    "lib": ["DOM", "ES5", "ES6", "ES7", "ScriptHost"], // TS需要引用的库
    "outDir": "./dist", // 指定输出目录
    "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
    "allowJs": true, // 允许编译器编译JS,JSX文件
    "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
    "removeComments": true, // 删除注释
    "esModuleInterop": true, // 允许export=导出,由import from 导入

    /* 严格检查选项 */
    "strict": true, // 开启所有严格的类型检查
    "alwaysStrict": true, // 在代码中注入'use strict'
    "noImplicitAny": true, // 不允许隐式的any类型
    "noImplicitThis": true, // 不允许this有隐式的any类型
    "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
    "strictBindCallApply": true, // 严格的bind/call/apply检查
    "strictFunctionTypes": true, // 不允许函数参数双向协变
    "strictPropertyInitialization": true, // 类的实例属性必须初始化

    /* 额外检查 */
    "noUnusedLocals": true, //是否检查未使用的局部变量
    "noUnusedParameters": true, //是否检查未使用的参数
    "noImplicitReturns": true, //检查函数是否不含有隐式返回值
    "noImplicitOverride": true, //是否检查子类继承自基类时,其重载的函数命名与基类的函数不同步问题
    "noFallthroughCasesInSwitch": true, //检查switch中是否含有case没有使用break跳出
    "noUncheckedIndexedAccess": true, //是否通过索引签名来描述对象上有未知键但已知值的对象
    "noPropertyAccessFromIndexSignature": true, //是否通过" . “(obj.key) 语法访问字段和"索引”( obj[“key”]), 以及在类型中声明属性的方式之间的一致性

    /* 实验选项 */
    "experimentalDecorators": true, //是否启用对装饰器的实验性支持,装饰器是一种语言特性,还没有完全被 JavaScript 规范批准
    "emitDecoratorMetadata": true, //为装饰器启用对发出类型元数据的实验性支持

    /* 高级选项 */
    "forceConsistentCasingInFileNames": true, //是否区分文件系统大小写规则
    "extendedDiagnostics": false, //是否查看 TS 在编译时花费的时间
    "noEmitOnError": true, //有错误时不进行编译
    "resolveJsonModule": true //是否解析 JSON 模块
  }
}

示例

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": false,
    "jsx": "preserve",
    "importHelpers": true,
    "experimentalDecorators": true,
    "strictFunctionTypes": false,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "sourceMap": true,
    "baseUrl": ".",
    "allowJs": false,
    "resolveJsonModule": true,
    "lib": [
      "ESNext",
      "DOM"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ],
      "@build/*": [
        "build/*"
      ]
    },
    "types": [
      "node",
      "vite/client",
      "element-plus/global",
      "@pureadmin/table/volar",
      "@pureadmin/descriptions/volar"
    ]
  },
  "include": [
    "mock/*.ts",
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "types/*.d.ts",
    "vite.config.ts"
  ],
  "exclude": [
    "dist",
    "**/*.js",
    "node_modules"
  ]
}

全局类型声明

global.d.ts和index.d.ts

在 global.d.ts (opens new window)和 index.d.ts (opens new window)文件中编写的类型可直接在 .ts、.tsx、.vue 中使用
在这里插入图片描述
在这里插入图片描述

shims-tsx.d.ts

该文件是为了给 .tsx 文件提供类型支持,在编写时能正确识别语法

import Vue, { VNode } from "vue";

declare module "*.tsx" {
  import Vue from "compatible-vue";
  export default Vue;
}

declare global {
  namespace JSX {
    interface Element extends VNode {}
    interface ElementClass extends Vue {}
    interface ElementAttributesProperty {
      $props: any;
    }
    interface IntrinsicElements {
      [elem: string]: any;
    }
    interface IntrinsicAttributes {
      [elem: string]: any;
    }
  }
}

shims-vue.d.ts

.vue、.scss 文件不是常规的文件类型,typescript 无法识别,所以我们需要通过下图的代码告诉 typescript 这些文件的类型,防止类型报错
在这里插入图片描述
另外,项目开发,我们可能需要安装一些库或者插件什么的,当它们对 typescript 支持不是很友好的时候,就会出现下图的情况

在这里插入图片描述
解决办法就是将这些通过 declare module “包名” 的形式添加到 shims-vue.d.ts 中去,如下图
在这里插入图片描述

全局导入的组件获取类型提示

从 npm下载的组件库或者第三方库

也就是您使用 pnpm add 添加的包,比如 @pureadmin/table ,我们只需要将这个包提供的全局类声明文件导入到 tsconfig.json 的 types 配置项
在这里插入图片描述

然后重启编辑器即可,如下图导入前和导入后的效果对比
导入前,pure-table 无高亮且鼠标覆盖无类型提示
在这里插入图片描述
导入后,pure-table 高亮且鼠标覆盖有类型提示
在这里插入图片描述

提示
当然这个导入前提是这个组件库或者第三方库是有导出全局类声明文件的。

在这里插入图片描述

平台内自定义的全局组件

封装的一个和权限相关的 Auth 组件举例

  • 我们将 Auth 组件在 main.ts 中进行了全局注册
    在这里插入图片描述
  • 然后将 Auth 组件在 global-components.d 中引入,所有的全局组件都应该在 GlobalComponents 下引入才可获得类型支持,如下图
    在这里插入图片描述
  • 最后我们直接写 xxx 就可以得到类型提示啦,如下图
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1347271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Armpro脱壳软件搭建教程附源代码

PHP8.0版本&#xff0c;数据库8.0版本 1.配置注册机文件&#xff0c;打开将arm.zip/res目录下&#xff0c;mt管理器搜索将其全部修改为你自己的域名或者是服务器IP 2.然后建立数据库 数据库账号arm 数据库用户名arm 数据库密码EsZfXY4tD3h2NNA4 3.导入数据库 4.配置Redi…

优化模型:matlab多目标规划

一、多目标规划 1.1 多目标规划的定义 多目标规划&#xff08;Multi-Objective Programming&#xff0c;MOP&#xff09; 是数学优化中的一类问题。与单目标规划不同&#xff0c;多目标规划有多个目标函数需要优化&#xff0c;这些目标函数通常是相互矛盾的。多目标规划的目标…

边界判断缺失

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 学习必须往深处挖&…

android开发调用百度地图api实现加载地图和定位

目录 一.踩的一些坑以及解决方法 1.权限声明不要少 2.地图初始化 3.定位问题 &#xff08;1&#xff09;监听器注册 &#xff08;2&#xff09;定位监听器类MyLocationListener的实现 &#xff08;3&#xff09;定位功能的调用 4.android studio连接真机调试问题 二.…

C语言函数篇——strcat()函数

strcat()函数介绍&#xff1a; strcat()函数是C语言中用于连接两个字符串的函数。它将第二个字符串连接到第一个字符串的末尾&#xff0c;并返回第一个字符串的地址。 strcat()函数的语法&#xff1a; char *strcat(char *dest, const char *src); 其中&#xff0c;dest是目标…

pytorch03:transforms常见数据增强操作

目录 一、数据增强二、transforms--Crop裁剪2.1 transforms.CenterCrop2.2 transforms.RandomCrop2.3 RandomResizedCrop2.4 FiveCrop和TenCrop 三、transforms—Flip翻转、旋转3.1RandomHorizontalFlip和RandomVerticalFlip3.2 RandomRotation 四、transforms —图像变换4.1 t…

【JavaScript】浮点数精度问题

✨ 专栏介绍 在现代Web开发中&#xff0c;JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性&#xff0c;还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言&#xff0c;JavaScript具有广泛的应用场景&#x…

【MySQL表的增删查改】

文章目录 前言1 Create1.1 单行数据 全列插入1.2 多行数据 指定列插入1.3 插入否则更新1.4 替换 2 Retrieve2.1 SELECT 列2.1.1 全列查询2.1.2 指定列查询2.1.3 查询字段为表达式2.1.4 为查询结果指定别名2.1.5 结果去重 2.2 WHERE 条件2.2.1 英语不及格的同学及英语成绩 ( &…

vue 基础学习 一

1. vue 使用快速入门三步走 (1) 新建 HTML 页面&#xff0c;引入 Vue.js文件 1 2 3 4 5 6 7 <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>Vue.js 入门示例</title> <script src"https://cdn.j…

设计模式:抽象工厂模式(讲故事易懂)

抽象工厂模式 定义&#xff1a;将有关联关系的系列产品放到一个工厂里&#xff0c;通过该工厂生产一系列产品。 设计模式有三大分类&#xff1a;创建型模式、结构型模式、行为型模式 抽象工厂模式属于创建型模式 上篇 工厂方法模式 提到工厂方法模式中每个工厂只生产一种特定…

12.29_黑马数据结构与算法笔记Java

目录 305 旅行商问题 动态规划 实现2 306 旅行商问题 动态规划 实现3 307 分治 概述 308 快速选择算法 分治 309 快速选择算法 数组第k大数 Leetcode215 310 快速选择算法 数组中位数 311 快速幂 分治 312 快速幂 Leetcode50 313 平方根整数部分 Leetcode69-1 314 平方…

从实际工作情况,介绍嵌入式(MCU)软件开发常用(通用)工具

目录 前言 1、代码阅读及编辑工具&#xff08;VSCode、Understand&#xff09; 2、代码对比工具&#xff08;Beyond Compare&#xff09; 3、代码仓库相关工具&#xff08;Git、SVN、Tortoise&#xff09; 4、文本编辑器&#xff08;Notepad&#xff09; 5、电脑文件搜索工…

arkts状态管理使用(@State、@Prop、@Link、@Provide、@Consume、@objectLink和@observed)

一、状态管理 1.在声明式UI中&#xff0c;是以状态驱动视图更新&#xff1a; ①状态&#xff08;State&#xff09;:指驱动视图更新的数据&#xff08;被装饰器标记的变量&#xff09; ②视图&#xff08;View&#xff09;:基于UI描述渲染得到用户界面 注意&#xff1a; ①…

Radar System Pro - Plug Play Solution

Radar System Pro是一款功能多样且可定制的资源,旨在通过功能齐全且易于使用的雷达系统增强您的Unity项目。无论您是在开发第一人称射击游戏、策略游戏还是太空探索模拟器,我们的雷达系统都将为您提供所需的工具,以创建引人入胜且身临其境的体验。 雷达系统是一个模块化资产…

mysql间隙锁demo分析

概述 通常用的mysql都是innodb引擎&#xff1b; 一般在update的时候用id都会认为是给行记录加锁&#xff1b; 在使用非唯一索引更新时&#xff0c;会遇到临键锁&#xff08;范围锁&#xff09;&#xff1b; 临键锁和表中的数据有关&#xff1b; mysq版本:8 隔离级别&#xf…

雨课堂作业整理

第一次作业 1.下列序列是图序列的是&#xff08; &#xff09; A.1&#xff0c;2&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;4&#xff0c;5 B.1&#xff0c;1&#xff0c;2&#xff0c;2&#xff0c;4&#xff0c;6&#xff0c;6 C.0&#xff0c;0&#xff0c;2&am…

解决基于VectorGrid的矢量瓦片Y轴偏移的问题

目录 前言 一、GeoServer的瓦片 1、GeoWebcache缓存配置 2、矢量瓦片本地缓存 3、瓦片访问 二、VectorGrid加载本地瓦片 1、加载关键代码 2、默认模式的问题 3、问题分析 4、tms参数修改 总结 前言 在前面的博文介绍中&#xff0c;在线连接如下&#xff1a;浅谈前端自定义…

AI与数字化映像:颜值开端,功能至上_光点科技

在人工智能的浪潮中&#xff0c;AI数字人的兴起正成为一个不可忽视的现象。随着ChatGPT等生成式AI算法的进步&#xff0c;AIGC&#xff08;人工智能生成内容&#xff09;的应用呈现出爆发性增长&#xff0c;不仅在技术圈引起广泛关注&#xff0c;也为元宇宙及其相关产业链带来了…

C++每日一练(8):图像相似度

题目描述 给出两幅相同大小的黑白图像&#xff08;用0-1矩阵&#xff09;表示&#xff0c;求它们的相似度。 说明&#xff1a;若两幅图像在相同位置上的像素点颜色相同&#xff0c;则称它们在该位置具有相同的像素点。两幅图像的相似度定义为相同像素点数占总像素点数的百分比。…

非科班,培训出身,怎么进大厂?

今天分享一下我是怎么进大厂的经历&#xff0c;希望能给大家带来一点点启发&#xff01; 阿七毕业于上海一所大学的管理学院&#xff0c;在读期间没写过一行 Java 代码。毕业之后二战考研失利。 回过头来看&#xff0c;也很庆幸这次考研失利&#xff0c;因为这个时候对社会一…