[前端] Leader:可以不用但要知道😠一文速查 TypeScript 基础知识点,字典式速查,全文干货!

写在开头

此文尽可能全面地聚焦基础知识,进阶内容较少,如有需要可针对基础知识自行学习(或者我更😭)

字典式笔记,无清晰逻辑线,配合目录食用更佳~

没有涉及到的部分后面也会持续更新~

简介

JavaScript 复制代码
基本简介:
- TypeScript 可以看成是 JavaScript 的超集,它继承了后者的全部语法,增加了一些自己的语法
- TypeScript 对 JavaScript 添加的最主要部分,就是一个独立的类型系统

- 在语法上,JavaScript 属于动态类型语言,而 TypeScript 属于静态类型语言,各有优缺点
- 因此,在 TypeScript 中进行以下行为都会报错:
    - 随意给变量赋其他类型的值
    - 随意增删对象属性
    - 在给变量前赋值前调用变量 

tsc 常用选项

JavaScript 复制代码
TypeScript 的编译:
- JavaScript 的运行环境(浏览器和 Node.js)不认识 TypeScript 代码
- TypeScript 官方只提供编译器 (tsc) ,编译为能运行的 JavaScript 代码
- 因此,TypeScript 只是编译时的类型检查,而不是运行时的类型检查,编译后运行中不再检查

tsconfig. json
- TypeScript 允许将tsc的编译参数,写在配置文件tsconfig.json
- 只要当前目录有这个文件,tsc就会自动读取,所以运行时可以不写参数

tsc 的一些编译选项

JavaScript 复制代码
1. ---outFile
    将多个 TypeScript 脚本编译成一个 JavaScript 文件
    $ tsc file1.ts file2.ts --outFile app.js

2. ---outDir
    编译结果默认都保存在当前目录,--outDir参数可以指定保存到其他目录
    $ tsc app.ts --outDir dist

3. ---target
    tsc 默认将代码编译为很久的 JavaScript,即3.0版本(es3)
    -target 可以指定编译后的 JavaScript 版本,建议使用es2015或者更新版本
    $ tsc --target es2015 app.ts
    
4. --noEmitOnError
    tsc 编译报错也会产生 JavaScript 产物
    如果希望报错就停止编译,不生成编译产物,可以使用--noEmitOnError参数
    $ tsc --noEmitOnError app.ts    

5. --noEmit
    只检查类型是否正确、是否有编译错误,不生成 JavaScript 文件
    $ tsc --noEmit app.ts

6. --noImplicitAny
    只要推断出 any 类型就会报错
    $ tsc --noImplicitAny app.ts
    例外: var x; let y; // 不报错,TypeScript 推断它们的类型为any
         因此,使用 let 和 var 声明变量时,如果不赋值,最好显式声明类型,否则可能存在安全隐患

7. --strictNullChecks
    启用后,undefined 和 null 无法赋值给 any 和 unknown 以外其他类型的变量(也无法相互赋值)
    没有声明类型的变量被赋值为 undefined 或 null,可以被编译器正常推断为自身,而非 any
    

[类型系统] 类型分类

基本类型 | 数组 | 元组 | 对象 | 值

JavaScript 复制代码
基本类型:
number、boolean、string、void、bigint、symbol、null、undefined、any、unknown、never

其中:
bigint:与 number 不兼容,不可赋值为整数和小数,ES2020 标准引入,编译参数target不得低于es2020
any:任何类型,允许赋予任何类型的值,属于"顶层类型"(top type)
     污染:容易污染其它变量,导致编译时不出错,但运行时出错
unknown:不能直接使用,必须经过类型检查才能使用,可以视为"严格的any",也属于"顶层类型"(top type)
         解决污染:不能赋值给 any 和 unknown 以外类型的变量,不能直接调用该类型变量的方法和属性,
不能直接当做函数执行,必须事先缩小类型
never:表示永远不会有值的类型,例如抛出异常或死循环的函数返回类型,属于"底层类型"(bottom type)
       集合论: never 类型可以赋值给任意其他类型,因为空集是任何集合的子集

let something: any = 42;
let value: unknown = "Hello unknown";

something = "Hello any";
if (typeof value === "string") {
  console.log(value.toUpperCase());    // 可以安全访问
}

function throwError(): never {
  throw new Error("An error occurred");
}

其他特点:

