最詳細(xì)的 Vue3 + TypeScript 使用教程【收藏】
哈嘍,大家好 我是xy???????。最近技術(shù)棧剛剛由之前的 React 轉(zhuǎn)向 Vue3了,由于之前多多少少也接觸過 Vue,所以這次只用了一個上午的時間就上手 Vue3 了,如果大家有關(guān)于vue或者react上的問題,歡迎來滴滴我,一起交流學(xué)習(xí) ??
至于技術(shù)棧為什么由 react 轉(zhuǎn)向 vue3,也是因為今年換了新工作,公司技術(shù)棧以vue3為主,剛好前段時間又接手了一個項目,就直接采用 Vue3 script setup typescript 開發(fā),于是今天就想給大家分享下 script setup 結(jié)合 typescript 使用的一些技巧,如果這些技巧能夠幫助到你,記得給我點個贊呦 ??
環(huán)境搭建
環(huán)境搭建這里就不詳細(xì)介紹了,可以直接使用官方的方式創(chuàng)建
npm init vue@latest
這一指令將會安裝并執(zhí)行 create-vue,它是 Vue 官方的項目腳手架工具。你將會看到一些諸如 TypeScript 和測試支持之類的可選功能提示:
? Project name: … <your-project-name>
? Add TypeScript? … No / Yes
? Add JSX Support? … No / Yes
? Add Vue Router for Single Page Application development? … No / Yes
? Add Pinia for state management? … No / Yes
? Add Vitest for Unit testing? … No / Yes
? Add Cypress for both Unit and End-to-End testing? … No / Yes
? Add ESLint for code quality? … No / Yes
? Add Prettier for code formatting? … No / Yes
Scaffolding project in ./<your-project-name>...
Done.
如果不確定是否要開啟某個功能,你可以直接按下回車鍵選擇 No。在項目被創(chuàng)建后,通過以下步驟安裝依賴并啟動開發(fā)服務(wù)器:
cd <your-project-name>
npm install
npm run dev
這里就不做過多的講解了,這篇文章的重點還是在 script setup typescript 結(jié)合使用上
ref()
ref()接受一個內(nèi)部值,返回一個響應(yīng)式的、可更改的 ref 對象,此對象只有一個指向其內(nèi)部值的 property .value。
類型定義
function ref<T>(value: T): Ref<UnwrapRef<T>>
interface Ref<T> {
value: T
}
為 ref() 標(biāo)注類型
ref() 標(biāo)注類型有三種方式:
通過泛型參數(shù)的形式來給 ref()增加類型
import { ref } from 'vue'
const initCode = ref<string | number>('200')
如果是遇到復(fù)雜點的類型,可以自定義 interface 然后泛型參數(shù)的形式傳入
import { ref } from 'vue'
interface User {
name: string
age: string | number
}
const user = ref<User>({
name:'前端開發(fā)愛好者',
age: 20
})
通過使用 Ref 這個類型為 ref 內(nèi)的值指定一個更復(fù)雜的類型
import { ref } from 'vue'
import type { Ref } from 'vue'
const initCode: Ref<string | number> = ref('200')
三種方式推薦
比較推薦使用前兩種方式,前兩種方式其實都是以泛型的形式來標(biāo)注類型的
第三種方式需要額外的引入:
import type { Ref } from 'vue'
所以不是很推薦(本著能少寫一行是一行原則)
reactive()
reactive() 返回一個對象的響應(yīng)式代理。
類型定義
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
為 reactive() 標(biāo)注類型
reactive()標(biāo)注類型有兩種方式:
直接給聲明的變量添加類型
import { reactive } from 'vue'
interface User {
name: string
age: string | number
}
const user:User = reactive({
name:"前端開發(fā)愛好者",
age:'20'
})
通過泛型參數(shù)的形式來給 reactive()增加類型
import { reactive } from 'vue'
interface User {
name: string
age: string | number
}
const user = reactive<User>({
name:"前端開發(fā)愛好者",
age:'20'
})
兩種方式推薦
不推薦使用 reactive() 的泛型參數(shù),因為處理了深層次 ref 解包的返回值與泛型參數(shù)的類型不同。推薦直接給聲明的變量添加類型。
computed ()
接受一個 getter 函數(shù),返回一個只讀的響應(yīng)式 ref 對象,即 getter 函數(shù)的返回值。它也可以接受一個帶有 get 和 set 函數(shù)的對象來創(chuàng)建一個可寫的 ref 對象。
類型定義
// 只讀
function computed<T>(
getter: () => T,
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
// 可寫的
function computed<T>(
options: {
get: () => T
set: (value: T) => void
},
debuggerOptions?: DebuggerOptions
): Ref<T>
為 computed() 標(biāo)注類型
computed()標(biāo)注類型有兩種方式:
從其計算函數(shù)的返回值上推導(dǎo)出類型
import { ref, computed } from 'vue'
const count = ref<number>(0)
// 推導(dǎo)得到的類型:ComputedRef<string>
const user = computed(() => count.value + '前端開發(fā)愛好者')
通過泛型參數(shù)顯式指定 computed() 類型
const user = computed<string>(() => {
// 若返回值不是 string 類型則會報錯
return '前端開發(fā)愛好者'
})
兩種方式推薦
自動推導(dǎo)類型雖然簡單快捷,但是還是希望手動的去指定其類型,這樣更加利于代碼的可維護性,所以這里推薦大家使用通過泛型參數(shù)顯式指定 computed() 類型
defineProps()
為了在聲明 props 選項時獲得完整的類型推斷支持,我們可以使用 defineProps API,它將自動地在 script setup 中使用
為 defineProps() 標(biāo)注類型
從它的參數(shù)中推導(dǎo)類型:
const props = defineProps({
name: { type: String, required: true },
age: Number
})
通過泛型參數(shù)來定義 props 的類型
const props = defineProps<{
name: string
age?: number
}>()
當(dāng)然了,我們也可以把以上的泛型參數(shù)定義成一個單獨的 interface
interface Props {
name: string
age?: number
}
const props = defineProps<Props>()
以上的兩種方式雖然都可以很方便的標(biāo)注類型, 但是失去了對 props 定義默認(rèn)值的能力
目前官方也給出了解決方案,但是目前這個方案還處于實驗性,并且需要顯式地選擇開啟。
// vite.config.js
export default {
plugins: [
vue({
reactivityTransform: true
})
]
}
通過對 defineProps() 的響應(yīng)性解構(gòu)來添加默認(rèn)值:
<script setup lang="ts">
interface Props {
name: string
age?: number
}
const { name = '前端開發(fā)愛好者', age = 100 } = defineProps<Props>()
</script>
defineEmits()
為了在聲明 emits 選項時獲得完整的類型推斷支持,我們可以使用 defineEmits API,它將自動地在 script setup 中使用
為 defineEmits() 標(biāo)注類型
defineEmits() 標(biāo)注類型直接推薦泛型形式
import type { GlobalTheme } from 'naive-ui'
const emit = defineEmits<{
(e: 'setThemeColor', val: GlobalTheme): void
}>()
雖然官方還推薦了運行時自動推導(dǎo)的一種形式,但是本人不是很推薦
defineExpose()
defineExpose() 編譯器宏來顯式指定在 script setup 組件中要暴露出去的 property,使得父組件通過模板ref的方式獲取到當(dāng)前組件的實例
為 defineExpose() 標(biāo)注類型
defineExpose() 類型推導(dǎo)直接使用參數(shù)類型自動推導(dǎo)即可
<script setup>
import { ref } from 'vue'
const name = ref<string>('前端開發(fā)愛好者')
defineExpose({
name
})
provide()
provide()供給一個值,可以被后代組件注入
類型定義
function provide<T>(key: InjectionKey<T> | string, value: T): void
為 provide() 標(biāo)注類型
為 provide() 標(biāo)注類型, Vue 提供了一個 InjectionKey 接口,它是一個繼承自 Symbol 的泛型類型,可以用來在提供者和消費者之間同步注入值的類型
import type { InjectionKey } from 'vue'
// 建議聲明 key (name) 放到公共的文件中
// 這樣就可以在 inject 的時候直接導(dǎo)入使用
const name = Symbol() as InjectionKey<string>
provide(name, '前端開發(fā)愛好者') // 若提供的是非字符串值會導(dǎo)致錯誤
以上方式是通過定義 key 的類型來標(biāo)注類型的,還有一種方式直接 key 采用字符串的形式添加
provide('name', '前端開發(fā)愛好者')
inject()
inject()注入一個由祖先組件或整個應(yīng)用供給的值
類型定義
// 沒有默認(rèn)值
function inject<T>(key: InjectionKey<T> | string): T | undefined
// 帶有默認(rèn)值
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
// 使用工廠函數(shù)
function inject<T>(
key: InjectionKey<T> | string,
defaultValue: () => T,
treatDefaultAsFactory: true
): T
為 inject() 標(biāo)注類型
provide() 的 key 的類型是聲明式提供的話(provide()類型標(biāo)注的第一種形式)
inject() 可以直接導(dǎo)入聲明的 key 來獲取父級組件提供的值
// 由外部導(dǎo)入
const name = Symbol() as InjectionKey<string>
const injectName = inject(name)
如果 provide() 的 key 直接使用的字符串形式添加的, 需要通過泛型參數(shù)聲明
const injectName = inject<string>('name')
模板 ref
模板 ref 需要通過一個顯式指定的泛型參數(shù)和一個初始值 null 來創(chuàng)建:
<img ref="el" class="logo" :src="Logo" alt="" />
const el = ref<HTMLImageElement | null>(null)
組件 ref
有時,你可能需要為一個子組件添加一個模板 ref,以便調(diào)用它公開的方法
<!-- Child.vue -->
<script setup lang="ts">
const handleLog = () => console.log('前端開發(fā)愛好者')
defineExpose({
open
})
</script>
為了獲取 MyModal 的類型,我們首先需要通過 typeof 得到其類型,再使用 TypeScript 內(nèi)置的 InstanceType 工具類型來獲取其實例類型:
<!-- parent.vue -->
<script setup lang="ts">
import Child from './Child.vue'
// 為子組件 ref 聲明類型
const child = ref<InstanceType<typeof Child> | null>(null)
// 調(diào)用子組件中的方法
const getChildHandleLog = () => {
child.value?.handleLog()
}
</script>
事件處理器
原生的 DOM 事件標(biāo)注類型
<template>
<input type="text" @change="handleChange" />
</template>
<script setup lang="ts">
function handleChange(event: Event) {
console.log((event.target as HTMLInputElement).value)
}
</script>
作者:前端小菜雞之菜雞互啄
歡迎關(guān)注微信公眾號 :前端開發(fā)愛好者
添加好友備注【進階學(xué)習(xí)】拉你進技術(shù)交流群