最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

歷史性的時(shí)刻!OpenTiny 跨端、跨框架組件庫(kù)正式升級(jí) TypeScript,10 萬(wàn)行代碼重獲新

2023-04-03 23:48 作者:AnthonyKagol  | 我要投稿

大家好,我是 Kagol,OpenTiny?開(kāi)源社區(qū)運(yùn)營(yíng),TinyVue?跨端、跨框架組件庫(kù)核心貢獻(xiàn)者,專注于前端組件庫(kù)建設(shè)和開(kāi)源社區(qū)運(yùn)營(yíng)。

微軟于3月16日發(fā)布了 TypeScript 5.0?版本。微軟表示新版本體積更小、開(kāi)發(fā)者更容易上手且運(yùn)行速度更快。

根據(jù) The Software House 發(fā)布的《2022 前端開(kāi)發(fā)市場(chǎng)狀態(tài)調(diào)查報(bào)告》數(shù)據(jù)顯示,使用 TypeScript 的人數(shù)已經(jīng)達(dá)到 84%,和 2021 年相比增加了 7 個(gè)百分點(diǎn)。

TypeScript 可謂逐年火熱,使用者呈現(xiàn)逐年上升的趨勢(shì),再不學(xué)起來(lái)就說(shuō)不過(guò)去。

我們?OpenTiny?近期做了一次大的升級(jí),將原來(lái)運(yùn)行了?9年?的?JavaScript?代碼升級(jí)到了?TypeScript,并通過(guò) Monorepo 進(jìn)行子包的管理,還在用 JavaScript 的朋友抓緊升級(jí)哦,我特意準(zhǔn)備了一份《JS項(xiàng)目改造TS指南》文檔供大家參考,順便介紹了一些 TS 基礎(chǔ)知識(shí)和 TS 在 Vue 中的一些實(shí)踐。

通過(guò)本文你將收獲:

  • 通過(guò)了解 TS 的四大好處,說(shuō)服自己下定決心學(xué)習(xí) TS

  • 5 分鐘學(xué)習(xí) TS 最基礎(chǔ)和常用的知識(shí)點(diǎn),快速入門(mén),包教包會(huì)

  • 了解如何在 Vue 中使用 TypeScript,給 Vue2 開(kāi)發(fā)者切換到 Vue3 + TypeScript 提供最基本的參考

  • 如何將現(xiàn)有的 JS 項(xiàng)目改造成 TS 項(xiàng)目

1 學(xué)習(xí) TS 的好處

1.1 好處一:緊跟潮流:讓自己看起來(lái)很酷

如果你沒(méi)學(xué)過(guò) TS
你的前端朋友:都 2023 年了,你還不會(huì) TS?給你一個(gè)眼色你自己感悟吧

如果你學(xué)過(guò) TS
你的前端朋友:哇,你們的項(xiàng)目已經(jīng)用上 Vue3 + TS 啦,看起來(lái)真棒!教教我吧

如果說(shuō)上面那個(gè)好處太虛了,那下面的3條好處可都是實(shí)實(shí)在在能讓自己受益的。

1.2 好處二:智能提示:提升開(kāi)發(fā)者體驗(yàn)和效率

當(dāng)循環(huán)一個(gè)對(duì)象數(shù)組時(shí),對(duì)象的屬性列表可以直接顯示出來(lái),不用到對(duì)象的定義中去查詢?cè)搶?duì)象有哪些屬性。

通過(guò)調(diào)用后臺(tái)接口獲取的異步數(shù)據(jù)也可以通過(guò)TS類型進(jìn)行智能提示,這樣相當(dāng)于集成了接口文檔,后續(xù)后臺(tái)修改字段,我們很容易就能發(fā)現(xiàn)。

Vue 組件的屬性和事件都可以智能提示。

下圖是我們?OpenTiny?跨端跨框架前端組件庫(kù)中的 Alert 組件,當(dāng)在組件標(biāo)簽中輸入?des?時(shí),會(huì)自動(dòng)提示?description?屬性;當(dāng)輸入?@c?時(shí),會(huì)自動(dòng)提示?@close?事件。