JavaScript 复制代码
1. 如果没有声明类型的变量被赋值为 undefined 或 null,它们的类型会被推断为 any
   undefined 和 null 也可以赋给任何类型的值
   在 tsc 打开 strictNullChecks 后,他们才可以被正常推断为自身,同时限制只能赋给 any 或 unknown

2. 包装对象 Boolean、String、Number、BigInt、Symbol
   大写类型同时包含包装对象和字面量,小写类型只包含字面量
注意: 建议只使用小写类型,因为绝大部分使用原始类型的场合,都是使用字面量,不使用包装对象
     而且,TypeScript 把很多内置方法的参数都定义成了小写类型,使用大写类型会报错

数组:

JavaScript 复制代码
- 数组:可以通过类型注解来指定数组元素的类型,例如 number[] 或 Array<number>

let numbers: number[] = [1, 2, 3];
let otherNumbers: Array<number> = [1, 2, 3];

-- [数组类型特点]
1. 允许使用方括号读取数组成员的类型

type Names = string[];
type Name = Names[0]; // string

2. 成员数量可以动态变化,编译时不会对数组边界进行检查,越界访问数组并不会报错

let arr:number[] = [1, 2, 3];
let foo = arr[3]; // 不存在,但不报错

3. TypeScript 会推断空数组的类型是 any[],后面赋值时会自动更新类型推断

const arr = [];    // 推断为 any[]
arr.push(123);     // 推断类型为 number[]
arr.push('abc');   // 推断类型为 (string | number)[]

4. 在数组类型前面加上 readonly 关键字或使用 const 断言可声明只读数组,它是数组的父类型
    
let a1:number[] = [0, 1]; // 或 const arr = [0, 1] as const;
let a2:readonly number[] = a1; // 不报错
a1 = a2; // 报错

注意: readonly 关键字不能与数组的泛型写法一起使用,但 TypeScript 提供了两个专门的泛型用于只读数组

const a1:ReadonlyArray<number> = [0, 1];
const a2:Readonly<number[]> = [0, 1];

5. 多维数组,表示形式为 T[][]的形式,其中 T 是最底层数组成员的类型

var multi:number[][] = [[1,2,3], [23,24,25]];

元组:

JavaScript 复制代码
- 元组:与数组类似,但固定长度,且可以包含不同类型的元素,例如 [string, number]

let person: [string, number] = ["John", 25];

-- [数组类型特点]
1. 使用元组必须明确给出类型声明,否则 TypeScript 会自动将其推断为数组

let a = [1, true];    // a 的类型为 (number | boolean)[]

对象:

JavaScript 复制代码
- 对象: 显式指定一个对象的结构

let point: { x: number, y: number } = { x: 10, y: 20 };

-- [对象类型其他特点] Object 与 object

Object 代表 JS 广义对象,表示所有可以转成对象的值,可简写为{}
除了 undefined 和 null 以外的任何值都可以转为对象并赋值给 Object 类型,太广泛了,不易用

object 代表 JS 狭义对象,即可以用字面量表示的对象,只包含对象、数组和函数,不包括原始类型的值
大多数时候使用对象都不希望包含原始类型,所以建议总是使用object

注意: Object 和 object 都只包含 JS 内置对象原生的属性和方法,不包含用户自定义的属性和方法

值类型:

JavaScript 复制代码
- 值类型
TypeScript 遇到 const 命令声明且未标注类型的变量,会推断其为值类型
单个值类型意义不大,实际开发往往将多个值结合,作为联合类型使用

注意: const命令声明的变量,如果赋值为对象,并不会推断为值类型
毕竟 JavaScript 里 const 变量赋值为对象时,属性值也是可以改变的

const x = { foo: 1 };  // x 的类型是 { foo: number }

接口 (Interfaces)

JavaScript 复制代码
接口是 TypeScript 中用来定义对象结构的方式,它定义了对象的属性和方法的类型要求。

interface Person {
    name: string;
    age: number;
}
const person: Person = { 
    name: 'Alice', 
    age: 25 
};

-- interface VS type
绝大多数情况下可以互换,用于定义对象结构,但也存在一些区别:

- 扩展性:interface 支持声明合并,多个同名接口会自动合并,在为第三方库扩展类型时很有用,type 不支持
- 实现:类可以 implements 一个 interface,但不能 implements 一个 type
- 复杂类型定义:type 的能力更强,可以用于定义联合类型、交叉类型、元组等任何复杂类型

