TypeScript
快来分享你的内容吧~
- 2024-11-26·后端
- 2024-09-05·前端开发
学习笔记-TypeScript 函数
TypeScript 函数是在 JavaScript 函数基础上增加了强类型系统的增强实现,以下是核心特性和最佳实践: --- ### 一、基础类型标注 ```typescript // 1. 显式标注参数和返回值类型 function add(x: number, y: number): number { return x + y; } // 2. 箭头函数类型 const multiply = (a: number, b: number): number => a * b; // 3. 自动类型推断(返回值类型可省略) const greet = (name: string) => `Hello, ${name}`; // 推断返回string ``` --- ### 二、高级函数特性 #### 1. 可选参数与默认值 ```typescript // 可选参数必须位于必选参数之后 function buildName(first: string, last?: string, title: string = "Dr.") { return `${title} ${first} ${last || ""}`.trim(); } ``` #### 2. 剩余参数(Rest Parameters) ```typescript function sum(...numbers: number[]): number { return numbers.reduce((acc, cur) => acc + cur, 0); } ``` --- ### 三、函数重载(Function Overloads) ```typescript // 1. 声明重载签名 function parseInput(input: string): Date; function parseInput(input: number): string; // 2. 实现函数 function parseInput(input: unknown): Date | string { if (typeof input === "string") return new Date(input); return new Date(input).toISOString(); } // 调用时会根据输入类型获得智能提示 const dateResult = parseInput("2023-10-01"); // Date类型 const strResult = parseInput(Date.now()); // string类型 ``` --- ### 四、泛型函数(Generic Functions) ```typescript // 1. 基础泛型 function identity<T>(arg: T): T { return arg; } // 2. 约束泛型类型 function mergeObjects<T extends object, U extends object>(a: T, b: U): T & U { return { ...a, ...b }; } // 3. 泛型与箭头函数(需在TSX中加上逗号避免语法冲突) const genericArrow = <T,>(items: T[]): number => items.length; ``` --- ### 五、特殊类型工具 #### 1. 函数类型别名 ```typescript type StringHandler = (input: string) => void; const logger: StringHandler = (str) => console.log(str.toUpperCase()); ``` #### 2. 参数解构类型标注 ```typescript function draw({ x, y }: { x: number; y: number }): void { console.log(`Drawing at (${x}, ${y})`); } ``` --- ### 六、异步函数处理 ```typescript async function fetchData<T>(url: string): Promise<T> { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return response.json() as Promise<T>; } // 使用示例 interface UserData { id: number; name: string } fetchData<UserData>("https://api.example.com/users/1").then(console.log); ``` --- ### 最佳实践建议: 1. **严格空值检查**:启用`strictNullChecks`避免意外null/undefined 2. **优先使用箭头函数**:保持this上下文稳定性 3. **合理使用重载**:当处理多类型输入时提升类型安全性 4. **利用泛型约束**:通过`extends`限制类型范围保证类型安全
TypeScript 极速梳理
# 1.类型声明 ```typescript let a: string //变量a只能存储字符串 let b: number //变量a只能存储数值 let c: boolean //变量a只能存储布尔值 a = 'hello' a = 100 //警告:不能将类型"number"分配给类型"string" b = 666 b = '你好'//警告:不能将类型"string"分配给类型"number" c = true c = 666 //警告:不能将类型"number"分配给类型"boolean" // 参数x必须是数字,参数y也必须是数字,函数返回值也必须是数字 function demo(x:number,y:number):number{ return x + y } demo(100,200) demo(100,'200') //警告:类型"string"的参数不能赋给类型"number"的参数 demo(100,200,300) //警告:应有 2 个参数,但获得 3 个 demo(100) //警告:应有 2 个参数,但获得 1 个 ``` # 2.类型推断 ```typescript let d = -99 //TypeScript会推断出变量d的类型是数字 d = false //警告:不能将类型"boolean"分配给类型"number" ``` # 3.类型总览 `JavaScript` 中的数据类型: ```markdown string 、 number 、 boolean 、 null 、 undefined 、 bigint 、 symbol 、object ``` `TypeScript` 中的数据类型: ```markdown 1. 以上所有 2. 四个新类型: void 、 never 、 unknown 、 any 、 enum 、 tuple 3. ⾃定义类型: type 、 interface ``` 注意点: `JS` 中的这三个构造函数: `Number` 、 `String` 、 `Boolean` ,他们只⽤于包装对象,正 常开发时,很少去使⽤他们,在` TS` 中也是同理。 ```typescript let n = 56 n.toFixed(2) /* 当执⾏n.toFixed(2) ,底层做了这⼏件事: 1.let temp = new Number(42) 2.value = temp.toFixed(2) 3.删除value 4.返回value */ ``` 类型总览: | 类型 | 描述 | 举例 | | :-------: | :--------------------------------: | :-------------------------------: | | `number` | 任意数字 | `1` , `-33` , `2.5` | | `string` | 任意字符串 | `'hello'` , `'ok'` , `'你 好'` | | `boolean` | 布尔值 `true` 或`false` | `true` 、 `false` | | `字⾯量` | 值只能是字⾯量值 | 值本身 | | `any` | 任意类型 | `1` 、 `'hello'` 、 `true` ...... | | `unknown` | 类型安全的 `any ` | `1` 、 `'hello'` 、 `true` ...... | | `never` | 不能是任何值 | ⽆值 | | `void` | 空 或 `undefined` | 空 或 `undefined` | | `object` | 任意的 `JS` 对象 | `{name:'张三'}` | | `tuple` | 元素, `TS` 新增类型,固定⻓度数组 | `[4,5]` | | `enum` | 枚举, `TS` 中新增类型 | `enum{A, B}` | # 4.常⽤类型 ## 4.1. 字⾯量 ```typescript let a: '你好' //a的值只能为字符串"你好" let b: 100 //b的值只能为数字100 a = '欢迎'//警告:不能将类型""欢迎""分配给类型""你好"" b = 200 //警告:不能将类型"200"分配给类型"100" let gender: '男'|'⼥' //定义⼀个gender变量,值只能为字符串"男"或"⼥" gender = '男' gender = '未知' //不能将类型""未知""分配给类型""男" | "⼥"" ``` ## 4.2. any `any` 的含义是:任意类型,⼀旦将变量类型限制为 `any` ,那就意味着放弃了对该变量的类型检查。 ```typescript //明确的表示a的类型是any —— 显式的any let a: any //以下对a的赋值,均⽆警告 a = 100 a = '你好' a = false //没有明确的表示b的类型是any,但TS主动推断了出来 —— 隐式的any let b //以下对b的赋值,均⽆警告 b = 100 b = '你好' b = false ``` 注意点: `any` 类型的变量,可以赋值给任意类型的变量 ```typescript /* 注意点:any类型的变量,可以赋值给任意类型的变量 */ let a let x: string x = a // ⽆警告 ``` ## 4.3. unknown `unknown` 的含义是:未知类型。 > 备注1: `unknown` 可以理解为⼀个类型安全的 `any` > > 备注2:` unknown` 适⽤于:开始不知道数据的具体类型,后期才能确定数据的类型 ```typescript // 设置a的类型为unknown let a: unknown //以下对a的赋值,均正常 a = 100 a = false a = '你好' // 设置x的数据类型为string let x: string x = a //警告:不能将类型"unknown"分配给类型"string" ``` 若就是想把 `a` 赋值给 `x` ,可以⽤以下三种写法: ```typescript // 设置a的类型为unknown let a: unknown a = 'hello' //第⼀种⽅式:加类型判断 if(typeof a === 'string'){ x = a } //第⼆种⽅式:加断⾔ x = a as string //第三种⽅式:加断⾔ x = <string> ``` `any`后点任何的东⻄都不会报错,⽽ `unknown` 正好与之相反。 ```typescript let str1: string = 'hello' str1.toUpperCase() //⽆警告 let str2: any = 'hello' str2.toUpperCase() //⽆警告 let str3: unknown = 'hello'; str3.toUpperCase() //警告:"str3"的类型为"未知" // 使⽤断⾔强制指定str3的类型为string (str3 as string).toUpperCase() //⽆警告 ``` ## 4.4. never `never` 的含义是:任何值都不是,简⾔之就是不能有值, `undefined` 、 `null` 、` ''` 、` 0` 都不 ⾏! 1. ⼏乎不⽤ `never `去直接限制变量,因为没有意义,例如: ```typescript /* 指定a的类型为never,那就意味着a以后不能存任何的数据了 */ let a: never // 以下对a的所有赋值都会有警告 a = 1 a = true a = undefined a = null ``` 2. `never` ⼀般是 `TypeScript`主动推断出来的,例如: ```typescript // 指定a的类型为string let a: string // 给a设置⼀个值 a = 'hello' if(typeof a === 'string'){ a.toUpperCase() }else{ console.log(a) // TypeScript会推断出此处的a是never,因为没有任何⼀个值符合此处的 逻辑 } ``` 3. `never` 也可⽤于限制函数的返回值 ```typescript // 限制demo函数不需要有任何返回值,任何值都不⾏,像undeifned、null都不⾏ function demo():never{ throw new Error('程序异常退出') } ``` ## 4.5. void `void` 的含义是:` 空` 或` undefined`,严格模式下不能将 `null` 赋值给`void` 类型。 ```typescript let a:void = undefined //严格模式下,该⾏会有警告:不能将类型"null"分配给类型"void" let b:void = null ``` `void` 常⽤于限制函数返回值 ```typescript // ⽆警告 function demo1():void{ } // ⽆警告 function demo2():void{ return } // ⽆警告 function demo3():void{ return undefined } // 有警告:不能将类型"number"分配给类型"void" function demo4():void{ return 666 } ``` ## 4.6. object 关于 `Object` 与 `object` ,直接说结论:在类型限制时, `Object` ⼏乎不⽤,因为范围太⼤了,⽆意义。 1. `object` 的含义:任何【⾮原始值类型】,包括:对象、函数、数组等,限制的范围⽐较宽泛,⽤的少。 ```typescript let a:object //a的值可以是任何【⾮原始值类型】,包括:对象、函数、数组等 // 以下代码,是将【⾮原始类型】赋给a,所以均⽆警告 a = {} a = {name:'张三'} a = [1,3,5,7,9] a = function(){} // 以下代码,是将【原始类型】赋给a,有警告 a = null // 警告:不能将类型"null"分配给类型"object" a = undefined // 警告:不能将类型"undefined"分配给类型"object" a = 1 // 警告:不能将类型"number"分配给类型"object" a = true // 警告:不能将类型"boolean"分配给类型"object" a = '你好' // 警告:不能将类型"string"分配给类型"object" ``` 2. `Object` 的含义: `Object` 的实例对象,限制的范围太⼤了,⼏乎不⽤。 ```typescript let a:Object //a的值必须是Object的实例对象, // 以下代码,均⽆警告,因为给a赋的值,都是Object的实例对象 a = {} a = {name:'张三'} a = [1,3,5,7,9] a = function(){} a = 1 // 1不是Object的实例对象,但其包装对象是Object的实例 a = true // truue不是Object的实例对象,但其包装对象是Object的实例 a = '你好' // "你好"不是Object的实例对象,但其包装对象是Object的实例 // 以下代码均有警告 a = null // 警告:不能将类型"null"分配给类型"Object" a = undefined // 警告:不能将类型"undefined"分配给类型"Object" ``` 3. 实际开发中,限制⼀般对象,通常使⽤以下形式 ```typescript // 限制person对象的具体内容,使⽤【,】分隔,问号代表可选属性 let person: { name: string, age?: number} // 限制car对象的具体内容,使⽤【;】分隔,必须有price和color属性,其他属性不去限制,有 没有都⾏ let car: { price: number; color: string; [k:string]:any} // 限制student对象的具体内容,使⽤【回⻋】分隔 let student: { id: string grade:number } // 以下代码均⽆警告 person = {name:'张三',age:18} person = {name:'李四'} car = {price:100,color:'红⾊'} student = {id:'tetqw76te01',grade:3} ``` 4. 限制函数的参数、返回值,使⽤以下形式 ```typescript let demo: (a: number, b: number) => number demo = function(x,y) { return x+y } ``` 5. 限制数组,使⽤以下形式 ```typescript let arr1: string[] // 该⾏代码等价于: let arr1: Array<string> let arr2: number[] // 该⾏代码等价于: let arr2: Array<number> arr1 = ['a','b','c'] arr2 = [1,3,5,7,9] ``` ## 4.7. tuple `tuple` 就是⼀个⻓度固定的数组。 ```typescript let t: [string,number] t = ['hello',123] // 警告,不能将类型"[string, number, boolean]"分配给类型"[string, number]" t = ['hello',123,false] ``` ## 4.8. enum `enum` 是枚举 ```typescript // 定义⼀个枚举 enum Color { Red, Blue, Black, Gold } // 定义⼀个枚举,并指定其初识数值 enum Color2 { Red = 6, Blue, Black, Gold } console.log(Color) /* { 0: 'Red', 1: 'Blue', 2: 'Black', 3: 'Gold', Red: 0, Blue: 1, Black: 2, Gold: 3 } */ console.log(Color2) /* { 6: 'Red', 7: 'Blue', 8: 'Black', 9: 'Gold', Red: 6, Blue: 7, Black: 8, Gold: 9 } */ // 定义⼀个phone变量,并设置对⻬进⾏限制 let phone: {name:string,price:number,color:Color} phone = {name:'华为Mate60',price:6500,color:Color.Red} 12 ⾃定义类型,可以更灵活的限制类型 常规类: 5. ⾃定义类型 6. 抽象类 phone = {name:'iPhone15Pro',price:7999,color:Color.Blue} if(phone.color === Color.Red){ console.log('⼿机是红⾊的') } ``` # 5.⾃定义类型 ⾃定义类型,可以更灵活的限制类型 ```typescript // 性别的枚举 enum Gender { Male, Female } // ⾃定义⼀个年级类型(⾼⼀、⾼⼆、⾼三) type Grade = 1 | 2 | 3 // ⾃定义⼀个学⽣类型 type Student = { name:string, age:number, gender:Gender, grade:Grade } // 定义两个学⽣变量:s1、s2 let s1:Student let s2:Student s1 = {name:'张三',age:18,gender:Gender.Male,grade:1} s2 = {name:'李四',age:18,gender:Gender.Female,grade:2} ``` # 6.抽象类 常规类: ```typescript class Person { name: string age: number constructor(name:string,age:number){ this.name = name this.age = age } } const p1 = new Person('张三',18) const p2 = new Person('李四',19) console.log(p1) console.log(p2) ``` 继承: ```typescript // Person类 class Person { } // Teacher类继承Person class Teacher extends Person { } // Student类继承Person class Student extends Person { } // Person实例 const p1 = new Person('周杰伦',38) // Student实例 const s1 = new Student('张同学',18) const s2 = new Student('李同学',20) // Teacher实例 const t1 = new Teacher('刘⽼师',40) const t2 = new Teacher('孙⽼师',50 ``` 抽象类:不能去实例化,但可以被别⼈继承,抽象类⾥有抽象⽅法 ```typescript // Person(抽象类) abstract class Person { } // Teacher类继承Person class Teacher extends Person { // 构造器 constructor(name: string,age: number){ super(name,age) } // ⽅法 speak(){ console.log('你好!我是⽼师:',this.name) } } // Student类继承Person class Student extends Person { } // Person实例 // const p1 = new Person('周杰伦',38) // 由于Person是抽象类,所以此处不可以new Perso n的实例对象 ``` # 7.接⼝ 接⼝梳理: 1. 接⼝⽤于限制⼀个类中包含哪些属性和⽅法: ```typescript // Person接⼝ interface Person { // 属性声明 name: string age: number // ⽅法声明 speak():void } // Teacher实现Person接⼝ class Teacher implements Person { name: string age: number // 构造器 constructor(name: string,age: number){ this.name = name this.age = age } // ⽅法 speak(){ console.log('你好!我是⽼师:',this.name) } } ``` 2. 接⼝是可以重复声明的: ```typescript // Person接⼝ interface PersonInter { // 属性声明 name: string age: number } // Person接⼝ interface PersonInter { // ⽅法声明 speak():void } // Person类继承PersonInter class Person implements PersonInter { name: string age: number // 构造器 constructor(name: string,age: number){ this.name = name this.age = age } // ⽅法 speak(){ console.log('你好!我是⽼师:',this.name) } } ``` 3. 【接⼝】与【⾃定义类型】的区别: > 接⼝可以: > > 1. 当⾃定义类型去使⽤; > 2. 可以限制类的结构; > > ⾃定义类型: > > 1. 仅仅就是⾃定义类型; ```typescript // Person接⼝ interface Person { // 应该具有的属性 name: string age: number //应该具有的⽅法 speak(): void } // Person类型 /* type Person = { // 应该具有的属性 name: string age: number // 应该具有的⽅法 speak():void } */ // 接⼝当成⾃定义类型去使⽤ const person: Person = { name: '张三', age: 18, speak() { console.log('你好!') } } ``` 4. 【 接 ⼝ 】 与 【 抽 象 类 】 的 区 别: > 抽象类: > > 1. 可以有普通⽅法,也可以 有 抽 象 ⽅ 法 ; > 2. 使 ⽤ extends 关 键 字 去 继承抽 象 类 ; > > 接 ⼝ 中: > > 1. 只能 有 抽 象 ⽅ 法 ; > 2. 使 ⽤ implements 关 键 字 去 实 现 接 ⼝ 抽 象 类举例: ```typescript // 抽象类 —— Person abstract class Person { // 属性 name:string age:number // 构造器 constructor(name:string,age:number){ this.name = name this.age = age } // 抽象⽅法 abstract speak():void // 普通⽅法 walk(){ console.log('我在⾏⾛中....') } } // Teacher类继承抽象类Person class Teacher extends Person { constructor(name:string,age:number){ super(name,age) } speak(){ console.log(`我是⽼师,我的名字是${this.name}`) } } ``` 接⼝举例: ```typescript // 接⼝ —— Person,只能包含抽象⽅法 interface Person { // 属性,不写具体值 name:string age:number // ⽅法,不写具体实现 speak():void } // 创建Teacher类实现Person接⼝ class Teacher implements Person { name:string age:number constructor(name:string,age:number){ this.name = name this.age = age } speak(){ console.log('我在⻜快的⾏⾛中......') } } ``` # 8.属性修饰符 | `readonly` | 只读属性 | 属性⽆法修改 | | :---------: | :------: | :------------------------: | | `public` | 公开的 | 可以在类、⼦类和对象中修改 | | `protected` | 受保护的 | 可以在类、⼦类中修改 | | `private` | 私有的 | 可以在类中修改 | # 9.泛型 定义⼀个函数或类时,有些情况下⽆法确定其中要使⽤的具体类型(返回值、参数、属性的类型不能确定),此时就需要泛型了 举例: `<T>` 就是泛型,(不⼀定⾮叫 `T` ),设置泛型后即可在函数中使⽤ `T` 来表示该类型: ```typescript function test<T>(arg: T): T{ return arg; } // 不指名类型,TS会⾃动推断出来 test(10) // 指名具体的类型 test<number>(10) ``` 泛型可以写多个: ```typescript function test<T, K>(a: T, b: K): K{ return b; } // 为多个泛型指定具体⾃值 test<number, string>(10, "hello"); ``` 类中同样可以使⽤泛型: ```typescript class MyClass<T>{ prop: T; constructor(prop: T){ this.prop = prop; } ``` 也可以对泛型的范围进⾏约束: ```typescript interface Demo{ length: number; } // 泛型T必须是MyInter的⼦类,即:必须拥有length属性 function test<T extends Demo>(arg: T): number{ return arg.length; } test(10) // 类型"number"的参数不能赋给类型"Demo"的参数 test({name:'张三'}) // 类型"{ name: string; }"的参数不能赋给类型"Demo"的参数 test('123') test({name:'张三',length:10}) ```
typeScript系列一:Ts数组哪些事
先说明一下部分人**对Ts的误解**,认为在结合ts的demo和实际开发的时候见到声明变量就要给类型,这不但增加不了什么,而且很无聊,我们需要让ts发挥的有价值。【后续讲原因,让你进一步了解原来它是这样的TS】 首先,想要使用,还是要先了解 ## TS的基本使用 **基本类型** ``` let num: number = 1 // 数字 let str: string = 'hello world' // 字符串(支持模板字符串) let bool: boolean = true // 布尔 ``` **引用类型** 数组 ``` // 1.单一的元素类型 let ary1: number[] = [9,5,2,7] // 2.混合元素类型 let ary2: (number | string)[] = [1,2,'ts','狗东西'] // 将类型提取出来。type关键字:声明联合类型,后续讲 type Iary = number | string let ary3: Iary[] = [3,4,'ts','好东西'] // Array表示数组,数组元素是Iary类型。<>是泛型,后续讲。 let ary4: Array<Iary> = [1,4,'狗东西','好东西'] // 同理 <Iary> 也可以展开 let ary5: Array<number | string> = [1,4,'狗东西','好东西'] // 复杂数据类型的数组 // 接口interface :描述对象,抽象的属性,表述一个数据结构 interface Iperson { name:string age:number } let ary5: Array<Iperson> = [{name:'kiki',age:18}] let ary5: Iperson[] = [{name:'jucky',age:18}] // ok 到这里你已经见到了type , interface , 泛型<>,ts语法中的高频角色,也是项目中的高频角色。 let ary5: Array<Iperson> = [{name:'kiki',age:18}] let ary5: Iperson[] = [{name:'jucky',age:18}] //可能有人想问这两种语法有什么不同,在这里是等价的。 //但是泛型<>的主要价值不在于此,泛型价值体现在函数使用上面,函数是我们调用, //所以我们在调用函数的时候可以为其传入各种数据, //所以泛型就是不把传入的类型写死,等你传啥类型我就是啥类型, //这里的类型一般指的是比较复杂的类型,详细的后续讲,晚安 ```
后台传递的数据的b,前端按大小转换KB,MB,GB?
对于我之前的代码 ```js function formatSizeUnits(kb) { var result = ''; if (kb < 1024) { result = kb + ' KB'; } else if (kb < 1024 * 1024) { result = (kb / 1024).toFixed(2) + ' MB'; } else if (kb < 1024 * 1024 * 1024) { result = (kb / 1024 / 1024).toFixed(2) + ' GB'; } else { result = (kb / 1024 / 1024 / 1024).toFixed(2) + ' TB'; } return result; } ``` 对吧,这代码一看就是初学者的,只为了完成业务而封装的一个转换类型的函数 再看看大佬的两种方案 ```js function formatSizeUnits(kb) { let units = ['KB', 'MB', 'GB', 'TB', 'PB']; let unitIndex = 0; while (kb >= 1024 && unitIndex < units.length - 1) { kb /= 1024; unitIndex++; } return `${kb.toFixed(2)} ${units[unitIndex]}`; } ``` ```js const formatSizeUnits = (kb) => { if (!kb) return '0 KB'; let unit = ['KB', 'MB', 'GB', 'TB', 'PB']; const step = 1024; const e = Math.floor(Math.log(kb) / Math.log(step)); return `${(kb / step ** Math.floor(e)).toFixed(2)} ${unit[e]}`; }; ``` 一目了然,大家还有什么好的方法
Vite + Vue3 实现版本更新检查,实现页面自动刷新
前端版本更新检查,实现页面自动刷新 <!-- more --> 使用 Vite 对 Vue 项目进行打包,对 js 和 css 文件使用了 chunkhash 进行了文件缓存控制,但是项目的 `index.html` 文件在版本频繁迭代更新时,会存在被浏览器缓存的情况。 在发版后,如果用户不强制刷新页面,浏览器会使用旧的 `index.html` 文件,在跳转页面时会向服务器端请求了上个版本 chunkhash 的 js 和 css 文件,但此时的文件已经在版本更新时已替换删除了,最终表现为页面卡顿,控制台报错 404。 ## 解决思路 ### 思路 1 服务器端发版时,上一个版本的 assets 内的文件不删除。 缺点是会随着频繁发版,服务器端前端项目文件会越来越多,浪费空间;若旧页面的接口涉及到参数改动等,会引起报错;流水线使用 docker 打包部署会变得非常麻烦。 ### 思路 2 在每次打包生产代码时,在 `public` 目录下生成一个 `version.json` 版本信息文件,页面跳转时请求服务器端的 `version.json` 中的版本号和浏览器本地缓存的版本号进行对比,从而监控版本迭代更新,实现页面自动更新,获取新的 `index.html` 文件(前提是服务器端对 `index.html` 和 `version.json` 不缓存)。 1. 首先应该禁止浏览器缓存 `index.html` 和 `version.json`,这里是 nginx 的不缓存处理的例子 添加 nginx 配置 ```conf location ~ .*\.(htm|html|json)?$ { expires -1; } ``` 2. 编写 [Vite 插件](https://vitejs.cn/guide/api-plugin.html)实现打包时自动生成版本信息 **版本信息插件** TypeScript 版本,新建文件 `src/plugins/versionUpdatePlugin.ts` ```typescript // versionUpdatePlugin.ts import fs from "node:fs" import path from "node:path" import type { ResolvedConfig } from "vite" function writeVersion(versionFile: string, content: string) { // 写入文件 fs.writeFile(versionFile, content, (err) => { if (err) throw err }) } export default (version: string | number) => { let config: ResolvedConfig return { name: "version-update", configResolved(resolvedConfig: ResolvedConfig) { // 存储最终解析的配置 config = resolvedConfig }, buildStart() { // 生成版本信息文件路径 const file = config.publicDir + path.sep + "version.json" // 这里使用编译时间作为版本信息 const content = JSON.stringify({ version }) if (fs.existsSync(config.publicDir)) { writeVersion(file, content) } else { fs.mkdir(config.publicDir, (err) => { if (err) throw err writeVersion(file, content) }) } }, } } ``` JavaScript 版本,新建文件 `src/plugins/versionUpdatePlugin.js` ```javascript // versionUpdatePlugin.js const fs = require("fs") const path = require("path") const writeVersion = (versionFile, content) => { // 写入文件 fs.writeFile(versionFile, content, (err) => { if (err) throw err }) } export default (options) => { let config return { name: "version-update", configResolved(resolvedConfig) { // 存储最终解析的配置 config = resolvedConfig }, buildStart() { // 生成版本信息文件路径 const file = config.publicDir + path.sep + "version.json" // 这里使用编译时间作为版本信息 const content = JSON.stringify({ version: options.version }) if (fs.existsSync(config.publicDir)) { writeVersion(file, content) } else { fs.mkdir(config.publicDir, (err) => { if (err) throw err writeVersion(file, content) }) } }, } } ``` **vite 配置文件** ::: tip 对于使用 TypeScript 的开发者来说,请确保在 `env.d.ts` 或 `vite-env.d.ts` 文件中添加类型声明,以获得类型检查以及代码提示。 ```typescript // vite-env.d.ts declare const __APP_VERSION__: string ``` ::: ```typescript // vite.config.js or vite.config.ts export default defineConfig((config) => { const now = new Date().getTime() return { // ... define: { // 定义全局变量 __APP_VERSION__: now, }, plugins: [ // ... versionUpdatePlugin({ version: now, }), ], // ... } }) ``` 3. 路由跳转时,实时检测版本 检测到新版本自动刷新页面,应该使用前置守卫,在跳转失败报错前检测,跳转失败不会触发后置守卫 ```javascript const router = useRouter() // 这里在路由全局前置守卫中检查版本 router.beforeEach(async () => { await versionCheck() }) // 版本监控 const versionCheck = async () => { if (import.meta.env.MODE === "development") return const response = await axios.get("version.json") if (__APP_VERSION__ !== response.data.version) { ElMessage({ message: "发现新内容,自动更新中...", type: "success", showClose: true, duration: 1500, onClose: () => { window.location.reload() }, }) } } ```
4-12,极简typescript入门
<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <h3><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">名词解释</strong></h3> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>JS:JavaScript缩写</li> <li data-list="ordered"><span class="ql-ui"></span>TS:TypeScript缩写</li> </ol> <p><br></p> <h3><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">先说结论</strong></h3> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>JS能做的事情,TS都能做,TS只是给JS增加了类型,地球人都知道JS是一门弱类型语言,非常灵活的同时,缺点也很明显,这意味着JS在运行时可能会出现类型错误。TS 提供了静态类型检查,有助于在编译阶段捕获类型错误。这可以防止运行时出现许多类型错误,并帮助开发人员编写更可靠的代码。</li> </ol> <p><br></p> <h3><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">如何学习?</strong></h3> <p><br></p> <p>本文通过技术蛋老师的一个视频进行学习,具体学习步骤就是,通过做一个小项目,进行学习TS,理解为什么需要TS,JS的缺点,TS的优点等等!</p> <p><br></p> <h4><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">项目介绍</strong></h4> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>一个很简单的表单增删功能操作,通过调用接口获取数据,然后渲染,</li> <li data-list="ordered"><span class="ql-ui"></span>通过 TS 来约束变量,方法的行为,提高代码的可读性</li> </ol> <p><br></p> <h4><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">步骤</strong></h4> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>全局安装 TS 命令: npm i typescript -g (mac不同)</li> <li data-list="ordered"><span class="ql-ui"></span>创建一个TS文件,创建并打印一个变量</li> <li data-list="ordered"><span class="ql-ui"></span>在终端输入tsc --init,我们会多了一个文件<a href="http://tsconfig.json" target="_blank">tsconfig.json</a></li> <li data-list="ordered"><span class="ql-ui"></span>打开<a href="http://tsconfig.json" target="_blank">tsconfig.json</a>我们可以看到有一个"target": "es2016" ,这里是将TS编译成JS遵循的标准</li> <li data-list="ordered"><span class="ql-ui"></span>终端输入tsc,就会将我们的TS转为JS,对应的同名的JS文件</li> <li data-list="ordered"><span class="ql-ui"></span>我们定义变量,可以在变量后使用冒号加一个类型值,就可以申明这个变量的类型了,举个例子:let name: string = 'xiaoming'这里如果给name赋值数值类型,编译的时候就会出错,而不是等到项目上线才会发现这个bug</li> <li data-list="ordered"><span class="ql-ui"></span>我们可以使用tsc -w这里的w表示watch,每当文件保存的时候,就会自动的编译此文件为js文件</li> <li data-list="ordered"><span class="ql-ui"></span>我们可以使用断言来解决一些问题,使用断言的方式非常简单,就是在值的末尾使用 as + 类型 即可,举个例子,let dom = document.querySelector('button') as HTMLButtonElement这样,我们就强制告诉dom是一个按钮类型的值</li> <li data-list="ordered"><span class="ql-ui"></span>但是,断言的问题是被断言的值是有问题的,所有我们可以使用联合类型,来处理类型为多种情况的场景,使用方式非常简单,使用 | 来连接两个类型,举个例子:let dom: HTMLButtonElement | null = document.querySelector('button')</li> <li data-list="ordered"><span class="ql-ui"></span>当我们要给一个对象申明类型的时候,并且这个类型还有很多地方需要复用,我们就可以使用 interface 接口了,使用非常简单,举个例子,interface item {name: string; age: number; sex: 'boy' | 'girl'}</li> <li data-list="ordered"><span class="ql-ui"></span>我们可以在方法形参括号后加上一个类型,来指定返回类型,举个例子:function aa(): void {}</li> <li data-list="ordered"><span class="ql-ui"></span>当我们并不知道Promise返回的具体内容的时候,我们可以用TS的泛型来表示,举个例子:function getJSON<T>(url: string) {},这里的T表示的就是一个占位符,表示现在并不确定里面的类型</li> <li data-list="ordered"><span class="ql-ui"></span>我们在使用 getJSON 方法的时候,可以去确定这个 T 的具体类型</li> <li data-list="ordered"><span class="ql-ui"></span>我们通常定义一个数组类型里面的元素类型,是通过在 [] 前面加上一个具体的类型,例如: let arr: string[] = ['1', '2'],如果数组的元素是对象,那么我们就通过接口(interface)来定义</li> <li data-list="ordered"><span class="ql-ui"></span>我们可以把error类型声明为 unkonwn 或者 Error</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> try {} </div> <div class="ql-code-block"> catch (err: Error | unkonwn) { </div> <div class="ql-code-block"> let message: string </div> <div class="ql-code-block"> if(err instanceof Error) { </div> <div class="ql-code-block"> message = err.message </div> <div class="ql-code-block"> }else { </div> <div class="ql-code-block"> message = String(err) </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> console.log(message, 'message') </div> <div class="ql-code-block"> } </div> </div> <ol> <li data-list="ordered"><span class="ql-ui"></span>我们可以在 addEventListener后面加上 <'click'>来防止我们在后面监听事件值上出现拼写错误</li> <li data-list="ordered"><span class="ql-ui"></span>a 标签的 TS 类型为HTMLAnchorElement</li> <li data-list="ordered"><span class="ql-ui"></span>Event事件类型,有鼠标事件和键盘事件的区别</li> </ol> <p><br></p> <h3><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">小知识</strong></h3> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>绝大多数游览器识别不了TS,我们需要将TS编译成JS(看过极简<a href="http://Next.js" target="_blank">Next.js</a>的应该理解),然后给游览器识别运行</li> <li data-list="ordered"><span class="ql-ui"></span>tsc表示typescript compiler编译器的意思!init表示初始化</li> <li data-list="ordered"><span class="ql-ui"></span>类型的首字母是小写,不是大写</li> <li data-list="ordered"><span class="ql-ui"></span><strong>使用TS,定义任何东西,要注明类型,调用任何东西要检查类型;</strong></li> <li data-list="ordered"><span class="ql-ui"></span>类在接收接口(interface)的时候,不能使用变量声明的方式,也就是不能使用冒号(:),我们需要使用 implements 关键字,也就是实现的意思,举个例子:class Cat implements item {}</li> <li data-list="ordered"><span class="ql-ui"></span>如果,你的接口里的有些键值对,有可能存在,也有可能不存在,我们可以在键的后面加上 ? 来告诉TS这个值可能不存在,举个例子 interface item {name: string; age: number; like?: object}</li> </ol> <p><br></p> <h3><strong style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">参考</strong></h3> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>冴羽:<a href="https://ts.yayujs.com/" target="_blank" style="color: rgb(65, 131, 196);">https://ts.yayujs.com/</a></li> <li data-list="ordered"><span class="ql-ui"></span>技术蛋老师:<a href="https://www.bilibili.com/video/BV12P411E79E" target="_blank" style="color: rgb(65, 131, 196);">https://www.bilibili.com/video/BV12P411E79E</a></li> </ol> <p><br></p> </div> </body> </html>