1.3 好處三:錯(cuò)誤標(biāo)記:代碼哪里有問(wèn)題一眼就知道

在 JS 項(xiàng)目使用不存在的對(duì)象屬性,在編碼階段不容易看出來(lái),到運(yùn)行時(shí)才會(huì)報(bào)錯(cuò)。

在 TS 項(xiàng)目使用不存在的對(duì)象屬性,在IDE中會(huì)有紅色波浪線標(biāo)記,鼠標(biāo)移上去能看到具體的錯(cuò)誤信息。

在 JS 項(xiàng)目,調(diào)用方法時(shí)拼錯(cuò)單詞不容易被發(fā)現(xiàn),要在運(yùn)行時(shí)才會(huì)將錯(cuò)誤暴露出來(lái)。

在 TS 項(xiàng)目會(huì)有紅色波浪線提示,一眼就看出拼錯(cuò)單詞。

1.4 好處四:類型約束:用我的代碼就得聽(tīng)我的

你寫(xiě)了一個(gè)工具函數(shù) getType 給別人用,限定參數(shù)只能是指定的字符串,這時(shí)如果使用這個(gè)函數(shù)的人傳入其他字符串,就會(huì)有紅色波浪線提示。

Vue 組件也是一樣的,可以限定組件 props 的類型,組件的使用者如果傳入不正確的類型,將會(huì)有錯(cuò)誤提示,比如:我們?OpenTiny?的 Alert 組件,closable 只能傳入 Boolean 值,如果傳入一個(gè)字符串就會(huì)有錯(cuò)誤提示。

2 極簡(jiǎn) TS 基礎(chǔ),5分鐘學(xué)會(huì)

以下內(nèi)容雖然不多,但包含了實(shí)際項(xiàng)目開(kāi)發(fā)中最實(shí)用的部分,對(duì)于 TS 入門(mén)者來(lái)說(shuō)也是能很快學(xué)會(huì)的,學(xué)不會(huì)的找我,手把手教,包教包會(huì),有手就會(huì)寫(xiě)。

2.1 基本類型

用得較多的類型就下面5個(gè),更多類型請(qǐng)參考:TS官網(wǎng)文檔

  • 布爾 boolean

  • 數(shù)值 number

  • 字符串 string

  • 空值 void:表示沒(méi)有任何返回值的函數(shù)

  • 任意 any:表示不被類型檢查

用法也很簡(jiǎn)單:

let isDone: boolean = false; let myFavoriteNumber: number = 6; let myName: string = 'Kagol'; function alertName(name: string): void { ? ? console.log(`My name is ${name}`); ? }

默認(rèn)情況下,name 會(huì)自動(dòng)類型推導(dǎo)成 string 類型,此時(shí)如果給它賦值為一個(gè) number 類型的值,會(huì)出現(xiàn)錯(cuò)誤提示。

let name = 'Kagol' ? name = 6

如果給 name 設(shè)置 any 類型,表示不做類型檢查,這時(shí)錯(cuò)誤提示消失。

let name: any = 'Kagol' ? name = 6

2.2 函數(shù)

主要定義函數(shù)參數(shù)和返回值類型。

看一下例子:

const sum = (x: number, y: number): number => { ? ? return x + y ? }

以上代碼包含以下 TS 校驗(yàn)規(guī)則:

  • 調(diào)用 sum 函數(shù)時(shí),必須傳入兩個(gè)參數(shù),多一個(gè)或者少一個(gè)都不行

  • 并且這兩個(gè)參數(shù)的類型要為 number 類型

  • 且函數(shù)的返回值為 number 類型

少參數(shù):

多參數(shù):

參數(shù)類型錯(cuò)誤:

返回值:

用問(wèn)號(hào)???可以表示該參數(shù)是可選的。

const sum = (x: number, y?: number): number => { ? ? return x + (y || 0); ? } ? sum(1) ?

如果將 y 定義為可選參數(shù),則調(diào)用 sum 函數(shù)時(shí)可以只傳入一個(gè)參數(shù)。

