vue3+ts开发干货笔记

总结一下在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 就可以得到类型提示啦,如下图
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法】数论---约数

约数里面的一个重要性质&#xff1a;一个数的约数都是成对存在的(以sqrt(x)为分界线) 一、求一个数的所有约数---试除法 int x; cin>>x; int yue[10000]{0},idx0; for(int i1;i<x/i;i) {if(x%i0){yue[idx]i;cout<<i<<" ";} }for(int iidx-1;i&…

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

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

现实世界中的人工智能:工业制造的 4 个成功案例研究

现实世界中的人工智能&#xff1a;工业制造的 4 个成功案例研究 从抓鸡翅到建立整个虚拟工厂&#xff0c;各种规模的制造商都利用人工智能以更快的速度、更低的成本和更低的风险生产更多的产品。 我们能否让工厂变得足够聪明&#xff0c;在发生故障之前告诉我们&#xff1f;我…

C语言之指针和函数

目录 作为函数参数的指针 二值互换 scanf函数和指针 指针的类型 空指针 标量型 在C语言程序中&#xff0c;指针的一个重要作用就是作为函数参数使用&#xff0c;下面我们就来学习作为函数参数的指针的相关内容。 作为函数参数的指针 假如我有一个神奇的能力&#xff0c;能…

HTML5和JS实现新年礼花效果

HTML5和JS实现新年礼花效果 2023兔年再见&#xff0c;2024龙年来临了&#xff01; 祝愿读者朋友们在2024年里&#xff0c;身体健康&#xff0c;心灵愉悦&#xff0c;梦想成真。 下面是用HTML5和JS实现新年礼花效果&#xff1a; 源码如下&#xff1a; <!DOCTYPE html>…

Apollo自动驾驶:从概念到现实的里程碑

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 ChatGPT体验地址 文章目录 前言1. Apollo Client2. Apollo Server3. Apollo Federation4. Apollo Tracing5. Apollo Codegen6. Apollo Link7. 其他工具和框架结论 &#x1f680;&#x1f…

插入排序 InsertionSort

插入排序的原理是默认前面的元素都是已经排序好的&#xff0c;然后从后面逐个读取插入到前面排序好的合适的位置&#xff0c;就相当于打扑克的时候每获取一张牌的时候就插入到合适的位置一样。 以{26,53,48,11,13,48,32,15)为例&#xff0c;插入排序的过程如下所示: 动态演示 …

如何实现WinApp的UI自动化测试?

WinApp&#xff08;WindowsAPP&#xff09;是运行在Windows操作系统上的应用程序&#xff0c;通常会提供一个可视的界面&#xff0c;用于和用户交互。例如运行在Windows系统上的Microsoft Office、PyCharm、Visual Studio Code、Chrome&#xff0c;都属于WinApp。常见的WinApp&…

gitee(码云)仓库内容更新,使用TortoiseGit同步本地仓库和远程仓库

前言&#xff1a; 网上有很多同步仓库教程&#xff0c;但都是git命令行操作。这篇使用TortoiseGit可视化操作同步本地仓库和远程仓库 克隆本地仓库&#xff0c;上传远程仓库&#xff0c;下载TortoiseGit可以看这篇使用gitee&#xff08;码云&#xff09;上传自己的代码&#xf…

DFS

目录 DFS 实现数字全排列 N 皇后问题 DFS 算法的理解 优先考虑深度&#xff0c;换句话说就是一条路走到黑&#xff0c;直到无路可走的情况下&#xff0c;才会选择回头&#xff0c;然后重新选择一条路。空间复杂度&#xff1a;O&#xff08;h&#xff09;和高度成正比 不具…

F12开发者工具如何找到对应接口

Web问题定位 1、进入 NetWork页面2、点击Fetch/XHR&#xff0c;这里可以看到页面发起的接口3、找到出问题的接口4、NetWork页面怎么看接口详情5、问题定位 最常用的定位前后端问题的方法。即&#xff1a;一般用来查看是后端返回给前端的数据有误&#xff0c;还是前端显示有误。…

详解Vue3中的鼠标事件click和dblclick

本文主要介绍Vue3中的常见鼠标事件。 目录 一、click——单击事件二、dblclick——双击事件三、在使用click和dbclick需要注意的地方 下面是Vue 3中常用的鼠标事件&#xff1a; 一、click——单击事件 click事件是一种常见的事件类型&#xff0c;用于在用户点击某个元素时触发…

网络运行状况监控工具

网络运行状况是网络在其操作和环境约束范围内按预期运行的能力&#xff0c;但是&#xff0c;随着云和人工智能等技术的出现&#xff0c;网络变得越来越复杂&#xff0c;维护其 IT 基础设施是一项越来越繁琐的任务。为了确保网络可靠性&#xff0c;组织需要了解每个端点的运行状…

高并发处理专题研究 - epoll并发编程[更新中]

文章目录 1 前置知识1.1 Socket编程基础Socket概述Socket通信模型Socket API一个简单的Socket编程实例 1.2 IO多路复用1.3 阻塞原理 2 epoll原理2.1 epoll概述2.2 epoll系统调用epoll_create()epoll_ctl()epoll_wait() 2.3 epoll工作原理 3 示例代码及演示 1 前置知识 1.1 Soc…

了解 NSA 关于管理 OSS 和 SBOM 的最新指南

开源软件很容易受到恶意行为者的攻击&#xff0c;但软件材料清单可以帮助减轻威胁。美国国家安全局的指导为管理生态系统奠定了坚实的基础。 软件供应链安全仍然是网络安全和软件行业的一个关键话题&#xff0c;并且有充分的理由&#xff0c;从针对大型软件供应商的持续攻击到…

vue3使用vuex

vuex: 状态管理工具 使用场景&#xff1a;用户登录状态 购物车 地理位置 等 数据位置&#xff1a;内存 安装 项目根目录 yarn add vuex 在src目录下新建store文件夹 下面新建index.js src/store/index.js 在main.js中引入并使用 // 导入状态管理工具vuex import store…

基于价值认同的需求侧电能共享分布式交易策略(matlab完全复现)

目录 1 主要内容 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序完全复现《基于价值认同的需求侧电能共享分布式交易策略》&#xff0c;针对电能共享市场的交易机制进行研究&#xff0c;提出了基于价值认同的需求侧电能共享分布式交易策略&#xff0c;旨在降低电力市…

23 UVM Event

even机制提供进程之间的同步。与System Verilo event相比&#xff0c;UVM event提供了额外的灵活性&#xff0c;如保持事件等待器/event waiters的数量和设置回调。 uvm_event类声明&#xff1a; virtual class uvm_event_base extends uvm_object class uvm_event#(type Tuv…

语义分割的应用及发展

语义分割(Semantic Segmentation)是一种计算机视觉领域的任务&#xff0c;旨在将一张图像中的每一个像素都分配一个语义标签&#xff0c;即将图像中的每个物体区域进行精确的分类划分。例如&#xff0c;在一张街景图中&#xff0c;语义分割可以将人、车、路、天空等每个像素分别…

Android14新特性 开启前台service服务

1. Android14新特性 1.1. 场景 在Android14&#xff08;targetSDK34&#xff09;系统手机开启前台service服务崩溃 ATAL EXCEPTION: mainProcess: com.inspur.lbrd, PID: 15634java.lang.RuntimeException: Unable to create service com.inspur.lbrd.service.KeepAliveServi…