- 总结
优先使用 interface 定义公共 API 的形状 (如对象、类),因为它具有更好的扩展性和面向对象的语义。
优先使用 type 定义复杂的、临时的类型,特别是联合类型、交叉类型或需要利用其他高级类型操作的场景。

泛型 (Generics)

JavaScript 复制代码
在定义函数、类、接口等时,可以不预先指定具体类型,而是在使用时再指定具体类型,提升代码的复用性和灵活性

function log<T>(value: T): T {
    return value;
}
let result = log(42);  // T 被推断为 number

- <T> 是一个泛型参数,它允许该函数接收任意类型的参数,并且返回相同类型的值
- 其中 T 只是一个占位符,可以理解成 "类型变量"。函数调用时 T 将会被实际的类型所替代

索引类型 | 工具类型

索引类型:

JavaScript 复制代码
索引类型允许根据给定的键来查询一个类型的值,或创建一个对象的类型

type Person = { name: string; age: number };
type PersonKeys = keyof Person;  // "name" | "age"

let key: PersonKeys = "name";
let person: Person = { name: "John", age: 30 };
console.log(person[key]);  // John

工具类型:

JavaScript 复制代码
TypeScript 提供了一些内置的工具类型,帮助简化常见的类型操作

- Partial <T> : 将类型 T 的所有属性变为可选
interface Person {
  name: string;
  age: number;
}
type PartialPerson = Partial<Person>;

- Required <T> : 将类型 T 的所有属性变为必选
type RequiredPerson = Required<Person>;

- Readonly <T> : 将类型 T 的所有属性变为只读
type ReadonlyPerson = Readonly<Person>;

- Record <K, T> : 创建一个对象类型,其中键的类型为 K,值的类型为 T
type NameAge = Record<string, number>;

- Pick <T, K> : 从类型 T 中选取一部分属性 K
type NameOnly = Pick<Person, "name">;

- Omit <T, K> : 从类型 T 中省略掉属性 K
type AgeOnly = Omit<Person, "name">;

-- 注
不应孤立看待这些工具从而死记硬背,它们都是基于映射类型和条件类型构建的最佳实践封装
eg:Omit<T, K> = Pick + Exclude

条件类型 | 映射类型

JavaScript 复制代码
- 条件类型根据给定的条件选择类型,语法为 T extends U ? X : Y
- 如果类型 T 可以赋值给类型 U(即 T extends U 为 true),则结果类型是 X,反之亦然

type IsString<T> = T extends string ? string : never;
type Test1 = IsString<string>;  // string
type Test2 = IsString<number>;  // never

1. 条件类型 + 推断(infer):

JavaScript 复制代码
- infer R 是 TypeScript 的条件类型中的一种推断类型,用于从类型 T 中提取出函数的返回类型,并返回
- 以下代码主要基于 (... args : any []) => infer R 指定推断一个函数类型的返回值类型

type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
type Test1 = ReturnTypeOf<() => string>;  // string
type Test2 = ReturnTypeOf<() => number>;  // number
type Test3 = ReturnTypeOf<() => boolean>; // boolean
type Test4 = ReturnTypeOf<string>;        // never

2. 条件类型 + 映射类型:

JavaScript 复制代码
- 以下代码主要基于 (infer U)[] 指定推断某个数组元素的类型
- 对于 泛型编程 或 不确定某类型是否是数组 时,此用法可以动态地提取其元素类型

type ElementType<T> = T extends (infer U)[] ? U : never;
type Test1 = ElementType<number[]>;  // number
type Test2 = ElementType<string[]>;  // string
type Test3 = ElementType<boolean[]>; // boolean
type Test4 = ElementType<number>;    // never

3. 递归打平:

JavaScript 复制代码
可以处理复杂的嵌套类型,以下代码通过递归展开嵌套数组,提取出它的最终元素类型,不管嵌套了多少层

type Flatten<T> = T extends (infer U)[] ? Flatten<U> : T;
type Test1 = Flatten<number[]>;         // number
type Test2 = Flatten<string[][]>;       // string
type Test3 = Flatten<(number | string)[]>;  // number | string

4. 分配映射:

JavaScript 复制代码
基于不同的传入类型条件映射到不同类型

type MyType<T> = T extends number ? string :
                 T extends boolean ? number :
                 T extends string ? boolean :
                 never;