需要注意的是,可選參數(shù)必須接在必需參數(shù)后面。換句話說(shuō),可選參數(shù)后面不允許再出現(xiàn)必需參數(shù)了。

給 y 增加默認(rèn)值 0 之后,y 會(huì)自動(dòng)類型推導(dǎo)成 number 類型,不需要加 number 類型,并且由于有默認(rèn)值,也不需要加可選參數(shù)。

const sum = (x: number, y = 0): number => { ? ? return x + y ? } sum(1) ? sum(1, 2) ?

2.3 數(shù)組

數(shù)組類型有兩種表示方式:

  • 類型 + 方括號(hào)?表示法

  • 泛型?表示法

// `類型 + 方括號(hào)` 表示法 let fibonacci: number[] = [1, 1, 2, 3, 5] // 泛型表示法 let fibonacci: Array<number> = [1, 1, 2, 3, 5]

這兩種都可以表示數(shù)組類型,看自己喜好進(jìn)行選擇即可。

如果是類數(shù)組,則不可以用數(shù)組的方式定義類型,因?yàn)樗皇钦娴臄?shù)組,需要用 interface 進(jìn)行定義

interface IArguments { ? [index: number]: any; ? length: number; ? callee: Function; } function sum() { ? let args: IArguments = arguments }

IArguments?類型已在 TypeScript 中內(nèi)置,類似的還有很多:

let body: HTMLElement = document.body; let allDiv: NodeList = document.querySelectorAll('div'); document.addEventListener('click', function(e: MouseEvent) { ? // Do something });

如果數(shù)組里的元素類型并不都是相同的怎么辦呢?

這時(shí) any 類型就發(fā)揮作用啦啦

let list: any[] = ['OpenTiny', 112, { website: 'https://opentiny.design/' }];

2.4 接口

接口簡(jiǎn)單理解就是一個(gè)對(duì)象的“輪廓”

interface IResourceItem { ? name: string; ? value?: string | number; ? total?: number; ? checked?: boolean; }

接口是可以繼承接口的

interface IClosableResourceItem extends IResourceItem { ? closable?: boolean; }

這樣 IClosableResourceItem 就包含了 IResourceItem 屬性和自己的 closable 可選屬性。

接口也是可以被類實(shí)現(xiàn)的

interface Alarm { ? alert(): void; } class Door { } class SecurityDoor extends Door implements Alarm { ? alert() { ? ? console.log('SecurityDoor alert') ? } }

如果類實(shí)現(xiàn)了一個(gè)接口,卻不寫(xiě)具體的實(shí)現(xiàn)代碼,則會(huì)有錯(cuò)誤提示

2.5 聯(lián)合類型 & 類型別名

聯(lián)合類型是指取值可以為多種類型中的一種,而類型別名常用于聯(lián)合類型。

看以下例子:

// 聯(lián)合類型 let myFavoriteNumber: string | number myFavoriteNumber = 'six' myFavoriteNumber = 6 // 類型別名 type FavoriteNumber = string | number let myFavoriteNumber: FavoriteNumber

當(dāng) TypeScript 不確定一個(gè)聯(lián)合類型的變量到底是哪個(gè)類型的時(shí)候,我們只能訪問(wèn)此聯(lián)合類型的所有類型里共有的屬性或方法:

function getLength(something: string | number): number { ? return something.length } // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'. // ? Property 'length' does not exist on type 'number'.

上例中,length 不是 string 和 number 的共有屬性,所以會(huì)報(bào)錯(cuò)。

訪問(wèn) string 和 number 的共有屬性是沒(méi)問(wèn)題的:

function getString(something: string | number): string { ? return something.toString() }

2.6 類型斷言

類型斷言(Type Assertion)可以用來(lái)手動(dòng)指定一個(gè)值的類型。

語(yǔ)法:值 as 類型,比如:(animal as Fish).swim()

類型斷言主要有以下用途:

  • 將一個(gè)聯(lián)合類型斷言為其中一個(gè)類型

  • 將一個(gè)父類斷言為更加具體的子類

  • 將任何一個(gè)類型斷言為 any

  • 將 any 斷言為一個(gè)具體的類型

我們一個(gè)個(gè)來(lái)看。

用途1:將一個(gè)聯(lián)合類型斷言為其中一個(gè)類型

interface Cat { ? name: string; ? run(): void; } interface Fish { ? name: string; ? swim(): void; } const animal: Cat | Fish = new Animal() animal.swim()

animal 是一個(gè)聯(lián)合類型,可能是貓 Cat,也可能是魚(yú) Fish,如果直接調(diào)用 swim 方法是要出現(xiàn)錯(cuò)誤提示的,因?yàn)樨埐粫?huì)游泳。

