ts相关笔记(基础必看)

推荐一下小册 TypeScript 全面进阶指南,此篇笔记来源于此,记录总结,加深印象!

另外,如果想了解更多ts相关知识,可以参考我的其他笔记:

  • vue3+ts开发干货笔记
  • TSConfig 配置(tsconfig.json)
  • ts相关笔记(Partial、Required、Readonly、Record、Exclude、Extract)
  • ts相关笔记(类型层级)
  • ts相关笔记(extends、infer、Pick、Omit)

原始类型和对象类型

原始类型

除了最常见的 number / string / boolean / null / undefined, ES6、ES11)又分别引入了 2 个新的原始类型:symbol 与 bigint

const name: string = 'ts';
const age: number = 18;
const male: boolean = false;
const undef: undefined = undefined;
const nul: null = null;
const obj: object = { name, age, male };
const bigintVar1: bigint = 90071992547409212n;
const bigintVar2: bigint = BigInt(9007199254740991);
const symbolVar: symbol = Symbol('unique');

null和undefined

  • 在js里,null表示一个空值,undefined表示没有值
  • 在ts里,null 与 undefined 类型都是有具体意义的类型

在没有开启strictNullChecks 检查的情况下,这两种类型会被视作其他类型的子类型,比如 string 类型会被认为包含了 null 与 undefined 类型:

const tmp1: null = null;
const tmp2: undefined = undefined;

const tmp3: string = null; // 仅在关闭 strictNullChecks 时成立,下同
const tmp4: string = undefined;

void

用于描述一个内部没有 return 语句,或者没有显式 return 一个值的函数的返回值,如:

function func1() {}
function func2() {
  return;
}
function func3() {
  return undefined;
}

数组类型标注

const arr1: string[] = []; // 更推荐此写法

const arr2: Array<string> = [];
元组(Tuple)

元组就是类型和数据的个数一开始就已经限定好了,某些情况下使用元组代替数组要更加妥当。
下面是一些应用

  • 提供越界提醒

    const arr3: string[] = ['aaa', 'bbb', 'ccc']
    const arr4: [string,string,string] = ['aaa', 'bbb', 'ccc']
    console.log(arr4[4])
    

    在这里插入图片描述

  • 可选

    const arr6: [string, number?, boolean?] = ['aaa'];
    // 下面这么写也可以
    // const arr6: [string, number?, boolean?] = ['aaa', , ,];
    
  • 具名元组
    在 TypeScript 4.0 中,有了具名元组(Labeled Tuple Elements)的支持,使得我们可以为元组中的元素打上类似属性的标记:

    const arr7: [name: string, age: number, male?: boolean] = ['aaa', 18, true];
    

对象类型标注

使用interface接口
interface IDescription {
  name: string;
  age: number;
  male: boolean;
}

const obj1: IDescription = {
  name: 'aaa',
  age: 599,
  male: true,
};

可选 ?

interface IDescription {
  name: string;
  age: number;
  male?: boolean;
  func?: Function;
}

const obj2: IDescription = {
  name: 'aaa',
  age: 599,
  male: true,
  // 无需实现 func 也是合法的
};

只读

interface IDescription {
  readonly name: string;
  age: number;
}

const obj3: IDescription = {
  name: 'aaa',
  age: 599,
};

// 无法分配到 "name" ,因为它是只读属性
obj3.name = "AAA";
使用type 类型别名
type User = {
    name: string
    age: number
};

很多人更喜欢用 type(Type Alias,类型别名)来代替接口结构描述对象,
而更推荐的方式是

  • interface 用来描述对象、类的结构
  • type类型别名用来将一个函数签名、一组联合类型、一个工具类型等等抽离成一个完整独立的类型

但大部分场景下接口结构都可以被类型别名所取代,取决于个人喜好或者团队的一些规范吧。

object、Object 以及 { }

Object

js中,原型链的顶端是 Object 以及 Function,这也就意味着所有的原始类型与对象类型最终都指向 Object,在 TypeScript 中就表现为 Object 包含了所有的类型

// 对于 undefined、null、void 0 ,需要关闭 strictNullChecks
const tmp1: Object = undefined;
const tmp2: Object = null;
const tmp3: Object = void 0;

const tmp4: Object = 'aaa';
const tmp5: Object = 599;
const tmp6: Object = { name: 'aaa' };
const tmp7: Object = () => {};
const tmp8: Object = [];