type Test1 = MyType<number>;  // string
type Test2 = MyType<boolean>; // number
type Test3 = MyType<string>;  // boolean
type Test4 = MyType<undefined>;  // never

5. 类型筛选、合并与转换

JavaScript 复制代码
- 类型筛选:基于某些条件过滤出符合条件的类型,如筛选出某个类型的子集
- 类型合并:通过条件类型动态生成联合类型、交叉类型等复杂类型
- 类型转换工具:通过条件类型实现类型自动转换

以下代码可将对象的所有属性类型转换为 number:

type ToNumber<T> = {
  [K in keyof T]: T[K] extends string ? number : T[K];
};
type Test = ToNumber<{ a: string; b: number }>;
// 结果:{ a: number; b: number }
JavaScript 复制代码
映射类型用于将一个类型的所有属性映射到另一个类型,通常用在类型修改或创建新类型时

type Person = { name: string; age: number };
type ReadOnlyPerson = { readonly [K in keyof Person]: Person[K] };

let person: ReadOnlyPerson = { name: "John", age: 30 };
person.name = "Doe"; // 错误:不能修改只读属性

枚举 | 联合类型 | 交叉类型

JavaScript 复制代码
- 枚举是一种基于数值(数字或字符串)的"集合类型",它允许你为一组值定义名字,可以是数字或字符串
- 枚举类型是一种对象类型,它将一组常量值封装成一个具名的集合(对象),包含了多个常量值的映射关系
- 枚举的类型定义是固定的,可以看作一个具名的集合体

**1. 数字枚举 **