這時(shí)類型斷言就派上用場(chǎng)啦啦,因?yàn)檎{(diào)用的是 swim 方法,那肯定是魚(yú),所以直接斷言為 Fish 就不會(huì)出現(xiàn)錯(cuò)誤提示。

const animal: Cat | Fish = new Animal() (animal as Fish).swim()

用途2:將一個(gè)父類斷言為更加具體的子類

class ApiError extends Error { ? code: number = 0; } class HttpError extends Error { ? statusCode: number = 200; } function isApiError(error: Error) { ? if (typeof (error as ApiError).code === 'number') { ? ? return true; ? } ? return false; }

ApiError 和 HttpError 都繼承自 Error 父類,error 變量的類型是 Error,去取 code 變量肯定是不行,因?yàn)槿〉氖?code 變量,我們可以直接斷言為 ApiError 類型。

用途3:將任何一個(gè)類型斷言為 any

這個(gè)非常有用,看一下例子:

function getCacheData(key: string): any { ? return (window as any).cache[key]; } interface Cat { ? name: string; ? run(): void; } const tom = getCacheData('tom') as Cat;

getCacheData 是一個(gè)歷史遺留函數(shù),不是你寫(xiě)的,由于他返回 any 類型,就等于放棄了 TS 的類型檢驗(yàn),假如 tom 是一只貓,里面有 name 屬性和?run()?方法,但由于返回 any 類型,tom.?是沒(méi)有任何提示的。

如果將其斷言為 Cat 類型,就可以?點(diǎn)?出 name 屬性和?run()?方法。

用途4:將 any 斷言為一個(gè)具體的類型

這個(gè)比較常見(jiàn)的場(chǎng)景是給 window 掛在一個(gè)自己的變量和方法。

window.foo = 1; // index.ts:1:8 - error TS2339: Property 'foo' does not exist on type 'Window & typeof globalThis'. (window as any).foo = 1;

由于 window 下沒(méi)有 foo 變量,直接賦值會(huì)有錯(cuò)誤提示,將 window 斷言為 any 就沒(méi)問(wèn)題啦啦。

2.7 元組

數(shù)組合并了相同類型的對(duì)象,而元組(Tuple)合并了不同類型的對(duì)象。

let tom: [string, number] = ['Tom', 25];

給元組類型賦值時(shí),數(shù)組每一項(xiàng)的類型需要和元組定義的類型對(duì)應(yīng)上。

當(dāng)賦值或訪問(wèn)一個(gè)已知索引的元素時(shí),會(huì)得到正確的類型:

let tom: [string, number]; tom[0] = 'Tom'; tom[1] = 25; tom[0].slice(1); tom[1].toFixed(2);

也可以只賦值其中一項(xiàng):

let tom: [string, number]; tom[0] = 'Tom';

但是當(dāng)直接對(duì)元組類型的變量進(jìn)行初始化或者賦值的時(shí)候,需要提供所有元組類型中指定的項(xiàng)。

let tom: [string, number]; tom = ['Tom']; // Property '1' is missing in type '[string]' but required in type '[string, number]'.

當(dāng)添加越界的元素時(shí),它的類型會(huì)被限制為元組中每個(gè)類型的聯(lián)合類型:

let tom: [string, number]; tom = ['Tom', 25]; tom.push('male'); tom.push(true); // Argument of type 'true' is not assignable to parameter of type 'string | number'.

push 字符串和數(shù)字都可以,布爾就不行。

2.8 枚舉

枚舉(Enum)類型用于取值被限定在一定范圍內(nèi)的場(chǎng)景,比如一周只能有七天,顏色限定為紅綠藍(lán)等。

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat}