和 Object 类似的还有 Boolean、Number、String、Symbol,这几个装箱类型(Boxed Types) 同样包含了一些超出预期的类型。
以 String 为例,它同样包括 undefined、null、void,以及代表的 拆箱类型(Unboxed Types) —> string,但并不包括其他装箱类型对应的拆箱类型,如 boolean 与 基本对象类型,我们看以下的代码:

const tmp9: String = undefined;
const tmp10: String = null;
const tmp11: String = void 0;
const tmp12: String = 'aaa';

// !!!  以下不成立,因为不是字符串类型的拆箱类型
const tmp13: String = 599; 
const tmp14: String = { name: 'aaa' }; 
const tmp15: String = () => {}; 
const tmp16: String = []; 

object
object 的引入就是为了解决对 Object 类型的错误使用,它代表所有非原始类型的类型,即数组、对象与函数类型这些:

const tmp17: object = undefined;
const tmp18: object = null;
const tmp19: object = void 0;

const tmp20: object = 'aaa';  // X 不成立,值为原始类型
const tmp21: object = 599; // X 不成立,值为原始类型

const tmp22: object = { name: 'aaa' };
const tmp23: object = () => {};
const tmp24: object = [];

{ }
可以认为使用{ }作为类型签名就是一个合法的,但内部无属性定义的空对象,
这类似于 Object(想想 new Object()),它意味着任何非 null / undefined 的值:

const tmp25: {} = undefined; // 仅在关闭 strictNullChecks 时成立,下同
const tmp26: {} = null;
const tmp27: {} = void 0; // void 0 等价于 undefined

const tmp28: {} = 'aaa';
const tmp29: {} = 599;
const tmp30: {} = { name: 'aaa' };
const tmp31: {} = () => {};
const tmp32: {} = [];

虽然能够将其作为变量的类型,但你实际上无法对这个变量进行任何赋值操作:

const tmp30: {} = { name: 'aaa' };

tmp30.age = 18; // X 类型“{}”上不存在属性“age”。

总结

  • 在任何时候都不要,不要,不要使用 Object 以及类似的装箱类型。

  • 当你不确定某个变量的具体类型,但能确定它不是原始类型,可以使用
    object。但更推荐进一步区分,也就是使用
    Record<string, unknown> 或 Record<string, any> 表示对象,
    unknown[] 或 any[] 表示数组,
    (…args: any[]) => any表示函数 这样。

  • 我们同样要避免使用{}。{}意味着任何非 null / undefined 的值,从这个层面上看,使用它和使用 any 一样恶劣。

字面量类型和枚举

字面量类型

看一个例子,我们开发中定义的请求 接口返回 结果可能如下

interface Res {
  code: 10000 | 10001 | 50000;
  status: "success" | "failure";
  data: any;
}

上面"success" 或者 “failure” 不是一个值吗?为什么它也可以作为类型?

在 TypeScript 中,这叫做字面量类型(Literal Types),它代表着比原始类型更精确的类型,同时也是原始类型的子类型

字面量类型主要包括:字符串字面量类型、数字字面量类型、布尔字面量类型和对象字面量类型,它们可以直接作为类型标注:

const str: "aaa" = "aaa";
const num: 599 = 599;
const bool: true = true
// 报错!Type '"aaa123"' is not assignable to type '"aaa"'
const str1: "aaa" = "aaa123";

单独使用字面量类型比较少见,因为单个字面量类型并没有什么实际意义。
它通常和联合类型(即这里的 |)一起使用,表达一组字面量类型:

interface Tmp {
  bool: true | false;
  num: 1 | 2 | 3;
  str: "aa" | "bb" | "cc"
}

补充:联合类型

联合类型你可以理解为,它代表了一组类型的可用集合,只要最终赋值的类型属于联合类型的成员之一,就可以认为符合这个联合类型。
联合类型对其成员并没有任何限制,除了上面这样对同一类型字面量的联合,我们还可以将各种类型混合到一起:

interface Tmp {
  mixed: true | string | 599 | {} | (() => {}) | (1 | 2)
}

联合类型的常用场景之一是通过多个对象类型的联合,来实现手动的互斥属性,即这一属性如果有字段1,那就没有字段2:

interface Tmp {
  user:
    | {
        vip: true;
        expires: string;
      }
    | {
        vip: false;
        promotion: string;
      };
}

