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: stringbar?: number
}>()
</script>

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

<script setup lang="ts">
interface Props {foo: stringbar?: 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?: stringlabels?: string[]
}const props = withDefaults(defineProps<Props>(), {msg: 'hello',labels: () => ['one', 'two']
})

复杂类型的props

<script setup lang="ts">
interface Book {title: stringauthor: stringyear: 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: stringyear?: 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.mzph.cn/news/588393.shtml

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

相关文章

Download Monitor Email Lock下载监控器邮件锁插件

打开Download Monitor Email Lock下载监控器邮件锁插件 Download Monitor Email Lock下载监控器邮件锁插件下载监视器的电子邮件锁定扩展允许您要求用户在获得下载访问权限之前填写他们的电子邮件地址。 Download Monitor Email Lock下载监控器邮件锁插件用法 安装扩展程序后…

段永平浙江大学捐赠;合计超10亿元;OpenAI 年收超16亿美元;邻汇吧5000万元C+轮融资

投融资 • 「邻汇吧」完成5000万元C轮融资&#xff0c;安吉政府产业基金投资• 投资者预计明年黄金价格或将创新高• 至臻云完成 A 轮数千万元融资 大模型 • ChatGPT 产品增长强劲 OpenAI 年化收入超 16 亿美元• 周鸿祎&#xff1a;明年大模型一方面追求“大”&#xff0c…

【JavaScript】浮点数精度问题

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

1.PHP简单入门

1.PHP代码执行方式 PHP是在服务器端执行&#xff0c;然后返回给用户结果。 如果直接使用浏览器打开&#xff0c;就会解析为文本。 意思是说&#xff0c;浏览器通过 http请求&#xff0c;才能够执行php页面。 2.PHP代码框架 开启本机服务器&#xff08;下载软件略&#xff09…

.NET进阶篇06-async异步、thread多线程2

知识须要不断积累、总结和沉淀&#xff0c;思考和写做是成长的催化剂web 内容目录 1、线程Thread 一、生命周期 二、后台线程 三、静态方法 1.线程本地存储 2.内存栅栏 四、返回值 2、线程池ThreadPool 一、工做队列 二、工做线程和IO线程 三、和Thread区别 四、定时器 1、线…

二叉树的后序遍历,力扣

目录 建议先刷一下中序遍历 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 注&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 代码实现&#xff08;递归&#xff09;&#xff1a; 代码实现&#x…

LINUX 抓包工具Tcpdump离线安装教程

本次教程基于内网环境无法访问网络使用安装包进行安装抓包工具 1、首先给大家看下一共有6个安装包&#xff0c;依次进行解压&#xff0c;包我就放到csdn上了&#xff0c;需要的可以联系我进行下载 2打包然后传到服务器任意一个目录下&#xff0c;进入到当前目录&#xff0c;然后…

React快速入门之交互性

响应事件 创建事件处理函数 处理函数名常以handle事件名命名 function handlePlayClick() {alert(Playing);}传递事件处理函数 函数名、匿名两种方式&#xff01; function PlayButton() {function handlePlayClick() {alert(Playing);}return (<Button handleClick{handl…

VS2019+OpenCV4.7.0+OpenCV_contrib4.7.0+CUDA安装+配置视频硬解码保姆级别教程

在算法开发过程中&#xff0c;涉及基于opencv的rtsp流硬解码&#xff0c;这里设计结合当前所有的资料&#xff0c;实现了现有opengl相关的所有跟视频硬解码相关的功能&#xff0c;下面对opencv4.7.0的编译流程进行说明&#xff1a; 一、准备工作 下载opencv &#xff1a;open…

matplotlib绘制柱状图

代码 import matplotlib.pyplot as plt import numpy as np# 数据 categories [denoise, double-digit, 100% 5R] existence [0.9778, 0.9768, 0.9767] non_existence [0.9772, 0.9767, 0.9778]# 设置每组柱状图的宽度 bar_width 0.25# 计算每组柱状图的位置 x np.arange…

最优轨迹生成(一)—— 微分平坦

本系列文章是学习深蓝学院-移动机器人运动规划课程第五章最优轨迹生成 过程中所记录的笔记&#xff0c;本系列文章共包含四篇文章&#xff0c;依次介绍了微分平坦特性、无约束BVP轨迹优化、无约束BIVP轨迹优、 带约束轨迹优化等内容 本系列文章链接如下&#xff1a; 最优轨迹生…

五、Spring AOP面向切面编程

本章概要 场景设定和问题复现解决技术代理模式面向切面编程思维&#xff08;AOP&#xff09;Spring AOP框架介绍和关系梳理 5.1 场景设定和问题复现 准备AOP项目 项目名&#xff1a;spring-aop-annotation pom.xml <dependencies><!--spring context依赖--><…

一文讲清数据资产化之确权和估值

《中共中央 国务院关于构建数据基础制度更好发挥数据要素作用的意见》已发布一年&#xff0c;数据资产化和入表已成为2023年的热门话题&#xff0c;随着2023年底国家数据局吹风《"数据要素x"三年行动计划&#xff08;2024-2026年&#xff09;》即将发布&#xff0c;这…

Android 13 - Media框架(27)- ACodec(五)

前面几节我们了解了OMXNodeInstance是如何处理setPortMode、allocateBuffer、useBuffer的&#xff0c;这一节我们再回到ACodec&#xff0c;来看看 ACodec start 的其他部分。 我们首先来回顾一下&#xff0c;ACodec start 的状态切换以及处理的事务&#xff0c;我们用一张不太准…

EOS运行启动 keosd 和 nodeos

EOS运行启动 keosd 和 nodeos 启动keosd:启动nodeos 安装完eosio后keosd和nodeos是一起带着的&#xff0c;我把EOS的运行keosd 和 nodeos官网教程放上来&#xff1a; https://developers.eos.io/welcome/latest/getting-started-guide/local-development-environment/start-nod…

软件工程期末复习习题

知识点总结 第一章&#xff1a;软件工程概述 1、软件的定义&#xff1a;在运行中能提供所希望的功能与性能的程序使程序能够正确运行的数据及其结构描述软件研制过程和方法所用的文档。 2、软件危机&#xff1a;软件开发的生产率远远不能满足客观需要。开发的软件产品往往不能…

【PowerMockito:编写单元测试过程中采用when打桩失效的问题】

问题描述 正如上图所示&#xff0c;采用when打桩了&#xff0c;但是&#xff0c;实际执行的时候还是返回null。 解决方案 打桩时直接用any() 但是这样可能出现一个mybatisplus的异常&#xff0c;所以在测试类中需要加入以下代码片段&#xff1a; Beforepublic void setUp() …

你真的懂Hello World!吗?(编译与链接,静态链接与动态链接)

&#x1f4ab;Hello World! 对于大家来说Hello World!应该是最熟悉不过的一句话&#xff0c;我们从Hello World!走进了计算机的世界&#xff0c;但是你真的了解Hello World!吗&#xff1f;你又思考过它背后蕴含的机理吗&#xff1f;他是怎么从代码变成程序的你真的思考过吗&…

Spring Boot程序输出远程访问IP

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《SpringBoot开发》。&#x1f3af;&#x1f3af;…

03 HAL库下UART的使用

引言&#xff1a; 需要使用到的uart调试工具在文章最后的资料里面 题外话&#xff1a;uart和usart的区别 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;和USART&#xff08;Universal Synchronous/Asynchronous Receiver/Transmitter&#xff09;…