枚舉成員會(huì)被賦值為從 0 開(kāi)始遞增的數(shù)字,同時(shí)也會(huì)對(duì)枚舉值到枚舉名進(jìn)行反向映射:

console.log(Days.Sun === 0) // true console.log(Days[0] === 'Sun') // true console.log('Days', Days)

手動(dòng)賦值:未手動(dòng)賦值的枚舉項(xiàng)會(huì)接著上一個(gè)枚舉項(xiàng)遞增。

enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat}

2.9 類

給類加上 TypeScript 的類型很簡(jiǎn)單,與接口類似:

class Animal { ? name: string ? constructor(name: string) { ? ? this.name = name ? } ? sayHi(welcome: string): string { ? ? return `${welcome} My name is ${this.name}` ? } }

類的語(yǔ)法涉及到較多概念,請(qǐng)參考:

  • https://es6.ruanyifeng.com/#docs/class

  • https://ts.xcatliu.com/advanced/class.html

2.10 泛型

泛型(Generics)是指在定義函數(shù)、接口或類的時(shí)候,不預(yù)先指定具體的類型,而在使用的時(shí)候再指定類型的一種特性。

可以簡(jiǎn)單理解為定義函數(shù)時(shí)的形參。

設(shè)想以下場(chǎng)景,我們有一個(gè) print 函數(shù),輸入什么,原樣打印,函數(shù)的入?yún)⒑头祷刂殿愋褪且恢碌摹?/p>

一開(kāi)始只需要打印字符串:

function print(arg: string): string { ? return arg }

后面需求變了,除了能打印字符串,還要能打印數(shù)字:

function print(arg: string | number): string | number { ? return arg }

假如需求又變了,要打印布爾值、對(duì)象、數(shù)組,甚至自定義的類型,怎么辦,寫(xiě)一串聯(lián)合類型?顯然是不可取的,用 any?那就失去了 TS 類型校驗(yàn)?zāi)芰?,淪為 JS。

function print(arg: any): any { ? return arg }

解決這個(gè)問(wèn)題的完美方法就是泛型!

print 后面加上一對(duì)尖括號(hào),里面寫(xiě)一個(gè) T,這個(gè) T 就類似是一個(gè)類型的形參。

這個(gè)類型形參可以在函數(shù)入?yún)⒗镉?,也可以在函?shù)返回值使用,甚至也可以在函數(shù)體里面的變量、函數(shù)里面用。

function print<T>(arg: T): T { ? return arg }

那么實(shí)參哪里來(lái)?用的時(shí)候傳進(jìn)來(lái)!

const res = print<number>(123)

我們還可以使用泛型來(lái)約束后端接口參數(shù)類型。

import axios from 'axios' interface API { ? '/book/detail': { ? ? ? id: number, ? }, ? '/book/comment': { ? ? ? id: number ? ? ? comment: string ? } ? ... } function request<T extends keyof API>(url: T, obj: API[T]) { ? return axios.post(url, obj) } request('/book/comment', { ? id: 1, ? comment: '非常棒!' })

以上代碼對(duì)接口進(jìn)行了約束:

  • url 只能是 API 中定義過(guò)的,其他 url 都會(huì)提示錯(cuò)誤

  • 接口參數(shù) obj 必須和 url 能對(duì)應(yīng)上,不能少屬性,屬性類型也不能錯(cuò)


而且調(diào)用 request 方法時(shí),也會(huì)提示 url 可以選擇哪些

如果后臺(tái)改了接口參數(shù)名,我們一眼就看出來(lái)了,都不用去找接口文檔,是不是很厲害!

泛型的例子參考了前端阿林的文章:

  • 輕松拿下 TS 泛型

3 TS 在 Vue 中的實(shí)踐

3.1 定義組件 props 的類型

不使用 setup 語(yǔ)法糖