declare var tmp: Tmp;

if (tmp.user.vip) {
  console.log(tmp.user.expires);
}
枚举
// js中定义一些常量
export const PageUrl = {
  Home_Page_Url: "url1",
  Setting_Page_Url: "url2",
  Share_Page_Url: "url3",
}

使用ts的枚举 enum

enum PageUrl {
  Home_Page_Url = "url1",
  Setting_Page_Url = "url2",
  Share_Page_Url = "url3",
}
// 使用
const home = PageUrl.Home_Page_Url;

如果你没有声明枚举的值,它会默认使用数字枚举,并且从 0 开始,以 1 递增:

enum Items {
  Foo,
  Bar,
  Baz
}
// Items.Foo , Items.Bar , Items.Baz的值依次是 0,1,2 

如果你只为某一个成员指定了枚举值,那么之前未赋值成员仍然会使用从 0 递增的方式,之后的成员则会开始从枚举值递增。

enum Items {
  Foo,  // 0 
  Bar = 599,
  Baz // 600
}

枚举和对象的重要差异在于,对象是单向映射的,我们只能从键映射到键值。
而枚举是双向映射的,即你可以从枚举成员映射到枚举值,也可以从枚举值映射到枚举成员:

enum Items {
  Foo,
  Bar,
  Baz
}

const fooValue = Items.Foo; // 0
const fooKey = Items[0]; // "Foo"

函数和class中的类型

函数
// 函数声明
function foo(name: string): number {
  return name.length;
}
// 函数表达式

const foo = function (name: string): number {
  return name.length
}
const foo: (name: string) => number = function (name) {
  return name.length
}

// 箭头函数
// 方式一
const foo = (name: string): number => {
  return name.length
}

// 方式二 代码的可读性会非常差,不推荐
const foo: (name: string) => number = (name) => {
  return name.length
}

要么直接在函数中进行参数和返回值的类型声明,要么使用类型别名将函数声明抽离出来,如下:

type FuncFoo = (name: string) => number

const foo: FuncFoo = (name) => {
  return name.length
}

如果只是为了描述这个函数的类型结构,我们甚至可以使用 interface 来进行函数声明:

interface FuncFooStruct {
  (name: string): number
}

void 类型

// 没有调用 return 语句
function foo(): void { }

// 调用了 return 语句,但没有返回值
function bar(): void {
  return;
}

可选参数与 rest 参数

使用 ? 描述一个可选参数
注意:可选参数必须位于必选参数之后

// 在函数逻辑中注入可选参数默认值
function foo1(name: string, age?: number): number {
  const inputAge = age || 18; // 或使用 age ?? 18
  return name.length + inputAge
}

// 直接为可选参数声明默认值
function foo2(name: string, age: number = 18): number {
  const inputAge = age;
  return name.length + inputAge
}

对于 rest 参数的类型标注也比较简单,由于其实际上是一个数组,这里我们也应当使用数组类型进行标注:

function foo(arg1: string, ...rest: any[]) { }

// 元组标注
function foo(arg1: string, ...rest: [number, boolean]) { }

foo("aaa", 18, true)

重载
在某些逻辑较复杂的情况下,函数可能有多组入参类型和返回值类型:

function func(foo: number, bar?: boolean): string | number {
  if (bar) {
    return String(foo);
  } else {
    return foo * 599;
  }
}

要想实现与入参关联的返回值类型,我们可以使用 TypeScript 提供的函数重载签名(Overload Signature),将以上的例子使用重载改写:

function func(foo: number, bar: true): string;
function func(foo: number, bar?: false): number;
function func(foo: number, bar?: boolean): string | number {
  if (bar) {
    return String(foo);
  } else {
    return foo * 599;
  }
}

const res1 = func(599); // number
const res2 = func(599, true); // string
const res3 = func(599, false); // number

异步函数

async function asyncFunc(): Promise<void> {}

对于异步函数(即标记为 async 的函数),其返回值必定为一个 Promise 类型,而 Promise 内部包含的类型则通过泛型的形式书写,即 Promise < T >

class

一个函数的主要结构即是参数、逻辑和返回值,对于逻辑的类型标注其实就是对普通代码的标注,所以我们只介绍了对参数以及返回值的类型标注。

而到了 Class 中其实也一样,它的主要结构只有构造函数、属性、方法和访问符(Accessor),我们也只需要关注这三个部分即可