JavaScript 复制代码
- 数字枚举为一组数值提供名称映射。默认情况下,枚举的成员从 0 开始递增
- 可以手动设置枚举成员的初始值,其他成员将根据前一个成员的值递增。  enum Direction {   Up,     // 默认值是 0   Down,   // 1   Left,   // 2   Right   // 3 } // 正向映射 let direction: Direction = Direction.Up; console.log(direction);  // 输出: 0
// 反向映射
let directionName: string = Direction[1];  // 值为 1 的枚举成员是 'Down'
console.log(directionName);  // 输出: Down

2. 字符串枚举

JavaScript 复制代码
- 字符串枚举为每个枚举成员指定一个具体的字符串值,而不是自动递增的数字
- 通常用于需要更具可读性和可维护性的场景,如 HTTP 状态码、错误类型等  - 注意: 与数字枚举不同,字符串枚举不支持反向映射,因为每个值本身就是唯一的字符串
enum TaskStatus {   NotStarted = "NOT_STARTED",   InProgress = "IN_PROGRESS",   Completed = "COMPLETED" }  let task: TaskStatus = TaskStatus.InProgress; console.log(task);  // 输出: IN_PROGRESS   

3. 异构枚举

JavaScript 复制代码
- 异构枚举是同时包含数字和字符串类型值的枚举,虽然不常见,但 TypeScript 允许
- 通常不推荐这样做,因为可能会导致代码理解上的混乱

enum MixedEnum {
  No = 0,
  Yes = "YES"
}
console.log(MixedEnum.No);   // 输出: 0
console.log(MixedEnum.Yes);  // 输出: YES

4. 常量枚举

JavaScript 复制代码
- 一种特殊的枚举类型,编译时会被内联到代码中,避免了创建枚举对象的额外开销
- 它是为性能优化而设计的,只能包含常量成员

const enum Direction {
  Up = 1,
  Down,
  Left,
  Right
}

let direction = Direction.Up;  // 这个会被直接编译为:let direction = 1;

5. 枚举的计算成员( Computed Members )

JavaScript 复制代码
- 枚举成员不仅可以是常量值,还可以是通过计算得出的值
- TypeScript 允许你在枚举中使用表达式来计算成员的值

enum Direction {
  Up = 1,
  Down = Up * 2,  // 计算得出:Up * 2 = 2
  Left = "LEFT",
  Right = "RIGHT"
}

console.log(Direction.Down);  // 输出: 2

6. 使用场景

JavaScript 复制代码
- 状态管理:比如表示任务、订单等状态的枚举
- 动作类型:比如表示用户操作或系统事件的枚举
- 配置选项:比如定义一组固定的选项来限制输入值

联合类型:

JavaScript 复制代码
联合类型:表示一个值可以是几种不同类型之一,可以视作"类型放大"

let value: string | number;
value = 'Hello';  // valid
value = 42;       // valid

优势:
打开编译选项strictNullChecks后,其他类型的变量不能赋值为 undefined或null
这时,如果某个变量确实可能包含空值,就可以采用联合类型的写法

let name:string|null;

交叉类型:

JavaScript 复制代码
- 交叉类型:将多个类型合并成一个新类型,表示一个值同时具有多个类型的属性
- 它主要用于表示对象合成,也常用来为对象类型添加新属性
- 虽然用 类型别名(type)比较方便,但使用接口(interface)也可以实现

type Employee = { name: string };
type Worker = { job: string };
type EmployeeWorker = Employee & Worker;
const person: EmployeeWorker = { 
    name: 'Alice', 
    job: 'Developer' 
};

[类型系统] 系统特性

类型别名 | 类型推断 | 类型断言

类型别名:

JavaScript 复制代码
- 类型别名是给类型创建一个新名字,常用于简化复杂类型的声明
- 它与接口类似,但某些情况更适合定义 联合类型(union types)和 交叉类型(intersection types)

type Point = {
    x: number;
    y: number };
const p: Point = {
    x: 10, 
    y: 20 
};

一些特点:
1. 别名不允许重名,同一块级作用域中重复设置别名会报错

type Color = 'red';
type Color = 'blue'; // 报错

2. 别名具有块级作用域,不同块级作用域内的同名别名实际上并不相同

type Color = 'red';
if (Math.random() < 0.5) {
  type Color = 'blue';
}

3. 别名允许嵌套,定义别名时可以使用其他别名

type World = "world";
type Greeting = `hello ${World}`;

类型推断

JavaScript 复制代码
- TypeScript 能在 没有明确指定类型 或 设置类型注解 时 自动推断类型
- 它在大多数情况下可靠,可以减少类型注解冗余,提升开发效率

let num = 10;  // TypeScript 自动推断 num 是 number 类型

类型断言

JavaScript 复制代码
- 类型断言能覆盖 TypeScript 编译器的类型推断,通常在确信变量的类型时使用,可以使用 as 或 <> 语法
- 注意: 类型断言并不会进行类型检查,它只是让编译器假设值是某种类型,所以使用时要谨慎

let someValue: any = "hello";
let strLength: number = (someValue as string).length;  // 将someValue断言为string
// 或者
let strLength2: number = (<string>someValue).length;

类型守卫 | 类型谓词

类型守卫:

JavaScript 复制代码
- 类型守卫是用于在运行时确定一个变量的类型
- 常见的类型守卫有:
    typeof:用来检查基本类型,如 string、number、boolean 等
    instanceof:用来检查某个对象是否是某个类的实例
    自定义类型守卫:通过函数来检查对象的类型

function isString(value: any): value is string {
    return typeof value === 'string';
}

类型谓词:

JavaScript 复制代码
- 上面代码中 value is string 是 TypeScript 的类型谓词(type predicate)语法
- 它的意思是:函数返回布尔值,若返回 true ,则 TypeScript 知道传入值 value 是 string 类型

声明文件(.d.ts)

javascript 复制代码
TypeScript中可以通过声明文件(.d.ts)来为第三方库提供类型支持,尤其是在没有类型定义的情况下。声明文件用于描述库的结构。

declare module 'some-library' {
    export function someFunction(): void;
}

[类与继承] 访问修饰符

markdown 复制代码
- 在类与继承方面,TypeScript 相比 JavaScript 有两个主要特点:
    - 静态类型检查
    - 访问修饰符 ★
    
- TypeScript 访问修饰符:
    - public:可以在任何地方访问
    - private:只能在类内部访问
    - protected:可以在类及其子类中访问

模拟访问修饰符:

JavaScript 复制代码
- 而在 JavaScript 中不存在访问修饰符,类的属性和方法默认是 public 的
- 但可以通过一些技巧模拟类似的行为

1. 默认是 public
class Animal {
  constructor(name) {
    this.name = name;  // 默认是 public
  }
  speak() {
    console.log(`${this.name} makes a sound`);
  }
}

2. 私有字段(ES2022引入)
class Animal {
  #name;  // 私有字段
  constructor(name) {
    this.#name = name;
  }
  speak() {
    console.log(`${this.#name} makes a sound`);
  }

3. 通过闭包模拟
function Animal(name) {
  let _name = name;  // 通过闭包定义私有变量
  this.speak = function() {
    console.log(`${_name} makes a sound`);
  };
}
JavaScript 复制代码
class Animal {
  constructor(public name: string, private age: number) {}
  speak(): void {  
    console.log(`${this.name} makes a sound`);
  }
  getAge(): number {
    return this.age;
  }
}

class Dog extends Animal {
  constructor(name: string, age: number, public breed: string) {
    super(name, age);
  }
  speak(): void {  
    console.log(`${this.name} barks`);
  }
}

const dog = new Dog('Buddy', 3, 'Golden Retriever');
dog.speak();
console.log(dog.name);  // 可以访问,因为 name 是 public
console.log(dog.age);   // 编译时错误:age 是 private

[其他]

装饰器(Decorators)

markdown 复制代码
- 装饰器是实验性功能,需要启用相应配置,它通常用于类的元编程中,允许你为类、方法、属性、参数等附加功能
- 在 JavaScript 中:
    - 启用装饰器:需要安装并配置 Babel 插件 @babel/plugin-proposal-decorators
    - JavaScript 装饰器功能基本上是运行时的操作,仍然不够稳定

- 在 TypeScript 中:
    - 启用装饰器:tsconfig.json 设置 "compilerOptions": experimentalDecorators 为 true
    - TypeScript 原生支持装饰器,语法更加成熟和稳定,并且使用时类型安全

类装饰器:用于类的构造函数

JavaScript 复制代码
function logClass(target: Function) {
  console.log(`Class ${target.name} is being created`);
}
@logClass
class Person {
  constructor(public name: string) {}
}

// Class Person is being created

方法装饰器:用于类的方法

JavaScript 复制代码
function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  console.log(`Method ${key} is being called`);
}
class Person {
  @logMethod
  greet() {
    console.log('Hello!');
  }
}

// Method greet is being called
// Hello!

- 详细解释:
    - target:方法所在的类的原型对象(对于实例方法)或类构造函数(对于静态方法)
    - key:方法名
    - descriptor:PropertyDescriptor,用于描述方法的属性

    - 对于 PropertyDescriptor,它是 JavaScript 中的一个接口,描述对象属性的特性
    - 在装饰器中,PropertyDescriptor 用于描述方法装饰器的目标方法属性
  
    interface PropertyDescriptor {
      value?: any;                // 属性的值。对于方法装饰器来说,通常表示方法本身
      writable?: boolean;         // 是否允许修改该属性
      enumerable?: boolean;       // 是否能在 for...in 或 Object.keys() 中被枚举出来
      configurable?: boolean;     // 是否可以删除或修改属性的特性
    }

属性装饰器:用于类的属性

JavaScript 复制代码
function logProperty(target: any, key: string) {
  console.log(`Property ${key} is being accessed`);
}
class Person {
  @logProperty
  name: string;
}

// Property name is being accessed

参数装饰器:用于类方法的参数

JavaScript 复制代码
function logParameter(target: any, key: string, index: number) {
  console.log(`Parameter at index ${index} of method ${key} is being used`);
}
class Person {
  greet(@logParameter name: string) {
    console.log(`Hello, ${name}`);
  }
}

// Parameter at index 0 of method greet is being used
// Hello, Alice

结语

不当之处恳请批评指正,感谢看到这里,觉得不错的话点个赞吧555

本人vx:wx072266,欢迎交流!

相关推荐
Lsx_7 分钟前
MultiRepo 和 Monorepo:代码管理的演进与选择
前端·javascript·架构
潘多编程18 分钟前
构建企业级Web应用:AWS全栈架构深度解析
前端·架构·aws
裕波21 分钟前
Vue 与 Vite 生态最新进展:迈向一体化与智能化的未来
前端·vue.js
destinying38 分钟前
当部分请求失败时,前端如何保证用户体验不崩溃?
前端·javascript·程序员
幼儿园技术家44 分钟前
Diff算法的简单介绍
前端
陈随易1 小时前
为VSCode扩展开发量身打造的UI库 - vscode-elements
前端·后端·程序员
叁金Coder1 小时前
业务系统跳转Nacos免登录方案实践
前端·javascript·nginx·nacos
蓝倾1 小时前
京东商品销量数据如何获取?API接口调用操作详解
前端·api·fastapi
stoneship1 小时前
满帮微前端
前端
GKDf1sh1 小时前
【前端安全】聊聊 HTML 闭合优先级和浏览器解析顺序
前端·安全·html