export default defineComponent({ ? props: { ? ? items: { ? ? ? type: Object as PropType<IResourceItem[]>, ? ? ? default() { ? ? ? ? return [] ? ? ? } ? ? }, ? ? span: { ? ? ? type: Number, ? ? ? default: 4 ? ? }, ? ? gap: { ? ? ? type: [String, Number] as PropType<string | number>, ? ? ? default: '12px' ? ? }, ? ? block: { ? ? ? type: Object as PropType<Component>, ? ? ? default: TvpBlock ? ? }, ? ? beforeClose: Function as PropType<() => boolean> ? } })

使用 setup 語(yǔ)法糖 – runtime 聲明

import { PropType, Component } from 'vue' const props = defineProps({ ? items: { ? ? type: Object as PropType<IResourceItem[]>, ? ? default() { ? ? ? return [] ? ? } ? }, ? span: { ? ? type: Number, ? ? default: 4 ? }, ? gap: { ? ? type: [String, Number] as PropType<string | number>, ? ? default: '12px' ? }, ? block: { ? ? type: Object as PropType<Component>, ? ? default: TvpBlock ? }, ? beforeClose: Function as PropType<() => boolean> })

使用 setup 語(yǔ)法糖 – type-based 聲明

import { Component, withDefaults } from 'vue' interface Props { ? items: IResourceItem[] ? span: number ? gap: string | number ? block: Component ? beforeClose: () => void } const props = withDefaults(defineProps<Props>(), { ? items: () => [], ? span: 4, ? gap: '12px', ? block: TvpBlock })

IResourceItem:

interface IResourceItem { ? name: string; ? value?: string | number; ? total?: number; ? checked?: boolean; ? closable?: boolean; }

3.2 定義 emits 類型

不使用 setup 語(yǔ)法糖

export default defineComponent({ ? emits: ['change', 'update'], ? setup(props, { emit }) { ? ? emit('change') ? } })

使用 setup 語(yǔ)法糖

<script setup lang="ts"> // runtime const emit = defineEmits(['change', 'update']) // type-based const emit = defineEmits<{ ? (e: 'change', id: number): void ? (e: 'update', value: string): void }>() </script>

3.3 定義 ref 類型

默認(rèn)會(huì)自動(dòng)進(jìn)行類型推導(dǎo)

import { ref } from 'vue' // inferred type: Ref<number> const year = ref(2020) // => TS Error: Type 'string' is not assignable to type 'number'. year.value = '2020'

兩種聲明 ref 類型的方法

import { ref } from 'vue' import type { Ref } from 'vue' // 方式一 const year: Ref<string | number> = ref('2020') year.value = 2020 // ok! // 方式二 // resulting type: Ref<string | number> const year = ref<string | number>('2020') year.value = 2020 // ok!

3.4 定義 reactive 類型

默認(rèn)會(huì)自動(dòng)進(jìn)行類型推導(dǎo)

import { reactive } from 'vue' // inferred type: { title: string } const book = reactive({ title: 'Vue 3 Guide' })

使用接口定義明確的類型

import { reactive } from 'vue' interface Book { ? title: string ? year?: number } const book: Book = reactive({ title: 'Vue 3 Guide' })

3.5 定義 computed 類型

默認(rèn)會(huì)自動(dòng)進(jìn)行類型推導(dǎo)

import { ref, computed } from 'vue' const count = ref(0) // inferred type: ComputedRef<number> const double = computed(() => count.value * 2) // => TS Error: Property 'split' does not exist on type 'number' const result = double.value.split('')

兩種聲明 computed 類型的方法