构造函数

class Foo {
  prop: string;

  constructor(inputProp: string) {
    this.prop = inputProp;
  }

  print(addon: string): void {
    console.log(`${this.prop} and ${addon}`)
  }

  get propA(): string {
    return `${this.prop}+A`;
  }

  set propA(value: string) {
    this.prop = `${value}+A`
  }
}

!!setter 方法不允许进行返回值的类型标注,你可以理解为 setter 的返回值并不会被消费,它是一个只关注过程的函数。

修饰符
分别有 public / private / protected / readonly
readonly 属于操作性修饰符(就和 interface 中的 readonly 意义一致)
其它三个属于访问性修饰符

  • public:此类成员在类、类的实例、子类中都能被访问。
  • private:此类成员仅能在类的内部被访问。
  • protected:此类成员仅能在类与子类中被访问,你可以将类和类的实例当成两种概念,即一旦实例化完毕(出厂零件),那就和类(工厂)没关系了,即不允许再访问受保护的成员。
class Foo {
  private prop: string;

  constructor(inputProp: string) {
    this.prop = inputProp;
  }

  protected print(addon: string): void {
    console.log(`${this.prop} and ${addon}`)
  }

  public get propA(): string {
    return `${this.prop}+A`;
  }

  public set propA(value: string) {
    this.propA = `${value}+A`
  }
}

抽象类
抽象类是对类结构与方法的抽象,简单来说,一个抽象类描述了一个类中应当有哪些成员(属性、方法等),一个抽象方法描述了这一方法在实际实现中的结构。

abstract class AbsFoo {
  abstract absProp: string;
  abstract get absGetter(): string;
  abstract absMethod(name: string): string
}

抽象类中的成员也需要使用 abstract 关键字才能被视为抽象类成员,如这里的抽象方法。我们可以实现(implements)一个抽象类:

class Foo implements AbsFoo {
  absProp: string = "aaa"

  get absGetter() {
    return "aaa"
  }

  absMethod(name: string) {
    return name
  }
}

另外使用 interface 不仅可以声明函数结构,也可以声明类的结构:

interface FooStruct {
  absProp: string;
  get absGetter(): string;
  absMethod(input: string): string
}

class Foo implements FooStruct {
  absProp: string = "aaa"

  get absGetter() {
    return "aaa"
  }

  absMethod(name: string) {
    return name
  }
}

any、unknown、never

any 类型的主要意义,其实就是为了表示一个无拘无束的“任意类型”,它能兼容所有类型,也能够被所有类型兼容。

千万不要 anyscript !!!

unknown 类型的变量可以再次赋值为任意其它类型,但只能赋值给 any 与 unknown 类型的变量

let unknownVar: unknown = "aaa";

unknownVar = false;
unknownVar = "aaa";
unknownVar = {
  site: "bbb"
};

unknownVar = () => { }

const val1: string = unknownVar; // Error
const val2: number = unknownVar; // Error
const val3: () => {} = unknownVar; // Error
const val4: {} = unknownVar; // Error

const val5: any = unknownVar;
const val6: unknown = unknownVar;

any 放弃了所有的类型检查,而 unknown 并没有

let unknownVar: unknown;

unknownVar.foo(); // 报错:对象类型为 unknown
// 类型断言
let unknownVar: unknown;

(unknownVar as { foo: () => {} }).foo();

类型断言: 虽然这是一个未知的类型,但我跟你保证它在这里就是这个类型!

never 类型被称为 Bottom Type,是整个类型系统层级中最底层的类型

never 类型不携带任何的类型信息,因此会在联合类型中被直接移除
在这里插入图片描述

泛型

可以理解为一个接受参数的函数

类型别名type中的泛型

type Factory<T> = T | number | string;

type中的泛型大多是用来进行工具类型封装,比如

// 把一个对象中的所有属性变成 string类型
type Stringify<T> = {
  [K in keyof T]: string;
};

// 复制对象中的所有属性类型
type Clone<T> = {
  [K in keyof T]: T[K];
};

看一个例子

type Partial<T> = {
    [P in keyof T]?: T[P];
};
interface IFoo {
  prop1: string;
  prop2: number;
  prop3: boolean;
  prop4: () => void;
}

type PartialIFoo = Partial<IFoo>;


// 等价于
interface PartialIFoo {
  prop1?: string;
  prop2?: number;
  prop3?: boolean;
  prop4?: () => void;
}

泛型还可以作为条件类型中的判断条件

type IsEqual<T> = T extends true ? 1 : 2;

type A = IsEqual<true>; // 1
type B = IsEqual<false>; // 2
type C = IsEqual<'linbudu'>; // 2

可以设置默认值

  • List item
type Factory<T = boolean> = T | number | string;

// 调用时就可以不带任何参数了,默认会使用我们声明的默认值来填充
const foo: Factory = false;

可以使用 extends 关键字来约束传入的泛型参数必须符合要求
比如:

  • A extends B 意味着 A 是 B 的子类型
  • ‘aaa’ extends string,18 extends number 成立。
  • 联合类型子集均为联合类型的子类型,即 1、 1 | 2 是 1 | 2 | 3 | 4 的子类型
  • { name: string } 是 {} 的子类型,因为在 {} 的基础上增加了额外的类型

看下面例子,根据传入的请求码判断请求是否成功

type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002
  ? 'success'
  : 'failure';

type Res1 = ResStatus<10000>; // "success"
type Res2 = ResStatus<20000>; // "failure"

type Res3 = ResStatus<'10000'>; // 类型“string”不满足约束“number”。
type ResStatus<ResCode extends number = 10000> = ResCode extends 10000 | 10001 | 10002
  ? 'success'
  : 'failure';

type Res4 = ResStatus; // "success"

多泛型关联
不仅可以同时传入多个泛型参数,还可以让这几个泛型参数之间也存在联系,类似于传入多个参数

type Conditional<Type, Condition, TruthyResult, FalsyResult> =
  Type extends Condition ? TruthyResult : FalsyResult;

//  "passed!"
type Result1 = Conditional<'aaa', string, 'passed!', 'rejected!'>;

// "rejected!"
type Result2 = Conditional<'bbb', boolean, 'passed!', 'rejected!'>;

接口interface中的泛型

常见的一个例子应该还是响应类型结构的泛型处理:

interface IRes<TData = unknown> {
  code: number;
  error?: string;
  data: TData;
}

这个接口描述了一个通用的响应类型结构,预留出了实际响应数据的泛型坑位,然后在你的请求函数中就可以传入特定的响应类型了:

interface IUserProfileRes {
  name: string;
  homepage: string;
  avatar: string;
}

function fetchUserProfile(): Promise<IRes<IUserProfileRes>> {}

type StatusSucceed = boolean;
function handleOperation(): Promise<IRes<StatusSucceed>> {}

函数中的泛型

函数中的泛型是很常用的,主要是做:类型的自动提取

function handle<T>(input: T): T {}

我们为函数声明了一个泛型参数 T,并将参数的类型与返回值类型指向这个泛型参数。这样,在这个函数接收到参数时,T 会自动地被填充为这个参数的类型。

例子:

function handle<T>(input: T): T {}

const author = "aaa"; // 使用 const 声明,被推导为 "aaa"

let authorAge = 18; // 使用 let 声明,被推导为 number

handle(author); // 填充为字面量类型 "aaa"
handle(authorAge); // 填充为基础类型 number

在基于参数类型进行填充泛型时,其类型信息会被推断到尽可能精确的程度,如这里会推导到字面量类型而不是基础类型。
这是因为在直接传入一个值时,这个值是不会再被修改的,因此可以推导到最精确的程度。而如果你使用一个变量作为参数,那么只会使用这个变量标注的类型(在没有标注时,会使用推导出的类型)。

例子:

function swap<T, U>([start, end]: [T, U]): [U, T] {
  return [end, start];
}

const swapped1 = swap(["aaa", 18]); // const swapped1: [number, string]
const swapped2 = swap([null, 18]); // const swapped2: [number, null]
const swapped3 = swap([{ name: "aaa" }, {}]); // const swapped3: [{}, { name: string;}]

函数中的泛型同样存在约束与默认值

// 不再处理对象类型的情况了
function handle<T extends string | number>(input: T): T {}
// 只想处理数字元组的情况
function swap<T extends number, U extends number>([start, end]: [T, U]): [U, T] {
  return [end, start];
}

函数的泛型参数也会被内部的逻辑消费,如:

function handle<T>(payload: T): Promise<[T]> {
  return new Promise<[T]>((res, rej) => {
    res([payload]);
  });

对于箭头函数的泛型,其书写方式是这样的:

const handle = <T>(input: T): T => {}

在 tsx 文件中泛型的尖括号可能会造成报错,编译器无法识别这是一个组件还是一个泛型,此时你可以让它长得更像泛型一些:

const handle = <T extends any>(input: T): T => {}

需要注意的是,不要为了用泛型而用泛型,就像这样:
没有意义

function handle<T>(arg: T): void {
  console.log(arg);
};

Class 中的泛型

Class 中的泛型和函数中的泛型非常类似,只不过函数中泛型参数的消费方是参数和返回值类型
Class 中的泛型消费方则是属性、方法、乃至装饰器等。
同时 Class 内的方法还可以再声明自己独有的泛型参数。我们直接来看完整的示例:

class Queue<TElementType> {
  private _list: TElementType[];

  constructor(initial: TElementType[]) {
    this._list = initial;
  }

  // 入队一个队列泛型子类型的元素
  enqueue<TType extends TElementType>(ele: TType): TElementType[] {
    this._list.push(ele);
    return this._list;
  }

  // 入队一个任意类型元素(无需为队列泛型子类型)
  enqueueWithUnknownType<TType>(element: TType): (TElementType | TType)[] {
    return [...this._list, element];
  }

  // 出队
  dequeue(): TElementType[] {
    this._list.shift();
    return this._list;
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/293167.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安全cdn有哪些优势

1. 免备案&#xff1a;在中国大陆地区&#xff0c;进行网站建设需要先进行备案手续&#xff0c;而安全cdn可以避免这一繁琐的步骤&#xff0c;节省时间和精力。 2. 精品线路&#xff1a;安全cdn使用的是覆盖范围更广、速度更快的香港CN2 GIA优化线路。 3. 高速稳定&#xff1a…

Java多线程技术10——线程池ThreadPoolExecutor之Executor接口

1 概述 在开发服务器软件项目时&#xff0c;经常需要处理执行时间很短并且数据巨大的请求&#xff0c;如果为每一个请求创建一个新的线程&#xff0c;则会导致性能上的瓶颈。因为JVM需要频繁地处理线程对象的创建和销毁&#xff0c;如果请求的执行时间很短&#xff0c;则有可能…

【量化金融】《证券投资学》吴晓求(第四版)(更新中)

这里写目录标题 第一篇 基本知识第1章 证券投资工具名词解释简答题 第2章 证券市场名词解释简答题 第二篇 基本分析第三篇 技术分析第四篇 组合管理第五篇 量化分析与交易策略 第一篇 基本知识 第1章 证券投资工具 名词解释 风险&#xff08;risk&#xff09; 未来结果的不…

Hive - Select 使用 in 限制范围

目录 一.引言 二.Select Uid Info 1.少量 Uid 2.大量 Uid ◆ 建表 ◆ 本地 Load ◆ HDFS Load ◆ Select In 三.总结 一.引言 工业场景下 Hive 表通常使用 uid 作为用户维度构建和更新 Hive 表&#xff0c;当我们需要查询指定批次用户信息时&#xff0c;可以使用 in …

Spark内核解析-Spark shuffle6(六)

1、Spark Shuffle过程 1.1MapReduce的Shuffle过程介绍 Shuffle的本义是洗牌、混洗&#xff0c;把一组有一定规则的数据尽量转换成一组无规则的数据&#xff0c;越随机越好。MapReduce中的Shuffle更像是洗牌的逆过程&#xff0c;把一组无规则的数据尽量转换成一组具有一定规则…

极速 JavaScript 打包器:esbuild

文章目录 引言什么是esbuild&#xff1f;esbuild的特点esbuild如何实现如此出色的性能&#xff1f;esbuild缺点基本配置入口文件输出文件模块格式targetplatformexternalbanner和footer 高级配置插件系统自定义插件压缩代码调试代码 结论&#x1f636; 写在结尾 引言 esbuild是…

月薪15000在春晚分会场西安,够花吗?

千寻的结论&#xff1a; 如果有房无贷&#xff0c;另一半也有工作收入&#xff0c;父母身体健康且均有不错的退休金&#xff0c; 满足这些条件的话&#xff0c;在西安月入1.5W是相当不错。

苹果电脑菜单栏应用管理软件Bartender 4 mac软件特点

Bartender mac是一款可以帮助用户更好地管理和组织菜单栏图标的 macOS 软件。它允许用户隐藏和重新排列菜单栏图标&#xff0c;从而减少混乱和杂乱。 Bartender mac软件特点 菜单栏图标隐藏&#xff1a;Bartender 允许用户隐藏菜单栏图标&#xff0c;只在需要时显示。这样可以…

Flutter 图片和资源的高效使用指南

Flutter 应用程序包含代码和 assets&#xff08;也为资源&#xff09;。资源是被打包到应用程序安装包中&#xff0c;可以在运行时访问的一种文件。常见的资源类型包括静态数据&#xff08;例如 JSON 文件&#xff09;&#xff0c;配置文件&#xff0c;图标和图片&#xff08;J…

14:00面试,14:08就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到10月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40…

springboot第47集:【思维导图】面向对象,关键字,标识符,变量,数组的使用...

关键字&#xff1a;class,public,static,void等&#xff0c;特点是全部关键字都是小写字母。 image.png image.png 凡是自己起的名字可以叫标识符 image.png image.png image.png image.png 整数类型的使用 image.png image.png image.png 浮点类型 image.png image.png 字符类…

回归预测 | Matlab实现基于GA-Elman遗传算法优化神经网络多输入单输出回归预测

回归预测 | Matlab实现基于GA-Elman遗传算法优化神经网络多输入单输出回归预测 目录 回归预测 | Matlab实现基于GA-Elman遗传算法优化神经网络多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现基于GA-Elman遗传算法优化神经网络多输入单输…

Java重修第一天—学习数组

1. 认识数组 建议1.5倍速学习&#xff0c;并且关闭弹幕。 数组的定义&#xff1a;数组是一个容器&#xff0c;用来存储一批同种类型的数据。 下述图&#xff1a;是生成数字数组和字符串数组。 为什么有了变量还需要定义数组呢&#xff1f;为了解决在某些场景下&#xff0c;变…

安装extiverse/mercury时报错

问题描述 作者在安装 Flarum 的插件 extiverse/mercury 时报错&#xff0c;内容如下图所示 解决方案 ⚠警告&#xff1a;请备份所有数据再进行接下来的操作&#xff0c;此操作可能会导致网站不可用&#xff01; 报错原因&#xff1a;主要问题是在安装过程中解决依赖关系。具…

Day24 回溯算法part01 理论基础 77.组合

回溯算法part01 理论基础 77.组合 理论基础(转载自卡码网) 什么是回溯法 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 在二叉树系列中&#xff0c;我们已经不止一次&#xff0c;提到了回溯&#xff0c;例如二叉树&#xff1a;以为使用了递归&#xff0c;其…

nVisual如何实现数据中心资产管理

背景 随着信息技术的迅速发展&#xff0c;数据中心已经成为了企业信息化建设的重要基础设施之一。数据中心不仅承载着大量的企业数据和业务应用&#xff0c;而且也需要大量的资产投入来支持其运营和发展。 因此&#xff0c;数据中心资产管理的重要性也日益凸显&#xff0c;数…

SparkStreaming基础解析(四)

1、 Spark Streaming概述 1.1 Spark Streaming是什么 Spark Streaming用于流式数据的处理。Spark Streaming支持的数据输入源很多&#xff0c;例如&#xff1a;Kafka、Flume、Twitter、ZeroMQ和简单的TCP套接字等等。数据输入后可以用Spark的高度抽象原语如&#xff1a;map、…

OpenCV-15位运算

OpenCV中的逻辑运算就是对应位置的元素进行与、或、非和异或。 Opencv与Python不同的是&#xff1a;OpenCV中0的非反过来是255&#xff0c;255反过来是0。 但是Python中255非为-256。 一、非运算 使用API---cv.bitwise_not(str) 示例代码如下&#xff1a; import cv2 imp…

Jenkins集成部署java项目

文章目录 Jenkins简介安装 Jenkins简介 Jenkins能实时监控集成中存在的错误&#xff0c;提供详细的日志文件和提醒功能&#xff0c;还能用图表的形式形象的展示项目构建的趋势和稳定性。 官网 安装 在官网下载windows版本的Jenkins 但是我点击这里浏览器没有反应&#xff0…

系列十一、(一)Sentinel简介

一、Sentinel简介 1.1、官网 【英文文档】 https://github.com/alibaba/Sentinel/wiki【中文文档】 https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 1.2、概述 1.3、功能