import { ComputedRef, computed } from 'vue' const double: ComputedRef<number> = computed(() => { ? // type error if this doesn't return a number }) const double = computed<number>(() => { ? // type error if this doesn't return a number })

3.6 定義 provide/inject 類型

provide

import { provide, inject } from 'vue' import type { InjectionKey } from 'vue' // 聲明 provide 的值為 string 類型 const key = Symbol() as InjectionKey<string> provide(key, 'foo') // providing non-string value will result in error

inject

// 自動(dòng)推導(dǎo)為 string 類型 const foo = inject(key) // type of foo: string | undefined // 明確指定為 string 類型 const foo = inject<string>('foo') // type: string | undefined // 增加默認(rèn)值 const foo = inject<string>('foo', 'bar') // type: string // 類型斷言為 string const foo = inject('foo') as string

3.7 定義模板引用的類型

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

3.8 定義組件模板引用的類型

定義一個(gè) MyModal 組件

<!-- MyModal.vue --> <script setup lang="ts"> import { ref } from 'vue' const isContentShown = ref(false) const open = () => (isContentShown.value = true) defineExpose({ ? open }) </script>

在 App.vue 中引用 MyModal 組件

<!-- 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>

參考 Vue 官網(wǎng)文檔:

  • TypeScript with Composition API

4 JS 項(xiàng)目轉(zhuǎn) TS

還是使用 JS 的同學(xué)有福啦!為了讓大家快速用上 TS,享受 TS 的絲滑體驗(yàn),我整理了一份《JS 項(xiàng)目改造成 TS 項(xiàng)目指南》。有了這份步驟指南,JS 項(xiàng)目轉(zhuǎn) TS 不再是難事!

我們新開(kāi)源的?TinyVue?組件庫(kù),就使用這份《JS 項(xiàng)目改造成 TS 項(xiàng)目指南》,成功地由 JS 項(xiàng)目改造成了 TS 項(xiàng)目,悄悄地告訴大家:

  • TinyVue?是一套跨端、跨框架的企業(yè)級(jí) UI 組件庫(kù),支持 Vue 2 和 Vue 3,支持 PC 端和移動(dòng)端。

  • 在內(nèi)部經(jīng)過(guò)9年持續(xù)打磨,服務(wù)于華為內(nèi)外部上千個(gè)項(xiàng)目。

  • 目前代碼量超過(guò)10萬(wàn)行。

這么龐大的代碼量都能從 JS 轉(zhuǎn) TS,其他小規(guī)模的項(xiàng)目更是不在話下。

為了驗(yàn)證自己的猜想,我又在 GitHub 找到了一個(gè)6年前的 Vue2 + JS 項(xiàng)目,目前早已不再維護(hù),打算嘗試將其改造成 TS 項(xiàng)目,結(jié)果按照這份指南,1個(gè)小時(shí)不用就搞定啦啦

https://github.com/liangxiaojuan/vue-todos

這個(gè)項(xiàng)目的效果圖長(zhǎng)這樣:

我已經(jīng)提了 issue,看下作者是否同意改造成 TS,同意的話,我立馬就是一個(gè) PR 過(guò)去!

話不多說(shuō),大家有需要的,可直接拿走!

《JS 項(xiàng)目改造成 TS 項(xiàng)目指南》

JS 項(xiàng)目改造成 TS 步驟:

  1. 安裝 TS:npm i typescript ts-loader -D

  2. 增加 TS 配置文件:tsconfig.json

  3. 修改文件后綴名:x.js -> x.ts

  4. x.vue?文件增加 lang:<script>

  5. vite.config.js?配置后綴名

  6. 升級(jí)依賴,修改本地啟動(dòng)和構(gòu)建腳本

  7. 添加?loader?/?plugin?等

  8. 逐步補(bǔ)充類型聲明

tsconfig.ts

{ ? "compilerOptions": { ? ? "target": "ESNext", ? ? "useDefineForClassFields": true, ? ? "module": "ESNext", ? ? "moduleResolution": "Node", ? ? "strict": true, ? ? "jsx": "preserve", ? ? "sourceMap": true, ? ? "resolveJsonModule": true, ? ? "isolatedModules": true, ? ? "esModuleInterop": true, ? ? "lib": ["ESNext", "DOM"], ? ? "skipLibCheck": true ? }, ? "include": [ ? ? "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue" ? ] }

配置文件后綴名,增加?.ts?和?.tsx

extensions: ['.js', '.vue', '.json', '.ts', 'tsx'],

入口文件要由?main.js?改成?main.ts

entry: { ? app: './src/main.ts' },

需要配置下 loader

{ ? test: /\.tsx?$/, ? loader: 'ts-loader', ? exclude: /node_modules/, ? options: { ? ? appendTsSuffixTo: [/\.vue$/] ? }, ? include: [resolve('src')] }

以及 plugin

const { VueLoaderPlugin } = require('vue-loader') plugins: [ ? new VueLoaderPlugin() ],

完成之后,先測(cè)試下項(xiàng)目是否能正常啟動(dòng)和構(gòu)建:npm run dev?/?npm run build

都沒(méi)問(wèn)題之后,本次 TS 項(xiàng)目改造就完成大部分啦啦!

后續(xù)就是逐步補(bǔ)充代碼涉及到的變量和函數(shù)的類型聲明即可。

改造過(guò)程中遇到問(wèn)題歡迎留言討論,希望你也能盡快享受 TS 的絲滑開(kāi)發(fā)者體驗(yàn)!

TinyVue 招募貢獻(xiàn)者啦

如果你對(duì)我們的跨端跨框架組件庫(kù)?TinyVue?感興趣,歡迎參與到我們的開(kāi)源社區(qū)中來(lái),一起將它建設(shè)得更好!

參與 TinyVue 組件庫(kù)建設(shè),你將收獲:

直接的價(jià)值:

  • 通過(guò)打造一個(gè)跨端、跨框架的組件庫(kù)項(xiàng)目,學(xué)習(xí)最新的?Monorepo?+?Vite?+?Vue3?+?TypeScript?技術(shù)

  • 學(xué)習(xí)從 0 到 1 搭建一個(gè)自己的組件庫(kù)的整套流程和方法論,包括組件庫(kù)工程化、組件的設(shè)計(jì)和開(kāi)發(fā)等

  • 為自己的簡(jiǎn)歷和職業(yè)生涯添彩,參與過(guò)優(yōu)秀的開(kāi)源項(xiàng)目,這本身就是受面試官青睞的亮點(diǎn)

  • 結(jié)識(shí)一群優(yōu)秀的、熱愛(ài)學(xué)習(xí)、熱愛(ài)開(kāi)源的小伙伴,大家一起打造一個(gè)偉大的產(chǎn)品

長(zhǎng)遠(yuǎn)的價(jià)值:

  • 打造個(gè)人品牌,提升個(gè)人影響力

  • 培養(yǎng)良好的編碼習(xí)慣

  • 獲得華為云?OpenTiny?開(kāi)源社區(qū)的榮譽(yù)&認(rèn)可和定制小禮物

  • 成為 PMC & Committer 之后還能參與 OpenTiny 整個(gè)開(kāi)源生態(tài)的決策和長(zhǎng)遠(yuǎn)規(guī)劃,培養(yǎng)自己的管理和規(guī)劃能力 未來(lái)有更多機(jī)會(huì)和可能

往期活動(dòng)禮品及貢獻(xiàn)者的反饋:


聯(lián)系我們

如果你對(duì)我們?OpenTiny?的開(kāi)源項(xiàng)目感興趣,歡迎添加小助手微信:opentiny-official,拉你進(jìn)群,一起交流前端技術(shù),一起玩開(kāi)源。

OpenTiny 官網(wǎng):https://opentiny.design/

OpenTiny 倉(cāng)庫(kù):https://github.com/opentiny/

Vue 組件庫(kù):https://github.com/opentiny/tiny-vue(歡迎 Star ??)

Angular 組件庫(kù):https://github.com/opentiny/ng(歡迎 Star ??)


歷史性的時(shí)刻!OpenTiny 跨端、跨框架組件庫(kù)正式升級(jí) TypeScript,10 萬(wàn)行代碼重獲新的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
车险| 万源市| 丘北县| 呼和浩特市| 河南省| 安仁县| 长沙县| 施秉县| 冀州市| 江口县| 长泰县| 田阳县| 广西| 武鸣县| 六盘水市| 凤翔县| 天柱县| 苍山县| 汉阴县| 毕节市| 嘉祥县| 台湾省| 招远市| 鹤峰县| 榆树市| 云龙县| 睢宁县| 静乐县| 衡山县| 陇南市| 海口市| 西华县| 石家庄市| 商丘市| 墨竹工卡县| 宝坻区| 佛坪县| 衡南县| 南涧| 揭东县| 六安市|