diff --git a/404.html b/404.html index 73a2463b90..351d904cf4 100644 --- a/404.html +++ b/404.html @@ -33,14 +33,14 @@ - +
- + \ No newline at end of file diff --git a/assets/cn_index.md.CC940uHT.js b/assets/cn_index.md.BLhfTKfk.js similarity index 93% rename from assets/cn_index.md.CC940uHT.js rename to assets/cn_index.md.BLhfTKfk.js index 7315f4785e..c584ff024c 100644 --- a/assets/cn_index.md.CC940uHT.js +++ b/assets/cn_index.md.BLhfTKfk.js @@ -1 +1 @@ -import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","text":"风起于青萍之末","tagline":"A ship in harbor is safe, but that is not what ships are built for.","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"alt","text":"更多详情","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"访问我的 GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"记录","details":"每段旅程都有终点"},{"icon":"🛠️","title":"解决","details":"无法衡量,无法改进"},{"icon":"🖖","title":"分享","details":"一个人可以走得很快,一群人可以走的很远"}]},"headers":[],"relativePath":"cn/index.md","filePath":"cn/index.md","lastUpdated":1728129125000}'),i={name:"cn/index.md"};function n(o,s,r,c,l,h){return e(),a("div")}const p=t(i,[["render",n]]);export{m as __pageData,p as default}; +import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","text":"风起于青萍之末","tagline":"A ship in harbor is safe, but that is not what ships are built for.","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"alt","text":"更多详情","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"访问我的 GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"记录","details":"每段旅程都有终点"},{"icon":"🛠️","title":"解决","details":"无法衡量,无法改进"},{"icon":"🖖","title":"分享","details":"一个人可以走得很快,一群人可以走的很远"}]},"headers":[],"relativePath":"cn/index.md","filePath":"cn/index.md","lastUpdated":1728129344000}'),i={name:"cn/index.md"};function n(o,s,r,c,l,h){return e(),a("div")}const p=t(i,[["render",n]]);export{m as __pageData,p as default}; diff --git a/assets/cn_index.md.CC940uHT.lean.js b/assets/cn_index.md.BLhfTKfk.lean.js similarity index 93% rename from assets/cn_index.md.CC940uHT.lean.js rename to assets/cn_index.md.BLhfTKfk.lean.js index 7315f4785e..c584ff024c 100644 --- a/assets/cn_index.md.CC940uHT.lean.js +++ b/assets/cn_index.md.BLhfTKfk.lean.js @@ -1 +1 @@ -import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","text":"风起于青萍之末","tagline":"A ship in harbor is safe, but that is not what ships are built for.","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"alt","text":"更多详情","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"访问我的 GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"记录","details":"每段旅程都有终点"},{"icon":"🛠️","title":"解决","details":"无法衡量,无法改进"},{"icon":"🖖","title":"分享","details":"一个人可以走得很快,一群人可以走的很远"}]},"headers":[],"relativePath":"cn/index.md","filePath":"cn/index.md","lastUpdated":1728129125000}'),i={name:"cn/index.md"};function n(o,s,r,c,l,h){return e(),a("div")}const p=t(i,[["render",n]]);export{m as __pageData,p as default}; +import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","text":"风起于青萍之末","tagline":"A ship in harbor is safe, but that is not what ships are built for.","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"alt","text":"更多详情","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"访问我的 GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"记录","details":"每段旅程都有终点"},{"icon":"🛠️","title":"解决","details":"无法衡量,无法改进"},{"icon":"🖖","title":"分享","details":"一个人可以走得很快,一群人可以走的很远"}]},"headers":[],"relativePath":"cn/index.md","filePath":"cn/index.md","lastUpdated":1728129344000}'),i={name:"cn/index.md"};function n(o,s,r,c,l,h){return e(),a("div")}const p=t(i,[["render",n]]);export{m as __pageData,p as default}; diff --git a/assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.js b/assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.js similarity index 99% rename from assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.js rename to assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.js index d296c1fcf1..928b96f1b2 100644 --- a/assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.js +++ b/assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.js @@ -1,4 +1,4 @@ -import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/astParse/tokenizer.md","filePath":"cn/src/article/astParse/tokenizer.md","lastUpdated":1728129125000}'),u={name:"cn/src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

Abstract Syntax Tree

一.(abstract syntax tree)抽象语法树的作用

源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

二。常见的 AST 节点

常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

我们分别来了解一下:

Literal

Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

下面这些字面量都有对应的 Literal 节点:

代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

Identifier

Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

js
const name = 'value';
+import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/astParse/tokenizer.md","filePath":"cn/src/article/astParse/tokenizer.md","lastUpdated":1728129344000}'),u={name:"cn/src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

Abstract Syntax Tree

一.(abstract syntax tree)抽象语法树的作用

源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

二。常见的 AST 节点

常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

我们分别来了解一下:

Literal

Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

下面这些字面量都有对应的 Literal 节点:

代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

Identifier

Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

js
const name = 'value';
 
 function say(name) {
   console.log(name);
diff --git a/assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.lean.js b/assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.lean.js
similarity index 99%
rename from assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.lean.js
rename to assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.lean.js
index d296c1fcf1..928b96f1b2 100644
--- a/assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.lean.js
+++ b/assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.lean.js
@@ -1,4 +1,4 @@
-import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/astParse/tokenizer.md","filePath":"cn/src/article/astParse/tokenizer.md","lastUpdated":1728129125000}'),u={name:"cn/src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

Abstract Syntax Tree

一.(abstract syntax tree)抽象语法树的作用

源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

二。常见的 AST 节点

常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

我们分别来了解一下:

Literal

Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

下面这些字面量都有对应的 Literal 节点:

代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

Identifier

Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

js
const name = 'value';
+import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/astParse/tokenizer.md","filePath":"cn/src/article/astParse/tokenizer.md","lastUpdated":1728129344000}'),u={name:"cn/src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

Abstract Syntax Tree

一.(abstract syntax tree)抽象语法树的作用

源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

二。常见的 AST 节点

常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

我们分别来了解一下:

Literal

Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

下面这些字面量都有对应的 Literal 节点:

代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

Identifier

Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

js
const name = 'value';
 
 function say(name) {
   console.log(name);
diff --git a/assets/cn_src_article_babel.md.Bb4oNZ50.js b/assets/cn_src_article_babel.md.igUT7dgP.js
similarity index 95%
rename from assets/cn_src_article_babel.md.Bb4oNZ50.js
rename to assets/cn_src_article_babel.md.igUT7dgP.js
index a3d556a40e..c70391f37f 100644
--- a/assets/cn_src_article_babel.md.Bb4oNZ50.js
+++ b/assets/cn_src_article_babel.md.igUT7dgP.js
@@ -1 +1 @@
-import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/babel.md","filePath":"cn/src/article/babel.md","lastUpdated":1728129125000}'),i={name:"cn/src/article/babel.md"};function s(b,e,o,c,p,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

Babel

babel 核心库主要是:

  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • @babel/code-frame 可以创建友好的报错信息
  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; +import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/babel.md","filePath":"cn/src/article/babel.md","lastUpdated":1728129344000}'),i={name:"cn/src/article/babel.md"};function s(b,e,o,c,p,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

Babel

babel 核心库主要是:

  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • @babel/code-frame 可以创建友好的报错信息
  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; diff --git a/assets/cn_src_article_babel.md.Bb4oNZ50.lean.js b/assets/cn_src_article_babel.md.igUT7dgP.lean.js similarity index 95% rename from assets/cn_src_article_babel.md.Bb4oNZ50.lean.js rename to assets/cn_src_article_babel.md.igUT7dgP.lean.js index a3d556a40e..c70391f37f 100644 --- a/assets/cn_src_article_babel.md.Bb4oNZ50.lean.js +++ b/assets/cn_src_article_babel.md.igUT7dgP.lean.js @@ -1 +1 @@ -import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/babel.md","filePath":"cn/src/article/babel.md","lastUpdated":1728129125000}'),i={name:"cn/src/article/babel.md"};function s(b,e,o,c,p,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

Babel

babel 核心库主要是:

  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • @babel/code-frame 可以创建友好的报错信息
  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; +import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/babel.md","filePath":"cn/src/article/babel.md","lastUpdated":1728129344000}'),i={name:"cn/src/article/babel.md"};function s(b,e,o,c,p,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

Babel

babel 核心库主要是:

  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • @babel/code-frame 可以创建友好的报错信息
  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; diff --git a/assets/cn_src_article_bundle.md.BgkzbBow.js b/assets/cn_src_article_bundle.md.DQFZ3YRD.js similarity index 94% rename from assets/cn_src_article_bundle.md.BgkzbBow.js rename to assets/cn_src_article_bundle.md.DQFZ3YRD.js index c3171f45fe..d1e9523d4b 100644 --- a/assets/cn_src_article_bundle.md.BgkzbBow.js +++ b/assets/cn_src_article_bundle.md.DQFZ3YRD.js @@ -1 +1 @@ -import{_ as a,o as n,c as t,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/bundle.md","filePath":"cn/src/article/bundle.md","lastUpdated":1728129125000}'),d={name:"cn/src/article/bundle.md"};function c(s,l,i,o,u,p){return n(),t("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",c]]);export{f as __pageData,m as default}; +import{_ as a,o as n,c as t,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/bundle.md","filePath":"cn/src/article/bundle.md","lastUpdated":1728129344000}'),d={name:"cn/src/article/bundle.md"};function c(s,l,i,o,u,p){return n(),t("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",c]]);export{f as __pageData,m as default}; diff --git a/assets/cn_src_article_bundle.md.BgkzbBow.lean.js b/assets/cn_src_article_bundle.md.DQFZ3YRD.lean.js similarity index 94% rename from assets/cn_src_article_bundle.md.BgkzbBow.lean.js rename to assets/cn_src_article_bundle.md.DQFZ3YRD.lean.js index c3171f45fe..d1e9523d4b 100644 --- a/assets/cn_src_article_bundle.md.BgkzbBow.lean.js +++ b/assets/cn_src_article_bundle.md.DQFZ3YRD.lean.js @@ -1 +1 @@ -import{_ as a,o as n,c as t,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/bundle.md","filePath":"cn/src/article/bundle.md","lastUpdated":1728129125000}'),d={name:"cn/src/article/bundle.md"};function c(s,l,i,o,u,p){return n(),t("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",c]]);export{f as __pageData,m as default}; +import{_ as a,o as n,c as t,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/bundle.md","filePath":"cn/src/article/bundle.md","lastUpdated":1728129344000}'),d={name:"cn/src/article/bundle.md"};function c(s,l,i,o,u,p){return n(),t("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",c]]);export{f as __pageData,m as default}; diff --git a/assets/cn_src_article_designMode.md.BzjcnDMg.js b/assets/cn_src_article_designMode.md.B2Mi_NTb.js similarity index 99% rename from assets/cn_src_article_designMode.md.BzjcnDMg.js rename to assets/cn_src_article_designMode.md.B2Mi_NTb.js index 67d0d52b2a..d4566cc4ae 100644 --- a/assets/cn_src_article_designMode.md.BzjcnDMg.js +++ b/assets/cn_src_article_designMode.md.B2Mi_NTb.js @@ -1,4 +1,4 @@ -import{_ as i,a,b as n,c as h,d as l,e as k,f as p,g as t,h as e,i as E,j as r,k as d,l as g,m as y,n as F,o as c,p as o,q as C,r as B,s as A,t as D,u,v as b,w as m,x as v,y as f,z as x,A as _,B as q}from"./chunks/访问者._0swtoJg.js";import{_ as P,o as w,c as j,a3 as M}from"./chunks/framework.eq-HTtE3.js";const W=JSON.parse('{"title":"23 种经典设计模式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/designMode.md","filePath":"cn/src/article/designMode.md","lastUpdated":1728129125000}'),S={name:"cn/src/article/designMode.md"};function T(L,s,V,I,N,O){return w(),j("div",{"data-pagefind-body":!0},s[0]||(s[0]=[M(`

23 种经典设计模式

设计模式 Design Pattern 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。。

在《设计模式:可复用面向对象软件的基础》一书中所介绍的 23 种经典设计模式,不过设计模式并不仅仅只有这 23 种,随着软件开发行业的发展,越来越多的新模式不断诞生并得以应用。有经验的开发者在学习设计模式可以和过往的经验互相印证,更容易理解这些设计模式。

设计模式一般包含模式名称、问题、目的、解决方案、效果等组成要素。问题描述了应该在何时使用模式,它包含了设计中存在的问题以及问题存在的原因。解决方案描述了一个设计模式的组成成分,以及这些组成成分之间的相互关系,各自的职责和协作方式,通常解决方案通过 UML 类图和核心代码来进行描述。效果描述了模式的优缺点以及在使用模式时应权衡的问题。

为什么要学习设计模式:

  • 设计模式来源众多专家的经验和智慧,它们是从许多优秀的软件系统中总结出的成功的、能够实现可维护性复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作

  • 设计模式提供了一套通用的设计词汇和一种通用的形式来方便开发人员之间沟通和交流,使得设计方案更加通俗易懂

  • 大部分设计模式都兼顾了系统的可重用性和可扩展性,这使得我们可以更好地重用一些已有的设计方案、功能模块甚至一个完整的软件系统,避免我们经常做一些重复的设计、编写一些重复的代码

  • 合理使用设计模式并对设计模式的使用情况进行文档化,将有助于别人更快地理解系统

  • 学习设计模式将有助于初学者更加深入地理解面向对象思想

储备知识

  • 抽象类:一般抽象类都是作为基类,比如说「电脑」就可以作为一个抽象类,根据抽象类派生出「台式电脑」和「笔记本电脑」2 种具体类。一般不对抽象类进行实例化。

  • 组合优于继承:不能滥用继承来拓展功能,配合组合会更灵活。同样拿「电脑」抽象类来举例,如果使用继承,区分不同类型的「电脑」我们可以派生出「台式电脑」和「笔记本电脑」,如果再增加一个维度,根据品牌又能继续细分出「联想台式电脑」、「联想笔记本电脑」、「苹果台式电脑」和「苹果笔记本电脑」等等,如果再增加一个维度继续细分下去,显然继承是无法胜任的。这个时候可以使用继承加组合方式,组合的对象也可以进行抽象化设计:

    ts
    // 品牌
    +import{_ as i,a,b as n,c as h,d as l,e as k,f as p,g as t,h as e,i as E,j as r,k as d,l as g,m as y,n as F,o as c,p as o,q as C,r as B,s as A,t as D,u,v as b,w as m,x as v,y as f,z as x,A as _,B as q}from"./chunks/访问者._0swtoJg.js";import{_ as P,o as w,c as j,a3 as M}from"./chunks/framework.eq-HTtE3.js";const W=JSON.parse('{"title":"23 种经典设计模式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/designMode.md","filePath":"cn/src/article/designMode.md","lastUpdated":1728129344000}'),S={name:"cn/src/article/designMode.md"};function T(L,s,V,I,N,O){return w(),j("div",{"data-pagefind-body":!0},s[0]||(s[0]=[M(`

    23 种经典设计模式

    设计模式 Design Pattern 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。。

    在《设计模式:可复用面向对象软件的基础》一书中所介绍的 23 种经典设计模式,不过设计模式并不仅仅只有这 23 种,随着软件开发行业的发展,越来越多的新模式不断诞生并得以应用。有经验的开发者在学习设计模式可以和过往的经验互相印证,更容易理解这些设计模式。

    设计模式一般包含模式名称、问题、目的、解决方案、效果等组成要素。问题描述了应该在何时使用模式,它包含了设计中存在的问题以及问题存在的原因。解决方案描述了一个设计模式的组成成分,以及这些组成成分之间的相互关系,各自的职责和协作方式,通常解决方案通过 UML 类图和核心代码来进行描述。效果描述了模式的优缺点以及在使用模式时应权衡的问题。

    为什么要学习设计模式:

    • 设计模式来源众多专家的经验和智慧,它们是从许多优秀的软件系统中总结出的成功的、能够实现可维护性复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作

    • 设计模式提供了一套通用的设计词汇和一种通用的形式来方便开发人员之间沟通和交流,使得设计方案更加通俗易懂

    • 大部分设计模式都兼顾了系统的可重用性和可扩展性,这使得我们可以更好地重用一些已有的设计方案、功能模块甚至一个完整的软件系统,避免我们经常做一些重复的设计、编写一些重复的代码

    • 合理使用设计模式并对设计模式的使用情况进行文档化,将有助于别人更快地理解系统

    • 学习设计模式将有助于初学者更加深入地理解面向对象思想

    储备知识

    • 抽象类:一般抽象类都是作为基类,比如说「电脑」就可以作为一个抽象类,根据抽象类派生出「台式电脑」和「笔记本电脑」2 种具体类。一般不对抽象类进行实例化。

    • 组合优于继承:不能滥用继承来拓展功能,配合组合会更灵活。同样拿「电脑」抽象类来举例,如果使用继承,区分不同类型的「电脑」我们可以派生出「台式电脑」和「笔记本电脑」,如果再增加一个维度,根据品牌又能继续细分出「联想台式电脑」、「联想笔记本电脑」、「苹果台式电脑」和「苹果笔记本电脑」等等,如果再增加一个维度继续细分下去,显然继承是无法胜任的。这个时候可以使用继承加组合方式,组合的对象也可以进行抽象化设计:

      ts
      // 品牌
       interface Brand {
         // ...
       }
      diff --git a/assets/cn_src_article_designMode.md.BzjcnDMg.lean.js b/assets/cn_src_article_designMode.md.B2Mi_NTb.lean.js
      similarity index 99%
      rename from assets/cn_src_article_designMode.md.BzjcnDMg.lean.js
      rename to assets/cn_src_article_designMode.md.B2Mi_NTb.lean.js
      index 67d0d52b2a..d4566cc4ae 100644
      --- a/assets/cn_src_article_designMode.md.BzjcnDMg.lean.js
      +++ b/assets/cn_src_article_designMode.md.B2Mi_NTb.lean.js
      @@ -1,4 +1,4 @@
      -import{_ as i,a,b as n,c as h,d as l,e as k,f as p,g as t,h as e,i as E,j as r,k as d,l as g,m as y,n as F,o as c,p as o,q as C,r as B,s as A,t as D,u,v as b,w as m,x as v,y as f,z as x,A as _,B as q}from"./chunks/访问者._0swtoJg.js";import{_ as P,o as w,c as j,a3 as M}from"./chunks/framework.eq-HTtE3.js";const W=JSON.parse('{"title":"23 种经典设计模式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/designMode.md","filePath":"cn/src/article/designMode.md","lastUpdated":1728129125000}'),S={name:"cn/src/article/designMode.md"};function T(L,s,V,I,N,O){return w(),j("div",{"data-pagefind-body":!0},s[0]||(s[0]=[M(`

      23 种经典设计模式

      设计模式 Design Pattern 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。。

      在《设计模式:可复用面向对象软件的基础》一书中所介绍的 23 种经典设计模式,不过设计模式并不仅仅只有这 23 种,随着软件开发行业的发展,越来越多的新模式不断诞生并得以应用。有经验的开发者在学习设计模式可以和过往的经验互相印证,更容易理解这些设计模式。

      设计模式一般包含模式名称、问题、目的、解决方案、效果等组成要素。问题描述了应该在何时使用模式,它包含了设计中存在的问题以及问题存在的原因。解决方案描述了一个设计模式的组成成分,以及这些组成成分之间的相互关系,各自的职责和协作方式,通常解决方案通过 UML 类图和核心代码来进行描述。效果描述了模式的优缺点以及在使用模式时应权衡的问题。

      为什么要学习设计模式:

      • 设计模式来源众多专家的经验和智慧,它们是从许多优秀的软件系统中总结出的成功的、能够实现可维护性复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作

      • 设计模式提供了一套通用的设计词汇和一种通用的形式来方便开发人员之间沟通和交流,使得设计方案更加通俗易懂

      • 大部分设计模式都兼顾了系统的可重用性和可扩展性,这使得我们可以更好地重用一些已有的设计方案、功能模块甚至一个完整的软件系统,避免我们经常做一些重复的设计、编写一些重复的代码

      • 合理使用设计模式并对设计模式的使用情况进行文档化,将有助于别人更快地理解系统

      • 学习设计模式将有助于初学者更加深入地理解面向对象思想

      储备知识

      • 抽象类:一般抽象类都是作为基类,比如说「电脑」就可以作为一个抽象类,根据抽象类派生出「台式电脑」和「笔记本电脑」2 种具体类。一般不对抽象类进行实例化。

      • 组合优于继承:不能滥用继承来拓展功能,配合组合会更灵活。同样拿「电脑」抽象类来举例,如果使用继承,区分不同类型的「电脑」我们可以派生出「台式电脑」和「笔记本电脑」,如果再增加一个维度,根据品牌又能继续细分出「联想台式电脑」、「联想笔记本电脑」、「苹果台式电脑」和「苹果笔记本电脑」等等,如果再增加一个维度继续细分下去,显然继承是无法胜任的。这个时候可以使用继承加组合方式,组合的对象也可以进行抽象化设计:

        ts
        // 品牌
        +import{_ as i,a,b as n,c as h,d as l,e as k,f as p,g as t,h as e,i as E,j as r,k as d,l as g,m as y,n as F,o as c,p as o,q as C,r as B,s as A,t as D,u,v as b,w as m,x as v,y as f,z as x,A as _,B as q}from"./chunks/访问者._0swtoJg.js";import{_ as P,o as w,c as j,a3 as M}from"./chunks/framework.eq-HTtE3.js";const W=JSON.parse('{"title":"23 种经典设计模式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/designMode.md","filePath":"cn/src/article/designMode.md","lastUpdated":1728129344000}'),S={name:"cn/src/article/designMode.md"};function T(L,s,V,I,N,O){return w(),j("div",{"data-pagefind-body":!0},s[0]||(s[0]=[M(`

        23 种经典设计模式

        设计模式 Design Pattern 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。。

        在《设计模式:可复用面向对象软件的基础》一书中所介绍的 23 种经典设计模式,不过设计模式并不仅仅只有这 23 种,随着软件开发行业的发展,越来越多的新模式不断诞生并得以应用。有经验的开发者在学习设计模式可以和过往的经验互相印证,更容易理解这些设计模式。

        设计模式一般包含模式名称、问题、目的、解决方案、效果等组成要素。问题描述了应该在何时使用模式,它包含了设计中存在的问题以及问题存在的原因。解决方案描述了一个设计模式的组成成分,以及这些组成成分之间的相互关系,各自的职责和协作方式,通常解决方案通过 UML 类图和核心代码来进行描述。效果描述了模式的优缺点以及在使用模式时应权衡的问题。

        为什么要学习设计模式:

        • 设计模式来源众多专家的经验和智慧,它们是从许多优秀的软件系统中总结出的成功的、能够实现可维护性复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作

        • 设计模式提供了一套通用的设计词汇和一种通用的形式来方便开发人员之间沟通和交流,使得设计方案更加通俗易懂

        • 大部分设计模式都兼顾了系统的可重用性和可扩展性,这使得我们可以更好地重用一些已有的设计方案、功能模块甚至一个完整的软件系统,避免我们经常做一些重复的设计、编写一些重复的代码

        • 合理使用设计模式并对设计模式的使用情况进行文档化,将有助于别人更快地理解系统

        • 学习设计模式将有助于初学者更加深入地理解面向对象思想

        储备知识

        • 抽象类:一般抽象类都是作为基类,比如说「电脑」就可以作为一个抽象类,根据抽象类派生出「台式电脑」和「笔记本电脑」2 种具体类。一般不对抽象类进行实例化。

        • 组合优于继承:不能滥用继承来拓展功能,配合组合会更灵活。同样拿「电脑」抽象类来举例,如果使用继承,区分不同类型的「电脑」我们可以派生出「台式电脑」和「笔记本电脑」,如果再增加一个维度,根据品牌又能继续细分出「联想台式电脑」、「联想笔记本电脑」、「苹果台式电脑」和「苹果笔记本电脑」等等,如果再增加一个维度继续细分下去,显然继承是无法胜任的。这个时候可以使用继承加组合方式,组合的对象也可以进行抽象化设计:

          ts
          // 品牌
           interface Brand {
             // ...
           }
          diff --git a/assets/cn_src_article_docPreview.md.Bk6_K5Up.js b/assets/cn_src_article_docPreview.md.BpvHIdsV.js
          similarity index 99%
          rename from assets/cn_src_article_docPreview.md.Bk6_K5Up.js
          rename to assets/cn_src_article_docPreview.md.BpvHIdsV.js
          index e0e6a07aff..d7ad712171 100644
          --- a/assets/cn_src_article_docPreview.md.Bk6_K5Up.js
          +++ b/assets/cn_src_article_docPreview.md.BpvHIdsV.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/ms_ppt.C2zYd_IC.webp",p="/ran/assets/ms_excel.CJXFi2bf.webp",l="/ran/assets/ms_word.3k-0mjp0.webp",k="/ran/assets/ms_file_not.DgOrkG3n.webp",e="/ran/assets/ms_answer.Cgv6ylFF.webp",r="/ran/assets/ms_answer_2.D-D9H1v0.webp",E="/ran/assets/google_doc_view.BknjnOKw.webp",d="/ran/assets/ali_doc.CYo30EHy.webp",g="/ran/assets/xdoc.CeowcMPq.webp",o="/ran/assets/ow365.CXWyYekS.webp",y="/ran/assets/wps_office.DiET-U8x.webp",c="/ran/assets/wps_office_price.ler9htjh.webp",F="/ran/assets/xml.NyzOyhr4.webp",C="/ran/assets/firefox_pdf.BWQR-ogn.webp",f="/ran/assets/mdn_pdf.DbNmeC8H.webp",u="/ran/assets/kkfile_des.CENUMtpY.webp",A="/ran/assets/kkfile.X1tAqvNa.webp",m="/ran/assets/kkfile_output.DD8iZdbo.webp",B="/ran/assets/kkfile_doc.CPfEUxoD.webp",O=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/docPreview.md","filePath":"cn/src/article/docPreview.md","lastUpdated":1728129125000}'),D={name:"cn/src/article/docPreview.md"};function b(x,s,v,w,_,P){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          最全的 docx,pptx,xlsx(excel),pdf 文件预览方案总结

          最近遇到了文件预览的需求,但一搜索发现,这还不是一个简单的功能。于是又去查询了很多资料,调研了一些方案,也踩了好多坑。最后总结方案如下

          1. 花钱解决 (使用市面上现有的文件预览服务)
            1. 微软
            2. google
            3. 阿里云 IMM
            4. XDOC
            5. Office Web 365
            6. wps 开放平台
          2. 前端方案
            1. pptx 的预览方案
            2. pdf 的预览方案
            3. docx 的预览方案
            4. xlsx(excel) 的预览方案
            5. 前端预览方案总结
          3. 服务端方案
            1. openOffice
            2. kkFileView
            3. onlyOffice

          如果有其他人也遇到了同样的问题,有了这篇文章,希望能更方便的解决。

          基本涵盖了所有解决方案。因此,标题写上 最全 的文件预览方案调研总结,应该不为过吧。

          一:市面上现有的文件预览服务

          1.微软

          docx,pptx,xlsx可以说是office三件套,那自然得看一下 微软官方 提供的文件预览服务。使用方法特别简单,只需要将文件链接,拼接到参数后面即可。

          记得encodeURL

          js
          https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}

          (1).PPTX 预览效果:

          image.png

          • 优点:还原度很高,功能很丰富,可以选择翻页,甚至支持点击播放动画。
          • 缺点:不知道是不是墙的原因,加载稍慢。

          (2).Excel 预览效果:

          image.png

          (3).Doxc 预览效果

          image.png

          (4).PDF 预览效果

          这个我测试没有成功,返回了一个错误,其他人可以试试。

          image.png

          (5).总的来说

          对于docx,pptx,xlsx都有较好的支持,pdf不行。

          还有一个坑点是:这个服务是否稳定,有什么限制,是否收费,都查不到一个定论。在office官方网站上甚至找不到介绍这个东西的地方。

          目前只能找到一个Q&A:https://answers.microsoft.com/en-us/msoffice/forum/all/what-is-the-status-of-viewofficeappslivecom/830fd75c-9b47-43f9-89c9-4303703fd7f6

          微软官方人员回答表示:

          image.png

          翻译翻译,就是:几乎永久使用,没有收费计划,不会存储预览的文件数据,限制文件10MB,建议用于 查看互联网上公开的文件

          但经过某些用户测试发现:

          image.png

          使用了微软的文件预览服务,然后删除了文件地址,仍然可访问,但过一段时间会失效。

          2.Google Drive 查看器

          接入简单,同 Office Web Viewer,只需要把 src 改为https://drive.google.com/viewer?url=${encodeURIComponent(url)}即可。

          限制25MB,支持以下格式:

          image.png

          测试效果,支持docx,pptx,xlsx,pdf预览,但pptx预览的效果不如微软,没有动画效果,样式有小部分会错乱。

          由于某些众所周知的原因,不可用

          3.阿里云 IMM

          官方文档如下:https://help.aliyun.com/document_detail/63273.html

          image.png

          付费使用

          4.XDOC 文档预览

          说了一些大厂的,在介绍一些其他的,需要自行分辨

          官网地址:https://view.xdocin.com/view-xdocin-com_6x5f4x.htm

          image.png

          5.Office Web 365

          需要注意的是,虽然名字很像office,但我们看网页的Copyright可以发现,其实是一个西安的公司,不是微软

          但毕竟也提供了文件预览的服务

          官网地址:https://www.officeweb365.com/

          image.png

          6.WPS 开放平台

          官方地址:https://solution.wps.cn/

          image.png

          付费使用,价格如下:

          image.png

          二:前端处理方案

          1.pptx 的预览方案

          先查一下有没有现成的轮子,目前pptx的开源预览方案能找到的只有这个:https://github.com/g21589/PPTX2HTML。但已经六七年没有更新,也没有维护,笔者使用的时候发现有很多兼容性问题。

          简单来说就是,没有。对于这种情况,我们可以自行解析,主要步骤如下:

          1. 查询pptx的国际标准
          2. 解析pptx文件
          3. 渲染成html或者canvas进行展示

          我们先去找一下pptx的国际标准,官方地址:officeopenxml

          先解释下什么是officeopenxml:

          Office OpenXML,也称为 OpenXML 或 OOXML,是一种基于 XML 的办公文档格式,包括文字处理文档、电子表格、演示文稿以及图表、图表、形状和其他图形材料。该规范由微软开发,并于 2006 年被 ECMA 国际采用为 ECMA-376。第二个版本于 2008 年 12 月发布,第三个版本于 2011 年 6 月发布。该规范已被 ISO 和 IEC 采用为 ISO/IEC 29500。

          虽然 Microsoft 继续支持较旧的二进制格式 (.doc、.xls 和.ppt),但 OOXML 现在是所有 Microsoft Office 文档 (.docx、.xlsx 和.pptx) 的默认格式。

          由此可见,Office OpenXML由微软开发,目前已经是国际标准。接下来我们看一下pptx里面有哪些内容,具体可以看pptx的官方标准:officeopenxml-pptx

          PresentationML 或.pptx 文件是一个zip 文件,其中包含许多“部分”(通常是 UTF-8 或 UTF-16 编码)或 XML 文件。该包还可能包含其他媒体文件,例如图像。该结构根据 OOXML 标准 ECMA-376 第 2 部分中概述的开放打包约定进行组织。

          image.png

          根据国际标准,我们知道,pptx文件本质就是一个zip文件,其中包含许多部分:

          部件的数量和类型将根据演示文稿中的内容而有所不同,但始终会有一个 [Content_Types].xml、一个或多个关系(.rels)部件和一个演示文稿部件(演示文稿.xml),它位于 ppt 文件夹中,用于 Microsoft Powerpoint 文件。通常,还将至少有一个幻灯片部件,以及一张母版幻灯片和一张版式幻灯片,从中形成幻灯片。

          那么js如何读取zip呢?

          找到一个工具:https://www.npmjs.com/package/jszip

          于是我们可以开始尝试解析pptx了。

          ts
          import JSZip from 'jszip';
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/ms_ppt.C2zYd_IC.webp",p="/ran/assets/ms_excel.CJXFi2bf.webp",l="/ran/assets/ms_word.3k-0mjp0.webp",k="/ran/assets/ms_file_not.DgOrkG3n.webp",e="/ran/assets/ms_answer.Cgv6ylFF.webp",r="/ran/assets/ms_answer_2.D-D9H1v0.webp",E="/ran/assets/google_doc_view.BknjnOKw.webp",d="/ran/assets/ali_doc.CYo30EHy.webp",g="/ran/assets/xdoc.CeowcMPq.webp",o="/ran/assets/ow365.CXWyYekS.webp",y="/ran/assets/wps_office.DiET-U8x.webp",c="/ran/assets/wps_office_price.ler9htjh.webp",F="/ran/assets/xml.NyzOyhr4.webp",C="/ran/assets/firefox_pdf.BWQR-ogn.webp",f="/ran/assets/mdn_pdf.DbNmeC8H.webp",u="/ran/assets/kkfile_des.CENUMtpY.webp",A="/ran/assets/kkfile.X1tAqvNa.webp",m="/ran/assets/kkfile_output.DD8iZdbo.webp",B="/ran/assets/kkfile_doc.CPfEUxoD.webp",O=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/docPreview.md","filePath":"cn/src/article/docPreview.md","lastUpdated":1728129344000}'),D={name:"cn/src/article/docPreview.md"};function b(x,s,v,w,_,P){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          最全的 docx,pptx,xlsx(excel),pdf 文件预览方案总结

          最近遇到了文件预览的需求,但一搜索发现,这还不是一个简单的功能。于是又去查询了很多资料,调研了一些方案,也踩了好多坑。最后总结方案如下

          1. 花钱解决 (使用市面上现有的文件预览服务)
            1. 微软
            2. google
            3. 阿里云 IMM
            4. XDOC
            5. Office Web 365
            6. wps 开放平台
          2. 前端方案
            1. pptx 的预览方案
            2. pdf 的预览方案
            3. docx 的预览方案
            4. xlsx(excel) 的预览方案
            5. 前端预览方案总结
          3. 服务端方案
            1. openOffice
            2. kkFileView
            3. onlyOffice

          如果有其他人也遇到了同样的问题,有了这篇文章,希望能更方便的解决。

          基本涵盖了所有解决方案。因此,标题写上 最全 的文件预览方案调研总结,应该不为过吧。

          一:市面上现有的文件预览服务

          1.微软

          docx,pptx,xlsx可以说是office三件套,那自然得看一下 微软官方 提供的文件预览服务。使用方法特别简单,只需要将文件链接,拼接到参数后面即可。

          记得encodeURL

          js
          https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}

          (1).PPTX 预览效果:

          image.png

          • 优点:还原度很高,功能很丰富,可以选择翻页,甚至支持点击播放动画。
          • 缺点:不知道是不是墙的原因,加载稍慢。

          (2).Excel 预览效果:

          image.png

          (3).Doxc 预览效果

          image.png

          (4).PDF 预览效果

          这个我测试没有成功,返回了一个错误,其他人可以试试。

          image.png

          (5).总的来说

          对于docx,pptx,xlsx都有较好的支持,pdf不行。

          还有一个坑点是:这个服务是否稳定,有什么限制,是否收费,都查不到一个定论。在office官方网站上甚至找不到介绍这个东西的地方。

          目前只能找到一个Q&A:https://answers.microsoft.com/en-us/msoffice/forum/all/what-is-the-status-of-viewofficeappslivecom/830fd75c-9b47-43f9-89c9-4303703fd7f6

          微软官方人员回答表示:

          image.png

          翻译翻译,就是:几乎永久使用,没有收费计划,不会存储预览的文件数据,限制文件10MB,建议用于 查看互联网上公开的文件

          但经过某些用户测试发现:

          image.png

          使用了微软的文件预览服务,然后删除了文件地址,仍然可访问,但过一段时间会失效。

          2.Google Drive 查看器

          接入简单,同 Office Web Viewer,只需要把 src 改为https://drive.google.com/viewer?url=${encodeURIComponent(url)}即可。

          限制25MB,支持以下格式:

          image.png

          测试效果,支持docx,pptx,xlsx,pdf预览,但pptx预览的效果不如微软,没有动画效果,样式有小部分会错乱。

          由于某些众所周知的原因,不可用

          3.阿里云 IMM

          官方文档如下:https://help.aliyun.com/document_detail/63273.html

          image.png

          付费使用

          4.XDOC 文档预览

          说了一些大厂的,在介绍一些其他的,需要自行分辨

          官网地址:https://view.xdocin.com/view-xdocin-com_6x5f4x.htm

          image.png

          5.Office Web 365

          需要注意的是,虽然名字很像office,但我们看网页的Copyright可以发现,其实是一个西安的公司,不是微软

          但毕竟也提供了文件预览的服务

          官网地址:https://www.officeweb365.com/

          image.png

          6.WPS 开放平台

          官方地址:https://solution.wps.cn/

          image.png

          付费使用,价格如下:

          image.png

          二:前端处理方案

          1.pptx 的预览方案

          先查一下有没有现成的轮子,目前pptx的开源预览方案能找到的只有这个:https://github.com/g21589/PPTX2HTML。但已经六七年没有更新,也没有维护,笔者使用的时候发现有很多兼容性问题。

          简单来说就是,没有。对于这种情况,我们可以自行解析,主要步骤如下:

          1. 查询pptx的国际标准
          2. 解析pptx文件
          3. 渲染成html或者canvas进行展示

          我们先去找一下pptx的国际标准,官方地址:officeopenxml

          先解释下什么是officeopenxml:

          Office OpenXML,也称为 OpenXML 或 OOXML,是一种基于 XML 的办公文档格式,包括文字处理文档、电子表格、演示文稿以及图表、图表、形状和其他图形材料。该规范由微软开发,并于 2006 年被 ECMA 国际采用为 ECMA-376。第二个版本于 2008 年 12 月发布,第三个版本于 2011 年 6 月发布。该规范已被 ISO 和 IEC 采用为 ISO/IEC 29500。

          虽然 Microsoft 继续支持较旧的二进制格式 (.doc、.xls 和.ppt),但 OOXML 现在是所有 Microsoft Office 文档 (.docx、.xlsx 和.pptx) 的默认格式。

          由此可见,Office OpenXML由微软开发,目前已经是国际标准。接下来我们看一下pptx里面有哪些内容,具体可以看pptx的官方标准:officeopenxml-pptx

          PresentationML 或.pptx 文件是一个zip 文件,其中包含许多“部分”(通常是 UTF-8 或 UTF-16 编码)或 XML 文件。该包还可能包含其他媒体文件,例如图像。该结构根据 OOXML 标准 ECMA-376 第 2 部分中概述的开放打包约定进行组织。

          image.png

          根据国际标准,我们知道,pptx文件本质就是一个zip文件,其中包含许多部分:

          部件的数量和类型将根据演示文稿中的内容而有所不同,但始终会有一个 [Content_Types].xml、一个或多个关系(.rels)部件和一个演示文稿部件(演示文稿.xml),它位于 ppt 文件夹中,用于 Microsoft Powerpoint 文件。通常,还将至少有一个幻灯片部件,以及一张母版幻灯片和一张版式幻灯片,从中形成幻灯片。

          那么js如何读取zip呢?

          找到一个工具:https://www.npmjs.com/package/jszip

          于是我们可以开始尝试解析pptx了。

          ts
          import JSZip from 'jszip';
           // 加载 pptx 数据
           const zip = await JSZip.loadAsync(pptxData);
          • 解析[Content_Types].xml

          每个pptx必然会有一个 [Content_Types].xml。此文件包含包中部件的所有内容类型的列表。每个部件及其类型都必须列在 [Content_Types].xml 中。通过它里面的内容,可以解析其他的文件数据

          ts
          const filesInfo = await getContentTypes(zip);
           
          diff --git a/assets/cn_src_article_docPreview.md.Bk6_K5Up.lean.js b/assets/cn_src_article_docPreview.md.BpvHIdsV.lean.js
          similarity index 99%
          rename from assets/cn_src_article_docPreview.md.Bk6_K5Up.lean.js
          rename to assets/cn_src_article_docPreview.md.BpvHIdsV.lean.js
          index e0e6a07aff..d7ad712171 100644
          --- a/assets/cn_src_article_docPreview.md.Bk6_K5Up.lean.js
          +++ b/assets/cn_src_article_docPreview.md.BpvHIdsV.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/ms_ppt.C2zYd_IC.webp",p="/ran/assets/ms_excel.CJXFi2bf.webp",l="/ran/assets/ms_word.3k-0mjp0.webp",k="/ran/assets/ms_file_not.DgOrkG3n.webp",e="/ran/assets/ms_answer.Cgv6ylFF.webp",r="/ran/assets/ms_answer_2.D-D9H1v0.webp",E="/ran/assets/google_doc_view.BknjnOKw.webp",d="/ran/assets/ali_doc.CYo30EHy.webp",g="/ran/assets/xdoc.CeowcMPq.webp",o="/ran/assets/ow365.CXWyYekS.webp",y="/ran/assets/wps_office.DiET-U8x.webp",c="/ran/assets/wps_office_price.ler9htjh.webp",F="/ran/assets/xml.NyzOyhr4.webp",C="/ran/assets/firefox_pdf.BWQR-ogn.webp",f="/ran/assets/mdn_pdf.DbNmeC8H.webp",u="/ran/assets/kkfile_des.CENUMtpY.webp",A="/ran/assets/kkfile.X1tAqvNa.webp",m="/ran/assets/kkfile_output.DD8iZdbo.webp",B="/ran/assets/kkfile_doc.CPfEUxoD.webp",O=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/docPreview.md","filePath":"cn/src/article/docPreview.md","lastUpdated":1728129125000}'),D={name:"cn/src/article/docPreview.md"};function b(x,s,v,w,_,P){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          最全的 docx,pptx,xlsx(excel),pdf 文件预览方案总结

          最近遇到了文件预览的需求,但一搜索发现,这还不是一个简单的功能。于是又去查询了很多资料,调研了一些方案,也踩了好多坑。最后总结方案如下

          1. 花钱解决 (使用市面上现有的文件预览服务)
            1. 微软
            2. google
            3. 阿里云 IMM
            4. XDOC
            5. Office Web 365
            6. wps 开放平台
          2. 前端方案
            1. pptx 的预览方案
            2. pdf 的预览方案
            3. docx 的预览方案
            4. xlsx(excel) 的预览方案
            5. 前端预览方案总结
          3. 服务端方案
            1. openOffice
            2. kkFileView
            3. onlyOffice

          如果有其他人也遇到了同样的问题,有了这篇文章,希望能更方便的解决。

          基本涵盖了所有解决方案。因此,标题写上 最全 的文件预览方案调研总结,应该不为过吧。

          一:市面上现有的文件预览服务

          1.微软

          docx,pptx,xlsx可以说是office三件套,那自然得看一下 微软官方 提供的文件预览服务。使用方法特别简单,只需要将文件链接,拼接到参数后面即可。

          记得encodeURL

          js
          https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}

          (1).PPTX 预览效果:

          image.png

          • 优点:还原度很高,功能很丰富,可以选择翻页,甚至支持点击播放动画。
          • 缺点:不知道是不是墙的原因,加载稍慢。

          (2).Excel 预览效果:

          image.png

          (3).Doxc 预览效果

          image.png

          (4).PDF 预览效果

          这个我测试没有成功,返回了一个错误,其他人可以试试。

          image.png

          (5).总的来说

          对于docx,pptx,xlsx都有较好的支持,pdf不行。

          还有一个坑点是:这个服务是否稳定,有什么限制,是否收费,都查不到一个定论。在office官方网站上甚至找不到介绍这个东西的地方。

          目前只能找到一个Q&A:https://answers.microsoft.com/en-us/msoffice/forum/all/what-is-the-status-of-viewofficeappslivecom/830fd75c-9b47-43f9-89c9-4303703fd7f6

          微软官方人员回答表示:

          image.png

          翻译翻译,就是:几乎永久使用,没有收费计划,不会存储预览的文件数据,限制文件10MB,建议用于 查看互联网上公开的文件

          但经过某些用户测试发现:

          image.png

          使用了微软的文件预览服务,然后删除了文件地址,仍然可访问,但过一段时间会失效。

          2.Google Drive 查看器

          接入简单,同 Office Web Viewer,只需要把 src 改为https://drive.google.com/viewer?url=${encodeURIComponent(url)}即可。

          限制25MB,支持以下格式:

          image.png

          测试效果,支持docx,pptx,xlsx,pdf预览,但pptx预览的效果不如微软,没有动画效果,样式有小部分会错乱。

          由于某些众所周知的原因,不可用

          3.阿里云 IMM

          官方文档如下:https://help.aliyun.com/document_detail/63273.html

          image.png

          付费使用

          4.XDOC 文档预览

          说了一些大厂的,在介绍一些其他的,需要自行分辨

          官网地址:https://view.xdocin.com/view-xdocin-com_6x5f4x.htm

          image.png

          5.Office Web 365

          需要注意的是,虽然名字很像office,但我们看网页的Copyright可以发现,其实是一个西安的公司,不是微软

          但毕竟也提供了文件预览的服务

          官网地址:https://www.officeweb365.com/

          image.png

          6.WPS 开放平台

          官方地址:https://solution.wps.cn/

          image.png

          付费使用,价格如下:

          image.png

          二:前端处理方案

          1.pptx 的预览方案

          先查一下有没有现成的轮子,目前pptx的开源预览方案能找到的只有这个:https://github.com/g21589/PPTX2HTML。但已经六七年没有更新,也没有维护,笔者使用的时候发现有很多兼容性问题。

          简单来说就是,没有。对于这种情况,我们可以自行解析,主要步骤如下:

          1. 查询pptx的国际标准
          2. 解析pptx文件
          3. 渲染成html或者canvas进行展示

          我们先去找一下pptx的国际标准,官方地址:officeopenxml

          先解释下什么是officeopenxml:

          Office OpenXML,也称为 OpenXML 或 OOXML,是一种基于 XML 的办公文档格式,包括文字处理文档、电子表格、演示文稿以及图表、图表、形状和其他图形材料。该规范由微软开发,并于 2006 年被 ECMA 国际采用为 ECMA-376。第二个版本于 2008 年 12 月发布,第三个版本于 2011 年 6 月发布。该规范已被 ISO 和 IEC 采用为 ISO/IEC 29500。

          虽然 Microsoft 继续支持较旧的二进制格式 (.doc、.xls 和.ppt),但 OOXML 现在是所有 Microsoft Office 文档 (.docx、.xlsx 和.pptx) 的默认格式。

          由此可见,Office OpenXML由微软开发,目前已经是国际标准。接下来我们看一下pptx里面有哪些内容,具体可以看pptx的官方标准:officeopenxml-pptx

          PresentationML 或.pptx 文件是一个zip 文件,其中包含许多“部分”(通常是 UTF-8 或 UTF-16 编码)或 XML 文件。该包还可能包含其他媒体文件,例如图像。该结构根据 OOXML 标准 ECMA-376 第 2 部分中概述的开放打包约定进行组织。

          image.png

          根据国际标准,我们知道,pptx文件本质就是一个zip文件,其中包含许多部分:

          部件的数量和类型将根据演示文稿中的内容而有所不同,但始终会有一个 [Content_Types].xml、一个或多个关系(.rels)部件和一个演示文稿部件(演示文稿.xml),它位于 ppt 文件夹中,用于 Microsoft Powerpoint 文件。通常,还将至少有一个幻灯片部件,以及一张母版幻灯片和一张版式幻灯片,从中形成幻灯片。

          那么js如何读取zip呢?

          找到一个工具:https://www.npmjs.com/package/jszip

          于是我们可以开始尝试解析pptx了。

          ts
          import JSZip from 'jszip';
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/ms_ppt.C2zYd_IC.webp",p="/ran/assets/ms_excel.CJXFi2bf.webp",l="/ran/assets/ms_word.3k-0mjp0.webp",k="/ran/assets/ms_file_not.DgOrkG3n.webp",e="/ran/assets/ms_answer.Cgv6ylFF.webp",r="/ran/assets/ms_answer_2.D-D9H1v0.webp",E="/ran/assets/google_doc_view.BknjnOKw.webp",d="/ran/assets/ali_doc.CYo30EHy.webp",g="/ran/assets/xdoc.CeowcMPq.webp",o="/ran/assets/ow365.CXWyYekS.webp",y="/ran/assets/wps_office.DiET-U8x.webp",c="/ran/assets/wps_office_price.ler9htjh.webp",F="/ran/assets/xml.NyzOyhr4.webp",C="/ran/assets/firefox_pdf.BWQR-ogn.webp",f="/ran/assets/mdn_pdf.DbNmeC8H.webp",u="/ran/assets/kkfile_des.CENUMtpY.webp",A="/ran/assets/kkfile.X1tAqvNa.webp",m="/ran/assets/kkfile_output.DD8iZdbo.webp",B="/ran/assets/kkfile_doc.CPfEUxoD.webp",O=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/docPreview.md","filePath":"cn/src/article/docPreview.md","lastUpdated":1728129344000}'),D={name:"cn/src/article/docPreview.md"};function b(x,s,v,w,_,P){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          最全的 docx,pptx,xlsx(excel),pdf 文件预览方案总结

          最近遇到了文件预览的需求,但一搜索发现,这还不是一个简单的功能。于是又去查询了很多资料,调研了一些方案,也踩了好多坑。最后总结方案如下

          1. 花钱解决 (使用市面上现有的文件预览服务)
            1. 微软
            2. google
            3. 阿里云 IMM
            4. XDOC
            5. Office Web 365
            6. wps 开放平台
          2. 前端方案
            1. pptx 的预览方案
            2. pdf 的预览方案
            3. docx 的预览方案
            4. xlsx(excel) 的预览方案
            5. 前端预览方案总结
          3. 服务端方案
            1. openOffice
            2. kkFileView
            3. onlyOffice

          如果有其他人也遇到了同样的问题,有了这篇文章,希望能更方便的解决。

          基本涵盖了所有解决方案。因此,标题写上 最全 的文件预览方案调研总结,应该不为过吧。

          一:市面上现有的文件预览服务

          1.微软

          docx,pptx,xlsx可以说是office三件套,那自然得看一下 微软官方 提供的文件预览服务。使用方法特别简单,只需要将文件链接,拼接到参数后面即可。

          记得encodeURL

          js
          https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}

          (1).PPTX 预览效果:

          image.png

          • 优点:还原度很高,功能很丰富,可以选择翻页,甚至支持点击播放动画。
          • 缺点:不知道是不是墙的原因,加载稍慢。

          (2).Excel 预览效果:

          image.png

          (3).Doxc 预览效果

          image.png

          (4).PDF 预览效果

          这个我测试没有成功,返回了一个错误,其他人可以试试。

          image.png

          (5).总的来说

          对于docx,pptx,xlsx都有较好的支持,pdf不行。

          还有一个坑点是:这个服务是否稳定,有什么限制,是否收费,都查不到一个定论。在office官方网站上甚至找不到介绍这个东西的地方。

          目前只能找到一个Q&A:https://answers.microsoft.com/en-us/msoffice/forum/all/what-is-the-status-of-viewofficeappslivecom/830fd75c-9b47-43f9-89c9-4303703fd7f6

          微软官方人员回答表示:

          image.png

          翻译翻译,就是:几乎永久使用,没有收费计划,不会存储预览的文件数据,限制文件10MB,建议用于 查看互联网上公开的文件

          但经过某些用户测试发现:

          image.png

          使用了微软的文件预览服务,然后删除了文件地址,仍然可访问,但过一段时间会失效。

          2.Google Drive 查看器

          接入简单,同 Office Web Viewer,只需要把 src 改为https://drive.google.com/viewer?url=${encodeURIComponent(url)}即可。

          限制25MB,支持以下格式:

          image.png

          测试效果,支持docx,pptx,xlsx,pdf预览,但pptx预览的效果不如微软,没有动画效果,样式有小部分会错乱。

          由于某些众所周知的原因,不可用

          3.阿里云 IMM

          官方文档如下:https://help.aliyun.com/document_detail/63273.html

          image.png

          付费使用

          4.XDOC 文档预览

          说了一些大厂的,在介绍一些其他的,需要自行分辨

          官网地址:https://view.xdocin.com/view-xdocin-com_6x5f4x.htm

          image.png

          5.Office Web 365

          需要注意的是,虽然名字很像office,但我们看网页的Copyright可以发现,其实是一个西安的公司,不是微软

          但毕竟也提供了文件预览的服务

          官网地址:https://www.officeweb365.com/

          image.png

          6.WPS 开放平台

          官方地址:https://solution.wps.cn/

          image.png

          付费使用,价格如下:

          image.png

          二:前端处理方案

          1.pptx 的预览方案

          先查一下有没有现成的轮子,目前pptx的开源预览方案能找到的只有这个:https://github.com/g21589/PPTX2HTML。但已经六七年没有更新,也没有维护,笔者使用的时候发现有很多兼容性问题。

          简单来说就是,没有。对于这种情况,我们可以自行解析,主要步骤如下:

          1. 查询pptx的国际标准
          2. 解析pptx文件
          3. 渲染成html或者canvas进行展示

          我们先去找一下pptx的国际标准,官方地址:officeopenxml

          先解释下什么是officeopenxml:

          Office OpenXML,也称为 OpenXML 或 OOXML,是一种基于 XML 的办公文档格式,包括文字处理文档、电子表格、演示文稿以及图表、图表、形状和其他图形材料。该规范由微软开发,并于 2006 年被 ECMA 国际采用为 ECMA-376。第二个版本于 2008 年 12 月发布,第三个版本于 2011 年 6 月发布。该规范已被 ISO 和 IEC 采用为 ISO/IEC 29500。

          虽然 Microsoft 继续支持较旧的二进制格式 (.doc、.xls 和.ppt),但 OOXML 现在是所有 Microsoft Office 文档 (.docx、.xlsx 和.pptx) 的默认格式。

          由此可见,Office OpenXML由微软开发,目前已经是国际标准。接下来我们看一下pptx里面有哪些内容,具体可以看pptx的官方标准:officeopenxml-pptx

          PresentationML 或.pptx 文件是一个zip 文件,其中包含许多“部分”(通常是 UTF-8 或 UTF-16 编码)或 XML 文件。该包还可能包含其他媒体文件,例如图像。该结构根据 OOXML 标准 ECMA-376 第 2 部分中概述的开放打包约定进行组织。

          image.png

          根据国际标准,我们知道,pptx文件本质就是一个zip文件,其中包含许多部分:

          部件的数量和类型将根据演示文稿中的内容而有所不同,但始终会有一个 [Content_Types].xml、一个或多个关系(.rels)部件和一个演示文稿部件(演示文稿.xml),它位于 ppt 文件夹中,用于 Microsoft Powerpoint 文件。通常,还将至少有一个幻灯片部件,以及一张母版幻灯片和一张版式幻灯片,从中形成幻灯片。

          那么js如何读取zip呢?

          找到一个工具:https://www.npmjs.com/package/jszip

          于是我们可以开始尝试解析pptx了。

          ts
          import JSZip from 'jszip';
           // 加载 pptx 数据
           const zip = await JSZip.loadAsync(pptxData);
          • 解析[Content_Types].xml

          每个pptx必然会有一个 [Content_Types].xml。此文件包含包中部件的所有内容类型的列表。每个部件及其类型都必须列在 [Content_Types].xml 中。通过它里面的内容,可以解析其他的文件数据

          ts
          const filesInfo = await getContentTypes(zip);
           
          diff --git a/assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.js b/assets/cn_src_article_functionalProgramming.md.B0WkJbH2.js
          similarity index 99%
          rename from assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.js
          rename to assets/cn_src_article_functionalProgramming.md.B0WkJbH2.js
          index cc1446657c..8317789313 100644
          --- a/assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.js
          +++ b/assets/cn_src_article_functionalProgramming.md.B0WkJbH2.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/functionalProgramming.md","filePath":"cn/src/article/functionalProgramming.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          函数式编程

          • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
          • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
          • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
            • 程序的本质:根据输入,通过某种运算,获得相应的输出
            • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
            • 相同的输入始终要得到相同的输出 (纯函数)
            • 函数式编程用来描述数据 (函数) 之间的映射关系
          js
          //非函数式编程,面向过程的编程方式
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/functionalProgramming.md","filePath":"cn/src/article/functionalProgramming.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          函数式编程

          • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
          • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
          • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
            • 程序的本质:根据输入,通过某种运算,获得相应的输出
            • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
            • 相同的输入始终要得到相同的输出 (纯函数)
            • 函数式编程用来描述数据 (函数) 之间的映射关系
          js
          //非函数式编程,面向过程的编程方式
           let num1 = 1;
           let num2 = 2;
           let sum = num1 + num2;
          diff --git a/assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.lean.js b/assets/cn_src_article_functionalProgramming.md.B0WkJbH2.lean.js
          similarity index 99%
          rename from assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.lean.js
          rename to assets/cn_src_article_functionalProgramming.md.B0WkJbH2.lean.js
          index cc1446657c..8317789313 100644
          --- a/assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.lean.js
          +++ b/assets/cn_src_article_functionalProgramming.md.B0WkJbH2.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/functionalProgramming.md","filePath":"cn/src/article/functionalProgramming.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          函数式编程

          • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
          • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
          • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
            • 程序的本质:根据输入,通过某种运算,获得相应的输出
            • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
            • 相同的输入始终要得到相同的输出 (纯函数)
            • 函数式编程用来描述数据 (函数) 之间的映射关系
          js
          //非函数式编程,面向过程的编程方式
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/functionalProgramming.md","filePath":"cn/src/article/functionalProgramming.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          函数式编程

          • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
          • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
          • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
            • 程序的本质:根据输入,通过某种运算,获得相应的输出
            • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
            • 相同的输入始终要得到相同的输出 (纯函数)
            • 函数式编程用来描述数据 (函数) 之间的映射关系
          js
          //非函数式编程,面向过程的编程方式
           let num1 = 1;
           let num2 = 2;
           let sum = num1 + num2;
          diff --git a/assets/cn_src_article_imagemin.md.CEVub_ZQ.js b/assets/cn_src_article_imagemin.md.O-LRpAFt.js
          similarity index 91%
          rename from assets/cn_src_article_imagemin.md.CEVub_ZQ.js
          rename to assets/cn_src_article_imagemin.md.O-LRpAFt.js
          index 6c9f7718ab..1fa03e847f 100644
          --- a/assets/cn_src_article_imagemin.md.CEVub_ZQ.js
          +++ b/assets/cn_src_article_imagemin.md.O-LRpAFt.js
          @@ -1 +1 @@
          -import{_ as t,o as i,c as r,j as a,a as n}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/imagemin.md","filePath":"cn/src/article/imagemin.md","lastUpdated":1728129125000}'),m={name:"cn/src/article/imagemin.md"};function s(c,e,o,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[n("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(m,[["render",s]]);export{f as __pageData,_ as default};
          +import{_ as t,o as i,c as r,j as a,a as n}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/imagemin.md","filePath":"cn/src/article/imagemin.md","lastUpdated":1728129344000}'),m={name:"cn/src/article/imagemin.md"};function s(c,e,o,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[n("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(m,[["render",s]]);export{f as __pageData,_ as default};
          diff --git a/assets/cn_src_article_imagemin.md.CEVub_ZQ.lean.js b/assets/cn_src_article_imagemin.md.O-LRpAFt.lean.js
          similarity index 91%
          rename from assets/cn_src_article_imagemin.md.CEVub_ZQ.lean.js
          rename to assets/cn_src_article_imagemin.md.O-LRpAFt.lean.js
          index 6c9f7718ab..1fa03e847f 100644
          --- a/assets/cn_src_article_imagemin.md.CEVub_ZQ.lean.js
          +++ b/assets/cn_src_article_imagemin.md.O-LRpAFt.lean.js
          @@ -1 +1 @@
          -import{_ as t,o as i,c as r,j as a,a as n}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/imagemin.md","filePath":"cn/src/article/imagemin.md","lastUpdated":1728129125000}'),m={name:"cn/src/article/imagemin.md"};function s(c,e,o,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[n("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(m,[["render",s]]);export{f as __pageData,_ as default};
          +import{_ as t,o as i,c as r,j as a,a as n}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/imagemin.md","filePath":"cn/src/article/imagemin.md","lastUpdated":1728129344000}'),m={name:"cn/src/article/imagemin.md"};function s(c,e,o,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[n("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(m,[["render",s]]);export{f as __pageData,_ as default};
          diff --git a/assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.js b/assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.js
          similarity index 97%
          rename from assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.js
          rename to assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.js
          index 9954a3812a..68f68535db 100644
          --- a/assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.js
          +++ b/assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.js
          @@ -1 +1 @@
          -import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/javascript/domLoad.md","filePath":"cn/src/article/javascript/domLoad.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/javascript/domLoad.md"};function o(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          页面加载完成后事件

          window.onload

          DOMContentLoaded

          js
          document.addEventListener('DOMContentLoaded', fun);

          <body onload="fun()">

          readyState

          js
          document.readyState;\n\ndocument.onreadystatechange;

          一个文档的 readyState 可以是以下之一:

          • loading / 加载。document 仍在加载。
          • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
          • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
          ',9)]))}const k=e(n,[["render",o]]);export{u as __pageData,k as default}; +import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/javascript/domLoad.md","filePath":"cn/src/article/javascript/domLoad.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/javascript/domLoad.md"};function o(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          页面加载完成后事件

          window.onload

          DOMContentLoaded

          js
          document.addEventListener('DOMContentLoaded', fun);

          <body onload="fun()">

          readyState

          js
          document.readyState;\n\ndocument.onreadystatechange;

          一个文档的 readyState 可以是以下之一:

          • loading / 加载。document 仍在加载。
          • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
          • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
          ',9)]))}const k=e(n,[["render",o]]);export{u as __pageData,k as default}; diff --git a/assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.lean.js b/assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.lean.js similarity index 97% rename from assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.lean.js rename to assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.lean.js index 9954a3812a..68f68535db 100644 --- a/assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.lean.js +++ b/assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/javascript/domLoad.md","filePath":"cn/src/article/javascript/domLoad.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/javascript/domLoad.md"};function o(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          页面加载完成后事件

          window.onload

          DOMContentLoaded

          js
          document.addEventListener('DOMContentLoaded', fun);

          <body onload="fun()">

          readyState

          js
          document.readyState;\n\ndocument.onreadystatechange;

          一个文档的 readyState 可以是以下之一:

          • loading / 加载。document 仍在加载。
          • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
          • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
          ',9)]))}const k=e(n,[["render",o]]);export{u as __pageData,k as default}; +import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/javascript/domLoad.md","filePath":"cn/src/article/javascript/domLoad.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/javascript/domLoad.md"};function o(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          页面加载完成后事件

          window.onload

          DOMContentLoaded

          js
          document.addEventListener('DOMContentLoaded', fun);

          <body onload="fun()">

          readyState

          js
          document.readyState;\n\ndocument.onreadystatechange;

          一个文档的 readyState 可以是以下之一:

          • loading / 加载。document 仍在加载。
          • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
          • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
          ',9)]))}const k=e(n,[["render",o]]);export{u as __pageData,k as default}; diff --git a/assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.js b/assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.js similarity index 99% rename from assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.js rename to assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.js index 8f3936f2cf..96be1d06bd 100644 --- a/assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.js +++ b/assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.js @@ -1,4 +1,4 @@ -import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as l,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"冒泡排序(Bubble Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bubble/index.md","filePath":"cn/src/article/sort/bubble/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/article/sort/bubble/index.md"};function k(p,s,e,r,E,d){return l(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

          冒泡排序(Bubble Sort)

          冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

          算法描述

          • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
          • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
          • 针对所有的元素重复以上的步骤,除了最后一个;
          • 重复步骤 1~3,直到排序完成。

          动图演示

          冒泡排序

          代码演示

          ts
          const bubble = (list: number[]) => {
          +import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as l,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"冒泡排序(Bubble Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bubble/index.md","filePath":"cn/src/article/sort/bubble/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/article/sort/bubble/index.md"};function k(p,s,e,r,E,d){return l(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

          冒泡排序(Bubble Sort)

          冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

          算法描述

          • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
          • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
          • 针对所有的元素重复以上的步骤,除了最后一个;
          • 重复步骤 1~3,直到排序完成。

          动图演示

          冒泡排序

          代码演示

          ts
          const bubble = (list: number[]) => {
             const size = list.length;
             for (let i = 0; i < size; i++) {
               for (let j = 0; j < size; j++) {
          diff --git a/assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.lean.js b/assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.lean.js
          rename to assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.lean.js
          index 8f3936f2cf..96be1d06bd 100644
          --- a/assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.lean.js
          +++ b/assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as l,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"冒泡排序(Bubble Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bubble/index.md","filePath":"cn/src/article/sort/bubble/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/article/sort/bubble/index.md"};function k(p,s,e,r,E,d){return l(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

          冒泡排序(Bubble Sort)

          冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

          算法描述

          • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
          • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
          • 针对所有的元素重复以上的步骤,除了最后一个;
          • 重复步骤 1~3,直到排序完成。

          动图演示

          冒泡排序

          代码演示

          ts
          const bubble = (list: number[]) => {
          +import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as l,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"冒泡排序(Bubble Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bubble/index.md","filePath":"cn/src/article/sort/bubble/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/article/sort/bubble/index.md"};function k(p,s,e,r,E,d){return l(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

          冒泡排序(Bubble Sort)

          冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

          算法描述

          • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
          • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
          • 针对所有的元素重复以上的步骤,除了最后一个;
          • 重复步骤 1~3,直到排序完成。

          动图演示

          冒泡排序

          代码演示

          ts
          const bubble = (list: number[]) => {
             const size = list.length;
             for (let i = 0; i < size; i++) {
               for (let j = 0; j < size; j++) {
          diff --git a/assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.js b/assets/cn_src_article_sort_bucket_index.md.CInFho5i.js
          similarity index 99%
          rename from assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.js
          rename to assets/cn_src_article_sort_bucket_index.md.CInFho5i.js
          index a2e98c0fc0..63e49d1818 100644
          --- a/assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.js
          +++ b/assets/cn_src_article_sort_bucket_index.md.CInFho5i.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"桶排序 (Bucket Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bucket/index.md","filePath":"cn/src/article/sort/bucket/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/sort/bucket/index.md"};function l(t,s,p,E,e,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          桶排序 (Bucket Sort)

          高效与否的关键在于这个分桶函数。将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

          算法描述

          • 设置一个定量的数组当作空桶;
          • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
          • 对每个不是空的桶进行排序;
          • 从不是空的桶里把排好序的数据拼接起来。

          代码演示

          ts
          const count = (list: Array<number>, max: number = 100): Array<number> => {
          +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"桶排序 (Bucket Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bucket/index.md","filePath":"cn/src/article/sort/bucket/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/sort/bucket/index.md"};function l(t,s,p,E,e,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          桶排序 (Bucket Sort)

          高效与否的关键在于这个分桶函数。将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

          算法描述

          • 设置一个定量的数组当作空桶;
          • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
          • 对每个不是空的桶进行排序;
          • 从不是空的桶里把排好序的数据拼接起来。

          代码演示

          ts
          const count = (list: Array<number>, max: number = 100): Array<number> => {
             const countList = new Array(max + 1);
             for (let i = 0; i < list.length; i++) {
               if (!countList[list[i]]) {
          diff --git a/assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.lean.js b/assets/cn_src_article_sort_bucket_index.md.CInFho5i.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.lean.js
          rename to assets/cn_src_article_sort_bucket_index.md.CInFho5i.lean.js
          index a2e98c0fc0..63e49d1818 100644
          --- a/assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.lean.js
          +++ b/assets/cn_src_article_sort_bucket_index.md.CInFho5i.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"桶排序 (Bucket Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bucket/index.md","filePath":"cn/src/article/sort/bucket/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/sort/bucket/index.md"};function l(t,s,p,E,e,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          桶排序 (Bucket Sort)

          高效与否的关键在于这个分桶函数。将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

          算法描述

          • 设置一个定量的数组当作空桶;
          • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
          • 对每个不是空的桶进行排序;
          • 从不是空的桶里把排好序的数据拼接起来。

          代码演示

          ts
          const count = (list: Array<number>, max: number = 100): Array<number> => {
          +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"桶排序 (Bucket Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/bucket/index.md","filePath":"cn/src/article/sort/bucket/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/sort/bucket/index.md"};function l(t,s,p,E,e,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          桶排序 (Bucket Sort)

          高效与否的关键在于这个分桶函数。将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

          算法描述

          • 设置一个定量的数组当作空桶;
          • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
          • 对每个不是空的桶进行排序;
          • 从不是空的桶里把排好序的数据拼接起来。

          代码演示

          ts
          const count = (list: Array<number>, max: number = 100): Array<number> => {
             const countList = new Array(max + 1);
             for (let i = 0; i < list.length; i++) {
               if (!countList[list[i]]) {
          diff --git a/assets/cn_src_article_sort_count_index.md.BwHTgoYv.js b/assets/cn_src_article_sort_count_index.md.CLrg_eAi.js
          similarity index 99%
          rename from assets/cn_src_article_sort_count_index.md.BwHTgoYv.js
          rename to assets/cn_src_article_sort_count_index.md.CLrg_eAi.js
          index 29a8685604..8947639b4c 100644
          --- a/assets/cn_src_article_sort_count_index.md.BwHTgoYv.js
          +++ b/assets/cn_src_article_sort_count_index.md.CLrg_eAi.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"计数排序( Count Sort )","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/count/index.md","filePath":"cn/src/article/sort/count/index.md","lastUpdated":1728129125000}'),t={name:"cn/src/article/sort/count/index.md"};function l(p,s,e,E,r,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          计数排序( Count Sort )

          计数排序(counting sort)就是一种牺牲内存空间来换取低时间复杂度的排序算法,同时它也是一种不基于比较的算法。这里的不基于比较指的是数组元素之间不存在比较大小的排序算法,我们知道,用分治法来解决排序问题最快也只能使算法的时间复杂度接近 Θ(nlogn),即基于比较的时间复杂度存在下界 Ω(nlog⁡n),而不基于比较的排序算法可以突破这一下界。

          算法描述

          • 找出待排序的数组中最大和最小的元素;
          • 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
          • 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);
          • 反向填充目标数组:将每个元素 i 放在新数组的第 C(i)项,每放一个元素就将 C(i)减去 1。

          动图演示

          计数排序

          代码演示

          ts
          const getMax = (list: number[]) => {
          +import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"计数排序( Count Sort )","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/count/index.md","filePath":"cn/src/article/sort/count/index.md","lastUpdated":1728129344000}'),t={name:"cn/src/article/sort/count/index.md"};function l(p,s,e,E,r,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          计数排序( Count Sort )

          计数排序(counting sort)就是一种牺牲内存空间来换取低时间复杂度的排序算法,同时它也是一种不基于比较的算法。这里的不基于比较指的是数组元素之间不存在比较大小的排序算法,我们知道,用分治法来解决排序问题最快也只能使算法的时间复杂度接近 Θ(nlogn),即基于比较的时间复杂度存在下界 Ω(nlog⁡n),而不基于比较的排序算法可以突破这一下界。

          算法描述

          • 找出待排序的数组中最大和最小的元素;
          • 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
          • 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);
          • 反向填充目标数组:将每个元素 i 放在新数组的第 C(i)项,每放一个元素就将 C(i)减去 1。

          动图演示

          计数排序

          代码演示

          ts
          const getMax = (list: number[]) => {
             let max = list[0];
             for (let i = 1; i < list.length; i++) {
               if (max < list[i]) {
          diff --git a/assets/cn_src_article_sort_count_index.md.BwHTgoYv.lean.js b/assets/cn_src_article_sort_count_index.md.CLrg_eAi.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_count_index.md.BwHTgoYv.lean.js
          rename to assets/cn_src_article_sort_count_index.md.CLrg_eAi.lean.js
          index 29a8685604..8947639b4c 100644
          --- a/assets/cn_src_article_sort_count_index.md.BwHTgoYv.lean.js
          +++ b/assets/cn_src_article_sort_count_index.md.CLrg_eAi.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"计数排序( Count Sort )","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/count/index.md","filePath":"cn/src/article/sort/count/index.md","lastUpdated":1728129125000}'),t={name:"cn/src/article/sort/count/index.md"};function l(p,s,e,E,r,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          计数排序( Count Sort )

          计数排序(counting sort)就是一种牺牲内存空间来换取低时间复杂度的排序算法,同时它也是一种不基于比较的算法。这里的不基于比较指的是数组元素之间不存在比较大小的排序算法,我们知道,用分治法来解决排序问题最快也只能使算法的时间复杂度接近 Θ(nlogn),即基于比较的时间复杂度存在下界 Ω(nlog⁡n),而不基于比较的排序算法可以突破这一下界。

          算法描述

          • 找出待排序的数组中最大和最小的元素;
          • 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
          • 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);
          • 反向填充目标数组:将每个元素 i 放在新数组的第 C(i)项,每放一个元素就将 C(i)减去 1。

          动图演示

          计数排序

          代码演示

          ts
          const getMax = (list: number[]) => {
          +import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"计数排序( Count Sort )","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/count/index.md","filePath":"cn/src/article/sort/count/index.md","lastUpdated":1728129344000}'),t={name:"cn/src/article/sort/count/index.md"};function l(p,s,e,E,r,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          计数排序( Count Sort )

          计数排序(counting sort)就是一种牺牲内存空间来换取低时间复杂度的排序算法,同时它也是一种不基于比较的算法。这里的不基于比较指的是数组元素之间不存在比较大小的排序算法,我们知道,用分治法来解决排序问题最快也只能使算法的时间复杂度接近 Θ(nlogn),即基于比较的时间复杂度存在下界 Ω(nlog⁡n),而不基于比较的排序算法可以突破这一下界。

          算法描述

          • 找出待排序的数组中最大和最小的元素;
          • 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;
          • 对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);
          • 反向填充目标数组:将每个元素 i 放在新数组的第 C(i)项,每放一个元素就将 C(i)减去 1。

          动图演示

          计数排序

          代码演示

          ts
          const getMax = (list: number[]) => {
             let max = list[0];
             for (let i = 1; i < list.length; i++) {
               if (max < list[i]) {
          diff --git a/assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.js b/assets/cn_src_article_sort_heap_index.md.CjUwiufT.js
          similarity index 99%
          rename from assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.js
          rename to assets/cn_src_article_sort_heap_index.md.CjUwiufT.js
          index 18a0a22e19..92e7b20249 100644
          --- a/assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.js
          +++ b/assets/cn_src_article_sort_heap_index.md.CjUwiufT.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"堆排序(Heap Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/heap/index.md","filePath":"cn/src/article/sort/heap/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/heap/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          堆排序(Heap Sort)

          堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

          算法描述

          • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
          • 将堆顶元素 R[1]与最后一个元素 R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足 R[1,2…n-1]<=R[n];
          • 由于交换后新的堆顶 R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将 R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。
          • 升序用大根堆,降序用小根堆

          动图演示

          堆排序

          代码演示

          ts
          class Heap {
          +import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"堆排序(Heap Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/heap/index.md","filePath":"cn/src/article/sort/heap/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/heap/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          堆排序(Heap Sort)

          堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

          算法描述

          • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
          • 将堆顶元素 R[1]与最后一个元素 R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足 R[1,2…n-1]<=R[n];
          • 由于交换后新的堆顶 R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将 R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。
          • 升序用大根堆,降序用小根堆

          动图演示

          堆排序

          代码演示

          ts
          class Heap {
             value: Array<number>;
             size: number;
             constructor(arr: Array<number> = []) {
          diff --git a/assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.lean.js b/assets/cn_src_article_sort_heap_index.md.CjUwiufT.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.lean.js
          rename to assets/cn_src_article_sort_heap_index.md.CjUwiufT.lean.js
          index 18a0a22e19..92e7b20249 100644
          --- a/assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.lean.js
          +++ b/assets/cn_src_article_sort_heap_index.md.CjUwiufT.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"堆排序(Heap Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/heap/index.md","filePath":"cn/src/article/sort/heap/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/heap/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          堆排序(Heap Sort)

          堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

          算法描述

          • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
          • 将堆顶元素 R[1]与最后一个元素 R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足 R[1,2…n-1]<=R[n];
          • 由于交换后新的堆顶 R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将 R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。
          • 升序用大根堆,降序用小根堆

          动图演示

          堆排序

          代码演示

          ts
          class Heap {
          +import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"堆排序(Heap Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/heap/index.md","filePath":"cn/src/article/sort/heap/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/heap/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          堆排序(Heap Sort)

          堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

          算法描述

          • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
          • 将堆顶元素 R[1]与最后一个元素 R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足 R[1,2…n-1]<=R[n];
          • 由于交换后新的堆顶 R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将 R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。
          • 升序用大根堆,降序用小根堆

          动图演示

          堆排序

          代码演示

          ts
          class Heap {
             value: Array<number>;
             size: number;
             constructor(arr: Array<number> = []) {
          diff --git a/assets/cn_src_article_sort_index.md.V1uGWtUp.js b/assets/cn_src_article_sort_index.md.BhyMit8Y.js
          similarity index 96%
          rename from assets/cn_src_article_sort_index.md.V1uGWtUp.js
          rename to assets/cn_src_article_sort_index.md.BhyMit8Y.js
          index 636347e11b..5fe3e4f714 100644
          --- a/assets/cn_src_article_sort_index.md.V1uGWtUp.js
          +++ b/assets/cn_src_article_sort_index.md.BhyMit8Y.js
          @@ -1 +1 @@
          -import{_ as t,a as e}from"./chunks/complexity.CSkvDr7k.js";import{_ as r,o as i,c as o,a3 as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"十大经典排序","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/index.md","filePath":"cn/src/article/sort/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/sort/index.md"};function s(c,a,d,p,m,h){return i(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[l('

          十大经典排序

          十种常见排序算法可以分为两大类:

          • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
          • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

          排序分类

          算法复杂度

          算法复杂度

          相关概念

          • 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
          • 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
          • 时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。
          • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。
          ',8)]))}const f=r(n,[["render",s]]);export{b as __pageData,f as default}; +import{_ as t,a as e}from"./chunks/complexity.CSkvDr7k.js";import{_ as r,o as i,c as o,a3 as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"十大经典排序","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/index.md","filePath":"cn/src/article/sort/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/sort/index.md"};function s(c,a,d,p,m,h){return i(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[l('

          十大经典排序

          十种常见排序算法可以分为两大类:

          • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
          • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

          排序分类

          算法复杂度

          算法复杂度

          相关概念

          • 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
          • 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
          • 时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。
          • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。
          ',8)]))}const f=r(n,[["render",s]]);export{b as __pageData,f as default}; diff --git a/assets/cn_src_article_sort_index.md.V1uGWtUp.lean.js b/assets/cn_src_article_sort_index.md.BhyMit8Y.lean.js similarity index 96% rename from assets/cn_src_article_sort_index.md.V1uGWtUp.lean.js rename to assets/cn_src_article_sort_index.md.BhyMit8Y.lean.js index 636347e11b..5fe3e4f714 100644 --- a/assets/cn_src_article_sort_index.md.V1uGWtUp.lean.js +++ b/assets/cn_src_article_sort_index.md.BhyMit8Y.lean.js @@ -1 +1 @@ -import{_ as t,a as e}from"./chunks/complexity.CSkvDr7k.js";import{_ as r,o as i,c as o,a3 as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"十大经典排序","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/index.md","filePath":"cn/src/article/sort/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/sort/index.md"};function s(c,a,d,p,m,h){return i(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[l('

          十大经典排序

          十种常见排序算法可以分为两大类:

          • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
          • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

          排序分类

          算法复杂度

          算法复杂度

          相关概念

          • 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
          • 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
          • 时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。
          • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。
          ',8)]))}const f=r(n,[["render",s]]);export{b as __pageData,f as default}; +import{_ as t,a as e}from"./chunks/complexity.CSkvDr7k.js";import{_ as r,o as i,c as o,a3 as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"十大经典排序","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/index.md","filePath":"cn/src/article/sort/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/sort/index.md"};function s(c,a,d,p,m,h){return i(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[l('

          十大经典排序

          十种常见排序算法可以分为两大类:

          • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
          • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

          排序分类

          算法复杂度

          算法复杂度

          相关概念

          • 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
          • 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
          • 时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。
          • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。
          ',8)]))}const f=r(n,[["render",s]]);export{b as __pageData,f as default}; diff --git a/assets/cn_src_article_sort_insert_index.md.BPxYKrUM.js b/assets/cn_src_article_sort_insert_index.md.C4G7YeWl.js similarity index 99% rename from assets/cn_src_article_sort_insert_index.md.BPxYKrUM.js rename to assets/cn_src_article_sort_insert_index.md.C4G7YeWl.js index a84b803de9..adbe614a8a 100644 --- a/assets/cn_src_article_sort_insert_index.md.BPxYKrUM.js +++ b/assets/cn_src_article_sort_insert_index.md.C4G7YeWl.js @@ -1,4 +1,4 @@ -import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"插入排序(Insert Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/insert/index.md","filePath":"cn/src/article/sort/insert/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/insert/index.md"};function k(p,s,e,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          插入排序(Insert Sort)

          表现稳定的排序算法,因为无论什么数据进去都是 O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。优点是不占用额外的内存空间。工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

          算法描述

          • 从第一个元素开始,该元素可以认为已经被排序;
          • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
          • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
          • 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
          • 将新元素插入到该位置后;
          • 重复步骤 2~5。

          动图演示

          插入排序

          代码演示

          ts
          const insert = (list: number[]): number[] => {
          +import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"插入排序(Insert Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/insert/index.md","filePath":"cn/src/article/sort/insert/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/insert/index.md"};function k(p,s,e,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          插入排序(Insert Sort)

          表现稳定的排序算法,因为无论什么数据进去都是 O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。优点是不占用额外的内存空间。工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

          算法描述

          • 从第一个元素开始,该元素可以认为已经被排序;
          • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
          • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
          • 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
          • 将新元素插入到该位置后;
          • 重复步骤 2~5。

          动图演示

          插入排序

          代码演示

          ts
          const insert = (list: number[]): number[] => {
             const size = list.length;
             for (let i = 1; i < size; i++) {
               const current = list[i];
          diff --git a/assets/cn_src_article_sort_insert_index.md.BPxYKrUM.lean.js b/assets/cn_src_article_sort_insert_index.md.C4G7YeWl.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_insert_index.md.BPxYKrUM.lean.js
          rename to assets/cn_src_article_sort_insert_index.md.C4G7YeWl.lean.js
          index a84b803de9..adbe614a8a 100644
          --- a/assets/cn_src_article_sort_insert_index.md.BPxYKrUM.lean.js
          +++ b/assets/cn_src_article_sort_insert_index.md.C4G7YeWl.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"插入排序(Insert Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/insert/index.md","filePath":"cn/src/article/sort/insert/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/insert/index.md"};function k(p,s,e,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          插入排序(Insert Sort)

          表现稳定的排序算法,因为无论什么数据进去都是 O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。优点是不占用额外的内存空间。工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

          算法描述

          • 从第一个元素开始,该元素可以认为已经被排序;
          • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
          • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
          • 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
          • 将新元素插入到该位置后;
          • 重复步骤 2~5。

          动图演示

          插入排序

          代码演示

          ts
          const insert = (list: number[]): number[] => {
          +import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"插入排序(Insert Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/insert/index.md","filePath":"cn/src/article/sort/insert/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/insert/index.md"};function k(p,s,e,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          插入排序(Insert Sort)

          表现稳定的排序算法,因为无论什么数据进去都是 O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。优点是不占用额外的内存空间。工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

          算法描述

          • 从第一个元素开始,该元素可以认为已经被排序;
          • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
          • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
          • 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
          • 将新元素插入到该位置后;
          • 重复步骤 2~5。

          动图演示

          插入排序

          代码演示

          ts
          const insert = (list: number[]): number[] => {
             const size = list.length;
             for (let i = 1; i < size; i++) {
               const current = list[i];
          diff --git a/assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.js b/assets/cn_src_article_sort_merge_index.md.CrLtJpMk.js
          similarity index 99%
          rename from assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.js
          rename to assets/cn_src_article_sort_merge_index.md.CrLtJpMk.js
          index 8ece4ff7ef..4aff7a2eb2 100644
          --- a/assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.js
          +++ b/assets/cn_src_article_sort_merge_index.md.CrLtJpMk.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"归并排序(Merge Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/merge/index.md","filePath":"cn/src/article/sort/merge/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/merge/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          归并排序(Merge Sort)

          归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2-路归并。

          算法描述

          • 把长度为 n 的输入序列分成两个长度为 n/2 的子序列;
          • 对这两个子序列分别采用归并排序;
          • 将两个排序好的子序列合并成一个最终的排序序列。

          动图演示

          归并排序

          代码演示

          ts
          const combine = (left: Array<number>, right: Array<number>) => {
          +import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"归并排序(Merge Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/merge/index.md","filePath":"cn/src/article/sort/merge/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/merge/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          归并排序(Merge Sort)

          归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2-路归并。

          算法描述

          • 把长度为 n 的输入序列分成两个长度为 n/2 的子序列;
          • 对这两个子序列分别采用归并排序;
          • 将两个排序好的子序列合并成一个最终的排序序列。

          动图演示

          归并排序

          代码演示

          ts
          const combine = (left: Array<number>, right: Array<number>) => {
             const list: Array<number> = [];
             while (left.length > 0 && right.length > 0) {
               if (left[0] <= right[0]) {
          diff --git a/assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.lean.js b/assets/cn_src_article_sort_merge_index.md.CrLtJpMk.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.lean.js
          rename to assets/cn_src_article_sort_merge_index.md.CrLtJpMk.lean.js
          index 8ece4ff7ef..4aff7a2eb2 100644
          --- a/assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.lean.js
          +++ b/assets/cn_src_article_sort_merge_index.md.CrLtJpMk.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"归并排序(Merge Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/merge/index.md","filePath":"cn/src/article/sort/merge/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/merge/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          归并排序(Merge Sort)

          归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2-路归并。

          算法描述

          • 把长度为 n 的输入序列分成两个长度为 n/2 的子序列;
          • 对这两个子序列分别采用归并排序;
          • 将两个排序好的子序列合并成一个最终的排序序列。

          动图演示

          归并排序

          代码演示

          ts
          const combine = (left: Array<number>, right: Array<number>) => {
          +import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"归并排序(Merge Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/merge/index.md","filePath":"cn/src/article/sort/merge/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/merge/index.md"};function t(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          归并排序(Merge Sort)

          归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2-路归并。

          算法描述

          • 把长度为 n 的输入序列分成两个长度为 n/2 的子序列;
          • 对这两个子序列分别采用归并排序;
          • 将两个排序好的子序列合并成一个最终的排序序列。

          动图演示

          归并排序

          代码演示

          ts
          const combine = (left: Array<number>, right: Array<number>) => {
             const list: Array<number> = [];
             while (left.length > 0 && right.length > 0) {
               if (left[0] <= right[0]) {
          diff --git a/assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.js b/assets/cn_src_article_sort_quick_index.md.CmIbA4AB.js
          similarity index 99%
          rename from assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.js
          rename to assets/cn_src_article_sort_quick_index.md.CmIbA4AB.js
          index e97c3db4a7..1b0e5b105d 100644
          --- a/assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.js
          +++ b/assets/cn_src_article_sort_quick_index.md.CmIbA4AB.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"快速排序(Quick Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/quick/index.md","filePath":"cn/src/article/sort/quick/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/quick/index.md"};function t(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          快速排序(Quick Sort)

          快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

          算法描述

          快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

          • 从数列中挑出一个元素,称为 “基准”(pivot);
          • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
          • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

          动图演示

          快速排序

          代码演示

          ts
          /**
          +import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"快速排序(Quick Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/quick/index.md","filePath":"cn/src/article/sort/quick/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/quick/index.md"};function t(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          快速排序(Quick Sort)

          快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

          算法描述

          快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

          • 从数列中挑出一个元素,称为 “基准”(pivot);
          • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
          • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

          动图演示

          快速排序

          代码演示

          ts
          /**
            * @description: 设置基准值pivot
            * @param {Array} list
            * @param {number} left
          diff --git a/assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.lean.js b/assets/cn_src_article_sort_quick_index.md.CmIbA4AB.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.lean.js
          rename to assets/cn_src_article_sort_quick_index.md.CmIbA4AB.lean.js
          index e97c3db4a7..1b0e5b105d 100644
          --- a/assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.lean.js
          +++ b/assets/cn_src_article_sort_quick_index.md.CmIbA4AB.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"快速排序(Quick Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/quick/index.md","filePath":"cn/src/article/sort/quick/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/quick/index.md"};function t(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          快速排序(Quick Sort)

          快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

          算法描述

          快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

          • 从数列中挑出一个元素,称为 “基准”(pivot);
          • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
          • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

          动图演示

          快速排序

          代码演示

          ts
          /**
          +import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"快速排序(Quick Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/quick/index.md","filePath":"cn/src/article/sort/quick/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/quick/index.md"};function t(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          快速排序(Quick Sort)

          快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

          算法描述

          快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

          • 从数列中挑出一个元素,称为 “基准”(pivot);
          • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
          • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

          动图演示

          快速排序

          代码演示

          ts
          /**
            * @description: 设置基准值pivot
            * @param {Array} list
            * @param {number} left
          diff --git a/assets/cn_src_article_sort_radix_index.md.CmXyO2h4.js b/assets/cn_src_article_sort_radix_index.md.vBHH0tHh.js
          similarity index 99%
          rename from assets/cn_src_article_sort_radix_index.md.CmXyO2h4.js
          rename to assets/cn_src_article_sort_radix_index.md.vBHH0tHh.js
          index 34ebf05ed8..8859e08f5c 100644
          --- a/assets/cn_src_article_sort_radix_index.md.CmXyO2h4.js
          +++ b/assets/cn_src_article_sort_radix_index.md.vBHH0tHh.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"基数排序(Radix Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/radix/index.md","filePath":"cn/src/article/sort/radix/index.md","lastUpdated":1728129125000}'),t={name:"cn/src/article/sort/radix/index.md"};function l(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          基数排序(Radix Sort)

          基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。桶排序扩展,类似于指定桶排序按位数排序规则,同时能利用计数排序适用于小范围数的特点。

          算法描述

          • 取得数组中的最大数,并取得位数;
          • arr 为原始数组,从最低位开始取每个位组成 radix 数组;
          • 对 radix 进行计数排序(利用计数排序适用于小范围数的特点);

          动图演示

          基数排序

          代码演示

          ts
          const getMax = (list: Array<number>) => {
          +import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"基数排序(Radix Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/radix/index.md","filePath":"cn/src/article/sort/radix/index.md","lastUpdated":1728129344000}'),t={name:"cn/src/article/sort/radix/index.md"};function l(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          基数排序(Radix Sort)

          基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。桶排序扩展,类似于指定桶排序按位数排序规则,同时能利用计数排序适用于小范围数的特点。

          算法描述

          • 取得数组中的最大数,并取得位数;
          • arr 为原始数组,从最低位开始取每个位组成 radix 数组;
          • 对 radix 进行计数排序(利用计数排序适用于小范围数的特点);

          动图演示

          基数排序

          代码演示

          ts
          const getMax = (list: Array<number>) => {
             let max = list[0];
             for (let i = 0; i < list.length; i++) {
               if (max < list[i]) {
          diff --git a/assets/cn_src_article_sort_radix_index.md.CmXyO2h4.lean.js b/assets/cn_src_article_sort_radix_index.md.vBHH0tHh.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_radix_index.md.CmXyO2h4.lean.js
          rename to assets/cn_src_article_sort_radix_index.md.vBHH0tHh.lean.js
          index 34ebf05ed8..8859e08f5c 100644
          --- a/assets/cn_src_article_sort_radix_index.md.CmXyO2h4.lean.js
          +++ b/assets/cn_src_article_sort_radix_index.md.vBHH0tHh.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"基数排序(Radix Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/radix/index.md","filePath":"cn/src/article/sort/radix/index.md","lastUpdated":1728129125000}'),t={name:"cn/src/article/sort/radix/index.md"};function l(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          基数排序(Radix Sort)

          基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。桶排序扩展,类似于指定桶排序按位数排序规则,同时能利用计数排序适用于小范围数的特点。

          算法描述

          • 取得数组中的最大数,并取得位数;
          • arr 为原始数组,从最低位开始取每个位组成 radix 数组;
          • 对 radix 进行计数排序(利用计数排序适用于小范围数的特点);

          动图演示

          基数排序

          代码演示

          ts
          const getMax = (list: Array<number>) => {
          +import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"基数排序(Radix Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/radix/index.md","filePath":"cn/src/article/sort/radix/index.md","lastUpdated":1728129344000}'),t={name:"cn/src/article/sort/radix/index.md"};function l(p,s,e,E,r,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

          基数排序(Radix Sort)

          基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。桶排序扩展,类似于指定桶排序按位数排序规则,同时能利用计数排序适用于小范围数的特点。

          算法描述

          • 取得数组中的最大数,并取得位数;
          • arr 为原始数组,从最低位开始取每个位组成 radix 数组;
          • 对 radix 进行计数排序(利用计数排序适用于小范围数的特点);

          动图演示

          基数排序

          代码演示

          ts
          const getMax = (list: Array<number>) => {
             let max = list[0];
             for (let i = 0; i < list.length; i++) {
               if (max < list[i]) {
          diff --git a/assets/cn_src_article_sort_select_index.md.C6f3fqev.js b/assets/cn_src_article_sort_select_index.md.Cd2SKviA.js
          similarity index 99%
          rename from assets/cn_src_article_sort_select_index.md.C6f3fqev.js
          rename to assets/cn_src_article_sort_select_index.md.Cd2SKviA.js
          index 1d87ff33ea..ad19cd7953 100644
          --- a/assets/cn_src_article_sort_select_index.md.C6f3fqev.js
          +++ b/assets/cn_src_article_sort_select_index.md.Cd2SKviA.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"选择排序(Selection Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/select/index.md","filePath":"cn/src/article/sort/select/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/select/index.md"};function k(p,s,e,E,r,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          选择排序(Selection Sort)

          选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

          算法描述

          n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。具体算法描述如下:

          • 初始状态:无序区为 R[1..n],有序区为空;
          • 第 i 趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为 R[1..i-1]和 R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第 1 个记录 R 交换,使 R[1..i]和 R[i+1..n)分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区;
          • n-1 趟结束,数组有序化了。

          动图演示

          选择排序

          代码实现

          js
          const select = (list: number[]): number[] => {
          +import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"选择排序(Selection Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/select/index.md","filePath":"cn/src/article/sort/select/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/select/index.md"};function k(p,s,e,E,r,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          选择排序(Selection Sort)

          选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

          算法描述

          n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。具体算法描述如下:

          • 初始状态:无序区为 R[1..n],有序区为空;
          • 第 i 趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为 R[1..i-1]和 R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第 1 个记录 R 交换,使 R[1..i]和 R[i+1..n)分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区;
          • n-1 趟结束,数组有序化了。

          动图演示

          选择排序

          代码实现

          js
          const select = (list: number[]): number[] => {
             const size = list.length;
             for (let i = 0; i < size; i++) {
               let minIndex = i;
          diff --git a/assets/cn_src_article_sort_select_index.md.C6f3fqev.lean.js b/assets/cn_src_article_sort_select_index.md.Cd2SKviA.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_select_index.md.C6f3fqev.lean.js
          rename to assets/cn_src_article_sort_select_index.md.Cd2SKviA.lean.js
          index 1d87ff33ea..ad19cd7953 100644
          --- a/assets/cn_src_article_sort_select_index.md.C6f3fqev.lean.js
          +++ b/assets/cn_src_article_sort_select_index.md.Cd2SKviA.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"选择排序(Selection Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/select/index.md","filePath":"cn/src/article/sort/select/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/article/sort/select/index.md"};function k(p,s,e,E,r,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          选择排序(Selection Sort)

          选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

          算法描述

          n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。具体算法描述如下:

          • 初始状态:无序区为 R[1..n],有序区为空;
          • 第 i 趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为 R[1..i-1]和 R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第 1 个记录 R 交换,使 R[1..i]和 R[i+1..n)分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区;
          • n-1 趟结束,数组有序化了。

          动图演示

          选择排序

          代码实现

          js
          const select = (list: number[]): number[] => {
          +import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"选择排序(Selection Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/select/index.md","filePath":"cn/src/article/sort/select/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/article/sort/select/index.md"};function k(p,s,e,E,r,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          选择排序(Selection Sort)

          选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

          算法描述

          n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。具体算法描述如下:

          • 初始状态:无序区为 R[1..n],有序区为空;
          • 第 i 趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为 R[1..i-1]和 R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第 1 个记录 R 交换,使 R[1..i]和 R[i+1..n)分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区;
          • n-1 趟结束,数组有序化了。

          动图演示

          选择排序

          代码实现

          js
          const select = (list: number[]): number[] => {
             const size = list.length;
             for (let i = 0; i < size; i++) {
               let minIndex = i;
          diff --git a/assets/cn_src_article_sort_shell_index.md.B0DKjSD6.js b/assets/cn_src_article_sort_shell_index.md.CQc6o0U8.js
          similarity index 99%
          rename from assets/cn_src_article_sort_shell_index.md.B0DKjSD6.js
          rename to assets/cn_src_article_sort_shell_index.md.CQc6o0U8.js
          index f1b26edd36..f3b2f0378f 100644
          --- a/assets/cn_src_article_sort_shell_index.md.B0DKjSD6.js
          +++ b/assets/cn_src_article_sort_shell_index.md.CQc6o0U8.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as h,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"希尔排序(Shell Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/shell/index.md","filePath":"cn/src/article/sort/shell/index.md","lastUpdated":1728129125000}'),t={name:"cn/src/article/sort/shell/index.md"};function k(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l('

          希尔排序(Shell Sort)

          1959 年 Shell 发明,第一个突破 O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

          算法描述

          先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

          • 选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
          • 按增量序列个数 k,对序列进行 k 趟排序;
          • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

          动图演示

          希尔排序

          代码实现

          js
          /**
          +import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as h,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"希尔排序(Shell Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/shell/index.md","filePath":"cn/src/article/sort/shell/index.md","lastUpdated":1728129344000}'),t={name:"cn/src/article/sort/shell/index.md"};function k(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l('

          希尔排序(Shell Sort)

          1959 年 Shell 发明,第一个突破 O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

          算法描述

          先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

          • 选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
          • 按增量序列个数 k,对序列进行 k 趟排序;
          • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

          动图演示

          希尔排序

          代码实现

          js
          /**
            * @description: 希尔排序,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
            * @param {Array} list
            * @return {Array}
          diff --git a/assets/cn_src_article_sort_shell_index.md.B0DKjSD6.lean.js b/assets/cn_src_article_sort_shell_index.md.CQc6o0U8.lean.js
          similarity index 99%
          rename from assets/cn_src_article_sort_shell_index.md.B0DKjSD6.lean.js
          rename to assets/cn_src_article_sort_shell_index.md.CQc6o0U8.lean.js
          index f1b26edd36..f3b2f0378f 100644
          --- a/assets/cn_src_article_sort_shell_index.md.B0DKjSD6.lean.js
          +++ b/assets/cn_src_article_sort_shell_index.md.CQc6o0U8.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as h,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"希尔排序(Shell Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/shell/index.md","filePath":"cn/src/article/sort/shell/index.md","lastUpdated":1728129125000}'),t={name:"cn/src/article/sort/shell/index.md"};function k(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l('

          希尔排序(Shell Sort)

          1959 年 Shell 发明,第一个突破 O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

          算法描述

          先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

          • 选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
          • 按增量序列个数 k,对序列进行 k 趟排序;
          • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

          动图演示

          希尔排序

          代码实现

          js
          /**
          +import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as h,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"希尔排序(Shell Sort)","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/sort/shell/index.md","filePath":"cn/src/article/sort/shell/index.md","lastUpdated":1728129344000}'),t={name:"cn/src/article/sort/shell/index.md"};function k(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l('

          希尔排序(Shell Sort)

          1959 年 Shell 发明,第一个突破 O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

          算法描述

          先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

          • 选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;
          • 按增量序列个数 k,对序列进行 k 趟排序;
          • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

          动图演示

          希尔排序

          代码实现

          js
          /**
            * @description: 希尔排序,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
            * @param {Array} list
            * @return {Array}
          diff --git a/assets/cn_src_article_systemDesign.md.Oy3scZvv.js b/assets/cn_src_article_systemDesign.md.hF7LXas7.js
          similarity index 97%
          rename from assets/cn_src_article_systemDesign.md.Oy3scZvv.js
          rename to assets/cn_src_article_systemDesign.md.hF7LXas7.js
          index 4e8c441678..d3ff79e2e0 100644
          --- a/assets/cn_src_article_systemDesign.md.Oy3scZvv.js
          +++ b/assets/cn_src_article_systemDesign.md.hF7LXas7.js
          @@ -1 +1 @@
          -import{_ as e,o as t,c as r,a3 as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"如何处理一个系统设计","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/systemDesign.md","filePath":"cn/src/article/systemDesign.md","lastUpdated":1728129125000}'),o={name:"cn/src/article/systemDesign.md"};function i(n,a,l,p,c,d){return t(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

          如何处理一个系统设计

          系统设计是一个开放式的对话。他们期望你去主导这个对话。

          你可以使用下面的步骤来指引讨论。为了巩固这个过程,请使用下面的步骤完成系统设计的面试题和解答这个章节。

          第一步:描述使用场景,约束和假设

          把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。

          谁会使用它? 他们会怎样使用它? 有多少用户? 系统的作用是什么? 系统的输入输出分别是什么? 我们希望处理多少数据? 我们希望每秒钟处理多少请求? 我们希望的读写比率?

          第二步:创造一个高层级的设计

          使用所有重要的组件来描绘出一个高层级的设计。

          画出主要的组件和连接 证明你的想法

          第三步:设计核心组件

          对每一个核心组件进行详细深入的分析。举例来说,如果你被问到设计一个 url 缩写服务,开始讨论:

          生成并储存一个完整 url 的 hash MD5 和 Base62 Hash 碰撞 SQL 还是 NoSQL 数据库模型 将一个 hashed url 翻译成完整的 url 数据库查找 API 和面向对象设计

          第四步:扩展设计

          确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成扩展性的议题吗?

          负载均衡 水平扩展 缓存 数据库分片 论述可能的解决办法和代价。每件事情需要取舍。可以使用可扩展系统的设计原则来处理瓶颈。

          ',15)]))}const m=e(o,[["render",i]]);export{u as __pageData,m as default}; +import{_ as e,o as t,c as r,a3 as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"如何处理一个系统设计","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/systemDesign.md","filePath":"cn/src/article/systemDesign.md","lastUpdated":1728129344000}'),o={name:"cn/src/article/systemDesign.md"};function i(n,a,l,p,c,d){return t(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

          如何处理一个系统设计

          系统设计是一个开放式的对话。他们期望你去主导这个对话。

          你可以使用下面的步骤来指引讨论。为了巩固这个过程,请使用下面的步骤完成系统设计的面试题和解答这个章节。

          第一步:描述使用场景,约束和假设

          把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。

          谁会使用它? 他们会怎样使用它? 有多少用户? 系统的作用是什么? 系统的输入输出分别是什么? 我们希望处理多少数据? 我们希望每秒钟处理多少请求? 我们希望的读写比率?

          第二步:创造一个高层级的设计

          使用所有重要的组件来描绘出一个高层级的设计。

          画出主要的组件和连接 证明你的想法

          第三步:设计核心组件

          对每一个核心组件进行详细深入的分析。举例来说,如果你被问到设计一个 url 缩写服务,开始讨论:

          生成并储存一个完整 url 的 hash MD5 和 Base62 Hash 碰撞 SQL 还是 NoSQL 数据库模型 将一个 hashed url 翻译成完整的 url 数据库查找 API 和面向对象设计

          第四步:扩展设计

          确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成扩展性的议题吗?

          负载均衡 水平扩展 缓存 数据库分片 论述可能的解决办法和代价。每件事情需要取舍。可以使用可扩展系统的设计原则来处理瓶颈。

          ',15)]))}const m=e(o,[["render",i]]);export{u as __pageData,m as default}; diff --git a/assets/cn_src_article_systemDesign.md.Oy3scZvv.lean.js b/assets/cn_src_article_systemDesign.md.hF7LXas7.lean.js similarity index 97% rename from assets/cn_src_article_systemDesign.md.Oy3scZvv.lean.js rename to assets/cn_src_article_systemDesign.md.hF7LXas7.lean.js index 4e8c441678..d3ff79e2e0 100644 --- a/assets/cn_src_article_systemDesign.md.Oy3scZvv.lean.js +++ b/assets/cn_src_article_systemDesign.md.hF7LXas7.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as r,a3 as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"如何处理一个系统设计","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/systemDesign.md","filePath":"cn/src/article/systemDesign.md","lastUpdated":1728129125000}'),o={name:"cn/src/article/systemDesign.md"};function i(n,a,l,p,c,d){return t(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

          如何处理一个系统设计

          系统设计是一个开放式的对话。他们期望你去主导这个对话。

          你可以使用下面的步骤来指引讨论。为了巩固这个过程,请使用下面的步骤完成系统设计的面试题和解答这个章节。

          第一步:描述使用场景,约束和假设

          把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。

          谁会使用它? 他们会怎样使用它? 有多少用户? 系统的作用是什么? 系统的输入输出分别是什么? 我们希望处理多少数据? 我们希望每秒钟处理多少请求? 我们希望的读写比率?

          第二步:创造一个高层级的设计

          使用所有重要的组件来描绘出一个高层级的设计。

          画出主要的组件和连接 证明你的想法

          第三步:设计核心组件

          对每一个核心组件进行详细深入的分析。举例来说,如果你被问到设计一个 url 缩写服务,开始讨论:

          生成并储存一个完整 url 的 hash MD5 和 Base62 Hash 碰撞 SQL 还是 NoSQL 数据库模型 将一个 hashed url 翻译成完整的 url 数据库查找 API 和面向对象设计

          第四步:扩展设计

          确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成扩展性的议题吗?

          负载均衡 水平扩展 缓存 数据库分片 论述可能的解决办法和代价。每件事情需要取舍。可以使用可扩展系统的设计原则来处理瓶颈。

          ',15)]))}const m=e(o,[["render",i]]);export{u as __pageData,m as default}; +import{_ as e,o as t,c as r,a3 as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"如何处理一个系统设计","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/systemDesign.md","filePath":"cn/src/article/systemDesign.md","lastUpdated":1728129344000}'),o={name:"cn/src/article/systemDesign.md"};function i(n,a,l,p,c,d){return t(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

          如何处理一个系统设计

          系统设计是一个开放式的对话。他们期望你去主导这个对话。

          你可以使用下面的步骤来指引讨论。为了巩固这个过程,请使用下面的步骤完成系统设计的面试题和解答这个章节。

          第一步:描述使用场景,约束和假设

          把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。

          谁会使用它? 他们会怎样使用它? 有多少用户? 系统的作用是什么? 系统的输入输出分别是什么? 我们希望处理多少数据? 我们希望每秒钟处理多少请求? 我们希望的读写比率?

          第二步:创造一个高层级的设计

          使用所有重要的组件来描绘出一个高层级的设计。

          画出主要的组件和连接 证明你的想法

          第三步:设计核心组件

          对每一个核心组件进行详细深入的分析。举例来说,如果你被问到设计一个 url 缩写服务,开始讨论:

          生成并储存一个完整 url 的 hash MD5 和 Base62 Hash 碰撞 SQL 还是 NoSQL 数据库模型 将一个 hashed url 翻译成完整的 url 数据库查找 API 和面向对象设计

          第四步:扩展设计

          确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成扩展性的议题吗?

          负载均衡 水平扩展 缓存 数据库分片 论述可能的解决办法和代价。每件事情需要取舍。可以使用可扩展系统的设计原则来处理瓶颈。

          ',15)]))}const m=e(o,[["render",i]]);export{u as __pageData,m as default}; diff --git a/assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.js b/assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.js similarity index 99% rename from assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.js rename to assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.js index 76e88ae2c9..8ede263509 100644 --- a/assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.js +++ b/assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.js @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/calculate.md","filePath":"cn/src/article/typescript/calculate.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          数组长度做计数

          类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

          没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

          这是类型体操的第四个套路:数组长度做计数。

          TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

          不知道大家有没有注意到数组类型取 length 就是数值。

          比如:

          ts
          type num1 = [unknown]['length'];
          +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/calculate.md","filePath":"cn/src/article/typescript/calculate.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          数组长度做计数

          类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

          没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

          这是类型体操的第四个套路:数组长度做计数。

          TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

          不知道大家有没有注意到数组类型取 length 就是数值。

          比如:

          ts
          type num1 = [unknown]['length'];
           // type num1 = 1
           type num2 = [unknown, unknown]['length'];
           // type num1 = 2
          diff --git a/assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.lean.js b/assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.lean.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.lean.js
          rename to assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.lean.js
          index 76e88ae2c9..8ede263509 100644
          --- a/assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.lean.js
          +++ b/assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/calculate.md","filePath":"cn/src/article/typescript/calculate.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          数组长度做计数

          类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

          没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

          这是类型体操的第四个套路:数组长度做计数。

          TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

          不知道大家有没有注意到数组类型取 length 就是数值。

          比如:

          ts
          type num1 = [unknown]['length'];
          +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/calculate.md","filePath":"cn/src/article/typescript/calculate.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          数组长度做计数

          类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

          没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

          这是类型体操的第四个套路:数组长度做计数。

          TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

          不知道大家有没有注意到数组类型取 length 就是数值。

          比如:

          ts
          type num1 = [unknown]['length'];
           // type num1 = 1
           type num2 = [unknown, unknown]['length'];
           // type num1 = 2
          diff --git a/assets/cn_src_article_typescript_index.md.CaFWWXxk.js b/assets/cn_src_article_typescript_index.md.BE6MDNaH.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_index.md.CaFWWXxk.js
          rename to assets/cn_src_article_typescript_index.md.BE6MDNaH.js
          index 88e5612ee8..2d8e6717c3 100644
          --- a/assets/cn_src_article_typescript_index.md.CaFWWXxk.js
          +++ b/assets/cn_src_article_typescript_index.md.BE6MDNaH.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/index.md","filePath":"cn/src/article/typescript/index.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          TypeScript 的类型系统

          一.类型是什么

          类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

          • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

          • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

          有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

          如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

          类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

          两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

          其中,最常见的错误应该是 “null is not an object”、“undefined is not a function” 之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

          所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

          静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

          静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

          不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

          所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

          知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

          动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

          而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

          所以,大型项目注定会用静态类型语言开发。

          二.类型系统的分类

          1.简单的类型系统

          变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

          这是最基础的类型系统,能保证类型安全,但有些死板。

          比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

          c
          int add(int a, int b) {
          +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/index.md","filePath":"cn/src/article/typescript/index.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          TypeScript 的类型系统

          一.类型是什么

          类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

          • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

          • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

          有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

          如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

          类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

          两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

          其中,最常见的错误应该是 “null is not an object”、“undefined is not a function” 之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

          所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

          静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

          静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

          不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

          所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

          知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

          动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

          而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

          所以,大型项目注定会用静态类型语言开发。

          二.类型系统的分类

          1.简单的类型系统

          变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

          这是最基础的类型系统,能保证类型安全,但有些死板。

          比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

          c
          int add(int a, int b) {
               return a + b;
           }
           
          diff --git a/assets/cn_src_article_typescript_index.md.CaFWWXxk.lean.js b/assets/cn_src_article_typescript_index.md.BE6MDNaH.lean.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_index.md.CaFWWXxk.lean.js
          rename to assets/cn_src_article_typescript_index.md.BE6MDNaH.lean.js
          index 88e5612ee8..2d8e6717c3 100644
          --- a/assets/cn_src_article_typescript_index.md.CaFWWXxk.lean.js
          +++ b/assets/cn_src_article_typescript_index.md.BE6MDNaH.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/index.md","filePath":"cn/src/article/typescript/index.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          TypeScript 的类型系统

          一.类型是什么

          类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

          • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

          • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

          有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

          如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

          类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

          两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

          其中,最常见的错误应该是 “null is not an object”、“undefined is not a function” 之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

          所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

          静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

          静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

          不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

          所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

          知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

          动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

          而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

          所以,大型项目注定会用静态类型语言开发。

          二.类型系统的分类

          1.简单的类型系统

          变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

          这是最基础的类型系统,能保证类型安全,但有些死板。

          比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

          c
          int add(int a, int b) {
          +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/index.md","filePath":"cn/src/article/typescript/index.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          TypeScript 的类型系统

          一.类型是什么

          类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

          • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

          • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

          有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

          如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

          类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

          两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

          其中,最常见的错误应该是 “null is not an object”、“undefined is not a function” 之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

          所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

          静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

          静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

          不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

          所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

          知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

          动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

          而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

          所以,大型项目注定会用静态类型语言开发。

          二.类型系统的分类

          1.简单的类型系统

          变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

          这是最基础的类型系统,能保证类型安全,但有些死板。

          比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

          c
          int add(int a, int b) {
               return a + b;
           }
           
          diff --git a/assets/cn_src_article_typescript_pattern.md.GG3CpSHa.js b/assets/cn_src_article_typescript_pattern.md.ClYdggen.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_pattern.md.GG3CpSHa.js
          rename to assets/cn_src_article_typescript_pattern.md.ClYdggen.js
          index e31a87a521..c496da01c3 100644
          --- a/assets/cn_src_article_typescript_pattern.md.GG3CpSHa.js
          +++ b/assets/cn_src_article_typescript_pattern.md.ClYdggen.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/pattern.md","filePath":"cn/src/article/typescript/pattern.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          模式匹配提取

          字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

          ts
          'abc'.replace(/a(b)c/, '$1,$1,$1');
          +import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/pattern.md","filePath":"cn/src/article/typescript/pattern.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          模式匹配提取

          字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

          ts
          'abc'.replace(/a(b)c/, '$1,$1,$1');
           // 'b,b,b'

          Typescript 的类型也同样可以做模式匹配。

          比如这样一个 Promise 类型:

          ts
          type p = Promise<'value'>;

          我们想提取 value 的类型,可以这样做:

          ts
          type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

          通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

          ts
          // type GetValueResult = 'value'
           type GetValueResult = GetValueType<Promise<'value'>>;

          这就是 Typescript 类型的模式匹配:

          Typescript 类型的模式匹配是通过 extends 对类型参数做匹配,结果保存到通过 infer 声明的局部类型变量里,如果匹配就能从该局部变量里拿到提取出的类型。

          这个模式匹配的套路有多有用呢?我们来看下在数组、字符串、函数、构造器等类型里的应用。

          1.数组类型

          提取第一个元素

          数组类型想提取第一个元素的类型怎么做呢?

          ts
          type arr = [1, 2, 3];

          用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

          ts
          type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;

          类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unkown 也就是可以是任何值。

          any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

          对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;
           type GetFirstValue = GetFirst<[1, 2, 3]>;
          diff --git a/assets/cn_src_article_typescript_pattern.md.GG3CpSHa.lean.js b/assets/cn_src_article_typescript_pattern.md.ClYdggen.lean.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_pattern.md.GG3CpSHa.lean.js
          rename to assets/cn_src_article_typescript_pattern.md.ClYdggen.lean.js
          index e31a87a521..c496da01c3 100644
          --- a/assets/cn_src_article_typescript_pattern.md.GG3CpSHa.lean.js
          +++ b/assets/cn_src_article_typescript_pattern.md.ClYdggen.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/pattern.md","filePath":"cn/src/article/typescript/pattern.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          模式匹配提取

          字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

          ts
          'abc'.replace(/a(b)c/, '$1,$1,$1');
          +import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/pattern.md","filePath":"cn/src/article/typescript/pattern.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          模式匹配提取

          字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

          ts
          'abc'.replace(/a(b)c/, '$1,$1,$1');
           // 'b,b,b'

          Typescript 的类型也同样可以做模式匹配。

          比如这样一个 Promise 类型:

          ts
          type p = Promise<'value'>;

          我们想提取 value 的类型,可以这样做:

          ts
          type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

          通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

          ts
          // type GetValueResult = 'value'
           type GetValueResult = GetValueType<Promise<'value'>>;

          这就是 Typescript 类型的模式匹配:

          Typescript 类型的模式匹配是通过 extends 对类型参数做匹配,结果保存到通过 infer 声明的局部类型变量里,如果匹配就能从该局部变量里拿到提取出的类型。

          这个模式匹配的套路有多有用呢?我们来看下在数组、字符串、函数、构造器等类型里的应用。

          1.数组类型

          提取第一个元素

          数组类型想提取第一个元素的类型怎么做呢?

          ts
          type arr = [1, 2, 3];

          用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

          ts
          type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;

          类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unkown 也就是可以是任何值。

          any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

          对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;
           type GetFirstValue = GetFirst<[1, 2, 3]>;
          diff --git a/assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.js b/assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.js
          rename to assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.js
          index 88cedc5379..2d6fe72a66 100644
          --- a/assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.js
          +++ b/assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/reconstruction.md","filePath":"cn/src/article/typescript/reconstruction.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          重新构造做变换

          类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

          TypeScript 类型系统支持 3 种可以声明任意类型的变量: type、infer、类型参数。

          type 叫做类型别名,其实就是声明一个变量存储某个类型:

          ts
          type ttt = Promise<number>;

          infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

          ts
          type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

          类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

          ts
          type isTwo<T> = T extends 2 ? true : false;

          但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

          TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

          答案是重新构造。

          这就涉及到了第二个类型体操套路:重新构造做变换。

          重新构造

          TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

          数组、字符串、函数等类型的重新构造比较简单。

          索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

          我们先从简单的开始:

          数组类型的重新构造

          Push

          有这样一个元组类型:

          ts
          type tuple = [1, 2, 3];

          我想给这个元组类型再添加一些类型,怎么做呢?

          TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

          ts
          type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

          类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

          类型参数 Ele 是添加的元素的类型。

          返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

          ts
          type PushResult = Push<[1, 2, 3], 4>;
          +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/reconstruction.md","filePath":"cn/src/article/typescript/reconstruction.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          重新构造做变换

          类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

          TypeScript 类型系统支持 3 种可以声明任意类型的变量: type、infer、类型参数。

          type 叫做类型别名,其实就是声明一个变量存储某个类型:

          ts
          type ttt = Promise<number>;

          infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

          ts
          type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

          类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

          ts
          type isTwo<T> = T extends 2 ? true : false;

          但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

          TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

          答案是重新构造。

          这就涉及到了第二个类型体操套路:重新构造做变换。

          重新构造

          TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

          数组、字符串、函数等类型的重新构造比较简单。

          索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

          我们先从简单的开始:

          数组类型的重新构造

          Push

          有这样一个元组类型:

          ts
          type tuple = [1, 2, 3];

          我想给这个元组类型再添加一些类型,怎么做呢?

          TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

          ts
          type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

          类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

          类型参数 Ele 是添加的元素的类型。

          返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

          ts
          type PushResult = Push<[1, 2, 3], 4>;
           // type PushResult = [1,2,3,4]

          这就是数组/元组的重新构造。

          数组和元组的区别:数组类型是指任意多个同一类型的元素构成的,比如 number[]Array<number>,而元组则是数量固定,类型可以不同的元素构成的,比如 [1, true, 'name']

          Unshift

          可以在后面添加,同样也可以在前面添加:

          ts
          type Unshift<Arr extends unknown[], Ele> = [Ele, ...Arr];

          Zip

          有这样两个元组:

          ts
          type tuple1 = [1, 2];
           type tuple2 = ['name', 'value'];

          我们想把它们合并成这样的元组:

          ts
          type tuple = [[1, 'name'], [2, 'value']];

          思路很容易想到,提取元组中的两个元素,构造成新的元组:

          ts
          type Zip<One extends [unknown, unknown], Other extends [unknown, unknown]> = One extends [
             infer OneFirst,
          diff --git a/assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.lean.js b/assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.lean.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.lean.js
          rename to assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.lean.js
          index 88cedc5379..2d6fe72a66 100644
          --- a/assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.lean.js
          +++ b/assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/reconstruction.md","filePath":"cn/src/article/typescript/reconstruction.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          重新构造做变换

          类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

          TypeScript 类型系统支持 3 种可以声明任意类型的变量: type、infer、类型参数。

          type 叫做类型别名,其实就是声明一个变量存储某个类型:

          ts
          type ttt = Promise<number>;

          infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

          ts
          type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

          类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

          ts
          type isTwo<T> = T extends 2 ? true : false;

          但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

          TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

          答案是重新构造。

          这就涉及到了第二个类型体操套路:重新构造做变换。

          重新构造

          TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

          数组、字符串、函数等类型的重新构造比较简单。

          索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

          我们先从简单的开始:

          数组类型的重新构造

          Push

          有这样一个元组类型:

          ts
          type tuple = [1, 2, 3];

          我想给这个元组类型再添加一些类型,怎么做呢?

          TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

          ts
          type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

          类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

          类型参数 Ele 是添加的元素的类型。

          返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

          ts
          type PushResult = Push<[1, 2, 3], 4>;
          +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/reconstruction.md","filePath":"cn/src/article/typescript/reconstruction.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          重新构造做变换

          类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

          TypeScript 类型系统支持 3 种可以声明任意类型的变量: type、infer、类型参数。

          type 叫做类型别名,其实就是声明一个变量存储某个类型:

          ts
          type ttt = Promise<number>;

          infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

          ts
          type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

          类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

          ts
          type isTwo<T> = T extends 2 ? true : false;

          但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

          TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

          答案是重新构造。

          这就涉及到了第二个类型体操套路:重新构造做变换。

          重新构造

          TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

          数组、字符串、函数等类型的重新构造比较简单。

          索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

          我们先从简单的开始:

          数组类型的重新构造

          Push

          有这样一个元组类型:

          ts
          type tuple = [1, 2, 3];

          我想给这个元组类型再添加一些类型,怎么做呢?

          TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

          ts
          type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

          类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

          类型参数 Ele 是添加的元素的类型。

          返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

          ts
          type PushResult = Push<[1, 2, 3], 4>;
           // type PushResult = [1,2,3,4]

          这就是数组/元组的重新构造。

          数组和元组的区别:数组类型是指任意多个同一类型的元素构成的,比如 number[]Array<number>,而元组则是数量固定,类型可以不同的元素构成的,比如 [1, true, 'name']

          Unshift

          可以在后面添加,同样也可以在前面添加:

          ts
          type Unshift<Arr extends unknown[], Ele> = [Ele, ...Arr];

          Zip

          有这样两个元组:

          ts
          type tuple1 = [1, 2];
           type tuple2 = ['name', 'value'];

          我们想把它们合并成这样的元组:

          ts
          type tuple = [[1, 'name'], [2, 'value']];

          思路很容易想到,提取元组中的两个元素,构造成新的元组:

          ts
          type Zip<One extends [unknown, unknown], Other extends [unknown, unknown]> = One extends [
             infer OneFirst,
          diff --git a/assets/cn_src_article_typescript_recursion.md.NrD8NlPF.js b/assets/cn_src_article_typescript_recursion.md.DXrQrnQX.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_recursion.md.NrD8NlPF.js
          rename to assets/cn_src_article_typescript_recursion.md.DXrQrnQX.js
          index 78acffff03..fa5aed715a 100644
          --- a/assets/cn_src_article_typescript_recursion.md.NrD8NlPF.js
          +++ b/assets/cn_src_article_typescript_recursion.md.DXrQrnQX.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/recursion.md","filePath":"cn/src/article/typescript/recursion.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          递归复用

          递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

          TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

          TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

          既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

          Promise 的递归复用

          DeepPromiseValueType

          先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

          ts
          type ttt = Promise<Promise<Promise<Record<string, any>>>>;

          这里是 3 层 Promise,value 类型是索引类型。

          数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

          所以高级类型是这样的:

          ts
          type DeepPromiseValueType<P extends Promise<unknown>> =
          +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/recursion.md","filePath":"cn/src/article/typescript/recursion.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          递归复用

          递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

          TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

          TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

          既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

          Promise 的递归复用

          DeepPromiseValueType

          先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

          ts
          type ttt = Promise<Promise<Promise<Record<string, any>>>>;

          这里是 3 层 Promise,value 类型是索引类型。

          数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

          所以高级类型是这样的:

          ts
          type DeepPromiseValueType<P extends Promise<unknown>> =
             P extends Promise<infer ValueType>
               ? ValueType extends Promise<unknown>
                 ? DeepPromiseValueType<ValueType>
          diff --git a/assets/cn_src_article_typescript_recursion.md.NrD8NlPF.lean.js b/assets/cn_src_article_typescript_recursion.md.DXrQrnQX.lean.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_recursion.md.NrD8NlPF.lean.js
          rename to assets/cn_src_article_typescript_recursion.md.DXrQrnQX.lean.js
          index 78acffff03..fa5aed715a 100644
          --- a/assets/cn_src_article_typescript_recursion.md.NrD8NlPF.lean.js
          +++ b/assets/cn_src_article_typescript_recursion.md.DXrQrnQX.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/recursion.md","filePath":"cn/src/article/typescript/recursion.md","lastUpdated":1728129125000}'),n={name:"cn/src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          递归复用

          递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

          TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

          TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

          既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

          Promise 的递归复用

          DeepPromiseValueType

          先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

          ts
          type ttt = Promise<Promise<Promise<Record<string, any>>>>;

          这里是 3 层 Promise,value 类型是索引类型。

          数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

          所以高级类型是这样的:

          ts
          type DeepPromiseValueType<P extends Promise<unknown>> =
          +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/recursion.md","filePath":"cn/src/article/typescript/recursion.md","lastUpdated":1728129344000}'),n={name:"cn/src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          递归复用

          递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

          TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

          TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

          既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

          Promise 的递归复用

          DeepPromiseValueType

          先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

          ts
          type ttt = Promise<Promise<Promise<Record<string, any>>>>;

          这里是 3 层 Promise,value 类型是索引类型。

          数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

          所以高级类型是这样的:

          ts
          type DeepPromiseValueType<P extends Promise<unknown>> =
             P extends Promise<infer ValueType>
               ? ValueType extends Promise<unknown>
                 ? DeepPromiseValueType<ValueType>
          diff --git a/assets/cn_src_article_typescript_unionType.md.BgeL6nAW.js b/assets/cn_src_article_typescript_unionType.md.BEJLXxlO.js
          similarity index 99%
          rename from assets/cn_src_article_typescript_unionType.md.BgeL6nAW.js
          rename to assets/cn_src_article_typescript_unionType.md.BEJLXxlO.js
          index 387e9b03bf..498d07738f 100644
          --- a/assets/cn_src_article_typescript_unionType.md.BgeL6nAW.js
          +++ b/assets/cn_src_article_typescript_unionType.md.BEJLXxlO.js
          @@ -1 +1 @@
          -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/unionType.md","filePath":"cn/src/article/typescript/unionType.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          分布式条件类型

          当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

          比如这样一个联合类型:

          ts
          type Union = 'a' | 'b' | 'c';

          我们想把其中的 a 大写,就可以这样写:

          ts
          type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
          ts
          type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

          可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

          这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

          这和联合类型遇到字符串时的处理一样:

          这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

          TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

          知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

          CamelcaseUnion

          Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

          ts
          type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

          提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

          ts
          type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

          如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

          ts
          type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

          类型参数 Arr 为待处理数组。

          递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

          那如果是联合类型呢?

          联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

          ts
          type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

          这不和单个字符串的处理没区别么?

          没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

          确实简化了很多,好像都是优点?

          也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

          IsUnion

          判断联合类型我们会这样写:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          当传入联合类型时,会返回 true:

          ts
          type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

          当传入其他类型时,会返回 false:

          ts
          type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

          这就是分布式条件类型带来的认知成本。

          我们先来看这样一个类型:

          ts
          type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

          传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

          A 和 B 都是同一个联合类型,为啥值还不一样呢?

          因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

          所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c', A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

          那么利用这个特点就可以实现 Union 类型的判断:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

          A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

          [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

          B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

          利用这个特点就可以判断出是否是联合类型。

          其中有两个点比较困惑,我们重点记一下:

          当 A 是联合类型时:

          A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

          A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

          理解了这两点,分布式条件类型就算掌握了。

          BEM

          bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

          那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

          这样使用:

          ts
          type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

          它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

          而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

          数组转联合类型可以这样写:

          ts
          type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

          那么 BEM 就可以这样实现:

          ts
          type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

          类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

          构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

          字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

          ts
          type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

          可以看到,用好了联合类型,确实能简化类型编程逻辑。

          AllCombinations

          我们再来实现一个全组合的高级类型,也是联合类型相关的:

          希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

          这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

          比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

          任何两个类型的组合有四种:A、B、AB、BA

          ts
          type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

          然后构造出来的字符串再和其他字符串组合。

          所以全组合的高级类型就是这样:

          ts
          type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

          类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

          A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

          A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

          而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

          总结

          联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

          条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

          有两点特别要注意:

          • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

          • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

          我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

          ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/unionType.md","filePath":"cn/src/article/typescript/unionType.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          分布式条件类型

          当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

          比如这样一个联合类型:

          ts
          type Union = 'a' | 'b' | 'c';

          我们想把其中的 a 大写,就可以这样写:

          ts
          type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
          ts
          type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

          可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

          这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

          这和联合类型遇到字符串时的处理一样:

          这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

          TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

          知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

          CamelcaseUnion

          Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

          ts
          type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

          提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

          ts
          type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

          如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

          ts
          type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

          类型参数 Arr 为待处理数组。

          递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

          那如果是联合类型呢?

          联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

          ts
          type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

          这不和单个字符串的处理没区别么?

          没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

          确实简化了很多,好像都是优点?

          也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

          IsUnion

          判断联合类型我们会这样写:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          当传入联合类型时,会返回 true:

          ts
          type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

          当传入其他类型时,会返回 false:

          ts
          type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

          这就是分布式条件类型带来的认知成本。

          我们先来看这样一个类型:

          ts
          type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

          传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

          A 和 B 都是同一个联合类型,为啥值还不一样呢?

          因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

          所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c', A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

          那么利用这个特点就可以实现 Union 类型的判断:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

          A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

          [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

          B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

          利用这个特点就可以判断出是否是联合类型。

          其中有两个点比较困惑,我们重点记一下:

          当 A 是联合类型时:

          A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

          A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

          理解了这两点,分布式条件类型就算掌握了。

          BEM

          bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

          那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

          这样使用:

          ts
          type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

          它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

          而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

          数组转联合类型可以这样写:

          ts
          type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

          那么 BEM 就可以这样实现:

          ts
          type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

          类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

          构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

          字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

          ts
          type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

          可以看到,用好了联合类型,确实能简化类型编程逻辑。

          AllCombinations

          我们再来实现一个全组合的高级类型,也是联合类型相关的:

          希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

          这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

          比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

          任何两个类型的组合有四种:A、B、AB、BA

          ts
          type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

          然后构造出来的字符串再和其他字符串组合。

          所以全组合的高级类型就是这样:

          ts
          type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

          类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

          A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

          A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

          而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

          总结

          联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

          条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

          有两点特别要注意:

          • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

          • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

          我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

          ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git a/assets/cn_src_article_typescript_unionType.md.BgeL6nAW.lean.js b/assets/cn_src_article_typescript_unionType.md.BEJLXxlO.lean.js similarity index 99% rename from assets/cn_src_article_typescript_unionType.md.BgeL6nAW.lean.js rename to assets/cn_src_article_typescript_unionType.md.BEJLXxlO.lean.js index 387e9b03bf..498d07738f 100644 --- a/assets/cn_src_article_typescript_unionType.md.BgeL6nAW.lean.js +++ b/assets/cn_src_article_typescript_unionType.md.BEJLXxlO.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/unionType.md","filePath":"cn/src/article/typescript/unionType.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          分布式条件类型

          当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

          比如这样一个联合类型:

          ts
          type Union = 'a' | 'b' | 'c';

          我们想把其中的 a 大写,就可以这样写:

          ts
          type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
          ts
          type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

          可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

          这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

          这和联合类型遇到字符串时的处理一样:

          这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

          TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

          知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

          CamelcaseUnion

          Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

          ts
          type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

          提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

          ts
          type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

          如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

          ts
          type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

          类型参数 Arr 为待处理数组。

          递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

          那如果是联合类型呢?

          联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

          ts
          type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

          这不和单个字符串的处理没区别么?

          没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

          确实简化了很多,好像都是优点?

          也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

          IsUnion

          判断联合类型我们会这样写:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          当传入联合类型时,会返回 true:

          ts
          type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

          当传入其他类型时,会返回 false:

          ts
          type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

          这就是分布式条件类型带来的认知成本。

          我们先来看这样一个类型:

          ts
          type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

          传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

          A 和 B 都是同一个联合类型,为啥值还不一样呢?

          因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

          所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c', A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

          那么利用这个特点就可以实现 Union 类型的判断:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

          A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

          [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

          B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

          利用这个特点就可以判断出是否是联合类型。

          其中有两个点比较困惑,我们重点记一下:

          当 A 是联合类型时:

          A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

          A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

          理解了这两点,分布式条件类型就算掌握了。

          BEM

          bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

          那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

          这样使用:

          ts
          type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

          它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

          而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

          数组转联合类型可以这样写:

          ts
          type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

          那么 BEM 就可以这样实现:

          ts
          type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

          类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

          构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

          字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

          ts
          type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

          可以看到,用好了联合类型,确实能简化类型编程逻辑。

          AllCombinations

          我们再来实现一个全组合的高级类型,也是联合类型相关的:

          希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

          这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

          比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

          任何两个类型的组合有四种:A、B、AB、BA

          ts
          type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

          然后构造出来的字符串再和其他字符串组合。

          所以全组合的高级类型就是这样:

          ts
          type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

          类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

          A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

          A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

          而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

          总结

          联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

          条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

          有两点特别要注意:

          • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

          • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

          我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

          ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/typescript/unionType.md","filePath":"cn/src/article/typescript/unionType.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          分布式条件类型

          当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

          比如这样一个联合类型:

          ts
          type Union = 'a' | 'b' | 'c';

          我们想把其中的 a 大写,就可以这样写:

          ts
          type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
          ts
          type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

          可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

          这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

          这和联合类型遇到字符串时的处理一样:

          这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

          TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

          知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

          CamelcaseUnion

          Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

          ts
          type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

          提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

          ts
          type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

          如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

          ts
          type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

          类型参数 Arr 为待处理数组。

          递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

          那如果是联合类型呢?

          联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

          ts
          type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

          这不和单个字符串的处理没区别么?

          没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

          确实简化了很多,好像都是优点?

          也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

          IsUnion

          判断联合类型我们会这样写:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          当传入联合类型时,会返回 true:

          ts
          type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

          当传入其他类型时,会返回 false:

          ts
          type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

          这就是分布式条件类型带来的认知成本。

          我们先来看这样一个类型:

          ts
          type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

          传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

          A 和 B 都是同一个联合类型,为啥值还不一样呢?

          因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

          所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c', A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

          那么利用这个特点就可以实现 Union 类型的判断:

          ts
          type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

          类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

          A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

          [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

          B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

          利用这个特点就可以判断出是否是联合类型。

          其中有两个点比较困惑,我们重点记一下:

          当 A 是联合类型时:

          A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

          A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

          理解了这两点,分布式条件类型就算掌握了。

          BEM

          bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

          那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

          这样使用:

          ts
          type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

          它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

          而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

          数组转联合类型可以这样写:

          ts
          type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

          那么 BEM 就可以这样实现:

          ts
          type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

          类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

          构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

          字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

          ts
          type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

          可以看到,用好了联合类型,确实能简化类型编程逻辑。

          AllCombinations

          我们再来实现一个全组合的高级类型,也是联合类型相关的:

          希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

          这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

          比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

          任何两个类型的组合有四种:A、B、AB、BA

          ts
          type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

          然后构造出来的字符串再和其他字符串组合。

          所以全组合的高级类型就是这样:

          ts
          type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

          类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

          A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

          A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

          而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

          总结

          联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

          条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

          有两点特别要注意:

          • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

          • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

          我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

          ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git a/assets/cn_src_article_video.md.Cz2oSvp1.js b/assets/cn_src_article_video.md.D_lFXyPJ.js similarity index 99% rename from assets/cn_src_article_video.md.Cz2oSvp1.js rename to assets/cn_src_article_video.md.D_lFXyPJ.js index 277a6b1165..0a8422173b 100644 --- a/assets/cn_src_article_video.md.Cz2oSvp1.js +++ b/assets/cn_src_article_video.md.D_lFXyPJ.js @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/bilibili_video_code.DgWtgAEf.webp",p="/ran/assets/bili_demo_url.BQDGtHUp.webp",l="/ran/assets/bilibili_video_m4s.BccuY7bk.webp",k="/ran/assets/aqiyi_demo.s0swGzvF.webp",e="/ran/assets/tiktok_video.DuwKyIdt.webp",d="/ran/assets/tiktik_video_demo.mxxBzLay.webp",E="/ran/assets/red_book.DiBEvryP.webp",r="/ran/assets/safari_support_media.Bafr23bR.webp",g="/ran/assets/firefox_support_media.BLmSeBNy.webp",o="/ran/assets/ms_support.CaZSSiRt.webp",c="/ran/assets/ts_demo.D_l_mv9k.webp",y="/ran/assets/video_format.CRMCTmKB.webp",F="/ran/assets/xgplayer_docs.CpHb1Wan.webp",C="/ran/assets/bili_demo.CI8Ur8IA.webp",u="/ran/assets/rplayer_demo.CoJ7kuJt.webp",x=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/video.md","filePath":"cn/src/article/video.md","lastUpdated":1728129125000}'),B={name:"cn/src/article/video.md"};function m(A,s,b,v,D,f){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          实现自适应码率 Web 视频加密播放:前后端的挑战与解决方案

          最近又遇到了web视频化的场景,之前也有过调研:H5 视频化调研浅析

          但这次稍微复杂一些,这次解决的是:

          1. 视频播放的技术方案调研

          服务端实现:

          1. 视频转码
          2. 生成不同码率的视频
          3. 进行视频标准加密
          4. 不同码率视频合并,用于动态码率播放

          web端实现

          1. web端播放器的设计
          2. web端播放器的自定义扩展
          3. 可拖拽进度条
          4. 音量控制
          5. 根据当前带宽自适应码率切换
          6. 手动清晰度切换
          7. 倍速播放
          8. 样式自定义覆盖
          9. 标准加密视频播放
          10. 基于原生开发,可在所有框架运行,统一跨框架情况
          11. 各浏览器控件统一

          其中web端源码已添加MIT协议并完全开源,如果看完对大家有帮助的话,欢迎大家star,issue,pr,也希望能友好交流~

          demo 地址:https://chaxus.github.io/ran/src/ranui/player/

          源码地址:https://github.com/chaxus/ran

          demo文档做了国际化,可切换到中文

          任何一个项目,立项肯定先是技术调研,我们先看看一些大公司的视频播放方案

          一:一些知名公司的 web 视频播放方案

          1.B 站

          我们先看看 B 站的,毕竟 B 站的主营业务就是视频弹幕网站,简直专业对口。

          先找一个例子:https://www.bilibili.com/video/BV1FM411N7LJ 访问它。

          image.png

          打开控制台,可以看到,视频在播放的时候,会不断的请求m4s的视频文件。

          毕竟一整个视频文件往往比较大,不可能先请求完视频文件,再进行播放。因此将一个大的视频文件分割成很多小的片段,边加载边播放,是一种更好的方式。

          image.png

          每次请求的m4s文件大概在几十kb到几百kb不等。

          image.png

          那为什么不采用httprange呢,可以请求一个文件的部分内容,而且粒度更细,可以设置字节范围。在http请求的header中,类似这样

          js
          Range: bytes = 3171375 - 3203867;

          我们可以检查这个链接请求https://upos-sz-mirror08c.bilivideo.com/upgcxcode/67/92/1008149267/1008149267-1-30064.m4s的请求头,就能发现,B 站采用的是,即分片加载,同时还用了range的方式。

          2. 爱奇艺:(爱奇艺、土豆、优酷)

          爱奇艺这里就不贴视频链接了,因为随便点一个视频,都要先看广告。

          image.png

          爱奇艺的视频主要请求的是f4v格式,也是分片加载。

          播放一个视频时,请求多个f4v文件。

          也采用Range。但和 B 站不一样的是,B 站的Range属性是在m4s请求的请求头里面,而爱奇艺的看起来是在querystring上,在请求query上带着range参数。

          因为没发现这个请求的header里面有range参数。比如:

          https://v-6fce1712.71edge.com/videos/other/20231113/6b/bb/3f3fe83b89124248c3216156dfe2f4c3.f4v?dis_k=2ba39ee8c55c4d23781e3fb9f91fa7a46&dis_t=1701439831&dis_dz=CNC-BeiJing&dis_st=46&src=iqiyi.com&dis_hit=0&dis_tag=01010000&uuid=72713f52-6569e957-351&cross-domain=1&ssl=1&pv=0.1&cphc=arta&range=0-9000

          3.抖音:

          抖音的方案简单粗暴,访问的链接是这个: https://m.ixigua.com/douyin/share/video/7206914252840370721?aweme_type=107&schema_type=1&utm_source=copy&utm_campaign=client_share&utm_medium=android&app=aweme

          通过查看控制台,我们可以发现,直接请求了一个视频的地址

          image.png

          没有进行分片,但用到了请求range,所以可以看到视频,是边播放边缓冲一部分。

          不过我在开发的时候发现,目前租用的服务云厂商,默认会帮我们实现这项技术。

          因为我把mp4视频上传到云服务器,通过链接进行播放的时候,就是边缓冲边播放的。

          我们可以直接把这个视频地址拿出来,放到浏览器里面能直接播放,这样观察更明显。

          image.png

          但 B 站和爱奇艺却不能这样,因为他们采用的m4sf4v都不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          4.小红书:

          测试用的例子链接:https://www.xiaohongshu.com/discovery/item/63b286d1000000001f00b495

          小红书的方案更加简单粗暴,打开控制台,直接告诉你就是请求一个mp4,然后直接播放就完事了。

          image.png

          5.总结

          看完了以上的各家大厂的方案,我们可以看到,基本原理都是边播放边加载,减少直接加载大视频文本的成本。并且通过分片传输,还能动态控制视频码率(清晰度)。做到根据网速,加载不同码率的分片文件,做到动态码率适应。

          同时采用的视频格式,比如f4vm4s,都不是能直接播放的媒体格式,需要一定的处理。增加盗取视频的成本,增加一定的安全性。

          如果没有强要求,也可以直接采用mp4,或者直接用video播放一个视频文件地址。

          二:常见的视频格式与协议

          我们知道视频的常见格式有mp4,同时上面介绍了 B 站播放用的m4s格式,爱奇艺用的f4v格式

          • 除了这些还有哪些视频格式?
          • 为什么有这么多视频格式,有哪些不同点呢?
          • 为什么这些公司会采用这种格式来播放视频呢?

          1. B 站用的m4s

          M4S格式不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          M4S 通常会和 MPEG-DASH 流媒体技术一起,通过流式传输的视频的一小部分。播放器会按接收顺序播放这些片段。第一个 M4S 段会包含一些初始化的数据标识。

          MPEG-DASH 是一种自适应比特率流媒体技术,通过将内容分解为一系列不同码率的M4S片段,然后根据当前网络带宽进行自动调整。如果想在在web音视频中采用DASH技术,可以看下 https://github.com/Dash-Industry-Forum/dash.js

          2. 爱奇艺的f4v

          F4V是一种流媒体格式,它是由Adobe公司推出的,继FLV格式之后支持H.264编码的流媒体格式。F4V格式的视频不是一种通用的视频格式,但通常情况下,都可以将文件后缀改为FLV,这样就可以使用支持FLV的播放器进行观看。

          FLV格式跟常见的MP4格式比起来,结构更加简单,所以加载metadata(视频元数据,比如视频时长等信息) 会更快。具体结构我们可以在这里查到:https://en.wikipedia.org/wiki/Flash_Video#Flash_Video_Structure

          比如,这是FLV文件的标准头,定义了从几个比特到几个比特之间,是什么含义。我们知道后,可以用MediaSource进行读取和转码。

          FieldData TypeDefaultDetails
          Signature 签名byte[3]"FLV"始终就是“FLV”
          Version 版本uint81只有 0x01 才有效
          Flags 标志uint8 位掩码0x050x04 是音频,0x01 是视频(所以 0x05 是音频 + 视频)
          Header Sizeuint32_be9用于跳过较新的扩展标头

          MP4格式会稍微复杂一些,具体标准在 ISO/IEC 14496-12 大概有两百多页,这里放不下,对这方面有兴趣的可以自行查看。

          然而这并不表示MP4更差,因为它是一种基础通用标准,所以定义上会留有很多空间,和各种情况,甚至允许在标准之内进行自行发挥和扩展。而FLV格式则更加固定,但优点也是更加简单。

          对于FLV的视频播放,我们可以采用:https://github.com/bilibili/flv.js flvjs主要作用就是用MediaSourceflv转码成mp4从而喂给浏览器进行播放。

          接下来是一些其他的视频格式,简单介绍一下:

          3.AVI

          文件名以.avi结尾,AVI 最初由 Microsoft1992 年开发,是 Windows 的标准视频格式。AVI 文件使用较少的压缩来存储文件,并且比许多其他视频格式占用更多空间,这导致文件大小非常大,每分钟视频大约 2-3 GB

          无损文件不会随着时间的推移而降低质量,无论您打开或保存文件多少次。此外,这允许在不使用任何编解码器的情况下播放。参考资料:Audio Video Interleave

          4.MPEG

          文件名以“.mpg”或“.mpeg”结尾,MPEG 是由 ISO 和 IEC 联合成立的工作组联盟,旨在制定媒体编码标准,包括音频、视频、图形和基因组数据的压缩编码;以及各种应用程序的传输和文件格式。MPEG 格式用于各种多媒体系统。最广为人知的旧 MPEG 媒体格式通常使用 MPEG-1、MPEG-2 和 MPEG-4 AVC 媒体编码,MPEG-2 系统传输流和节目流。较新的系统通常使用 MPEG 基本媒体文件格式和动态流式处理(又名 .MPEG-DASH)。参考资料:Moving Picture Experts Group

          5.MP4

          带有音频和视频的 MPEG-4 文件通常使用标准的 .mp4 扩展名。纯音频 MPEG-4 文件通常具有 .m4a 扩展名,原始 MPEG-4 可视比特流命名为 .m4v。Apple iPhone 使用 MPEG-4 音频作为其铃声,但使用.m4r 扩展名而不是.m4a 扩展名。参考资料:MPEG-4 Part 14

          6.QuickTime

          文件名以“.mov”结尾,QuickTime 能够包含媒体数据的抽象数据引用,并将媒体数据与媒体偏移和轨道编辑列表分离,这意味着 QuickTime 特别适合编辑,因为它能够就地导入和编辑(无需数据复制)。由于 QuickTime 和 MP4 容器格式都可以使用相同的 MPEG-4 格式,因此在仅限 QuickTime 的环境中,它们大多可以互换。MP4 作为国际标准,得到了更多的支持。参考资料:QuickTime File Format

          7.TS

          TS 是 MPEG2-TS 的简称,是一种音视频封装格式。TS 流的后缀通常是.ts、.mpg 或者.mpeg,多数播放器直接支持这种格式的播放。TS 格式主要用于直播的码流结构,具有很好的容错能力。

          三:浏览器对各种视频格式的兼容性

          上面了解常用的视频格式,和适用范围之后,还需要看一下当前浏览器,对各种视频格式的支持程度,然后制定技术方案。

          1. Chrome

          支持的视频格式从官方文档可以查到,主要有以下这些

          • MP4 (QuickTime/ MOV / ISO-BMFF / CMAF)
          • Ogg
          • WebM
          • WAV
          • HLS [Only on Android and only single-origin manifests]

          官方文档如下:https://www.chromium.org/audio-video/

          2. Safari

          支持的视频格式有这些:

          image.png

          官方文档:https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html

          3.Firefox

          支持的视频格式:

          image.png

          官方文档:https://support.mozilla.org/en-US/kb/html5-audio-and-video-firefox

          四:MediaSource 和视频编码,解码,封装介绍

          上面介绍了一些视频格式,和目前浏览器的一些兼容性问题。就能发现,在web上播放音视频其实限制还是很大的。如何解决这些限制,就会用到MediaSource

          视频其实是无数个图片的叠加,如果视频是一秒60帧,大约一秒中需要播放60张图片。这就导致一个几分钟的视频,就会非常大。比如上面介绍的无损格式,avi格式,每分钟视频大约 2-3 GB。这时候视频就需要进行编码。其实就是压缩。

          编码分为视频编码和音频编码,常见的视频编码有:

          • MPEG 系列MPEG-1第二部分、MPEG-2第二部分(等同于H.262)、MPEG-4第二部分、MPEG-4第十部分(等同于H.264,有时候也被叫做“MPEG-4 AVC”或“H.264/AVC”)。
          • H.26x 系列H.261H.262H.263H.264(等同于MPEG-4第十部分)、H.265/HEVCITU-TISO/IEC联合推出)。
          • 其它视频编码WMV系列、RV系列、VC-1DivXXviDX264X265VP8VP9Sorenson VideoAVS

          常见的音频编码有:AACMP3AC-3

          编码之后,还需要将音频和视频合并在一个文件里,这就是封装

          所以相对的,播放一个视频,就需要解封装,解码,音视频同步喂给声卡和显卡进行播放。

          MediaSource做的就是这个工作,读取视频流,转换成浏览器能播放的格式。

          以下是flv.jsparseChunks部分内容。读取buffer,一个字节一个字节的根据标准进行解析。然后转码。

          js
          if (byteStart === 0) {
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/bilibili_video_code.DgWtgAEf.webp",p="/ran/assets/bili_demo_url.BQDGtHUp.webp",l="/ran/assets/bilibili_video_m4s.BccuY7bk.webp",k="/ran/assets/aqiyi_demo.s0swGzvF.webp",e="/ran/assets/tiktok_video.DuwKyIdt.webp",d="/ran/assets/tiktik_video_demo.mxxBzLay.webp",E="/ran/assets/red_book.DiBEvryP.webp",r="/ran/assets/safari_support_media.Bafr23bR.webp",g="/ran/assets/firefox_support_media.BLmSeBNy.webp",o="/ran/assets/ms_support.CaZSSiRt.webp",c="/ran/assets/ts_demo.D_l_mv9k.webp",y="/ran/assets/video_format.CRMCTmKB.webp",F="/ran/assets/xgplayer_docs.CpHb1Wan.webp",C="/ran/assets/bili_demo.CI8Ur8IA.webp",u="/ran/assets/rplayer_demo.CoJ7kuJt.webp",x=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/video.md","filePath":"cn/src/article/video.md","lastUpdated":1728129344000}'),B={name:"cn/src/article/video.md"};function m(A,s,b,v,D,f){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          实现自适应码率 Web 视频加密播放:前后端的挑战与解决方案

          最近又遇到了web视频化的场景,之前也有过调研:H5 视频化调研浅析

          但这次稍微复杂一些,这次解决的是:

          1. 视频播放的技术方案调研

          服务端实现:

          1. 视频转码
          2. 生成不同码率的视频
          3. 进行视频标准加密
          4. 不同码率视频合并,用于动态码率播放

          web端实现

          1. web端播放器的设计
          2. web端播放器的自定义扩展
          3. 可拖拽进度条
          4. 音量控制
          5. 根据当前带宽自适应码率切换
          6. 手动清晰度切换
          7. 倍速播放
          8. 样式自定义覆盖
          9. 标准加密视频播放
          10. 基于原生开发,可在所有框架运行,统一跨框架情况
          11. 各浏览器控件统一

          其中web端源码已添加MIT协议并完全开源,如果看完对大家有帮助的话,欢迎大家star,issue,pr,也希望能友好交流~

          demo 地址:https://chaxus.github.io/ran/src/ranui/player/

          源码地址:https://github.com/chaxus/ran

          demo文档做了国际化,可切换到中文

          任何一个项目,立项肯定先是技术调研,我们先看看一些大公司的视频播放方案

          一:一些知名公司的 web 视频播放方案

          1.B 站

          我们先看看 B 站的,毕竟 B 站的主营业务就是视频弹幕网站,简直专业对口。

          先找一个例子:https://www.bilibili.com/video/BV1FM411N7LJ 访问它。

          image.png

          打开控制台,可以看到,视频在播放的时候,会不断的请求m4s的视频文件。

          毕竟一整个视频文件往往比较大,不可能先请求完视频文件,再进行播放。因此将一个大的视频文件分割成很多小的片段,边加载边播放,是一种更好的方式。

          image.png

          每次请求的m4s文件大概在几十kb到几百kb不等。

          image.png

          那为什么不采用httprange呢,可以请求一个文件的部分内容,而且粒度更细,可以设置字节范围。在http请求的header中,类似这样

          js
          Range: bytes = 3171375 - 3203867;

          我们可以检查这个链接请求https://upos-sz-mirror08c.bilivideo.com/upgcxcode/67/92/1008149267/1008149267-1-30064.m4s的请求头,就能发现,B 站采用的是,即分片加载,同时还用了range的方式。

          2. 爱奇艺:(爱奇艺、土豆、优酷)

          爱奇艺这里就不贴视频链接了,因为随便点一个视频,都要先看广告。

          image.png

          爱奇艺的视频主要请求的是f4v格式,也是分片加载。

          播放一个视频时,请求多个f4v文件。

          也采用Range。但和 B 站不一样的是,B 站的Range属性是在m4s请求的请求头里面,而爱奇艺的看起来是在querystring上,在请求query上带着range参数。

          因为没发现这个请求的header里面有range参数。比如:

          https://v-6fce1712.71edge.com/videos/other/20231113/6b/bb/3f3fe83b89124248c3216156dfe2f4c3.f4v?dis_k=2ba39ee8c55c4d23781e3fb9f91fa7a46&dis_t=1701439831&dis_dz=CNC-BeiJing&dis_st=46&src=iqiyi.com&dis_hit=0&dis_tag=01010000&uuid=72713f52-6569e957-351&cross-domain=1&ssl=1&pv=0.1&cphc=arta&range=0-9000

          3.抖音:

          抖音的方案简单粗暴,访问的链接是这个: https://m.ixigua.com/douyin/share/video/7206914252840370721?aweme_type=107&schema_type=1&utm_source=copy&utm_campaign=client_share&utm_medium=android&app=aweme

          通过查看控制台,我们可以发现,直接请求了一个视频的地址

          image.png

          没有进行分片,但用到了请求range,所以可以看到视频,是边播放边缓冲一部分。

          不过我在开发的时候发现,目前租用的服务云厂商,默认会帮我们实现这项技术。

          因为我把mp4视频上传到云服务器,通过链接进行播放的时候,就是边缓冲边播放的。

          我们可以直接把这个视频地址拿出来,放到浏览器里面能直接播放,这样观察更明显。

          image.png

          但 B 站和爱奇艺却不能这样,因为他们采用的m4sf4v都不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          4.小红书:

          测试用的例子链接:https://www.xiaohongshu.com/discovery/item/63b286d1000000001f00b495

          小红书的方案更加简单粗暴,打开控制台,直接告诉你就是请求一个mp4,然后直接播放就完事了。

          image.png

          5.总结

          看完了以上的各家大厂的方案,我们可以看到,基本原理都是边播放边加载,减少直接加载大视频文本的成本。并且通过分片传输,还能动态控制视频码率(清晰度)。做到根据网速,加载不同码率的分片文件,做到动态码率适应。

          同时采用的视频格式,比如f4vm4s,都不是能直接播放的媒体格式,需要一定的处理。增加盗取视频的成本,增加一定的安全性。

          如果没有强要求,也可以直接采用mp4,或者直接用video播放一个视频文件地址。

          二:常见的视频格式与协议

          我们知道视频的常见格式有mp4,同时上面介绍了 B 站播放用的m4s格式,爱奇艺用的f4v格式

          • 除了这些还有哪些视频格式?
          • 为什么有这么多视频格式,有哪些不同点呢?
          • 为什么这些公司会采用这种格式来播放视频呢?

          1. B 站用的m4s

          M4S格式不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          M4S 通常会和 MPEG-DASH 流媒体技术一起,通过流式传输的视频的一小部分。播放器会按接收顺序播放这些片段。第一个 M4S 段会包含一些初始化的数据标识。

          MPEG-DASH 是一种自适应比特率流媒体技术,通过将内容分解为一系列不同码率的M4S片段,然后根据当前网络带宽进行自动调整。如果想在在web音视频中采用DASH技术,可以看下 https://github.com/Dash-Industry-Forum/dash.js

          2. 爱奇艺的f4v

          F4V是一种流媒体格式,它是由Adobe公司推出的,继FLV格式之后支持H.264编码的流媒体格式。F4V格式的视频不是一种通用的视频格式,但通常情况下,都可以将文件后缀改为FLV,这样就可以使用支持FLV的播放器进行观看。

          FLV格式跟常见的MP4格式比起来,结构更加简单,所以加载metadata(视频元数据,比如视频时长等信息) 会更快。具体结构我们可以在这里查到:https://en.wikipedia.org/wiki/Flash_Video#Flash_Video_Structure

          比如,这是FLV文件的标准头,定义了从几个比特到几个比特之间,是什么含义。我们知道后,可以用MediaSource进行读取和转码。

          FieldData TypeDefaultDetails
          Signature 签名byte[3]"FLV"始终就是“FLV”
          Version 版本uint81只有 0x01 才有效
          Flags 标志uint8 位掩码0x050x04 是音频,0x01 是视频(所以 0x05 是音频 + 视频)
          Header Sizeuint32_be9用于跳过较新的扩展标头

          MP4格式会稍微复杂一些,具体标准在 ISO/IEC 14496-12 大概有两百多页,这里放不下,对这方面有兴趣的可以自行查看。

          然而这并不表示MP4更差,因为它是一种基础通用标准,所以定义上会留有很多空间,和各种情况,甚至允许在标准之内进行自行发挥和扩展。而FLV格式则更加固定,但优点也是更加简单。

          对于FLV的视频播放,我们可以采用:https://github.com/bilibili/flv.js flvjs主要作用就是用MediaSourceflv转码成mp4从而喂给浏览器进行播放。

          接下来是一些其他的视频格式,简单介绍一下:

          3.AVI

          文件名以.avi结尾,AVI 最初由 Microsoft1992 年开发,是 Windows 的标准视频格式。AVI 文件使用较少的压缩来存储文件,并且比许多其他视频格式占用更多空间,这导致文件大小非常大,每分钟视频大约 2-3 GB

          无损文件不会随着时间的推移而降低质量,无论您打开或保存文件多少次。此外,这允许在不使用任何编解码器的情况下播放。参考资料:Audio Video Interleave

          4.MPEG

          文件名以“.mpg”或“.mpeg”结尾,MPEG 是由 ISO 和 IEC 联合成立的工作组联盟,旨在制定媒体编码标准,包括音频、视频、图形和基因组数据的压缩编码;以及各种应用程序的传输和文件格式。MPEG 格式用于各种多媒体系统。最广为人知的旧 MPEG 媒体格式通常使用 MPEG-1、MPEG-2 和 MPEG-4 AVC 媒体编码,MPEG-2 系统传输流和节目流。较新的系统通常使用 MPEG 基本媒体文件格式和动态流式处理(又名 .MPEG-DASH)。参考资料:Moving Picture Experts Group

          5.MP4

          带有音频和视频的 MPEG-4 文件通常使用标准的 .mp4 扩展名。纯音频 MPEG-4 文件通常具有 .m4a 扩展名,原始 MPEG-4 可视比特流命名为 .m4v。Apple iPhone 使用 MPEG-4 音频作为其铃声,但使用.m4r 扩展名而不是.m4a 扩展名。参考资料:MPEG-4 Part 14

          6.QuickTime

          文件名以“.mov”结尾,QuickTime 能够包含媒体数据的抽象数据引用,并将媒体数据与媒体偏移和轨道编辑列表分离,这意味着 QuickTime 特别适合编辑,因为它能够就地导入和编辑(无需数据复制)。由于 QuickTime 和 MP4 容器格式都可以使用相同的 MPEG-4 格式,因此在仅限 QuickTime 的环境中,它们大多可以互换。MP4 作为国际标准,得到了更多的支持。参考资料:QuickTime File Format

          7.TS

          TS 是 MPEG2-TS 的简称,是一种音视频封装格式。TS 流的后缀通常是.ts、.mpg 或者.mpeg,多数播放器直接支持这种格式的播放。TS 格式主要用于直播的码流结构,具有很好的容错能力。

          三:浏览器对各种视频格式的兼容性

          上面了解常用的视频格式,和适用范围之后,还需要看一下当前浏览器,对各种视频格式的支持程度,然后制定技术方案。

          1. Chrome

          支持的视频格式从官方文档可以查到,主要有以下这些

          • MP4 (QuickTime/ MOV / ISO-BMFF / CMAF)
          • Ogg
          • WebM
          • WAV
          • HLS [Only on Android and only single-origin manifests]

          官方文档如下:https://www.chromium.org/audio-video/

          2. Safari

          支持的视频格式有这些:

          image.png

          官方文档:https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html

          3.Firefox

          支持的视频格式:

          image.png

          官方文档:https://support.mozilla.org/en-US/kb/html5-audio-and-video-firefox

          四:MediaSource 和视频编码,解码,封装介绍

          上面介绍了一些视频格式,和目前浏览器的一些兼容性问题。就能发现,在web上播放音视频其实限制还是很大的。如何解决这些限制,就会用到MediaSource

          视频其实是无数个图片的叠加,如果视频是一秒60帧,大约一秒中需要播放60张图片。这就导致一个几分钟的视频,就会非常大。比如上面介绍的无损格式,avi格式,每分钟视频大约 2-3 GB。这时候视频就需要进行编码。其实就是压缩。

          编码分为视频编码和音频编码,常见的视频编码有:

          • MPEG 系列MPEG-1第二部分、MPEG-2第二部分(等同于H.262)、MPEG-4第二部分、MPEG-4第十部分(等同于H.264,有时候也被叫做“MPEG-4 AVC”或“H.264/AVC”)。
          • H.26x 系列H.261H.262H.263H.264(等同于MPEG-4第十部分)、H.265/HEVCITU-TISO/IEC联合推出)。
          • 其它视频编码WMV系列、RV系列、VC-1DivXXviDX264X265VP8VP9Sorenson VideoAVS

          常见的音频编码有:AACMP3AC-3

          编码之后,还需要将音频和视频合并在一个文件里,这就是封装

          所以相对的,播放一个视频,就需要解封装,解码,音视频同步喂给声卡和显卡进行播放。

          MediaSource做的就是这个工作,读取视频流,转换成浏览器能播放的格式。

          以下是flv.jsparseChunks部分内容。读取buffer,一个字节一个字节的根据标准进行解析。然后转码。

          js
          if (byteStart === 0) {
             // buffer with FLV header
             if (chunk.byteLength > 13) {
               let probeData = FLVDemuxer.probe(chunk);
          diff --git a/assets/cn_src_article_video.md.Cz2oSvp1.lean.js b/assets/cn_src_article_video.md.D_lFXyPJ.lean.js
          similarity index 99%
          rename from assets/cn_src_article_video.md.Cz2oSvp1.lean.js
          rename to assets/cn_src_article_video.md.D_lFXyPJ.lean.js
          index 277a6b1165..0a8422173b 100644
          --- a/assets/cn_src_article_video.md.Cz2oSvp1.lean.js
          +++ b/assets/cn_src_article_video.md.D_lFXyPJ.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/bilibili_video_code.DgWtgAEf.webp",p="/ran/assets/bili_demo_url.BQDGtHUp.webp",l="/ran/assets/bilibili_video_m4s.BccuY7bk.webp",k="/ran/assets/aqiyi_demo.s0swGzvF.webp",e="/ran/assets/tiktok_video.DuwKyIdt.webp",d="/ran/assets/tiktik_video_demo.mxxBzLay.webp",E="/ran/assets/red_book.DiBEvryP.webp",r="/ran/assets/safari_support_media.Bafr23bR.webp",g="/ran/assets/firefox_support_media.BLmSeBNy.webp",o="/ran/assets/ms_support.CaZSSiRt.webp",c="/ran/assets/ts_demo.D_l_mv9k.webp",y="/ran/assets/video_format.CRMCTmKB.webp",F="/ran/assets/xgplayer_docs.CpHb1Wan.webp",C="/ran/assets/bili_demo.CI8Ur8IA.webp",u="/ran/assets/rplayer_demo.CoJ7kuJt.webp",x=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/video.md","filePath":"cn/src/article/video.md","lastUpdated":1728129125000}'),B={name:"cn/src/article/video.md"};function m(A,s,b,v,D,f){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          实现自适应码率 Web 视频加密播放:前后端的挑战与解决方案

          最近又遇到了web视频化的场景,之前也有过调研:H5 视频化调研浅析

          但这次稍微复杂一些,这次解决的是:

          1. 视频播放的技术方案调研

          服务端实现:

          1. 视频转码
          2. 生成不同码率的视频
          3. 进行视频标准加密
          4. 不同码率视频合并,用于动态码率播放

          web端实现

          1. web端播放器的设计
          2. web端播放器的自定义扩展
          3. 可拖拽进度条
          4. 音量控制
          5. 根据当前带宽自适应码率切换
          6. 手动清晰度切换
          7. 倍速播放
          8. 样式自定义覆盖
          9. 标准加密视频播放
          10. 基于原生开发,可在所有框架运行,统一跨框架情况
          11. 各浏览器控件统一

          其中web端源码已添加MIT协议并完全开源,如果看完对大家有帮助的话,欢迎大家star,issue,pr,也希望能友好交流~

          demo 地址:https://chaxus.github.io/ran/src/ranui/player/

          源码地址:https://github.com/chaxus/ran

          demo文档做了国际化,可切换到中文

          任何一个项目,立项肯定先是技术调研,我们先看看一些大公司的视频播放方案

          一:一些知名公司的 web 视频播放方案

          1.B 站

          我们先看看 B 站的,毕竟 B 站的主营业务就是视频弹幕网站,简直专业对口。

          先找一个例子:https://www.bilibili.com/video/BV1FM411N7LJ 访问它。

          image.png

          打开控制台,可以看到,视频在播放的时候,会不断的请求m4s的视频文件。

          毕竟一整个视频文件往往比较大,不可能先请求完视频文件,再进行播放。因此将一个大的视频文件分割成很多小的片段,边加载边播放,是一种更好的方式。

          image.png

          每次请求的m4s文件大概在几十kb到几百kb不等。

          image.png

          那为什么不采用httprange呢,可以请求一个文件的部分内容,而且粒度更细,可以设置字节范围。在http请求的header中,类似这样

          js
          Range: bytes = 3171375 - 3203867;

          我们可以检查这个链接请求https://upos-sz-mirror08c.bilivideo.com/upgcxcode/67/92/1008149267/1008149267-1-30064.m4s的请求头,就能发现,B 站采用的是,即分片加载,同时还用了range的方式。

          2. 爱奇艺:(爱奇艺、土豆、优酷)

          爱奇艺这里就不贴视频链接了,因为随便点一个视频,都要先看广告。

          image.png

          爱奇艺的视频主要请求的是f4v格式,也是分片加载。

          播放一个视频时,请求多个f4v文件。

          也采用Range。但和 B 站不一样的是,B 站的Range属性是在m4s请求的请求头里面,而爱奇艺的看起来是在querystring上,在请求query上带着range参数。

          因为没发现这个请求的header里面有range参数。比如:

          https://v-6fce1712.71edge.com/videos/other/20231113/6b/bb/3f3fe83b89124248c3216156dfe2f4c3.f4v?dis_k=2ba39ee8c55c4d23781e3fb9f91fa7a46&dis_t=1701439831&dis_dz=CNC-BeiJing&dis_st=46&src=iqiyi.com&dis_hit=0&dis_tag=01010000&uuid=72713f52-6569e957-351&cross-domain=1&ssl=1&pv=0.1&cphc=arta&range=0-9000

          3.抖音:

          抖音的方案简单粗暴,访问的链接是这个: https://m.ixigua.com/douyin/share/video/7206914252840370721?aweme_type=107&schema_type=1&utm_source=copy&utm_campaign=client_share&utm_medium=android&app=aweme

          通过查看控制台,我们可以发现,直接请求了一个视频的地址

          image.png

          没有进行分片,但用到了请求range,所以可以看到视频,是边播放边缓冲一部分。

          不过我在开发的时候发现,目前租用的服务云厂商,默认会帮我们实现这项技术。

          因为我把mp4视频上传到云服务器,通过链接进行播放的时候,就是边缓冲边播放的。

          我们可以直接把这个视频地址拿出来,放到浏览器里面能直接播放,这样观察更明显。

          image.png

          但 B 站和爱奇艺却不能这样,因为他们采用的m4sf4v都不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          4.小红书:

          测试用的例子链接:https://www.xiaohongshu.com/discovery/item/63b286d1000000001f00b495

          小红书的方案更加简单粗暴,打开控制台,直接告诉你就是请求一个mp4,然后直接播放就完事了。

          image.png

          5.总结

          看完了以上的各家大厂的方案,我们可以看到,基本原理都是边播放边加载,减少直接加载大视频文本的成本。并且通过分片传输,还能动态控制视频码率(清晰度)。做到根据网速,加载不同码率的分片文件,做到动态码率适应。

          同时采用的视频格式,比如f4vm4s,都不是能直接播放的媒体格式,需要一定的处理。增加盗取视频的成本,增加一定的安全性。

          如果没有强要求,也可以直接采用mp4,或者直接用video播放一个视频文件地址。

          二:常见的视频格式与协议

          我们知道视频的常见格式有mp4,同时上面介绍了 B 站播放用的m4s格式,爱奇艺用的f4v格式

          • 除了这些还有哪些视频格式?
          • 为什么有这么多视频格式,有哪些不同点呢?
          • 为什么这些公司会采用这种格式来播放视频呢?

          1. B 站用的m4s

          M4S格式不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          M4S 通常会和 MPEG-DASH 流媒体技术一起,通过流式传输的视频的一小部分。播放器会按接收顺序播放这些片段。第一个 M4S 段会包含一些初始化的数据标识。

          MPEG-DASH 是一种自适应比特率流媒体技术,通过将内容分解为一系列不同码率的M4S片段,然后根据当前网络带宽进行自动调整。如果想在在web音视频中采用DASH技术,可以看下 https://github.com/Dash-Industry-Forum/dash.js

          2. 爱奇艺的f4v

          F4V是一种流媒体格式,它是由Adobe公司推出的,继FLV格式之后支持H.264编码的流媒体格式。F4V格式的视频不是一种通用的视频格式,但通常情况下,都可以将文件后缀改为FLV,这样就可以使用支持FLV的播放器进行观看。

          FLV格式跟常见的MP4格式比起来,结构更加简单,所以加载metadata(视频元数据,比如视频时长等信息) 会更快。具体结构我们可以在这里查到:https://en.wikipedia.org/wiki/Flash_Video#Flash_Video_Structure

          比如,这是FLV文件的标准头,定义了从几个比特到几个比特之间,是什么含义。我们知道后,可以用MediaSource进行读取和转码。

          FieldData TypeDefaultDetails
          Signature 签名byte[3]"FLV"始终就是“FLV”
          Version 版本uint81只有 0x01 才有效
          Flags 标志uint8 位掩码0x050x04 是音频,0x01 是视频(所以 0x05 是音频 + 视频)
          Header Sizeuint32_be9用于跳过较新的扩展标头

          MP4格式会稍微复杂一些,具体标准在 ISO/IEC 14496-12 大概有两百多页,这里放不下,对这方面有兴趣的可以自行查看。

          然而这并不表示MP4更差,因为它是一种基础通用标准,所以定义上会留有很多空间,和各种情况,甚至允许在标准之内进行自行发挥和扩展。而FLV格式则更加固定,但优点也是更加简单。

          对于FLV的视频播放,我们可以采用:https://github.com/bilibili/flv.js flvjs主要作用就是用MediaSourceflv转码成mp4从而喂给浏览器进行播放。

          接下来是一些其他的视频格式,简单介绍一下:

          3.AVI

          文件名以.avi结尾,AVI 最初由 Microsoft1992 年开发,是 Windows 的标准视频格式。AVI 文件使用较少的压缩来存储文件,并且比许多其他视频格式占用更多空间,这导致文件大小非常大,每分钟视频大约 2-3 GB

          无损文件不会随着时间的推移而降低质量,无论您打开或保存文件多少次。此外,这允许在不使用任何编解码器的情况下播放。参考资料:Audio Video Interleave

          4.MPEG

          文件名以“.mpg”或“.mpeg”结尾,MPEG 是由 ISO 和 IEC 联合成立的工作组联盟,旨在制定媒体编码标准,包括音频、视频、图形和基因组数据的压缩编码;以及各种应用程序的传输和文件格式。MPEG 格式用于各种多媒体系统。最广为人知的旧 MPEG 媒体格式通常使用 MPEG-1、MPEG-2 和 MPEG-4 AVC 媒体编码,MPEG-2 系统传输流和节目流。较新的系统通常使用 MPEG 基本媒体文件格式和动态流式处理(又名 .MPEG-DASH)。参考资料:Moving Picture Experts Group

          5.MP4

          带有音频和视频的 MPEG-4 文件通常使用标准的 .mp4 扩展名。纯音频 MPEG-4 文件通常具有 .m4a 扩展名,原始 MPEG-4 可视比特流命名为 .m4v。Apple iPhone 使用 MPEG-4 音频作为其铃声,但使用.m4r 扩展名而不是.m4a 扩展名。参考资料:MPEG-4 Part 14

          6.QuickTime

          文件名以“.mov”结尾,QuickTime 能够包含媒体数据的抽象数据引用,并将媒体数据与媒体偏移和轨道编辑列表分离,这意味着 QuickTime 特别适合编辑,因为它能够就地导入和编辑(无需数据复制)。由于 QuickTime 和 MP4 容器格式都可以使用相同的 MPEG-4 格式,因此在仅限 QuickTime 的环境中,它们大多可以互换。MP4 作为国际标准,得到了更多的支持。参考资料:QuickTime File Format

          7.TS

          TS 是 MPEG2-TS 的简称,是一种音视频封装格式。TS 流的后缀通常是.ts、.mpg 或者.mpeg,多数播放器直接支持这种格式的播放。TS 格式主要用于直播的码流结构,具有很好的容错能力。

          三:浏览器对各种视频格式的兼容性

          上面了解常用的视频格式,和适用范围之后,还需要看一下当前浏览器,对各种视频格式的支持程度,然后制定技术方案。

          1. Chrome

          支持的视频格式从官方文档可以查到,主要有以下这些

          • MP4 (QuickTime/ MOV / ISO-BMFF / CMAF)
          • Ogg
          • WebM
          • WAV
          • HLS [Only on Android and only single-origin manifests]

          官方文档如下:https://www.chromium.org/audio-video/

          2. Safari

          支持的视频格式有这些:

          image.png

          官方文档:https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html

          3.Firefox

          支持的视频格式:

          image.png

          官方文档:https://support.mozilla.org/en-US/kb/html5-audio-and-video-firefox

          四:MediaSource 和视频编码,解码,封装介绍

          上面介绍了一些视频格式,和目前浏览器的一些兼容性问题。就能发现,在web上播放音视频其实限制还是很大的。如何解决这些限制,就会用到MediaSource

          视频其实是无数个图片的叠加,如果视频是一秒60帧,大约一秒中需要播放60张图片。这就导致一个几分钟的视频,就会非常大。比如上面介绍的无损格式,avi格式,每分钟视频大约 2-3 GB。这时候视频就需要进行编码。其实就是压缩。

          编码分为视频编码和音频编码,常见的视频编码有:

          • MPEG 系列MPEG-1第二部分、MPEG-2第二部分(等同于H.262)、MPEG-4第二部分、MPEG-4第十部分(等同于H.264,有时候也被叫做“MPEG-4 AVC”或“H.264/AVC”)。
          • H.26x 系列H.261H.262H.263H.264(等同于MPEG-4第十部分)、H.265/HEVCITU-TISO/IEC联合推出)。
          • 其它视频编码WMV系列、RV系列、VC-1DivXXviDX264X265VP8VP9Sorenson VideoAVS

          常见的音频编码有:AACMP3AC-3

          编码之后,还需要将音频和视频合并在一个文件里,这就是封装

          所以相对的,播放一个视频,就需要解封装,解码,音视频同步喂给声卡和显卡进行播放。

          MediaSource做的就是这个工作,读取视频流,转换成浏览器能播放的格式。

          以下是flv.jsparseChunks部分内容。读取buffer,一个字节一个字节的根据标准进行解析。然后转码。

          js
          if (byteStart === 0) {
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const t="/ran/assets/bilibili_video_code.DgWtgAEf.webp",p="/ran/assets/bili_demo_url.BQDGtHUp.webp",l="/ran/assets/bilibili_video_m4s.BccuY7bk.webp",k="/ran/assets/aqiyi_demo.s0swGzvF.webp",e="/ran/assets/tiktok_video.DuwKyIdt.webp",d="/ran/assets/tiktik_video_demo.mxxBzLay.webp",E="/ran/assets/red_book.DiBEvryP.webp",r="/ran/assets/safari_support_media.Bafr23bR.webp",g="/ran/assets/firefox_support_media.BLmSeBNy.webp",o="/ran/assets/ms_support.CaZSSiRt.webp",c="/ran/assets/ts_demo.D_l_mv9k.webp",y="/ran/assets/video_format.CRMCTmKB.webp",F="/ran/assets/xgplayer_docs.CpHb1Wan.webp",C="/ran/assets/bili_demo.CI8Ur8IA.webp",u="/ran/assets/rplayer_demo.CoJ7kuJt.webp",x=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/video.md","filePath":"cn/src/article/video.md","lastUpdated":1728129344000}'),B={name:"cn/src/article/video.md"};function m(A,s,b,v,D,f){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

          实现自适应码率 Web 视频加密播放:前后端的挑战与解决方案

          最近又遇到了web视频化的场景,之前也有过调研:H5 视频化调研浅析

          但这次稍微复杂一些,这次解决的是:

          1. 视频播放的技术方案调研

          服务端实现:

          1. 视频转码
          2. 生成不同码率的视频
          3. 进行视频标准加密
          4. 不同码率视频合并,用于动态码率播放

          web端实现

          1. web端播放器的设计
          2. web端播放器的自定义扩展
          3. 可拖拽进度条
          4. 音量控制
          5. 根据当前带宽自适应码率切换
          6. 手动清晰度切换
          7. 倍速播放
          8. 样式自定义覆盖
          9. 标准加密视频播放
          10. 基于原生开发,可在所有框架运行,统一跨框架情况
          11. 各浏览器控件统一

          其中web端源码已添加MIT协议并完全开源,如果看完对大家有帮助的话,欢迎大家star,issue,pr,也希望能友好交流~

          demo 地址:https://chaxus.github.io/ran/src/ranui/player/

          源码地址:https://github.com/chaxus/ran

          demo文档做了国际化,可切换到中文

          任何一个项目,立项肯定先是技术调研,我们先看看一些大公司的视频播放方案

          一:一些知名公司的 web 视频播放方案

          1.B 站

          我们先看看 B 站的,毕竟 B 站的主营业务就是视频弹幕网站,简直专业对口。

          先找一个例子:https://www.bilibili.com/video/BV1FM411N7LJ 访问它。

          image.png

          打开控制台,可以看到,视频在播放的时候,会不断的请求m4s的视频文件。

          毕竟一整个视频文件往往比较大,不可能先请求完视频文件,再进行播放。因此将一个大的视频文件分割成很多小的片段,边加载边播放,是一种更好的方式。

          image.png

          每次请求的m4s文件大概在几十kb到几百kb不等。

          image.png

          那为什么不采用httprange呢,可以请求一个文件的部分内容,而且粒度更细,可以设置字节范围。在http请求的header中,类似这样

          js
          Range: bytes = 3171375 - 3203867;

          我们可以检查这个链接请求https://upos-sz-mirror08c.bilivideo.com/upgcxcode/67/92/1008149267/1008149267-1-30064.m4s的请求头,就能发现,B 站采用的是,即分片加载,同时还用了range的方式。

          2. 爱奇艺:(爱奇艺、土豆、优酷)

          爱奇艺这里就不贴视频链接了,因为随便点一个视频,都要先看广告。

          image.png

          爱奇艺的视频主要请求的是f4v格式,也是分片加载。

          播放一个视频时,请求多个f4v文件。

          也采用Range。但和 B 站不一样的是,B 站的Range属性是在m4s请求的请求头里面,而爱奇艺的看起来是在querystring上,在请求query上带着range参数。

          因为没发现这个请求的header里面有range参数。比如:

          https://v-6fce1712.71edge.com/videos/other/20231113/6b/bb/3f3fe83b89124248c3216156dfe2f4c3.f4v?dis_k=2ba39ee8c55c4d23781e3fb9f91fa7a46&dis_t=1701439831&dis_dz=CNC-BeiJing&dis_st=46&src=iqiyi.com&dis_hit=0&dis_tag=01010000&uuid=72713f52-6569e957-351&cross-domain=1&ssl=1&pv=0.1&cphc=arta&range=0-9000

          3.抖音:

          抖音的方案简单粗暴,访问的链接是这个: https://m.ixigua.com/douyin/share/video/7206914252840370721?aweme_type=107&schema_type=1&utm_source=copy&utm_campaign=client_share&utm_medium=android&app=aweme

          通过查看控制台,我们可以发现,直接请求了一个视频的地址

          image.png

          没有进行分片,但用到了请求range,所以可以看到视频,是边播放边缓冲一部分。

          不过我在开发的时候发现,目前租用的服务云厂商,默认会帮我们实现这项技术。

          因为我把mp4视频上传到云服务器,通过链接进行播放的时候,就是边缓冲边播放的。

          我们可以直接把这个视频地址拿出来,放到浏览器里面能直接播放,这样观察更明显。

          image.png

          但 B 站和爱奇艺却不能这样,因为他们采用的m4sf4v都不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          4.小红书:

          测试用的例子链接:https://www.xiaohongshu.com/discovery/item/63b286d1000000001f00b495

          小红书的方案更加简单粗暴,打开控制台,直接告诉你就是请求一个mp4,然后直接播放就完事了。

          image.png

          5.总结

          看完了以上的各家大厂的方案,我们可以看到,基本原理都是边播放边加载,减少直接加载大视频文本的成本。并且通过分片传输,还能动态控制视频码率(清晰度)。做到根据网速,加载不同码率的分片文件,做到动态码率适应。

          同时采用的视频格式,比如f4vm4s,都不是能直接播放的媒体格式,需要一定的处理。增加盗取视频的成本,增加一定的安全性。

          如果没有强要求,也可以直接采用mp4,或者直接用video播放一个视频文件地址。

          二:常见的视频格式与协议

          我们知道视频的常见格式有mp4,同时上面介绍了 B 站播放用的m4s格式,爱奇艺用的f4v格式

          • 除了这些还有哪些视频格式?
          • 为什么有这么多视频格式,有哪些不同点呢?
          • 为什么这些公司会采用这种格式来播放视频呢?

          1. B 站用的m4s

          M4S格式不是一种通用的视频格式,需要使用专门的软件或工具才能打开和编辑。

          M4S 通常会和 MPEG-DASH 流媒体技术一起,通过流式传输的视频的一小部分。播放器会按接收顺序播放这些片段。第一个 M4S 段会包含一些初始化的数据标识。

          MPEG-DASH 是一种自适应比特率流媒体技术,通过将内容分解为一系列不同码率的M4S片段,然后根据当前网络带宽进行自动调整。如果想在在web音视频中采用DASH技术,可以看下 https://github.com/Dash-Industry-Forum/dash.js

          2. 爱奇艺的f4v

          F4V是一种流媒体格式,它是由Adobe公司推出的,继FLV格式之后支持H.264编码的流媒体格式。F4V格式的视频不是一种通用的视频格式,但通常情况下,都可以将文件后缀改为FLV,这样就可以使用支持FLV的播放器进行观看。

          FLV格式跟常见的MP4格式比起来,结构更加简单,所以加载metadata(视频元数据,比如视频时长等信息) 会更快。具体结构我们可以在这里查到:https://en.wikipedia.org/wiki/Flash_Video#Flash_Video_Structure

          比如,这是FLV文件的标准头,定义了从几个比特到几个比特之间,是什么含义。我们知道后,可以用MediaSource进行读取和转码。

          FieldData TypeDefaultDetails
          Signature 签名byte[3]"FLV"始终就是“FLV”
          Version 版本uint81只有 0x01 才有效
          Flags 标志uint8 位掩码0x050x04 是音频,0x01 是视频(所以 0x05 是音频 + 视频)
          Header Sizeuint32_be9用于跳过较新的扩展标头

          MP4格式会稍微复杂一些,具体标准在 ISO/IEC 14496-12 大概有两百多页,这里放不下,对这方面有兴趣的可以自行查看。

          然而这并不表示MP4更差,因为它是一种基础通用标准,所以定义上会留有很多空间,和各种情况,甚至允许在标准之内进行自行发挥和扩展。而FLV格式则更加固定,但优点也是更加简单。

          对于FLV的视频播放,我们可以采用:https://github.com/bilibili/flv.js flvjs主要作用就是用MediaSourceflv转码成mp4从而喂给浏览器进行播放。

          接下来是一些其他的视频格式,简单介绍一下:

          3.AVI

          文件名以.avi结尾,AVI 最初由 Microsoft1992 年开发,是 Windows 的标准视频格式。AVI 文件使用较少的压缩来存储文件,并且比许多其他视频格式占用更多空间,这导致文件大小非常大,每分钟视频大约 2-3 GB

          无损文件不会随着时间的推移而降低质量,无论您打开或保存文件多少次。此外,这允许在不使用任何编解码器的情况下播放。参考资料:Audio Video Interleave

          4.MPEG

          文件名以“.mpg”或“.mpeg”结尾,MPEG 是由 ISO 和 IEC 联合成立的工作组联盟,旨在制定媒体编码标准,包括音频、视频、图形和基因组数据的压缩编码;以及各种应用程序的传输和文件格式。MPEG 格式用于各种多媒体系统。最广为人知的旧 MPEG 媒体格式通常使用 MPEG-1、MPEG-2 和 MPEG-4 AVC 媒体编码,MPEG-2 系统传输流和节目流。较新的系统通常使用 MPEG 基本媒体文件格式和动态流式处理(又名 .MPEG-DASH)。参考资料:Moving Picture Experts Group

          5.MP4

          带有音频和视频的 MPEG-4 文件通常使用标准的 .mp4 扩展名。纯音频 MPEG-4 文件通常具有 .m4a 扩展名,原始 MPEG-4 可视比特流命名为 .m4v。Apple iPhone 使用 MPEG-4 音频作为其铃声,但使用.m4r 扩展名而不是.m4a 扩展名。参考资料:MPEG-4 Part 14

          6.QuickTime

          文件名以“.mov”结尾,QuickTime 能够包含媒体数据的抽象数据引用,并将媒体数据与媒体偏移和轨道编辑列表分离,这意味着 QuickTime 特别适合编辑,因为它能够就地导入和编辑(无需数据复制)。由于 QuickTime 和 MP4 容器格式都可以使用相同的 MPEG-4 格式,因此在仅限 QuickTime 的环境中,它们大多可以互换。MP4 作为国际标准,得到了更多的支持。参考资料:QuickTime File Format

          7.TS

          TS 是 MPEG2-TS 的简称,是一种音视频封装格式。TS 流的后缀通常是.ts、.mpg 或者.mpeg,多数播放器直接支持这种格式的播放。TS 格式主要用于直播的码流结构,具有很好的容错能力。

          三:浏览器对各种视频格式的兼容性

          上面了解常用的视频格式,和适用范围之后,还需要看一下当前浏览器,对各种视频格式的支持程度,然后制定技术方案。

          1. Chrome

          支持的视频格式从官方文档可以查到,主要有以下这些

          • MP4 (QuickTime/ MOV / ISO-BMFF / CMAF)
          • Ogg
          • WebM
          • WAV
          • HLS [Only on Android and only single-origin manifests]

          官方文档如下:https://www.chromium.org/audio-video/

          2. Safari

          支持的视频格式有这些:

          image.png

          官方文档:https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html

          3.Firefox

          支持的视频格式:

          image.png

          官方文档:https://support.mozilla.org/en-US/kb/html5-audio-and-video-firefox

          四:MediaSource 和视频编码,解码,封装介绍

          上面介绍了一些视频格式,和目前浏览器的一些兼容性问题。就能发现,在web上播放音视频其实限制还是很大的。如何解决这些限制,就会用到MediaSource

          视频其实是无数个图片的叠加,如果视频是一秒60帧,大约一秒中需要播放60张图片。这就导致一个几分钟的视频,就会非常大。比如上面介绍的无损格式,avi格式,每分钟视频大约 2-3 GB。这时候视频就需要进行编码。其实就是压缩。

          编码分为视频编码和音频编码,常见的视频编码有:

          • MPEG 系列MPEG-1第二部分、MPEG-2第二部分(等同于H.262)、MPEG-4第二部分、MPEG-4第十部分(等同于H.264,有时候也被叫做“MPEG-4 AVC”或“H.264/AVC”)。
          • H.26x 系列H.261H.262H.263H.264(等同于MPEG-4第十部分)、H.265/HEVCITU-TISO/IEC联合推出)。
          • 其它视频编码WMV系列、RV系列、VC-1DivXXviDX264X265VP8VP9Sorenson VideoAVS

          常见的音频编码有:AACMP3AC-3

          编码之后,还需要将音频和视频合并在一个文件里,这就是封装

          所以相对的,播放一个视频,就需要解封装,解码,音视频同步喂给声卡和显卡进行播放。

          MediaSource做的就是这个工作,读取视频流,转换成浏览器能播放的格式。

          以下是flv.jsparseChunks部分内容。读取buffer,一个字节一个字节的根据标准进行解析。然后转码。

          js
          if (byteStart === 0) {
             // buffer with FLV header
             if (chunk.byteLength > 13) {
               let probeData = FLVDemuxer.probe(chunk);
          diff --git a/assets/cn_src_article_visual.md.Be4MMnab.js b/assets/cn_src_article_visual.md.DseWRfx9.js
          similarity index 99%
          rename from assets/cn_src_article_visual.md.Be4MMnab.js
          rename to assets/cn_src_article_visual.md.DseWRfx9.js
          index 86959f0c63..81bd040d4b 100644
          --- a/assets/cn_src_article_visual.md.Be4MMnab.js
          +++ b/assets/cn_src_article_visual.md.DseWRfx9.js
          @@ -1,4 +1,4 @@
          -import{_ as h,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"实现曲线","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/visual.md","filePath":"cn/src/article/visual.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/visual.md"};function p(t,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          在业务需求的持续演进中,遇到了高度定制化图表的需求,为了寻求解决方案,我们首先寻找了一下目前的开源图表库。确实能解决大部分的场景。但在一些定制化设计就难以实现,但 UI 层的定制化又是十分常见的。

          为了完美的复现设计愿景,我们打算自己去实现绘制,然后就发现 canvas,webGL,webGPU 的 API 实在太过于底层。开发起来几乎像是命令式编程。能实现需求,但代码就非常不好维护。

          因此需要设计一个对于当前业务场景的绘制引擎。

          一 系统设计

          系统设计的过程中,我们需要明确使用场景,约束条件,边界情况。描述出最主要实现的功能,将这些功能进行高层级的设计,分类,链接。

          有了具体要实现的功能模块后,我们就再根据功能模块,深入细节,讨论具体的实现。

          功能实现后,通过可扩展的设计原则,将这些重要的功能进行链接。

          最后是一些业务场景的具体实现。

          一个通用的绘制设计引擎,主要需要考虑以下几个方面:

          1. **层级管理:**对于 2D 图形来说,必然需要层级关系的处理,定义元素之间的堆叠顺序,如确保文字总是绘制在图表的上方。层级管理将确保视觉呈现符合预期。
          2. **组的管理:**将多个图形元素组织成一个整体(即“组”)。这样的设计使得对组进行整体移动、缩放或变形时,组内所有元素都能响应,简化了复杂场景下的操作和管理。
          3. **变换矩阵:**对一个图形组执行平移、旋转、缩放等变形操作,从而以动态和灵活的方式调整图形的展示效果。为了实现这些变形操作,变形操作类通常会采用矩阵变换的原理。通过维护一个变换矩阵,并在绘制图形组之前应用该矩阵,可以一次性完成所有变形操作的计算,提高绘制效率。
          4. **事件系统:**允许用户将事件监听器绑定到单个图形元素或整个组上。实现用户交互(如点击、拖动)。
          5. **应用层封装:**构建丰富的基础图形类库,提供便捷的 API 来绘制常见的几何形状,如矩形、圆形、多边形、曲线等。这些基础图形应支持自定义样式和属性,以满足多样化的设计需求。
          6. **扩展设计:**明确整个渲染过程的生命周期,并且允许开发者在对应的生命周期中插入自定义的代码,从而实现对渲染流程,事件处理,资源控制等方面的控制。

          在应用层,我们需要封装绘制引擎提供的底层功能,使其更加贴近业务需求。这包括提供易于使用的 API 接口、优化性能、处理异常和错误等。同时,通过持续的应用层反馈,不断优化和调整绘制引擎,确保其能够更好地服务于业务的发展。

          因此,在绘制引擎架构中,我们会首先实现一个节点类 Vertex,这个类代表了最原始的‘节点’的概念,所有可以被展示到 canvas 画布上的、各种类型的节点都会继承于这个类,这是一个抽象类,我们并不会直接实例化这个类。

          这个类上面挂载了‘节点’的各种通用属性,比如:父元素、透明度、旋转角度、缩放、平移、节点是否可见等。

          ts

          为了进行图形组的管理,会继续实现一个容器类 Container,这个类代表了‘组’的概念,它提供了添加子元素,移除子元素等的方法;后续的要被渲染的一些类 (如 Graphics,Text,Sprite 等) 会继承于这个类;这个类本身不会被渲染 (因为它只是一个‘组’,它本身没有内容可以渲染)。

          这个类继承于 Vertex 类,‘组’也算作‘节点’。

          Graphics 这个类会用来构建一些几何图形元素;它会继承 Container 类。

          在渲染引擎中,一切变换 (平移、旋转、缩放等) 都会转化成变换矩阵 (matrix),因为 canvas 只接受矩阵变换,虽然 canvas 为了开发的便捷,也提供了 ctx.rotate,ctx.scale 等操作,但是 canvas 中的这些操作会直接转换成变换矩阵,而不像 DOM 那样,有锚点的概念,所以 canvas 提供的 rotate,scale 等操作,和 DOM 提供的 rotate,scale 的表现是不一样的。

          Matrix 类将会提供各种各样的与矩阵操作相关的函数 (矩阵相乘,矩阵求逆等),任何变换的叠加都将会转换成 matrix,方便我们调用 canvas 的指令。

          Transform 类就类似 CSS 的 transform,它提供了一些更清晰、更符合人类直觉的变换,而不用直接使用矩阵变换,当然,这些变换最终会转换成矩阵变换。

          线性变换从几何直观有三个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变
          • 变换前是原点的,变换后依然是原点

          比如有一个二维基向量

          ',23),s("r-math",{latex:"\\left[\\begin{matrix}x & 0 \\\\0 & y \\\\ \\end{matrix}\\right]"},null,-1),s("p",null,"旋转矩阵:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(r) & -sin(r) \\\\ sin(r) & cos(r) \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"浏览器的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} 1 & tan(x) \\\\ tan(y) & 1 \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"pixijs 的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(y) & sin(x) \\\\ sin(y) & cos(x) \\\\ \\end{matrix} \\right]"},null,-1),i('

          平移操作:

          这里就需要仿射变换

          仿射变换从几何直观只有两个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变

          少了原点保持不变这一条。

          因此,平移不再是线性变化了,而是仿射变化。

          每一个矩阵变换都是线性变换,反正则不成立。

          仿射变换不能光通过矩阵乘法来实现,还得有加法。

          主要用途是,通过高纬度的线性变换,模拟低维度的仿射变换。从而把平移操作也数据化。

          实现曲线

          二阶贝塞尔曲线

          ',11),s("r-math",{latex:"B(t) = (1-t)^2 \\mathbf{P_0} + 2t(1-t) \\mathbf{P_1} + t^2 \\mathbf{P_2}, \\quad t \\in [0, 1]"},null,-1),i(`

          起点(P0):这是曲线开始的位置。在绘制过程中,曲线会精确地通过这个点。 控制点(P1):这个点是用来控制曲线形状和弯曲程度的。它不一定在曲线上,但会对曲线的走向产生重要影响。通过调整控制点的位置,可以改变曲线的弯曲程度和方向。 终点(P2):这是曲线结束的位置。同样地,曲线也会精确地通过这个点。

          二 基础图形库:

          常见的基础图形有:

          1. 椭圆
          2. 多边形
          3. 矩形
          4. 圆角矩形

          先实现一个基础类,然后其他的所有图形类都继承这个抽象类:

          ts
          export abstract class Shape {
          +import{_ as h,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"实现曲线","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/visual.md","filePath":"cn/src/article/visual.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/visual.md"};function p(t,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          在业务需求的持续演进中,遇到了高度定制化图表的需求,为了寻求解决方案,我们首先寻找了一下目前的开源图表库。确实能解决大部分的场景。但在一些定制化设计就难以实现,但 UI 层的定制化又是十分常见的。

          为了完美的复现设计愿景,我们打算自己去实现绘制,然后就发现 canvas,webGL,webGPU 的 API 实在太过于底层。开发起来几乎像是命令式编程。能实现需求,但代码就非常不好维护。

          因此需要设计一个对于当前业务场景的绘制引擎。

          一 系统设计

          系统设计的过程中,我们需要明确使用场景,约束条件,边界情况。描述出最主要实现的功能,将这些功能进行高层级的设计,分类,链接。

          有了具体要实现的功能模块后,我们就再根据功能模块,深入细节,讨论具体的实现。

          功能实现后,通过可扩展的设计原则,将这些重要的功能进行链接。

          最后是一些业务场景的具体实现。

          一个通用的绘制设计引擎,主要需要考虑以下几个方面:

          1. **层级管理:**对于 2D 图形来说,必然需要层级关系的处理,定义元素之间的堆叠顺序,如确保文字总是绘制在图表的上方。层级管理将确保视觉呈现符合预期。
          2. **组的管理:**将多个图形元素组织成一个整体(即“组”)。这样的设计使得对组进行整体移动、缩放或变形时,组内所有元素都能响应,简化了复杂场景下的操作和管理。
          3. **变换矩阵:**对一个图形组执行平移、旋转、缩放等变形操作,从而以动态和灵活的方式调整图形的展示效果。为了实现这些变形操作,变形操作类通常会采用矩阵变换的原理。通过维护一个变换矩阵,并在绘制图形组之前应用该矩阵,可以一次性完成所有变形操作的计算,提高绘制效率。
          4. **事件系统:**允许用户将事件监听器绑定到单个图形元素或整个组上。实现用户交互(如点击、拖动)。
          5. **应用层封装:**构建丰富的基础图形类库,提供便捷的 API 来绘制常见的几何形状,如矩形、圆形、多边形、曲线等。这些基础图形应支持自定义样式和属性,以满足多样化的设计需求。
          6. **扩展设计:**明确整个渲染过程的生命周期,并且允许开发者在对应的生命周期中插入自定义的代码,从而实现对渲染流程,事件处理,资源控制等方面的控制。

          在应用层,我们需要封装绘制引擎提供的底层功能,使其更加贴近业务需求。这包括提供易于使用的 API 接口、优化性能、处理异常和错误等。同时,通过持续的应用层反馈,不断优化和调整绘制引擎,确保其能够更好地服务于业务的发展。

          因此,在绘制引擎架构中,我们会首先实现一个节点类 Vertex,这个类代表了最原始的‘节点’的概念,所有可以被展示到 canvas 画布上的、各种类型的节点都会继承于这个类,这是一个抽象类,我们并不会直接实例化这个类。

          这个类上面挂载了‘节点’的各种通用属性,比如:父元素、透明度、旋转角度、缩放、平移、节点是否可见等。

          ts

          为了进行图形组的管理,会继续实现一个容器类 Container,这个类代表了‘组’的概念,它提供了添加子元素,移除子元素等的方法;后续的要被渲染的一些类 (如 Graphics,Text,Sprite 等) 会继承于这个类;这个类本身不会被渲染 (因为它只是一个‘组’,它本身没有内容可以渲染)。

          这个类继承于 Vertex 类,‘组’也算作‘节点’。

          Graphics 这个类会用来构建一些几何图形元素;它会继承 Container 类。

          在渲染引擎中,一切变换 (平移、旋转、缩放等) 都会转化成变换矩阵 (matrix),因为 canvas 只接受矩阵变换,虽然 canvas 为了开发的便捷,也提供了 ctx.rotate,ctx.scale 等操作,但是 canvas 中的这些操作会直接转换成变换矩阵,而不像 DOM 那样,有锚点的概念,所以 canvas 提供的 rotate,scale 等操作,和 DOM 提供的 rotate,scale 的表现是不一样的。

          Matrix 类将会提供各种各样的与矩阵操作相关的函数 (矩阵相乘,矩阵求逆等),任何变换的叠加都将会转换成 matrix,方便我们调用 canvas 的指令。

          Transform 类就类似 CSS 的 transform,它提供了一些更清晰、更符合人类直觉的变换,而不用直接使用矩阵变换,当然,这些变换最终会转换成矩阵变换。

          线性变换从几何直观有三个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变
          • 变换前是原点的,变换后依然是原点

          比如有一个二维基向量

          ',23),s("r-math",{latex:"\\left[\\begin{matrix}x & 0 \\\\0 & y \\\\ \\end{matrix}\\right]"},null,-1),s("p",null,"旋转矩阵:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(r) & -sin(r) \\\\ sin(r) & cos(r) \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"浏览器的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} 1 & tan(x) \\\\ tan(y) & 1 \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"pixijs 的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(y) & sin(x) \\\\ sin(y) & cos(x) \\\\ \\end{matrix} \\right]"},null,-1),i('

          平移操作:

          这里就需要仿射变换

          仿射变换从几何直观只有两个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变

          少了原点保持不变这一条。

          因此,平移不再是线性变化了,而是仿射变化。

          每一个矩阵变换都是线性变换,反正则不成立。

          仿射变换不能光通过矩阵乘法来实现,还得有加法。

          主要用途是,通过高纬度的线性变换,模拟低维度的仿射变换。从而把平移操作也数据化。

          实现曲线

          二阶贝塞尔曲线

          ',11),s("r-math",{latex:"B(t) = (1-t)^2 \\mathbf{P_0} + 2t(1-t) \\mathbf{P_1} + t^2 \\mathbf{P_2}, \\quad t \\in [0, 1]"},null,-1),i(`

          起点(P0):这是曲线开始的位置。在绘制过程中,曲线会精确地通过这个点。 控制点(P1):这个点是用来控制曲线形状和弯曲程度的。它不一定在曲线上,但会对曲线的走向产生重要影响。通过调整控制点的位置,可以改变曲线的弯曲程度和方向。 终点(P2):这是曲线结束的位置。同样地,曲线也会精确地通过这个点。

          二 基础图形库:

          常见的基础图形有:

          1. 椭圆
          2. 多边形
          3. 矩形
          4. 圆角矩形

          先实现一个基础类,然后其他的所有图形类都继承这个抽象类:

          ts
          export abstract class Shape {
             // 支持的所有几何图形都会继承自这个 Shape 基类
             public abstract type: ShapeType;
             constructor() {}
          diff --git a/assets/cn_src_article_visual.md.Be4MMnab.lean.js b/assets/cn_src_article_visual.md.DseWRfx9.lean.js
          similarity index 99%
          rename from assets/cn_src_article_visual.md.Be4MMnab.lean.js
          rename to assets/cn_src_article_visual.md.DseWRfx9.lean.js
          index 86959f0c63..81bd040d4b 100644
          --- a/assets/cn_src_article_visual.md.Be4MMnab.lean.js
          +++ b/assets/cn_src_article_visual.md.DseWRfx9.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as h,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"实现曲线","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/visual.md","filePath":"cn/src/article/visual.md","lastUpdated":1728129125000}'),k={name:"cn/src/article/visual.md"};function p(t,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          在业务需求的持续演进中,遇到了高度定制化图表的需求,为了寻求解决方案,我们首先寻找了一下目前的开源图表库。确实能解决大部分的场景。但在一些定制化设计就难以实现,但 UI 层的定制化又是十分常见的。

          为了完美的复现设计愿景,我们打算自己去实现绘制,然后就发现 canvas,webGL,webGPU 的 API 实在太过于底层。开发起来几乎像是命令式编程。能实现需求,但代码就非常不好维护。

          因此需要设计一个对于当前业务场景的绘制引擎。

          一 系统设计

          系统设计的过程中,我们需要明确使用场景,约束条件,边界情况。描述出最主要实现的功能,将这些功能进行高层级的设计,分类,链接。

          有了具体要实现的功能模块后,我们就再根据功能模块,深入细节,讨论具体的实现。

          功能实现后,通过可扩展的设计原则,将这些重要的功能进行链接。

          最后是一些业务场景的具体实现。

          一个通用的绘制设计引擎,主要需要考虑以下几个方面:

          1. **层级管理:**对于 2D 图形来说,必然需要层级关系的处理,定义元素之间的堆叠顺序,如确保文字总是绘制在图表的上方。层级管理将确保视觉呈现符合预期。
          2. **组的管理:**将多个图形元素组织成一个整体(即“组”)。这样的设计使得对组进行整体移动、缩放或变形时,组内所有元素都能响应,简化了复杂场景下的操作和管理。
          3. **变换矩阵:**对一个图形组执行平移、旋转、缩放等变形操作,从而以动态和灵活的方式调整图形的展示效果。为了实现这些变形操作,变形操作类通常会采用矩阵变换的原理。通过维护一个变换矩阵,并在绘制图形组之前应用该矩阵,可以一次性完成所有变形操作的计算,提高绘制效率。
          4. **事件系统:**允许用户将事件监听器绑定到单个图形元素或整个组上。实现用户交互(如点击、拖动)。
          5. **应用层封装:**构建丰富的基础图形类库,提供便捷的 API 来绘制常见的几何形状,如矩形、圆形、多边形、曲线等。这些基础图形应支持自定义样式和属性,以满足多样化的设计需求。
          6. **扩展设计:**明确整个渲染过程的生命周期,并且允许开发者在对应的生命周期中插入自定义的代码,从而实现对渲染流程,事件处理,资源控制等方面的控制。

          在应用层,我们需要封装绘制引擎提供的底层功能,使其更加贴近业务需求。这包括提供易于使用的 API 接口、优化性能、处理异常和错误等。同时,通过持续的应用层反馈,不断优化和调整绘制引擎,确保其能够更好地服务于业务的发展。

          因此,在绘制引擎架构中,我们会首先实现一个节点类 Vertex,这个类代表了最原始的‘节点’的概念,所有可以被展示到 canvas 画布上的、各种类型的节点都会继承于这个类,这是一个抽象类,我们并不会直接实例化这个类。

          这个类上面挂载了‘节点’的各种通用属性,比如:父元素、透明度、旋转角度、缩放、平移、节点是否可见等。

          ts

          为了进行图形组的管理,会继续实现一个容器类 Container,这个类代表了‘组’的概念,它提供了添加子元素,移除子元素等的方法;后续的要被渲染的一些类 (如 Graphics,Text,Sprite 等) 会继承于这个类;这个类本身不会被渲染 (因为它只是一个‘组’,它本身没有内容可以渲染)。

          这个类继承于 Vertex 类,‘组’也算作‘节点’。

          Graphics 这个类会用来构建一些几何图形元素;它会继承 Container 类。

          在渲染引擎中,一切变换 (平移、旋转、缩放等) 都会转化成变换矩阵 (matrix),因为 canvas 只接受矩阵变换,虽然 canvas 为了开发的便捷,也提供了 ctx.rotate,ctx.scale 等操作,但是 canvas 中的这些操作会直接转换成变换矩阵,而不像 DOM 那样,有锚点的概念,所以 canvas 提供的 rotate,scale 等操作,和 DOM 提供的 rotate,scale 的表现是不一样的。

          Matrix 类将会提供各种各样的与矩阵操作相关的函数 (矩阵相乘,矩阵求逆等),任何变换的叠加都将会转换成 matrix,方便我们调用 canvas 的指令。

          Transform 类就类似 CSS 的 transform,它提供了一些更清晰、更符合人类直觉的变换,而不用直接使用矩阵变换,当然,这些变换最终会转换成矩阵变换。

          线性变换从几何直观有三个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变
          • 变换前是原点的,变换后依然是原点

          比如有一个二维基向量

          ',23),s("r-math",{latex:"\\left[\\begin{matrix}x & 0 \\\\0 & y \\\\ \\end{matrix}\\right]"},null,-1),s("p",null,"旋转矩阵:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(r) & -sin(r) \\\\ sin(r) & cos(r) \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"浏览器的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} 1 & tan(x) \\\\ tan(y) & 1 \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"pixijs 的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(y) & sin(x) \\\\ sin(y) & cos(x) \\\\ \\end{matrix} \\right]"},null,-1),i('

          平移操作:

          这里就需要仿射变换

          仿射变换从几何直观只有两个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变

          少了原点保持不变这一条。

          因此,平移不再是线性变化了,而是仿射变化。

          每一个矩阵变换都是线性变换,反正则不成立。

          仿射变换不能光通过矩阵乘法来实现,还得有加法。

          主要用途是,通过高纬度的线性变换,模拟低维度的仿射变换。从而把平移操作也数据化。

          实现曲线

          二阶贝塞尔曲线

          ',11),s("r-math",{latex:"B(t) = (1-t)^2 \\mathbf{P_0} + 2t(1-t) \\mathbf{P_1} + t^2 \\mathbf{P_2}, \\quad t \\in [0, 1]"},null,-1),i(`

          起点(P0):这是曲线开始的位置。在绘制过程中,曲线会精确地通过这个点。 控制点(P1):这个点是用来控制曲线形状和弯曲程度的。它不一定在曲线上,但会对曲线的走向产生重要影响。通过调整控制点的位置,可以改变曲线的弯曲程度和方向。 终点(P2):这是曲线结束的位置。同样地,曲线也会精确地通过这个点。

          二 基础图形库:

          常见的基础图形有:

          1. 椭圆
          2. 多边形
          3. 矩形
          4. 圆角矩形

          先实现一个基础类,然后其他的所有图形类都继承这个抽象类:

          ts
          export abstract class Shape {
          +import{_ as h,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"实现曲线","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/article/visual.md","filePath":"cn/src/article/visual.md","lastUpdated":1728129344000}'),k={name:"cn/src/article/visual.md"};function p(t,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          在业务需求的持续演进中,遇到了高度定制化图表的需求,为了寻求解决方案,我们首先寻找了一下目前的开源图表库。确实能解决大部分的场景。但在一些定制化设计就难以实现,但 UI 层的定制化又是十分常见的。

          为了完美的复现设计愿景,我们打算自己去实现绘制,然后就发现 canvas,webGL,webGPU 的 API 实在太过于底层。开发起来几乎像是命令式编程。能实现需求,但代码就非常不好维护。

          因此需要设计一个对于当前业务场景的绘制引擎。

          一 系统设计

          系统设计的过程中,我们需要明确使用场景,约束条件,边界情况。描述出最主要实现的功能,将这些功能进行高层级的设计,分类,链接。

          有了具体要实现的功能模块后,我们就再根据功能模块,深入细节,讨论具体的实现。

          功能实现后,通过可扩展的设计原则,将这些重要的功能进行链接。

          最后是一些业务场景的具体实现。

          一个通用的绘制设计引擎,主要需要考虑以下几个方面:

          1. **层级管理:**对于 2D 图形来说,必然需要层级关系的处理,定义元素之间的堆叠顺序,如确保文字总是绘制在图表的上方。层级管理将确保视觉呈现符合预期。
          2. **组的管理:**将多个图形元素组织成一个整体(即“组”)。这样的设计使得对组进行整体移动、缩放或变形时,组内所有元素都能响应,简化了复杂场景下的操作和管理。
          3. **变换矩阵:**对一个图形组执行平移、旋转、缩放等变形操作,从而以动态和灵活的方式调整图形的展示效果。为了实现这些变形操作,变形操作类通常会采用矩阵变换的原理。通过维护一个变换矩阵,并在绘制图形组之前应用该矩阵,可以一次性完成所有变形操作的计算,提高绘制效率。
          4. **事件系统:**允许用户将事件监听器绑定到单个图形元素或整个组上。实现用户交互(如点击、拖动)。
          5. **应用层封装:**构建丰富的基础图形类库,提供便捷的 API 来绘制常见的几何形状,如矩形、圆形、多边形、曲线等。这些基础图形应支持自定义样式和属性,以满足多样化的设计需求。
          6. **扩展设计:**明确整个渲染过程的生命周期,并且允许开发者在对应的生命周期中插入自定义的代码,从而实现对渲染流程,事件处理,资源控制等方面的控制。

          在应用层,我们需要封装绘制引擎提供的底层功能,使其更加贴近业务需求。这包括提供易于使用的 API 接口、优化性能、处理异常和错误等。同时,通过持续的应用层反馈,不断优化和调整绘制引擎,确保其能够更好地服务于业务的发展。

          因此,在绘制引擎架构中,我们会首先实现一个节点类 Vertex,这个类代表了最原始的‘节点’的概念,所有可以被展示到 canvas 画布上的、各种类型的节点都会继承于这个类,这是一个抽象类,我们并不会直接实例化这个类。

          这个类上面挂载了‘节点’的各种通用属性,比如:父元素、透明度、旋转角度、缩放、平移、节点是否可见等。

          ts

          为了进行图形组的管理,会继续实现一个容器类 Container,这个类代表了‘组’的概念,它提供了添加子元素,移除子元素等的方法;后续的要被渲染的一些类 (如 Graphics,Text,Sprite 等) 会继承于这个类;这个类本身不会被渲染 (因为它只是一个‘组’,它本身没有内容可以渲染)。

          这个类继承于 Vertex 类,‘组’也算作‘节点’。

          Graphics 这个类会用来构建一些几何图形元素;它会继承 Container 类。

          在渲染引擎中,一切变换 (平移、旋转、缩放等) 都会转化成变换矩阵 (matrix),因为 canvas 只接受矩阵变换,虽然 canvas 为了开发的便捷,也提供了 ctx.rotate,ctx.scale 等操作,但是 canvas 中的这些操作会直接转换成变换矩阵,而不像 DOM 那样,有锚点的概念,所以 canvas 提供的 rotate,scale 等操作,和 DOM 提供的 rotate,scale 的表现是不一样的。

          Matrix 类将会提供各种各样的与矩阵操作相关的函数 (矩阵相乘,矩阵求逆等),任何变换的叠加都将会转换成 matrix,方便我们调用 canvas 的指令。

          Transform 类就类似 CSS 的 transform,它提供了一些更清晰、更符合人类直觉的变换,而不用直接使用矩阵变换,当然,这些变换最终会转换成矩阵变换。

          线性变换从几何直观有三个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变
          • 变换前是原点的,变换后依然是原点

          比如有一个二维基向量

          ',23),s("r-math",{latex:"\\left[\\begin{matrix}x & 0 \\\\0 & y \\\\ \\end{matrix}\\right]"},null,-1),s("p",null,"旋转矩阵:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(r) & -sin(r) \\\\ sin(r) & cos(r) \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"浏览器的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} 1 & tan(x) \\\\ tan(y) & 1 \\\\ \\end{matrix} \\right]"},null,-1),s("p",null,"pixijs 的 skew:",-1),s("r-math",{latex:"\\left[ \\begin{matrix} cos(y) & sin(x) \\\\ sin(y) & cos(x) \\\\ \\end{matrix} \\right]"},null,-1),i('

          平移操作:

          这里就需要仿射变换

          仿射变换从几何直观只有两个要点:

          • 变换前是直线的,变换后依然是直线
          • 直线比例保持不变

          少了原点保持不变这一条。

          因此,平移不再是线性变化了,而是仿射变化。

          每一个矩阵变换都是线性变换,反正则不成立。

          仿射变换不能光通过矩阵乘法来实现,还得有加法。

          主要用途是,通过高纬度的线性变换,模拟低维度的仿射变换。从而把平移操作也数据化。

          实现曲线

          二阶贝塞尔曲线

          ',11),s("r-math",{latex:"B(t) = (1-t)^2 \\mathbf{P_0} + 2t(1-t) \\mathbf{P_1} + t^2 \\mathbf{P_2}, \\quad t \\in [0, 1]"},null,-1),i(`

          起点(P0):这是曲线开始的位置。在绘制过程中,曲线会精确地通过这个点。 控制点(P1):这个点是用来控制曲线形状和弯曲程度的。它不一定在曲线上,但会对曲线的走向产生重要影响。通过调整控制点的位置,可以改变曲线的弯曲程度和方向。 终点(P2):这是曲线结束的位置。同样地,曲线也会精确地通过这个点。

          二 基础图形库:

          常见的基础图形有:

          1. 椭圆
          2. 多边形
          3. 矩形
          4. 圆角矩形

          先实现一个基础类,然后其他的所有图形类都继承这个抽象类:

          ts
          export abstract class Shape {
             // 支持的所有几何图形都会继承自这个 Shape 基类
             public abstract type: ShapeType;
             constructor() {}
          diff --git a/assets/cn_src_note_centos.md.BcqWCqVs.js b/assets/cn_src_note_centos.md.DNIN5Wz6.js
          similarity index 99%
          rename from assets/cn_src_note_centos.md.BcqWCqVs.js
          rename to assets/cn_src_note_centos.md.DNIN5Wz6.js
          index 1cfb0cddfd..0ac702650e 100644
          --- a/assets/cn_src_note_centos.md.BcqWCqVs.js
          +++ b/assets/cn_src_note_centos.md.DNIN5Wz6.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CentOS","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/centos.md","filePath":"cn/src/note/centos.md","lastUpdated":1728129125000}'),t={name:"cn/src/note/centos.md"};function p(l,s,h,k,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          CentOS

          查看当前系统的版本:

          sh
          cat /etc/centos-release

          YUM

          YUM 是 Yellowdog Updater, Modified 的缩写,虽然不是 CentOS 开发的,但已成为 CentOS 系统中常用的包管理工具。YUM 作为 RPM 的前端程序,解决了 RPM 软件包之间的依赖性问题,并提供了更加便捷的软件包管理方式。

          YUM 仓库源

          CentOS 8 的官方支持已经结束,但您仍然可以使用 CentOS Stream 8 或其他第三方仓库。如果您正在使用 CentOS 8,并且希望继续使用类似的仓库,可以考虑切换到 CentOS Stream 8。

          1.替换镜像源

          由于众所周知的原因,访问官方镜像会非常慢,所以

          修改 /etc/yum.repos.d/CentOS-AppStream.repo 文件(或相应的仓库配置文件),将 baseurl 或 mirrorlist 指向一个可用的源。

          我个人尝试,发现需要修改三个文件:

          sh
          /etc/yum.repos.d/CentOS-Base.repo
          +import{_ as i,o as a,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CentOS","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/centos.md","filePath":"cn/src/note/centos.md","lastUpdated":1728129344000}'),t={name:"cn/src/note/centos.md"};function p(l,s,h,k,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          CentOS

          查看当前系统的版本:

          sh
          cat /etc/centos-release

          YUM

          YUM 是 Yellowdog Updater, Modified 的缩写,虽然不是 CentOS 开发的,但已成为 CentOS 系统中常用的包管理工具。YUM 作为 RPM 的前端程序,解决了 RPM 软件包之间的依赖性问题,并提供了更加便捷的软件包管理方式。

          YUM 仓库源

          CentOS 8 的官方支持已经结束,但您仍然可以使用 CentOS Stream 8 或其他第三方仓库。如果您正在使用 CentOS 8,并且希望继续使用类似的仓库,可以考虑切换到 CentOS Stream 8。

          1.替换镜像源

          由于众所周知的原因,访问官方镜像会非常慢,所以

          修改 /etc/yum.repos.d/CentOS-AppStream.repo 文件(或相应的仓库配置文件),将 baseurl 或 mirrorlist 指向一个可用的源。

          我个人尝试,发现需要修改三个文件:

          sh
          /etc/yum.repos.d/CentOS-Base.repo
           /etc/yum.repos.d/CentOS-AppStream.repo
           /etc/yum.repos.d/CentOS-Extras.repo

          修改文件内容:

          sh
          [baseos]
           name=CentOS-8 - Base
          diff --git a/assets/cn_src_note_centos.md.BcqWCqVs.lean.js b/assets/cn_src_note_centos.md.DNIN5Wz6.lean.js
          similarity index 99%
          rename from assets/cn_src_note_centos.md.BcqWCqVs.lean.js
          rename to assets/cn_src_note_centos.md.DNIN5Wz6.lean.js
          index 1cfb0cddfd..0ac702650e 100644
          --- a/assets/cn_src_note_centos.md.BcqWCqVs.lean.js
          +++ b/assets/cn_src_note_centos.md.DNIN5Wz6.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CentOS","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/centos.md","filePath":"cn/src/note/centos.md","lastUpdated":1728129125000}'),t={name:"cn/src/note/centos.md"};function p(l,s,h,k,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          CentOS

          查看当前系统的版本:

          sh
          cat /etc/centos-release

          YUM

          YUM 是 Yellowdog Updater, Modified 的缩写,虽然不是 CentOS 开发的,但已成为 CentOS 系统中常用的包管理工具。YUM 作为 RPM 的前端程序,解决了 RPM 软件包之间的依赖性问题,并提供了更加便捷的软件包管理方式。

          YUM 仓库源

          CentOS 8 的官方支持已经结束,但您仍然可以使用 CentOS Stream 8 或其他第三方仓库。如果您正在使用 CentOS 8,并且希望继续使用类似的仓库,可以考虑切换到 CentOS Stream 8。

          1.替换镜像源

          由于众所周知的原因,访问官方镜像会非常慢,所以

          修改 /etc/yum.repos.d/CentOS-AppStream.repo 文件(或相应的仓库配置文件),将 baseurl 或 mirrorlist 指向一个可用的源。

          我个人尝试,发现需要修改三个文件:

          sh
          /etc/yum.repos.d/CentOS-Base.repo
          +import{_ as i,o as a,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CentOS","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/centos.md","filePath":"cn/src/note/centos.md","lastUpdated":1728129344000}'),t={name:"cn/src/note/centos.md"};function p(l,s,h,k,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          CentOS

          查看当前系统的版本:

          sh
          cat /etc/centos-release

          YUM

          YUM 是 Yellowdog Updater, Modified 的缩写,虽然不是 CentOS 开发的,但已成为 CentOS 系统中常用的包管理工具。YUM 作为 RPM 的前端程序,解决了 RPM 软件包之间的依赖性问题,并提供了更加便捷的软件包管理方式。

          YUM 仓库源

          CentOS 8 的官方支持已经结束,但您仍然可以使用 CentOS Stream 8 或其他第三方仓库。如果您正在使用 CentOS 8,并且希望继续使用类似的仓库,可以考虑切换到 CentOS Stream 8。

          1.替换镜像源

          由于众所周知的原因,访问官方镜像会非常慢,所以

          修改 /etc/yum.repos.d/CentOS-AppStream.repo 文件(或相应的仓库配置文件),将 baseurl 或 mirrorlist 指向一个可用的源。

          我个人尝试,发现需要修改三个文件:

          sh
          /etc/yum.repos.d/CentOS-Base.repo
           /etc/yum.repos.d/CentOS-AppStream.repo
           /etc/yum.repos.d/CentOS-Extras.repo

          修改文件内容:

          sh
          [baseos]
           name=CentOS-8 - Base
          diff --git a/assets/cn_src_note_docker.md.BeX3ffKU.js b/assets/cn_src_note_docker.md.Dwk9bEUN.js
          similarity index 99%
          rename from assets/cn_src_note_docker.md.BeX3ffKU.js
          rename to assets/cn_src_note_docker.md.Dwk9bEUN.js
          index 209f7cac2b..aec36ac0b2 100644
          --- a/assets/cn_src_note_docker.md.BeX3ffKU.js
          +++ b/assets/cn_src_note_docker.md.Dwk9bEUN.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"docker","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/docker.md","filePath":"cn/src/note/docker.md","lastUpdated":1728129125000}'),n={name:"cn/src/note/docker.md"};function h(t,s,p,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          docker

          查询 docker 是否在运行:

          1. 输入以下命令来查找 Docker 守护进程的 PID(进程 ID):
          sh
          ps -ef | grep docker

          如果命令返回了包含/usr/local/bin/dockerd(或类似路径)的行,并且 PID 列有数字显示,那么 Docker 守护进程正在运行。

          1. 使用 docker ps 命令

          虽然 docker ps 命令主要用于列出正在运行的容器,但如果 Docker 服务没有运行,该命令会返回一个错误消息。因此,你也可以通过运行该命令并观察输出来判断 Docker 服务是否启动。

          如果 Docker 服务正在运行,该命令将列出所有正在运行的容器(如果没有运行的容器,则可能只显示表头)。如果 Docker 服务没有运行,你将看到一个错误消息,如“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?”

          1. 使用 docker info 命令

          docker info 命令提供 Docker 系统的详细信息,包括容器数量、镜像数量、Docker 版本等。如果 Docker 服务正在运行,该命令将正常输出这些信息。

          如果 Docker 服务正在运行,你将看到一系列关于 Docker 系统的信息。如果 Docker 服务没有运行,你将看到一个错误消息,与 docker ps 命令类似。

          安装 docker 的网站:https://hub.docker.com/

          images

          Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作容器的模板。Docker 根据 image 文件生成的容器实例。同一个 image 文件,可以生成多个同时运行的容器实例

          image 是二进制文件。实际开发过程中,一个 image 文件往往通过继承另一个 image 文件,再加上一些个性化的设置而成。

          shell
          # 搜索镜像
          +import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"docker","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/docker.md","filePath":"cn/src/note/docker.md","lastUpdated":1728129344000}'),n={name:"cn/src/note/docker.md"};function h(t,s,p,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          docker

          查询 docker 是否在运行:

          1. 输入以下命令来查找 Docker 守护进程的 PID(进程 ID):
          sh
          ps -ef | grep docker

          如果命令返回了包含/usr/local/bin/dockerd(或类似路径)的行,并且 PID 列有数字显示,那么 Docker 守护进程正在运行。

          1. 使用 docker ps 命令

          虽然 docker ps 命令主要用于列出正在运行的容器,但如果 Docker 服务没有运行,该命令会返回一个错误消息。因此,你也可以通过运行该命令并观察输出来判断 Docker 服务是否启动。

          如果 Docker 服务正在运行,该命令将列出所有正在运行的容器(如果没有运行的容器,则可能只显示表头)。如果 Docker 服务没有运行,你将看到一个错误消息,如“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?”

          1. 使用 docker info 命令

          docker info 命令提供 Docker 系统的详细信息,包括容器数量、镜像数量、Docker 版本等。如果 Docker 服务正在运行,该命令将正常输出这些信息。

          如果 Docker 服务正在运行,你将看到一系列关于 Docker 系统的信息。如果 Docker 服务没有运行,你将看到一个错误消息,与 docker ps 命令类似。

          安装 docker 的网站:https://hub.docker.com/

          images

          Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作容器的模板。Docker 根据 image 文件生成的容器实例。同一个 image 文件,可以生成多个同时运行的容器实例

          image 是二进制文件。实际开发过程中,一个 image 文件往往通过继承另一个 image 文件,再加上一些个性化的设置而成。

          shell
          # 搜索镜像
           $ docker search [keywords]
           # 列出本机所有的image文件
           $docker image ls
          diff --git a/assets/cn_src_note_docker.md.BeX3ffKU.lean.js b/assets/cn_src_note_docker.md.Dwk9bEUN.lean.js
          similarity index 99%
          rename from assets/cn_src_note_docker.md.BeX3ffKU.lean.js
          rename to assets/cn_src_note_docker.md.Dwk9bEUN.lean.js
          index 209f7cac2b..aec36ac0b2 100644
          --- a/assets/cn_src_note_docker.md.BeX3ffKU.lean.js
          +++ b/assets/cn_src_note_docker.md.Dwk9bEUN.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"docker","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/docker.md","filePath":"cn/src/note/docker.md","lastUpdated":1728129125000}'),n={name:"cn/src/note/docker.md"};function h(t,s,p,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          docker

          查询 docker 是否在运行:

          1. 输入以下命令来查找 Docker 守护进程的 PID(进程 ID):
          sh
          ps -ef | grep docker

          如果命令返回了包含/usr/local/bin/dockerd(或类似路径)的行,并且 PID 列有数字显示,那么 Docker 守护进程正在运行。

          1. 使用 docker ps 命令

          虽然 docker ps 命令主要用于列出正在运行的容器,但如果 Docker 服务没有运行,该命令会返回一个错误消息。因此,你也可以通过运行该命令并观察输出来判断 Docker 服务是否启动。

          如果 Docker 服务正在运行,该命令将列出所有正在运行的容器(如果没有运行的容器,则可能只显示表头)。如果 Docker 服务没有运行,你将看到一个错误消息,如“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?”

          1. 使用 docker info 命令

          docker info 命令提供 Docker 系统的详细信息,包括容器数量、镜像数量、Docker 版本等。如果 Docker 服务正在运行,该命令将正常输出这些信息。

          如果 Docker 服务正在运行,你将看到一系列关于 Docker 系统的信息。如果 Docker 服务没有运行,你将看到一个错误消息,与 docker ps 命令类似。

          安装 docker 的网站:https://hub.docker.com/

          images

          Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作容器的模板。Docker 根据 image 文件生成的容器实例。同一个 image 文件,可以生成多个同时运行的容器实例

          image 是二进制文件。实际开发过程中,一个 image 文件往往通过继承另一个 image 文件,再加上一些个性化的设置而成。

          shell
          # 搜索镜像
          +import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"docker","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/docker.md","filePath":"cn/src/note/docker.md","lastUpdated":1728129344000}'),n={name:"cn/src/note/docker.md"};function h(t,s,p,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          docker

          查询 docker 是否在运行:

          1. 输入以下命令来查找 Docker 守护进程的 PID(进程 ID):
          sh
          ps -ef | grep docker

          如果命令返回了包含/usr/local/bin/dockerd(或类似路径)的行,并且 PID 列有数字显示,那么 Docker 守护进程正在运行。

          1. 使用 docker ps 命令

          虽然 docker ps 命令主要用于列出正在运行的容器,但如果 Docker 服务没有运行,该命令会返回一个错误消息。因此,你也可以通过运行该命令并观察输出来判断 Docker 服务是否启动。

          如果 Docker 服务正在运行,该命令将列出所有正在运行的容器(如果没有运行的容器,则可能只显示表头)。如果 Docker 服务没有运行,你将看到一个错误消息,如“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?”

          1. 使用 docker info 命令

          docker info 命令提供 Docker 系统的详细信息,包括容器数量、镜像数量、Docker 版本等。如果 Docker 服务正在运行,该命令将正常输出这些信息。

          如果 Docker 服务正在运行,你将看到一系列关于 Docker 系统的信息。如果 Docker 服务没有运行,你将看到一个错误消息,与 docker ps 命令类似。

          安装 docker 的网站:https://hub.docker.com/

          images

          Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作容器的模板。Docker 根据 image 文件生成的容器实例。同一个 image 文件,可以生成多个同时运行的容器实例

          image 是二进制文件。实际开发过程中,一个 image 文件往往通过继承另一个 image 文件,再加上一些个性化的设置而成。

          shell
          # 搜索镜像
           $ docker search [keywords]
           # 列出本机所有的image文件
           $docker image ls
          diff --git a/assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.js b/assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.js
          similarity index 99%
          rename from assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.js
          rename to assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.js
          index 55474239ee..fa7d7d61c6 100644
          --- a/assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.js
          +++ b/assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"克隆 Binaryen 仓库","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/libreoffice2wasm.md","filePath":"cn/src/note/libreoffice2wasm.md","lastUpdated":1728129125000}'),n={name:"cn/src/note/libreoffice2wasm.md"};function t(p,s,h,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          项目地址:

          sh
          git clone https://git.libreoffice.org/core

          github 地址:https://github.com/LibreOffice/core/blob/master/static/README.wasm.md

          容器化环境

          由于是 c/c++ 的项目,编译过程需要很多系统级别配置。如果当前设备不支持的话,不建议强行去适配。可以进行容器化处理。

          比如用一个最常见的服务器系统:

          sh
          docker image pull ubuntu

          下载完成后,就可以去构建一个服务了:

          sh
          docker container run -p 30105:30105 --name=alit -itd ubuntu /bin/bash

          构建并启动成功后,可以通过docker container ls去查看运行情况:

          sh
          CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS                      NAMES
          +import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"克隆 Binaryen 仓库","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/libreoffice2wasm.md","filePath":"cn/src/note/libreoffice2wasm.md","lastUpdated":1728129344000}'),n={name:"cn/src/note/libreoffice2wasm.md"};function t(p,s,h,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          项目地址:

          sh
          git clone https://git.libreoffice.org/core

          github 地址:https://github.com/LibreOffice/core/blob/master/static/README.wasm.md

          容器化环境

          由于是 c/c++ 的项目,编译过程需要很多系统级别配置。如果当前设备不支持的话,不建议强行去适配。可以进行容器化处理。

          比如用一个最常见的服务器系统:

          sh
          docker image pull ubuntu

          下载完成后,就可以去构建一个服务了:

          sh
          docker container run -p 30105:30105 --name=alit -itd ubuntu /bin/bash

          构建并启动成功后,可以通过docker container ls去查看运行情况:

          sh
          CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS                      NAMES
           efd831ab0ba8   ubuntu    "/bin/bash"   5 seconds ago   Up 4 seconds   0.0.0.0:30105->30105/tcp   alit

          再将项目移动到容器中:

          sh
          docker cp ./core alit:/home
           # 总的来说,项目还是非常大的
           # Successfully copied 3.59GB to alit:/home

          进入容器,进行操作。

          sh
          docker exec -it alit /bin/bash

          这样,就可以把开发构建环境和电脑系统隔离开,防止一些系统级别不安全的操作了。

          安装必要的依赖:

          sh
          dnf install -y git cmake python3 nodejs
          diff --git a/assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.lean.js b/assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.lean.js
          similarity index 99%
          rename from assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.lean.js
          rename to assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.lean.js
          index 55474239ee..fa7d7d61c6 100644
          --- a/assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.lean.js
          +++ b/assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"克隆 Binaryen 仓库","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/libreoffice2wasm.md","filePath":"cn/src/note/libreoffice2wasm.md","lastUpdated":1728129125000}'),n={name:"cn/src/note/libreoffice2wasm.md"};function t(p,s,h,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          项目地址:

          sh
          git clone https://git.libreoffice.org/core

          github 地址:https://github.com/LibreOffice/core/blob/master/static/README.wasm.md

          容器化环境

          由于是 c/c++ 的项目,编译过程需要很多系统级别配置。如果当前设备不支持的话,不建议强行去适配。可以进行容器化处理。

          比如用一个最常见的服务器系统:

          sh
          docker image pull ubuntu

          下载完成后,就可以去构建一个服务了:

          sh
          docker container run -p 30105:30105 --name=alit -itd ubuntu /bin/bash

          构建并启动成功后,可以通过docker container ls去查看运行情况:

          sh
          CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS                      NAMES
          +import{_ as i,o as a,c as e,a3 as l}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"克隆 Binaryen 仓库","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/libreoffice2wasm.md","filePath":"cn/src/note/libreoffice2wasm.md","lastUpdated":1728129344000}'),n={name:"cn/src/note/libreoffice2wasm.md"};function t(p,s,h,k,d,r){return a(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[l(`

          项目地址:

          sh
          git clone https://git.libreoffice.org/core

          github 地址:https://github.com/LibreOffice/core/blob/master/static/README.wasm.md

          容器化环境

          由于是 c/c++ 的项目,编译过程需要很多系统级别配置。如果当前设备不支持的话,不建议强行去适配。可以进行容器化处理。

          比如用一个最常见的服务器系统:

          sh
          docker image pull ubuntu

          下载完成后,就可以去构建一个服务了:

          sh
          docker container run -p 30105:30105 --name=alit -itd ubuntu /bin/bash

          构建并启动成功后,可以通过docker container ls去查看运行情况:

          sh
          CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS                      NAMES
           efd831ab0ba8   ubuntu    "/bin/bash"   5 seconds ago   Up 4 seconds   0.0.0.0:30105->30105/tcp   alit

          再将项目移动到容器中:

          sh
          docker cp ./core alit:/home
           # 总的来说,项目还是非常大的
           # Successfully copied 3.59GB to alit:/home

          进入容器,进行操作。

          sh
          docker exec -it alit /bin/bash

          这样,就可以把开发构建环境和电脑系统隔离开,防止一些系统级别不安全的操作了。

          安装必要的依赖:

          sh
          dnf install -y git cmake python3 nodejs
          diff --git a/assets/cn_src_note_ubuntu.md.C1lhavpN.js b/assets/cn_src_note_ubuntu.md.boe-scbt.js
          similarity index 98%
          rename from assets/cn_src_note_ubuntu.md.C1lhavpN.js
          rename to assets/cn_src_note_ubuntu.md.boe-scbt.js
          index ae1857f2c6..1e3076e122 100644
          --- a/assets/cn_src_note_ubuntu.md.C1lhavpN.js
          +++ b/assets/cn_src_note_ubuntu.md.boe-scbt.js
          @@ -1,3 +1,3 @@
          -import{_ as i,o as a,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"ubuntu","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/ubuntu.md","filePath":"cn/src/note/ubuntu.md","lastUpdated":1728129125000}'),h={name:"cn/src/note/ubuntu.md"};function l(k,s,e,p,F,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          ubuntu

          sh
          apt update
          +import{_ as i,o as a,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"ubuntu","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/ubuntu.md","filePath":"cn/src/note/ubuntu.md","lastUpdated":1728129344000}'),h={name:"cn/src/note/ubuntu.md"};function l(k,s,e,p,F,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          ubuntu

          sh
          apt update
           
           apt install openjdk-11-jdk  wget curl junit4 ant libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgtk-3-dev libglib2.0-dev libatk1.0-dev libcairo2-dev zip flex uuid-runtime bison libxrandr-dev libxrender-dev libxext-dev libnss3-dev libnspr4-dev libkrb5-dev python3 python3-pip libxml2-utils xsltproc libxslt1-dev gperf libfontconfig1-dev libcups2-dev gcc make autoconf pkg-config automake
          `,2)]))}const u=i(h,[["render",l]]);export{g as __pageData,u as default}; diff --git a/assets/cn_src_note_ubuntu.md.C1lhavpN.lean.js b/assets/cn_src_note_ubuntu.md.boe-scbt.lean.js similarity index 98% rename from assets/cn_src_note_ubuntu.md.C1lhavpN.lean.js rename to assets/cn_src_note_ubuntu.md.boe-scbt.lean.js index ae1857f2c6..1e3076e122 100644 --- a/assets/cn_src_note_ubuntu.md.C1lhavpN.lean.js +++ b/assets/cn_src_note_ubuntu.md.boe-scbt.lean.js @@ -1,3 +1,3 @@ -import{_ as i,o as a,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"ubuntu","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/ubuntu.md","filePath":"cn/src/note/ubuntu.md","lastUpdated":1728129125000}'),h={name:"cn/src/note/ubuntu.md"};function l(k,s,e,p,F,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          ubuntu

          sh
          apt update
          +import{_ as i,o as a,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"ubuntu","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/note/ubuntu.md","filePath":"cn/src/note/ubuntu.md","lastUpdated":1728129344000}'),h={name:"cn/src/note/ubuntu.md"};function l(k,s,e,p,F,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          ubuntu

          sh
          apt update
           
           apt install openjdk-11-jdk  wget curl junit4 ant libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgtk-3-dev libglib2.0-dev libatk1.0-dev libcairo2-dev zip flex uuid-runtime bison libxrandr-dev libxrender-dev libxext-dev libnss3-dev libnspr4-dev libkrb5-dev python3 python3-pip libxml2-utils xsltproc libxslt1-dev gperf libfontconfig1-dev libcups2-dev gcc make autoconf pkg-config automake
          `,2)]))}const u=i(h,[["render",l]]);export{g as __pageData,u as default}; diff --git a/assets/cn_src_ranui_button_index.md.B-YucaRE.js b/assets/cn_src_ranui_button_index.md.ByX0UCnc.js similarity index 99% rename from assets/cn_src_ranui_button_index.md.B-YucaRE.js rename to assets/cn_src_ranui_button_index.md.ByX0UCnc.js index 161ad9f290..70aff354c4 100644 --- a/assets/cn_src_ranui_button_index.md.B-YucaRE.js +++ b/assets/cn_src_ranui_button_index.md.ByX0UCnc.js @@ -1,4 +1,4 @@ -import{_ as a,o as n,c as l,a3 as s,j as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button 按钮","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/button/index.md","filePath":"cn/src/ranui/button/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranui/button/index.md"};function p(e,i,k,r,d,E){return n(),l("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s(`

          Button 按钮

          按钮用于开始一个即时操作。

          代码演示

          Button
          xml
           <r-button >Button</r-button>

          属性

          类型type

          按钮有四种类型

          主要按钮
          警告按钮
          文本按钮
          默认按钮
          xml
           <r-button type="primary">主要按钮</r-button>
          +import{_ as a,o as n,c as l,a3 as s,j as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button 按钮","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/button/index.md","filePath":"cn/src/ranui/button/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranui/button/index.md"};function p(e,i,k,r,d,E){return n(),l("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s(`

          Button 按钮

          按钮用于开始一个即时操作。

          代码演示

          Button
          xml
           <r-button >Button</r-button>

          属性

          类型type

          按钮有四种类型

          主要按钮
          警告按钮
          文本按钮
          默认按钮
          xml
           <r-button type="primary">主要按钮</r-button>
            <r-button type="warning">警告按钮</r-button>
            <r-button type="text">文本按钮</r-button>
            <r-button >默认按钮</r-button>

          不可用状态disabled

          添加 disabled 属性即可让按钮处于不可用状态,同时按钮样式也会改变。

          主要按钮
          警告按钮
          文本按钮
          默认按钮
          xml
           <r-button type="primary" disabled>主要按钮</r-button>
          diff --git a/assets/cn_src_ranui_button_index.md.B-YucaRE.lean.js b/assets/cn_src_ranui_button_index.md.ByX0UCnc.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_button_index.md.B-YucaRE.lean.js
          rename to assets/cn_src_ranui_button_index.md.ByX0UCnc.lean.js
          index 161ad9f290..70aff354c4 100644
          --- a/assets/cn_src_ranui_button_index.md.B-YucaRE.lean.js
          +++ b/assets/cn_src_ranui_button_index.md.ByX0UCnc.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as a,o as n,c as l,a3 as s,j as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button 按钮","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/button/index.md","filePath":"cn/src/ranui/button/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranui/button/index.md"};function p(e,i,k,r,d,E){return n(),l("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s(`

          Button 按钮

          按钮用于开始一个即时操作。

          代码演示

          Button
          xml
           <r-button >Button</r-button>

          属性

          类型type

          按钮有四种类型

          主要按钮
          警告按钮
          文本按钮
          默认按钮
          xml
           <r-button type="primary">主要按钮</r-button>
          +import{_ as a,o as n,c as l,a3 as s,j as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button 按钮","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/button/index.md","filePath":"cn/src/ranui/button/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranui/button/index.md"};function p(e,i,k,r,d,E){return n(),l("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s(`

          Button 按钮

          按钮用于开始一个即时操作。

          代码演示

          Button
          xml
           <r-button >Button</r-button>

          属性

          类型type

          按钮有四种类型

          主要按钮
          警告按钮
          文本按钮
          默认按钮
          xml
           <r-button type="primary">主要按钮</r-button>
            <r-button type="warning">警告按钮</r-button>
            <r-button type="text">文本按钮</r-button>
            <r-button >默认按钮</r-button>

          不可用状态disabled

          添加 disabled 属性即可让按钮处于不可用状态,同时按钮样式也会改变。

          主要按钮
          警告按钮
          文本按钮
          默认按钮
          xml
           <r-button type="primary" disabled>主要按钮</r-button>
          diff --git a/assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.js b/assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.js
          similarity index 99%
          rename from assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.js
          rename to assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.js
          index 365e774c93..c31e8ba795 100644
          --- a/assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.js
          +++ b/assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.js
          @@ -1,2 +1,2 @@
          -import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox 多选框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/checkbox/index.md","filePath":"cn/src/ranui/checkbox/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranui/checkbox/index.md"};function n(k,s,c,p,d,r){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

          CheckBox 多选框

          代码演示

          xml
           <r-checkbox ></r-checkbox>

          属性

          checked

          xml
           <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

          disabled

          xml
           <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

          事件event

          改变的时候触发。

          onchange

          ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
          xml
           <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
          +import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox 多选框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/checkbox/index.md","filePath":"cn/src/ranui/checkbox/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranui/checkbox/index.md"};function n(k,s,c,p,d,r){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

          CheckBox 多选框

          代码演示

          xml
           <r-checkbox ></r-checkbox>

          属性

          checked

          xml
           <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

          disabled

          xml
           <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

          事件event

          改变的时候触发。

          onchange

          ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
          xml
           <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
            <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
          `,1)]))}const g=e(l,[["render",n]]);export{E as __pageData,g as default}; diff --git a/assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.lean.js b/assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.lean.js similarity index 99% rename from assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.lean.js rename to assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.lean.js index 365e774c93..c31e8ba795 100644 --- a/assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.lean.js +++ b/assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.lean.js @@ -1,2 +1,2 @@ -import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox 多选框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/checkbox/index.md","filePath":"cn/src/ranui/checkbox/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranui/checkbox/index.md"};function n(k,s,c,p,d,r){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

          CheckBox 多选框

          代码演示

          xml
           <r-checkbox ></r-checkbox>

          属性

          checked

          xml
           <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

          disabled

          xml
           <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

          事件event

          改变的时候触发。

          onchange

          ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
          xml
           <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
          +import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox 多选框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/checkbox/index.md","filePath":"cn/src/ranui/checkbox/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranui/checkbox/index.md"};function n(k,s,c,p,d,r){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

          CheckBox 多选框

          代码演示

          xml
           <r-checkbox ></r-checkbox>

          属性

          checked

          xml
           <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

          disabled

          xml
           <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

          事件event

          改变的时候触发。

          onchange

          ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
          xml
           <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
            <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
          `,1)]))}const g=e(l,[["render",n]]);export{E as __pageData,g as default}; diff --git a/assets/cn_src_ranui_icon_index.md.7jVbQ82_.js b/assets/cn_src_ranui_icon_index.md.VgKq_hwc.js similarity index 99% rename from assets/cn_src_ranui_icon_index.md.7jVbQ82_.js rename to assets/cn_src_ranui_icon_index.md.VgKq_hwc.js index 3b200e52c4..c96cfc020b 100644 --- a/assets/cn_src_ranui_icon_index.md.7jVbQ82_.js +++ b/assets/cn_src_ranui_icon_index.md.VgKq_hwc.js @@ -1,4 +1,4 @@ -import{_ as p,o as E,c as r,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/icon/index.md","filePath":"cn/src/ranui/icon/index.md","lastUpdated":1728129125000}'),d={name:"cn/src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return E(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          Icon 图标

          语义化的矢量图形

          代码演示

          xml
           <r-icon name="lock"  ></r-icon>
          +import{_ as p,o as E,c as r,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/icon/index.md","filePath":"cn/src/ranui/icon/index.md","lastUpdated":1728129344000}'),d={name:"cn/src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return E(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          Icon 图标

          语义化的矢量图形

          代码演示

          xml
           <r-icon name="lock"  ></r-icon>
            <r-icon name="eye"  ></r-icon>
            <r-icon name="user"  ></r-icon>

          属性

          名称name

          根据名称选择不同的图标

          html
          <r-icon name="lock"></r-icon>
           <r-icon name="eye"></r-icon>
          diff --git a/assets/cn_src_ranui_icon_index.md.7jVbQ82_.lean.js b/assets/cn_src_ranui_icon_index.md.VgKq_hwc.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_icon_index.md.7jVbQ82_.lean.js
          rename to assets/cn_src_ranui_icon_index.md.VgKq_hwc.lean.js
          index 3b200e52c4..c96cfc020b 100644
          --- a/assets/cn_src_ranui_icon_index.md.7jVbQ82_.lean.js
          +++ b/assets/cn_src_ranui_icon_index.md.VgKq_hwc.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as p,o as E,c as r,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/icon/index.md","filePath":"cn/src/ranui/icon/index.md","lastUpdated":1728129125000}'),d={name:"cn/src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return E(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          Icon 图标

          语义化的矢量图形

          代码演示

          xml
           <r-icon name="lock"  ></r-icon>
          +import{_ as p,o as E,c as r,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/icon/index.md","filePath":"cn/src/ranui/icon/index.md","lastUpdated":1728129344000}'),d={name:"cn/src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return E(),r("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          Icon 图标

          语义化的矢量图形

          代码演示

          xml
           <r-icon name="lock"  ></r-icon>
            <r-icon name="eye"  ></r-icon>
            <r-icon name="user"  ></r-icon>

          属性

          名称name

          根据名称选择不同的图标

          html
          <r-icon name="lock"></r-icon>
           <r-icon name="eye"></r-icon>
          diff --git a/assets/cn_src_ranui_image_index.md.CEdjKNte.js b/assets/cn_src_ranui_image_index.md.DTxkaIXi.js
          similarity index 98%
          rename from assets/cn_src_ranui_image_index.md.CEdjKNte.js
          rename to assets/cn_src_ranui_image_index.md.DTxkaIXi.js
          index 606e67477b..1c19c96fd5 100644
          --- a/assets/cn_src_ranui_image_index.md.CEdjKNte.js
          +++ b/assets/cn_src_ranui_image_index.md.DTxkaIXi.js
          @@ -1 +1 @@
          -import{_ as a,o as E,c as i,a3 as Q,j as e}from"./chunks/framework.eq-HTtE3.js";const B=JSON.parse('{"title":"Image 图片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/image/index.md","filePath":"cn/src/ranui/image/index.md","lastUpdated":1728129125000}'),g={name:"cn/src/ranui/image/index.md"};function s(n,A,t,h,o,l){return E(),i("div",{"data-pagefind-body":!0},A[0]||(A[0]=[Q('

          Image 图片

          代码演示

          xml
           <r-img src="" fallback=""></r-img>

          属性

          图片加载地址src

          图片的地址

          图片加载失败fallback

          src配置的图片加载失败,兜底的图片地址,下面是默认加载失败图片

          ',8),e("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const r=a(g,[["render",s]]);export{B as __pageData,r as default}; +import{_ as a,o as E,c as i,a3 as Q,j as e}from"./chunks/framework.eq-HTtE3.js";const B=JSON.parse('{"title":"Image 图片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/image/index.md","filePath":"cn/src/ranui/image/index.md","lastUpdated":1728129344000}'),g={name:"cn/src/ranui/image/index.md"};function s(n,A,t,h,o,l){return E(),i("div",{"data-pagefind-body":!0},A[0]||(A[0]=[Q('

          Image 图片

          代码演示

          xml
           <r-img src="" fallback=""></r-img>

          属性

          图片加载地址src

          图片的地址

          图片加载失败fallback

          src配置的图片加载失败,兜底的图片地址,下面是默认加载失败图片

          ',8),e("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const r=a(g,[["render",s]]);export{B as __pageData,r as default}; diff --git a/assets/cn_src_ranui_image_index.md.CEdjKNte.lean.js b/assets/cn_src_ranui_image_index.md.DTxkaIXi.lean.js similarity index 98% rename from assets/cn_src_ranui_image_index.md.CEdjKNte.lean.js rename to assets/cn_src_ranui_image_index.md.DTxkaIXi.lean.js index 606e67477b..1c19c96fd5 100644 --- a/assets/cn_src_ranui_image_index.md.CEdjKNte.lean.js +++ b/assets/cn_src_ranui_image_index.md.DTxkaIXi.lean.js @@ -1 +1 @@ -import{_ as a,o as E,c as i,a3 as Q,j as e}from"./chunks/framework.eq-HTtE3.js";const B=JSON.parse('{"title":"Image 图片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/image/index.md","filePath":"cn/src/ranui/image/index.md","lastUpdated":1728129125000}'),g={name:"cn/src/ranui/image/index.md"};function s(n,A,t,h,o,l){return E(),i("div",{"data-pagefind-body":!0},A[0]||(A[0]=[Q('

          Image 图片

          代码演示

          xml
           <r-img src="" fallback=""></r-img>

          属性

          图片加载地址src

          图片的地址

          图片加载失败fallback

          src配置的图片加载失败,兜底的图片地址,下面是默认加载失败图片

          ',8),e("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const r=a(g,[["render",s]]);export{B as __pageData,r as default}; +import{_ as a,o as E,c as i,a3 as Q,j as e}from"./chunks/framework.eq-HTtE3.js";const B=JSON.parse('{"title":"Image 图片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/image/index.md","filePath":"cn/src/ranui/image/index.md","lastUpdated":1728129344000}'),g={name:"cn/src/ranui/image/index.md"};function s(n,A,t,h,o,l){return E(),i("div",{"data-pagefind-body":!0},A[0]||(A[0]=[Q('

          Image 图片

          代码演示

          xml
           <r-img src="" fallback=""></r-img>

          属性

          图片加载地址src

          图片的地址

          图片加载失败fallback

          src配置的图片加载失败,兜底的图片地址,下面是默认加载失败图片

          ',8),e("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const r=a(g,[["render",s]]);export{B as __pageData,r as default}; diff --git a/assets/cn_src_ranui_index.md.Cv0LhI4v.js b/assets/cn_src_ranui_index.md.DbAIrpTI.js similarity index 99% rename from assets/cn_src_ranui_index.md.Cv0LhI4v.js rename to assets/cn_src_ranui_index.md.DbAIrpTI.js index 8dfdf423a0..b2771c7b49 100644 --- a/assets/cn_src_ranui_index.md.Cv0LhI4v.js +++ b/assets/cn_src_ranui_index.md.DbAIrpTI.js @@ -1,4 +1,4 @@ -import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/index.md","filePath":"cn/src/ranui/index.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranui/index.md"};function p(k,a,r,E,d,g){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          ranui

          基于 Web Components开发方案

          特点

          1. 跨框架兼容: 与 React, Vue, Preact, SolidJS, Svelte 等兼容。可以和遵循 W3C 标准的任何 JavaScript 项目集成。
          2. 原生体验: 易于入门,像使用本地 div 标签,简化项目大小和减少学习成本。
          3. 模块化设计: 可选导入和全量导入,以增强可维护性和可伸缩性。
          4. 交互式丰富文档: 提供详细的交互式文档,并附有有效的示例子。
          5. 支持类型校验: 基于 TypeScript 构建,具有类型支持,确保代码的健壮性和可维护性。
          6. 持久和稳定: 与框架 (React/vue) 无关,避免破坏性的更新,并确保持续的项目运行。

          安装

          使用 npm:

          console
          npm install ranui --save

          引入方式

          支持按需导入,以显著减少包体积大小

          js
          import 'ranui/button';

          如果遇到样式问题,可以选择手动导入样式文件

          js
          import 'ranui/style';

          如果遇到类型问题,可以选择手动导入类型文件

          ts
          import 'ranui/types';

          或者

          ts
          import 'ranui/dist/typings';

          也支持全量导入

          ts
          import 'ranui';
          • ES module
          js
          import 'ranui';

          或者

          js
          import 'ranui/button';
          • UMD, IIFE, CJS
          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>

          使用方式

          它是基于Web Components的组件,你可以不用关注框架就可以使用它。

          在大多数情况下,您可以像使用本地 div 标签一样使用它

          下面是一些例子:

          • html
          • js
          • jsx
          • vue
          • tsx

          html

          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>
          +import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/index.md","filePath":"cn/src/ranui/index.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranui/index.md"};function p(k,a,r,E,d,g){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          ranui

          基于 Web Components开发方案

          特点

          1. 跨框架兼容: 与 React, Vue, Preact, SolidJS, Svelte 等兼容。可以和遵循 W3C 标准的任何 JavaScript 项目集成。
          2. 原生体验: 易于入门,像使用本地 div 标签,简化项目大小和减少学习成本。
          3. 模块化设计: 可选导入和全量导入,以增强可维护性和可伸缩性。
          4. 交互式丰富文档: 提供详细的交互式文档,并附有有效的示例子。
          5. 支持类型校验: 基于 TypeScript 构建,具有类型支持,确保代码的健壮性和可维护性。
          6. 持久和稳定: 与框架 (React/vue) 无关,避免破坏性的更新,并确保持续的项目运行。

          安装

          使用 npm:

          console
          npm install ranui --save

          引入方式

          支持按需导入,以显著减少包体积大小

          js
          import 'ranui/button';

          如果遇到样式问题,可以选择手动导入样式文件

          js
          import 'ranui/style';

          如果遇到类型问题,可以选择手动导入类型文件

          ts
          import 'ranui/types';

          或者

          ts
          import 'ranui/dist/typings';

          也支持全量导入

          ts
          import 'ranui';
          • ES module
          js
          import 'ranui';

          或者

          js
          import 'ranui/button';
          • UMD, IIFE, CJS
          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>

          使用方式

          它是基于Web Components的组件,你可以不用关注框架就可以使用它。

          在大多数情况下,您可以像使用本地 div 标签一样使用它

          下面是一些例子:

          • html
          • js
          • jsx
          • vue
          • tsx

          html

          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>
           
           <body>
             <r-button>Button</r-button>
          diff --git a/assets/cn_src_ranui_index.md.Cv0LhI4v.lean.js b/assets/cn_src_ranui_index.md.DbAIrpTI.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_index.md.Cv0LhI4v.lean.js
          rename to assets/cn_src_ranui_index.md.DbAIrpTI.lean.js
          index 8dfdf423a0..b2771c7b49 100644
          --- a/assets/cn_src_ranui_index.md.Cv0LhI4v.lean.js
          +++ b/assets/cn_src_ranui_index.md.DbAIrpTI.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/index.md","filePath":"cn/src/ranui/index.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranui/index.md"};function p(k,a,r,E,d,g){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          ranui

          基于 Web Components开发方案

          特点

          1. 跨框架兼容: 与 React, Vue, Preact, SolidJS, Svelte 等兼容。可以和遵循 W3C 标准的任何 JavaScript 项目集成。
          2. 原生体验: 易于入门,像使用本地 div 标签,简化项目大小和减少学习成本。
          3. 模块化设计: 可选导入和全量导入,以增强可维护性和可伸缩性。
          4. 交互式丰富文档: 提供详细的交互式文档,并附有有效的示例子。
          5. 支持类型校验: 基于 TypeScript 构建,具有类型支持,确保代码的健壮性和可维护性。
          6. 持久和稳定: 与框架 (React/vue) 无关,避免破坏性的更新,并确保持续的项目运行。

          安装

          使用 npm:

          console
          npm install ranui --save

          引入方式

          支持按需导入,以显著减少包体积大小

          js
          import 'ranui/button';

          如果遇到样式问题,可以选择手动导入样式文件

          js
          import 'ranui/style';

          如果遇到类型问题,可以选择手动导入类型文件

          ts
          import 'ranui/types';

          或者

          ts
          import 'ranui/dist/typings';

          也支持全量导入

          ts
          import 'ranui';
          • ES module
          js
          import 'ranui';

          或者

          js
          import 'ranui/button';
          • UMD, IIFE, CJS
          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>

          使用方式

          它是基于Web Components的组件,你可以不用关注框架就可以使用它。

          在大多数情况下,您可以像使用本地 div 标签一样使用它

          下面是一些例子:

          • html
          • js
          • jsx
          • vue
          • tsx

          html

          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>
          +import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/index.md","filePath":"cn/src/ranui/index.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranui/index.md"};function p(k,a,r,E,d,g){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          ranui

          基于 Web Components开发方案

          特点

          1. 跨框架兼容: 与 React, Vue, Preact, SolidJS, Svelte 等兼容。可以和遵循 W3C 标准的任何 JavaScript 项目集成。
          2. 原生体验: 易于入门,像使用本地 div 标签,简化项目大小和减少学习成本。
          3. 模块化设计: 可选导入和全量导入,以增强可维护性和可伸缩性。
          4. 交互式丰富文档: 提供详细的交互式文档,并附有有效的示例子。
          5. 支持类型校验: 基于 TypeScript 构建,具有类型支持,确保代码的健壮性和可维护性。
          6. 持久和稳定: 与框架 (React/vue) 无关,避免破坏性的更新,并确保持续的项目运行。

          安装

          使用 npm:

          console
          npm install ranui --save

          引入方式

          支持按需导入,以显著减少包体积大小

          js
          import 'ranui/button';

          如果遇到样式问题,可以选择手动导入样式文件

          js
          import 'ranui/style';

          如果遇到类型问题,可以选择手动导入类型文件

          ts
          import 'ranui/types';

          或者

          ts
          import 'ranui/dist/typings';

          也支持全量导入

          ts
          import 'ranui';
          • ES module
          js
          import 'ranui';

          或者

          js
          import 'ranui/button';
          • UMD, IIFE, CJS
          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>

          使用方式

          它是基于Web Components的组件,你可以不用关注框架就可以使用它。

          在大多数情况下,您可以像使用本地 div 标签一样使用它

          下面是一些例子:

          • html
          • js
          • jsx
          • vue
          • tsx

          html

          html
          <script src="./ranui/dist/umd/index.umd.cjs"></script>
           
           <body>
             <r-button>Button</r-button>
          diff --git a/assets/cn_src_ranui_input_index.md.CKpr9bUR.js b/assets/cn_src_ranui_input_index.md.Xj5VXLle.js
          similarity index 99%
          rename from assets/cn_src_ranui_input_index.md.CKpr9bUR.js
          rename to assets/cn_src_ranui_input_index.md.Xj5VXLle.js
          index 3384376fc4..c7f2732ea3 100644
          --- a/assets/cn_src_ranui_input_index.md.CKpr9bUR.js
          +++ b/assets/cn_src_ranui_input_index.md.Xj5VXLle.js
          @@ -1,4 +1,4 @@
          -import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"Input 输入框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/input/index.md","filePath":"cn/src/ranui/input/index.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranui/input/index.md"};function p(k,a,d,r,E,o){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          Input 输入框

          通过鼠标或键盘输入内容,是最基础的表单域的包装。

          代码演示

          输入框:
          xml
          <r-input></r-input>

          属性

          标签label

          提供类似于 Metiral Design 的输入体验。

          html
          <r-input label="user"></r-input>

          占位placeholder

          与原生placeholder一致。

          html
          <r-input placeholder="user"></r-input>

          禁用disabled

          通过disabled可以禁用输入框,禁用后该按钮上的事件失效。

          html
          <r-input label="user" disabled></r-input>

          value

          设置或返回输入框的value属性值。

          类型type

          目前支持passwordnumber这几种类型,设置后会出现额外的ui控件。

          密码输入框

          支持密码明文和密文切换。

          html
          <r-input icon="lock" type="password"></r-input>

          图标icon

          可以设置一个icon来表示标签标识。

          html
          <r-input icon="user"></r-input>

          数字输入框

          数字输入框,类似于原生input[type=number],支持minmaxstep属性,支持键盘上下键切换数字。

          html
          <r-input type="number" min="-10" max="10" step="0.5"></r-input>

          name 属性名

          跟 form 组件联动的时候有效,form 提交时收集的字段名字

          status 状态

          • error

          默认色值: #ff4d4f

          ',40),s("div",null,[s("r-input",{status:"error"})],-1),i('
          xml
          <r-input status="error"></r-input>
          • warning

          默认色值: #ff7875

          ',3),s("div",null,[s("r-input",{status:"warning"})],-1),i('
          xml
          <r-input  status="warning"></r-input>

          事件event

          常见的回调事件。

          onchange

          文本改变的时候触发。

          ',5),s("r-input",{onchange:"console.log(this.value)"},null,-1),i(`
          html
          <r-input onchange="func(this.value)"></r-input>
          js
          const input = document.createElement('r-input');
          +import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"Input 输入框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/input/index.md","filePath":"cn/src/ranui/input/index.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranui/input/index.md"};function p(k,a,d,r,E,o){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          Input 输入框

          通过鼠标或键盘输入内容,是最基础的表单域的包装。

          代码演示

          输入框:
          xml
          <r-input></r-input>

          属性

          标签label

          提供类似于 Metiral Design 的输入体验。

          html
          <r-input label="user"></r-input>

          占位placeholder

          与原生placeholder一致。

          html
          <r-input placeholder="user"></r-input>

          禁用disabled

          通过disabled可以禁用输入框,禁用后该按钮上的事件失效。

          html
          <r-input label="user" disabled></r-input>

          value

          设置或返回输入框的value属性值。

          类型type

          目前支持passwordnumber这几种类型,设置后会出现额外的ui控件。

          密码输入框

          支持密码明文和密文切换。

          html
          <r-input icon="lock" type="password"></r-input>

          图标icon

          可以设置一个icon来表示标签标识。

          html
          <r-input icon="user"></r-input>

          数字输入框

          数字输入框,类似于原生input[type=number],支持minmaxstep属性,支持键盘上下键切换数字。

          html
          <r-input type="number" min="-10" max="10" step="0.5"></r-input>

          name 属性名

          跟 form 组件联动的时候有效,form 提交时收集的字段名字

          status 状态

          • error

          默认色值: #ff4d4f

          ',40),s("div",null,[s("r-input",{status:"error"})],-1),i('
          xml
          <r-input status="error"></r-input>
          • warning

          默认色值: #ff7875

          ',3),s("div",null,[s("r-input",{status:"warning"})],-1),i('
          xml
          <r-input  status="warning"></r-input>

          事件event

          常见的回调事件。

          onchange

          文本改变的时候触发。

          ',5),s("r-input",{onchange:"console.log(this.value)"},null,-1),i(`
          html
          <r-input onchange="func(this.value)"></r-input>
          js
          const input = document.createElement('r-input');
           input.setAttribute('label', 'home');
           const func = (e) => {
             console.log(e);
          diff --git a/assets/cn_src_ranui_input_index.md.CKpr9bUR.lean.js b/assets/cn_src_ranui_input_index.md.Xj5VXLle.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_input_index.md.CKpr9bUR.lean.js
          rename to assets/cn_src_ranui_input_index.md.Xj5VXLle.lean.js
          index 3384376fc4..c7f2732ea3 100644
          --- a/assets/cn_src_ranui_input_index.md.CKpr9bUR.lean.js
          +++ b/assets/cn_src_ranui_input_index.md.Xj5VXLle.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"Input 输入框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/input/index.md","filePath":"cn/src/ranui/input/index.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranui/input/index.md"};function p(k,a,d,r,E,o){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          Input 输入框

          通过鼠标或键盘输入内容,是最基础的表单域的包装。

          代码演示

          输入框:
          xml
          <r-input></r-input>

          属性

          标签label

          提供类似于 Metiral Design 的输入体验。

          html
          <r-input label="user"></r-input>

          占位placeholder

          与原生placeholder一致。

          html
          <r-input placeholder="user"></r-input>

          禁用disabled

          通过disabled可以禁用输入框,禁用后该按钮上的事件失效。

          html
          <r-input label="user" disabled></r-input>

          value

          设置或返回输入框的value属性值。

          类型type

          目前支持passwordnumber这几种类型,设置后会出现额外的ui控件。

          密码输入框

          支持密码明文和密文切换。

          html
          <r-input icon="lock" type="password"></r-input>

          图标icon

          可以设置一个icon来表示标签标识。

          html
          <r-input icon="user"></r-input>

          数字输入框

          数字输入框,类似于原生input[type=number],支持minmaxstep属性,支持键盘上下键切换数字。

          html
          <r-input type="number" min="-10" max="10" step="0.5"></r-input>

          name 属性名

          跟 form 组件联动的时候有效,form 提交时收集的字段名字

          status 状态

          • error

          默认色值: #ff4d4f

          ',40),s("div",null,[s("r-input",{status:"error"})],-1),i('
          xml
          <r-input status="error"></r-input>
          • warning

          默认色值: #ff7875

          ',3),s("div",null,[s("r-input",{status:"warning"})],-1),i('
          xml
          <r-input  status="warning"></r-input>

          事件event

          常见的回调事件。

          onchange

          文本改变的时候触发。

          ',5),s("r-input",{onchange:"console.log(this.value)"},null,-1),i(`
          html
          <r-input onchange="func(this.value)"></r-input>
          js
          const input = document.createElement('r-input');
          +import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"Input 输入框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/input/index.md","filePath":"cn/src/ranui/input/index.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranui/input/index.md"};function p(k,a,d,r,E,o){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          Input 输入框

          通过鼠标或键盘输入内容,是最基础的表单域的包装。

          代码演示

          输入框:
          xml
          <r-input></r-input>

          属性

          标签label

          提供类似于 Metiral Design 的输入体验。

          html
          <r-input label="user"></r-input>

          占位placeholder

          与原生placeholder一致。

          html
          <r-input placeholder="user"></r-input>

          禁用disabled

          通过disabled可以禁用输入框,禁用后该按钮上的事件失效。

          html
          <r-input label="user" disabled></r-input>

          value

          设置或返回输入框的value属性值。

          类型type

          目前支持passwordnumber这几种类型,设置后会出现额外的ui控件。

          密码输入框

          支持密码明文和密文切换。

          html
          <r-input icon="lock" type="password"></r-input>

          图标icon

          可以设置一个icon来表示标签标识。

          html
          <r-input icon="user"></r-input>

          数字输入框

          数字输入框,类似于原生input[type=number],支持minmaxstep属性,支持键盘上下键切换数字。

          html
          <r-input type="number" min="-10" max="10" step="0.5"></r-input>

          name 属性名

          跟 form 组件联动的时候有效,form 提交时收集的字段名字

          status 状态

          • error

          默认色值: #ff4d4f

          ',40),s("div",null,[s("r-input",{status:"error"})],-1),i('
          xml
          <r-input status="error"></r-input>
          • warning

          默认色值: #ff7875

          ',3),s("div",null,[s("r-input",{status:"warning"})],-1),i('
          xml
          <r-input  status="warning"></r-input>

          事件event

          常见的回调事件。

          onchange

          文本改变的时候触发。

          ',5),s("r-input",{onchange:"console.log(this.value)"},null,-1),i(`
          html
          <r-input onchange="func(this.value)"></r-input>
          js
          const input = document.createElement('r-input');
           input.setAttribute('label', 'home');
           const func = (e) => {
             console.log(e);
          diff --git a/assets/cn_src_ranui_loading_index.md.CcRWELn0.js b/assets/cn_src_ranui_loading_index.md.Cd9-GnXr.js
          similarity index 98%
          rename from assets/cn_src_ranui_loading_index.md.CcRWELn0.js
          rename to assets/cn_src_ranui_loading_index.md.Cd9-GnXr.js
          index 4d5e108ada..99f7678022 100644
          --- a/assets/cn_src_ranui_loading_index.md.CcRWELn0.js
          +++ b/assets/cn_src_ranui_loading_index.md.Cd9-GnXr.js
          @@ -1,4 +1,4 @@
          -import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/loading/index.md","filePath":"cn/src/ranui/loading/index.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

          Loading

          一些好看的 loading

          Code demo

          xml
          <r-loading name="circle"></r-loading>

          属性

          name

          这里有很多好看的 loading,可供选择

          xml
          <r-loading name="double-bounce"></r-loading>
          +import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/loading/index.md","filePath":"cn/src/ranui/loading/index.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

          Loading

          一些好看的 loading

          Code demo

          xml
          <r-loading name="circle"></r-loading>

          属性

          name

          这里有很多好看的 loading,可供选择

          xml
          <r-loading name="double-bounce"></r-loading>
           <r-loading name="rotate"></r-loading>
           <r-loading name="stretch"></r-loading>
           <r-loading name="cube"></r-loading>

          Loading list

          `,14)),l(a)]))}});export{o as __pageData,g as default}; diff --git a/assets/cn_src_ranui_loading_index.md.CcRWELn0.lean.js b/assets/cn_src_ranui_loading_index.md.Cd9-GnXr.lean.js similarity index 98% rename from assets/cn_src_ranui_loading_index.md.CcRWELn0.lean.js rename to assets/cn_src_ranui_loading_index.md.Cd9-GnXr.lean.js index 4d5e108ada..99f7678022 100644 --- a/assets/cn_src_ranui_loading_index.md.CcRWELn0.lean.js +++ b/assets/cn_src_ranui_loading_index.md.Cd9-GnXr.lean.js @@ -1,4 +1,4 @@ -import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/loading/index.md","filePath":"cn/src/ranui/loading/index.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

          Loading

          一些好看的 loading

          Code demo

          xml
          <r-loading name="circle"></r-loading>

          属性

          name

          这里有很多好看的 loading,可供选择

          xml
          <r-loading name="double-bounce"></r-loading>
          +import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/loading/index.md","filePath":"cn/src/ranui/loading/index.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

          Loading

          一些好看的 loading

          Code demo

          xml
          <r-loading name="circle"></r-loading>

          属性

          name

          这里有很多好看的 loading,可供选择

          xml
          <r-loading name="double-bounce"></r-loading>
           <r-loading name="rotate"></r-loading>
           <r-loading name="stretch"></r-loading>
           <r-loading name="cube"></r-loading>

          Loading list

          `,14)),l(a)]))}});export{o as __pageData,g as default}; diff --git a/assets/cn_src_ranui_math_index.md.nmeuJ7af.js b/assets/cn_src_ranui_math_index.md.vkrzCmwa.js similarity index 97% rename from assets/cn_src_ranui_math_index.md.nmeuJ7af.js rename to assets/cn_src_ranui_math_index.md.vkrzCmwa.js index 6aabdabd5e..8317365086 100644 --- a/assets/cn_src_ranui_math_index.md.nmeuJ7af.js +++ b/assets/cn_src_ranui_math_index.md.vkrzCmwa.js @@ -1 +1 @@ -import{_ as e,o as l,c as n,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"math 数学公式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/math/index.md","filePath":"cn/src/ranui/math/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranui/math/index.md"};function r(d,t,p,k,o,c){return l(),n("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math-数学公式",tabindex:"-1"},[s("math 数学公式 "),a("a",{class:"header-anchor",href:"#math-数学公式","aria-label":'Permalink to "math 数学公式"'},"​")],-1),a("p",null,[s("在 "),a("code",null,"HTML"),s(" 页面中高质量展示 "),a("code",null,"LaTeX"),s(" 数学公式")],-1),a("h2",{id:"代码演示",tabindex:"-1"},[s("代码演示 "),a("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
          xml
          <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

          属性

          latex string

          ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
          xml
            <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
          ',1)]))}const m=e(h,[["render",r]]);export{E as __pageData,m as default}; +import{_ as e,o as l,c as n,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"math 数学公式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/math/index.md","filePath":"cn/src/ranui/math/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranui/math/index.md"};function r(d,t,p,k,o,c){return l(),n("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math-数学公式",tabindex:"-1"},[s("math 数学公式 "),a("a",{class:"header-anchor",href:"#math-数学公式","aria-label":'Permalink to "math 数学公式"'},"​")],-1),a("p",null,[s("在 "),a("code",null,"HTML"),s(" 页面中高质量展示 "),a("code",null,"LaTeX"),s(" 数学公式")],-1),a("h2",{id:"代码演示",tabindex:"-1"},[s("代码演示 "),a("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
          xml
          <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

          属性

          latex string

          ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
          xml
            <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
          ',1)]))}const m=e(h,[["render",r]]);export{E as __pageData,m as default}; diff --git a/assets/cn_src_ranui_math_index.md.nmeuJ7af.lean.js b/assets/cn_src_ranui_math_index.md.vkrzCmwa.lean.js similarity index 97% rename from assets/cn_src_ranui_math_index.md.nmeuJ7af.lean.js rename to assets/cn_src_ranui_math_index.md.vkrzCmwa.lean.js index 6aabdabd5e..8317365086 100644 --- a/assets/cn_src_ranui_math_index.md.nmeuJ7af.lean.js +++ b/assets/cn_src_ranui_math_index.md.vkrzCmwa.lean.js @@ -1 +1 @@ -import{_ as e,o as l,c as n,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"math 数学公式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/math/index.md","filePath":"cn/src/ranui/math/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranui/math/index.md"};function r(d,t,p,k,o,c){return l(),n("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math-数学公式",tabindex:"-1"},[s("math 数学公式 "),a("a",{class:"header-anchor",href:"#math-数学公式","aria-label":'Permalink to "math 数学公式"'},"​")],-1),a("p",null,[s("在 "),a("code",null,"HTML"),s(" 页面中高质量展示 "),a("code",null,"LaTeX"),s(" 数学公式")],-1),a("h2",{id:"代码演示",tabindex:"-1"},[s("代码演示 "),a("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
          xml
          <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

          属性

          latex string

          ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
          xml
            <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
          ',1)]))}const m=e(h,[["render",r]]);export{E as __pageData,m as default}; +import{_ as e,o as l,c as n,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"math 数学公式","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/math/index.md","filePath":"cn/src/ranui/math/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranui/math/index.md"};function r(d,t,p,k,o,c){return l(),n("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math-数学公式",tabindex:"-1"},[s("math 数学公式 "),a("a",{class:"header-anchor",href:"#math-数学公式","aria-label":'Permalink to "math 数学公式"'},"​")],-1),a("p",null,[s("在 "),a("code",null,"HTML"),s(" 页面中高质量展示 "),a("code",null,"LaTeX"),s(" 数学公式")],-1),a("h2",{id:"代码演示",tabindex:"-1"},[s("代码演示 "),a("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
          xml
          <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

          属性

          latex string

          ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
          xml
            <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
          ',1)]))}const m=e(h,[["render",r]]);export{E as __pageData,m as default}; diff --git a/assets/cn_src_ranui_message_index.md.OWf7PDqT.js b/assets/cn_src_ranui_message_index.md.DQaOXw4A.js similarity index 99% rename from assets/cn_src_ranui_message_index.md.OWf7PDqT.js rename to assets/cn_src_ranui_message_index.md.DQaOXw4A.js index b605d23a8f..07cb2d6efa 100644 --- a/assets/cn_src_ranui_message_index.md.OWf7PDqT.js +++ b/assets/cn_src_ranui_message_index.md.DQaOXw4A.js @@ -1,4 +1,4 @@ -import{_ as e,o as n,c as h,j as s,a,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"message 全局提示","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/message/index.md","filePath":"cn/src/ranui/message/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranui/message/index.md"};function k(p,i,r,d,o,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message-全局提示",tabindex:"-1"},[a("message 全局提示 "),s("a",{class:"header-anchor",href:"#message-全局提示","aria-label":'Permalink to "message 全局提示"'},"​")],-1),s("p",null,"全局展示操作反馈信息。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[a("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('这是一条提示')"},"点击触发全局提示")],-1),t('
          xml
          <r-button type="primary" onclick="message.info('这是一条提示')">点击触发全局提示</r-button>

          属性

          类型type

          不同的提示类型

          ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('这是一条提示')"},"信息提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('这是一条提示')"},"警告提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('这是一条提示')"},"错误提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('这是一条提示')"},"成功提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('这是一条提示')"},"toast提示")],-1),t(`
          html
          <r-button onclick="message.info('这是一条提示')">信息提示</r-button>
          +import{_ as e,o as n,c as h,j as s,a,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"message 全局提示","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/message/index.md","filePath":"cn/src/ranui/message/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranui/message/index.md"};function k(p,i,r,d,o,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message-全局提示",tabindex:"-1"},[a("message 全局提示 "),s("a",{class:"header-anchor",href:"#message-全局提示","aria-label":'Permalink to "message 全局提示"'},"​")],-1),s("p",null,"全局展示操作反馈信息。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[a("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('这是一条提示')"},"点击触发全局提示")],-1),t('
          xml
          <r-button type="primary" onclick="message.info('这是一条提示')">点击触发全局提示</r-button>

          属性

          类型type

          不同的提示类型

          ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('这是一条提示')"},"信息提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('这是一条提示')"},"警告提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('这是一条提示')"},"错误提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('这是一条提示')"},"成功提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('这是一条提示')"},"toast提示")],-1),t(`
          html
          <r-button onclick="message.info('这是一条提示')">信息提示</r-button>
           <r-button onclick="message.warning('这是一条提示')">警告提示</r-button>
           <r-button onclick="message.error('这是一条提示')">错误提示</r-button>
           <r-button onclick="message.success('这是一条提示')">成功提示</r-button>
          diff --git a/assets/cn_src_ranui_message_index.md.OWf7PDqT.lean.js b/assets/cn_src_ranui_message_index.md.DQaOXw4A.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_message_index.md.OWf7PDqT.lean.js
          rename to assets/cn_src_ranui_message_index.md.DQaOXw4A.lean.js
          index b605d23a8f..07cb2d6efa 100644
          --- a/assets/cn_src_ranui_message_index.md.OWf7PDqT.lean.js
          +++ b/assets/cn_src_ranui_message_index.md.DQaOXw4A.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as e,o as n,c as h,j as s,a,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"message 全局提示","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/message/index.md","filePath":"cn/src/ranui/message/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranui/message/index.md"};function k(p,i,r,d,o,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message-全局提示",tabindex:"-1"},[a("message 全局提示 "),s("a",{class:"header-anchor",href:"#message-全局提示","aria-label":'Permalink to "message 全局提示"'},"​")],-1),s("p",null,"全局展示操作反馈信息。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[a("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('这是一条提示')"},"点击触发全局提示")],-1),t('
          xml
          <r-button type="primary" onclick="message.info('这是一条提示')">点击触发全局提示</r-button>

          属性

          类型type

          不同的提示类型

          ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('这是一条提示')"},"信息提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('这是一条提示')"},"警告提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('这是一条提示')"},"错误提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('这是一条提示')"},"成功提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('这是一条提示')"},"toast提示")],-1),t(`
          html
          <r-button onclick="message.info('这是一条提示')">信息提示</r-button>
          +import{_ as e,o as n,c as h,j as s,a,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"message 全局提示","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/message/index.md","filePath":"cn/src/ranui/message/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranui/message/index.md"};function k(p,i,r,d,o,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message-全局提示",tabindex:"-1"},[a("message 全局提示 "),s("a",{class:"header-anchor",href:"#message-全局提示","aria-label":'Permalink to "message 全局提示"'},"​")],-1),s("p",null,"全局展示操作反馈信息。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[a("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('这是一条提示')"},"点击触发全局提示")],-1),t('
          xml
          <r-button type="primary" onclick="message.info('这是一条提示')">点击触发全局提示</r-button>

          属性

          类型type

          不同的提示类型

          ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('这是一条提示')"},"信息提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('这是一条提示')"},"警告提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('这是一条提示')"},"错误提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('这是一条提示')"},"成功提示")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('这是一条提示')"},"toast提示")],-1),t(`
          html
          <r-button onclick="message.info('这是一条提示')">信息提示</r-button>
           <r-button onclick="message.warning('这是一条提示')">警告提示</r-button>
           <r-button onclick="message.error('这是一条提示')">错误提示</r-button>
           <r-button onclick="message.success('这是一条提示')">成功提示</r-button>
          diff --git a/assets/cn_src_ranui_modal_index.md.C5SgGuYU.js b/assets/cn_src_ranui_modal_index.md.Ct_5BTpn.js
          similarity index 84%
          rename from assets/cn_src_ranui_modal_index.md.C5SgGuYU.js
          rename to assets/cn_src_ranui_modal_index.md.Ct_5BTpn.js
          index d85e8c137c..13ecce18c1 100644
          --- a/assets/cn_src_ranui_modal_index.md.C5SgGuYU.js
          +++ b/assets/cn_src_ranui_modal_index.md.Ct_5BTpn.js
          @@ -1 +1 @@
          -import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/modal/index.md","filePath":"cn/src/ranui/modal/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/modal/index.md"};function r(c,o,s,d,i,m){return a(),t("div")}const _=e(n,[["render",r]]);export{l as __pageData,_ as default};
          +import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/modal/index.md","filePath":"cn/src/ranui/modal/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/modal/index.md"};function r(c,o,s,d,i,m){return a(),t("div")}const _=e(n,[["render",r]]);export{l as __pageData,_ as default};
          diff --git a/assets/cn_src_ranui_modal_index.md.C5SgGuYU.lean.js b/assets/cn_src_ranui_modal_index.md.Ct_5BTpn.lean.js
          similarity index 84%
          rename from assets/cn_src_ranui_modal_index.md.C5SgGuYU.lean.js
          rename to assets/cn_src_ranui_modal_index.md.Ct_5BTpn.lean.js
          index d85e8c137c..13ecce18c1 100644
          --- a/assets/cn_src_ranui_modal_index.md.C5SgGuYU.lean.js
          +++ b/assets/cn_src_ranui_modal_index.md.Ct_5BTpn.lean.js
          @@ -1 +1 @@
          -import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/modal/index.md","filePath":"cn/src/ranui/modal/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/modal/index.md"};function r(c,o,s,d,i,m){return a(),t("div")}const _=e(n,[["render",r]]);export{l as __pageData,_ as default};
          +import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/modal/index.md","filePath":"cn/src/ranui/modal/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/modal/index.md"};function r(c,o,s,d,i,m){return a(),t("div")}const _=e(n,[["render",r]]);export{l as __pageData,_ as default};
          diff --git a/assets/cn_src_ranui_player_index.md.DXjOFy1h.js b/assets/cn_src_ranui_player_index.md.ClG2z7Rd.js
          similarity index 99%
          rename from assets/cn_src_ranui_player_index.md.DXjOFy1h.js
          rename to assets/cn_src_ranui_player_index.md.ClG2z7Rd.js
          index c793b7713a..605f89be3e 100644
          --- a/assets/cn_src_ranui_player_index.md.DXjOFy1h.js
          +++ b/assets/cn_src_ranui_player_index.md.ClG2z7Rd.js
          @@ -1 +1 @@
          -import{_ as e,o as d,c as a,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player 视频播放器","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/player/index.md","filePath":"cn/src/ranui/player/index.md","lastUpdated":1728129125000}'),i={name:"cn/src/ranui/player/index.md"};function l(o,t,n,s,h,c){return d(),a("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          r-player 视频播放器

          基于hlsjsweb components,让原生的标签r-player拥有统一的视频控件。 不采用new Player(options)的方式挂载到指定dom,视图的归视图,逻辑的归逻辑,所见及所得,更加直观。

          1. 可拖拽进度条
          2. 音量控制
          3. 根据当前带宽自适应码率切换
          4. 手动清晰度切换
          5. 倍速播放
          6. 样式自定义覆盖
          7. hls协议标准加密视频播放
          8. 基于原生开发,可在所有框架运行,统一跨框架情况
          9. 各浏览器控件统一

          代码演示

          xml
            <r-player src="/ran/hls/example.m3u8"></r-player>

          属性

          src

          视频的资源地址

          volume

          设置初始音量,默认 0.5

          currentTime

          设置初始播放时间,默认从头开始播放

          playbackRate

          设置倍速,默认 1.0

          debug

          控制台会打印输出一些信息

          事件event

          onchange

          监听任何播放器发生的变化,返回的值如下。

          可通过这个方法获得播放器的实例

          活着通过type判断不同的事件类型,进行不同的操作

          属性说明类型
          type发生变化的事件类型string
          data事件的值Object
          currentTime播放的当前时间number
          duration视频的总时长number
          tag播放器的实例Element

          其中type类型有

          名称说明
          canplay浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。
          canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。
          completeOfflineAudioContext 渲染完成。
          durationchangeduration 属性的值改变时触发。
          emptied媒体内容变为空;例如,当这个 media 已经加载完成(或者部分加载完成),则发送此事件,并调用 load() 方法重新加载它。
          ended视频停止播放,因为 media 已经到达结束点。
          loadedmetadata已加载元数据。
          progress在浏览器加载资源时周期性触发。
          ratechange播放速率发生变化。
          seeked跳帧(seek)操作完成。
          seeking跳帧(seek)操作开始。
          stalled用户代理(user agent)正在尝试获取媒体数据,但数据意外未出现。
          suspend媒体数据加载已暂停。
          loadeddatamedia 中的首帧已经完成加载。
          timeupdatecurrentTime 属性指定的时间发生变化。
          volumechange音量发生变化。
          waiting由于暂时缺少数据,播放已停止。
          play播放已开始。
          playing由于缺乏数据而暂停或延迟后,播放准备开始。
          pause播放已暂停。
          volume音量发生变化。
          fullscreen触发全屏事件
          ',25)]))}const m=e(i,[["render",l]]);export{u as __pageData,m as default}; +import{_ as e,o as d,c as a,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player 视频播放器","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/player/index.md","filePath":"cn/src/ranui/player/index.md","lastUpdated":1728129344000}'),i={name:"cn/src/ranui/player/index.md"};function l(o,t,n,s,h,c){return d(),a("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          r-player 视频播放器

          基于hlsjsweb components,让原生的标签r-player拥有统一的视频控件。 不采用new Player(options)的方式挂载到指定dom,视图的归视图,逻辑的归逻辑,所见及所得,更加直观。

          1. 可拖拽进度条
          2. 音量控制
          3. 根据当前带宽自适应码率切换
          4. 手动清晰度切换
          5. 倍速播放
          6. 样式自定义覆盖
          7. hls协议标准加密视频播放
          8. 基于原生开发,可在所有框架运行,统一跨框架情况
          9. 各浏览器控件统一

          代码演示

          xml
            <r-player src="/ran/hls/example.m3u8"></r-player>

          属性

          src

          视频的资源地址

          volume

          设置初始音量,默认 0.5

          currentTime

          设置初始播放时间,默认从头开始播放

          playbackRate

          设置倍速,默认 1.0

          debug

          控制台会打印输出一些信息

          事件event

          onchange

          监听任何播放器发生的变化,返回的值如下。

          可通过这个方法获得播放器的实例

          活着通过type判断不同的事件类型,进行不同的操作

          属性说明类型
          type发生变化的事件类型string
          data事件的值Object
          currentTime播放的当前时间number
          duration视频的总时长number
          tag播放器的实例Element

          其中type类型有

          名称说明
          canplay浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。
          canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。
          completeOfflineAudioContext 渲染完成。
          durationchangeduration 属性的值改变时触发。
          emptied媒体内容变为空;例如,当这个 media 已经加载完成(或者部分加载完成),则发送此事件,并调用 load() 方法重新加载它。
          ended视频停止播放,因为 media 已经到达结束点。
          loadedmetadata已加载元数据。
          progress在浏览器加载资源时周期性触发。
          ratechange播放速率发生变化。
          seeked跳帧(seek)操作完成。
          seeking跳帧(seek)操作开始。
          stalled用户代理(user agent)正在尝试获取媒体数据,但数据意外未出现。
          suspend媒体数据加载已暂停。
          loadeddatamedia 中的首帧已经完成加载。
          timeupdatecurrentTime 属性指定的时间发生变化。
          volumechange音量发生变化。
          waiting由于暂时缺少数据,播放已停止。
          play播放已开始。
          playing由于缺乏数据而暂停或延迟后,播放准备开始。
          pause播放已暂停。
          volume音量发生变化。
          fullscreen触发全屏事件
          ',25)]))}const m=e(i,[["render",l]]);export{u as __pageData,m as default}; diff --git a/assets/cn_src_ranui_player_index.md.DXjOFy1h.lean.js b/assets/cn_src_ranui_player_index.md.ClG2z7Rd.lean.js similarity index 99% rename from assets/cn_src_ranui_player_index.md.DXjOFy1h.lean.js rename to assets/cn_src_ranui_player_index.md.ClG2z7Rd.lean.js index c793b7713a..605f89be3e 100644 --- a/assets/cn_src_ranui_player_index.md.DXjOFy1h.lean.js +++ b/assets/cn_src_ranui_player_index.md.ClG2z7Rd.lean.js @@ -1 +1 @@ -import{_ as e,o as d,c as a,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player 视频播放器","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/player/index.md","filePath":"cn/src/ranui/player/index.md","lastUpdated":1728129125000}'),i={name:"cn/src/ranui/player/index.md"};function l(o,t,n,s,h,c){return d(),a("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          r-player 视频播放器

          基于hlsjsweb components,让原生的标签r-player拥有统一的视频控件。 不采用new Player(options)的方式挂载到指定dom,视图的归视图,逻辑的归逻辑,所见及所得,更加直观。

          1. 可拖拽进度条
          2. 音量控制
          3. 根据当前带宽自适应码率切换
          4. 手动清晰度切换
          5. 倍速播放
          6. 样式自定义覆盖
          7. hls协议标准加密视频播放
          8. 基于原生开发,可在所有框架运行,统一跨框架情况
          9. 各浏览器控件统一

          代码演示

          xml
            <r-player src="/ran/hls/example.m3u8"></r-player>

          属性

          src

          视频的资源地址

          volume

          设置初始音量,默认 0.5

          currentTime

          设置初始播放时间,默认从头开始播放

          playbackRate

          设置倍速,默认 1.0

          debug

          控制台会打印输出一些信息

          事件event

          onchange

          监听任何播放器发生的变化,返回的值如下。

          可通过这个方法获得播放器的实例

          活着通过type判断不同的事件类型,进行不同的操作

          属性说明类型
          type发生变化的事件类型string
          data事件的值Object
          currentTime播放的当前时间number
          duration视频的总时长number
          tag播放器的实例Element

          其中type类型有

          名称说明
          canplay浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。
          canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。
          completeOfflineAudioContext 渲染完成。
          durationchangeduration 属性的值改变时触发。
          emptied媒体内容变为空;例如,当这个 media 已经加载完成(或者部分加载完成),则发送此事件,并调用 load() 方法重新加载它。
          ended视频停止播放,因为 media 已经到达结束点。
          loadedmetadata已加载元数据。
          progress在浏览器加载资源时周期性触发。
          ratechange播放速率发生变化。
          seeked跳帧(seek)操作完成。
          seeking跳帧(seek)操作开始。
          stalled用户代理(user agent)正在尝试获取媒体数据,但数据意外未出现。
          suspend媒体数据加载已暂停。
          loadeddatamedia 中的首帧已经完成加载。
          timeupdatecurrentTime 属性指定的时间发生变化。
          volumechange音量发生变化。
          waiting由于暂时缺少数据,播放已停止。
          play播放已开始。
          playing由于缺乏数据而暂停或延迟后,播放准备开始。
          pause播放已暂停。
          volume音量发生变化。
          fullscreen触发全屏事件
          ',25)]))}const m=e(i,[["render",l]]);export{u as __pageData,m as default}; +import{_ as e,o as d,c as a,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player 视频播放器","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/player/index.md","filePath":"cn/src/ranui/player/index.md","lastUpdated":1728129344000}'),i={name:"cn/src/ranui/player/index.md"};function l(o,t,n,s,h,c){return d(),a("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          r-player 视频播放器

          基于hlsjsweb components,让原生的标签r-player拥有统一的视频控件。 不采用new Player(options)的方式挂载到指定dom,视图的归视图,逻辑的归逻辑,所见及所得,更加直观。

          1. 可拖拽进度条
          2. 音量控制
          3. 根据当前带宽自适应码率切换
          4. 手动清晰度切换
          5. 倍速播放
          6. 样式自定义覆盖
          7. hls协议标准加密视频播放
          8. 基于原生开发,可在所有框架运行,统一跨框架情况
          9. 各浏览器控件统一

          代码演示

          xml
            <r-player src="/ran/hls/example.m3u8"></r-player>

          属性

          src

          视频的资源地址

          volume

          设置初始音量,默认 0.5

          currentTime

          设置初始播放时间,默认从头开始播放

          playbackRate

          设置倍速,默认 1.0

          debug

          控制台会打印输出一些信息

          事件event

          onchange

          监听任何播放器发生的变化,返回的值如下。

          可通过这个方法获得播放器的实例

          活着通过type判断不同的事件类型,进行不同的操作

          属性说明类型
          type发生变化的事件类型string
          data事件的值Object
          currentTime播放的当前时间number
          duration视频的总时长number
          tag播放器的实例Element

          其中type类型有

          名称说明
          canplay浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。
          canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。
          completeOfflineAudioContext 渲染完成。
          durationchangeduration 属性的值改变时触发。
          emptied媒体内容变为空;例如,当这个 media 已经加载完成(或者部分加载完成),则发送此事件,并调用 load() 方法重新加载它。
          ended视频停止播放,因为 media 已经到达结束点。
          loadedmetadata已加载元数据。
          progress在浏览器加载资源时周期性触发。
          ratechange播放速率发生变化。
          seeked跳帧(seek)操作完成。
          seeking跳帧(seek)操作开始。
          stalled用户代理(user agent)正在尝试获取媒体数据,但数据意外未出现。
          suspend媒体数据加载已暂停。
          loadeddatamedia 中的首帧已经完成加载。
          timeupdatecurrentTime 属性指定的时间发生变化。
          volumechange音量发生变化。
          waiting由于暂时缺少数据,播放已停止。
          play播放已开始。
          playing由于缺乏数据而暂停或延迟后,播放准备开始。
          pause播放已暂停。
          volume音量发生变化。
          fullscreen触发全屏事件
          ',25)]))}const m=e(i,[["render",l]]);export{u as __pageData,m as default}; diff --git a/assets/cn_src_ranui_popover_index.md.YQ2K5eFD.js b/assets/cn_src_ranui_popover_index.md.CKCBCC0z.js similarity index 99% rename from assets/cn_src_ranui_popover_index.md.YQ2K5eFD.js rename to assets/cn_src_ranui_popover_index.md.CKCBCC0z.js index f198f15b82..e775da17d5 100644 --- a/assets/cn_src_ranui_popover_index.md.YQ2K5eFD.js +++ b/assets/cn_src_ranui_popover_index.md.CKCBCC0z.js @@ -1,4 +1,4 @@ -import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Popover 气泡卡片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/popover/index.md","filePath":"cn/src/ranui/popover/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Popover 气泡卡片

          点击/鼠标移入元素,弹出气泡式的卡片浮层。

          代码演示

          popover
          this is content
          xml
          <r-popover>
          +import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Popover 气泡卡片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/popover/index.md","filePath":"cn/src/ranui/popover/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Popover 气泡卡片

          点击/鼠标移入元素,弹出气泡式的卡片浮层。

          代码演示

          popover
          this is content
          xml
          <r-popover>
               <r-button>popover</r-button>
               <r-content>
                 <div>this is content</div>
          diff --git a/assets/cn_src_ranui_popover_index.md.YQ2K5eFD.lean.js b/assets/cn_src_ranui_popover_index.md.CKCBCC0z.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_popover_index.md.YQ2K5eFD.lean.js
          rename to assets/cn_src_ranui_popover_index.md.CKCBCC0z.lean.js
          index f198f15b82..e775da17d5 100644
          --- a/assets/cn_src_ranui_popover_index.md.YQ2K5eFD.lean.js
          +++ b/assets/cn_src_ranui_popover_index.md.CKCBCC0z.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Popover 气泡卡片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/popover/index.md","filePath":"cn/src/ranui/popover/index.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Popover 气泡卡片

          点击/鼠标移入元素,弹出气泡式的卡片浮层。

          代码演示

          popover
          this is content
          xml
          <r-popover>
          +import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Popover 气泡卡片","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/popover/index.md","filePath":"cn/src/ranui/popover/index.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Popover 气泡卡片

          点击/鼠标移入元素,弹出气泡式的卡片浮层。

          代码演示

          popover
          this is content
          xml
          <r-popover>
               <r-button>popover</r-button>
               <r-content>
                 <div>this is content</div>
          diff --git a/assets/cn_src_ranui_preview_index.md.MwV2D84f.js b/assets/cn_src_ranui_preview_index.md.HNgH1cdk.js
          similarity index 99%
          rename from assets/cn_src_ranui_preview_index.md.MwV2D84f.js
          rename to assets/cn_src_ranui_preview_index.md.HNgH1cdk.js
          index ab27676631..ab7ae8d3b0 100644
          --- a/assets/cn_src_ranui_preview_index.md.MwV2D84f.js
          +++ b/assets/cn_src_ranui_preview_index.md.HNgH1cdk.js
          @@ -1,4 +1,4 @@
          -import{_ as h,o as t,c as n,a3 as a,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"preview 文件预览","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/preview/index.md","filePath":"cn/src/ranui/preview/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranui/preview/index.md"};function k(p,i,e,E,r,d){return t(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[a('

          preview 文件预览

          支持docxpptxpdf,xlsx文件的预览

          代码演示

          ',3),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fhdjskafk"}),s("r-button",{type:"primary",onclick:"uploadFile('fhdjskafk')"},"choose file to preview")],-1),a(`
          html
          <r-preview id="preview"></r-preview>
          +import{_ as h,o as t,c as n,a3 as a,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"preview 文件预览","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/preview/index.md","filePath":"cn/src/ranui/preview/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranui/preview/index.md"};function k(p,i,e,E,r,d){return t(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[a('

          preview 文件预览

          支持docxpptxpdf,xlsx文件的预览

          代码演示

          ',3),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fhdjskafk"}),s("r-button",{type:"primary",onclick:"uploadFile('fhdjskafk')"},"choose file to preview")],-1),a(`
          html
          <r-preview id="preview"></r-preview>
           <r-button type="primary" onclick="uploadFile()">choose file to preview</r-button>
           
           <script>
          diff --git a/assets/cn_src_ranui_preview_index.md.MwV2D84f.lean.js b/assets/cn_src_ranui_preview_index.md.HNgH1cdk.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_preview_index.md.MwV2D84f.lean.js
          rename to assets/cn_src_ranui_preview_index.md.HNgH1cdk.lean.js
          index ab27676631..ab7ae8d3b0 100644
          --- a/assets/cn_src_ranui_preview_index.md.MwV2D84f.lean.js
          +++ b/assets/cn_src_ranui_preview_index.md.HNgH1cdk.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as h,o as t,c as n,a3 as a,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"preview 文件预览","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/preview/index.md","filePath":"cn/src/ranui/preview/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranui/preview/index.md"};function k(p,i,e,E,r,d){return t(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[a('

          preview 文件预览

          支持docxpptxpdf,xlsx文件的预览

          代码演示

          ',3),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fhdjskafk"}),s("r-button",{type:"primary",onclick:"uploadFile('fhdjskafk')"},"choose file to preview")],-1),a(`
          html
          <r-preview id="preview"></r-preview>
          +import{_ as h,o as t,c as n,a3 as a,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"preview 文件预览","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/preview/index.md","filePath":"cn/src/ranui/preview/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranui/preview/index.md"};function k(p,i,e,E,r,d){return t(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[a('

          preview 文件预览

          支持docxpptxpdf,xlsx文件的预览

          代码演示

          ',3),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fhdjskafk"}),s("r-button",{type:"primary",onclick:"uploadFile('fhdjskafk')"},"choose file to preview")],-1),a(`
          html
          <r-preview id="preview"></r-preview>
           <r-button type="primary" onclick="uploadFile()">choose file to preview</r-button>
           
           <script>
          diff --git a/assets/cn_src_ranui_progress_index.md.BhrzleOl.js b/assets/cn_src_ranui_progress_index.md.B6p9KAvb.js
          similarity index 99%
          rename from assets/cn_src_ranui_progress_index.md.BhrzleOl.js
          rename to assets/cn_src_ranui_progress_index.md.B6p9KAvb.js
          index 19981df892..2e017a56d5 100644
          --- a/assets/cn_src_ranui_progress_index.md.BhrzleOl.js
          +++ b/assets/cn_src_ranui_progress_index.md.B6p9KAvb.js
          @@ -1 +1 @@
          -import{_ as t,o as h,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"progress 进度条","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/progress/index.md","filePath":"cn/src/ranui/progress/index.md","lastUpdated":1728129125000}'),p={name:"cn/src/ranui/progress/index.md"};function l(n,a,k,r,d,E){return h(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          progress 进度条

          可交互的进度条

          代码演示

          xml
          <r-progress type="drag" ></r-progress>

          属性

          总进度total

          设置进度条总进度,允许百分比和数字。

          ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
          html
          <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

          当前进度percent

          设置进度条的当前进度,可以设置百分比和数字,percent不能超过total。如果不设置total,默认total100%也就是1

          ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
          html
          <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

          进度条的点dot

          默认展示,设置成false可隐藏

          ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

          类型type

          • primary: 默认的进度条,不设置type属性是默认
          • drag: 可拖动,可点击的进度条(拖动需要设置dottrue
          ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

          方法

          change

          percenttotal属性发生变化时,触发change事件。

          属性说明类型
          value当前进度string|number
          percent当前进度string|number
          total总进度string|number
          ',5)]))}const y=t(p,[["render",l]]);export{g as __pageData,y as default}; +import{_ as t,o as h,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"progress 进度条","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/progress/index.md","filePath":"cn/src/ranui/progress/index.md","lastUpdated":1728129344000}'),p={name:"cn/src/ranui/progress/index.md"};function l(n,a,k,r,d,E){return h(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          progress 进度条

          可交互的进度条

          代码演示

          xml
          <r-progress type="drag" ></r-progress>

          属性

          总进度total

          设置进度条总进度,允许百分比和数字。

          ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
          html
          <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

          当前进度percent

          设置进度条的当前进度,可以设置百分比和数字,percent不能超过total。如果不设置total,默认total100%也就是1

          ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
          html
          <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

          进度条的点dot

          默认展示,设置成false可隐藏

          ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

          类型type

          • primary: 默认的进度条,不设置type属性是默认
          • drag: 可拖动,可点击的进度条(拖动需要设置dottrue
          ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

          方法

          change

          percenttotal属性发生变化时,触发change事件。

          属性说明类型
          value当前进度string|number
          percent当前进度string|number
          total总进度string|number
          ',5)]))}const y=t(p,[["render",l]]);export{g as __pageData,y as default}; diff --git a/assets/cn_src_ranui_progress_index.md.BhrzleOl.lean.js b/assets/cn_src_ranui_progress_index.md.B6p9KAvb.lean.js similarity index 99% rename from assets/cn_src_ranui_progress_index.md.BhrzleOl.lean.js rename to assets/cn_src_ranui_progress_index.md.B6p9KAvb.lean.js index 19981df892..2e017a56d5 100644 --- a/assets/cn_src_ranui_progress_index.md.BhrzleOl.lean.js +++ b/assets/cn_src_ranui_progress_index.md.B6p9KAvb.lean.js @@ -1 +1 @@ -import{_ as t,o as h,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"progress 进度条","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/progress/index.md","filePath":"cn/src/ranui/progress/index.md","lastUpdated":1728129125000}'),p={name:"cn/src/ranui/progress/index.md"};function l(n,a,k,r,d,E){return h(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          progress 进度条

          可交互的进度条

          代码演示

          xml
          <r-progress type="drag" ></r-progress>

          属性

          总进度total

          设置进度条总进度,允许百分比和数字。

          ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
          html
          <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

          当前进度percent

          设置进度条的当前进度,可以设置百分比和数字,percent不能超过total。如果不设置total,默认total100%也就是1

          ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
          html
          <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

          进度条的点dot

          默认展示,设置成false可隐藏

          ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

          类型type

          • primary: 默认的进度条,不设置type属性是默认
          • drag: 可拖动,可点击的进度条(拖动需要设置dottrue
          ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

          方法

          change

          percenttotal属性发生变化时,触发change事件。

          属性说明类型
          value当前进度string|number
          percent当前进度string|number
          total总进度string|number
          ',5)]))}const y=t(p,[["render",l]]);export{g as __pageData,y as default}; +import{_ as t,o as h,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"progress 进度条","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/progress/index.md","filePath":"cn/src/ranui/progress/index.md","lastUpdated":1728129344000}'),p={name:"cn/src/ranui/progress/index.md"};function l(n,a,k,r,d,E){return h(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          progress 进度条

          可交互的进度条

          代码演示

          xml
          <r-progress type="drag" ></r-progress>

          属性

          总进度total

          设置进度条总进度,允许百分比和数字。

          ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
          html
          <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

          当前进度percent

          设置进度条的当前进度,可以设置百分比和数字,percent不能超过total。如果不设置total,默认total100%也就是1

          ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
          html
          <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

          进度条的点dot

          默认展示,设置成false可隐藏

          ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

          类型type

          • primary: 默认的进度条,不设置type属性是默认
          • drag: 可拖动,可点击的进度条(拖动需要设置dottrue
          ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
          html
          <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

          方法

          change

          percenttotal属性发生变化时,触发change事件。

          属性说明类型
          value当前进度string|number
          percent当前进度string|number
          total总进度string|number
          ',5)]))}const y=t(p,[["render",l]]);export{g as __pageData,y as default}; diff --git a/assets/cn_src_ranui_radar_index.md.JUC5Tblz.js b/assets/cn_src_ranui_radar_index.md.D7lwGVbW.js similarity index 99% rename from assets/cn_src_ranui_radar_index.md.JUC5Tblz.js rename to assets/cn_src_ranui_radar_index.md.D7lwGVbW.js index a76bdfc72d..555a50abb1 100644 --- a/assets/cn_src_ranui_radar_index.md.JUC5Tblz.js +++ b/assets/cn_src_ranui_radar_index.md.D7lwGVbW.js @@ -1,4 +1,4 @@ -import{_ as l,o as e,c as o,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar 雷达图","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/radar/index.md","filePath":"cn/src/ranui/radar/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/radar/index.md"};function h(p,a,k,u,r,q){return e(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"radar-雷达图",tabindex:"-1"},[t("Radar 雷达图 "),s("a",{class:"header-anchor",href:"#radar-雷达图","aria-label":'Permalink to "Radar 雷达图"'},"​")],-1),s("p",null,"以二维形式综合对比多组数据的差异,常用于比较 2 组或更多组数据集",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"生命","scoreRate":"10"},{"abilityName":"攻击","scoreRate":"90"},{"abilityName":"防御","scoreRate":"20"},{"abilityName":"元素精通","scoreRate":"50"},{"abilityName":"暴击率","scoreRate":"80"},{"abilityName":"暴击伤害","scoreRate":"50"}]'},null,-1),i(`
          xml
          <r-radar
          +import{_ as l,o as e,c as o,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar 雷达图","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/radar/index.md","filePath":"cn/src/ranui/radar/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/radar/index.md"};function h(p,a,k,u,r,q){return e(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"radar-雷达图",tabindex:"-1"},[t("Radar 雷达图 "),s("a",{class:"header-anchor",href:"#radar-雷达图","aria-label":'Permalink to "Radar 雷达图"'},"​")],-1),s("p",null,"以二维形式综合对比多组数据的差异,常用于比较 2 组或更多组数据集",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"生命","scoreRate":"10"},{"abilityName":"攻击","scoreRate":"90"},{"abilityName":"防御","scoreRate":"20"},{"abilityName":"元素精通","scoreRate":"50"},{"abilityName":"暴击率","scoreRate":"80"},{"abilityName":"暴击伤害","scoreRate":"50"}]'},null,-1),i(`
          xml
          <r-radar
               abilitys='[{"abilityName":"生命","scoreRate":"10"},{"abilityName":"攻击","scoreRate":"90"},{"abilityName":"防御","scoreRate":"20"},{"abilityName":"元素精通","scoreRate":"50"},{"abilityName":"暴击率","scoreRate":"80"},{"abilityName":"暴击伤害","scoreRate":"50"}]'
               style="width:300px;height:300px;display: block;"
           >
          diff --git a/assets/cn_src_ranui_radar_index.md.JUC5Tblz.lean.js b/assets/cn_src_ranui_radar_index.md.D7lwGVbW.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_radar_index.md.JUC5Tblz.lean.js
          rename to assets/cn_src_ranui_radar_index.md.D7lwGVbW.lean.js
          index a76bdfc72d..555a50abb1 100644
          --- a/assets/cn_src_ranui_radar_index.md.JUC5Tblz.lean.js
          +++ b/assets/cn_src_ranui_radar_index.md.D7lwGVbW.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as l,o as e,c as o,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar 雷达图","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/radar/index.md","filePath":"cn/src/ranui/radar/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/radar/index.md"};function h(p,a,k,u,r,q){return e(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"radar-雷达图",tabindex:"-1"},[t("Radar 雷达图 "),s("a",{class:"header-anchor",href:"#radar-雷达图","aria-label":'Permalink to "Radar 雷达图"'},"​")],-1),s("p",null,"以二维形式综合对比多组数据的差异,常用于比较 2 组或更多组数据集",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"生命","scoreRate":"10"},{"abilityName":"攻击","scoreRate":"90"},{"abilityName":"防御","scoreRate":"20"},{"abilityName":"元素精通","scoreRate":"50"},{"abilityName":"暴击率","scoreRate":"80"},{"abilityName":"暴击伤害","scoreRate":"50"}]'},null,-1),i(`
          xml
          <r-radar
          +import{_ as l,o as e,c as o,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar 雷达图","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/radar/index.md","filePath":"cn/src/ranui/radar/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/radar/index.md"};function h(p,a,k,u,r,q){return e(),o("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"radar-雷达图",tabindex:"-1"},[t("Radar 雷达图 "),s("a",{class:"header-anchor",href:"#radar-雷达图","aria-label":'Permalink to "Radar 雷达图"'},"​")],-1),s("p",null,"以二维形式综合对比多组数据的差异,常用于比较 2 组或更多组数据集",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"生命","scoreRate":"10"},{"abilityName":"攻击","scoreRate":"90"},{"abilityName":"防御","scoreRate":"20"},{"abilityName":"元素精通","scoreRate":"50"},{"abilityName":"暴击率","scoreRate":"80"},{"abilityName":"暴击伤害","scoreRate":"50"}]'},null,-1),i(`
          xml
          <r-radar
               abilitys='[{"abilityName":"生命","scoreRate":"10"},{"abilityName":"攻击","scoreRate":"90"},{"abilityName":"防御","scoreRate":"20"},{"abilityName":"元素精通","scoreRate":"50"},{"abilityName":"暴击率","scoreRate":"80"},{"abilityName":"暴击伤害","scoreRate":"50"}]'
               style="width:300px;height:300px;display: block;"
           >
          diff --git a/assets/cn_src_ranui_select_index.md.CqrSnS3F.js b/assets/cn_src_ranui_select_index.md.x3hqXtMG.js
          similarity index 99%
          rename from assets/cn_src_ranui_select_index.md.CqrSnS3F.js
          rename to assets/cn_src_ranui_select_index.md.x3hqXtMG.js
          index 6379234016..e22e0c1b8c 100644
          --- a/assets/cn_src_ranui_select_index.md.CqrSnS3F.js
          +++ b/assets/cn_src_ranui_select_index.md.x3hqXtMG.js
          @@ -1,4 +1,4 @@
          -import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select 下拉选择框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/select/index.md","filePath":"cn/src/ranui/select/index.md","lastUpdated":1728129125000}'),k={name:"cn/src/ranui/select/index.md"};function p(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select-下拉选择框",tabindex:"-1"},[t("Select 下拉选择框 "),s("a",{class:"header-anchor",href:"#select-下拉选择框","aria-label":'Permalink to "Select 下拉选择框"'},"​")],-1),s("p",null,"一个普通的下拉选择器。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
          xml
          <r-select style="width: 120px; height: 40px" defaultValue="185">
          +import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select 下拉选择框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/select/index.md","filePath":"cn/src/ranui/select/index.md","lastUpdated":1728129344000}'),k={name:"cn/src/ranui/select/index.md"};function p(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select-下拉选择框",tabindex:"-1"},[t("Select 下拉选择框 "),s("a",{class:"header-anchor",href:"#select-下拉选择框","aria-label":'Permalink to "Select 下拉选择框"'},"​")],-1),s("p",null,"一个普通的下拉选择器。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
          xml
          <r-select style="width: 120px; height: 40px" defaultValue="185">
                 <r-option value="185">Mike</r-option>
                 <r-option value="186">Tom</r-option>
                 <r-option value="187">Lucy</r-option>
          diff --git a/assets/cn_src_ranui_select_index.md.CqrSnS3F.lean.js b/assets/cn_src_ranui_select_index.md.x3hqXtMG.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_select_index.md.CqrSnS3F.lean.js
          rename to assets/cn_src_ranui_select_index.md.x3hqXtMG.lean.js
          index 6379234016..e22e0c1b8c 100644
          --- a/assets/cn_src_ranui_select_index.md.CqrSnS3F.lean.js
          +++ b/assets/cn_src_ranui_select_index.md.x3hqXtMG.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select 下拉选择框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/select/index.md","filePath":"cn/src/ranui/select/index.md","lastUpdated":1728129125000}'),k={name:"cn/src/ranui/select/index.md"};function p(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select-下拉选择框",tabindex:"-1"},[t("Select 下拉选择框 "),s("a",{class:"header-anchor",href:"#select-下拉选择框","aria-label":'Permalink to "Select 下拉选择框"'},"​")],-1),s("p",null,"一个普通的下拉选择器。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
          xml
          <r-select style="width: 120px; height: 40px" defaultValue="185">
          +import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select 下拉选择框","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/select/index.md","filePath":"cn/src/ranui/select/index.md","lastUpdated":1728129344000}'),k={name:"cn/src/ranui/select/index.md"};function p(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select-下拉选择框",tabindex:"-1"},[t("Select 下拉选择框 "),s("a",{class:"header-anchor",href:"#select-下拉选择框","aria-label":'Permalink to "Select 下拉选择框"'},"​")],-1),s("p",null,"一个普通的下拉选择器。",-1),s("h2",{id:"代码演示",tabindex:"-1"},[t("代码演示 "),s("a",{class:"header-anchor",href:"#代码演示","aria-label":'Permalink to "代码演示"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
          xml
          <r-select style="width: 120px; height: 40px" defaultValue="185">
                 <r-option value="185">Mike</r-option>
                 <r-option value="186">Tom</r-option>
                 <r-option value="187">Lucy</r-option>
          diff --git a/assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.js b/assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.js
          similarity index 96%
          rename from assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.js
          rename to assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.js
          index 8726a77e41..85c90ccdaa 100644
          --- a/assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.js
          +++ b/assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.js
          @@ -1 +1 @@
          -import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton 骨架屏","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/skeleton/index.md","filePath":"cn/src/ranui/skeleton/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/skeleton/index.md"};function l(r,e,o,d,p,k){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          skeleton 骨架屏

          在需要等待加载内容的位置提供一个占位图形组合。

          代码演示

          骨架长度跟随父级元素的长度

          xml
          <r-skeleton ></r-skeleton>
          ',9)]))}const g=t(n,[["render",l]]);export{c as __pageData,g as default}; +import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton 骨架屏","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/skeleton/index.md","filePath":"cn/src/ranui/skeleton/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/skeleton/index.md"};function l(r,e,o,d,p,k){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          skeleton 骨架屏

          在需要等待加载内容的位置提供一个占位图形组合。

          代码演示

          骨架长度跟随父级元素的长度

          xml
          <r-skeleton ></r-skeleton>
          ',9)]))}const g=t(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.lean.js b/assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.lean.js similarity index 96% rename from assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.lean.js rename to assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.lean.js index 8726a77e41..85c90ccdaa 100644 --- a/assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.lean.js +++ b/assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.lean.js @@ -1 +1 @@ -import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton 骨架屏","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/skeleton/index.md","filePath":"cn/src/ranui/skeleton/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/skeleton/index.md"};function l(r,e,o,d,p,k){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          skeleton 骨架屏

          在需要等待加载内容的位置提供一个占位图形组合。

          代码演示

          骨架长度跟随父级元素的长度

          xml
          <r-skeleton ></r-skeleton>
          ',9)]))}const g=t(n,[["render",l]]);export{c as __pageData,g as default}; +import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton 骨架屏","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/skeleton/index.md","filePath":"cn/src/ranui/skeleton/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/skeleton/index.md"};function l(r,e,o,d,p,k){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          skeleton 骨架屏

          在需要等待加载内容的位置提供一个占位图形组合。

          代码演示

          骨架长度跟随父级元素的长度

          xml
          <r-skeleton ></r-skeleton>
          ',9)]))}const g=t(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/assets/cn_src_ranui_tab_index.md.BlcshkmY.js b/assets/cn_src_ranui_tab_index.md._Yrr2Do-.js similarity index 99% rename from assets/cn_src_ranui_tab_index.md.BlcshkmY.js rename to assets/cn_src_ranui_tab_index.md._Yrr2Do-.js index a39ad4f74b..8225b6f79b 100644 --- a/assets/cn_src_ranui_tab_index.md.BlcshkmY.js +++ b/assets/cn_src_ranui_tab_index.md._Yrr2Do-.js @@ -1,4 +1,4 @@ -import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tab/index.md","filePath":"cn/src/ranui/tab/index.md","lastUpdated":1728129125000}'),k={name:"cn/src/ranui/tab/index.md"};function n(p,a,E,e,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab 图标

          标签页,其中r-tab需要和r-tabs搭配使用

          代码演示

          111112222233333
          xml
          <r-tabs >
          +import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tab/index.md","filePath":"cn/src/ranui/tab/index.md","lastUpdated":1728129344000}'),k={name:"cn/src/ranui/tab/index.md"};function n(p,a,E,e,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab 图标

          标签页,其中r-tab需要和r-tabs搭配使用

          代码演示

          111112222233333
          xml
          <r-tabs >
                 <r-tab label="tab1">11111</r-tab>
                 <r-tab label="tab2">22222</r-tab>
                 <r-tab label="tab3">33333</r-tab>
          diff --git a/assets/cn_src_ranui_tab_index.md.BlcshkmY.lean.js b/assets/cn_src_ranui_tab_index.md._Yrr2Do-.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_tab_index.md.BlcshkmY.lean.js
          rename to assets/cn_src_ranui_tab_index.md._Yrr2Do-.lean.js
          index a39ad4f74b..8225b6f79b 100644
          --- a/assets/cn_src_ranui_tab_index.md.BlcshkmY.lean.js
          +++ b/assets/cn_src_ranui_tab_index.md._Yrr2Do-.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tab/index.md","filePath":"cn/src/ranui/tab/index.md","lastUpdated":1728129125000}'),k={name:"cn/src/ranui/tab/index.md"};function n(p,a,E,e,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab 图标

          标签页,其中r-tab需要和r-tabs搭配使用

          代码演示

          111112222233333
          xml
          <r-tabs >
          +import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab 图标","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tab/index.md","filePath":"cn/src/ranui/tab/index.md","lastUpdated":1728129344000}'),k={name:"cn/src/ranui/tab/index.md"};function n(p,a,E,e,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab 图标

          标签页,其中r-tab需要和r-tabs搭配使用

          代码演示

          111112222233333
          xml
          <r-tabs >
                 <r-tab label="tab1">11111</r-tab>
                 <r-tab label="tab2">22222</r-tab>
                 <r-tab label="tab3">33333</r-tab>
          diff --git a/assets/cn_src_ranui_tabs_index.md.BnLsjdMq.js b/assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.js
          similarity index 99%
          rename from assets/cn_src_ranui_tabs_index.md.BnLsjdMq.js
          rename to assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.js
          index 43253d2cdc..a30d1ba262 100644
          --- a/assets/cn_src_ranui_tabs_index.md.BnLsjdMq.js
          +++ b/assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tabs/index.md","filePath":"cn/src/ranui/tabs/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab

          代码展示

          tab1tab2tab3
          xml
          <r-tabs>
          +import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tabs/index.md","filePath":"cn/src/ranui/tabs/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab

          代码展示

          tab1tab2tab3
          xml
          <r-tabs>
               <r-tab label="tab1">tab1</r-tab>
               <r-tab label="tab2">tab2</r-tab>
               <r-tab label="tab3">tab3</r-tab>
          diff --git a/assets/cn_src_ranui_tabs_index.md.BnLsjdMq.lean.js b/assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.lean.js
          similarity index 99%
          rename from assets/cn_src_ranui_tabs_index.md.BnLsjdMq.lean.js
          rename to assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.lean.js
          index 43253d2cdc..a30d1ba262 100644
          --- a/assets/cn_src_ranui_tabs_index.md.BnLsjdMq.lean.js
          +++ b/assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tabs/index.md","filePath":"cn/src/ranui/tabs/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab

          代码展示

          tab1tab2tab3
          xml
          <r-tabs>
          +import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranui/tabs/index.md","filePath":"cn/src/ranui/tabs/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          Tab

          代码展示

          tab1tab2tab3
          xml
          <r-tabs>
               <r-tab label="tab1">tab1</r-tab>
               <r-tab label="tab2">tab2</r-tab>
               <r-tab label="tab3">tab3</r-tab>
          diff --git a/assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.js b/assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.js
          similarity index 99%
          rename from assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.js
          rename to assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.js
          index 864c3a3c15..83294386fe 100644
          --- a/assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.js
          +++ b/assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.js
          @@ -1 +1 @@
          -import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/binaryTree/index.md","filePath":"cn/src/ranuts/binaryTree/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

          二叉树的定义

          在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

          二叉树的性质

          • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
          • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
          • 包含 n 个结点的二叉树的高度至少为(log2n)+1
          • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
          • 二叉树的总结点数 n = n1 + n2 + n0
          • 总连线数等于总节点数减一(B = n - 1)
          • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

          二叉树的类型

          满二叉树

          一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

          完全二叉树

          一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

          二叉搜索树

          二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

          平衡二叉树

          平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

          B 树

          B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

          B+树

          B+树是 B 树的变体,也是一种多路搜索树。

          B*树

          B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

          红黑树

          红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

          遍历

          前序遍历

          后序遍历

          中序遍历

          层序遍历

          常见算法题

          镜像二叉树

          重建二叉树

          二叉树深度

          二叉树节点总数

          判断二叉树子结构

          输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

          参考文档

          1. 维基百科二叉树
          2. 百度百科满二叉树
          ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; +import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/binaryTree/index.md","filePath":"cn/src/ranuts/binaryTree/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

          二叉树的定义

          在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

          二叉树的性质

          • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
          • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
          • 包含 n 个结点的二叉树的高度至少为(log2n)+1
          • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
          • 二叉树的总结点数 n = n1 + n2 + n0
          • 总连线数等于总节点数减一(B = n - 1)
          • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

          二叉树的类型

          满二叉树

          一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

          完全二叉树

          一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

          二叉搜索树

          二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

          平衡二叉树

          平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

          B 树

          B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

          B+树

          B+树是 B 树的变体,也是一种多路搜索树。

          B*树

          B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

          红黑树

          红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

          遍历

          前序遍历

          后序遍历

          中序遍历

          层序遍历

          常见算法题

          镜像二叉树

          重建二叉树

          二叉树深度

          二叉树节点总数

          判断二叉树子结构

          输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

          参考文档

          1. 维基百科二叉树
          2. 百度百科满二叉树
          ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; diff --git a/assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.lean.js b/assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.lean.js similarity index 99% rename from assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.lean.js rename to assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.lean.js index 864c3a3c15..83294386fe 100644 --- a/assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.lean.js +++ b/assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.lean.js @@ -1 +1 @@ -import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/binaryTree/index.md","filePath":"cn/src/ranuts/binaryTree/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

          二叉树的定义

          在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

          二叉树的性质

          • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
          • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
          • 包含 n 个结点的二叉树的高度至少为(log2n)+1
          • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
          • 二叉树的总结点数 n = n1 + n2 + n0
          • 总连线数等于总节点数减一(B = n - 1)
          • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

          二叉树的类型

          满二叉树

          一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

          完全二叉树

          一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

          二叉搜索树

          二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

          平衡二叉树

          平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

          B 树

          B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

          B+树

          B+树是 B 树的变体,也是一种多路搜索树。

          B*树

          B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

          红黑树

          红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

          遍历

          前序遍历

          后序遍历

          中序遍历

          层序遍历

          常见算法题

          镜像二叉树

          重建二叉树

          二叉树深度

          二叉树节点总数

          判断二叉树子结构

          输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

          参考文档

          1. 维基百科二叉树
          2. 百度百科满二叉树
          ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; +import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/binaryTree/index.md","filePath":"cn/src/ranuts/binaryTree/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

          二叉树的定义

          在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

          二叉树的性质

          • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
          • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
          • 包含 n 个结点的二叉树的高度至少为(log2n)+1
          • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
          • 二叉树的总结点数 n = n1 + n2 + n0
          • 总连线数等于总节点数减一(B = n - 1)
          • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

          二叉树的类型

          满二叉树

          一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

          完全二叉树

          一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

          二叉搜索树

          二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

          平衡二叉树

          平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

          B 树

          B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

          B+树

          B+树是 B 树的变体,也是一种多路搜索树。

          B*树

          B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

          红黑树

          红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

          遍历

          前序遍历

          后序遍历

          中序遍历

          层序遍历

          常见算法题

          镜像二叉树

          重建二叉树

          二叉树深度

          二叉树节点总数

          判断二叉树子结构

          输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

          参考文档

          1. 维基百科二叉树
          2. 百度百科满二叉树
          ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; diff --git a/assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.js b/assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.js similarity index 96% rename from assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.js rename to assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.js index 97f73a5021..111386ad2b 100644 --- a/assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.js +++ b/assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.js @@ -1,4 +1,4 @@ -import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/bundler/index.md","filePath":"cn/src/ranuts/bundler/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

          Bundler

          Bundler的使用: 传入 options 参数

          function build(options: Options):Promise<Build> {
          +import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/bundler/index.md","filePath":"cn/src/ranuts/bundler/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

          Bundler

          Bundler的使用: 传入 options 参数

          function build(options: Options):Promise<Build> {
             const bundle = new Bundle({
               entry: options.input
             });
          diff --git a/assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.lean.js b/assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.lean.js
          similarity index 96%
          rename from assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.lean.js
          rename to assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.lean.js
          index 97f73a5021..111386ad2b 100644
          --- a/assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.lean.js
          +++ b/assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/bundler/index.md","filePath":"cn/src/ranuts/bundler/index.md","lastUpdated":1728129125000}'),l={name:"cn/src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

          Bundler

          Bundler的使用: 传入 options 参数

          function build(options: Options):Promise<Build> {
          +import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/bundler/index.md","filePath":"cn/src/ranuts/bundler/index.md","lastUpdated":1728129344000}'),l={name:"cn/src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

          Bundler

          Bundler的使用: 传入 options 参数

          function build(options: Options):Promise<Build> {
             const bundle = new Bundle({
               entry: options.input
             });
          diff --git a/assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.js b/assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.js
          similarity index 96%
          rename from assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.js
          rename to assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.js
          index 0d40bdc948..5b19a424b6 100644
          --- a/assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.js
          +++ b/assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.js
          @@ -1 +1 @@
          -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/appendFile.md","filePath":"cn/src/ranuts/file/appendFile.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          AppendFile

          追加一些数据到文件内

          API

          Return

          • Promise
          参数说明类型描述
          success是否追加成功booleantrue 追加成功 false 追加失败
          data追加失败的原因,添加成功后的文件内容any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string

          Example

          ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/appendFile.md","filePath":"cn/src/ranuts/file/appendFile.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          AppendFile

          追加一些数据到文件内

          API

          Return

          • Promise
          参数说明类型描述
          success是否追加成功booleantrue 追加成功 false 追加失败
          data追加失败的原因,添加成功后的文件内容any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string

          Example

          ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.lean.js b/assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.lean.js similarity index 96% rename from assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.lean.js rename to assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.lean.js index 0d40bdc948..5b19a424b6 100644 --- a/assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.lean.js +++ b/assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/appendFile.md","filePath":"cn/src/ranuts/file/appendFile.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          AppendFile

          追加一些数据到文件内

          API

          Return

          • Promise
          参数说明类型描述
          success是否追加成功booleantrue 追加成功 false 追加失败
          data追加失败的原因,添加成功后的文件内容any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string

          Example

          ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/appendFile.md","filePath":"cn/src/ranuts/file/appendFile.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          AppendFile

          追加一些数据到文件内

          API

          Return

          • Promise
          参数说明类型描述
          success是否追加成功booleantrue 追加成功 false 追加失败
          data追加失败的原因,添加成功后的文件内容any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string

          Example

          ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.js b/assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.js similarity index 96% rename from assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.js rename to assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.js index 7e215fb302..e9278dffab 100644 --- a/assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.js +++ b/assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/fileInfo.md","filePath":"cn/src/ranuts/file/fileInfo.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

          QueryFileInfo

          查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

          API

          Return

          • Promise
          参数说明类型描述
          success是否检查成功booleantrue 成功 false 失败
          data文件的信息,或者错误的原因Stats

          Options

          参数说明类型默认值
          path文件路径,需要检查的文件路径stringundefined

          Example

          ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; +import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/fileInfo.md","filePath":"cn/src/ranuts/file/fileInfo.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

          QueryFileInfo

          查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

          API

          Return

          • Promise
          参数说明类型描述
          success是否检查成功booleantrue 成功 false 失败
          data文件的信息,或者错误的原因Stats

          Options

          参数说明类型默认值
          path文件路径,需要检查的文件路径stringundefined

          Example

          ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; diff --git a/assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.lean.js b/assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.lean.js similarity index 96% rename from assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.lean.js rename to assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.lean.js index 7e215fb302..e9278dffab 100644 --- a/assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.lean.js +++ b/assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/fileInfo.md","filePath":"cn/src/ranuts/file/fileInfo.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

          QueryFileInfo

          查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

          API

          Return

          • Promise
          参数说明类型描述
          success是否检查成功booleantrue 成功 false 失败
          data文件的信息,或者错误的原因Stats

          Options

          参数说明类型默认值
          path文件路径,需要检查的文件路径stringundefined

          Example

          ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; +import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/fileInfo.md","filePath":"cn/src/ranuts/file/fileInfo.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

          QueryFileInfo

          查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

          API

          Return

          • Promise
          参数说明类型描述
          success是否检查成功booleantrue 成功 false 失败
          data文件的信息,或者错误的原因Stats

          Options

          参数说明类型默认值
          path文件路径,需要检查的文件路径stringundefined

          Example

          ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; diff --git a/assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.js b/assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.js similarity index 96% rename from assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.js rename to assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.js index 304d946fb1..cf667600ca 100644 --- a/assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.js +++ b/assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.js @@ -1 +1 @@ -import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readDir.md","filePath":"cn/src/ranuts/file/readDir.md","lastUpdated":1728129125000}'),i={name:"cn/src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

          ReadDir

          读一个目录下的所有文件

          API

          Return

          • Promise
          参数说明类型描述
          result目录下所有文件的数组array传入函数的参数

          Options

          ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readDir.md","filePath":"cn/src/ranuts/file/readDir.md","lastUpdated":1728129344000}'),i={name:"cn/src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

          ReadDir

          读一个目录下的所有文件

          API

          Return

          • Promise
          参数说明类型描述
          result目录下所有文件的数组array传入函数的参数

          Options

          ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.lean.js b/assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.lean.js similarity index 96% rename from assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.lean.js rename to assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.lean.js index 304d946fb1..cf667600ca 100644 --- a/assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.lean.js +++ b/assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.lean.js @@ -1 +1 @@ -import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readDir.md","filePath":"cn/src/ranuts/file/readDir.md","lastUpdated":1728129125000}'),i={name:"cn/src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

          ReadDir

          读一个目录下的所有文件

          API

          Return

          • Promise
          参数说明类型描述
          result目录下所有文件的数组array传入函数的参数

          Options

          ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readDir.md","filePath":"cn/src/ranuts/file/readDir.md","lastUpdated":1728129344000}'),i={name:"cn/src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

          ReadDir

          读一个目录下的所有文件

          API

          Return

          • Promise
          参数说明类型描述
          result目录下所有文件的数组array传入函数的参数

          Options

          ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.js b/assets/cn_src_ranuts_file_readFile.md._sEkTrwK.js similarity index 96% rename from assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.js rename to assets/cn_src_ranuts_file_readFile.md._sEkTrwK.js index c1acd4b69d..4b5b9e1d97 100644 --- a/assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.js +++ b/assets/cn_src_ranuts_file_readFile.md._sEkTrwK.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readFile.md","filePath":"cn/src/ranuts/file/readFile.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          ReadFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          data文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20

          Example

          ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readFile.md","filePath":"cn/src/ranuts/file/readFile.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          ReadFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          data文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20

          Example

          ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.lean.js b/assets/cn_src_ranuts_file_readFile.md._sEkTrwK.lean.js similarity index 96% rename from assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.lean.js rename to assets/cn_src_ranuts_file_readFile.md._sEkTrwK.lean.js index c1acd4b69d..4b5b9e1d97 100644 --- a/assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.lean.js +++ b/assets/cn_src_ranuts_file_readFile.md._sEkTrwK.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readFile.md","filePath":"cn/src/ranuts/file/readFile.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          ReadFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          data文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20

          Example

          ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/readFile.md","filePath":"cn/src/ranuts/file/readFile.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          ReadFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          data文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20

          Example

          ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.js b/assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.js similarity index 96% rename from assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.js rename to assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.js index 45a2bade6e..9e00e5f3de 100644 --- a/assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.js +++ b/assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.js @@ -1 +1 @@ -import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/watchFile.md","filePath":"cn/src/ranuts/file/watchFile.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/file/watchFile.md"};function i(h,t,n,l,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WatchFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          status文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20
          ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/watchFile.md","filePath":"cn/src/ranuts/file/watchFile.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/file/watchFile.md"};function i(h,t,n,l,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WatchFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          status文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20
          ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.lean.js b/assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.lean.js similarity index 96% rename from assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.lean.js rename to assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.lean.js index 45a2bade6e..9e00e5f3de 100644 --- a/assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.lean.js +++ b/assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/watchFile.md","filePath":"cn/src/ranuts/file/watchFile.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/file/watchFile.md"};function i(h,t,n,l,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WatchFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          status文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20
          ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/watchFile.md","filePath":"cn/src/ranuts/file/watchFile.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/file/watchFile.md"};function i(h,t,n,l,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WatchFile

          观察一个文件是否改变

          API

          Return

          • Promise
          参数说明类型描述
          status文件是否被改变booleantrue 文件改变 false 文件没变

          Options

          参数说明类型默认值
          path文件路径,需要监听的文件stringundefined
          interval监听文件改变的时间,单位毫秒。number20
          ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.js b/assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.js similarity index 96% rename from assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.js rename to assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.js index aa54bbb59b..c1686bb187 100644 --- a/assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.js +++ b/assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/writeFile.md","filePath":"cn/src/ranuts/file/writeFile.md","lastUpdated":1728129125000}'),i={name:"cn/src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WriteFile

          将内容写入文件

          API

          Return

          • Promise
          参数说明类型描述
          success是否写入成功booleantrue 成功 false 失败
          data写入失败的原因,添加成功后的文件内容和文件路径any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string
          ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/writeFile.md","filePath":"cn/src/ranuts/file/writeFile.md","lastUpdated":1728129344000}'),i={name:"cn/src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WriteFile

          将内容写入文件

          API

          Return

          • Promise
          参数说明类型描述
          success是否写入成功booleantrue 成功 false 失败
          data写入失败的原因,添加成功后的文件内容和文件路径any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string
          ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.lean.js b/assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.lean.js similarity index 96% rename from assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.lean.js rename to assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.lean.js index aa54bbb59b..c1686bb187 100644 --- a/assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.lean.js +++ b/assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/writeFile.md","filePath":"cn/src/ranuts/file/writeFile.md","lastUpdated":1728129125000}'),i={name:"cn/src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WriteFile

          将内容写入文件

          API

          Return

          • Promise
          参数说明类型描述
          success是否写入成功booleantrue 成功 false 失败
          data写入失败的原因,添加成功后的文件内容和文件路径any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string
          ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/file/writeFile.md","filePath":"cn/src/ranuts/file/writeFile.md","lastUpdated":1728129344000}'),i={name:"cn/src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

          WriteFile

          将内容写入文件

          API

          Return

          • Promise
          参数说明类型描述
          success是否写入成功booleantrue 成功 false 失败
          data写入失败的原因,添加成功后的文件内容和文件路径any

          Options

          参数说明类型默认值
          path文件路径,需要追加的文件stringundefined
          content需要追加的内容string
          ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; diff --git a/assets/cn_src_ranuts_index.md.B_7KgyI-.js b/assets/cn_src_ranuts_index.md.Br1tGjNi.js similarity index 97% rename from assets/cn_src_ranuts_index.md.B_7KgyI-.js rename to assets/cn_src_ranuts_index.md.Br1tGjNi.js index 06c662aa51..6b33408765 100644 --- a/assets/cn_src_ranuts_index.md.B_7KgyI-.js +++ b/assets/cn_src_ranuts_index.md.Br1tGjNi.js @@ -1 +1 @@ -import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/index.md","filePath":"cn/src/ranuts/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/index.md"};function s(h,t,m,f,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

          ranuts overview

          方法列表

          方法说明详细内容
          writeFile写入文件writeFile
          readFile读取文件readFile
          readDir读取目录,获取目录下所有文件的名字readDir
          watchFile观察文件的内容是否发生变化watchFile
          queryFileInfo查询文件信息queryFileInfo
          filterObj过滤对象filterObj
          EventEmitter发布订阅类EventEmitter
          str2Xml字符串转成xmlstr2Xml
          getMime根据文件格式后缀获取 mime typegetMime
          getCookie获取指定 cookie 的值writeFile
          formatJson格式化 JSONformatJson

          TOTP

          ',4)),o(e)])}const v=r(n,[["render",s]]);export{b as __pageData,v as default}; +import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/index.md","filePath":"cn/src/ranuts/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/index.md"};function s(h,t,m,f,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

          ranuts overview

          方法列表

          方法说明详细内容
          writeFile写入文件writeFile
          readFile读取文件readFile
          readDir读取目录,获取目录下所有文件的名字readDir
          watchFile观察文件的内容是否发生变化watchFile
          queryFileInfo查询文件信息queryFileInfo
          filterObj过滤对象filterObj
          EventEmitter发布订阅类EventEmitter
          str2Xml字符串转成xmlstr2Xml
          getMime根据文件格式后缀获取 mime typegetMime
          getCookie获取指定 cookie 的值writeFile
          formatJson格式化 JSONformatJson

          TOTP

          ',4)),o(e)])}const v=r(n,[["render",s]]);export{b as __pageData,v as default}; diff --git a/assets/cn_src_ranuts_index.md.B_7KgyI-.lean.js b/assets/cn_src_ranuts_index.md.Br1tGjNi.lean.js similarity index 97% rename from assets/cn_src_ranuts_index.md.B_7KgyI-.lean.js rename to assets/cn_src_ranuts_index.md.Br1tGjNi.lean.js index 06c662aa51..6b33408765 100644 --- a/assets/cn_src_ranuts_index.md.B_7KgyI-.lean.js +++ b/assets/cn_src_ranuts_index.md.Br1tGjNi.lean.js @@ -1 +1 @@ -import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/index.md","filePath":"cn/src/ranuts/index.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/index.md"};function s(h,t,m,f,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

          ranuts overview

          方法列表

          方法说明详细内容
          writeFile写入文件writeFile
          readFile读取文件readFile
          readDir读取目录,获取目录下所有文件的名字readDir
          watchFile观察文件的内容是否发生变化watchFile
          queryFileInfo查询文件信息queryFileInfo
          filterObj过滤对象filterObj
          EventEmitter发布订阅类EventEmitter
          str2Xml字符串转成xmlstr2Xml
          getMime根据文件格式后缀获取 mime typegetMime
          getCookie获取指定 cookie 的值writeFile
          formatJson格式化 JSONformatJson

          TOTP

          ',4)),o(e)])}const v=r(n,[["render",s]]);export{b as __pageData,v as default}; +import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/index.md","filePath":"cn/src/ranuts/index.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/index.md"};function s(h,t,m,f,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

          ranuts overview

          方法列表

          方法说明详细内容
          writeFile写入文件writeFile
          readFile读取文件readFile
          readDir读取目录,获取目录下所有文件的名字readDir
          watchFile观察文件的内容是否发生变化watchFile
          queryFileInfo查询文件信息queryFileInfo
          filterObj过滤对象filterObj
          EventEmitter发布订阅类EventEmitter
          str2Xml字符串转成xmlstr2Xml
          getMime根据文件格式后缀获取 mime typegetMime
          getCookie获取指定 cookie 的值writeFile
          formatJson格式化 JSONformatJson

          TOTP

          ',4)),o(e)])}const v=r(n,[["render",s]]);export{b as __pageData,v as default}; diff --git a/assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.js b/assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.js similarity index 98% rename from assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.js rename to assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.js index f9ed3bb0be..8c257e79cb 100644 --- a/assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.js +++ b/assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.js @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mimeType/mimeType.md","filePath":"cn/src/ranuts/mimeType/mimeType.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          getMime

          传入文件格式后缀,返回mime type

          API

          Return

          参数说明类型
          string返回mime typestring

          Options

          参数说明类型默认值
          ext文件后缀格式string

          Example

          js
          import { getMime } from 'ranuts';
          +import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mimeType/mimeType.md","filePath":"cn/src/ranuts/mimeType/mimeType.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          getMime

          传入文件格式后缀,返回mime type

          API

          Return

          参数说明类型
          string返回mime typestring

          Options

          参数说明类型默认值
          ext文件后缀格式string

          Example

          js
          import { getMime } from 'ranuts';
           
           const result = getMime('.pptx');
           console.log(result);
          diff --git a/assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.lean.js b/assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.lean.js
          similarity index 98%
          rename from assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.lean.js
          rename to assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.lean.js
          index f9ed3bb0be..8c257e79cb 100644
          --- a/assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.lean.js
          +++ b/assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mimeType/mimeType.md","filePath":"cn/src/ranuts/mimeType/mimeType.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          getMime

          传入文件格式后缀,返回mime type

          API

          Return

          参数说明类型
          string返回mime typestring

          Options

          参数说明类型默认值
          ext文件后缀格式string

          Example

          js
          import { getMime } from 'ranuts';
          +import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mimeType/mimeType.md","filePath":"cn/src/ranuts/mimeType/mimeType.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

          getMime

          传入文件格式后缀,返回mime type

          API

          Return

          参数说明类型
          string返回mime typestring

          Options

          参数说明类型默认值
          ext文件后缀格式string

          Example

          js
          import { getMime } from 'ranuts';
           
           const result = getMime('.pptx');
           console.log(result);
          diff --git a/assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.js b/assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.js
          similarity index 99%
          rename from assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.js
          rename to assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.js
          index 48d866d9f4..c273d6ec06 100644
          --- a/assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.js
          +++ b/assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mode/subscribe.md","filePath":"cn/src/ranuts/mode/subscribe.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          EventEmitter

          发布订阅的类

          Class

          Methods

          方法参数说明默认值
          on订阅事件订阅事件,传入参数事件名,回调函数
          once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
          off取消订阅事件,传入参数事件名,回调函数取消订阅事件
          emit触发事件,需要事件名触发事件

          Example

          js
          import { Subscribe } from 'ranuts';
          +import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mode/subscribe.md","filePath":"cn/src/ranuts/mode/subscribe.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          EventEmitter

          发布订阅的类

          Class

          Methods

          方法参数说明默认值
          on订阅事件订阅事件,传入参数事件名,回调函数
          once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
          off取消订阅事件,传入参数事件名,回调函数取消订阅事件
          emit触发事件,需要事件名触发事件

          Example

          js
          import { Subscribe } from 'ranuts';
           
           const subscribe = new Subscribe();
           
          diff --git a/assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.lean.js b/assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.lean.js
          similarity index 99%
          rename from assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.lean.js
          rename to assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.lean.js
          index 48d866d9f4..c273d6ec06 100644
          --- a/assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.lean.js
          +++ b/assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mode/subscribe.md","filePath":"cn/src/ranuts/mode/subscribe.md","lastUpdated":1728129125000}'),h={name:"cn/src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          EventEmitter

          发布订阅的类

          Class

          Methods

          方法参数说明默认值
          on订阅事件订阅事件,传入参数事件名,回调函数
          once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
          off取消订阅事件,传入参数事件名,回调函数取消订阅事件
          emit触发事件,需要事件名触发事件

          Example

          js
          import { Subscribe } from 'ranuts';
          +import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/mode/subscribe.md","filePath":"cn/src/ranuts/mode/subscribe.md","lastUpdated":1728129344000}'),h={name:"cn/src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

          EventEmitter

          发布订阅的类

          Class

          Methods

          方法参数说明默认值
          on订阅事件订阅事件,传入参数事件名,回调函数
          once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
          off取消订阅事件,传入参数事件名,回调函数取消订阅事件
          emit触发事件,需要事件名触发事件

          Example

          js
          import { Subscribe } from 'ranuts';
           
           const subscribe = new Subscribe();
           
          diff --git a/assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.js b/assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.js
          rename to assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.js
          index 2d10cc4b62..0b1ab297cf 100644
          --- a/assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.js
          +++ b/assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as s,c as e,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/convertImageToBase64.md","filePath":"cn/src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/utils/convertImageToBase64.md"};function d(r,a,o,h,l,p){return s(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          convertImageToBase64

          图片转base64

          API

          Return

          参数说明类型
          success是否转换成功boolean
          data转换成功后的值string,ArrayBuffer , null
          message转换成功或失败的原因string

          Options

          参数说明类型默认值
          file传入的文件File

          Example

          js
          import { convertImageToBase64 } from 'ranuts';
          +import{_ as t,o as s,c as e,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/convertImageToBase64.md","filePath":"cn/src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/utils/convertImageToBase64.md"};function d(r,a,o,h,l,p){return s(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          convertImageToBase64

          图片转base64

          API

          Return

          参数说明类型
          success是否转换成功boolean
          data转换成功后的值string,ArrayBuffer , null
          message转换成功或失败的原因string

          Options

          参数说明类型默认值
          file传入的文件File

          Example

          js
          import { convertImageToBase64 } from 'ranuts';
           
           convertImageToBase64(file).then((res) => {
             console.log(result);
          diff --git a/assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.lean.js b/assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.lean.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.lean.js
          rename to assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.lean.js
          index 2d10cc4b62..0b1ab297cf 100644
          --- a/assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.lean.js
          +++ b/assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as s,c as e,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/convertImageToBase64.md","filePath":"cn/src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/utils/convertImageToBase64.md"};function d(r,a,o,h,l,p){return s(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          convertImageToBase64

          图片转base64

          API

          Return

          参数说明类型
          success是否转换成功boolean
          data转换成功后的值string,ArrayBuffer , null
          message转换成功或失败的原因string

          Options

          参数说明类型默认值
          file传入的文件File

          Example

          js
          import { convertImageToBase64 } from 'ranuts';
          +import{_ as t,o as s,c as e,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/convertImageToBase64.md","filePath":"cn/src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/utils/convertImageToBase64.md"};function d(r,a,o,h,l,p){return s(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

          convertImageToBase64

          图片转base64

          API

          Return

          参数说明类型
          success是否转换成功boolean
          data转换成功后的值string,ArrayBuffer , null
          message转换成功或失败的原因string

          Options

          参数说明类型默认值
          file传入的文件File

          Example

          js
          import { convertImageToBase64 } from 'ranuts';
           
           convertImageToBase64(file).then((res) => {
             console.log(result);
          diff --git a/assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.js b/assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.js
          rename to assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.js
          index f38577c8cd..4ca02e3c97 100644
          --- a/assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.js
          +++ b/assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.js
          @@ -1,4 +1,4 @@
          -import{_ as a,o as i,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/filterObj.md","filePath":"cn/src/ranuts/utils/filterObj.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranuts/utils/filterObj.md"};function l(h,s,p,r,d,k){return i(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          filterObj

          过滤对象的属性,去除对象中在 list 数组里面有的属性,返回一个新对象,一般是用于去除空字符和 null

          API

          Return

          参数说明类型
          Object返回的一个对象Object

          Options

          参数说明类型默认值
          obj需要过滤的对象object
          list需要过滤的熟悉数组array

          Example

          js
          import { filterObj } from 'ranuts';
          +import{_ as a,o as i,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/filterObj.md","filePath":"cn/src/ranuts/utils/filterObj.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranuts/utils/filterObj.md"};function l(h,s,p,r,d,k){return i(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          filterObj

          过滤对象的属性,去除对象中在 list 数组里面有的属性,返回一个新对象,一般是用于去除空字符和 null

          API

          Return

          参数说明类型
          Object返回的一个对象Object

          Options

          参数说明类型默认值
          obj需要过滤的对象object
          list需要过滤的熟悉数组array

          Example

          js
          import { filterObj } from 'ranuts';
           
           const obj = {
             name: 'chaxus',
          diff --git a/assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.lean.js b/assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.lean.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.lean.js
          rename to assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.lean.js
          index f38577c8cd..4ca02e3c97 100644
          --- a/assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.lean.js
          +++ b/assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as a,o as i,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/filterObj.md","filePath":"cn/src/ranuts/utils/filterObj.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranuts/utils/filterObj.md"};function l(h,s,p,r,d,k){return i(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          filterObj

          过滤对象的属性,去除对象中在 list 数组里面有的属性,返回一个新对象,一般是用于去除空字符和 null

          API

          Return

          参数说明类型
          Object返回的一个对象Object

          Options

          参数说明类型默认值
          obj需要过滤的对象object
          list需要过滤的熟悉数组array

          Example

          js
          import { filterObj } from 'ranuts';
          +import{_ as a,o as i,c as t,a3 as n}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/filterObj.md","filePath":"cn/src/ranuts/utils/filterObj.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranuts/utils/filterObj.md"};function l(h,s,p,r,d,k){return i(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          filterObj

          过滤对象的属性,去除对象中在 list 数组里面有的属性,返回一个新对象,一般是用于去除空字符和 null

          API

          Return

          参数说明类型
          Object返回的一个对象Object

          Options

          参数说明类型默认值
          obj需要过滤的对象object
          list需要过滤的熟悉数组array

          Example

          js
          import { filterObj } from 'ranuts';
           
           const obj = {
             name: 'chaxus',
          diff --git a/assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.js b/assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.js
          rename to assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.js
          index a17d782c3c..ee8c3886a0 100644
          --- a/assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.js
          +++ b/assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.js
          @@ -1,4 +1,4 @@
          -import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/formatJson.md","filePath":"cn/src/ranuts/utils/formatJson.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranuts/utils/formatJson.md"};function h(l,s,r,d,p,o){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          formatJson

          传入一个 JSON 或者 JSON 的字符串,添加空格和换行进行返回一个格式化的 JSON 字符串

          API

          Return

          参数说明类型
          string返回的一个对象Object

          Options

          参数说明类型默认值
          json需要格式化的 JSON 对象object,string
          callback错误回调,可选function

          Example

          js
          import { formatJson } from 'ranuts';
          +import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/formatJson.md","filePath":"cn/src/ranuts/utils/formatJson.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranuts/utils/formatJson.md"};function h(l,s,r,d,p,o){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          formatJson

          传入一个 JSON 或者 JSON 的字符串,添加空格和换行进行返回一个格式化的 JSON 字符串

          API

          Return

          参数说明类型
          string返回的一个对象Object

          Options

          参数说明类型默认值
          json需要格式化的 JSON 对象object,string
          callback错误回调,可选function

          Example

          js
          import { formatJson } from 'ranuts';
           
           const json = {
             name: 'chaxus',
          diff --git a/assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.lean.js b/assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.lean.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.lean.js
          rename to assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.lean.js
          index a17d782c3c..ee8c3886a0 100644
          --- a/assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.lean.js
          +++ b/assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/formatJson.md","filePath":"cn/src/ranuts/utils/formatJson.md","lastUpdated":1728129125000}'),e={name:"cn/src/ranuts/utils/formatJson.md"};function h(l,s,r,d,p,o){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          formatJson

          传入一个 JSON 或者 JSON 的字符串,添加空格和换行进行返回一个格式化的 JSON 字符串

          API

          Return

          参数说明类型
          string返回的一个对象Object

          Options

          参数说明类型默认值
          json需要格式化的 JSON 对象object,string
          callback错误回调,可选function

          Example

          js
          import { formatJson } from 'ranuts';
          +import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/formatJson.md","filePath":"cn/src/ranuts/utils/formatJson.md","lastUpdated":1728129344000}'),e={name:"cn/src/ranuts/utils/formatJson.md"};function h(l,s,r,d,p,o){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

          formatJson

          传入一个 JSON 或者 JSON 的字符串,添加空格和换行进行返回一个格式化的 JSON 字符串

          API

          Return

          参数说明类型
          string返回的一个对象Object

          Options

          参数说明类型默认值
          json需要格式化的 JSON 对象object,string
          callback错误回调,可选function

          Example

          js
          import { formatJson } from 'ranuts';
           
           const json = {
             name: 'chaxus',
          diff --git a/assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.js b/assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.js
          rename to assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.js
          index 60b02ac7d9..452f9f490a 100644
          --- a/assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.js
          +++ b/assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as s,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/getCookie.md","filePath":"cn/src/ranuts/utils/getCookie.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/utils/getCookie.md"};function h(o,a,l,r,d,p){return s(),i("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          getCookie

          传入字符串,获取指定名字的 cookie 的值

          API

          Return

          参数说明类型
          sting返回的一个指定名称的 cookie 的值string

          Options

          参数说明类型默认值
          name指定获取 cookie 的名称的值object

          Example

          js
          import { getCookie } from 'ranuts';
          +import{_ as t,o as s,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/getCookie.md","filePath":"cn/src/ranuts/utils/getCookie.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/utils/getCookie.md"};function h(o,a,l,r,d,p){return s(),i("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          getCookie

          传入字符串,获取指定名字的 cookie 的值

          API

          Return

          参数说明类型
          sting返回的一个指定名称的 cookie 的值string

          Options

          参数说明类型默认值
          name指定获取 cookie 的名称的值object

          Example

          js
          import { getCookie } from 'ranuts';
           
           const result = getCookie('name');
           
          diff --git a/assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.lean.js b/assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.lean.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.lean.js
          rename to assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.lean.js
          index 60b02ac7d9..452f9f490a 100644
          --- a/assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.lean.js
          +++ b/assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as t,o as s,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/getCookie.md","filePath":"cn/src/ranuts/utils/getCookie.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/utils/getCookie.md"};function h(o,a,l,r,d,p){return s(),i("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          getCookie

          传入字符串,获取指定名字的 cookie 的值

          API

          Return

          参数说明类型
          sting返回的一个指定名称的 cookie 的值string

          Options

          参数说明类型默认值
          name指定获取 cookie 的名称的值object

          Example

          js
          import { getCookie } from 'ranuts';
          +import{_ as t,o as s,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/getCookie.md","filePath":"cn/src/ranuts/utils/getCookie.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/utils/getCookie.md"};function h(o,a,l,r,d,p){return s(),i("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

          getCookie

          传入字符串,获取指定名字的 cookie 的值

          API

          Return

          参数说明类型
          sting返回的一个指定名称的 cookie 的值string

          Options

          参数说明类型默认值
          name指定获取 cookie 的名称的值object

          Example

          js
          import { getCookie } from 'ranuts';
           
           const result = getCookie('name');
           
          diff --git a/assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.js b/assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.js
          similarity index 99%
          rename from assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.js
          rename to assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.js
          index cf4646a30e..f34a386d11 100644
          --- a/assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.js
          +++ b/assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.js
          @@ -1,4 +1,4 @@
          -import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/ocr.md","filePath":"cn/src/ranuts/utils/ocr.md","lastUpdated":1728129125000}'),s={name:"cn/src/ranuts/utils/ocr.md"};function n(e,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

          OCR

          传入图片和对应的语言类型,返回图片中的文本。

          API

          Return

          参数说明类型
          success是否解析成功boolean
          data解析成功后的对象obj
          message解析成功或失败的原因string

          Options

          参数说明类型默认值
          images图片的数组,支持urlbase64Array<string>
          language指定生成文本的语言,具体参数见lang-codestring
          langPath使用的时候需要能访问cdn.jsdelivr.net,会下载对应的语言包,如果无法访问,也可以将语言包放在本地,传入对应的 目录 路径string可选参数,默认走网络下载

          Example

          js
          import { ocr } from 'ranuts';
          +import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/ocr.md","filePath":"cn/src/ranuts/utils/ocr.md","lastUpdated":1728129344000}'),s={name:"cn/src/ranuts/utils/ocr.md"};function n(e,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

          OCR

          传入图片和对应的语言类型,返回图片中的文本。

          API

          Return

          参数说明类型
          success是否解析成功boolean
          data解析成功后的对象obj
          message解析成功或失败的原因string

          Options

          参数说明类型默认值
          images图片的数组,支持urlbase64Array<string>
          language指定生成文本的语言,具体参数见lang-codestring
          langPath使用的时候需要能访问cdn.jsdelivr.net,会下载对应的语言包,如果无法访问,也可以将语言包放在本地,传入对应的 目录 路径string可选参数,默认走网络下载

          Example

          js
          import { ocr } from 'ranuts';
           
           const images = ['https://chaxus.github.io/ran/ocr/eng.png'];
           const languages = 'eng';
          diff --git a/assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.lean.js b/assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.lean.js
          similarity index 99%
          rename from assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.lean.js
          rename to assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.lean.js
          index cf4646a30e..f34a386d11 100644
          --- a/assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.lean.js
          +++ b/assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/ocr.md","filePath":"cn/src/ranuts/utils/ocr.md","lastUpdated":1728129125000}'),s={name:"cn/src/ranuts/utils/ocr.md"};function n(e,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

          OCR

          传入图片和对应的语言类型,返回图片中的文本。

          API

          Return

          参数说明类型
          success是否解析成功boolean
          data解析成功后的对象obj
          message解析成功或失败的原因string

          Options

          参数说明类型默认值
          images图片的数组,支持urlbase64Array<string>
          language指定生成文本的语言,具体参数见lang-codestring
          langPath使用的时候需要能访问cdn.jsdelivr.net,会下载对应的语言包,如果无法访问,也可以将语言包放在本地,传入对应的 目录 路径string可选参数,默认走网络下载

          Example

          js
          import { ocr } from 'ranuts';
          +import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/ocr.md","filePath":"cn/src/ranuts/utils/ocr.md","lastUpdated":1728129344000}'),s={name:"cn/src/ranuts/utils/ocr.md"};function n(e,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

          OCR

          传入图片和对应的语言类型,返回图片中的文本。

          API

          Return

          参数说明类型
          success是否解析成功boolean
          data解析成功后的对象obj
          message解析成功或失败的原因string

          Options

          参数说明类型默认值
          images图片的数组,支持urlbase64Array<string>
          language指定生成文本的语言,具体参数见lang-codestring
          langPath使用的时候需要能访问cdn.jsdelivr.net,会下载对应的语言包,如果无法访问,也可以将语言包放在本地,传入对应的 目录 路径string可选参数,默认走网络下载

          Example

          js
          import { ocr } from 'ranuts';
           
           const images = ['https://chaxus.github.io/ran/ocr/eng.png'];
           const languages = 'eng';
          diff --git a/assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.js b/assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.js
          rename to assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.js
          index bf697e368a..8a25082df2 100644
          --- a/assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.js
          +++ b/assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.js
          @@ -1,4 +1,4 @@
          -import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/str2xml.md","filePath":"cn/src/ranuts/utils/str2xml.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/utils/str2xml.md"};function l(d,t,h,o,r,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

          str2Xml

          传入字符串,转成xml

          API

          Return

          参数说明类型
          HTMLElement返回一个HTMLElementHTMLElement

          Options

          参数说明类型默认值
          xmlStr传入的参数string
          format设置需要转换的格式,默认text/xmlDOMParserSupportedType

          Example

          比如在做图标库的时候,我们需要动态导入目录下的所有icon。这时候导入的是字符串,但字符串无法添加到xml中。 因此我们需要将字符串转换成xml,然后就可以将它加入到xml中。

          js
          import { str2Xml } from 'ranuts';
          +import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/str2xml.md","filePath":"cn/src/ranuts/utils/str2xml.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/utils/str2xml.md"};function l(d,t,h,o,r,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

          str2Xml

          传入字符串,转成xml

          API

          Return

          参数说明类型
          HTMLElement返回一个HTMLElementHTMLElement

          Options

          参数说明类型默认值
          xmlStr传入的参数string
          format设置需要转换的格式,默认text/xmlDOMParserSupportedType

          Example

          比如在做图标库的时候,我们需要动态导入目录下的所有icon。这时候导入的是字符串,但字符串无法添加到xml中。 因此我们需要将字符串转换成xml,然后就可以将它加入到xml中。

          js
          import { str2Xml } from 'ranuts';
           
           // import 'assets/*.svg'
           const svg = \`<svg t="1667483498347" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8544" width="200" height="200"><path d="M858.5 763.6c-18.9-44.8-46.1-85-80.6-119.5-34.5-34.5-74.7-61.6-119.5-80.6-0.4-0.2-0.8-0.3-1.2-0.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-0.4 0.2-0.8 0.3-1.2 0.5-44.8 18.9-85 46-119.5 80.6-34.5 34.5-61.6 74.7-80.6 119.5C146.9 807.5 137 854 136 901.8c-0.1 4.5 3.5 8.2 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c0.1 4.4 3.6 7.8 8 7.8h60c4.5 0 8.1-3.7 8-8.2-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z" p-id="8545"></path></svg>\`;
          diff --git a/assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.lean.js b/assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.lean.js
          similarity index 98%
          rename from assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.lean.js
          rename to assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.lean.js
          index bf697e368a..8a25082df2 100644
          --- a/assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.lean.js
          +++ b/assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/str2xml.md","filePath":"cn/src/ranuts/utils/str2xml.md","lastUpdated":1728129125000}'),n={name:"cn/src/ranuts/utils/str2xml.md"};function l(d,t,h,o,r,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

          str2Xml

          传入字符串,转成xml

          API

          Return

          参数说明类型
          HTMLElement返回一个HTMLElementHTMLElement

          Options

          参数说明类型默认值
          xmlStr传入的参数string
          format设置需要转换的格式,默认text/xmlDOMParserSupportedType

          Example

          比如在做图标库的时候,我们需要动态导入目录下的所有icon。这时候导入的是字符串,但字符串无法添加到xml中。 因此我们需要将字符串转换成xml,然后就可以将它加入到xml中。

          js
          import { str2Xml } from 'ranuts';
          +import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/str2xml.md","filePath":"cn/src/ranuts/utils/str2xml.md","lastUpdated":1728129344000}'),n={name:"cn/src/ranuts/utils/str2xml.md"};function l(d,t,h,o,r,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

          str2Xml

          传入字符串,转成xml

          API

          Return

          参数说明类型
          HTMLElement返回一个HTMLElementHTMLElement

          Options

          参数说明类型默认值
          xmlStr传入的参数string
          format设置需要转换的格式,默认text/xmlDOMParserSupportedType

          Example

          比如在做图标库的时候,我们需要动态导入目录下的所有icon。这时候导入的是字符串,但字符串无法添加到xml中。 因此我们需要将字符串转换成xml,然后就可以将它加入到xml中。

          js
          import { str2Xml } from 'ranuts';
           
           // import 'assets/*.svg'
           const svg = \`<svg t="1667483498347" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8544" width="200" height="200"><path d="M858.5 763.6c-18.9-44.8-46.1-85-80.6-119.5-34.5-34.5-74.7-61.6-119.5-80.6-0.4-0.2-0.8-0.3-1.2-0.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-0.4 0.2-0.8 0.3-1.2 0.5-44.8 18.9-85 46-119.5 80.6-34.5 34.5-61.6 74.7-80.6 119.5C146.9 807.5 137 854 136 901.8c-0.1 4.5 3.5 8.2 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c0.1 4.4 3.6 7.8 8 7.8h60c4.5 0 8.1-3.7 8-8.2-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z" p-id="8545"></path></svg>\`;
          diff --git a/assets/cn_src_ranuts_utils_task.md.BLYztfpY.js b/assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.js
          similarity index 99%
          rename from assets/cn_src_ranuts_utils_task.md.BLYztfpY.js
          rename to assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.js
          index 5db2c7332b..4656cf9b5b 100644
          --- a/assets/cn_src_ranuts_utils_task.md.BLYztfpY.js
          +++ b/assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.js
          @@ -1 +1 @@
          -import{_ as a,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"统计执行时间","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/task.md","filePath":"cn/src/ranuts/utils/task.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return t(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          统计执行时间

          有的时候,我们需要统计一个函数的执行时间,用于分析性能。因此封装了startTasktaskEnd函数。同时介绍其他三种统计方法

          1. new Date().getTime(),
          2. console.time()console.timeEnd(),
          3. performance.now()

          一.startTask,taskEnd

          1.startTask

          任务开始之前执行

          Return

          参数说明类型
          taskId任务标识unique symbol

          2.taskEnd

          任务结束的时候执行,需要传入startTask返回的任务标识

          Options

          参数说明类型默认值
          taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

          Return

          参数说明类型
          timetask执行的时间number

          3.使用例子

          js
          const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

          二.new Date().getTime()

          new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

          1. 某些情况下,毫秒级精度可能不够。
          2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

            由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

          三.console.time(), console.timeEnd()

          启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

          四.performance.now()

          performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

          注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

          ',24)]))}const m=a(o,[["render",n]]);export{k as __pageData,m as default}; +import{_ as a,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"统计执行时间","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/task.md","filePath":"cn/src/ranuts/utils/task.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return t(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          统计执行时间

          有的时候,我们需要统计一个函数的执行时间,用于分析性能。因此封装了startTasktaskEnd函数。同时介绍其他三种统计方法

          1. new Date().getTime(),
          2. console.time()console.timeEnd(),
          3. performance.now()

          一.startTask,taskEnd

          1.startTask

          任务开始之前执行

          Return

          参数说明类型
          taskId任务标识unique symbol

          2.taskEnd

          任务结束的时候执行,需要传入startTask返回的任务标识

          Options

          参数说明类型默认值
          taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

          Return

          参数说明类型
          timetask执行的时间number

          3.使用例子

          js
          const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

          二.new Date().getTime()

          new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

          1. 某些情况下,毫秒级精度可能不够。
          2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

            由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

          三.console.time(), console.timeEnd()

          启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

          四.performance.now()

          performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

          注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

          ',24)]))}const m=a(o,[["render",n]]);export{k as __pageData,m as default}; diff --git a/assets/cn_src_ranuts_utils_task.md.BLYztfpY.lean.js b/assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.lean.js similarity index 99% rename from assets/cn_src_ranuts_utils_task.md.BLYztfpY.lean.js rename to assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.lean.js index 5db2c7332b..4656cf9b5b 100644 --- a/assets/cn_src_ranuts_utils_task.md.BLYztfpY.lean.js +++ b/assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.lean.js @@ -1 +1 @@ -import{_ as a,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"统计执行时间","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/task.md","filePath":"cn/src/ranuts/utils/task.md","lastUpdated":1728129125000}'),o={name:"cn/src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return t(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          统计执行时间

          有的时候,我们需要统计一个函数的执行时间,用于分析性能。因此封装了startTasktaskEnd函数。同时介绍其他三种统计方法

          1. new Date().getTime(),
          2. console.time()console.timeEnd(),
          3. performance.now()

          一.startTask,taskEnd

          1.startTask

          任务开始之前执行

          Return

          参数说明类型
          taskId任务标识unique symbol

          2.taskEnd

          任务结束的时候执行,需要传入startTask返回的任务标识

          Options

          参数说明类型默认值
          taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

          Return

          参数说明类型
          timetask执行的时间number

          3.使用例子

          js
          const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

          二.new Date().getTime()

          new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

          1. 某些情况下,毫秒级精度可能不够。
          2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

            由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

          三.console.time(), console.timeEnd()

          启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

          四.performance.now()

          performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

          注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

          ',24)]))}const m=a(o,[["render",n]]);export{k as __pageData,m as default}; +import{_ as a,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"统计执行时间","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/ranuts/utils/task.md","filePath":"cn/src/ranuts/utils/task.md","lastUpdated":1728129344000}'),o={name:"cn/src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return t(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

          统计执行时间

          有的时候,我们需要统计一个函数的执行时间,用于分析性能。因此封装了startTasktaskEnd函数。同时介绍其他三种统计方法

          1. new Date().getTime(),
          2. console.time()console.timeEnd(),
          3. performance.now()

          一.startTask,taskEnd

          1.startTask

          任务开始之前执行

          Return

          参数说明类型
          taskId任务标识unique symbol

          2.taskEnd

          任务结束的时候执行,需要传入startTask返回的任务标识

          Options

          参数说明类型默认值
          taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

          Return

          参数说明类型
          timetask执行的时间number

          3.使用例子

          js
          const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

          二.new Date().getTime()

          new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

          1. 某些情况下,毫秒级精度可能不够。
          2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

            由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

          三.console.time(), console.timeEnd()

          启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

          四.performance.now()

          performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

          注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

          ',24)]))}const m=a(o,[["render",n]]);export{k as __pageData,m as default}; diff --git "a/assets/cn_src_types_TS\347\261\273\345\236\213.md.ctrtnDXL.js" "b/assets/cn_src_types_TS\347\261\273\345\236\213.md.BqYfr0BF.js" similarity index 99% rename from "assets/cn_src_types_TS\347\261\273\345\236\213.md.ctrtnDXL.js" rename to "assets/cn_src_types_TS\347\261\273\345\236\213.md.BqYfr0BF.js" index 131e357c8a..9548782ffc 100644 --- "a/assets/cn_src_types_TS\347\261\273\345\236\213.md.ctrtnDXL.js" +++ "b/assets/cn_src_types_TS\347\261\273\345\236\213.md.BqYfr0BF.js" @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/TS类型.md","filePath":"cn/src/types/TS类型.md","lastUpdated":1728129125000}'),l={name:"cn/src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          TypeScript 类型系统中的类型

          1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
          2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
          3. 特殊的类型:void、never、any、unknown

          Tuple

          元组(Tuple)就是元素个数和类型固定的数组类型:

          ts
          type Tuple = [number, string];

          Interface

          接口(Interface)可以描述函数、对象、构造器的结构:

          • 对象
          ts
          interface IPerson {
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/TS类型.md","filePath":"cn/src/types/TS类型.md","lastUpdated":1728129344000}'),l={name:"cn/src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          TypeScript 类型系统中的类型

          1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
          2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
          3. 特殊的类型:void、never、any、unknown

          Tuple

          元组(Tuple)就是元素个数和类型固定的数组类型:

          ts
          type Tuple = [number, string];

          Interface

          接口(Interface)可以描述函数、对象、构造器的结构:

          • 对象
          ts
          interface IPerson {
             name: string;
             age: number;
           }
          diff --git "a/assets/cn_src_types_TS\347\261\273\345\236\213.md.ctrtnDXL.lean.js" "b/assets/cn_src_types_TS\347\261\273\345\236\213.md.BqYfr0BF.lean.js"
          similarity index 99%
          rename from "assets/cn_src_types_TS\347\261\273\345\236\213.md.ctrtnDXL.lean.js"
          rename to "assets/cn_src_types_TS\347\261\273\345\236\213.md.BqYfr0BF.lean.js"
          index 131e357c8a..9548782ffc 100644
          --- "a/assets/cn_src_types_TS\347\261\273\345\236\213.md.ctrtnDXL.lean.js"
          +++ "b/assets/cn_src_types_TS\347\261\273\345\236\213.md.BqYfr0BF.lean.js"
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/TS类型.md","filePath":"cn/src/types/TS类型.md","lastUpdated":1728129125000}'),l={name:"cn/src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          TypeScript 类型系统中的类型

          1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
          2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
          3. 特殊的类型:void、never、any、unknown

          Tuple

          元组(Tuple)就是元素个数和类型固定的数组类型:

          ts
          type Tuple = [number, string];

          Interface

          接口(Interface)可以描述函数、对象、构造器的结构:

          • 对象
          ts
          interface IPerson {
          +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/TS类型.md","filePath":"cn/src/types/TS类型.md","lastUpdated":1728129344000}'),l={name:"cn/src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

          TypeScript 类型系统中的类型

          1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
          2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
          3. 特殊的类型:void、never、any、unknown

          Tuple

          元组(Tuple)就是元素个数和类型固定的数组类型:

          ts
          type Tuple = [number, string];

          Interface

          接口(Interface)可以描述函数、对象、构造器的结构:

          • 对象
          ts
          interface IPerson {
             name: string;
             age: number;
           }
          diff --git "a/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DWHjoF0h.js" "b/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.Cv2DtzlL.js"
          similarity index 99%
          rename from "assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DWHjoF0h.js"
          rename to "assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.Cv2DtzlL.js"
          index f8e05f2c40..f7b82906d8 100644
          --- "a/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DWHjoF0h.js"
          +++ "b/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.Cv2DtzlL.js"
          @@ -1 +1 @@
          -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/模式匹配.md","filePath":"cn/src/types/模式匹配.md","lastUpdated":1728129125000}'),k={name:"cn/src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          模式匹配

          Typescript 的类型也同样可以做模式匹配。

          比如这样一个 Promise 类型:

          ts
          type p = Promise<'value'>;

          我们想提取 value 的类型,可以这样做:

          ts
          type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

          通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

          ts
          type result = GetPromiseValue<Promise<'name'>>; // name

          数组类型

          数组类型想提取第一个元素的类型怎么做呢?

          ts
          type arr = [1, 2, 3];

          用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

          ts
          type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

          类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

          any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

          对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type result = GetArrayFirstItem<[1, 2, 3]>; // 1

          当类型参数 Arr 为 [] 时:

          ts
          type result = GetArrayFirstItem<[]>; // never

          可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

          ts
          type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

          我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

          ts
          type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

          如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type Result = Pop<[1, 2, 3]>; // [1,2]

          当类型参数 Arr 为 [] 时:

          ts
          type Result = Pop<[]>; // []

          同理可得 ShiftArr 的实现:

          ts
          type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

          字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

          判断字符串是否以某个前缀开头,也是通过模式匹配:

          ts
          type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

          需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

          用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

          字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

          比如实现字符串替换:

          ts
          type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

          声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

          用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

          用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

          Trim

          能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

          不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

          先实现 TrimRight:

          ts
          type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

          类型参数 Str 是要 Trim 的字符串。

          如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

          把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

          同理可得 TrimLeft:

          ts
          type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

          TrimRight 和 TrimLeft 结合就是 Trim:

          ts
          type Trim<S extends string> = TrimLeft<TrimRight<S>>;

          函数

          函数同样也可以做类型匹配,比如提取参数、返回值的类型。

          GetParameters

          函数类型可以通过模式匹配来提取参数的类型:

          ts
          type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

          类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

          Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

          返回提取到的参数类型 Args。

          GetReturnType

          ts
          type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

          Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

          参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

          GetThisParameterType

          方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

          但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

          这里的 this 类型同样也可以通过模式匹配提取出来:

          ts
          type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

          构造器

          构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

          同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

          GetInstanceType

          构造器类型可以用 interface 声明,使用 new(): xx 的语法。

          ts
          interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

          这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

          ts
          type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
          ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/模式匹配.md","filePath":"cn/src/types/模式匹配.md","lastUpdated":1728129344000}'),k={name:"cn/src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          模式匹配

          Typescript 的类型也同样可以做模式匹配。

          比如这样一个 Promise 类型:

          ts
          type p = Promise<'value'>;

          我们想提取 value 的类型,可以这样做:

          ts
          type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

          通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

          ts
          type result = GetPromiseValue<Promise<'name'>>; // name

          数组类型

          数组类型想提取第一个元素的类型怎么做呢?

          ts
          type arr = [1, 2, 3];

          用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

          ts
          type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

          类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

          any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

          对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type result = GetArrayFirstItem<[1, 2, 3]>; // 1

          当类型参数 Arr 为 [] 时:

          ts
          type result = GetArrayFirstItem<[]>; // never

          可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

          ts
          type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

          我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

          ts
          type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

          如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type Result = Pop<[1, 2, 3]>; // [1,2]

          当类型参数 Arr 为 [] 时:

          ts
          type Result = Pop<[]>; // []

          同理可得 ShiftArr 的实现:

          ts
          type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

          字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

          判断字符串是否以某个前缀开头,也是通过模式匹配:

          ts
          type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

          需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

          用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

          字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

          比如实现字符串替换:

          ts
          type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

          声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

          用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

          用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

          Trim

          能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

          不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

          先实现 TrimRight:

          ts
          type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

          类型参数 Str 是要 Trim 的字符串。

          如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

          把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

          同理可得 TrimLeft:

          ts
          type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

          TrimRight 和 TrimLeft 结合就是 Trim:

          ts
          type Trim<S extends string> = TrimLeft<TrimRight<S>>;

          函数

          函数同样也可以做类型匹配,比如提取参数、返回值的类型。

          GetParameters

          函数类型可以通过模式匹配来提取参数的类型:

          ts
          type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

          类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

          Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

          返回提取到的参数类型 Args。

          GetReturnType

          ts
          type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

          Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

          参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

          GetThisParameterType

          方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

          但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

          这里的 this 类型同样也可以通过模式匹配提取出来:

          ts
          type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

          构造器

          构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

          同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

          GetInstanceType

          构造器类型可以用 interface 声明,使用 new(): xx 的语法。

          ts
          interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

          这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

          ts
          type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
          ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git "a/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DWHjoF0h.lean.js" "b/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.Cv2DtzlL.lean.js" similarity index 99% rename from "assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DWHjoF0h.lean.js" rename to "assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.Cv2DtzlL.lean.js" index f8e05f2c40..f7b82906d8 100644 --- "a/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DWHjoF0h.lean.js" +++ "b/assets/cn_src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.Cv2DtzlL.lean.js" @@ -1 +1 @@ -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/模式匹配.md","filePath":"cn/src/types/模式匹配.md","lastUpdated":1728129125000}'),k={name:"cn/src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          模式匹配

          Typescript 的类型也同样可以做模式匹配。

          比如这样一个 Promise 类型:

          ts
          type p = Promise<'value'>;

          我们想提取 value 的类型,可以这样做:

          ts
          type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

          通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

          ts
          type result = GetPromiseValue<Promise<'name'>>; // name

          数组类型

          数组类型想提取第一个元素的类型怎么做呢?

          ts
          type arr = [1, 2, 3];

          用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

          ts
          type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

          类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

          any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

          对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type result = GetArrayFirstItem<[1, 2, 3]>; // 1

          当类型参数 Arr 为 [] 时:

          ts
          type result = GetArrayFirstItem<[]>; // never

          可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

          ts
          type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

          我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

          ts
          type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

          如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type Result = Pop<[1, 2, 3]>; // [1,2]

          当类型参数 Arr 为 [] 时:

          ts
          type Result = Pop<[]>; // []

          同理可得 ShiftArr 的实现:

          ts
          type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

          字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

          判断字符串是否以某个前缀开头,也是通过模式匹配:

          ts
          type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

          需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

          用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

          字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

          比如实现字符串替换:

          ts
          type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

          声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

          用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

          用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

          Trim

          能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

          不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

          先实现 TrimRight:

          ts
          type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

          类型参数 Str 是要 Trim 的字符串。

          如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

          把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

          同理可得 TrimLeft:

          ts
          type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

          TrimRight 和 TrimLeft 结合就是 Trim:

          ts
          type Trim<S extends string> = TrimLeft<TrimRight<S>>;

          函数

          函数同样也可以做类型匹配,比如提取参数、返回值的类型。

          GetParameters

          函数类型可以通过模式匹配来提取参数的类型:

          ts
          type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

          类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

          Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

          返回提取到的参数类型 Args。

          GetReturnType

          ts
          type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

          Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

          参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

          GetThisParameterType

          方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

          但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

          这里的 this 类型同样也可以通过模式匹配提取出来:

          ts
          type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

          构造器

          构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

          同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

          GetInstanceType

          构造器类型可以用 interface 声明,使用 new(): xx 的语法。

          ts
          interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

          这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

          ts
          type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
          ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/模式匹配.md","filePath":"cn/src/types/模式匹配.md","lastUpdated":1728129344000}'),k={name:"cn/src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

          模式匹配

          Typescript 的类型也同样可以做模式匹配。

          比如这样一个 Promise 类型:

          ts
          type p = Promise<'value'>;

          我们想提取 value 的类型,可以这样做:

          ts
          type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

          通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

          ts
          type result = GetPromiseValue<Promise<'name'>>; // name

          数组类型

          数组类型想提取第一个元素的类型怎么做呢?

          ts
          type arr = [1, 2, 3];

          用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

          ts
          type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

          类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

          any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

          对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type result = GetArrayFirstItem<[1, 2, 3]>; // 1

          当类型参数 Arr 为 [] 时:

          ts
          type result = GetArrayFirstItem<[]>; // never

          可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

          ts
          type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

          我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

          ts
          type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

          如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

          当类型参数 Arr 为 [1,2,3] 时:

          ts
          type Result = Pop<[1, 2, 3]>; // [1,2]

          当类型参数 Arr 为 [] 时:

          ts
          type Result = Pop<[]>; // []

          同理可得 ShiftArr 的实现:

          ts
          type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

          字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

          判断字符串是否以某个前缀开头,也是通过模式匹配:

          ts
          type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

          需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

          用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

          字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

          比如实现字符串替换:

          ts
          type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

          声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

          用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

          用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

          Trim

          能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

          不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

          先实现 TrimRight:

          ts
          type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

          类型参数 Str 是要 Trim 的字符串。

          如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

          把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

          同理可得 TrimLeft:

          ts
          type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

          TrimRight 和 TrimLeft 结合就是 Trim:

          ts
          type Trim<S extends string> = TrimLeft<TrimRight<S>>;

          函数

          函数同样也可以做类型匹配,比如提取参数、返回值的类型。

          GetParameters

          函数类型可以通过模式匹配来提取参数的类型:

          ts
          type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

          类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

          Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

          返回提取到的参数类型 Args。

          GetReturnType

          ts
          type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

          Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

          参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

          GetThisParameterType

          方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

          但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

          这里的 this 类型同样也可以通过模式匹配提取出来:

          ts
          type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

          构造器

          构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

          同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

          GetInstanceType

          构造器类型可以用 interface 声明,使用 new(): xx 的语法。

          ts
          interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

          这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

          ts
          type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
          ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git "a/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.BALncgle.js" "b/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DMkykj3f.js" similarity index 99% rename from "assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.BALncgle.js" rename to "assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DMkykj3f.js" index fc290f99c3..d0096fc23a 100644 --- "a/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.BALncgle.js" +++ "b/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DMkykj3f.js" @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/类型运算.md","filePath":"cn/src/types/类型运算.md","lastUpdated":1728129125000}'),n={name:"cn/src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          TypeScript 类型系统中的类型运算

          条件:extends ? :

          TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

          ts
          type isTwo<T> = T extends 2 ? true : false;
          +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/类型运算.md","filePath":"cn/src/types/类型运算.md","lastUpdated":1728129344000}'),n={name:"cn/src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          TypeScript 类型系统中的类型运算

          条件:extends ? :

          TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

          ts
          type isTwo<T> = T extends 2 ? true : false;
           
           type res = isTwo<1>; // true
           type res2 = isTwo<2>; // false

          这种类型也叫做高级类型。

          高级类型的特点是传入类型参数,经过一系列类型运算逻辑后,返回新的类型。

          推导:infer

          如何提取类型的一部分呢?答案是 infer。

          比如提取元组类型的第一个元素:

          ts
          type FirstTupleItem<Tuple extends unknown[]> = Tuple extends [infer T, ...inter R] ? T : never;
          diff --git "a/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.BALncgle.lean.js" "b/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DMkykj3f.lean.js"
          similarity index 99%
          rename from "assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.BALncgle.lean.js"
          rename to "assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DMkykj3f.lean.js"
          index fc290f99c3..d0096fc23a 100644
          --- "a/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.BALncgle.lean.js"
          +++ "b/assets/cn_src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DMkykj3f.lean.js"
          @@ -1,4 +1,4 @@
          -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/类型运算.md","filePath":"cn/src/types/类型运算.md","lastUpdated":1728129125000}'),n={name:"cn/src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          TypeScript 类型系统中的类型运算

          条件:extends ? :

          TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

          ts
          type isTwo<T> = T extends 2 ? true : false;
          +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/类型运算.md","filePath":"cn/src/types/类型运算.md","lastUpdated":1728129344000}'),n={name:"cn/src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

          TypeScript 类型系统中的类型运算

          条件:extends ? :

          TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

          ts
          type isTwo<T> = T extends 2 ? true : false;
           
           type res = isTwo<1>; // true
           type res2 = isTwo<2>; // false

          这种类型也叫做高级类型。

          高级类型的特点是传入类型参数,经过一系列类型运算逻辑后,返回新的类型。

          推导:infer

          如何提取类型的一部分呢?答案是 infer。

          比如提取元组类型的第一个元素:

          ts
          type FirstTupleItem<Tuple extends unknown[]> = Tuple extends [infer T, ...inter R] ? T : never;
          diff --git "a/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.C_sodMsN.js" "b/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BDtDighX.js"
          similarity index 98%
          rename from "assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.C_sodMsN.js"
          rename to "assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BDtDighX.js"
          index a390a9c2bc..f6ea72f03b 100644
          --- "a/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.C_sodMsN.js"
          +++ "b/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BDtDighX.js"
          @@ -1 +1 @@
          -import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/高级类型.md","filePath":"cn/src/types/高级类型.md","lastUpdated":1728129125000}'),o={name:"cn/src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          TypeScript 内置的高级类型

          Parameters

          Parameters 用于提取函数类型的参数类型。

          ReturnType

          ReturnType 用于提取函数类型的返回值类型。

          ConstructorParameters

          构造器类型和函数类型的区别就是可以被 new。

          Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

          InstanceType

          提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

          ThisParameterType

          OmitThisParameter

          Partial

          Required

          Readonly

          Pick

          Record

          Exclude

          Extract

          Omit

          Awaited

          NonNullable

          Uppercase

          Lowercase

          Capitalize

          Uncapitalize

          总结

          比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

          用模式匹配 + 重新构造可以实现:OmitThisParameter

          用重新构造可以实现:Partial、Required、Readonly、Pick、Record

          用模式匹配 + 递归可以实现: Awaited

          用联合类型在分布式条件类型的特性可以实现: Exclude

          此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

          ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; +import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/高级类型.md","filePath":"cn/src/types/高级类型.md","lastUpdated":1728129344000}'),o={name:"cn/src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          TypeScript 内置的高级类型

          Parameters

          Parameters 用于提取函数类型的参数类型。

          ReturnType

          ReturnType 用于提取函数类型的返回值类型。

          ConstructorParameters

          构造器类型和函数类型的区别就是可以被 new。

          Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

          InstanceType

          提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

          ThisParameterType

          OmitThisParameter

          Partial

          Required

          Readonly

          Pick

          Record

          Exclude

          Extract

          Omit

          Awaited

          NonNullable

          Uppercase

          Lowercase

          Capitalize

          Uncapitalize

          总结

          比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

          用模式匹配 + 重新构造可以实现:OmitThisParameter

          用重新构造可以实现:Partial、Required、Readonly、Pick、Record

          用模式匹配 + 递归可以实现: Awaited

          用联合类型在分布式条件类型的特性可以实现: Exclude

          此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

          ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; diff --git "a/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.C_sodMsN.lean.js" "b/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BDtDighX.lean.js" similarity index 98% rename from "assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.C_sodMsN.lean.js" rename to "assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BDtDighX.lean.js" index a390a9c2bc..f6ea72f03b 100644 --- "a/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.C_sodMsN.lean.js" +++ "b/assets/cn_src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BDtDighX.lean.js" @@ -1 +1 @@ -import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/高级类型.md","filePath":"cn/src/types/高级类型.md","lastUpdated":1728129125000}'),o={name:"cn/src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          TypeScript 内置的高级类型

          Parameters

          Parameters 用于提取函数类型的参数类型。

          ReturnType

          ReturnType 用于提取函数类型的返回值类型。

          ConstructorParameters

          构造器类型和函数类型的区别就是可以被 new。

          Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

          InstanceType

          提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

          ThisParameterType

          OmitThisParameter

          Partial

          Required

          Readonly

          Pick

          Record

          Exclude

          Extract

          Omit

          Awaited

          NonNullable

          Uppercase

          Lowercase

          Capitalize

          Uncapitalize

          总结

          比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

          用模式匹配 + 重新构造可以实现:OmitThisParameter

          用重新构造可以实现:Partial、Required、Readonly、Pick、Record

          用模式匹配 + 递归可以实现: Awaited

          用联合类型在分布式条件类型的特性可以实现: Exclude

          此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

          ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; +import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"cn/src/types/高级类型.md","filePath":"cn/src/types/高级类型.md","lastUpdated":1728129344000}'),o={name:"cn/src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

          TypeScript 内置的高级类型

          Parameters

          Parameters 用于提取函数类型的参数类型。

          ReturnType

          ReturnType 用于提取函数类型的返回值类型。

          ConstructorParameters

          构造器类型和函数类型的区别就是可以被 new。

          Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

          InstanceType

          提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

          ThisParameterType

          OmitThisParameter

          Partial

          Required

          Readonly

          Pick

          Record

          Exclude

          Extract

          Omit

          Awaited

          NonNullable

          Uppercase

          Lowercase

          Capitalize

          Uncapitalize

          总结

          比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

          用模式匹配 + 重新构造可以实现:OmitThisParameter

          用重新构造可以实现:Partial、Required、Readonly、Pick、Record

          用模式匹配 + 递归可以实现: Awaited

          用联合类型在分布式条件类型的特性可以实现: Exclude

          此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

          ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; diff --git a/assets/index.md.C1mV7KLR.js b/assets/index.md.D7BH-fMN.js similarity index 93% rename from assets/index.md.C1mV7KLR.js rename to assets/index.md.D7BH-fMN.js index 2a5308c8bb..17864bd752 100644 --- a/assets/index.md.C1mV7KLR.js +++ b/assets/index.md.D7BH-fMN.js @@ -1 +1 @@ -import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse(`{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","tagline":"A Troupe of little vagrants of the world , leave your footprints in my words .","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"brand","text":"Thanks to star","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"Visit my GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"record","details":"Every journey has its final day."},{"icon":"🛠️","title":"solve","details":"If you can't measure it, you can't improve it."},{"icon":"🖖","title":"share","details":"If you want to go fast, go alone. If you want to go far, go together."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1728129125000}`),o={name:"index.md"};function i(n,r,s,l,c,d){return e(),a("div")}const u=t(o,[["render",i]]);export{h as __pageData,u as default}; +import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse(`{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","tagline":"A Troupe of little vagrants of the world , leave your footprints in my words .","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"brand","text":"Thanks to star","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"Visit my GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"record","details":"Every journey has its final day."},{"icon":"🛠️","title":"solve","details":"If you can't measure it, you can't improve it."},{"icon":"🖖","title":"share","details":"If you want to go fast, go alone. If you want to go far, go together."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1728129344000}`),o={name:"index.md"};function i(n,r,s,l,c,d){return e(),a("div")}const u=t(o,[["render",i]]);export{h as __pageData,u as default}; diff --git a/assets/index.md.C1mV7KLR.lean.js b/assets/index.md.D7BH-fMN.lean.js similarity index 93% rename from assets/index.md.C1mV7KLR.lean.js rename to assets/index.md.D7BH-fMN.lean.js index 2a5308c8bb..17864bd752 100644 --- a/assets/index.md.C1mV7KLR.lean.js +++ b/assets/index.md.D7BH-fMN.lean.js @@ -1 +1 @@ -import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse(`{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","tagline":"A Troupe of little vagrants of the world , leave your footprints in my words .","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"brand","text":"Thanks to star","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"Visit my GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"record","details":"Every journey has its final day."},{"icon":"🛠️","title":"solve","details":"If you can't measure it, you can't improve it."},{"icon":"🖖","title":"share","details":"If you want to go fast, go alone. If you want to go far, go together."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1728129125000}`),o={name:"index.md"};function i(n,r,s,l,c,d){return e(),a("div")}const u=t(o,[["render",i]]);export{h as __pageData,u as default}; +import{_ as t,o as e,c as a}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse(`{"title":"Home","description":"","frontmatter":{"layout":"home","title":"Home","hero":{"name":"ran","tagline":"A Troupe of little vagrants of the world , leave your footprints in my words .","image":{"src":"/home.svg","alt":"logo"},"actions":[{"theme":"brand","text":"Thanks to star","link":"https://github.com/chaxus/ran"},{"theme":"alt","text":"Visit my GitHub","link":"https://github.com/chaxus/ran"}]},"features":[{"icon":"⚡️","title":"record","details":"Every journey has its final day."},{"icon":"🛠️","title":"solve","details":"If you can't measure it, you can't improve it."},{"icon":"🖖","title":"share","details":"If you want to go fast, go alone. If you want to go far, go together."}]},"headers":[],"relativePath":"index.md","filePath":"index.md","lastUpdated":1728129344000}`),o={name:"index.md"};function i(n,r,s,l,c,d){return e(),a("div")}const u=t(o,[["render",i]]);export{h as __pageData,u as default}; diff --git a/assets/src_article_astParse_tokenizer.md.m5lyUfzX.js b/assets/src_article_astParse_tokenizer.md.CuCr3w1T.js similarity index 99% rename from assets/src_article_astParse_tokenizer.md.m5lyUfzX.js rename to assets/src_article_astParse_tokenizer.md.CuCr3w1T.js index 46a1a8ded1..185862b855 100644 --- a/assets/src_article_astParse_tokenizer.md.m5lyUfzX.js +++ b/assets/src_article_astParse_tokenizer.md.CuCr3w1T.js @@ -1,4 +1,4 @@ -import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/astParse/tokenizer.md","filePath":"src/article/astParse/tokenizer.md","lastUpdated":1728129125000}'),u={name:"src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

          Abstract Syntax Tree

          一.(abstract syntax tree)抽象语法树的作用

          源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

          之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

          有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

          转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

          二。常见的 AST 节点

          常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

          我们分别来了解一下:

          Literal

          Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

          下面这些字面量都有对应的 Literal 节点:

          代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

          Identifier

          Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

          我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

          尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

          js
          const name = 'value';
          +import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/astParse/tokenizer.md","filePath":"src/article/astParse/tokenizer.md","lastUpdated":1728129344000}'),u={name:"src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

          Abstract Syntax Tree

          一.(abstract syntax tree)抽象语法树的作用

          源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

          之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

          有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

          转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

          二。常见的 AST 节点

          常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

          我们分别来了解一下:

          Literal

          Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

          下面这些字面量都有对应的 Literal 节点:

          代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

          Identifier

          Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

          我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

          尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

          js
          const name = 'value';
           
           function say(name) {
             console.log(name);
          diff --git a/assets/src_article_astParse_tokenizer.md.m5lyUfzX.lean.js b/assets/src_article_astParse_tokenizer.md.CuCr3w1T.lean.js
          similarity index 99%
          rename from assets/src_article_astParse_tokenizer.md.m5lyUfzX.lean.js
          rename to assets/src_article_astParse_tokenizer.md.CuCr3w1T.lean.js
          index 46a1a8ded1..185862b855 100644
          --- a/assets/src_article_astParse_tokenizer.md.m5lyUfzX.lean.js
          +++ b/assets/src_article_astParse_tokenizer.md.CuCr3w1T.lean.js
          @@ -1,4 +1,4 @@
          -import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/astParse/tokenizer.md","filePath":"src/article/astParse/tokenizer.md","lastUpdated":1728129125000}'),u={name:"src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

          Abstract Syntax Tree

          一.(abstract syntax tree)抽象语法树的作用

          源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

          之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

          有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

          转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

          二。常见的 AST 节点

          常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

          我们分别来了解一下:

          Literal

          Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

          下面这些字面量都有对应的 Literal 节点:

          代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

          Identifier

          Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

          我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

          尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

          js
          const name = 'value';
          +import{_ as a,a as n,b as h,c as p,d as k,e as t,f as l,g as e,h as d,i as E,j as r,k as g,l as c,m as i,n as y}from"./chunks/extra.Cu56q3CZ.js";import{_ as o,o as F,c as C,a3 as A}from"./chunks/framework.eq-HTtE3.js";const S=JSON.parse('{"title":"Abstract Syntax Tree","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/astParse/tokenizer.md","filePath":"src/article/astParse/tokenizer.md","lastUpdated":1728129344000}'),u={name:"src/article/astParse/tokenizer.md"};function D(B,s,m,b,v,T){return F(),C("div",{"data-pagefind-body":!0},s[0]||(s[0]=[A('

          Abstract Syntax Tree

          一.(abstract syntax tree)抽象语法树的作用

          源码是一串按照语法格式来组织的字符串,人能够认识,但是计算机并不认识,想让计算机认识就要转成一种数据结构,通过不同的对象来保存不同的数据,并且按照依赖关系组织起来,这种数据结构就是抽象语法树(abstract syntax tree)。

          之所以叫“抽象”语法树是因为数据结构中省略掉了一些无具体意义的分隔符比如 ; { } 等。

          有了 AST,计算机就能理解源码字符串的意思,而理解是能够转换的前提,所以编译的第一步需要把源码 parseAST

          转成 AST 之后就可以通过修改 AST ,分析 AST 的方式来修改和分析代码,比如 babel 就通过这种方式进行代码的转换,比如 rollupTree Shaking ,就是通过分析 AST的 导入导出语法,从而分析出没有使用的代码,进行去除。

          二。常见的 AST 节点

          常见的 AST 节点 AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法都有各自的 AST。

          我们分别来了解一下:

          Literal

          Literal 是字面量的意思,比如 let name = 'value'中,'value'就是一个字符串字面量 StringLiteral,相应的还有数字字面量 NumericLiteral,布尔字面量 BooleanLiteral,字符串字面量 StringLiteral,正则表达式字面量 RegExpLiteral 等。

          下面这些字面量都有对应的 Literal 节点:

          代码中的字面量很多,babel 就是通过 xxLiteral 来抽象这部分内容的。

          Identifier

          Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer

          我们知道, JS 中的标识符只能包含字母或数字或下划线 (“_”) 或美元符号 (“$”) ,且不能以数字开头。这是 Identifier 的词法特点。

          尝试分析一下,下面这一段代码里面有多少 Identifier 呢?

          js
          const name = 'value';
           
           function say(name) {
             console.log(name);
          diff --git a/assets/src_article_babel.md.D0mGQXMo.js b/assets/src_article_babel.md.Cy_u0Zgd.js
          similarity index 95%
          rename from assets/src_article_babel.md.D0mGQXMo.js
          rename to assets/src_article_babel.md.Cy_u0Zgd.js
          index 6a171725d5..41d76580db 100644
          --- a/assets/src_article_babel.md.D0mGQXMo.js
          +++ b/assets/src_article_babel.md.Cy_u0Zgd.js
          @@ -1 +1 @@
          -import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/babel.md","filePath":"src/article/babel.md","lastUpdated":1728129125000}'),i={name:"src/article/babel.md"};function s(b,e,o,p,c,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

          Babel

          babel 核心库主要是:

          • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
          • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
          • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
          • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
          • @babel/code-frame 可以创建友好的报错信息
          • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
          • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
          ',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; +import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/babel.md","filePath":"src/article/babel.md","lastUpdated":1728129344000}'),i={name:"src/article/babel.md"};function s(b,e,o,p,c,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

          Babel

          babel 核心库主要是:

          • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
          • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
          • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
          • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
          • @babel/code-frame 可以创建友好的报错信息
          • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
          • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
          ',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; diff --git a/assets/src_article_babel.md.D0mGQXMo.lean.js b/assets/src_article_babel.md.Cy_u0Zgd.lean.js similarity index 95% rename from assets/src_article_babel.md.D0mGQXMo.lean.js rename to assets/src_article_babel.md.Cy_u0Zgd.lean.js index 6a171725d5..41d76580db 100644 --- a/assets/src_article_babel.md.D0mGQXMo.lean.js +++ b/assets/src_article_babel.md.Cy_u0Zgd.lean.js @@ -1 +1 @@ -import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/babel.md","filePath":"src/article/babel.md","lastUpdated":1728129125000}'),i={name:"src/article/babel.md"};function s(b,e,o,p,c,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

          Babel

          babel 核心库主要是:

          • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
          • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
          • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
          • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
          • @babel/code-frame 可以创建友好的报错信息
          • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
          • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
          ',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; +import{_ as a,o as t,c as l,a3 as r}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"Babel","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/babel.md","filePath":"src/article/babel.md","lastUpdated":1728129344000}'),i={name:"src/article/babel.md"};function s(b,e,o,p,c,n){return t(),l("div",{"data-pagefind-body":!0},e[0]||(e[0]=[r('

          Babel

          babel 核心库主要是:

          • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
          • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
          • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
          • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
          • @babel/code-frame 可以创建友好的报错信息
          • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
          • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。
          ',3)]))}const u=a(i,[["render",s]]);export{m as __pageData,u as default}; diff --git a/assets/src_article_bundle.md.BvqfF4Lk.js b/assets/src_article_bundle.md.CJFsaLBq.js similarity index 93% rename from assets/src_article_bundle.md.BvqfF4Lk.js rename to assets/src_article_bundle.md.CJFsaLBq.js index b7efd7ceb2..e2e414a0ea 100644 --- a/assets/src_article_bundle.md.BvqfF4Lk.js +++ b/assets/src_article_bundle.md.CJFsaLBq.js @@ -1 +1 @@ -import{_ as a,o as t,c as n,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/bundle.md","filePath":"src/article/bundle.md","lastUpdated":1728129125000}'),d={name:"src/article/bundle.md"};function s(i,l,c,o,u,p){return t(),n("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",s]]);export{f as __pageData,m as default}; +import{_ as a,o as t,c as n,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/bundle.md","filePath":"src/article/bundle.md","lastUpdated":1728129344000}'),d={name:"src/article/bundle.md"};function s(i,l,c,o,u,p){return t(),n("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",s]]);export{f as __pageData,m as default}; diff --git a/assets/src_article_bundle.md.BvqfF4Lk.lean.js b/assets/src_article_bundle.md.CJFsaLBq.lean.js similarity index 93% rename from assets/src_article_bundle.md.BvqfF4Lk.lean.js rename to assets/src_article_bundle.md.CJFsaLBq.lean.js index b7efd7ceb2..e2e414a0ea 100644 --- a/assets/src_article_bundle.md.BvqfF4Lk.lean.js +++ b/assets/src_article_bundle.md.CJFsaLBq.lean.js @@ -1 +1 @@ -import{_ as a,o as t,c as n,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/bundle.md","filePath":"src/article/bundle.md","lastUpdated":1728129125000}'),d={name:"src/article/bundle.md"};function s(i,l,c,o,u,p){return t(),n("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",s]]);export{f as __pageData,m as default}; +import{_ as a,o as t,c as n,j as e,a as r}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"Bundle","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/bundle.md","filePath":"src/article/bundle.md","lastUpdated":1728129344000}'),d={name:"src/article/bundle.md"};function s(i,l,c,o,u,p){return t(),n("div",{"data-pagefind-body":!0},l[0]||(l[0]=[e("h1",{id:"bundle",tabindex:"-1"},[r("Bundle "),e("a",{class:"header-anchor",href:"#bundle","aria-label":'Permalink to "Bundle"'},"​")],-1),e("p",null,"Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:",-1),e("ul",null,[e("li",null,"编译能力"),e("li",null,"插件机制"),e("li",null,"HMR"),e("li",null,"cli 和命令行能力")],-1)]))}const m=a(d,[["render",s]]);export{f as __pageData,m as default}; diff --git a/assets/src_article_designMode.md.DWnxNFS_.js b/assets/src_article_designMode.md.CsQyDQ9L.js similarity index 99% rename from assets/src_article_designMode.md.DWnxNFS_.js rename to assets/src_article_designMode.md.CsQyDQ9L.js index 84ddb902ce..815bffdd0a 100644 --- a/assets/src_article_designMode.md.DWnxNFS_.js +++ b/assets/src_article_designMode.md.CsQyDQ9L.js @@ -1,4 +1,4 @@ -import{_ as i,a,b as n,c as e,d as t,e as h,f as l,g as p,h as k,i as r,j as o,k as d,l as E,m as c,n as g,o as y,p as F,q as u,r as m,s as b,t as f,u as C,v as A,w as B,x as v,y as D,z as w,A as x,B as j}from"./chunks/访问者._0swtoJg.js";import{_ as q,o as P,c as T,a3 as _}from"./chunks/framework.eq-HTtE3.js";const U=JSON.parse('{"title":"23 classic design patterns","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/designMode.md","filePath":"src/article/designMode.md","lastUpdated":1728129125000}'),I={name:"src/article/designMode.md"};function S(M,s,z,L,V,W){return P(),T("div",{"data-pagefind-body":!0},s[0]||(s[0]=[_(`

          23 classic design patterns

          The Design Pattern is a set of frequently used, widely known, cataloged code design experiences that are used in order to reuse code, make it easier for others to understand, and ensure code reliability.

          In the book "Design Patterns: The Foundation of Reusable Object-Oriented Software" introduced 23 classic design patterns, but the design pattern is not only these 23, with the development of software development industry, more and more new patterns continue to be born and applied. Experienced developers learn design patterns that can be corroborated with past experience, making it easier to understand them.

          A design pattern generally contains elements such as the pattern name, problem, purpose, solution, and effect. The problem describes when patterns should be used, and it contains problems with the design and why they exist. A solution describes the components of a design pattern and how these components relate to each other, their respective responsibilities, and how they work together. Typically, the solution is described through UML class diagrams and core code. Effects describe the advantages and disadvantages of the pattern and the trade-offs that should be made when using the pattern.

          Why learn design patterns:

          • Design patterns are derived from the experience and wisdom of many experts. They are successful and reusable design solutions from many excellent software systems. Using these solutions will allow us to avoid doing some repetitive work

          • Design patterns provide a common set of design vocabulary and a common form to facilitate communication and exchange between developers, making design solutions more understandable

          • Most of the design patterns take into account the reusability and extensibility of the system, which enables us to better reuse some existing design schemes, functional modules and even a complete software system, so as to avoid us often doing some repetitive design and writing some repetitive code

          • Proper use of design patterns and documentation of their use will help others understand the system more quickly

          • Learning design patterns will help beginners understand object-oriented ideas more deeply

          Reserve knowledge

          • Abstract class: The general abstract class is as a base class, for example, "computer" can be used as an abstract class, according to the abstract class derived "desktop computer" and "laptop computer" 2 concrete classes. Abstract classes are generally not instantiated.

          • Combination is better than inheritance: inheritance cannot be abused to expand functionality, and combination is more flexible. Also take the "computer" abstract class for example, if we use inheritance to distinguish different types of "computer" we can derive "desktop computer" and "laptop computer", if we add another dimension, according to the brand can continue to subdivide "Lenovo desktop computer", "Lenovo laptop", "Apple desktop computer" and "Apple laptop" and so on. If you add another dimension and continue to subdivide, it is clear that inheritance is not adequate. At this time, you can use inheritance and composition, and the combined object can also be abstract design:

            ts
            interface Brand {
            +import{_ as i,a,b as n,c as e,d as t,e as h,f as l,g as p,h as k,i as r,j as o,k as d,l as E,m as c,n as g,o as y,p as F,q as u,r as m,s as b,t as f,u as C,v as A,w as B,x as v,y as D,z as w,A as x,B as j}from"./chunks/访问者._0swtoJg.js";import{_ as q,o as P,c as T,a3 as _}from"./chunks/framework.eq-HTtE3.js";const U=JSON.parse('{"title":"23 classic design patterns","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/designMode.md","filePath":"src/article/designMode.md","lastUpdated":1728129344000}'),I={name:"src/article/designMode.md"};function S(M,s,z,L,V,W){return P(),T("div",{"data-pagefind-body":!0},s[0]||(s[0]=[_(`

            23 classic design patterns

            The Design Pattern is a set of frequently used, widely known, cataloged code design experiences that are used in order to reuse code, make it easier for others to understand, and ensure code reliability.

            In the book "Design Patterns: The Foundation of Reusable Object-Oriented Software" introduced 23 classic design patterns, but the design pattern is not only these 23, with the development of software development industry, more and more new patterns continue to be born and applied. Experienced developers learn design patterns that can be corroborated with past experience, making it easier to understand them.

            A design pattern generally contains elements such as the pattern name, problem, purpose, solution, and effect. The problem describes when patterns should be used, and it contains problems with the design and why they exist. A solution describes the components of a design pattern and how these components relate to each other, their respective responsibilities, and how they work together. Typically, the solution is described through UML class diagrams and core code. Effects describe the advantages and disadvantages of the pattern and the trade-offs that should be made when using the pattern.

            Why learn design patterns:

            • Design patterns are derived from the experience and wisdom of many experts. They are successful and reusable design solutions from many excellent software systems. Using these solutions will allow us to avoid doing some repetitive work

            • Design patterns provide a common set of design vocabulary and a common form to facilitate communication and exchange between developers, making design solutions more understandable

            • Most of the design patterns take into account the reusability and extensibility of the system, which enables us to better reuse some existing design schemes, functional modules and even a complete software system, so as to avoid us often doing some repetitive design and writing some repetitive code

            • Proper use of design patterns and documentation of their use will help others understand the system more quickly

            • Learning design patterns will help beginners understand object-oriented ideas more deeply

            Reserve knowledge

            • Abstract class: The general abstract class is as a base class, for example, "computer" can be used as an abstract class, according to the abstract class derived "desktop computer" and "laptop computer" 2 concrete classes. Abstract classes are generally not instantiated.

            • Combination is better than inheritance: inheritance cannot be abused to expand functionality, and combination is more flexible. Also take the "computer" abstract class for example, if we use inheritance to distinguish different types of "computer" we can derive "desktop computer" and "laptop computer", if we add another dimension, according to the brand can continue to subdivide "Lenovo desktop computer", "Lenovo laptop", "Apple desktop computer" and "Apple laptop" and so on. If you add another dimension and continue to subdivide, it is clear that inheritance is not adequate. At this time, you can use inheritance and composition, and the combined object can also be abstract design:

              ts
              interface Brand {
                 // ...
               }
               interface Lenovo extends Brand {
              diff --git a/assets/src_article_designMode.md.DWnxNFS_.lean.js b/assets/src_article_designMode.md.CsQyDQ9L.lean.js
              similarity index 99%
              rename from assets/src_article_designMode.md.DWnxNFS_.lean.js
              rename to assets/src_article_designMode.md.CsQyDQ9L.lean.js
              index 84ddb902ce..815bffdd0a 100644
              --- a/assets/src_article_designMode.md.DWnxNFS_.lean.js
              +++ b/assets/src_article_designMode.md.CsQyDQ9L.lean.js
              @@ -1,4 +1,4 @@
              -import{_ as i,a,b as n,c as e,d as t,e as h,f as l,g as p,h as k,i as r,j as o,k as d,l as E,m as c,n as g,o as y,p as F,q as u,r as m,s as b,t as f,u as C,v as A,w as B,x as v,y as D,z as w,A as x,B as j}from"./chunks/访问者._0swtoJg.js";import{_ as q,o as P,c as T,a3 as _}from"./chunks/framework.eq-HTtE3.js";const U=JSON.parse('{"title":"23 classic design patterns","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/designMode.md","filePath":"src/article/designMode.md","lastUpdated":1728129125000}'),I={name:"src/article/designMode.md"};function S(M,s,z,L,V,W){return P(),T("div",{"data-pagefind-body":!0},s[0]||(s[0]=[_(`

              23 classic design patterns

              The Design Pattern is a set of frequently used, widely known, cataloged code design experiences that are used in order to reuse code, make it easier for others to understand, and ensure code reliability.

              In the book "Design Patterns: The Foundation of Reusable Object-Oriented Software" introduced 23 classic design patterns, but the design pattern is not only these 23, with the development of software development industry, more and more new patterns continue to be born and applied. Experienced developers learn design patterns that can be corroborated with past experience, making it easier to understand them.

              A design pattern generally contains elements such as the pattern name, problem, purpose, solution, and effect. The problem describes when patterns should be used, and it contains problems with the design and why they exist. A solution describes the components of a design pattern and how these components relate to each other, their respective responsibilities, and how they work together. Typically, the solution is described through UML class diagrams and core code. Effects describe the advantages and disadvantages of the pattern and the trade-offs that should be made when using the pattern.

              Why learn design patterns:

              • Design patterns are derived from the experience and wisdom of many experts. They are successful and reusable design solutions from many excellent software systems. Using these solutions will allow us to avoid doing some repetitive work

              • Design patterns provide a common set of design vocabulary and a common form to facilitate communication and exchange between developers, making design solutions more understandable

              • Most of the design patterns take into account the reusability and extensibility of the system, which enables us to better reuse some existing design schemes, functional modules and even a complete software system, so as to avoid us often doing some repetitive design and writing some repetitive code

              • Proper use of design patterns and documentation of their use will help others understand the system more quickly

              • Learning design patterns will help beginners understand object-oriented ideas more deeply

              Reserve knowledge

              • Abstract class: The general abstract class is as a base class, for example, "computer" can be used as an abstract class, according to the abstract class derived "desktop computer" and "laptop computer" 2 concrete classes. Abstract classes are generally not instantiated.

              • Combination is better than inheritance: inheritance cannot be abused to expand functionality, and combination is more flexible. Also take the "computer" abstract class for example, if we use inheritance to distinguish different types of "computer" we can derive "desktop computer" and "laptop computer", if we add another dimension, according to the brand can continue to subdivide "Lenovo desktop computer", "Lenovo laptop", "Apple desktop computer" and "Apple laptop" and so on. If you add another dimension and continue to subdivide, it is clear that inheritance is not adequate. At this time, you can use inheritance and composition, and the combined object can also be abstract design:

                ts
                interface Brand {
                +import{_ as i,a,b as n,c as e,d as t,e as h,f as l,g as p,h as k,i as r,j as o,k as d,l as E,m as c,n as g,o as y,p as F,q as u,r as m,s as b,t as f,u as C,v as A,w as B,x as v,y as D,z as w,A as x,B as j}from"./chunks/访问者._0swtoJg.js";import{_ as q,o as P,c as T,a3 as _}from"./chunks/framework.eq-HTtE3.js";const U=JSON.parse('{"title":"23 classic design patterns","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/designMode.md","filePath":"src/article/designMode.md","lastUpdated":1728129344000}'),I={name:"src/article/designMode.md"};function S(M,s,z,L,V,W){return P(),T("div",{"data-pagefind-body":!0},s[0]||(s[0]=[_(`

                23 classic design patterns

                The Design Pattern is a set of frequently used, widely known, cataloged code design experiences that are used in order to reuse code, make it easier for others to understand, and ensure code reliability.

                In the book "Design Patterns: The Foundation of Reusable Object-Oriented Software" introduced 23 classic design patterns, but the design pattern is not only these 23, with the development of software development industry, more and more new patterns continue to be born and applied. Experienced developers learn design patterns that can be corroborated with past experience, making it easier to understand them.

                A design pattern generally contains elements such as the pattern name, problem, purpose, solution, and effect. The problem describes when patterns should be used, and it contains problems with the design and why they exist. A solution describes the components of a design pattern and how these components relate to each other, their respective responsibilities, and how they work together. Typically, the solution is described through UML class diagrams and core code. Effects describe the advantages and disadvantages of the pattern and the trade-offs that should be made when using the pattern.

                Why learn design patterns:

                • Design patterns are derived from the experience and wisdom of many experts. They are successful and reusable design solutions from many excellent software systems. Using these solutions will allow us to avoid doing some repetitive work

                • Design patterns provide a common set of design vocabulary and a common form to facilitate communication and exchange between developers, making design solutions more understandable

                • Most of the design patterns take into account the reusability and extensibility of the system, which enables us to better reuse some existing design schemes, functional modules and even a complete software system, so as to avoid us often doing some repetitive design and writing some repetitive code

                • Proper use of design patterns and documentation of their use will help others understand the system more quickly

                • Learning design patterns will help beginners understand object-oriented ideas more deeply

                Reserve knowledge

                • Abstract class: The general abstract class is as a base class, for example, "computer" can be used as an abstract class, according to the abstract class derived "desktop computer" and "laptop computer" 2 concrete classes. Abstract classes are generally not instantiated.

                • Combination is better than inheritance: inheritance cannot be abused to expand functionality, and combination is more flexible. Also take the "computer" abstract class for example, if we use inheritance to distinguish different types of "computer" we can derive "desktop computer" and "laptop computer", if we add another dimension, according to the brand can continue to subdivide "Lenovo desktop computer", "Lenovo laptop", "Apple desktop computer" and "Apple laptop" and so on. If you add another dimension and continue to subdivide, it is clear that inheritance is not adequate. At this time, you can use inheritance and composition, and the combined object can also be abstract design:

                  ts
                  interface Brand {
                     // ...
                   }
                   interface Lenovo extends Brand {
                  diff --git a/assets/src_article_functionalProgramming.md.Bbl-kdPi.js b/assets/src_article_functionalProgramming.md.BPj6iTX9.js
                  similarity index 99%
                  rename from assets/src_article_functionalProgramming.md.Bbl-kdPi.js
                  rename to assets/src_article_functionalProgramming.md.BPj6iTX9.js
                  index 97305b7909..8b4ed7fa32 100644
                  --- a/assets/src_article_functionalProgramming.md.Bbl-kdPi.js
                  +++ b/assets/src_article_functionalProgramming.md.BPj6iTX9.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/functionalProgramming.md","filePath":"src/article/functionalProgramming.md","lastUpdated":1728129125000}'),k={name:"src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  函数式编程

                  • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
                  • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
                  • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
                    • 程序的本质:根据输入,通过某种运算,获得相应的输出
                    • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
                    • 相同的输入始终要得到相同的输出 (纯函数)
                    • 函数式编程用来描述数据 (函数) 之间的映射关系
                  js
                  //非函数式编程,面向过程的编程方式
                  +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/functionalProgramming.md","filePath":"src/article/functionalProgramming.md","lastUpdated":1728129344000}'),k={name:"src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  函数式编程

                  • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
                  • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
                  • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
                    • 程序的本质:根据输入,通过某种运算,获得相应的输出
                    • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
                    • 相同的输入始终要得到相同的输出 (纯函数)
                    • 函数式编程用来描述数据 (函数) 之间的映射关系
                  js
                  //非函数式编程,面向过程的编程方式
                   let num1 = 1;
                   let num2 = 2;
                   let sum = num1 + num2;
                  diff --git a/assets/src_article_functionalProgramming.md.Bbl-kdPi.lean.js b/assets/src_article_functionalProgramming.md.BPj6iTX9.lean.js
                  similarity index 99%
                  rename from assets/src_article_functionalProgramming.md.Bbl-kdPi.lean.js
                  rename to assets/src_article_functionalProgramming.md.BPj6iTX9.lean.js
                  index 97305b7909..8b4ed7fa32 100644
                  --- a/assets/src_article_functionalProgramming.md.Bbl-kdPi.lean.js
                  +++ b/assets/src_article_functionalProgramming.md.BPj6iTX9.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/functionalProgramming.md","filePath":"src/article/functionalProgramming.md","lastUpdated":1728129125000}'),k={name:"src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  函数式编程

                  • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
                  • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
                  • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
                    • 程序的本质:根据输入,通过某种运算,获得相应的输出
                    • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
                    • 相同的输入始终要得到相同的输出 (纯函数)
                    • 函数式编程用来描述数据 (函数) 之间的映射关系
                  js
                  //非函数式编程,面向过程的编程方式
                  +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"函数式编程","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/functionalProgramming.md","filePath":"src/article/functionalProgramming.md","lastUpdated":1728129344000}'),k={name:"src/article/functionalProgramming.md"};function l(p,s,t,E,e,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  函数式编程

                  • 概述:函数式编程 (Functional Programming)FP就是编程规范之一,我们常听说的编程规范还有面向对象编程,面向过程编程。
                  • 面向对象的编程思维方式:把现实世界中的事物抽象成程序世界的类和对象,通过封装,继承和多态演示事物事件的联系
                  • 函数编程的思维方式:把现实世界的事物和事物之间的联系抽象到程序世界 (对运算过程进行抽象)
                    • 程序的本质:根据输入,通过某种运算,获得相应的输出
                    • 函数式编程中的函数不是指程序中的 (函数) 方法,而是数学中的函数,即映射关系
                    • 相同的输入始终要得到相同的输出 (纯函数)
                    • 函数式编程用来描述数据 (函数) 之间的映射关系
                  js
                  //非函数式编程,面向过程的编程方式
                   let num1 = 1;
                   let num2 = 2;
                   let sum = num1 + num2;
                  diff --git a/assets/src_article_imagemin.md.CNzOGYN8.js b/assets/src_article_imagemin.md.F6ka3QYU.js
                  similarity index 91%
                  rename from assets/src_article_imagemin.md.CNzOGYN8.js
                  rename to assets/src_article_imagemin.md.F6ka3QYU.js
                  index ad3c64bc47..9707457e68 100644
                  --- a/assets/src_article_imagemin.md.CNzOGYN8.js
                  +++ b/assets/src_article_imagemin.md.F6ka3QYU.js
                  @@ -1 +1 @@
                  -import{_ as t,o as i,c as r,j as a,a as m}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/imagemin.md","filePath":"src/article/imagemin.md","lastUpdated":1728129125000}'),n={name:"src/article/imagemin.md"};function s(o,e,c,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[m("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(n,[["render",s]]);export{f as __pageData,_ as default};
                  +import{_ as t,o as i,c as r,j as a,a as m}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/imagemin.md","filePath":"src/article/imagemin.md","lastUpdated":1728129344000}'),n={name:"src/article/imagemin.md"};function s(o,e,c,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[m("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(n,[["render",s]]);export{f as __pageData,_ as default};
                  diff --git a/assets/src_article_imagemin.md.CNzOGYN8.lean.js b/assets/src_article_imagemin.md.F6ka3QYU.lean.js
                  similarity index 91%
                  rename from assets/src_article_imagemin.md.CNzOGYN8.lean.js
                  rename to assets/src_article_imagemin.md.F6ka3QYU.lean.js
                  index ad3c64bc47..9707457e68 100644
                  --- a/assets/src_article_imagemin.md.CNzOGYN8.lean.js
                  +++ b/assets/src_article_imagemin.md.F6ka3QYU.lean.js
                  @@ -1 +1 @@
                  -import{_ as t,o as i,c as r,j as a,a as m}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/imagemin.md","filePath":"src/article/imagemin.md","lastUpdated":1728129125000}'),n={name:"src/article/imagemin.md"};function s(o,e,c,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[m("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(n,[["render",s]]);export{f as __pageData,_ as default};
                  +import{_ as t,o as i,c as r,j as a,a as m}from"./chunks/framework.eq-HTtE3.js";const f=JSON.parse('{"title":"imagemin 图片压缩源码分析","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/imagemin.md","filePath":"src/article/imagemin.md","lastUpdated":1728129344000}'),n={name:"src/article/imagemin.md"};function s(o,e,c,d,l,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[a("h1",{id:"imagemin-图片压缩源码分析",tabindex:"-1"},[m("imagemin 图片压缩源码分析 "),a("a",{class:"header-anchor",href:"#imagemin-图片压缩源码分析","aria-label":'Permalink to "imagemin 图片压缩源码分析"'},"​")],-1)]))}const _=t(n,[["render",s]]);export{f as __pageData,_ as default};
                  diff --git a/assets/src_article_javascript_domLoad.md.f1BWKaWm.js b/assets/src_article_javascript_domLoad.md.ISLuBQzT.js
                  similarity index 97%
                  rename from assets/src_article_javascript_domLoad.md.f1BWKaWm.js
                  rename to assets/src_article_javascript_domLoad.md.ISLuBQzT.js
                  index 0669d5abf4..7c127e9304 100644
                  --- a/assets/src_article_javascript_domLoad.md.f1BWKaWm.js
                  +++ b/assets/src_article_javascript_domLoad.md.ISLuBQzT.js
                  @@ -1 +1 @@
                  -import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/javascript/domLoad.md","filePath":"src/article/javascript/domLoad.md","lastUpdated":1728129125000}'),o={name:"src/article/javascript/domLoad.md"};function n(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  页面加载完成后事件

                  window.onload

                  DOMContentLoaded

                  js
                  document.addEventListener('DOMContentLoaded', fun);

                  <body onload="fun()">

                  readyState

                  js
                  document.readyState;\n\ndocument.onreadystatechange;

                  一个文档的 readyState 可以是以下之一:

                  • loading / 加载。document 仍在加载。
                  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
                  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
                  ',9)]))}const k=e(o,[["render",n]]);export{u as __pageData,k as default}; +import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/javascript/domLoad.md","filePath":"src/article/javascript/domLoad.md","lastUpdated":1728129344000}'),o={name:"src/article/javascript/domLoad.md"};function n(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  页面加载完成后事件

                  window.onload

                  DOMContentLoaded

                  js
                  document.addEventListener('DOMContentLoaded', fun);

                  <body onload="fun()">

                  readyState

                  js
                  document.readyState;\n\ndocument.onreadystatechange;

                  一个文档的 readyState 可以是以下之一:

                  • loading / 加载。document 仍在加载。
                  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
                  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
                  ',9)]))}const k=e(o,[["render",n]]);export{u as __pageData,k as default}; diff --git a/assets/src_article_javascript_domLoad.md.f1BWKaWm.lean.js b/assets/src_article_javascript_domLoad.md.ISLuBQzT.lean.js similarity index 97% rename from assets/src_article_javascript_domLoad.md.f1BWKaWm.lean.js rename to assets/src_article_javascript_domLoad.md.ISLuBQzT.lean.js index 0669d5abf4..7c127e9304 100644 --- a/assets/src_article_javascript_domLoad.md.f1BWKaWm.lean.js +++ b/assets/src_article_javascript_domLoad.md.ISLuBQzT.lean.js @@ -1 +1 @@ -import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/javascript/domLoad.md","filePath":"src/article/javascript/domLoad.md","lastUpdated":1728129125000}'),o={name:"src/article/javascript/domLoad.md"};function n(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  页面加载完成后事件

                  window.onload

                  DOMContentLoaded

                  js
                  document.addEventListener('DOMContentLoaded', fun);

                  <body onload="fun()">

                  readyState

                  js
                  document.readyState;\n\ndocument.onreadystatechange;

                  一个文档的 readyState 可以是以下之一:

                  • loading / 加载。document 仍在加载。
                  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
                  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
                  ',9)]))}const k=e(o,[["render",n]]);export{u as __pageData,k as default}; +import{_ as e,o as t,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"页面加载完成后事件","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/javascript/domLoad.md","filePath":"src/article/javascript/domLoad.md","lastUpdated":1728129344000}'),o={name:"src/article/javascript/domLoad.md"};function n(d,a,l,r,c,h){return t(),s("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  页面加载完成后事件

                  window.onload

                  DOMContentLoaded

                  js
                  document.addEventListener('DOMContentLoaded', fun);

                  <body onload="fun()">

                  readyState

                  js
                  document.readyState;\n\ndocument.onreadystatechange;

                  一个文档的 readyState 可以是以下之一:

                  • loading / 加载。document 仍在加载。
                  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
                  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
                  ',9)]))}const k=e(o,[["render",n]]);export{u as __pageData,k as default}; diff --git a/assets/src_article_sort_bubble_index.md.DM2Rnnat.js b/assets/src_article_sort_bubble_index.md.DmhPntEW.js similarity index 99% rename from assets/src_article_sort_bubble_index.md.DM2Rnnat.js rename to assets/src_article_sort_bubble_index.md.DmhPntEW.js index 1573b9ce90..0b6c8851b4 100644 --- a/assets/src_article_sort_bubble_index.md.DM2Rnnat.js +++ b/assets/src_article_sort_bubble_index.md.DmhPntEW.js @@ -1,4 +1,4 @@ -import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Bubble Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bubble/index.md","filePath":"src/article/sort/bubble/index.md","lastUpdated":1728129125000}'),h={name:"src/article/sort/bubble/index.md"};function l(p,s,k,r,d,E){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Bubble Sort

                  Bubble sort is a simple sort algorithm. It repeatedly visits the sequence to be sorted, comparing two elements at a time and swapping them if they are in the wrong order. The work of visiting the sequence is repeated until no more exchanges are needed, that is, the sequence has been sorted. The algorithm gets its name from the fact that smaller elements slowly "float" to the top of the sequence through exchange.

                  Algorithm description

                  • Compare adjacent elements. If the first one is bigger than the second, swap them both;
                  • Do the same for each pair of adjacent elements, from the first pair at the beginning to the last pair at the end, so that the last element should be the largest;
                  • Repeat the above steps for all elements except the last one;
                  • Repeat steps 1 to 3 until the sorting is complete.

                  GIF presentation

                  Bubble Sort

                  Code demo

                  ts
                  const bubble = (list: number[]) => {
                  +import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Bubble Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bubble/index.md","filePath":"src/article/sort/bubble/index.md","lastUpdated":1728129344000}'),h={name:"src/article/sort/bubble/index.md"};function l(p,s,k,r,d,E){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Bubble Sort

                  Bubble sort is a simple sort algorithm. It repeatedly visits the sequence to be sorted, comparing two elements at a time and swapping them if they are in the wrong order. The work of visiting the sequence is repeated until no more exchanges are needed, that is, the sequence has been sorted. The algorithm gets its name from the fact that smaller elements slowly "float" to the top of the sequence through exchange.

                  Algorithm description

                  • Compare adjacent elements. If the first one is bigger than the second, swap them both;
                  • Do the same for each pair of adjacent elements, from the first pair at the beginning to the last pair at the end, so that the last element should be the largest;
                  • Repeat the above steps for all elements except the last one;
                  • Repeat steps 1 to 3 until the sorting is complete.

                  GIF presentation

                  Bubble Sort

                  Code demo

                  ts
                  const bubble = (list: number[]) => {
                     const size = list.length;
                     for (let i = 0; i < size; i++) {
                       for (let j = 0; j < size; j++) {
                  diff --git a/assets/src_article_sort_bubble_index.md.DM2Rnnat.lean.js b/assets/src_article_sort_bubble_index.md.DmhPntEW.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_bubble_index.md.DM2Rnnat.lean.js
                  rename to assets/src_article_sort_bubble_index.md.DmhPntEW.lean.js
                  index 1573b9ce90..0b6c8851b4 100644
                  --- a/assets/src_article_sort_bubble_index.md.DM2Rnnat.lean.js
                  +++ b/assets/src_article_sort_bubble_index.md.DmhPntEW.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Bubble Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bubble/index.md","filePath":"src/article/sort/bubble/index.md","lastUpdated":1728129125000}'),h={name:"src/article/sort/bubble/index.md"};function l(p,s,k,r,d,E){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Bubble Sort

                  Bubble sort is a simple sort algorithm. It repeatedly visits the sequence to be sorted, comparing two elements at a time and swapping them if they are in the wrong order. The work of visiting the sequence is repeated until no more exchanges are needed, that is, the sequence has been sorted. The algorithm gets its name from the fact that smaller elements slowly "float" to the top of the sequence through exchange.

                  Algorithm description

                  • Compare adjacent elements. If the first one is bigger than the second, swap them both;
                  • Do the same for each pair of adjacent elements, from the first pair at the beginning to the last pair at the end, so that the last element should be the largest;
                  • Repeat the above steps for all elements except the last one;
                  • Repeat steps 1 to 3 until the sorting is complete.

                  GIF presentation

                  Bubble Sort

                  Code demo

                  ts
                  const bubble = (list: number[]) => {
                  +import{_ as i}from"./chunks/bubble.Dg5jgvyl.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Bubble Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bubble/index.md","filePath":"src/article/sort/bubble/index.md","lastUpdated":1728129344000}'),h={name:"src/article/sort/bubble/index.md"};function l(p,s,k,r,d,E){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Bubble Sort

                  Bubble sort is a simple sort algorithm. It repeatedly visits the sequence to be sorted, comparing two elements at a time and swapping them if they are in the wrong order. The work of visiting the sequence is repeated until no more exchanges are needed, that is, the sequence has been sorted. The algorithm gets its name from the fact that smaller elements slowly "float" to the top of the sequence through exchange.

                  Algorithm description

                  • Compare adjacent elements. If the first one is bigger than the second, swap them both;
                  • Do the same for each pair of adjacent elements, from the first pair at the beginning to the last pair at the end, so that the last element should be the largest;
                  • Repeat the above steps for all elements except the last one;
                  • Repeat steps 1 to 3 until the sorting is complete.

                  GIF presentation

                  Bubble Sort

                  Code demo

                  ts
                  const bubble = (list: number[]) => {
                     const size = list.length;
                     for (let i = 0; i < size; i++) {
                       for (let j = 0; j < size; j++) {
                  diff --git a/assets/src_article_sort_bucket_index.md.BTbi9aJ6.js b/assets/src_article_sort_bucket_index.md.1nocTUlS.js
                  similarity index 99%
                  rename from assets/src_article_sort_bucket_index.md.BTbi9aJ6.js
                  rename to assets/src_article_sort_bucket_index.md.1nocTUlS.js
                  index ee2e4cb0f2..09fe0e64ab 100644
                  --- a/assets/src_article_sort_bucket_index.md.BTbi9aJ6.js
                  +++ b/assets/src_article_sort_bucket_index.md.1nocTUlS.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Bucket Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bucket/index.md","filePath":"src/article/sort/bucket/index.md","lastUpdated":1728129125000}'),k={name:"src/article/sort/bucket/index.md"};function t(l,s,p,e,E,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  Bucket Sort

                  The key to efficiency is this bucket function. The data is divided into a limited number of buckets, and each bucket is sorted separately (it is possible to use another sorting algorithm or recursively continue to use bucket sorting).

                  Algorithm description

                  • Set a quantitative array as an empty bucket;
                  • Iterate over the input data and place the data one by one into the corresponding bucket;
                  • Sort each bucket that is not empty;
                  • Piecing together sorted data from a bucket that is never empty.

                  Code demo

                  ts
                  const count = (list: Array<number>, max: number = 100): Array<number> => {
                  +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Bucket Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bucket/index.md","filePath":"src/article/sort/bucket/index.md","lastUpdated":1728129344000}'),k={name:"src/article/sort/bucket/index.md"};function t(l,s,p,e,E,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  Bucket Sort

                  The key to efficiency is this bucket function. The data is divided into a limited number of buckets, and each bucket is sorted separately (it is possible to use another sorting algorithm or recursively continue to use bucket sorting).

                  Algorithm description

                  • Set a quantitative array as an empty bucket;
                  • Iterate over the input data and place the data one by one into the corresponding bucket;
                  • Sort each bucket that is not empty;
                  • Piecing together sorted data from a bucket that is never empty.

                  Code demo

                  ts
                  const count = (list: Array<number>, max: number = 100): Array<number> => {
                     const countList = new Array(max + 1);
                     for (let i = 0; i < list.length; i++) {
                       if (!countList[list[i]]) {
                  diff --git a/assets/src_article_sort_bucket_index.md.BTbi9aJ6.lean.js b/assets/src_article_sort_bucket_index.md.1nocTUlS.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_bucket_index.md.BTbi9aJ6.lean.js
                  rename to assets/src_article_sort_bucket_index.md.1nocTUlS.lean.js
                  index ee2e4cb0f2..09fe0e64ab 100644
                  --- a/assets/src_article_sort_bucket_index.md.BTbi9aJ6.lean.js
                  +++ b/assets/src_article_sort_bucket_index.md.1nocTUlS.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Bucket Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bucket/index.md","filePath":"src/article/sort/bucket/index.md","lastUpdated":1728129125000}'),k={name:"src/article/sort/bucket/index.md"};function t(l,s,p,e,E,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  Bucket Sort

                  The key to efficiency is this bucket function. The data is divided into a limited number of buckets, and each bucket is sorted separately (it is possible to use another sorting algorithm or recursively continue to use bucket sorting).

                  Algorithm description

                  • Set a quantitative array as an empty bucket;
                  • Iterate over the input data and place the data one by one into the corresponding bucket;
                  • Sort each bucket that is not empty;
                  • Piecing together sorted data from a bucket that is never empty.

                  Code demo

                  ts
                  const count = (list: Array<number>, max: number = 100): Array<number> => {
                  +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Bucket Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/bucket/index.md","filePath":"src/article/sort/bucket/index.md","lastUpdated":1728129344000}'),k={name:"src/article/sort/bucket/index.md"};function t(l,s,p,e,E,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  Bucket Sort

                  The key to efficiency is this bucket function. The data is divided into a limited number of buckets, and each bucket is sorted separately (it is possible to use another sorting algorithm or recursively continue to use bucket sorting).

                  Algorithm description

                  • Set a quantitative array as an empty bucket;
                  • Iterate over the input data and place the data one by one into the corresponding bucket;
                  • Sort each bucket that is not empty;
                  • Piecing together sorted data from a bucket that is never empty.

                  Code demo

                  ts
                  const count = (list: Array<number>, max: number = 100): Array<number> => {
                     const countList = new Array(max + 1);
                     for (let i = 0; i < list.length; i++) {
                       if (!countList[list[i]]) {
                  diff --git a/assets/src_article_sort_count_index.md.Ch5HRRp8.js b/assets/src_article_sort_count_index.md.Du6QhWUu.js
                  similarity index 99%
                  rename from assets/src_article_sort_count_index.md.Ch5HRRp8.js
                  rename to assets/src_article_sort_count_index.md.Du6QhWUu.js
                  index cae0af1eff..cacb97f122 100644
                  --- a/assets/src_article_sort_count_index.md.Ch5HRRp8.js
                  +++ b/assets/src_article_sort_count_index.md.Du6QhWUu.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Count Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/count/index.md","filePath":"src/article/sort/count/index.md","lastUpdated":1728129125000}'),l={name:"src/article/sort/count/index.md"};function k(e,s,p,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Count Sort

                  counting sort is a kind of sorting algorithm that sacrifices memory space for low time complexity, and it is also a kind of algorithm that is not based on comparison. The non-comparison-based sorting algorithm here means that there is no comparison size between array elements. We know that the divide-and-conquer method to solve the sorting problem can only make the time complexity of the algorithm approach Θ(nlogn) at the fastest, that is, the time complexity based on comparison has a lower bound Ω(nlog⁡n), and the sorting algorithm based on no comparison can break through this lower bound.

                  Algorithm description

                  • Find the largest and smallest elements of the array to be sorted;
                  • Count the number of occurrences of each element with value i in the array, stored in the i - th item of the array C;
                  • Add up all counts (starting with the first element in C and adding each term to the previous one);
                  • Backfill the target array: Place each element i in the C(i) item of the new array, and subtract 1 from C(i) for each element.

                  GIF presentation

                  Count Sort

                  Code demo

                  ts
                  const getMax = (list: number[]) => {
                  +import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Count Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/count/index.md","filePath":"src/article/sort/count/index.md","lastUpdated":1728129344000}'),l={name:"src/article/sort/count/index.md"};function k(e,s,p,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Count Sort

                  counting sort is a kind of sorting algorithm that sacrifices memory space for low time complexity, and it is also a kind of algorithm that is not based on comparison. The non-comparison-based sorting algorithm here means that there is no comparison size between array elements. We know that the divide-and-conquer method to solve the sorting problem can only make the time complexity of the algorithm approach Θ(nlogn) at the fastest, that is, the time complexity based on comparison has a lower bound Ω(nlog⁡n), and the sorting algorithm based on no comparison can break through this lower bound.

                  Algorithm description

                  • Find the largest and smallest elements of the array to be sorted;
                  • Count the number of occurrences of each element with value i in the array, stored in the i - th item of the array C;
                  • Add up all counts (starting with the first element in C and adding each term to the previous one);
                  • Backfill the target array: Place each element i in the C(i) item of the new array, and subtract 1 from C(i) for each element.

                  GIF presentation

                  Count Sort

                  Code demo

                  ts
                  const getMax = (list: number[]) => {
                     let max = list[0];
                     for (let i = 1; i < list.length; i++) {
                       if (max < list[i]) {
                  diff --git a/assets/src_article_sort_count_index.md.Ch5HRRp8.lean.js b/assets/src_article_sort_count_index.md.Du6QhWUu.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_count_index.md.Ch5HRRp8.lean.js
                  rename to assets/src_article_sort_count_index.md.Du6QhWUu.lean.js
                  index cae0af1eff..cacb97f122 100644
                  --- a/assets/src_article_sort_count_index.md.Ch5HRRp8.lean.js
                  +++ b/assets/src_article_sort_count_index.md.Du6QhWUu.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Count Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/count/index.md","filePath":"src/article/sort/count/index.md","lastUpdated":1728129125000}'),l={name:"src/article/sort/count/index.md"};function k(e,s,p,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Count Sort

                  counting sort is a kind of sorting algorithm that sacrifices memory space for low time complexity, and it is also a kind of algorithm that is not based on comparison. The non-comparison-based sorting algorithm here means that there is no comparison size between array elements. We know that the divide-and-conquer method to solve the sorting problem can only make the time complexity of the algorithm approach Θ(nlogn) at the fastest, that is, the time complexity based on comparison has a lower bound Ω(nlog⁡n), and the sorting algorithm based on no comparison can break through this lower bound.

                  Algorithm description

                  • Find the largest and smallest elements of the array to be sorted;
                  • Count the number of occurrences of each element with value i in the array, stored in the i - th item of the array C;
                  • Add up all counts (starting with the first element in C and adding each term to the previous one);
                  • Backfill the target array: Place each element i in the C(i) item of the new array, and subtract 1 from C(i) for each element.

                  GIF presentation

                  Count Sort

                  Code demo

                  ts
                  const getMax = (list: number[]) => {
                  +import{_ as i}from"./chunks/count.CcfK-WL7.js";import{_ as a,o as n,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Count Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/count/index.md","filePath":"src/article/sort/count/index.md","lastUpdated":1728129344000}'),l={name:"src/article/sort/count/index.md"};function k(e,s,p,r,E,d){return n(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Count Sort

                  counting sort is a kind of sorting algorithm that sacrifices memory space for low time complexity, and it is also a kind of algorithm that is not based on comparison. The non-comparison-based sorting algorithm here means that there is no comparison size between array elements. We know that the divide-and-conquer method to solve the sorting problem can only make the time complexity of the algorithm approach Θ(nlogn) at the fastest, that is, the time complexity based on comparison has a lower bound Ω(nlog⁡n), and the sorting algorithm based on no comparison can break through this lower bound.

                  Algorithm description

                  • Find the largest and smallest elements of the array to be sorted;
                  • Count the number of occurrences of each element with value i in the array, stored in the i - th item of the array C;
                  • Add up all counts (starting with the first element in C and adding each term to the previous one);
                  • Backfill the target array: Place each element i in the C(i) item of the new array, and subtract 1 from C(i) for each element.

                  GIF presentation

                  Count Sort

                  Code demo

                  ts
                  const getMax = (list: number[]) => {
                     let max = list[0];
                     for (let i = 1; i < list.length; i++) {
                       if (max < list[i]) {
                  diff --git a/assets/src_article_sort_heap_index.md.D-XARjAx.js b/assets/src_article_sort_heap_index.md.DUjhT9HH.js
                  similarity index 99%
                  rename from assets/src_article_sort_heap_index.md.D-XARjAx.js
                  rename to assets/src_article_sort_heap_index.md.DUjhT9HH.js
                  index 6db7b2005f..d6b50a9d7c 100644
                  --- a/assets/src_article_sort_heap_index.md.D-XARjAx.js
                  +++ b/assets/src_article_sort_heap_index.md.DUjhT9HH.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Heap Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/heap/index.md","filePath":"src/article/sort/heap/index.md","lastUpdated":1728129125000}'),t={name:"src/article/sort/heap/index.md"};function l(p,s,e,r,E,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

                  Heap Sort

                  Heapsort (Heapsort) is a kind of sorting algorithm designed by using the heap data structure. A heap is an almost complete binary tree structure, but also satisfies the property of a heap: the key value or index of a child node is always less than (or greater than) its parent node.

                  Algorithm description

                  • The initial sequence of keywords to be sorted (R1,R2... .Rn) builds a large top heap, which is the initial disordered region;
                  • Swap the top element R[1] with the last element R[n] to get a new unordered region (R1,R2,... Rn-1) and a new ordered region (Rn), satisfying R[1,2... n-1]< =R[n];
                  • Since the new top R[1] may violate the nature of the heap after exchange, it is necessary to adjust the current disorder region (R1,R2,... Rn-1) adjusts to the new heap, then swaps R[1] again with the last element of the unordered area, giving a new unordered area (R1,R2... .Rn-2) and the new ordered region (RN-1,Rn). Repeat this process until the number of elements in the ordered area is n-1, then the sorting process is complete.
                  • Use large root piles in ascending order and small root piles in descending order

                  GIF presentation

                  Heap Sort

                  Code demo

                  ts
                  class Heap {
                  +import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Heap Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/heap/index.md","filePath":"src/article/sort/heap/index.md","lastUpdated":1728129344000}'),t={name:"src/article/sort/heap/index.md"};function l(p,s,e,r,E,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

                  Heap Sort

                  Heapsort (Heapsort) is a kind of sorting algorithm designed by using the heap data structure. A heap is an almost complete binary tree structure, but also satisfies the property of a heap: the key value or index of a child node is always less than (or greater than) its parent node.

                  Algorithm description

                  • The initial sequence of keywords to be sorted (R1,R2... .Rn) builds a large top heap, which is the initial disordered region;
                  • Swap the top element R[1] with the last element R[n] to get a new unordered region (R1,R2,... Rn-1) and a new ordered region (Rn), satisfying R[1,2... n-1]< =R[n];
                  • Since the new top R[1] may violate the nature of the heap after exchange, it is necessary to adjust the current disorder region (R1,R2,... Rn-1) adjusts to the new heap, then swaps R[1] again with the last element of the unordered area, giving a new unordered area (R1,R2... .Rn-2) and the new ordered region (RN-1,Rn). Repeat this process until the number of elements in the ordered area is n-1, then the sorting process is complete.
                  • Use large root piles in ascending order and small root piles in descending order

                  GIF presentation

                  Heap Sort

                  Code demo

                  ts
                  class Heap {
                     value: Array<number>;
                     size: number;
                     constructor(arr: Array<number> = []) {
                  diff --git a/assets/src_article_sort_heap_index.md.D-XARjAx.lean.js b/assets/src_article_sort_heap_index.md.DUjhT9HH.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_heap_index.md.D-XARjAx.lean.js
                  rename to assets/src_article_sort_heap_index.md.DUjhT9HH.lean.js
                  index 6db7b2005f..d6b50a9d7c 100644
                  --- a/assets/src_article_sort_heap_index.md.D-XARjAx.lean.js
                  +++ b/assets/src_article_sort_heap_index.md.DUjhT9HH.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Heap Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/heap/index.md","filePath":"src/article/sort/heap/index.md","lastUpdated":1728129125000}'),t={name:"src/article/sort/heap/index.md"};function l(p,s,e,r,E,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

                  Heap Sort

                  Heapsort (Heapsort) is a kind of sorting algorithm designed by using the heap data structure. A heap is an almost complete binary tree structure, but also satisfies the property of a heap: the key value or index of a child node is always less than (or greater than) its parent node.

                  Algorithm description

                  • The initial sequence of keywords to be sorted (R1,R2... .Rn) builds a large top heap, which is the initial disordered region;
                  • Swap the top element R[1] with the last element R[n] to get a new unordered region (R1,R2,... Rn-1) and a new ordered region (Rn), satisfying R[1,2... n-1]< =R[n];
                  • Since the new top R[1] may violate the nature of the heap after exchange, it is necessary to adjust the current disorder region (R1,R2,... Rn-1) adjusts to the new heap, then swaps R[1] again with the last element of the unordered area, giving a new unordered area (R1,R2... .Rn-2) and the new ordered region (RN-1,Rn). Repeat this process until the number of elements in the ordered area is n-1, then the sorting process is complete.
                  • Use large root piles in ascending order and small root piles in descending order

                  GIF presentation

                  Heap Sort

                  Code demo

                  ts
                  class Heap {
                  +import{_ as i}from"./chunks/heap.xduQWyUN.js";import{_ as a,o as h,c as n,a3 as k}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Heap Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/heap/index.md","filePath":"src/article/sort/heap/index.md","lastUpdated":1728129344000}'),t={name:"src/article/sort/heap/index.md"};function l(p,s,e,r,E,d){return h(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k('

                  Heap Sort

                  Heapsort (Heapsort) is a kind of sorting algorithm designed by using the heap data structure. A heap is an almost complete binary tree structure, but also satisfies the property of a heap: the key value or index of a child node is always less than (or greater than) its parent node.

                  Algorithm description

                  • The initial sequence of keywords to be sorted (R1,R2... .Rn) builds a large top heap, which is the initial disordered region;
                  • Swap the top element R[1] with the last element R[n] to get a new unordered region (R1,R2,... Rn-1) and a new ordered region (Rn), satisfying R[1,2... n-1]< =R[n];
                  • Since the new top R[1] may violate the nature of the heap after exchange, it is necessary to adjust the current disorder region (R1,R2,... Rn-1) adjusts to the new heap, then swaps R[1] again with the last element of the unordered area, giving a new unordered area (R1,R2... .Rn-2) and the new ordered region (RN-1,Rn). Repeat this process until the number of elements in the ordered area is n-1, then the sorting process is complete.
                  • Use large root piles in ascending order and small root piles in descending order

                  GIF presentation

                  Heap Sort

                  Code demo

                  ts
                  class Heap {
                     value: Array<number>;
                     size: number;
                     constructor(arr: Array<number> = []) {
                  diff --git a/assets/src_article_sort_index.md.LNhlg9Oa.js b/assets/src_article_sort_index.md.Day6RJ-s.js
                  similarity index 97%
                  rename from assets/src_article_sort_index.md.LNhlg9Oa.js
                  rename to assets/src_article_sort_index.md.Day6RJ-s.js
                  index 9561692345..35f9a7c550 100644
                  --- a/assets/src_article_sort_index.md.LNhlg9Oa.js
                  +++ b/assets/src_article_sort_index.md.Day6RJ-s.js
                  @@ -1 +1 @@
                  -import{_ as t,a}from"./chunks/complexity.CSkvDr7k.js";import{_ as o,o as i,c as r,a3 as n}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Ten classic sorting algorithms","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/index.md","filePath":"src/article/sort/index.md","lastUpdated":1728129125000}'),s={name:"src/article/sort/index.md"};function l(c,e,m,d,h,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[n('

                  Ten classic sorting algorithms

                  The ten common sorting algorithms can be divided into two broad categories:

                  • Comparator sort:By comparison to determine the relative order between elements, because its time complexity can not break through O(nlogn), so it is also called nonlinear time comparison class sorting.
                  • Non-comparison sort:Instead of determining the relative order between elements by comparison, it can break through the time lower bound based on comparison sort and run in linear time, so it is also called linear time non-comparison sort.

                  Sorting algorithm classification

                  Algorithm complexity

                  Algorithm complexity

                  • Stable: If a was originally in front of b and a=b, a is still in front of b after sorting.
                  • Unstable: If a originally comes before b and a=b, a may come after b after sorting.
                  • Time complexity: The total number of operations on sorted data. Reflects what rule the number of operations presents when n changes.
                  • Spatial complexity: refers to the measure of the storage space required when the algorithm is executed in the computer, and it is also a function of the data size n.
                  ',8)]))}const u=o(s,[["render",l]]);export{b as __pageData,u as default}; +import{_ as t,a}from"./chunks/complexity.CSkvDr7k.js";import{_ as o,o as i,c as r,a3 as n}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Ten classic sorting algorithms","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/index.md","filePath":"src/article/sort/index.md","lastUpdated":1728129344000}'),s={name:"src/article/sort/index.md"};function l(c,e,m,d,h,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[n('

                  Ten classic sorting algorithms

                  The ten common sorting algorithms can be divided into two broad categories:

                  • Comparator sort:By comparison to determine the relative order between elements, because its time complexity can not break through O(nlogn), so it is also called nonlinear time comparison class sorting.
                  • Non-comparison sort:Instead of determining the relative order between elements by comparison, it can break through the time lower bound based on comparison sort and run in linear time, so it is also called linear time non-comparison sort.

                  Sorting algorithm classification

                  Algorithm complexity

                  Algorithm complexity

                  • Stable: If a was originally in front of b and a=b, a is still in front of b after sorting.
                  • Unstable: If a originally comes before b and a=b, a may come after b after sorting.
                  • Time complexity: The total number of operations on sorted data. Reflects what rule the number of operations presents when n changes.
                  • Spatial complexity: refers to the measure of the storage space required when the algorithm is executed in the computer, and it is also a function of the data size n.
                  ',8)]))}const u=o(s,[["render",l]]);export{b as __pageData,u as default}; diff --git a/assets/src_article_sort_index.md.LNhlg9Oa.lean.js b/assets/src_article_sort_index.md.Day6RJ-s.lean.js similarity index 97% rename from assets/src_article_sort_index.md.LNhlg9Oa.lean.js rename to assets/src_article_sort_index.md.Day6RJ-s.lean.js index 9561692345..35f9a7c550 100644 --- a/assets/src_article_sort_index.md.LNhlg9Oa.lean.js +++ b/assets/src_article_sort_index.md.Day6RJ-s.lean.js @@ -1 +1 @@ -import{_ as t,a}from"./chunks/complexity.CSkvDr7k.js";import{_ as o,o as i,c as r,a3 as n}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Ten classic sorting algorithms","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/index.md","filePath":"src/article/sort/index.md","lastUpdated":1728129125000}'),s={name:"src/article/sort/index.md"};function l(c,e,m,d,h,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[n('

                  Ten classic sorting algorithms

                  The ten common sorting algorithms can be divided into two broad categories:

                  • Comparator sort:By comparison to determine the relative order between elements, because its time complexity can not break through O(nlogn), so it is also called nonlinear time comparison class sorting.
                  • Non-comparison sort:Instead of determining the relative order between elements by comparison, it can break through the time lower bound based on comparison sort and run in linear time, so it is also called linear time non-comparison sort.

                  Sorting algorithm classification

                  Algorithm complexity

                  Algorithm complexity

                  • Stable: If a was originally in front of b and a=b, a is still in front of b after sorting.
                  • Unstable: If a originally comes before b and a=b, a may come after b after sorting.
                  • Time complexity: The total number of operations on sorted data. Reflects what rule the number of operations presents when n changes.
                  • Spatial complexity: refers to the measure of the storage space required when the algorithm is executed in the computer, and it is also a function of the data size n.
                  ',8)]))}const u=o(s,[["render",l]]);export{b as __pageData,u as default}; +import{_ as t,a}from"./chunks/complexity.CSkvDr7k.js";import{_ as o,o as i,c as r,a3 as n}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Ten classic sorting algorithms","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/index.md","filePath":"src/article/sort/index.md","lastUpdated":1728129344000}'),s={name:"src/article/sort/index.md"};function l(c,e,m,d,h,p){return i(),r("div",{"data-pagefind-body":!0},e[0]||(e[0]=[n('

                  Ten classic sorting algorithms

                  The ten common sorting algorithms can be divided into two broad categories:

                  • Comparator sort:By comparison to determine the relative order between elements, because its time complexity can not break through O(nlogn), so it is also called nonlinear time comparison class sorting.
                  • Non-comparison sort:Instead of determining the relative order between elements by comparison, it can break through the time lower bound based on comparison sort and run in linear time, so it is also called linear time non-comparison sort.

                  Sorting algorithm classification

                  Algorithm complexity

                  Algorithm complexity

                  • Stable: If a was originally in front of b and a=b, a is still in front of b after sorting.
                  • Unstable: If a originally comes before b and a=b, a may come after b after sorting.
                  • Time complexity: The total number of operations on sorted data. Reflects what rule the number of operations presents when n changes.
                  • Spatial complexity: refers to the measure of the storage space required when the algorithm is executed in the computer, and it is also a function of the data size n.
                  ',8)]))}const u=o(s,[["render",l]]);export{b as __pageData,u as default}; diff --git a/assets/src_article_sort_insert_index.md.B-oNm95-.js b/assets/src_article_sort_insert_index.md.Cmba0n6W.js similarity index 99% rename from assets/src_article_sort_insert_index.md.B-oNm95-.js rename to assets/src_article_sort_insert_index.md.Cmba0n6W.js index 1aaad78399..d0914fad2e 100644 --- a/assets/src_article_sort_insert_index.md.B-oNm95-.js +++ b/assets/src_article_sort_insert_index.md.Cmba0n6W.js @@ -1,4 +1,4 @@ -import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Insert Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/insert/index.md","filePath":"src/article/sort/insert/index.md","lastUpdated":1728129125000}'),h={name:"src/article/sort/insert/index.md"};function l(p,s,r,k,d,o){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Insert Sort

                  Stable sorting algorithm, because no matter what data is entered is O(n2) time complexity, so when it is used, the smaller the data size, the better. The advantage is that no additional memory space is taken up. It works by building an ordered sequence and, for unsorted data, scanning from back to front in the sorted sequence, finding the appropriate position and inserting it.

                  Algorithm description

                  • Start with the first element, which can be considered to have been sorted;
                  • Take the next element and scan it from back to front in the already sorted sequence of elements;
                  • If the element (sorted) is larger than the new element, move the element to the next position;
                  • Repeat step 3 until you find a position where the sorted element is less than or equal to the new element;
                  • After inserting a new element into this position;
                  • Repeat Steps 2 to 5.

                  GIF presentation

                  Insert Sort

                  Code demo

                  ts
                  const insert = (list: number[]): number[] => {
                  +import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Insert Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/insert/index.md","filePath":"src/article/sort/insert/index.md","lastUpdated":1728129344000}'),h={name:"src/article/sort/insert/index.md"};function l(p,s,r,k,d,o){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Insert Sort

                  Stable sorting algorithm, because no matter what data is entered is O(n2) time complexity, so when it is used, the smaller the data size, the better. The advantage is that no additional memory space is taken up. It works by building an ordered sequence and, for unsorted data, scanning from back to front in the sorted sequence, finding the appropriate position and inserting it.

                  Algorithm description

                  • Start with the first element, which can be considered to have been sorted;
                  • Take the next element and scan it from back to front in the already sorted sequence of elements;
                  • If the element (sorted) is larger than the new element, move the element to the next position;
                  • Repeat step 3 until you find a position where the sorted element is less than or equal to the new element;
                  • After inserting a new element into this position;
                  • Repeat Steps 2 to 5.

                  GIF presentation

                  Insert Sort

                  Code demo

                  ts
                  const insert = (list: number[]): number[] => {
                     const size = list.length;
                     for (let i = 1; i < size; i++) {
                       const current = list[i];
                  diff --git a/assets/src_article_sort_insert_index.md.B-oNm95-.lean.js b/assets/src_article_sort_insert_index.md.Cmba0n6W.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_insert_index.md.B-oNm95-.lean.js
                  rename to assets/src_article_sort_insert_index.md.Cmba0n6W.lean.js
                  index 1aaad78399..d0914fad2e 100644
                  --- a/assets/src_article_sort_insert_index.md.B-oNm95-.lean.js
                  +++ b/assets/src_article_sort_insert_index.md.Cmba0n6W.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Insert Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/insert/index.md","filePath":"src/article/sort/insert/index.md","lastUpdated":1728129125000}'),h={name:"src/article/sort/insert/index.md"};function l(p,s,r,k,d,o){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Insert Sort

                  Stable sorting algorithm, because no matter what data is entered is O(n2) time complexity, so when it is used, the smaller the data size, the better. The advantage is that no additional memory space is taken up. It works by building an ordered sequence and, for unsorted data, scanning from back to front in the sorted sequence, finding the appropriate position and inserting it.

                  Algorithm description

                  • Start with the first element, which can be considered to have been sorted;
                  • Take the next element and scan it from back to front in the already sorted sequence of elements;
                  • If the element (sorted) is larger than the new element, move the element to the next position;
                  • Repeat step 3 until you find a position where the sorted element is less than or equal to the new element;
                  • After inserting a new element into this position;
                  • Repeat Steps 2 to 5.

                  GIF presentation

                  Insert Sort

                  Code demo

                  ts
                  const insert = (list: number[]): number[] => {
                  +import{_ as i}from"./chunks/insert.Bde3uDH4.js";import{_ as a,o as t,c as e,a3 as n}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Insert Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/insert/index.md","filePath":"src/article/sort/insert/index.md","lastUpdated":1728129344000}'),h={name:"src/article/sort/insert/index.md"};function l(p,s,r,k,d,o){return t(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n('

                  Insert Sort

                  Stable sorting algorithm, because no matter what data is entered is O(n2) time complexity, so when it is used, the smaller the data size, the better. The advantage is that no additional memory space is taken up. It works by building an ordered sequence and, for unsorted data, scanning from back to front in the sorted sequence, finding the appropriate position and inserting it.

                  Algorithm description

                  • Start with the first element, which can be considered to have been sorted;
                  • Take the next element and scan it from back to front in the already sorted sequence of elements;
                  • If the element (sorted) is larger than the new element, move the element to the next position;
                  • Repeat step 3 until you find a position where the sorted element is less than or equal to the new element;
                  • After inserting a new element into this position;
                  • Repeat Steps 2 to 5.

                  GIF presentation

                  Insert Sort

                  Code demo

                  ts
                  const insert = (list: number[]): number[] => {
                     const size = list.length;
                     for (let i = 1; i < size; i++) {
                       const current = list[i];
                  diff --git a/assets/src_article_sort_merge_index.md.D6h05wbs.js b/assets/src_article_sort_merge_index.md.BYCAvkHG.js
                  similarity index 99%
                  rename from assets/src_article_sort_merge_index.md.D6h05wbs.js
                  rename to assets/src_article_sort_merge_index.md.BYCAvkHG.js
                  index 046afd4e85..b5e4694316 100644
                  --- a/assets/src_article_sort_merge_index.md.D6h05wbs.js
                  +++ b/assets/src_article_sort_merge_index.md.BYCAvkHG.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Merge Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/merge/index.md","filePath":"src/article/sort/merge/index.md","lastUpdated":1728129125000}'),l={name:"src/article/sort/merge/index.md"};function k(e,s,p,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Merge Sort

                  Merge sort is an effective sort algorithm based on merge operation. This algorithm is a very typical application of Divide and Conquer. By combining the ordered subsequences, a completely ordered sequence is obtained. That is, each subsequence is ordered first, and then the subsequence segments are ordered. If two ordered tables are merged into one ordered table, it is called a 2-way merge.

                  Algorithm description

                  • Divide the input sequence of length n into two subsequences of length n/2;
                  • The two subsequences were merged and sorted respectively.
                  • Merge two sorted subsequences into one final sorted sequence.

                  GIF presentation

                  Merge Sort

                  Code demo

                  ts
                  const combine = (left: Array<number>, right: Array<number>) => {
                  +import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Merge Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/merge/index.md","filePath":"src/article/sort/merge/index.md","lastUpdated":1728129344000}'),l={name:"src/article/sort/merge/index.md"};function k(e,s,p,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Merge Sort

                  Merge sort is an effective sort algorithm based on merge operation. This algorithm is a very typical application of Divide and Conquer. By combining the ordered subsequences, a completely ordered sequence is obtained. That is, each subsequence is ordered first, and then the subsequence segments are ordered. If two ordered tables are merged into one ordered table, it is called a 2-way merge.

                  Algorithm description

                  • Divide the input sequence of length n into two subsequences of length n/2;
                  • The two subsequences were merged and sorted respectively.
                  • Merge two sorted subsequences into one final sorted sequence.

                  GIF presentation

                  Merge Sort

                  Code demo

                  ts
                  const combine = (left: Array<number>, right: Array<number>) => {
                     const list: Array<number> = [];
                     while (left.length > 0 && right.length > 0) {
                       if (left[0] <= right[0]) {
                  diff --git a/assets/src_article_sort_merge_index.md.D6h05wbs.lean.js b/assets/src_article_sort_merge_index.md.BYCAvkHG.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_merge_index.md.D6h05wbs.lean.js
                  rename to assets/src_article_sort_merge_index.md.BYCAvkHG.lean.js
                  index 046afd4e85..b5e4694316 100644
                  --- a/assets/src_article_sort_merge_index.md.D6h05wbs.lean.js
                  +++ b/assets/src_article_sort_merge_index.md.BYCAvkHG.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Merge Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/merge/index.md","filePath":"src/article/sort/merge/index.md","lastUpdated":1728129125000}'),l={name:"src/article/sort/merge/index.md"};function k(e,s,p,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Merge Sort

                  Merge sort is an effective sort algorithm based on merge operation. This algorithm is a very typical application of Divide and Conquer. By combining the ordered subsequences, a completely ordered sequence is obtained. That is, each subsequence is ordered first, and then the subsequence segments are ordered. If two ordered tables are merged into one ordered table, it is called a 2-way merge.

                  Algorithm description

                  • Divide the input sequence of length n into two subsequences of length n/2;
                  • The two subsequences were merged and sorted respectively.
                  • Merge two sorted subsequences into one final sorted sequence.

                  GIF presentation

                  Merge Sort

                  Code demo

                  ts
                  const combine = (left: Array<number>, right: Array<number>) => {
                  +import{_ as i}from"./chunks/merge.D_M4N_iU.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Merge Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/merge/index.md","filePath":"src/article/sort/merge/index.md","lastUpdated":1728129344000}'),l={name:"src/article/sort/merge/index.md"};function k(e,s,p,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Merge Sort

                  Merge sort is an effective sort algorithm based on merge operation. This algorithm is a very typical application of Divide and Conquer. By combining the ordered subsequences, a completely ordered sequence is obtained. That is, each subsequence is ordered first, and then the subsequence segments are ordered. If two ordered tables are merged into one ordered table, it is called a 2-way merge.

                  Algorithm description

                  • Divide the input sequence of length n into two subsequences of length n/2;
                  • The two subsequences were merged and sorted respectively.
                  • Merge two sorted subsequences into one final sorted sequence.

                  GIF presentation

                  Merge Sort

                  Code demo

                  ts
                  const combine = (left: Array<number>, right: Array<number>) => {
                     const list: Array<number> = [];
                     while (left.length > 0 && right.length > 0) {
                       if (left[0] <= right[0]) {
                  diff --git a/assets/src_article_sort_quick_index.md.C9uemKEa.js b/assets/src_article_sort_quick_index.md.BVXZPTlC.js
                  similarity index 99%
                  rename from assets/src_article_sort_quick_index.md.C9uemKEa.js
                  rename to assets/src_article_sort_quick_index.md.BVXZPTlC.js
                  index 9701ffee36..b50f5a3f6e 100644
                  --- a/assets/src_article_sort_quick_index.md.C9uemKEa.js
                  +++ b/assets/src_article_sort_quick_index.md.BVXZPTlC.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Quick Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/quick/index.md","filePath":"src/article/sort/quick/index.md","lastUpdated":1728129125000}'),k={name:"src/article/sort/quick/index.md"};function l(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Quick Sort

                  The basic idea of quick sorting is that the records to be sorted are separated into two independent parts through one sort. The keywords of one part of the records are smaller than those of the other part. Then the two parts of the records can be sorted separately to achieve the entire sequence.

                  Algorithm description

                  Quicksort uses divide-and-conquer to divide a list into two sub-lists. The specific algorithm is described as follows:

                  • Picking out an element from the sequence, called a "pivot";
                  • Reorder the sequence so that all elements smaller than the base value are placed in front of the base value and all elements larger than the base value are placed behind the base value (the same number can go to either side). After the partition exits, the benchmark is in the middle of the sequence. This is called a partition operation;
                  • Recursively sorts subsequences of elements less than the base value and subsequences of elements greater than the base value.

                  GIF presentation

                  Quick Sort

                  Code demo

                  ts
                  /**
                  +import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Quick Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/quick/index.md","filePath":"src/article/sort/quick/index.md","lastUpdated":1728129344000}'),k={name:"src/article/sort/quick/index.md"};function l(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Quick Sort

                  The basic idea of quick sorting is that the records to be sorted are separated into two independent parts through one sort. The keywords of one part of the records are smaller than those of the other part. Then the two parts of the records can be sorted separately to achieve the entire sequence.

                  Algorithm description

                  Quicksort uses divide-and-conquer to divide a list into two sub-lists. The specific algorithm is described as follows:

                  • Picking out an element from the sequence, called a "pivot";
                  • Reorder the sequence so that all elements smaller than the base value are placed in front of the base value and all elements larger than the base value are placed behind the base value (the same number can go to either side). After the partition exits, the benchmark is in the middle of the sequence. This is called a partition operation;
                  • Recursively sorts subsequences of elements less than the base value and subsequences of elements greater than the base value.

                  GIF presentation

                  Quick Sort

                  Code demo

                  ts
                  /**
                    * @description: Sets the base value pivot
                    * @param {Array} list
                    * @param {number} left
                  diff --git a/assets/src_article_sort_quick_index.md.C9uemKEa.lean.js b/assets/src_article_sort_quick_index.md.BVXZPTlC.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_quick_index.md.C9uemKEa.lean.js
                  rename to assets/src_article_sort_quick_index.md.BVXZPTlC.lean.js
                  index 9701ffee36..b50f5a3f6e 100644
                  --- a/assets/src_article_sort_quick_index.md.C9uemKEa.lean.js
                  +++ b/assets/src_article_sort_quick_index.md.BVXZPTlC.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Quick Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/quick/index.md","filePath":"src/article/sort/quick/index.md","lastUpdated":1728129125000}'),k={name:"src/article/sort/quick/index.md"};function l(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Quick Sort

                  The basic idea of quick sorting is that the records to be sorted are separated into two independent parts through one sort. The keywords of one part of the records are smaller than those of the other part. Then the two parts of the records can be sorted separately to achieve the entire sequence.

                  Algorithm description

                  Quicksort uses divide-and-conquer to divide a list into two sub-lists. The specific algorithm is described as follows:

                  • Picking out an element from the sequence, called a "pivot";
                  • Reorder the sequence so that all elements smaller than the base value are placed in front of the base value and all elements larger than the base value are placed behind the base value (the same number can go to either side). After the partition exits, the benchmark is in the middle of the sequence. This is called a partition operation;
                  • Recursively sorts subsequences of elements less than the base value and subsequences of elements greater than the base value.

                  GIF presentation

                  Quick Sort

                  Code demo

                  ts
                  /**
                  +import{_ as i}from"./chunks/quick.WcLzRUPH.js";import{_ as a,o as n,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"Quick Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/quick/index.md","filePath":"src/article/sort/quick/index.md","lastUpdated":1728129344000}'),k={name:"src/article/sort/quick/index.md"};function l(p,s,e,r,E,d){return n(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Quick Sort

                  The basic idea of quick sorting is that the records to be sorted are separated into two independent parts through one sort. The keywords of one part of the records are smaller than those of the other part. Then the two parts of the records can be sorted separately to achieve the entire sequence.

                  Algorithm description

                  Quicksort uses divide-and-conquer to divide a list into two sub-lists. The specific algorithm is described as follows:

                  • Picking out an element from the sequence, called a "pivot";
                  • Reorder the sequence so that all elements smaller than the base value are placed in front of the base value and all elements larger than the base value are placed behind the base value (the same number can go to either side). After the partition exits, the benchmark is in the middle of the sequence. This is called a partition operation;
                  • Recursively sorts subsequences of elements less than the base value and subsequences of elements greater than the base value.

                  GIF presentation

                  Quick Sort

                  Code demo

                  ts
                  /**
                    * @description: Sets the base value pivot
                    * @param {Array} list
                    * @param {number} left
                  diff --git a/assets/src_article_sort_radix_index.md.Ca-wVT5Q.js b/assets/src_article_sort_radix_index.md.CZgriu-L.js
                  similarity index 99%
                  rename from assets/src_article_sort_radix_index.md.Ca-wVT5Q.js
                  rename to assets/src_article_sort_radix_index.md.CZgriu-L.js
                  index d4f2b1ef52..78c0376083 100644
                  --- a/assets/src_article_sort_radix_index.md.Ca-wVT5Q.js
                  +++ b/assets/src_article_sort_radix_index.md.CZgriu-L.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as t,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Radix Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/radix/index.md","filePath":"src/article/sort/radix/index.md","lastUpdated":1728129125000}'),k={name:"src/article/sort/radix/index.md"};function l(p,s,e,r,E,d){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Radix Sort

                  Radix sort is sorted by the lowest order first, and then collected; Then sort by high order, and then collect; And so on until you reach the top. Sometimes properties are prioritized, first by low priority and then by high priority. The final order is that the higher priority comes first and the lower priority comes first if the higher priority is the same. Bucket sort extension, similar to specifying bucket sort by bit, but can take advantage of the count sort for a small range of numbers.

                  Algorithm description

                  • Get the largest number in the array, and get the number of bits;
                  • arr is the original array, and each bit is taken from the lowest point to form radix array.
                  • radix was sorted by counting (taking advantage of the feature that counting sort is suitable for a small range of numbers);

                  GIF presentation

                  Radix Sort

                  Code demo

                  ts
                  const getMax = (list: Array<number>) => {
                  +import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as t,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Radix Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/radix/index.md","filePath":"src/article/sort/radix/index.md","lastUpdated":1728129344000}'),k={name:"src/article/sort/radix/index.md"};function l(p,s,e,r,E,d){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Radix Sort

                  Radix sort is sorted by the lowest order first, and then collected; Then sort by high order, and then collect; And so on until you reach the top. Sometimes properties are prioritized, first by low priority and then by high priority. The final order is that the higher priority comes first and the lower priority comes first if the higher priority is the same. Bucket sort extension, similar to specifying bucket sort by bit, but can take advantage of the count sort for a small range of numbers.

                  Algorithm description

                  • Get the largest number in the array, and get the number of bits;
                  • arr is the original array, and each bit is taken from the lowest point to form radix array.
                  • radix was sorted by counting (taking advantage of the feature that counting sort is suitable for a small range of numbers);

                  GIF presentation

                  Radix Sort

                  Code demo

                  ts
                  const getMax = (list: Array<number>) => {
                     let max = list[0];
                     for (let i = 0; i < list.length; i++) {
                       if (max < list[i]) {
                  diff --git a/assets/src_article_sort_radix_index.md.Ca-wVT5Q.lean.js b/assets/src_article_sort_radix_index.md.CZgriu-L.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_radix_index.md.Ca-wVT5Q.lean.js
                  rename to assets/src_article_sort_radix_index.md.CZgriu-L.lean.js
                  index d4f2b1ef52..78c0376083 100644
                  --- a/assets/src_article_sort_radix_index.md.Ca-wVT5Q.lean.js
                  +++ b/assets/src_article_sort_radix_index.md.CZgriu-L.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as t,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Radix Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/radix/index.md","filePath":"src/article/sort/radix/index.md","lastUpdated":1728129125000}'),k={name:"src/article/sort/radix/index.md"};function l(p,s,e,r,E,d){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Radix Sort

                  Radix sort is sorted by the lowest order first, and then collected; Then sort by high order, and then collect; And so on until you reach the top. Sometimes properties are prioritized, first by low priority and then by high priority. The final order is that the higher priority comes first and the lower priority comes first if the higher priority is the same. Bucket sort extension, similar to specifying bucket sort by bit, but can take advantage of the count sort for a small range of numbers.

                  Algorithm description

                  • Get the largest number in the array, and get the number of bits;
                  • arr is the original array, and each bit is taken from the lowest point to form radix array.
                  • radix was sorted by counting (taking advantage of the feature that counting sort is suitable for a small range of numbers);

                  GIF presentation

                  Radix Sort

                  Code demo

                  ts
                  const getMax = (list: Array<number>) => {
                  +import{_ as i}from"./chunks/radix.CHOmrmB0.js";import{_ as a,o as t,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"Radix Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/radix/index.md","filePath":"src/article/sort/radix/index.md","lastUpdated":1728129344000}'),k={name:"src/article/sort/radix/index.md"};function l(p,s,e,r,E,d){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h('

                  Radix Sort

                  Radix sort is sorted by the lowest order first, and then collected; Then sort by high order, and then collect; And so on until you reach the top. Sometimes properties are prioritized, first by low priority and then by high priority. The final order is that the higher priority comes first and the lower priority comes first if the higher priority is the same. Bucket sort extension, similar to specifying bucket sort by bit, but can take advantage of the count sort for a small range of numbers.

                  Algorithm description

                  • Get the largest number in the array, and get the number of bits;
                  • arr is the original array, and each bit is taken from the lowest point to form radix array.
                  • radix was sorted by counting (taking advantage of the feature that counting sort is suitable for a small range of numbers);

                  GIF presentation

                  Radix Sort

                  Code demo

                  ts
                  const getMax = (list: Array<number>) => {
                     let max = list[0];
                     for (let i = 0; i < list.length; i++) {
                       if (max < list[i]) {
                  diff --git a/assets/src_article_sort_select_index.md.CgrU_Nnu.js b/assets/src_article_sort_select_index.md.DVQbDLxi.js
                  similarity index 99%
                  rename from assets/src_article_sort_select_index.md.CgrU_Nnu.js
                  rename to assets/src_article_sort_select_index.md.DVQbDLxi.js
                  index 62070c8e07..5336e8d360 100644
                  --- a/assets/src_article_sort_select_index.md.CgrU_Nnu.js
                  +++ b/assets/src_article_sort_select_index.md.DVQbDLxi.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as t,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Selection Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/select/index.md","filePath":"src/article/sort/select/index.md","lastUpdated":1728129125000}'),h={name:"src/article/sort/select/index.md"};function l(k,s,r,p,d,o){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e('

                  Selection Sort

                  Select-sort is a simple and intuitive sorting algorithm. It works by first finding the smallest (large) element in the unsorted sequence and storing it at the beginning of the sorted sequence, then continuing to find the smallest (large) element from the remaining unsorted elements and placing it at the end of the sorted sequence. And so on until all the elements are sorted.

                  Algorithm description

                  Ordering results can be obtained by direct selection sorting of n records through n-1 direct selection sorting. The specific algorithm is described as follows:

                  • Initial state: disordered region is R[1..n], ordered region is empty;
                  • i sort (i=1,2,3... n-1) At the beginning, the current ordered and disordered regions are R[1..i-1] and R(i.. n). The run sort selects the record R[k] with the smallest keyword from the current unordered area and swaps it with the first record R in the unordered area, so that R[1..i] and R[i+1..n) become a new ordered area with 1 more records and a new unordered area with 1 less records, respectively.
                  • n-1 is done. The array is ordered.

                  GIF presentation

                  Selection Sort

                  Code demo

                  js
                  const select = (list: number[]): number[] => {
                  +import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as t,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Selection Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/select/index.md","filePath":"src/article/sort/select/index.md","lastUpdated":1728129344000}'),h={name:"src/article/sort/select/index.md"};function l(k,s,r,p,d,o){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e('

                  Selection Sort

                  Select-sort is a simple and intuitive sorting algorithm. It works by first finding the smallest (large) element in the unsorted sequence and storing it at the beginning of the sorted sequence, then continuing to find the smallest (large) element from the remaining unsorted elements and placing it at the end of the sorted sequence. And so on until all the elements are sorted.

                  Algorithm description

                  Ordering results can be obtained by direct selection sorting of n records through n-1 direct selection sorting. The specific algorithm is described as follows:

                  • Initial state: disordered region is R[1..n], ordered region is empty;
                  • i sort (i=1,2,3... n-1) At the beginning, the current ordered and disordered regions are R[1..i-1] and R(i.. n). The run sort selects the record R[k] with the smallest keyword from the current unordered area and swaps it with the first record R in the unordered area, so that R[1..i] and R[i+1..n) become a new ordered area with 1 more records and a new unordered area with 1 less records, respectively.
                  • n-1 is done. The array is ordered.

                  GIF presentation

                  Selection Sort

                  Code demo

                  js
                  const select = (list: number[]): number[] => {
                     const size = list.length;
                     for (let i = 0; i < size; i++) {
                       let minIndex = i;
                  diff --git a/assets/src_article_sort_select_index.md.CgrU_Nnu.lean.js b/assets/src_article_sort_select_index.md.DVQbDLxi.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_select_index.md.CgrU_Nnu.lean.js
                  rename to assets/src_article_sort_select_index.md.DVQbDLxi.lean.js
                  index 62070c8e07..5336e8d360 100644
                  --- a/assets/src_article_sort_select_index.md.CgrU_Nnu.lean.js
                  +++ b/assets/src_article_sort_select_index.md.DVQbDLxi.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as t,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Selection Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/select/index.md","filePath":"src/article/sort/select/index.md","lastUpdated":1728129125000}'),h={name:"src/article/sort/select/index.md"};function l(k,s,r,p,d,o){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e('

                  Selection Sort

                  Select-sort is a simple and intuitive sorting algorithm. It works by first finding the smallest (large) element in the unsorted sequence and storing it at the beginning of the sorted sequence, then continuing to find the smallest (large) element from the remaining unsorted elements and placing it at the end of the sorted sequence. And so on until all the elements are sorted.

                  Algorithm description

                  Ordering results can be obtained by direct selection sorting of n records through n-1 direct selection sorting. The specific algorithm is described as follows:

                  • Initial state: disordered region is R[1..n], ordered region is empty;
                  • i sort (i=1,2,3... n-1) At the beginning, the current ordered and disordered regions are R[1..i-1] and R(i.. n). The run sort selects the record R[k] with the smallest keyword from the current unordered area and swaps it with the first record R in the unordered area, so that R[1..i] and R[i+1..n) become a new ordered area with 1 more records and a new unordered area with 1 less records, respectively.
                  • n-1 is done. The array is ordered.

                  GIF presentation

                  Selection Sort

                  Code demo

                  js
                  const select = (list: number[]): number[] => {
                  +import{_ as i}from"./chunks/select.BGReufCV.js";import{_ as a,o as t,c as n,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Selection Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/select/index.md","filePath":"src/article/sort/select/index.md","lastUpdated":1728129344000}'),h={name:"src/article/sort/select/index.md"};function l(k,s,r,p,d,o){return t(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e('

                  Selection Sort

                  Select-sort is a simple and intuitive sorting algorithm. It works by first finding the smallest (large) element in the unsorted sequence and storing it at the beginning of the sorted sequence, then continuing to find the smallest (large) element from the remaining unsorted elements and placing it at the end of the sorted sequence. And so on until all the elements are sorted.

                  Algorithm description

                  Ordering results can be obtained by direct selection sorting of n records through n-1 direct selection sorting. The specific algorithm is described as follows:

                  • Initial state: disordered region is R[1..n], ordered region is empty;
                  • i sort (i=1,2,3... n-1) At the beginning, the current ordered and disordered regions are R[1..i-1] and R(i.. n). The run sort selects the record R[k] with the smallest keyword from the current unordered area and swaps it with the first record R in the unordered area, so that R[1..i] and R[i+1..n) become a new ordered area with 1 more records and a new unordered area with 1 less records, respectively.
                  • n-1 is done. The array is ordered.

                  GIF presentation

                  Selection Sort

                  Code demo

                  js
                  const select = (list: number[]): number[] => {
                     const size = list.length;
                     for (let i = 0; i < size; i++) {
                       let minIndex = i;
                  diff --git a/assets/src_article_sort_shell_index.md.DOUhMGGU.js b/assets/src_article_sort_shell_index.md.CGmp_Zfr.js
                  similarity index 99%
                  rename from assets/src_article_sort_shell_index.md.DOUhMGGU.js
                  rename to assets/src_article_sort_shell_index.md.CGmp_Zfr.js
                  index 4038bc7cc9..3bba741c05 100644
                  --- a/assets/src_article_sort_shell_index.md.DOUhMGGU.js
                  +++ b/assets/src_article_sort_shell_index.md.CGmp_Zfr.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as e,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Shell Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/shell/index.md","filePath":"src/article/sort/shell/index.md","lastUpdated":1728129125000}'),l={name:"src/article/sort/shell/index.md"};function h(p,s,k,r,d,o){return n(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Shell Sort

                  Shell was invented in 1959, and the first breakthrough O(n2) sorting algorithm was an improved version of simple insertion sorting. It differs from insertion sort in that it gives preference to more distant elements. Hill sort is also called reduced increment sort.

                  Algorithm description

                  First, the whole record sequence to be sorted is divided into several sub-sequences for direct insertion sorting. The specific algorithm is described as follows:

                  • Select an incremental sequence t1, t2,... tk, wherein ti> tj, tk=1;
                  • According to the number of incremental sequences k, the sequence is sorted by k passes.
                  • For each sequence, according to the corresponding increment ti, the sequence to be sorted is divided into several sub-sequences with length m, and each sub-table is sorted by direct insertion. When the increment factor is only 1, the entire sequence is treated as a table, and the length of the table is the length of the entire sequence.

                  GIF presentation

                  Shell Sort

                  Code implementation

                  js
                  /**
                  +import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as e,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Shell Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/shell/index.md","filePath":"src/article/sort/shell/index.md","lastUpdated":1728129344000}'),l={name:"src/article/sort/shell/index.md"};function h(p,s,k,r,d,o){return n(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Shell Sort

                  Shell was invented in 1959, and the first breakthrough O(n2) sorting algorithm was an improved version of simple insertion sorting. It differs from insertion sort in that it gives preference to more distant elements. Hill sort is also called reduced increment sort.

                  Algorithm description

                  First, the whole record sequence to be sorted is divided into several sub-sequences for direct insertion sorting. The specific algorithm is described as follows:

                  • Select an incremental sequence t1, t2,... tk, wherein ti> tj, tk=1;
                  • According to the number of incremental sequences k, the sequence is sorted by k passes.
                  • For each sequence, according to the corresponding increment ti, the sequence to be sorted is divided into several sub-sequences with length m, and each sub-table is sorted by direct insertion. When the increment factor is only 1, the entire sequence is treated as a table, and the length of the table is the length of the entire sequence.

                  GIF presentation

                  Shell Sort

                  Code implementation

                  js
                  /**
                    * @description: Hill sort is an improved version of simple insertion sort. It differs from insertion sort in that it gives preference to more distant elements. Hill sort is also called reduced increment sort.
                    * @param {Array} list
                    * @return {Array}
                  diff --git a/assets/src_article_sort_shell_index.md.DOUhMGGU.lean.js b/assets/src_article_sort_shell_index.md.CGmp_Zfr.lean.js
                  similarity index 99%
                  rename from assets/src_article_sort_shell_index.md.DOUhMGGU.lean.js
                  rename to assets/src_article_sort_shell_index.md.CGmp_Zfr.lean.js
                  index 4038bc7cc9..3bba741c05 100644
                  --- a/assets/src_article_sort_shell_index.md.DOUhMGGU.lean.js
                  +++ b/assets/src_article_sort_shell_index.md.CGmp_Zfr.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as e,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Shell Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/shell/index.md","filePath":"src/article/sort/shell/index.md","lastUpdated":1728129125000}'),l={name:"src/article/sort/shell/index.md"};function h(p,s,k,r,d,o){return n(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Shell Sort

                  Shell was invented in 1959, and the first breakthrough O(n2) sorting algorithm was an improved version of simple insertion sorting. It differs from insertion sort in that it gives preference to more distant elements. Hill sort is also called reduced increment sort.

                  Algorithm description

                  First, the whole record sequence to be sorted is divided into several sub-sequences for direct insertion sorting. The specific algorithm is described as follows:

                  • Select an incremental sequence t1, t2,... tk, wherein ti> tj, tk=1;
                  • According to the number of incremental sequences k, the sequence is sorted by k passes.
                  • For each sequence, according to the corresponding increment ti, the sequence to be sorted is divided into several sub-sequences with length m, and each sub-table is sorted by direct insertion. When the increment factor is only 1, the entire sequence is treated as a table, and the length of the table is the length of the entire sequence.

                  GIF presentation

                  Shell Sort

                  Code implementation

                  js
                  /**
                  +import{_ as i}from"./chunks/shell.CGEkKxrp.js";import{_ as a,o as n,c as e,a3 as t}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Shell Sort","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/sort/shell/index.md","filePath":"src/article/sort/shell/index.md","lastUpdated":1728129344000}'),l={name:"src/article/sort/shell/index.md"};function h(p,s,k,r,d,o){return n(),e("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  Shell Sort

                  Shell was invented in 1959, and the first breakthrough O(n2) sorting algorithm was an improved version of simple insertion sorting. It differs from insertion sort in that it gives preference to more distant elements. Hill sort is also called reduced increment sort.

                  Algorithm description

                  First, the whole record sequence to be sorted is divided into several sub-sequences for direct insertion sorting. The specific algorithm is described as follows:

                  • Select an incremental sequence t1, t2,... tk, wherein ti> tj, tk=1;
                  • According to the number of incremental sequences k, the sequence is sorted by k passes.
                  • For each sequence, according to the corresponding increment ti, the sequence to be sorted is divided into several sub-sequences with length m, and each sub-table is sorted by direct insertion. When the increment factor is only 1, the entire sequence is treated as a table, and the length of the table is the length of the entire sequence.

                  GIF presentation

                  Shell Sort

                  Code implementation

                  js
                  /**
                    * @description: Hill sort is an improved version of simple insertion sort. It differs from insertion sort in that it gives preference to more distant elements. Hill sort is also called reduced increment sort.
                    * @param {Array} list
                    * @return {Array}
                  diff --git a/assets/src_article_typescript_calculate.md.CVW8Xao2.js b/assets/src_article_typescript_calculate.md.vojGlJJE.js
                  similarity index 99%
                  rename from assets/src_article_typescript_calculate.md.CVW8Xao2.js
                  rename to assets/src_article_typescript_calculate.md.vojGlJJE.js
                  index 8bfc27daac..56d83b22ad 100644
                  --- a/assets/src_article_typescript_calculate.md.CVW8Xao2.js
                  +++ b/assets/src_article_typescript_calculate.md.vojGlJJE.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/calculate.md","filePath":"src/article/typescript/calculate.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  数组长度做计数

                  类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

                  没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

                  这是类型体操的第四个套路:数组长度做计数。

                  TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

                  不知道大家有没有注意到数组类型取 length 就是数值。

                  比如:

                  ts
                  type num1 = [unknown]['length'];
                  +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/calculate.md","filePath":"src/article/typescript/calculate.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  数组长度做计数

                  类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

                  没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

                  这是类型体操的第四个套路:数组长度做计数。

                  TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

                  不知道大家有没有注意到数组类型取 length 就是数值。

                  比如:

                  ts
                  type num1 = [unknown]['length'];
                   // type num1 = 1
                   type num2 = [unknown, unknown]['length'];
                   // type num1 = 2
                  diff --git a/assets/src_article_typescript_calculate.md.CVW8Xao2.lean.js b/assets/src_article_typescript_calculate.md.vojGlJJE.lean.js
                  similarity index 99%
                  rename from assets/src_article_typescript_calculate.md.CVW8Xao2.lean.js
                  rename to assets/src_article_typescript_calculate.md.vojGlJJE.lean.js
                  index 8bfc27daac..56d83b22ad 100644
                  --- a/assets/src_article_typescript_calculate.md.CVW8Xao2.lean.js
                  +++ b/assets/src_article_typescript_calculate.md.vojGlJJE.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/calculate.md","filePath":"src/article/typescript/calculate.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  数组长度做计数

                  类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

                  没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

                  这是类型体操的第四个套路:数组长度做计数。

                  TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

                  不知道大家有没有注意到数组类型取 length 就是数值。

                  比如:

                  ts
                  type num1 = [unknown]['length'];
                  +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"数组长度做计数","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/calculate.md","filePath":"src/article/typescript/calculate.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/calculate.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  数组长度做计数

                  类型系统不是图灵完备,各种逻辑都能写么,但好像没发现数值相关的逻辑。

                  没错,数值相关的逻辑比较绕,被我单独摘了出来,就是这节要讲的内容。

                  这是类型体操的第四个套路:数组长度做计数。

                  TypeScript 类型系统没有加减乘除运算符,怎么做数值运算呢?

                  不知道大家有没有注意到数组类型取 length 就是数值。

                  比如:

                  ts
                  type num1 = [unknown]['length'];
                   // type num1 = 1
                   type num2 = [unknown, unknown]['length'];
                   // type num1 = 2
                  diff --git a/assets/src_article_typescript_index.md.CZyb-Nhf.js b/assets/src_article_typescript_index.md.CkcO1_RK.js
                  similarity index 99%
                  rename from assets/src_article_typescript_index.md.CZyb-Nhf.js
                  rename to assets/src_article_typescript_index.md.CkcO1_RK.js
                  index cc7b2e5992..e05746e31e 100644
                  --- a/assets/src_article_typescript_index.md.CZyb-Nhf.js
                  +++ b/assets/src_article_typescript_index.md.CkcO1_RK.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/index.md","filePath":"src/article/typescript/index.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  TypeScript 的类型系统

                  一。类型是什么

                  类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

                  • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

                  • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

                  有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

                  如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

                  类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

                  两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

                  其中,最常见的错误应该是“null is not an object”、“undefined is not a function”之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

                  所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

                  静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

                  静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

                  不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

                  所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

                  知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

                  动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

                  而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

                  所以,大型项目注定会用静态类型语言开发。

                  二。类型系统的分类

                  1.简单的类型系统

                  变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

                  这是最基础的类型系统,能保证类型安全,但有些死板。

                  比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

                  c
                  int add(int a, int b) {
                  +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/index.md","filePath":"src/article/typescript/index.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  TypeScript 的类型系统

                  一。类型是什么

                  类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

                  • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

                  • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

                  有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

                  如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

                  类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

                  两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

                  其中,最常见的错误应该是“null is not an object”、“undefined is not a function”之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

                  所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

                  静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

                  静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

                  不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

                  所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

                  知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

                  动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

                  而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

                  所以,大型项目注定会用静态类型语言开发。

                  二。类型系统的分类

                  1.简单的类型系统

                  变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

                  这是最基础的类型系统,能保证类型安全,但有些死板。

                  比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

                  c
                  int add(int a, int b) {
                       return a + b;
                   }
                   
                  diff --git a/assets/src_article_typescript_index.md.CZyb-Nhf.lean.js b/assets/src_article_typescript_index.md.CkcO1_RK.lean.js
                  similarity index 99%
                  rename from assets/src_article_typescript_index.md.CZyb-Nhf.lean.js
                  rename to assets/src_article_typescript_index.md.CkcO1_RK.lean.js
                  index cc7b2e5992..e05746e31e 100644
                  --- a/assets/src_article_typescript_index.md.CZyb-Nhf.lean.js
                  +++ b/assets/src_article_typescript_index.md.CkcO1_RK.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/index.md","filePath":"src/article/typescript/index.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  TypeScript 的类型系统

                  一。类型是什么

                  类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

                  • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

                  • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

                  有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

                  如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

                  类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

                  两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

                  其中,最常见的错误应该是“null is not an object”、“undefined is not a function”之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

                  所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

                  静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

                  静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

                  不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

                  所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

                  知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

                  动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

                  而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

                  所以,大型项目注定会用静态类型语言开发。

                  二。类型系统的分类

                  1.简单的类型系统

                  变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

                  这是最基础的类型系统,能保证类型安全,但有些死板。

                  比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

                  c
                  int add(int a, int b) {
                  +import{_ as i,o as a,c as h,a3 as n}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 的类型系统","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/index.md","filePath":"src/article/typescript/index.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/index.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  TypeScript 的类型系统

                  一。类型是什么

                  类型具体点来说就是指 number、boolean、string 等基础类型和 Object、Function 等复合类型,它们是编程语言提供的对不同内容的抽象:

                  • 不同类型变量占据的内存大小不同: boolean 类型的变量会分配 4 个字节的内存,而 number 类型的变量则会分配 8 个字节的内存,给变量声明了不同的类型就代表了会占据不同的内存空间。

                  • 不同类型变量可做的操作不同: number 类型可以做加减乘除等运算,boolean 就不可以,复合类型中不同类型的对象可用的方法不同,比如 Date 和 RegExp,变量的类型不同代表可以对该变量做的操作就不同。

                  有了类型,那我们的操作必须和类型相匹配,否则就会报错,这就是类型检查。

                  如果能保证对某种类型只做该类型允许的操作,这就叫做类型安全。

                  类型检查可以在运行时做,也可以运行之前的编译期做。这是两种不同的类型,前者叫做动态类型检查,后者叫做静态类型检查。

                  两种类型检查各有优缺点。动态类型检查 在源码中不保留类型信息,对某个变量赋什么值、做什么操作都是允许的,写代码很灵活。但这也埋下了类型不安全的隐患,比如对 string 做了乘除,对 Date 对象调用了 exec 方法,这些都是运行时才能检查出来的错误。

                  其中,最常见的错误应该是“null is not an object”、“undefined is not a function”之类的了,写代码时没发现类型不匹配,到了运行的时候才发现,就会有很多这种报错。

                  所以,动态类型虽然代码写起来简单,但代码中很容易藏着一些类型不匹配的隐患。

                  静态类型检查则是在源码中保留类型信息,声明变量要指定类型,对变量做的操作要和类型匹配,会有专门的编译器在编译期间做检查。

                  静态类型给写代码增加了一些难度,因为你除了要考虑代码要表达的逻辑之外,还要考虑类型逻辑:变量是什么类型的、是不是匹配、要不要做类型转换等。

                  不过,静态类型也消除了类型不安全的隐患,因为在编译期间就做了类型检查,就不会出现对 string 做了乘除,调用了 Date 的 exec 方法这类问题。

                  所以,静态类型虽然代码写起来要考虑的问题多一些,会复杂一些,但是却消除了代码中潜藏类型不安全问题的可能。

                  知道了动态类型检查和静态类型检查的区别,我们自然可以得出这样的结论:

                  动态类型只适合简单的场景,对于大项目却不太合适,因为代码中可能藏着的隐患太多了,万一线上报一个类型不匹配的错误,那可能就是大问题。

                  而静态类型虽然会增加写代码的成本,但是却能更好的保证代码的健壮性,减少 Bug 率。

                  所以,大型项目注定会用静态类型语言开发。

                  二。类型系统的分类

                  1.简单的类型系统

                  变量、函数、类等都可以声明类型,编译器会基于声明的类型做类型检查,类型不匹配时会报错。

                  这是最基础的类型系统,能保证类型安全,但有些死板。

                  比如一个 add 函数既可以做整数加法、又可以做浮点数加法,却需要声明两个函数:

                  c
                  int add(int a, int b) {
                       return a + b;
                   }
                   
                  diff --git a/assets/src_article_typescript_pattern.md.dRLdKmiq.js b/assets/src_article_typescript_pattern.md.CLkB9hDT.js
                  similarity index 99%
                  rename from assets/src_article_typescript_pattern.md.dRLdKmiq.js
                  rename to assets/src_article_typescript_pattern.md.CLkB9hDT.js
                  index d6f5845be4..7791f9bdb4 100644
                  --- a/assets/src_article_typescript_pattern.md.dRLdKmiq.js
                  +++ b/assets/src_article_typescript_pattern.md.CLkB9hDT.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/pattern.md","filePath":"src/article/typescript/pattern.md","lastUpdated":1728129125000}'),n={name:"src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  模式匹配提取

                  字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

                  ts
                  'abc'.replace(/a(b)c/, '$1,$1,$1');
                  +import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/pattern.md","filePath":"src/article/typescript/pattern.md","lastUpdated":1728129344000}'),n={name:"src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  模式匹配提取

                  字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

                  ts
                  'abc'.replace(/a(b)c/, '$1,$1,$1');
                   // 'b,b,b'

                  Typescript 的类型也同样可以做模式匹配。

                  比如这样一个 Promise 类型:

                  ts
                  type p = Promise<'value'>;

                  我们想提取 value 的类型,可以这样做:

                  ts
                  type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

                  通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

                  ts
                  // type GetValueResult = 'value'
                   type GetValueResult = GetValueType<Promise<'value'>>;

                  这就是 Typescript 类型的模式匹配:

                  Typescript 类型的模式匹配是通过 extends 对类型参数做匹配,结果保存到通过 infer 声明的局部类型变量里,如果匹配就能从该局部变量里拿到提取出的类型。

                  这个模式匹配的套路有多有用呢?我们来看下在数组、字符串、函数、构造器等类型里的应用。

                  1.数组类型

                  提取第一个元素

                  数组类型想提取第一个元素的类型怎么做呢?

                  ts
                  type arr = [1, 2, 3];

                  用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

                  ts
                  type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;

                  类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unkown 也就是可以是任何值。

                  any 和 unknown 的区别:any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

                  对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;
                   type GetFirstValue = GetFirst<[1, 2, 3]>;
                  diff --git a/assets/src_article_typescript_pattern.md.dRLdKmiq.lean.js b/assets/src_article_typescript_pattern.md.CLkB9hDT.lean.js
                  similarity index 99%
                  rename from assets/src_article_typescript_pattern.md.dRLdKmiq.lean.js
                  rename to assets/src_article_typescript_pattern.md.CLkB9hDT.lean.js
                  index d6f5845be4..7791f9bdb4 100644
                  --- a/assets/src_article_typescript_pattern.md.dRLdKmiq.lean.js
                  +++ b/assets/src_article_typescript_pattern.md.CLkB9hDT.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/pattern.md","filePath":"src/article/typescript/pattern.md","lastUpdated":1728129125000}'),n={name:"src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  模式匹配提取

                  字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

                  ts
                  'abc'.replace(/a(b)c/, '$1,$1,$1');
                  +import{_ as i,o as a,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"模式匹配提取","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/pattern.md","filePath":"src/article/typescript/pattern.md","lastUpdated":1728129344000}'),n={name:"src/article/typescript/pattern.md"};function k(p,s,l,e,r,d){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  模式匹配提取

                  字符串可以和正则做模式匹配,找到匹配的部分,提取子组,之后可以用 1,2 等引用匹配的子组。

                  ts
                  'abc'.replace(/a(b)c/, '$1,$1,$1');
                   // 'b,b,b'

                  Typescript 的类型也同样可以做模式匹配。

                  比如这样一个 Promise 类型:

                  ts
                  type p = Promise<'value'>;

                  我们想提取 value 的类型,可以这样做:

                  ts
                  type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

                  通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

                  ts
                  // type GetValueResult = 'value'
                   type GetValueResult = GetValueType<Promise<'value'>>;

                  这就是 Typescript 类型的模式匹配:

                  Typescript 类型的模式匹配是通过 extends 对类型参数做匹配,结果保存到通过 infer 声明的局部类型变量里,如果匹配就能从该局部变量里拿到提取出的类型。

                  这个模式匹配的套路有多有用呢?我们来看下在数组、字符串、函数、构造器等类型里的应用。

                  1.数组类型

                  提取第一个元素

                  数组类型想提取第一个元素的类型怎么做呢?

                  ts
                  type arr = [1, 2, 3];

                  用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

                  ts
                  type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;

                  类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unkown 也就是可以是任何值。

                  any 和 unknown 的区别:any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

                  对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type GetFirst<Arr extends unknown[]> = Arr extends [infer First, ...unknown[]] ? First : never;
                   type GetFirstValue = GetFirst<[1, 2, 3]>;
                  diff --git a/assets/src_article_typescript_reconstruction.md.RJXBSd9W.js b/assets/src_article_typescript_reconstruction.md.6hei5wds.js
                  similarity index 99%
                  rename from assets/src_article_typescript_reconstruction.md.RJXBSd9W.js
                  rename to assets/src_article_typescript_reconstruction.md.6hei5wds.js
                  index bdb5ec2d21..5c1202ae16 100644
                  --- a/assets/src_article_typescript_reconstruction.md.RJXBSd9W.js
                  +++ b/assets/src_article_typescript_reconstruction.md.6hei5wds.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/reconstruction.md","filePath":"src/article/typescript/reconstruction.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  重新构造做变换

                  类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

                  TypeScript 类型系统支持 3 种可以声明任意类型的变量:type、infer、类型参数。

                  type 叫做类型别名,其实就是声明一个变量存储某个类型:

                  ts
                  type ttt = Promise<number>;

                  infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

                  ts
                  type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

                  类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;

                  但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

                  TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

                  答案是重新构造。

                  这就涉及到了第二个类型体操套路:重新构造做变换。

                  重新构造

                  TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

                  数组、字符串、函数等类型的重新构造比较简单。

                  索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

                  我们先从简单的开始:

                  数组类型的重新构造

                  Push

                  有这样一个元组类型:

                  ts
                  type tuple = [1, 2, 3];

                  我想给这个元组类型再添加一些类型,怎么做呢?

                  TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

                  ts
                  type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

                  类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

                  类型参数 Ele 是添加的元素的类型。

                  返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

                  ts
                  type PushResult = Push<[1, 2, 3], 4>;
                  +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/reconstruction.md","filePath":"src/article/typescript/reconstruction.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  重新构造做变换

                  类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

                  TypeScript 类型系统支持 3 种可以声明任意类型的变量:type、infer、类型参数。

                  type 叫做类型别名,其实就是声明一个变量存储某个类型:

                  ts
                  type ttt = Promise<number>;

                  infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

                  ts
                  type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

                  类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;

                  但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

                  TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

                  答案是重新构造。

                  这就涉及到了第二个类型体操套路:重新构造做变换。

                  重新构造

                  TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

                  数组、字符串、函数等类型的重新构造比较简单。

                  索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

                  我们先从简单的开始:

                  数组类型的重新构造

                  Push

                  有这样一个元组类型:

                  ts
                  type tuple = [1, 2, 3];

                  我想给这个元组类型再添加一些类型,怎么做呢?

                  TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

                  ts
                  type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

                  类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

                  类型参数 Ele 是添加的元素的类型。

                  返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

                  ts
                  type PushResult = Push<[1, 2, 3], 4>;
                   // type PushResult = [1,2,3,4]

                  这就是数组/元组的重新构造。

                  数组和元组的区别:数组类型是指任意多个同一类型的元素构成的,比如 number[]Array<number>,而元组则是数量固定,类型可以不同的元素构成的,比如 [1, true, 'name']

                  Unshift

                  可以在后面添加,同样也可以在前面添加:

                  ts
                  type Unshift<Arr extends unknown[], Ele> = [Ele, ...Arr];

                  Zip

                  有这样两个元组:

                  ts
                  type tuple1 = [1, 2];
                   type tuple2 = ['name', 'value'];

                  我们想把它们合并成这样的元组:

                  ts
                  type tuple = [[1, 'name'], [2, 'value']];

                  思路很容易想到,提取元组中的两个元素,构造成新的元组:

                  ts
                  type Zip<One extends [unknown, unknown], Other extends [unknown, unknown]> = One extends [
                     infer OneFirst,
                  diff --git a/assets/src_article_typescript_reconstruction.md.RJXBSd9W.lean.js b/assets/src_article_typescript_reconstruction.md.6hei5wds.lean.js
                  similarity index 99%
                  rename from assets/src_article_typescript_reconstruction.md.RJXBSd9W.lean.js
                  rename to assets/src_article_typescript_reconstruction.md.6hei5wds.lean.js
                  index bdb5ec2d21..5c1202ae16 100644
                  --- a/assets/src_article_typescript_reconstruction.md.RJXBSd9W.lean.js
                  +++ b/assets/src_article_typescript_reconstruction.md.6hei5wds.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/reconstruction.md","filePath":"src/article/typescript/reconstruction.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  重新构造做变换

                  类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

                  TypeScript 类型系统支持 3 种可以声明任意类型的变量:type、infer、类型参数。

                  type 叫做类型别名,其实就是声明一个变量存储某个类型:

                  ts
                  type ttt = Promise<number>;

                  infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

                  ts
                  type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

                  类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;

                  但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

                  TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

                  答案是重新构造。

                  这就涉及到了第二个类型体操套路:重新构造做变换。

                  重新构造

                  TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

                  数组、字符串、函数等类型的重新构造比较简单。

                  索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

                  我们先从简单的开始:

                  数组类型的重新构造

                  Push

                  有这样一个元组类型:

                  ts
                  type tuple = [1, 2, 3];

                  我想给这个元组类型再添加一些类型,怎么做呢?

                  TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

                  ts
                  type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

                  类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

                  类型参数 Ele 是添加的元素的类型。

                  返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

                  ts
                  type PushResult = Push<[1, 2, 3], 4>;
                  +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"重新构造做变换","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/reconstruction.md","filePath":"src/article/typescript/reconstruction.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/reconstruction.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  重新构造做变换

                  类型编程主要的目的就是对类型做各种转换,那么如何对类型做修改呢?

                  TypeScript 类型系统支持 3 种可以声明任意类型的变量:type、infer、类型参数。

                  type 叫做类型别名,其实就是声明一个变量存储某个类型:

                  ts
                  type ttt = Promise<number>;

                  infer 用于类型的提取,然后存到一个变量里,相当于局部变量:

                  ts
                  type GetValueType<P> = P extends Promise<infer Value> ? Value : never;

                  类型参数用于接受具体的类型,在类型运算中也相当于局部变量:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;

                  但是,严格来说这三种也都不叫变量,因为它们不能被重新赋值。

                  TypeScript 设计可以做类型编程的类型系统的目的就是为了产生各种复杂的类型,那不能修改怎么产生新类型呢?

                  答案是重新构造。

                  这就涉及到了第二个类型体操套路:重新构造做变换。

                  重新构造

                  TypeScript 的 type、infer、类型参数声明的变量都不能修改,想对类型做各种变换产生新的类型就需要重新构造。

                  数组、字符串、函数等类型的重新构造比较简单。

                  索引类型,也就是多个元素的聚合类型的重新构造复杂一些,涉及到了映射类型的语法。

                  我们先从简单的开始:

                  数组类型的重新构造

                  Push

                  有这样一个元组类型:

                  ts
                  type tuple = [1, 2, 3];

                  我想给这个元组类型再添加一些类型,怎么做呢?

                  TypeScript 类型变量不支持修改,我们可以构造一个新的元组类型:

                  ts
                  type Push<Arr extends unknown[], Ele> = [...Arr, Ele];

                  类型参数 Arr 是要修改的数组/元组类型,元素的类型任意,也就是 unknown。

                  类型参数 Ele 是添加的元素的类型。

                  返回的是用 Arr 已有的元素加上 Ele 构造的新的元组类型。

                  ts
                  type PushResult = Push<[1, 2, 3], 4>;
                   // type PushResult = [1,2,3,4]

                  这就是数组/元组的重新构造。

                  数组和元组的区别:数组类型是指任意多个同一类型的元素构成的,比如 number[]Array<number>,而元组则是数量固定,类型可以不同的元素构成的,比如 [1, true, 'name']

                  Unshift

                  可以在后面添加,同样也可以在前面添加:

                  ts
                  type Unshift<Arr extends unknown[], Ele> = [Ele, ...Arr];

                  Zip

                  有这样两个元组:

                  ts
                  type tuple1 = [1, 2];
                   type tuple2 = ['name', 'value'];

                  我们想把它们合并成这样的元组:

                  ts
                  type tuple = [[1, 'name'], [2, 'value']];

                  思路很容易想到,提取元组中的两个元素,构造成新的元组:

                  ts
                  type Zip<One extends [unknown, unknown], Other extends [unknown, unknown]> = One extends [
                     infer OneFirst,
                  diff --git a/assets/src_article_typescript_recursion.md.CxwYW6pd.js b/assets/src_article_typescript_recursion.md.Cklrk5ux.js
                  similarity index 99%
                  rename from assets/src_article_typescript_recursion.md.CxwYW6pd.js
                  rename to assets/src_article_typescript_recursion.md.Cklrk5ux.js
                  index 042631b6df..8759ba1a2b 100644
                  --- a/assets/src_article_typescript_recursion.md.CxwYW6pd.js
                  +++ b/assets/src_article_typescript_recursion.md.Cklrk5ux.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/recursion.md","filePath":"src/article/typescript/recursion.md","lastUpdated":1728129125000}'),n={name:"src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  递归复用

                  递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

                  TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

                  TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

                  既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

                  Promise 的递归复用

                  DeepPromiseValueType

                  先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

                  ts
                  type ttt = Promise<Promise<Promise<Record<string, any>>>>;

                  这里是 3 层 Promise,value 类型是索引类型。

                  数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

                  所以高级类型是这样的:

                  ts
                  type DeepPromiseValueType<P extends Promise<unknown>> =
                  +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/recursion.md","filePath":"src/article/typescript/recursion.md","lastUpdated":1728129344000}'),n={name:"src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  递归复用

                  递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

                  TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

                  TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

                  既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

                  Promise 的递归复用

                  DeepPromiseValueType

                  先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

                  ts
                  type ttt = Promise<Promise<Promise<Record<string, any>>>>;

                  这里是 3 层 Promise,value 类型是索引类型。

                  数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

                  所以高级类型是这样的:

                  ts
                  type DeepPromiseValueType<P extends Promise<unknown>> =
                     P extends Promise<infer ValueType>
                       ? ValueType extends Promise<unknown>
                         ? DeepPromiseValueType<ValueType>
                  diff --git a/assets/src_article_typescript_recursion.md.CxwYW6pd.lean.js b/assets/src_article_typescript_recursion.md.Cklrk5ux.lean.js
                  similarity index 99%
                  rename from assets/src_article_typescript_recursion.md.CxwYW6pd.lean.js
                  rename to assets/src_article_typescript_recursion.md.Cklrk5ux.lean.js
                  index 042631b6df..8759ba1a2b 100644
                  --- a/assets/src_article_typescript_recursion.md.CxwYW6pd.lean.js
                  +++ b/assets/src_article_typescript_recursion.md.Cklrk5ux.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/recursion.md","filePath":"src/article/typescript/recursion.md","lastUpdated":1728129125000}'),n={name:"src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  递归复用

                  递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

                  TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

                  TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

                  既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

                  Promise 的递归复用

                  DeepPromiseValueType

                  先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

                  ts
                  type ttt = Promise<Promise<Promise<Record<string, any>>>>;

                  这里是 3 层 Promise,value 类型是索引类型。

                  数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

                  所以高级类型是这样的:

                  ts
                  type DeepPromiseValueType<P extends Promise<unknown>> =
                  +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"递归复用","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/recursion.md","filePath":"src/article/typescript/recursion.md","lastUpdated":1728129344000}'),n={name:"src/article/typescript/recursion.md"};function t(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  递归复用

                  递归是把问题分解为一系列相似的小问题,通过函数不断调用自身来解决这一个个小问题,直到满足结束条件,就完成了问题的求解。

                  TypeScript 的高级类型支持类型参数,可以做各种类型运算逻辑,返回新的类型,和函数调用是对应的,自然也支持递归。

                  TypeScript 类型系统不支持循环,但支持递归。当处理数量(个数、长度、层数)不固定的类型的时候,可以只处理一个类型,然后递归的调用自身处理下一个类型,直到结束条件也就是所有的类型都处理完了,就完成了不确定数量的类型编程,达到循环的效果。

                  既然提到了数组、字符串、对象等类型,那么我们就来看一下这些类型的递归案例吧。

                  Promise 的递归复用

                  DeepPromiseValueType

                  先用 Promise 热热身,实现一个提取不确定层数的 Promise 中的 value 类型的高级类型。

                  ts
                  type ttt = Promise<Promise<Promise<Record<string, any>>>>;

                  这里是 3 层 Promise,value 类型是索引类型。

                  数量不确定,一涉及到这个就要想到用递归来做,每次只处理一层的提取,然后剩下的到下次递归做,直到结束条件。

                  所以高级类型是这样的:

                  ts
                  type DeepPromiseValueType<P extends Promise<unknown>> =
                     P extends Promise<infer ValueType>
                       ? ValueType extends Promise<unknown>
                         ? DeepPromiseValueType<ValueType>
                  diff --git a/assets/src_article_typescript_unionType.md.lwThafAB.js b/assets/src_article_typescript_unionType.md.DWIWcdin.js
                  similarity index 99%
                  rename from assets/src_article_typescript_unionType.md.lwThafAB.js
                  rename to assets/src_article_typescript_unionType.md.DWIWcdin.js
                  index 7f4ecf7312..acb8aeebcc 100644
                  --- a/assets/src_article_typescript_unionType.md.lwThafAB.js
                  +++ b/assets/src_article_typescript_unionType.md.DWIWcdin.js
                  @@ -1 +1 @@
                  -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/unionType.md","filePath":"src/article/typescript/unionType.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  分布式条件类型

                  当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

                  比如这样一个联合类型:

                  ts
                  type Union = 'a' | 'b' | 'c';

                  我们想把其中的 a 大写,就可以这样写:

                  ts
                  type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
                  ts
                  type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

                  可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

                  这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

                  这和联合类型遇到字符串时的处理一样:

                  这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

                  TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

                  知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

                  CamelcaseUnion

                  Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

                  ts
                  type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

                  提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

                  ts
                  type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

                  如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

                  ts
                  type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

                  类型参数 Arr 为待处理数组。

                  递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

                  那如果是联合类型呢?

                  联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

                  ts
                  type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

                  这不和单个字符串的处理没区别么?

                  没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

                  确实简化了很多,好像都是优点?

                  也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

                  IsUnion

                  判断联合类型我们会这样写:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  当传入联合类型时,会返回 true:

                  ts
                  type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

                  当传入其他类型时,会返回 false:

                  ts
                  type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

                  这就是分布式条件类型带来的认知成本。

                  我们先来看这样一个类型:

                  ts
                  type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

                  传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

                  A 和 B 都是同一个联合类型,为啥值还不一样呢?

                  因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

                  所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c',A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

                  那么利用这个特点就可以实现 Union 类型的判断:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

                  A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

                  [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

                  B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

                  利用这个特点就可以判断出是否是联合类型。

                  其中有两个点比较困惑,我们重点记一下:

                  当 A 是联合类型时:

                  A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

                  A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

                  理解了这两点,分布式条件类型就算掌握了。

                  BEM

                  bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

                  那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

                  这样使用:

                  ts
                  type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

                  它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

                  而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

                  数组转联合类型可以这样写:

                  ts
                  type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

                  那么 BEM 就可以这样实现:

                  ts
                  type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

                  类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

                  构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

                  字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

                  ts
                  type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

                  可以看到,用好了联合类型,确实能简化类型编程逻辑。

                  AllCombinations

                  我们再来实现一个全组合的高级类型,也是联合类型相关的:

                  希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

                  这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

                  比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

                  任何两个类型的组合有四种:A、B、AB、BA

                  ts
                  type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

                  然后构造出来的字符串再和其他字符串组合。

                  所以全组合的高级类型就是这样:

                  ts
                  type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

                  类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

                  A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

                  A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

                  而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

                  总结

                  联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

                  条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

                  有两点特别要注意:

                  • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

                  • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

                  我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

                  ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/unionType.md","filePath":"src/article/typescript/unionType.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  分布式条件类型

                  当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

                  比如这样一个联合类型:

                  ts
                  type Union = 'a' | 'b' | 'c';

                  我们想把其中的 a 大写,就可以这样写:

                  ts
                  type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
                  ts
                  type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

                  可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

                  这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

                  这和联合类型遇到字符串时的处理一样:

                  这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

                  TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

                  知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

                  CamelcaseUnion

                  Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

                  ts
                  type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

                  提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

                  ts
                  type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

                  如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

                  ts
                  type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

                  类型参数 Arr 为待处理数组。

                  递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

                  那如果是联合类型呢?

                  联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

                  ts
                  type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

                  这不和单个字符串的处理没区别么?

                  没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

                  确实简化了很多,好像都是优点?

                  也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

                  IsUnion

                  判断联合类型我们会这样写:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  当传入联合类型时,会返回 true:

                  ts
                  type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

                  当传入其他类型时,会返回 false:

                  ts
                  type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

                  这就是分布式条件类型带来的认知成本。

                  我们先来看这样一个类型:

                  ts
                  type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

                  传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

                  A 和 B 都是同一个联合类型,为啥值还不一样呢?

                  因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

                  所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c',A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

                  那么利用这个特点就可以实现 Union 类型的判断:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

                  A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

                  [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

                  B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

                  利用这个特点就可以判断出是否是联合类型。

                  其中有两个点比较困惑,我们重点记一下:

                  当 A 是联合类型时:

                  A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

                  A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

                  理解了这两点,分布式条件类型就算掌握了。

                  BEM

                  bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

                  那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

                  这样使用:

                  ts
                  type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

                  它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

                  而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

                  数组转联合类型可以这样写:

                  ts
                  type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

                  那么 BEM 就可以这样实现:

                  ts
                  type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

                  类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

                  构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

                  字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

                  ts
                  type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

                  可以看到,用好了联合类型,确实能简化类型编程逻辑。

                  AllCombinations

                  我们再来实现一个全组合的高级类型,也是联合类型相关的:

                  希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

                  这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

                  比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

                  任何两个类型的组合有四种:A、B、AB、BA

                  ts
                  type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

                  然后构造出来的字符串再和其他字符串组合。

                  所以全组合的高级类型就是这样:

                  ts
                  type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

                  类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

                  A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

                  A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

                  而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

                  总结

                  联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

                  条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

                  有两点特别要注意:

                  • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

                  • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

                  我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

                  ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git a/assets/src_article_typescript_unionType.md.lwThafAB.lean.js b/assets/src_article_typescript_unionType.md.DWIWcdin.lean.js similarity index 99% rename from assets/src_article_typescript_unionType.md.lwThafAB.lean.js rename to assets/src_article_typescript_unionType.md.DWIWcdin.lean.js index 7f4ecf7312..acb8aeebcc 100644 --- a/assets/src_article_typescript_unionType.md.lwThafAB.lean.js +++ b/assets/src_article_typescript_unionType.md.DWIWcdin.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/unionType.md","filePath":"src/article/typescript/unionType.md","lastUpdated":1728129125000}'),k={name:"src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  分布式条件类型

                  当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

                  比如这样一个联合类型:

                  ts
                  type Union = 'a' | 'b' | 'c';

                  我们想把其中的 a 大写,就可以这样写:

                  ts
                  type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
                  ts
                  type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

                  可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

                  这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

                  这和联合类型遇到字符串时的处理一样:

                  这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

                  TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

                  知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

                  CamelcaseUnion

                  Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

                  ts
                  type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

                  提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

                  ts
                  type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

                  如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

                  ts
                  type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

                  类型参数 Arr 为待处理数组。

                  递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

                  那如果是联合类型呢?

                  联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

                  ts
                  type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

                  这不和单个字符串的处理没区别么?

                  没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

                  确实简化了很多,好像都是优点?

                  也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

                  IsUnion

                  判断联合类型我们会这样写:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  当传入联合类型时,会返回 true:

                  ts
                  type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

                  当传入其他类型时,会返回 false:

                  ts
                  type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

                  这就是分布式条件类型带来的认知成本。

                  我们先来看这样一个类型:

                  ts
                  type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

                  传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

                  A 和 B 都是同一个联合类型,为啥值还不一样呢?

                  因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

                  所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c',A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

                  那么利用这个特点就可以实现 Union 类型的判断:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

                  A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

                  [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

                  B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

                  利用这个特点就可以判断出是否是联合类型。

                  其中有两个点比较困惑,我们重点记一下:

                  当 A 是联合类型时:

                  A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

                  A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

                  理解了这两点,分布式条件类型就算掌握了。

                  BEM

                  bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

                  那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

                  这样使用:

                  ts
                  type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

                  它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

                  而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

                  数组转联合类型可以这样写:

                  ts
                  type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

                  那么 BEM 就可以这样实现:

                  ts
                  type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

                  类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

                  构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

                  字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

                  ts
                  type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

                  可以看到,用好了联合类型,确实能简化类型编程逻辑。

                  AllCombinations

                  我们再来实现一个全组合的高级类型,也是联合类型相关的:

                  希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

                  这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

                  比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

                  任何两个类型的组合有四种:A、B、AB、BA

                  ts
                  type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

                  然后构造出来的字符串再和其他字符串组合。

                  所以全组合的高级类型就是这样:

                  ts
                  type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

                  类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

                  A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

                  A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

                  而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

                  总结

                  联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

                  条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

                  有两点特别要注意:

                  • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

                  • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

                  我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

                  ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"分布式条件类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/article/typescript/unionType.md","filePath":"src/article/typescript/unionType.md","lastUpdated":1728129344000}'),k={name:"src/article/typescript/unionType.md"};function n(p,s,l,e,d,r){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  分布式条件类型

                  当类型参数为联合类型,并且在条件类型左边直接引用该类型参数的时候,TypeScript 会把每一个元素单独传入来做类型运算,最后再合并成联合类型,这种语法叫做分布式条件类型。

                  比如这样一个联合类型:

                  ts
                  type Union = 'a' | 'b' | 'c';

                  我们想把其中的 a 大写,就可以这样写:

                  ts
                  type UppercaseA<Item extends string> = Item extends 'a' ? Uppercase<Item> : Item;
                  ts
                  type result = UppercaseA<Union>;\n// type result = 'A' | 'b' | 'c';

                  可以看到,我们类型参数 Item 约束为 string,条件类型的判断中也是判断是否是 a,但传入的是联合类型。

                  这就是 TypeScript 对联合类型在条件类型中使用时的特殊处理:会把联合类型的每一个元素单独传入做类型计算,最后合并。

                  这和联合类型遇到字符串时的处理一样:

                  这样确实是简化了类型编程逻辑的,不需要递归提取每个元素再处理。

                  TypeScript 之所以这样处理联合类型也很容易理解,因为联合类型的每个元素都是互不相关的,不像数组、索引、字符串那样元素之间是有关系的。所以设计成了每一个单独处理,最后合并。

                  知道了 TypeScript 怎么处理的联合类型,趁热打铁来练习一下:

                  CamelcaseUnion

                  Camelcase 我们实现过,就是提取字符串中的字符,首字母大写以后重新构造一个新的。

                  ts
                  type Camelcase<Str extends string> = Str extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${Camelcase<Rest>}`\n  : Str;

                  提取 _ 左右的字符,把右边字符大写之后构造成新的字符串,余下的字符串递归处理。

                  ts
                  type CamelcaseResult = Camelcase<'aa_aa_aa'>;\n// type CamelcaseResult = 'aaAaAa'

                  如果是对字符串数组做 Camelcase,那就要递归处理每一个元素:

                  ts
                  type CamelcaseArr<Arr extends unknown[]> = Arr extends [infer Item, ...infer RestArr]\n  ? [Camelcase<Item & string>, ...CamelcaseArr<RestArr>]\n  : [];

                  类型参数 Arr 为待处理数组。

                  递归提取每一个元素做 Camelcase,因为 Camelcase 要求传入 string,这里要 & string 来变成 string 类型。

                  那如果是联合类型呢?

                  联合类型不需要递归提取每个元素,TypeScript 内部会把每一个元素传入单独做计算,之后把每个元素的计算结果合并成联合类型。

                  ts
                  type CamelcaseUnion<Item extends string> = Item extends `${infer Left}_${infer Right}${infer Rest}`\n  ? `${Left}${Uppercase<Right>}${CamelcaseUnion<Rest>}`\n  : Item;

                  这不和单个字符串的处理没区别么?

                  没错,对联合类型的处理和对单个类型的处理没什么区别,TypeScript 会把每个单独的类型拆开传入。不需要像数组类型那样需要递归提取每个元素做处理。

                  确实简化了很多,好像都是优点?

                  也不全是,其实这样处理也增加了一些认知成本,不信我们再来看个例子:

                  IsUnion

                  判断联合类型我们会这样写:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  当传入联合类型时,会返回 true:

                  ts
                  type IsUnionResult = IsUnion<'a' | 'b' 'c'>\n// type IsUnionResult = true

                  当传入其他类型时,会返回 false:

                  ts
                  type IsUnionResult = IsUnion<['a' | 'b' 'c']>\n// type IsUnionResult = false

                  这就是分布式条件类型带来的认知成本。

                  我们先来看这样一个类型:

                  ts
                  type TestUnion<A, B = A> = A extends A ? { a: A; b: B } : never;\n\ntype TestUnionResult = TestUnion<'a' | 'b' | 'c'>;

                  传入联合类型 'a' | 'b' | 'c' 的时候,结果是这样的:

                  A 和 B 都是同一个联合类型,为啥值还不一样呢?

                  因为条件类型中如果左边的类型是联合类型,会把每个元素单独传入做计算,而右边不会。

                  所以 A 是 'a' 的时候,B 是 'a' | 'b' | 'c',A 是 'b' 的时候,B 是 'a' | 'b' | 'c'。。。

                  那么利用这个特点就可以实现 Union 类型的判断:

                  ts
                  type IsUnion<A, B = A> = A extends A ? ([B] extends [A] ? false : true) : never;

                  类型参数 A、B 是待判断的联合类型,B 默认值为 A,也就是同一个类型。

                  A extends A 这段看似没啥意义,主要是为了触发分布式条件类型,让 A 的每个类型单独传入。

                  [B] extends [A] 这样不直接写 B 就可以避免触发分布式条件类型,那么 B 就是整个联合类型。

                  B 是联合类型整体,而 A 是单个类型,自然不成立,而其它类型没有这种特殊处理,A 和 B 都是同一个,怎么判断都成立。

                  利用这个特点就可以判断出是否是联合类型。

                  其中有两个点比较困惑,我们重点记一下:

                  当 A 是联合类型时:

                  A extends A 这种写法是为了触发分布式条件类型,让每个类型单独传入处理的,没别的意义。

                  A extends A 和 [A] extends [A] 是不同的处理,前者是单个类型和整个类型做判断,后者两边都是整个联合类型,因为只有 extends 左边直接是类型参数才会触发分布式条件类型。

                  理解了这两点,分布式条件类型就算掌握了。

                  BEM

                  bem 是 css 命名规范,用 block__element--modifier 的形式来描述某个区块下面的某个元素的某个状态的样式。

                  那么我们可以写这样一个高级类型,传入 block、element、modifier,返回构造出的 class 名:

                  这样使用:

                  ts
                  type bemResult = BEM<'guang', ['aaa', 'bbb'], ['warning', 'success']>;

                  它的实现就是三部分的合并,但传入的是数组,要递归遍历取出每一个元素来和其他部分组合,这样太麻烦了。

                  而如果是联合类型就不用递归遍历了,因为联合类型遇到字符串也是会单独每个元素单独传入做处理。

                  数组转联合类型可以这样写:

                  ts
                  type union = ['aaa', 'bbb'][number];\n// type union = 'aaa' | 'bbb'

                  那么 BEM 就可以这样实现:

                  ts
                  type BEM<\n  Block extends string,\n  Element extends string[],\n  Modifiers extends string[],\n> = `${Block}__${Element[number]}--${Modifiers[number]}`;

                  类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

                  构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

                  字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

                  ts
                  type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;\n// type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

                  可以看到,用好了联合类型,确实能简化类型编程逻辑。

                  AllCombinations

                  我们再来实现一个全组合的高级类型,也是联合类型相关的:

                  希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

                  这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

                  比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

                  任何两个类型的组合有四种:A、B、AB、BA

                  ts
                  type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

                  然后构造出来的字符串再和其他字符串组合。

                  所以全组合的高级类型就是这样:

                  ts
                  type AllCombinations<A extends string, B extends string = A> = A extends A\n  ? Combination<A, AllCombinations<Exclude<B, A>>>\n  : never;

                  类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

                  A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

                  A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

                  而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

                  总结

                  联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

                  条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

                  有两点特别要注意:

                  • A extends A 不是没意义,意义是取出联合类型中的单个类型放入 A

                  • A extends A 才是分布式条件类型, [A] extends [A] 就不是了,只有左边是单独的类型参数才可以。

                  我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

                  ',91)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git a/assets/src_ranui_button_index.md.Bv4CarK8.js b/assets/src_ranui_button_index.md.xyRWgUv8.js similarity index 99% rename from assets/src_ranui_button_index.md.Bv4CarK8.js rename to assets/src_ranui_button_index.md.xyRWgUv8.js index 10a24231b6..fd2507b847 100644 --- a/assets/src_ranui_button_index.md.Bv4CarK8.js +++ b/assets/src_ranui_button_index.md.xyRWgUv8.js @@ -1,4 +1,4 @@ -import{_ as a,o as n,c as e,a3 as t,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/button/index.md","filePath":"src/ranui/button/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/button/index.md"};function h(p,i,k,r,o,d){return n(),e("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t(`

                  Button

                  The button is used to start an instant action.

                  Code demo

                  Button
                  xml
                   <r-button >Button</r-button>

                  Attribute

                  type

                  There are four types of buttons

                  Primary button
                  Warning button
                  Text button
                  Default button
                  xml
                   <r-button type="primary">Primary button</r-button>
                  +import{_ as a,o as n,c as e,a3 as t,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/button/index.md","filePath":"src/ranui/button/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/button/index.md"};function h(p,i,k,r,o,d){return n(),e("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t(`

                  Button

                  The button is used to start an instant action.

                  Code demo

                  Button
                  xml
                   <r-button >Button</r-button>

                  Attribute

                  type

                  There are four types of buttons

                  Primary button
                  Warning button
                  Text button
                  Default button
                  xml
                   <r-button type="primary">Primary button</r-button>
                    <r-button type="warning">Warning button</r-button>
                    <r-button type="text">Text button</r-button>
                    <r-button >Default button</r-button>

                  disabled

                  Adding the disabled attribute makes the button unavailable and changes the button style.

                  Primary button
                  Warning button
                  Text button
                  Default button
                  xml
                   <r-button type="primary" disabled>Primary butto</r-button>
                  diff --git a/assets/src_ranui_button_index.md.Bv4CarK8.lean.js b/assets/src_ranui_button_index.md.xyRWgUv8.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_button_index.md.Bv4CarK8.lean.js
                  rename to assets/src_ranui_button_index.md.xyRWgUv8.lean.js
                  index 10a24231b6..fd2507b847 100644
                  --- a/assets/src_ranui_button_index.md.Bv4CarK8.lean.js
                  +++ b/assets/src_ranui_button_index.md.xyRWgUv8.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as n,c as e,a3 as t,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/button/index.md","filePath":"src/ranui/button/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/button/index.md"};function h(p,i,k,r,o,d){return n(),e("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t(`

                  Button

                  The button is used to start an instant action.

                  Code demo

                  Button
                  xml
                   <r-button >Button</r-button>

                  Attribute

                  type

                  There are four types of buttons

                  Primary button
                  Warning button
                  Text button
                  Default button
                  xml
                   <r-button type="primary">Primary button</r-button>
                  +import{_ as a,o as n,c as e,a3 as t,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Button","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/button/index.md","filePath":"src/ranui/button/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/button/index.md"};function h(p,i,k,r,o,d){return n(),e("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t(`

                  Button

                  The button is used to start an instant action.

                  Code demo

                  Button
                  xml
                   <r-button >Button</r-button>

                  Attribute

                  type

                  There are four types of buttons

                  Primary button
                  Warning button
                  Text button
                  Default button
                  xml
                   <r-button type="primary">Primary button</r-button>
                    <r-button type="warning">Warning button</r-button>
                    <r-button type="text">Text button</r-button>
                    <r-button >Default button</r-button>

                  disabled

                  Adding the disabled attribute makes the button unavailable and changes the button style.

                  Primary button
                  Warning button
                  Text button
                  Default button
                  xml
                   <r-button type="primary" disabled>Primary butto</r-button>
                  diff --git a/assets/src_ranui_checkbox_index.md.D6UQK38M.js b/assets/src_ranui_checkbox_index.md.DZWEhA_6.js
                  similarity index 99%
                  rename from assets/src_ranui_checkbox_index.md.D6UQK38M.js
                  rename to assets/src_ranui_checkbox_index.md.DZWEhA_6.js
                  index 38fd268969..445de73bc3 100644
                  --- a/assets/src_ranui_checkbox_index.md.D6UQK38M.js
                  +++ b/assets/src_ranui_checkbox_index.md.DZWEhA_6.js
                  @@ -1,2 +1,2 @@
                  -import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/checkbox/index.md","filePath":"src/ranui/checkbox/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/checkbox/index.md"};function k(n,s,c,d,p,o){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

                  CheckBox

                  Code demo

                  xml
                   <r-checkbox ></r-checkbox>

                  Attribute

                  checked

                  xml
                   <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

                  disabled

                  xml
                   <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

                  event

                  Common callback events.

                  onchange

                  ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
                  xml
                   <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
                  +import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/checkbox/index.md","filePath":"src/ranui/checkbox/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/checkbox/index.md"};function k(n,s,c,d,p,o){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

                  CheckBox

                  Code demo

                  xml
                   <r-checkbox ></r-checkbox>

                  Attribute

                  checked

                  xml
                   <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

                  disabled

                  xml
                   <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

                  event

                  Common callback events.

                  onchange

                  ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
                  xml
                   <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
                    <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
                  `,1)]))}const g=e(l,[["render",k]]);export{E as __pageData,g as default}; diff --git a/assets/src_ranui_checkbox_index.md.D6UQK38M.lean.js b/assets/src_ranui_checkbox_index.md.DZWEhA_6.lean.js similarity index 99% rename from assets/src_ranui_checkbox_index.md.D6UQK38M.lean.js rename to assets/src_ranui_checkbox_index.md.DZWEhA_6.lean.js index 38fd268969..445de73bc3 100644 --- a/assets/src_ranui_checkbox_index.md.D6UQK38M.lean.js +++ b/assets/src_ranui_checkbox_index.md.DZWEhA_6.lean.js @@ -1,2 +1,2 @@ -import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/checkbox/index.md","filePath":"src/ranui/checkbox/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/checkbox/index.md"};function k(n,s,c,d,p,o){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

                  CheckBox

                  Code demo

                  xml
                   <r-checkbox ></r-checkbox>

                  Attribute

                  checked

                  xml
                   <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

                  disabled

                  xml
                   <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

                  event

                  Common callback events.

                  onchange

                  ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
                  xml
                   <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
                  +import{_ as e,o as h,c as t,a3 as i,j as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"CheckBox","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/checkbox/index.md","filePath":"src/ranui/checkbox/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/checkbox/index.md"};function k(n,s,c,d,p,o){return h(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[i('

                  CheckBox

                  Code demo

                  xml
                   <r-checkbox ></r-checkbox>

                  Attribute

                  checked

                  xml
                   <r-checkbox checked="true"></r-checkbox>\n <r-checkbox checked="false"></r-checkbox>

                  disabled

                  xml
                   <r-checkbox checked="true" disabled></r-checkbox>\n <r-checkbox checked="false" disabled></r-checkbox>

                  event

                  Common callback events.

                  onchange

                  ',16),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),a("r-checkbox",{onchange:"console.log(this.checked)"},null,-1),i(`
                  xml
                   <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
                    <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
                  `,1)]))}const g=e(l,[["render",k]]);export{E as __pageData,g as default}; diff --git a/assets/src_ranui_icon_index.md.Cada4EMN.js b/assets/src_ranui_icon_index.md.CCbuLCdk.js similarity index 99% rename from assets/src_ranui_icon_index.md.Cada4EMN.js rename to assets/src_ranui_icon_index.md.CCbuLCdk.js index dcbaf9800c..d0707e6a27 100644 --- a/assets/src_ranui_icon_index.md.Cada4EMN.js +++ b/assets/src_ranui_icon_index.md.CCbuLCdk.js @@ -1,4 +1,4 @@ -import{_ as p,o as r,c as E,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/icon/index.md","filePath":"src/ranui/icon/index.md","lastUpdated":1728129125000}'),d={name:"src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return r(),E("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

                  Icon

                  Semantic vector graphics

                  Code demo

                  xml
                   <r-icon name="lock"  ></r-icon>
                  +import{_ as p,o as r,c as E,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/icon/index.md","filePath":"src/ranui/icon/index.md","lastUpdated":1728129344000}'),d={name:"src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return r(),E("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

                  Icon

                  Semantic vector graphics

                  Code demo

                  xml
                   <r-icon name="lock"  ></r-icon>
                    <r-icon name="eye"  ></r-icon>
                    <r-icon name="user"  ></r-icon>

                  Attribute

                  name

                  Select a different icon based on the name

                  html
                  <r-icon name="lock"></r-icon>
                   <r-icon name="eye"></r-icon>
                  diff --git a/assets/src_ranui_icon_index.md.Cada4EMN.lean.js b/assets/src_ranui_icon_index.md.CCbuLCdk.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_icon_index.md.Cada4EMN.lean.js
                  rename to assets/src_ranui_icon_index.md.CCbuLCdk.lean.js
                  index dcbaf9800c..d0707e6a27 100644
                  --- a/assets/src_ranui_icon_index.md.Cada4EMN.lean.js
                  +++ b/assets/src_ranui_icon_index.md.CCbuLCdk.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as p,o as r,c as E,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/icon/index.md","filePath":"src/ranui/icon/index.md","lastUpdated":1728129125000}'),d={name:"src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return r(),E("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

                  Icon

                  Semantic vector graphics

                  Code demo

                  xml
                   <r-icon name="lock"  ></r-icon>
                  +import{_ as p,o as r,c as E,a3 as e,j as n}from"./chunks/framework.eq-HTtE3.js";const o=()=>{setTimeout(()=>{const l=["add-user","book","check-circle","close-circle","eye-close","eye","info-circle","loading","lock","message","power-off","setting","team","unlock","user"];if(typeof document<"u"){const a=document.getElementById("icon-list"),s=document.createElement("div");s.style.setProperty("display","grid"),s.style.setProperty("grid-template-columns","repeat(3, 200px)"),s.style.setProperty("grid-template-rows","repeat(3, 200px);"),l.forEach(h=>{const i=document.createElement("div");i.style.setProperty("display","flex"),i.style.setProperty("align-items","center"),i.style.setProperty("margin","15px"),i.style.setProperty("justify-content","center"),i.style.setProperty("flex-flow","column nowrap");const t=document.createElement("r-icon");t.setAttribute("name",h),t.setAttribute("size","50"),i.appendChild(t);const k=document.createElement("span");k.innerHTML=h,i.appendChild(k),s==null||s.appendChild(i)}),a==null||a.appendChild(s)}},0)};o();const y=JSON.parse('{"title":"Icon","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/icon/index.md","filePath":"src/ranui/icon/index.md","lastUpdated":1728129344000}'),d={name:"src/ranui/icon/index.md"};function g(l,a,s,h,i,t){return r(),E("div",{"data-pagefind-body":!0},a[0]||(a[0]=[e(`

                  Icon

                  Semantic vector graphics

                  Code demo

                  xml
                   <r-icon name="lock"  ></r-icon>
                    <r-icon name="eye"  ></r-icon>
                    <r-icon name="user"  ></r-icon>

                  Attribute

                  name

                  Select a different icon based on the name

                  html
                  <r-icon name="lock"></r-icon>
                   <r-icon name="eye"></r-icon>
                  diff --git a/assets/src_ranui_image_index.md.iND1p-rT.js b/assets/src_ranui_image_index.md.DWE25Dbt.js
                  similarity index 98%
                  rename from assets/src_ranui_image_index.md.iND1p-rT.js
                  rename to assets/src_ranui_image_index.md.DWE25Dbt.js
                  index 94c7cb5ea6..ed699cfea3 100644
                  --- a/assets/src_ranui_image_index.md.iND1p-rT.js
                  +++ b/assets/src_ranui_image_index.md.DWE25Dbt.js
                  @@ -1 +1 @@
                  -import{_ as a,o as i,c as e,a3 as E,j as g}from"./chunks/framework.eq-HTtE3.js";const r=JSON.parse('{"title":"Image","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/image/index.md","filePath":"src/ranui/image/index.md","lastUpdated":1728129125000}'),Q={name:"src/ranui/image/index.md"};function s(t,A,o,n,d,h){return i(),e("div",{"data-pagefind-body":!0},A[0]||(A[0]=[E('

                  Image

                  Code demo

                  xml
                   <r-img src="" fallback=""></r-img>

                  Attribute

                  src

                  Image address

                  Image loading failurefallback

                  srcConfiguration of the picture loading failure, the bottom of the picture address, the following is the default loading failure picture

                  ',8),g("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const B=a(Q,[["render",s]]);export{r as __pageData,B as default}; +import{_ as a,o as i,c as e,a3 as E,j as g}from"./chunks/framework.eq-HTtE3.js";const r=JSON.parse('{"title":"Image","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/image/index.md","filePath":"src/ranui/image/index.md","lastUpdated":1728129344000}'),Q={name:"src/ranui/image/index.md"};function s(t,A,o,n,d,h){return i(),e("div",{"data-pagefind-body":!0},A[0]||(A[0]=[E('

                  Image

                  Code demo

                  xml
                   <r-img src="" fallback=""></r-img>

                  Attribute

                  src

                  Image address

                  Image loading failurefallback

                  srcConfiguration of the picture loading failure, the bottom of the picture address, the following is the default loading failure picture

                  ',8),g("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const B=a(Q,[["render",s]]);export{r as __pageData,B as default}; diff --git a/assets/src_ranui_image_index.md.iND1p-rT.lean.js b/assets/src_ranui_image_index.md.DWE25Dbt.lean.js similarity index 98% rename from assets/src_ranui_image_index.md.iND1p-rT.lean.js rename to assets/src_ranui_image_index.md.DWE25Dbt.lean.js index 94c7cb5ea6..ed699cfea3 100644 --- a/assets/src_ranui_image_index.md.iND1p-rT.lean.js +++ b/assets/src_ranui_image_index.md.DWE25Dbt.lean.js @@ -1 +1 @@ -import{_ as a,o as i,c as e,a3 as E,j as g}from"./chunks/framework.eq-HTtE3.js";const r=JSON.parse('{"title":"Image","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/image/index.md","filePath":"src/ranui/image/index.md","lastUpdated":1728129125000}'),Q={name:"src/ranui/image/index.md"};function s(t,A,o,n,d,h){return i(),e("div",{"data-pagefind-body":!0},A[0]||(A[0]=[E('

                  Image

                  Code demo

                  xml
                   <r-img src="" fallback=""></r-img>

                  Attribute

                  src

                  Image address

                  Image loading failurefallback

                  srcConfiguration of the picture loading failure, the bottom of the picture address, the following is the default loading failure picture

                  ',8),g("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const B=a(Q,[["render",s]]);export{r as __pageData,B as default}; +import{_ as a,o as i,c as e,a3 as E,j as g}from"./chunks/framework.eq-HTtE3.js";const r=JSON.parse('{"title":"Image","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/image/index.md","filePath":"src/ranui/image/index.md","lastUpdated":1728129344000}'),Q={name:"src/ranui/image/index.md"};function s(t,A,o,n,d,h){return i(),e("div",{"data-pagefind-body":!0},A[0]||(A[0]=[E('

                  Image

                  Code demo

                  xml
                   <r-img src="" fallback=""></r-img>

                  Attribute

                  src

                  Image address

                  Image loading failurefallback

                  srcConfiguration of the picture loading failure, the bottom of the picture address, the following is the default loading failure picture

                  ',8),g("r-img",{fallback:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="},null,-1)]))}const B=a(Q,[["render",s]]);export{r as __pageData,B as default}; diff --git a/assets/src_ranui_index.md.BQzsJSIN.js b/assets/src_ranui_index.md.CLoF5T0G.js similarity index 99% rename from assets/src_ranui_index.md.BQzsJSIN.js rename to assets/src_ranui_index.md.CLoF5T0G.js index 0cce7cdf39..606779c9c7 100644 --- a/assets/src_ranui_index.md.BQzsJSIN.js +++ b/assets/src_ranui_index.md.CLoF5T0G.js @@ -1,4 +1,4 @@ -import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/index.md","filePath":"src/ranui/index.md","lastUpdated":1728129125000}'),h={name:"src/ranui/index.md"};function p(k,a,r,d,E,o){return l(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  ranui

                  Development scheme based on Web Components

                  Feature

                  1. Cross-Framework Compatibility: Works seamlessly with React, Vue, Preact, SolidJS, Svelte, and more. Integrates with any JavaScript project following W3C standards.
                  2. Pure Native Experience: No need for npm, React/Vue, or build tools. Easy to start, like using native div tags, simplifying structure and reducing learning costs.
                  3. Modular Design: Breaks systems into small, reusable components for enhanced maintainability and scalability.
                  4. Open-Source: Licensed under MIT, providing free access to all source code for personal or commercial use.
                  5. Interactive Documentation: Offers detailed, interactive documentation with live examples for efficient learning.
                  6. Type-Checking: Built on TypeScript with full type support, ensuring robust and maintainable code.
                  7. Stability and Durability: Provides exceptional stability, avoiding disruptive updates and ensuring continuous project operation.

                  Situation

                  Build Statusnpm-vnpm-dbrotlimodule formats: umd, esm

                  Usage

                  In most cases, you can use it just like a native div tag.

                  Here are some examples

                  1. html
                  2. js
                  3. jsx
                  4. vue
                  5. tsx

                  1.html

                  html
                  <script src="./ranui/dist/umd/index.umd.cjs"></script>
                  +import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/index.md","filePath":"src/ranui/index.md","lastUpdated":1728129344000}'),h={name:"src/ranui/index.md"};function p(k,a,r,d,E,o){return l(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  ranui

                  Development scheme based on Web Components

                  Feature

                  1. Cross-Framework Compatibility: Works seamlessly with React, Vue, Preact, SolidJS, Svelte, and more. Integrates with any JavaScript project following W3C standards.
                  2. Pure Native Experience: No need for npm, React/Vue, or build tools. Easy to start, like using native div tags, simplifying structure and reducing learning costs.
                  3. Modular Design: Breaks systems into small, reusable components for enhanced maintainability and scalability.
                  4. Open-Source: Licensed under MIT, providing free access to all source code for personal or commercial use.
                  5. Interactive Documentation: Offers detailed, interactive documentation with live examples for efficient learning.
                  6. Type-Checking: Built on TypeScript with full type support, ensuring robust and maintainable code.
                  7. Stability and Durability: Provides exceptional stability, avoiding disruptive updates and ensuring continuous project operation.

                  Situation

                  Build Statusnpm-vnpm-dbrotlimodule formats: umd, esm

                  Usage

                  In most cases, you can use it just like a native div tag.

                  Here are some examples

                  1. html
                  2. js
                  3. jsx
                  4. vue
                  5. tsx

                  1.html

                  html
                  <script src="./ranui/dist/umd/index.umd.cjs"></script>
                   
                   <body>
                     <r-button>Button</r-button>
                  diff --git a/assets/src_ranui_index.md.BQzsJSIN.lean.js b/assets/src_ranui_index.md.CLoF5T0G.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_index.md.BQzsJSIN.lean.js
                  rename to assets/src_ranui_index.md.CLoF5T0G.lean.js
                  index 0cce7cdf39..606779c9c7 100644
                  --- a/assets/src_ranui_index.md.BQzsJSIN.lean.js
                  +++ b/assets/src_ranui_index.md.CLoF5T0G.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/index.md","filePath":"src/ranui/index.md","lastUpdated":1728129125000}'),h={name:"src/ranui/index.md"};function p(k,a,r,d,E,o){return l(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  ranui

                  Development scheme based on Web Components

                  Feature

                  1. Cross-Framework Compatibility: Works seamlessly with React, Vue, Preact, SolidJS, Svelte, and more. Integrates with any JavaScript project following W3C standards.
                  2. Pure Native Experience: No need for npm, React/Vue, or build tools. Easy to start, like using native div tags, simplifying structure and reducing learning costs.
                  3. Modular Design: Breaks systems into small, reusable components for enhanced maintainability and scalability.
                  4. Open-Source: Licensed under MIT, providing free access to all source code for personal or commercial use.
                  5. Interactive Documentation: Offers detailed, interactive documentation with live examples for efficient learning.
                  6. Type-Checking: Built on TypeScript with full type support, ensuring robust and maintainable code.
                  7. Stability and Durability: Provides exceptional stability, avoiding disruptive updates and ensuring continuous project operation.

                  Situation

                  Build Statusnpm-vnpm-dbrotlimodule formats: umd, esm

                  Usage

                  In most cases, you can use it just like a native div tag.

                  Here are some examples

                  1. html
                  2. js
                  3. jsx
                  4. vue
                  5. tsx

                  1.html

                  html
                  <script src="./ranui/dist/umd/index.umd.cjs"></script>
                  +import{_ as t}from"./chunks/customElements.qitHOM3M.js";import{_ as n,o as l,c as e,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"ranui","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/index.md","filePath":"src/ranui/index.md","lastUpdated":1728129344000}'),h={name:"src/ranui/index.md"};function p(k,a,r,d,E,o){return l(),e("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  ranui

                  Development scheme based on Web Components

                  Feature

                  1. Cross-Framework Compatibility: Works seamlessly with React, Vue, Preact, SolidJS, Svelte, and more. Integrates with any JavaScript project following W3C standards.
                  2. Pure Native Experience: No need for npm, React/Vue, or build tools. Easy to start, like using native div tags, simplifying structure and reducing learning costs.
                  3. Modular Design: Breaks systems into small, reusable components for enhanced maintainability and scalability.
                  4. Open-Source: Licensed under MIT, providing free access to all source code for personal or commercial use.
                  5. Interactive Documentation: Offers detailed, interactive documentation with live examples for efficient learning.
                  6. Type-Checking: Built on TypeScript with full type support, ensuring robust and maintainable code.
                  7. Stability and Durability: Provides exceptional stability, avoiding disruptive updates and ensuring continuous project operation.

                  Situation

                  Build Statusnpm-vnpm-dbrotlimodule formats: umd, esm

                  Usage

                  In most cases, you can use it just like a native div tag.

                  Here are some examples

                  1. html
                  2. js
                  3. jsx
                  4. vue
                  5. tsx

                  1.html

                  html
                  <script src="./ranui/dist/umd/index.umd.cjs"></script>
                   
                   <body>
                     <r-button>Button</r-button>
                  diff --git a/assets/src_ranui_input_index.md.dqo--3Vk.js b/assets/src_ranui_input_index.md.BYzbt7Jg.js
                  similarity index 99%
                  rename from assets/src_ranui_input_index.md.dqo--3Vk.js
                  rename to assets/src_ranui_input_index.md.BYzbt7Jg.js
                  index 5b004f4a75..116fc5dc22 100644
                  --- a/assets/src_ranui_input_index.md.dqo--3Vk.js
                  +++ b/assets/src_ranui_input_index.md.BYzbt7Jg.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as e,c as h,a3 as s,j as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Input","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/input/index.md","filePath":"src/ranui/input/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/input/index.md"};function p(k,a,r,d,E,o){return e(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

                  Input

                  Entering content via mouse or keyboard is the most basic form field packaging.

                  Code demo

                  Input field:
                  xml
                  <r-input></r-input>

                  Attribute

                  label

                  Provide an input experience similar to Metiral Design.

                  html
                  <r-input label="user"></r-input>

                  placeholder

                  Consistent with native 'placeholder'.

                  html
                  <r-input placeholder="user"></r-input>

                  disabled

                  The input box can be disabled by disabled. After disabled, the events on the button become invalid.

                  html
                  <r-input label="user" disabled></r-input>

                  value

                  Sets or returns the value of the 'value' property of the input box.

                  type

                  Currently support 'password', 'number' these types, set will appear additional 'ui' controls.

                  Password entry field

                  The password can be switched between plain text and ciphertext.

                  html
                  <r-input icon="lock" type="password"></r-input>

                  icon

                  You can set an 'icon' to represent the tag identifier.

                  html
                  <r-input icon="user"></r-input>

                  Digital input box

                  Numeric input box, similar to the native 'input[type=number]', support 'min', 'max', 'step' attributes, support keyboard up and down keys to switch numbers.

                  html
                  <r-input type="number" min="-10" max="10" step="0.5"></r-input>

                  name

                  Valid when associated with the form component, the field name collected when the form is submitted

                  status

                  • error

                  Default color value: #ff4d4f

                  ',40),i("div",null,[i("r-input",{status:"error"})],-1),s('
                  xml
                  <r-input status="error"></r-input>
                  • warning

                  Default color value:#ff7875

                  ',3),i("div",null,[i("r-input",{status:"warning"})],-1),s('
                  xml
                  <r-input  status="warning"></r-input>

                  event

                  Common callback events.

                  onchange

                  Triggered when text changes.

                  ',5),i("r-input",{onchange:"console.log(this.value)"},null,-1),s(`
                  html
                  <r-input onchange="func(this.value)"></r-input>
                  js
                  const input = document.createElement('r-input');
                  +import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as e,c as h,a3 as s,j as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Input","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/input/index.md","filePath":"src/ranui/input/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/input/index.md"};function p(k,a,r,d,E,o){return e(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

                  Input

                  Entering content via mouse or keyboard is the most basic form field packaging.

                  Code demo

                  Input field:
                  xml
                  <r-input></r-input>

                  Attribute

                  label

                  Provide an input experience similar to Metiral Design.

                  html
                  <r-input label="user"></r-input>

                  placeholder

                  Consistent with native 'placeholder'.

                  html
                  <r-input placeholder="user"></r-input>

                  disabled

                  The input box can be disabled by disabled. After disabled, the events on the button become invalid.

                  html
                  <r-input label="user" disabled></r-input>

                  value

                  Sets or returns the value of the 'value' property of the input box.

                  type

                  Currently support 'password', 'number' these types, set will appear additional 'ui' controls.

                  Password entry field

                  The password can be switched between plain text and ciphertext.

                  html
                  <r-input icon="lock" type="password"></r-input>

                  icon

                  You can set an 'icon' to represent the tag identifier.

                  html
                  <r-input icon="user"></r-input>

                  Digital input box

                  Numeric input box, similar to the native 'input[type=number]', support 'min', 'max', 'step' attributes, support keyboard up and down keys to switch numbers.

                  html
                  <r-input type="number" min="-10" max="10" step="0.5"></r-input>

                  name

                  Valid when associated with the form component, the field name collected when the form is submitted

                  status

                  • error

                  Default color value: #ff4d4f

                  ',40),i("div",null,[i("r-input",{status:"error"})],-1),s('
                  xml
                  <r-input status="error"></r-input>
                  • warning

                  Default color value:#ff7875

                  ',3),i("div",null,[i("r-input",{status:"warning"})],-1),s('
                  xml
                  <r-input  status="warning"></r-input>

                  event

                  Common callback events.

                  onchange

                  Triggered when text changes.

                  ',5),i("r-input",{onchange:"console.log(this.value)"},null,-1),s(`
                  html
                  <r-input onchange="func(this.value)"></r-input>
                  js
                  const input = document.createElement('r-input');
                   input.setAttribute('label', 'home');
                   const func = (e) => {
                     console.log(e);
                  diff --git a/assets/src_ranui_input_index.md.dqo--3Vk.lean.js b/assets/src_ranui_input_index.md.BYzbt7Jg.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_input_index.md.dqo--3Vk.lean.js
                  rename to assets/src_ranui_input_index.md.BYzbt7Jg.lean.js
                  index 5b004f4a75..116fc5dc22 100644
                  --- a/assets/src_ranui_input_index.md.dqo--3Vk.lean.js
                  +++ b/assets/src_ranui_input_index.md.BYzbt7Jg.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as e,c as h,a3 as s,j as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Input","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/input/index.md","filePath":"src/ranui/input/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/input/index.md"};function p(k,a,r,d,E,o){return e(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

                  Input

                  Entering content via mouse or keyboard is the most basic form field packaging.

                  Code demo

                  Input field:
                  xml
                  <r-input></r-input>

                  Attribute

                  label

                  Provide an input experience similar to Metiral Design.

                  html
                  <r-input label="user"></r-input>

                  placeholder

                  Consistent with native 'placeholder'.

                  html
                  <r-input placeholder="user"></r-input>

                  disabled

                  The input box can be disabled by disabled. After disabled, the events on the button become invalid.

                  html
                  <r-input label="user" disabled></r-input>

                  value

                  Sets or returns the value of the 'value' property of the input box.

                  type

                  Currently support 'password', 'number' these types, set will appear additional 'ui' controls.

                  Password entry field

                  The password can be switched between plain text and ciphertext.

                  html
                  <r-input icon="lock" type="password"></r-input>

                  icon

                  You can set an 'icon' to represent the tag identifier.

                  html
                  <r-input icon="user"></r-input>

                  Digital input box

                  Numeric input box, similar to the native 'input[type=number]', support 'min', 'max', 'step' attributes, support keyboard up and down keys to switch numbers.

                  html
                  <r-input type="number" min="-10" max="10" step="0.5"></r-input>

                  name

                  Valid when associated with the form component, the field name collected when the form is submitted

                  status

                  • error

                  Default color value: #ff4d4f

                  ',40),i("div",null,[i("r-input",{status:"error"})],-1),s('
                  xml
                  <r-input status="error"></r-input>
                  • warning

                  Default color value:#ff7875

                  ',3),i("div",null,[i("r-input",{status:"warning"})],-1),s('
                  xml
                  <r-input  status="warning"></r-input>

                  event

                  Common callback events.

                  onchange

                  Triggered when text changes.

                  ',5),i("r-input",{onchange:"console.log(this.value)"},null,-1),s(`
                  html
                  <r-input onchange="func(this.value)"></r-input>
                  js
                  const input = document.createElement('r-input');
                  +import{_ as t}from"./chunks/input-input.MnARRJC6.js";import{_ as n,o as e,c as h,a3 as s,j as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"Input","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/input/index.md","filePath":"src/ranui/input/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/input/index.md"};function p(k,a,r,d,E,o){return e(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s('

                  Input

                  Entering content via mouse or keyboard is the most basic form field packaging.

                  Code demo

                  Input field:
                  xml
                  <r-input></r-input>

                  Attribute

                  label

                  Provide an input experience similar to Metiral Design.

                  html
                  <r-input label="user"></r-input>

                  placeholder

                  Consistent with native 'placeholder'.

                  html
                  <r-input placeholder="user"></r-input>

                  disabled

                  The input box can be disabled by disabled. After disabled, the events on the button become invalid.

                  html
                  <r-input label="user" disabled></r-input>

                  value

                  Sets or returns the value of the 'value' property of the input box.

                  type

                  Currently support 'password', 'number' these types, set will appear additional 'ui' controls.

                  Password entry field

                  The password can be switched between plain text and ciphertext.

                  html
                  <r-input icon="lock" type="password"></r-input>

                  icon

                  You can set an 'icon' to represent the tag identifier.

                  html
                  <r-input icon="user"></r-input>

                  Digital input box

                  Numeric input box, similar to the native 'input[type=number]', support 'min', 'max', 'step' attributes, support keyboard up and down keys to switch numbers.

                  html
                  <r-input type="number" min="-10" max="10" step="0.5"></r-input>

                  name

                  Valid when associated with the form component, the field name collected when the form is submitted

                  status

                  • error

                  Default color value: #ff4d4f

                  ',40),i("div",null,[i("r-input",{status:"error"})],-1),s('
                  xml
                  <r-input status="error"></r-input>
                  • warning

                  Default color value:#ff7875

                  ',3),i("div",null,[i("r-input",{status:"warning"})],-1),s('
                  xml
                  <r-input  status="warning"></r-input>

                  event

                  Common callback events.

                  onchange

                  Triggered when text changes.

                  ',5),i("r-input",{onchange:"console.log(this.value)"},null,-1),s(`
                  html
                  <r-input onchange="func(this.value)"></r-input>
                  js
                  const input = document.createElement('r-input');
                   input.setAttribute('label', 'home');
                   const func = (e) => {
                     console.log(e);
                  diff --git a/assets/src_ranui_loading_index.md.Cr0qirnC.js b/assets/src_ranui_loading_index.md.CPbTFkjm.js
                  similarity index 98%
                  rename from assets/src_ranui_loading_index.md.Cr0qirnC.js
                  rename to assets/src_ranui_loading_index.md.CPbTFkjm.js
                  index ee4f7281c6..b70241c051 100644
                  --- a/assets/src_ranui_loading_index.md.Cr0qirnC.js
                  +++ b/assets/src_ranui_loading_index.md.CPbTFkjm.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/loading/index.md","filePath":"src/ranui/loading/index.md","lastUpdated":1728129125000}'),e={name:"src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

                  Loading

                  Some nice looking loading.

                  Code demo

                  xml
                  <r-loading name="circle"></r-loading>

                  Attribute

                  name

                  There's a lot of different loading here

                  xml
                  <r-loading name="double-bounce"></r-loading>
                  +import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/loading/index.md","filePath":"src/ranui/loading/index.md","lastUpdated":1728129344000}'),e={name:"src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

                  Loading

                  Some nice looking loading.

                  Code demo

                  xml
                  <r-loading name="circle"></r-loading>

                  Attribute

                  name

                  There's a lot of different loading here

                  xml
                  <r-loading name="double-bounce"></r-loading>
                   <r-loading name="rotate"></r-loading>
                   <r-loading name="stretch"></r-loading>
                   <r-loading name="cube"></r-loading>

                  Loading list

                  `,14)),l(a)]))}});export{o as __pageData,g as default}; diff --git a/assets/src_ranui_loading_index.md.Cr0qirnC.lean.js b/assets/src_ranui_loading_index.md.CPbTFkjm.lean.js similarity index 98% rename from assets/src_ranui_loading_index.md.Cr0qirnC.lean.js rename to assets/src_ranui_loading_index.md.CPbTFkjm.lean.js index ee4f7281c6..b70241c051 100644 --- a/assets/src_ranui_loading_index.md.Cr0qirnC.lean.js +++ b/assets/src_ranui_loading_index.md.CPbTFkjm.lean.js @@ -1,4 +1,4 @@ -import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/loading/index.md","filePath":"src/ranui/loading/index.md","lastUpdated":1728129125000}'),e={name:"src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

                  Loading

                  Some nice looking loading.

                  Code demo

                  xml
                  <r-loading name="circle"></r-loading>

                  Attribute

                  name

                  There's a lot of different loading here

                  xml
                  <r-loading name="double-bounce"></r-loading>
                  +import{_ as a}from"./chunks/loading.vue_vue_type_style_index_0_lang.Byf79MII.js";import{o as s,c as t,a3 as n,G as l}from"./chunks/framework.eq-HTtE3.js";import"./chunks/index.CafPLwUV.js";const o=JSON.parse('{"title":"Loading","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/loading/index.md","filePath":"src/ranui/loading/index.md","lastUpdated":1728129344000}'),e={name:"src/ranui/loading/index.md"},g=Object.assign(e,{setup(h){return(d,i)=>(s(),t("div",{"data-pagefind-body":!0},[i[0]||(i[0]=n(`

                  Loading

                  Some nice looking loading.

                  Code demo

                  xml
                  <r-loading name="circle"></r-loading>

                  Attribute

                  name

                  There's a lot of different loading here

                  xml
                  <r-loading name="double-bounce"></r-loading>
                   <r-loading name="rotate"></r-loading>
                   <r-loading name="stretch"></r-loading>
                   <r-loading name="cube"></r-loading>

                  Loading list

                  `,14)),l(a)]))}});export{o as __pageData,g as default}; diff --git a/assets/src_ranui_math_index.md.CAvnqA_Q.js b/assets/src_ranui_math_index.md.BEaDYiBh.js similarity index 97% rename from assets/src_ranui_math_index.md.CAvnqA_Q.js rename to assets/src_ranui_math_index.md.BEaDYiBh.js index db4c59c533..c636da726f 100644 --- a/assets/src_ranui_math_index.md.CAvnqA_Q.js +++ b/assets/src_ranui_math_index.md.BEaDYiBh.js @@ -1 +1 @@ -import{_ as e,o as l,c as h,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"math","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/math/index.md","filePath":"src/ranui/math/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/math/index.md"};function r(d,t,p,o,k,c){return l(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math",tabindex:"-1"},[s("math "),a("a",{class:"header-anchor",href:"#math","aria-label":'Permalink to "math"'},"​")],-1),a("p",null,"High-quality display of LaTeX in HTML pages",-1),a("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),a("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
                  xml
                  <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

                  Attribute

                  latex string

                  ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
                  xml
                    <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
                  ',1)]))}const E=e(n,[["render",r]]);export{m as __pageData,E as default}; +import{_ as e,o as l,c as h,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"math","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/math/index.md","filePath":"src/ranui/math/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/math/index.md"};function r(d,t,p,o,k,c){return l(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math",tabindex:"-1"},[s("math "),a("a",{class:"header-anchor",href:"#math","aria-label":'Permalink to "math"'},"​")],-1),a("p",null,"High-quality display of LaTeX in HTML pages",-1),a("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),a("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
                  xml
                  <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

                  Attribute

                  latex string

                  ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
                  xml
                    <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
                  ',1)]))}const E=e(n,[["render",r]]);export{m as __pageData,E as default}; diff --git a/assets/src_ranui_math_index.md.CAvnqA_Q.lean.js b/assets/src_ranui_math_index.md.BEaDYiBh.lean.js similarity index 97% rename from assets/src_ranui_math_index.md.CAvnqA_Q.lean.js rename to assets/src_ranui_math_index.md.BEaDYiBh.lean.js index db4c59c533..c636da726f 100644 --- a/assets/src_ranui_math_index.md.CAvnqA_Q.lean.js +++ b/assets/src_ranui_math_index.md.BEaDYiBh.lean.js @@ -1 +1 @@ -import{_ as e,o as l,c as h,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"math","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/math/index.md","filePath":"src/ranui/math/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/math/index.md"};function r(d,t,p,o,k,c){return l(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math",tabindex:"-1"},[s("math "),a("a",{class:"header-anchor",href:"#math","aria-label":'Permalink to "math"'},"​")],-1),a("p",null,"High-quality display of LaTeX in HTML pages",-1),a("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),a("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
                  xml
                  <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

                  Attribute

                  latex string

                  ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
                  xml
                    <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
                  ',1)]))}const E=e(n,[["render",r]]);export{m as __pageData,E as default}; +import{_ as e,o as l,c as h,j as a,a as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"math","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/math/index.md","filePath":"src/ranui/math/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/math/index.md"};function r(d,t,p,o,k,c){return l(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[a("h1",{id:"math",tabindex:"-1"},[s("math "),a("a",{class:"header-anchor",href:"#math","aria-label":'Permalink to "math"'},"​")],-1),a("p",null,"High-quality display of LaTeX in HTML pages",-1),a("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),a("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),a("r-math",{latex:"\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"},null,-1),i('
                  xml
                  <r-math latex="\\frac{x^2}{a^2} + \\frac{y^2}{b^2} = 1 \\quad (a > b > 0)"></r-math>

                  Attribute

                  latex string

                  ',3),a("r-math",{latex:"x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"},null,-1),i('
                  xml
                    <r-math latex="x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}"></r-math>
                  ',1)]))}const E=e(n,[["render",r]]);export{m as __pageData,E as default}; diff --git a/assets/src_ranui_message_index.md.Dr_PZANG.js b/assets/src_ranui_message_index.md.C9TUA1qN.js similarity index 99% rename from assets/src_ranui_message_index.md.Dr_PZANG.js rename to assets/src_ranui_message_index.md.C9TUA1qN.js index b0034649c2..edc3a8f55d 100644 --- a/assets/src_ranui_message_index.md.Dr_PZANG.js +++ b/assets/src_ranui_message_index.md.C9TUA1qN.js @@ -1,4 +1,4 @@ -import{_ as e,o as n,c as h,j as s,a as t,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"message","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/message/index.md","filePath":"src/ranui/message/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/message/index.md"};function p(o,i,k,r,d,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message",tabindex:"-1"},[t("message "),s("a",{class:"header-anchor",href:"#message","aria-label":'Permalink to "message"'},"​")],-1),s("p",null,"Global display of operation feedback.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('This is a hint')"},"Click to trigger the global prompt")],-1),a('
                  xml
                  <r-button type="primary" onclick="message.info('This is a hint')">Click to trigger the global prompt</r-button>

                  Attribute

                  type

                  Different prompt types

                  ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('This is a hint')"},"Information prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('This is a hint')"},"Warning prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('This is a hint')"},"Error prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('This is a hint')"},"Success tip")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('This is a hint')"},"toast tip")],-1),a(`
                  html
                  <r-button onclick="message.info('This is a hint')">Information prompt</r-button>
                  +import{_ as e,o as n,c as h,j as s,a as t,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"message","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/message/index.md","filePath":"src/ranui/message/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/message/index.md"};function p(o,i,k,r,d,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message",tabindex:"-1"},[t("message "),s("a",{class:"header-anchor",href:"#message","aria-label":'Permalink to "message"'},"​")],-1),s("p",null,"Global display of operation feedback.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('This is a hint')"},"Click to trigger the global prompt")],-1),a('
                  xml
                  <r-button type="primary" onclick="message.info('This is a hint')">Click to trigger the global prompt</r-button>

                  Attribute

                  type

                  Different prompt types

                  ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('This is a hint')"},"Information prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('This is a hint')"},"Warning prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('This is a hint')"},"Error prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('This is a hint')"},"Success tip")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('This is a hint')"},"toast tip")],-1),a(`
                  html
                  <r-button onclick="message.info('This is a hint')">Information prompt</r-button>
                   <r-button onclick="message.warning('This is a hint')">Warning prompt</r-button>
                   <r-button onclick="message.error('This is a hint')">Error prompt</r-button>
                   <r-button onclick="message.success('This is a hint')">Success tip</r-button>
                  diff --git a/assets/src_ranui_message_index.md.Dr_PZANG.lean.js b/assets/src_ranui_message_index.md.C9TUA1qN.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_message_index.md.Dr_PZANG.lean.js
                  rename to assets/src_ranui_message_index.md.C9TUA1qN.lean.js
                  index b0034649c2..edc3a8f55d 100644
                  --- a/assets/src_ranui_message_index.md.Dr_PZANG.lean.js
                  +++ b/assets/src_ranui_message_index.md.C9TUA1qN.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as e,o as n,c as h,j as s,a as t,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"message","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/message/index.md","filePath":"src/ranui/message/index.md","lastUpdated":1728129125000}'),l={name:"src/ranui/message/index.md"};function p(o,i,k,r,d,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message",tabindex:"-1"},[t("message "),s("a",{class:"header-anchor",href:"#message","aria-label":'Permalink to "message"'},"​")],-1),s("p",null,"Global display of operation feedback.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('This is a hint')"},"Click to trigger the global prompt")],-1),a('
                  xml
                  <r-button type="primary" onclick="message.info('This is a hint')">Click to trigger the global prompt</r-button>

                  Attribute

                  type

                  Different prompt types

                  ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('This is a hint')"},"Information prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('This is a hint')"},"Warning prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('This is a hint')"},"Error prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('This is a hint')"},"Success tip")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('This is a hint')"},"toast tip")],-1),a(`
                  html
                  <r-button onclick="message.info('This is a hint')">Information prompt</r-button>
                  +import{_ as e,o as n,c as h,j as s,a as t,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"message","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/message/index.md","filePath":"src/ranui/message/index.md","lastUpdated":1728129344000}'),l={name:"src/ranui/message/index.md"};function p(o,i,k,r,d,g){return n(),h("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"message",tabindex:"-1"},[t("message "),s("a",{class:"header-anchor",href:"#message","aria-label":'Permalink to "message"'},"​")],-1),s("p",null,"Global display of operation feedback.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{type:"primary",onclick:"message.info('This is a hint')"},"Click to trigger the global prompt")],-1),a('
                  xml
                  <r-button type="primary" onclick="message.info('This is a hint')">Click to trigger the global prompt</r-button>

                  Attribute

                  type

                  Different prompt types

                  ',4),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.info('This is a hint')"},"Information prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.warning('This is a hint')"},"Warning prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.error('This is a hint')"},"Error prompt")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.success('This is a hint')"},"Success tip")],-1),s("div",{style:{display:"inline-block","margin-right":"8px","margin-bottom":"12px"}},[s("r-button",{onclick:"message.toast('This is a hint')"},"toast tip")],-1),a(`
                  html
                  <r-button onclick="message.info('This is a hint')">Information prompt</r-button>
                   <r-button onclick="message.warning('This is a hint')">Warning prompt</r-button>
                   <r-button onclick="message.error('This is a hint')">Error prompt</r-button>
                   <r-button onclick="message.success('This is a hint')">Success tip</r-button>
                  diff --git a/assets/src_ranui_modal_index.md.BChGxskG.js b/assets/src_ranui_modal_index.md.CeMIN_J0.js
                  similarity index 84%
                  rename from assets/src_ranui_modal_index.md.BChGxskG.js
                  rename to assets/src_ranui_modal_index.md.CeMIN_J0.js
                  index a95d6b7e96..383117ee43 100644
                  --- a/assets/src_ranui_modal_index.md.BChGxskG.js
                  +++ b/assets/src_ranui_modal_index.md.CeMIN_J0.js
                  @@ -1 +1 @@
                  -import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/modal/index.md","filePath":"src/ranui/modal/index.md","lastUpdated":1728129125000}'),r={name:"src/ranui/modal/index.md"};function n(o,s,d,c,i,m){return a(),t("div")}const _=e(r,[["render",n]]);export{l as __pageData,_ as default};
                  +import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/modal/index.md","filePath":"src/ranui/modal/index.md","lastUpdated":1728129344000}'),r={name:"src/ranui/modal/index.md"};function n(o,s,d,c,i,m){return a(),t("div")}const _=e(r,[["render",n]]);export{l as __pageData,_ as default};
                  diff --git a/assets/src_ranui_modal_index.md.BChGxskG.lean.js b/assets/src_ranui_modal_index.md.CeMIN_J0.lean.js
                  similarity index 84%
                  rename from assets/src_ranui_modal_index.md.BChGxskG.lean.js
                  rename to assets/src_ranui_modal_index.md.CeMIN_J0.lean.js
                  index a95d6b7e96..383117ee43 100644
                  --- a/assets/src_ranui_modal_index.md.BChGxskG.lean.js
                  +++ b/assets/src_ranui_modal_index.md.CeMIN_J0.lean.js
                  @@ -1 +1 @@
                  -import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/modal/index.md","filePath":"src/ranui/modal/index.md","lastUpdated":1728129125000}'),r={name:"src/ranui/modal/index.md"};function n(o,s,d,c,i,m){return a(),t("div")}const _=e(r,[["render",n]]);export{l as __pageData,_ as default};
                  +import{_ as e,o as a,c as t}from"./chunks/framework.eq-HTtE3.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/modal/index.md","filePath":"src/ranui/modal/index.md","lastUpdated":1728129344000}'),r={name:"src/ranui/modal/index.md"};function n(o,s,d,c,i,m){return a(),t("div")}const _=e(r,[["render",n]]);export{l as __pageData,_ as default};
                  diff --git a/assets/src_ranui_player_index.md.DSXQVeNM.js b/assets/src_ranui_player_index.md.DwTQwuCV.js
                  similarity index 99%
                  rename from assets/src_ranui_player_index.md.DSXQVeNM.js
                  rename to assets/src_ranui_player_index.md.DwTQwuCV.js
                  index 6c3ce96a90..f07cfe63ee 100644
                  --- a/assets/src_ranui_player_index.md.DSXQVeNM.js
                  +++ b/assets/src_ranui_player_index.md.DwTQwuCV.js
                  @@ -1 +1 @@
                  -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/player/index.md","filePath":"src/ranui/player/index.md","lastUpdated":1728129125000}'),i={name:"src/ranui/player/index.md"};function n(o,t,l,s,h,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  r-player

                  Video player

                  Based on 'hlsjs' and' web components', let the native tag 'r-player' have a unified video control. Do not use the 'new Player(options)' way to mount to the specified 'dom', view return view, logic return logic, see and get, more intuitive.

                  1. Drag and drop the progress bar
                  2. Volume control
                  3. The bitrate is automatically switched based on the current bandwidth
                  4. Manual definition switch
                  5. Play at double speed
                  6. Style custom overlay
                  7. 'hls' protocol standard encryption video playback
                  8. Based on native development, it can run in all frameworks and unify the cross-framework situation
                  9. Unified browser controls

                  Code demo

                  xml
                    <r-player src="/ran/hls/example.m3u8"></r-player>

                  Attribute

                  src

                  Resource address of the video

                  volume

                  Set the initial volume. The default is 0.5

                  currentTime

                  Set the initial playback time. By default, the playback starts from the beginning

                  playbackRate

                  Set the double speed. The default is 1.0

                  debug

                  console.log some info

                  event

                  onchange

                  Listen for any player changes, and the value returned is as follows.

                  An 'instance of the player' can be obtained through this method.

                  Live by 'type' to judge different event types, perform different operations

                  propertyexplains thatis of type
                  typeIndicates the type of the changed event'string'
                  dataThe value of the'Object'
                  currentTimeThe current playback time'number'
                  durationTotal duration of videos'number'
                  tagAn example of the player'Element'

                  Where 'type' type has

                  NameDescription
                  canplayYour browser is ready to play the media file, but it probably doesn't have enough data to play it to the end without pausing to buffer the content further.
                  canplaythroughThe browser estimates that it canplay media until the end without stopping content buffering.
                  completeOfflineAudioContext The rendering is complete.
                  durationchangeduration is triggered when the value of the duration property changes.
                  emptiedMedia content emptied; For example, when the media is already loaded (or partially loaded), this event is sent and the load() method is called to reload it.
                  endedThe video stops playing because the media has reached the end point.
                  loadedmetadataThe metadata is loaded.
                  progressis triggered periodically when the browser loads the resource.
                  ratechangeThe play rate changes.
                  seekedThe seek operation is complete.
                  seekingseek begins.
                  stalledThe user agent is trying to obtain media data but it has not appeared unexpectedly.
                  suspendMedia data loading has been suspended.
                  loadeddataThe first frame in media has been added. media has loaded.
                  timeupdateThe time specified by the currentTime property changes. currentTime attribute has changed.
                  volumechangeThe volume changes.
                  waitingPlaying has stopped due to lack of data.
                  playPlayback has started.
                  playingAfter a pause or delay due to lack of data, playback is ready to begin.
                  pausePlay is paused.
                  volumeThe volume changes.
                  fullscreenTriggers a full-screen event
                  ',26)]))}const m=e(i,[["render",n]]);export{u as __pageData,m as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/player/index.md","filePath":"src/ranui/player/index.md","lastUpdated":1728129344000}'),i={name:"src/ranui/player/index.md"};function n(o,t,l,s,h,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  r-player

                  Video player

                  Based on 'hlsjs' and' web components', let the native tag 'r-player' have a unified video control. Do not use the 'new Player(options)' way to mount to the specified 'dom', view return view, logic return logic, see and get, more intuitive.

                  1. Drag and drop the progress bar
                  2. Volume control
                  3. The bitrate is automatically switched based on the current bandwidth
                  4. Manual definition switch
                  5. Play at double speed
                  6. Style custom overlay
                  7. 'hls' protocol standard encryption video playback
                  8. Based on native development, it can run in all frameworks and unify the cross-framework situation
                  9. Unified browser controls

                  Code demo

                  xml
                    <r-player src="/ran/hls/example.m3u8"></r-player>

                  Attribute

                  src

                  Resource address of the video

                  volume

                  Set the initial volume. The default is 0.5

                  currentTime

                  Set the initial playback time. By default, the playback starts from the beginning

                  playbackRate

                  Set the double speed. The default is 1.0

                  debug

                  console.log some info

                  event

                  onchange

                  Listen for any player changes, and the value returned is as follows.

                  An 'instance of the player' can be obtained through this method.

                  Live by 'type' to judge different event types, perform different operations

                  propertyexplains thatis of type
                  typeIndicates the type of the changed event'string'
                  dataThe value of the'Object'
                  currentTimeThe current playback time'number'
                  durationTotal duration of videos'number'
                  tagAn example of the player'Element'

                  Where 'type' type has

                  NameDescription
                  canplayYour browser is ready to play the media file, but it probably doesn't have enough data to play it to the end without pausing to buffer the content further.
                  canplaythroughThe browser estimates that it canplay media until the end without stopping content buffering.
                  completeOfflineAudioContext The rendering is complete.
                  durationchangeduration is triggered when the value of the duration property changes.
                  emptiedMedia content emptied; For example, when the media is already loaded (or partially loaded), this event is sent and the load() method is called to reload it.
                  endedThe video stops playing because the media has reached the end point.
                  loadedmetadataThe metadata is loaded.
                  progressis triggered periodically when the browser loads the resource.
                  ratechangeThe play rate changes.
                  seekedThe seek operation is complete.
                  seekingseek begins.
                  stalledThe user agent is trying to obtain media data but it has not appeared unexpectedly.
                  suspendMedia data loading has been suspended.
                  loadeddataThe first frame in media has been added. media has loaded.
                  timeupdateThe time specified by the currentTime property changes. currentTime attribute has changed.
                  volumechangeThe volume changes.
                  waitingPlaying has stopped due to lack of data.
                  playPlayback has started.
                  playingAfter a pause or delay due to lack of data, playback is ready to begin.
                  pausePlay is paused.
                  volumeThe volume changes.
                  fullscreenTriggers a full-screen event
                  ',26)]))}const m=e(i,[["render",n]]);export{u as __pageData,m as default}; diff --git a/assets/src_ranui_player_index.md.DSXQVeNM.lean.js b/assets/src_ranui_player_index.md.DwTQwuCV.lean.js similarity index 99% rename from assets/src_ranui_player_index.md.DSXQVeNM.lean.js rename to assets/src_ranui_player_index.md.DwTQwuCV.lean.js index 6c3ce96a90..f07cfe63ee 100644 --- a/assets/src_ranui_player_index.md.DSXQVeNM.lean.js +++ b/assets/src_ranui_player_index.md.DwTQwuCV.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/player/index.md","filePath":"src/ranui/player/index.md","lastUpdated":1728129125000}'),i={name:"src/ranui/player/index.md"};function n(o,t,l,s,h,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  r-player

                  Video player

                  Based on 'hlsjs' and' web components', let the native tag 'r-player' have a unified video control. Do not use the 'new Player(options)' way to mount to the specified 'dom', view return view, logic return logic, see and get, more intuitive.

                  1. Drag and drop the progress bar
                  2. Volume control
                  3. The bitrate is automatically switched based on the current bandwidth
                  4. Manual definition switch
                  5. Play at double speed
                  6. Style custom overlay
                  7. 'hls' protocol standard encryption video playback
                  8. Based on native development, it can run in all frameworks and unify the cross-framework situation
                  9. Unified browser controls

                  Code demo

                  xml
                    <r-player src="/ran/hls/example.m3u8"></r-player>

                  Attribute

                  src

                  Resource address of the video

                  volume

                  Set the initial volume. The default is 0.5

                  currentTime

                  Set the initial playback time. By default, the playback starts from the beginning

                  playbackRate

                  Set the double speed. The default is 1.0

                  debug

                  console.log some info

                  event

                  onchange

                  Listen for any player changes, and the value returned is as follows.

                  An 'instance of the player' can be obtained through this method.

                  Live by 'type' to judge different event types, perform different operations

                  propertyexplains thatis of type
                  typeIndicates the type of the changed event'string'
                  dataThe value of the'Object'
                  currentTimeThe current playback time'number'
                  durationTotal duration of videos'number'
                  tagAn example of the player'Element'

                  Where 'type' type has

                  NameDescription
                  canplayYour browser is ready to play the media file, but it probably doesn't have enough data to play it to the end without pausing to buffer the content further.
                  canplaythroughThe browser estimates that it canplay media until the end without stopping content buffering.
                  completeOfflineAudioContext The rendering is complete.
                  durationchangeduration is triggered when the value of the duration property changes.
                  emptiedMedia content emptied; For example, when the media is already loaded (or partially loaded), this event is sent and the load() method is called to reload it.
                  endedThe video stops playing because the media has reached the end point.
                  loadedmetadataThe metadata is loaded.
                  progressis triggered periodically when the browser loads the resource.
                  ratechangeThe play rate changes.
                  seekedThe seek operation is complete.
                  seekingseek begins.
                  stalledThe user agent is trying to obtain media data but it has not appeared unexpectedly.
                  suspendMedia data loading has been suspended.
                  loadeddataThe first frame in media has been added. media has loaded.
                  timeupdateThe time specified by the currentTime property changes. currentTime attribute has changed.
                  volumechangeThe volume changes.
                  waitingPlaying has stopped due to lack of data.
                  playPlayback has started.
                  playingAfter a pause or delay due to lack of data, playback is ready to begin.
                  pausePlay is paused.
                  volumeThe volume changes.
                  fullscreenTriggers a full-screen event
                  ',26)]))}const m=e(i,[["render",n]]);export{u as __pageData,m as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"r-player","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/player/index.md","filePath":"src/ranui/player/index.md","lastUpdated":1728129344000}'),i={name:"src/ranui/player/index.md"};function n(o,t,l,s,h,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  r-player

                  Video player

                  Based on 'hlsjs' and' web components', let the native tag 'r-player' have a unified video control. Do not use the 'new Player(options)' way to mount to the specified 'dom', view return view, logic return logic, see and get, more intuitive.

                  1. Drag and drop the progress bar
                  2. Volume control
                  3. The bitrate is automatically switched based on the current bandwidth
                  4. Manual definition switch
                  5. Play at double speed
                  6. Style custom overlay
                  7. 'hls' protocol standard encryption video playback
                  8. Based on native development, it can run in all frameworks and unify the cross-framework situation
                  9. Unified browser controls

                  Code demo

                  xml
                    <r-player src="/ran/hls/example.m3u8"></r-player>

                  Attribute

                  src

                  Resource address of the video

                  volume

                  Set the initial volume. The default is 0.5

                  currentTime

                  Set the initial playback time. By default, the playback starts from the beginning

                  playbackRate

                  Set the double speed. The default is 1.0

                  debug

                  console.log some info

                  event

                  onchange

                  Listen for any player changes, and the value returned is as follows.

                  An 'instance of the player' can be obtained through this method.

                  Live by 'type' to judge different event types, perform different operations

                  propertyexplains thatis of type
                  typeIndicates the type of the changed event'string'
                  dataThe value of the'Object'
                  currentTimeThe current playback time'number'
                  durationTotal duration of videos'number'
                  tagAn example of the player'Element'

                  Where 'type' type has

                  NameDescription
                  canplayYour browser is ready to play the media file, but it probably doesn't have enough data to play it to the end without pausing to buffer the content further.
                  canplaythroughThe browser estimates that it canplay media until the end without stopping content buffering.
                  completeOfflineAudioContext The rendering is complete.
                  durationchangeduration is triggered when the value of the duration property changes.
                  emptiedMedia content emptied; For example, when the media is already loaded (or partially loaded), this event is sent and the load() method is called to reload it.
                  endedThe video stops playing because the media has reached the end point.
                  loadedmetadataThe metadata is loaded.
                  progressis triggered periodically when the browser loads the resource.
                  ratechangeThe play rate changes.
                  seekedThe seek operation is complete.
                  seekingseek begins.
                  stalledThe user agent is trying to obtain media data but it has not appeared unexpectedly.
                  suspendMedia data loading has been suspended.
                  loadeddataThe first frame in media has been added. media has loaded.
                  timeupdateThe time specified by the currentTime property changes. currentTime attribute has changed.
                  volumechangeThe volume changes.
                  waitingPlaying has stopped due to lack of data.
                  playPlayback has started.
                  playingAfter a pause or delay due to lack of data, playback is ready to begin.
                  pausePlay is paused.
                  volumeThe volume changes.
                  fullscreenTriggers a full-screen event
                  ',26)]))}const m=e(i,[["render",n]]);export{u as __pageData,m as default}; diff --git a/assets/src_ranui_popover_index.md.4cp6AOCz.js b/assets/src_ranui_popover_index.md.PFeffqLM.js similarity index 99% rename from assets/src_ranui_popover_index.md.4cp6AOCz.js rename to assets/src_ranui_popover_index.md.PFeffqLM.js index 212ed02533..504a564cf2 100644 --- a/assets/src_ranui_popover_index.md.4cp6AOCz.js +++ b/assets/src_ranui_popover_index.md.PFeffqLM.js @@ -1,4 +1,4 @@ -import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Popover","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/popover/index.md","filePath":"src/ranui/popover/index.md","lastUpdated":1728129125000}'),h={name:"src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Popover

                  Click/mouse to move into the element and pop up a bubbling card layer.

                  Code demo

                  popover
                  this is content
                  xml
                  <r-popover>
                  +import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Popover","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/popover/index.md","filePath":"src/ranui/popover/index.md","lastUpdated":1728129344000}'),h={name:"src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Popover

                  Click/mouse to move into the element and pop up a bubbling card layer.

                  Code demo

                  popover
                  this is content
                  xml
                  <r-popover>
                       <r-button>popover</r-button>
                       <r-content>
                         <div>this is content</div>
                  diff --git a/assets/src_ranui_popover_index.md.4cp6AOCz.lean.js b/assets/src_ranui_popover_index.md.PFeffqLM.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_popover_index.md.4cp6AOCz.lean.js
                  rename to assets/src_ranui_popover_index.md.PFeffqLM.lean.js
                  index 212ed02533..504a564cf2 100644
                  --- a/assets/src_ranui_popover_index.md.4cp6AOCz.lean.js
                  +++ b/assets/src_ranui_popover_index.md.PFeffqLM.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Popover","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/popover/index.md","filePath":"src/ranui/popover/index.md","lastUpdated":1728129125000}'),h={name:"src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Popover

                  Click/mouse to move into the element and pop up a bubbling card layer.

                  Code demo

                  popover
                  this is content
                  xml
                  <r-popover>
                  +import{_ as t,o as n,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"Popover","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/popover/index.md","filePath":"src/ranui/popover/index.md","lastUpdated":1728129344000}'),h={name:"src/ranui/popover/index.md"};function p(k,a,e,E,r,d){return n(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Popover

                  Click/mouse to move into the element and pop up a bubbling card layer.

                  Code demo

                  popover
                  this is content
                  xml
                  <r-popover>
                       <r-button>popover</r-button>
                       <r-content>
                         <div>this is content</div>
                  diff --git a/assets/src_ranui_preview_index.md.GrIEkeLk.js b/assets/src_ranui_preview_index.md.hkKqqHBY.js
                  similarity index 99%
                  rename from assets/src_ranui_preview_index.md.GrIEkeLk.js
                  rename to assets/src_ranui_preview_index.md.hkKqqHBY.js
                  index 054033fb5e..ed7a9353b2 100644
                  --- a/assets/src_ranui_preview_index.md.GrIEkeLk.js
                  +++ b/assets/src_ranui_preview_index.md.hkKqqHBY.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t,o as h,c as n,j as s,a,a3 as l}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"preview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/preview/index.md","filePath":"src/ranui/preview/index.md","lastUpdated":1728129125000}'),e={name:"src/ranui/preview/index.md"};function p(k,i,E,r,d,g){return h(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"preview",tabindex:"-1"},[a("preview "),s("a",{class:"header-anchor",href:"#preview","aria-label":'Permalink to "preview"'},"​")],-1),s("p",null,"Support 'docx', 'pptx', 'pdf', 'xlsx' file preview",-1),s("h2",{id:"code-demo",tabindex:"-1"},[a("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fdsafdsafdsafdsaf"}),s("r-button",{type:"primary",onclick:"uploadFile('fdsafdsafdsafdsaf')"},"choose file to preview")],-1),l(`
                  html
                  <r-preview id="preview"></r-preview>
                  +import{_ as t,o as h,c as n,j as s,a,a3 as l}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"preview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/preview/index.md","filePath":"src/ranui/preview/index.md","lastUpdated":1728129344000}'),e={name:"src/ranui/preview/index.md"};function p(k,i,E,r,d,g){return h(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"preview",tabindex:"-1"},[a("preview "),s("a",{class:"header-anchor",href:"#preview","aria-label":'Permalink to "preview"'},"​")],-1),s("p",null,"Support 'docx', 'pptx', 'pdf', 'xlsx' file preview",-1),s("h2",{id:"code-demo",tabindex:"-1"},[a("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fdsafdsafdsafdsaf"}),s("r-button",{type:"primary",onclick:"uploadFile('fdsafdsafdsafdsaf')"},"choose file to preview")],-1),l(`
                  html
                  <r-preview id="preview"></r-preview>
                   <r-button type="primary" onclick="uploadFile()">choose file to preview</r-button>
                   
                   <script>
                  diff --git a/assets/src_ranui_preview_index.md.GrIEkeLk.lean.js b/assets/src_ranui_preview_index.md.hkKqqHBY.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_preview_index.md.GrIEkeLk.lean.js
                  rename to assets/src_ranui_preview_index.md.hkKqqHBY.lean.js
                  index 054033fb5e..ed7a9353b2 100644
                  --- a/assets/src_ranui_preview_index.md.GrIEkeLk.lean.js
                  +++ b/assets/src_ranui_preview_index.md.hkKqqHBY.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t,o as h,c as n,j as s,a,a3 as l}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"preview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/preview/index.md","filePath":"src/ranui/preview/index.md","lastUpdated":1728129125000}'),e={name:"src/ranui/preview/index.md"};function p(k,i,E,r,d,g){return h(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"preview",tabindex:"-1"},[a("preview "),s("a",{class:"header-anchor",href:"#preview","aria-label":'Permalink to "preview"'},"​")],-1),s("p",null,"Support 'docx', 'pptx', 'pdf', 'xlsx' file preview",-1),s("h2",{id:"code-demo",tabindex:"-1"},[a("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fdsafdsafdsafdsaf"}),s("r-button",{type:"primary",onclick:"uploadFile('fdsafdsafdsafdsaf')"},"choose file to preview")],-1),l(`
                  html
                  <r-preview id="preview"></r-preview>
                  +import{_ as t,o as h,c as n,j as s,a,a3 as l}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"preview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/preview/index.md","filePath":"src/ranui/preview/index.md","lastUpdated":1728129344000}'),e={name:"src/ranui/preview/index.md"};function p(k,i,E,r,d,g){return h(),n("div",{"data-pagefind-body":!0},i[0]||(i[0]=[s("h1",{id:"preview",tabindex:"-1"},[a("preview "),s("a",{class:"header-anchor",href:"#preview","aria-label":'Permalink to "preview"'},"​")],-1),s("p",null,"Support 'docx', 'pptx', 'pdf', 'xlsx' file preview",-1),s("h2",{id:"code-demo",tabindex:"-1"},[a("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("div",{style:{width:"100px","margin-top":"10px"}},[s("r-preview",{id:"fdsafdsafdsafdsaf"}),s("r-button",{type:"primary",onclick:"uploadFile('fdsafdsafdsafdsaf')"},"choose file to preview")],-1),l(`
                  html
                  <r-preview id="preview"></r-preview>
                   <r-button type="primary" onclick="uploadFile()">choose file to preview</r-button>
                   
                   <script>
                  diff --git a/assets/src_ranui_progress_index.md.Be_sOmHB.js b/assets/src_ranui_progress_index.md.CubSOb3E.js
                  similarity index 99%
                  rename from assets/src_ranui_progress_index.md.Be_sOmHB.js
                  rename to assets/src_ranui_progress_index.md.CubSOb3E.js
                  index 3a1844971d..3f2e822253 100644
                  --- a/assets/src_ranui_progress_index.md.Be_sOmHB.js
                  +++ b/assets/src_ranui_progress_index.md.CubSOb3E.js
                  @@ -1 +1 @@
                  -import{_ as a,o as e,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"progress","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/progress/index.md","filePath":"src/ranui/progress/index.md","lastUpdated":1728129125000}'),p={name:"src/ranui/progress/index.md"};function l(r,t,n,k,d,E){return e(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i('

                  progress

                  Interactive progress bar

                  Code demo

                  xml
                  <r-progress type="drag" ></r-progress>

                  Attribute

                  total

                  Set progress bar Total progress, allowed percentages and numbers.

                  ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
                  html
                  <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

                  percent

                  Set the current progress of the progress bar, you can set the percentage and number, 'percent' cannot exceed 'total'. If 'total' is not set, the default 'total' is' 100% ', that is, '1'.

                  ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
                  html
                  <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

                  dot

                  Point of the progress bar, Default display, set to 'false' can be hidden

                  ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

                  type

                  • primary: Default progress bar, not setting the 'type' attribute is the default
                  • drag: Draggable, clickable progress bar (dragging requires' dot 'to be' true ')
                  ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

                  Method

                  change

                  The 'change' event is triggered when the 'percent' and 'total' properties change.

                  propertyexplains thattype
                  valueCurrent progress'string or number'
                  percentCurrent progress'string or number'
                  totalTotal progress'string or number'
                  ',5)]))}const y=a(p,[["render",l]]);export{o as __pageData,y as default}; +import{_ as a,o as e,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"progress","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/progress/index.md","filePath":"src/ranui/progress/index.md","lastUpdated":1728129344000}'),p={name:"src/ranui/progress/index.md"};function l(r,t,n,k,d,E){return e(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i('

                  progress

                  Interactive progress bar

                  Code demo

                  xml
                  <r-progress type="drag" ></r-progress>

                  Attribute

                  total

                  Set progress bar Total progress, allowed percentages and numbers.

                  ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
                  html
                  <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

                  percent

                  Set the current progress of the progress bar, you can set the percentage and number, 'percent' cannot exceed 'total'. If 'total' is not set, the default 'total' is' 100% ', that is, '1'.

                  ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
                  html
                  <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

                  dot

                  Point of the progress bar, Default display, set to 'false' can be hidden

                  ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

                  type

                  • primary: Default progress bar, not setting the 'type' attribute is the default
                  • drag: Draggable, clickable progress bar (dragging requires' dot 'to be' true ')
                  ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

                  Method

                  change

                  The 'change' event is triggered when the 'percent' and 'total' properties change.

                  propertyexplains thattype
                  valueCurrent progress'string or number'
                  percentCurrent progress'string or number'
                  totalTotal progress'string or number'
                  ',5)]))}const y=a(p,[["render",l]]);export{o as __pageData,y as default}; diff --git a/assets/src_ranui_progress_index.md.Be_sOmHB.lean.js b/assets/src_ranui_progress_index.md.CubSOb3E.lean.js similarity index 99% rename from assets/src_ranui_progress_index.md.Be_sOmHB.lean.js rename to assets/src_ranui_progress_index.md.CubSOb3E.lean.js index 3a1844971d..3f2e822253 100644 --- a/assets/src_ranui_progress_index.md.Be_sOmHB.lean.js +++ b/assets/src_ranui_progress_index.md.CubSOb3E.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"progress","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/progress/index.md","filePath":"src/ranui/progress/index.md","lastUpdated":1728129125000}'),p={name:"src/ranui/progress/index.md"};function l(r,t,n,k,d,E){return e(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i('

                  progress

                  Interactive progress bar

                  Code demo

                  xml
                  <r-progress type="drag" ></r-progress>

                  Attribute

                  total

                  Set progress bar Total progress, allowed percentages and numbers.

                  ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
                  html
                  <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

                  percent

                  Set the current progress of the progress bar, you can set the percentage and number, 'percent' cannot exceed 'total'. If 'total' is not set, the default 'total' is' 100% ', that is, '1'.

                  ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
                  html
                  <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

                  dot

                  Point of the progress bar, Default display, set to 'false' can be hidden

                  ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

                  type

                  • primary: Default progress bar, not setting the 'type' attribute is the default
                  • drag: Draggable, clickable progress bar (dragging requires' dot 'to be' true ')
                  ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

                  Method

                  change

                  The 'change' event is triggered when the 'percent' and 'total' properties change.

                  propertyexplains thattype
                  valueCurrent progress'string or number'
                  percentCurrent progress'string or number'
                  totalTotal progress'string or number'
                  ',5)]))}const y=a(p,[["render",l]]);export{o as __pageData,y as default}; +import{_ as a,o as e,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const o=JSON.parse('{"title":"progress","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/progress/index.md","filePath":"src/ranui/progress/index.md","lastUpdated":1728129344000}'),p={name:"src/ranui/progress/index.md"};function l(r,t,n,k,d,E){return e(),h("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i('

                  progress

                  Interactive progress bar

                  Code demo

                  xml
                  <r-progress type="drag" ></r-progress>

                  Attribute

                  total

                  Set progress bar Total progress, allowed percentages and numbers.

                  ',8),s("r-progress",{percent:"30",total:"1000"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"70",total:"100"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{percent:"10%",total:"100%"},null,-1),i('
                  html
                  <r-progress percent="30" total="1000"></r-progress>\n<r-progress percent="70" total="100"></r-progress>\n<r-progress percent="10%" total="100%"></r-progress>

                  percent

                  Set the current progress of the progress bar, you can set the percentage and number, 'percent' cannot exceed 'total'. If 'total' is not set, the default 'total' is' 100% ', that is, '1'.

                  ',3),s("r-progress",{type:"primary",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"70%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"100%"},null,-1),i('
                  html
                  <r-progress type="primary" percent="30%"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>\n<r-progress type="primary" percent="100%"></r-progress>

                  dot

                  Point of the progress bar, Default display, set to 'false' can be hidden

                  ',3),s("r-progress",{type:"drag",percent:"30%",dot:"false"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%",dot:"true"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%" dot="false"></r-progress>\n<r-progress type="primary" percent="40%" dot="true"></r-progress>\n<r-progress type="primary" percent="40%"></r-progress>

                  type

                  • primary: Default progress bar, not setting the 'type' attribute is the default
                  • drag: Draggable, clickable progress bar (dragging requires' dot 'to be' true ')
                  ',3),s("r-progress",{type:"drag",percent:"30%"},null,-1),s("div",{style:{height:"20px",width:"10px"}},null,-1),s("r-progress",{type:"primary",percent:"40%"},null,-1),i('
                  html
                  <r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

                  Method

                  change

                  The 'change' event is triggered when the 'percent' and 'total' properties change.

                  propertyexplains thattype
                  valueCurrent progress'string or number'
                  percentCurrent progress'string or number'
                  totalTotal progress'string or number'
                  ',5)]))}const y=a(p,[["render",l]]);export{o as __pageData,y as default}; diff --git a/assets/src_ranui_radar_index.md.CkBeOv58.js b/assets/src_ranui_radar_index.md.DVubbf7p.js similarity index 99% rename from assets/src_ranui_radar_index.md.CkBeOv58.js rename to assets/src_ranui_radar_index.md.DVubbf7p.js index ea87bd9f1f..4f4594f2d6 100644 --- a/assets/src_ranui_radar_index.md.CkBeOv58.js +++ b/assets/src_ranui_radar_index.md.DVubbf7p.js @@ -1,4 +1,4 @@ -import{_ as e,o as l,c as o,j as t,a as s,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/radar/index.md","filePath":"src/ranui/radar/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/radar/index.md"};function h(p,i,r,k,u,d){return l(),o("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t("h1",{id:"radar",tabindex:"-1"},[s("Radar "),t("a",{class:"header-anchor",href:"#radar","aria-label":'Permalink to "Radar"'},"​")],-1),t("p",null,"Comprehensive comparison of differences between multiple sets of data in two-dimensional form, often used to compare 2 or more sets of data",-1),t("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),t("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),t("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"HP","scoreRate":"10"},{"abilityName":"Attack","scoreRate":"90"},{"abilityName":"DEF","scoreRate":"20"},{"abilityName":"Element mastery","scoreRate":"50"},{"abilityName":"Critical Hit Chance","scoreRate":"80"},{"abilityName":"Critical hit damage","scoreRate":"50"}]'},null,-1),a(`
                  xml
                  <r-radar
                  +import{_ as e,o as l,c as o,j as t,a as s,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/radar/index.md","filePath":"src/ranui/radar/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/radar/index.md"};function h(p,i,r,k,u,d){return l(),o("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t("h1",{id:"radar",tabindex:"-1"},[s("Radar "),t("a",{class:"header-anchor",href:"#radar","aria-label":'Permalink to "Radar"'},"​")],-1),t("p",null,"Comprehensive comparison of differences between multiple sets of data in two-dimensional form, often used to compare 2 or more sets of data",-1),t("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),t("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),t("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"HP","scoreRate":"10"},{"abilityName":"Attack","scoreRate":"90"},{"abilityName":"DEF","scoreRate":"20"},{"abilityName":"Element mastery","scoreRate":"50"},{"abilityName":"Critical Hit Chance","scoreRate":"80"},{"abilityName":"Critical hit damage","scoreRate":"50"}]'},null,-1),a(`
                  xml
                  <r-radar
                   abilitys='[{"abilityName":"HP","scoreRate":"10"},{"abilityName":"Attack","scoreRate":"90"},{"abilityName":"DEF","scoreRate":"20"},{"abilityName":"Element mastery","scoreRate":"50"},{"abilityName":"Critical Hit Chance","scoreRate":"80"},{"abilityName":"Critical hit damage","scoreRate":"50"}]'
                       style="width:300px;height:300px;display: block;"
                   >
                  diff --git a/assets/src_ranui_radar_index.md.CkBeOv58.lean.js b/assets/src_ranui_radar_index.md.DVubbf7p.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_radar_index.md.CkBeOv58.lean.js
                  rename to assets/src_ranui_radar_index.md.DVubbf7p.lean.js
                  index ea87bd9f1f..4f4594f2d6 100644
                  --- a/assets/src_ranui_radar_index.md.CkBeOv58.lean.js
                  +++ b/assets/src_ranui_radar_index.md.DVubbf7p.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as e,o as l,c as o,j as t,a as s,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/radar/index.md","filePath":"src/ranui/radar/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/radar/index.md"};function h(p,i,r,k,u,d){return l(),o("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t("h1",{id:"radar",tabindex:"-1"},[s("Radar "),t("a",{class:"header-anchor",href:"#radar","aria-label":'Permalink to "Radar"'},"​")],-1),t("p",null,"Comprehensive comparison of differences between multiple sets of data in two-dimensional form, often used to compare 2 or more sets of data",-1),t("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),t("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),t("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"HP","scoreRate":"10"},{"abilityName":"Attack","scoreRate":"90"},{"abilityName":"DEF","scoreRate":"20"},{"abilityName":"Element mastery","scoreRate":"50"},{"abilityName":"Critical Hit Chance","scoreRate":"80"},{"abilityName":"Critical hit damage","scoreRate":"50"}]'},null,-1),a(`
                  xml
                  <r-radar
                  +import{_ as e,o as l,c as o,j as t,a as s,a3 as a}from"./chunks/framework.eq-HTtE3.js";const E=JSON.parse('{"title":"Radar","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/radar/index.md","filePath":"src/ranui/radar/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/radar/index.md"};function h(p,i,r,k,u,d){return l(),o("div",{"data-pagefind-body":!0},i[0]||(i[0]=[t("h1",{id:"radar",tabindex:"-1"},[s("Radar "),t("a",{class:"header-anchor",href:"#radar","aria-label":'Permalink to "Radar"'},"​")],-1),t("p",null,"Comprehensive comparison of differences between multiple sets of data in two-dimensional form, often used to compare 2 or more sets of data",-1),t("h2",{id:"code-demo",tabindex:"-1"},[s("Code demo "),t("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),t("r-radar",{style:{width:"300px",height:"300px",display:"block"},abilitys:'[{"abilityName":"HP","scoreRate":"10"},{"abilityName":"Attack","scoreRate":"90"},{"abilityName":"DEF","scoreRate":"20"},{"abilityName":"Element mastery","scoreRate":"50"},{"abilityName":"Critical Hit Chance","scoreRate":"80"},{"abilityName":"Critical hit damage","scoreRate":"50"}]'},null,-1),a(`
                  xml
                  <r-radar
                   abilitys='[{"abilityName":"HP","scoreRate":"10"},{"abilityName":"Attack","scoreRate":"90"},{"abilityName":"DEF","scoreRate":"20"},{"abilityName":"Element mastery","scoreRate":"50"},{"abilityName":"Critical Hit Chance","scoreRate":"80"},{"abilityName":"Critical hit damage","scoreRate":"50"}]'
                       style="width:300px;height:300px;display: block;"
                   >
                  diff --git a/assets/src_ranui_select_index.md.DgN8GI_q.js b/assets/src_ranui_select_index.md.DQq3MVHp.js
                  similarity index 99%
                  rename from assets/src_ranui_select_index.md.DgN8GI_q.js
                  rename to assets/src_ranui_select_index.md.DQq3MVHp.js
                  index 9e87f036a4..1238ad8c2e 100644
                  --- a/assets/src_ranui_select_index.md.DgN8GI_q.js
                  +++ b/assets/src_ranui_select_index.md.DQq3MVHp.js
                  @@ -1,4 +1,4 @@
                  -import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/select/index.md","filePath":"src/ranui/select/index.md","lastUpdated":1728129125000}'),p={name:"src/ranui/select/index.md"};function k(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select",tabindex:"-1"},[t("Select "),s("a",{class:"header-anchor",href:"#select","aria-label":'Permalink to "Select"'},"​")],-1),s("p",null,"A regular pull-down selector.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
                  xml
                  <r-select style="width: 120px; height: 40px" defaultValue="185">
                  +import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/select/index.md","filePath":"src/ranui/select/index.md","lastUpdated":1728129344000}'),p={name:"src/ranui/select/index.md"};function k(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select",tabindex:"-1"},[t("Select "),s("a",{class:"header-anchor",href:"#select","aria-label":'Permalink to "Select"'},"​")],-1),s("p",null,"A regular pull-down selector.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
                  xml
                  <r-select style="width: 120px; height: 40px" defaultValue="185">
                         <r-option value="185">Mike</r-option>
                         <r-option value="186">Tom</r-option>
                         <r-option value="187">Lucy</r-option>
                  diff --git a/assets/src_ranui_select_index.md.DgN8GI_q.lean.js b/assets/src_ranui_select_index.md.DQq3MVHp.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_select_index.md.DgN8GI_q.lean.js
                  rename to assets/src_ranui_select_index.md.DQq3MVHp.lean.js
                  index 9e87f036a4..1238ad8c2e 100644
                  --- a/assets/src_ranui_select_index.md.DgN8GI_q.lean.js
                  +++ b/assets/src_ranui_select_index.md.DQq3MVHp.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/select/index.md","filePath":"src/ranui/select/index.md","lastUpdated":1728129125000}'),p={name:"src/ranui/select/index.md"};function k(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select",tabindex:"-1"},[t("Select "),s("a",{class:"header-anchor",href:"#select","aria-label":'Permalink to "Select"'},"​")],-1),s("p",null,"A regular pull-down selector.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
                  xml
                  <r-select style="width: 120px; height: 40px" defaultValue="185">
                  +import{_ as h,o as l,c as n,j as s,a as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Select","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/select/index.md","filePath":"src/ranui/select/index.md","lastUpdated":1728129344000}'),p={name:"src/ranui/select/index.md"};function k(e,a,E,r,d,o){return l(),n("div",{"data-pagefind-body":!0},a[0]||(a[0]=[s("h1",{id:"select",tabindex:"-1"},[t("Select "),s("a",{class:"header-anchor",href:"#select","aria-label":'Permalink to "Select"'},"​")],-1),s("p",null,"A regular pull-down selector.",-1),s("h2",{id:"code-demo",tabindex:"-1"},[t("Code demo "),s("a",{class:"header-anchor",href:"#code-demo","aria-label":'Permalink to "Code demo"'},"​")],-1),s("r-select",{style:{width:"120px",height:"40px"},defaultValue:"185"},[s("r-option",{value:"185"},"Mike"),s("r-option",{value:"186"},"Tom"),s("r-option",{value:"187"},"Lucy")],-1),i(`
                  xml
                  <r-select style="width: 120px; height: 40px" defaultValue="185">
                         <r-option value="185">Mike</r-option>
                         <r-option value="186">Tom</r-option>
                         <r-option value="187">Lucy</r-option>
                  diff --git a/assets/src_ranui_skeleton_index.md.DnZOMEV1.js b/assets/src_ranui_skeleton_index.md.BUf_Burp.js
                  similarity index 96%
                  rename from assets/src_ranui_skeleton_index.md.DnZOMEV1.js
                  rename to assets/src_ranui_skeleton_index.md.BUf_Burp.js
                  index 43e717af91..edea8e6867 100644
                  --- a/assets/src_ranui_skeleton_index.md.DnZOMEV1.js
                  +++ b/assets/src_ranui_skeleton_index.md.BUf_Burp.js
                  @@ -1 +1 @@
                  -import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/skeleton/index.md","filePath":"src/ranui/skeleton/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/skeleton/index.md"};function o(l,e,r,d,p,h){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  skeleton

                  Provides a combination of placeholder graphics where you need to wait for content to load.

                  Code demo

                  The skeleton length follows the length of the parent element

                  xml
                  <r-skeleton ></r-skeleton>
                  ',9)]))}const m=t(n,[["render",o]]);export{c as __pageData,m as default}; +import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/skeleton/index.md","filePath":"src/ranui/skeleton/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/skeleton/index.md"};function o(l,e,r,d,p,h){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  skeleton

                  Provides a combination of placeholder graphics where you need to wait for content to load.

                  Code demo

                  The skeleton length follows the length of the parent element

                  xml
                  <r-skeleton ></r-skeleton>
                  ',9)]))}const m=t(n,[["render",o]]);export{c as __pageData,m as default}; diff --git a/assets/src_ranui_skeleton_index.md.DnZOMEV1.lean.js b/assets/src_ranui_skeleton_index.md.BUf_Burp.lean.js similarity index 96% rename from assets/src_ranui_skeleton_index.md.DnZOMEV1.lean.js rename to assets/src_ranui_skeleton_index.md.BUf_Burp.lean.js index 43e717af91..edea8e6867 100644 --- a/assets/src_ranui_skeleton_index.md.DnZOMEV1.lean.js +++ b/assets/src_ranui_skeleton_index.md.BUf_Burp.lean.js @@ -1 +1 @@ -import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/skeleton/index.md","filePath":"src/ranui/skeleton/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/skeleton/index.md"};function o(l,e,r,d,p,h){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  skeleton

                  Provides a combination of placeholder graphics where you need to wait for content to load.

                  Code demo

                  The skeleton length follows the length of the parent element

                  xml
                  <r-skeleton ></r-skeleton>
                  ',9)]))}const m=t(n,[["render",o]]);export{c as __pageData,m as default}; +import{_ as t,o as s,c as a,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"skeleton","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/skeleton/index.md","filePath":"src/ranui/skeleton/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/skeleton/index.md"};function o(l,e,r,d,p,h){return s(),a("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  skeleton

                  Provides a combination of placeholder graphics where you need to wait for content to load.

                  Code demo

                  The skeleton length follows the length of the parent element

                  xml
                  <r-skeleton ></r-skeleton>
                  ',9)]))}const m=t(n,[["render",o]]);export{c as __pageData,m as default}; diff --git a/assets/src_ranui_tab_index.md.CciVbuSi.js b/assets/src_ranui_tab_index.md.DSSJuz31.js similarity index 99% rename from assets/src_ranui_tab_index.md.CciVbuSi.js rename to assets/src_ranui_tab_index.md.DSSJuz31.js index 4c74e9f281..627df1ad77 100644 --- a/assets/src_ranui_tab_index.md.CciVbuSi.js +++ b/assets/src_ranui_tab_index.md.DSSJuz31.js @@ -1,4 +1,4 @@ -import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tab/index.md","filePath":"src/ranui/tab/index.md","lastUpdated":1728129125000}'),k={name:"src/ranui/tab/index.md"};function n(p,a,e,E,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  TAB pages, where 'r-tab' needs to be used with 'r-tabs'

                  Code demo

                  111112222233333
                  xml
                  <r-tabs >
                  +import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tab/index.md","filePath":"src/ranui/tab/index.md","lastUpdated":1728129344000}'),k={name:"src/ranui/tab/index.md"};function n(p,a,e,E,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  TAB pages, where 'r-tab' needs to be used with 'r-tabs'

                  Code demo

                  111112222233333
                  xml
                  <r-tabs >
                         <r-tab label="tab1">11111</r-tab>
                         <r-tab label="tab2">22222</r-tab>
                         <r-tab label="tab3">33333</r-tab>
                  diff --git a/assets/src_ranui_tab_index.md.CciVbuSi.lean.js b/assets/src_ranui_tab_index.md.DSSJuz31.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_tab_index.md.CciVbuSi.lean.js
                  rename to assets/src_ranui_tab_index.md.DSSJuz31.lean.js
                  index 4c74e9f281..627df1ad77 100644
                  --- a/assets/src_ranui_tab_index.md.CciVbuSi.lean.js
                  +++ b/assets/src_ranui_tab_index.md.DSSJuz31.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tab/index.md","filePath":"src/ranui/tab/index.md","lastUpdated":1728129125000}'),k={name:"src/ranui/tab/index.md"};function n(p,a,e,E,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  TAB pages, where 'r-tab' needs to be used with 'r-tabs'

                  Code demo

                  111112222233333
                  xml
                  <r-tabs >
                  +import{_ as t,o as l,c as h,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const y=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tab/index.md","filePath":"src/ranui/tab/index.md","lastUpdated":1728129344000}'),k={name:"src/ranui/tab/index.md"};function n(p,a,e,E,r,d){return l(),h("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  TAB pages, where 'r-tab' needs to be used with 'r-tabs'

                  Code demo

                  111112222233333
                  xml
                  <r-tabs >
                         <r-tab label="tab1">11111</r-tab>
                         <r-tab label="tab2">22222</r-tab>
                         <r-tab label="tab3">33333</r-tab>
                  diff --git a/assets/src_ranui_tabs_index.md.CkEcQ38o.js b/assets/src_ranui_tabs_index.md.Y_dSHlvB.js
                  similarity index 99%
                  rename from assets/src_ranui_tabs_index.md.CkEcQ38o.js
                  rename to assets/src_ranui_tabs_index.md.Y_dSHlvB.js
                  index f1764d5d82..e28574b09a 100644
                  --- a/assets/src_ranui_tabs_index.md.CkEcQ38o.js
                  +++ b/assets/src_ranui_tabs_index.md.Y_dSHlvB.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tabs/index.md","filePath":"src/ranui/tabs/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  Code demo

                  tab1tab2tab3
                  xml
                  <r-tabs>
                  +import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tabs/index.md","filePath":"src/ranui/tabs/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  Code demo

                  tab1tab2tab3
                  xml
                  <r-tabs>
                       <r-tab label="tab1">tab1</r-tab>
                       <r-tab label="tab2">tab2</r-tab>
                       <r-tab label="tab3">tab3</r-tab>
                  diff --git a/assets/src_ranui_tabs_index.md.CkEcQ38o.lean.js b/assets/src_ranui_tabs_index.md.Y_dSHlvB.lean.js
                  similarity index 99%
                  rename from assets/src_ranui_tabs_index.md.CkEcQ38o.lean.js
                  rename to assets/src_ranui_tabs_index.md.Y_dSHlvB.lean.js
                  index f1764d5d82..e28574b09a 100644
                  --- a/assets/src_ranui_tabs_index.md.CkEcQ38o.lean.js
                  +++ b/assets/src_ranui_tabs_index.md.Y_dSHlvB.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tabs/index.md","filePath":"src/ranui/tabs/index.md","lastUpdated":1728129125000}'),n={name:"src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  Code demo

                  tab1tab2tab3
                  xml
                  <r-tabs>
                  +import{_ as t,o as h,c as l,a3 as i,j as s}from"./chunks/framework.eq-HTtE3.js";const b=JSON.parse('{"title":"Tab","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranui/tabs/index.md","filePath":"src/ranui/tabs/index.md","lastUpdated":1728129344000}'),n={name:"src/ranui/tabs/index.md"};function k(e,a,p,E,r,d){return h(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i(`

                  Tab

                  Code demo

                  tab1tab2tab3
                  xml
                  <r-tabs>
                       <r-tab label="tab1">tab1</r-tab>
                       <r-tab label="tab2">tab2</r-tab>
                       <r-tab label="tab3">tab3</r-tab>
                  diff --git a/assets/src_ranuts_binaryTree_index.md.DvglfkNJ.js b/assets/src_ranuts_binaryTree_index.md.TEku7NN9.js
                  similarity index 99%
                  rename from assets/src_ranuts_binaryTree_index.md.DvglfkNJ.js
                  rename to assets/src_ranuts_binaryTree_index.md.TEku7NN9.js
                  index d85cc0762a..46c3a79ccf 100644
                  --- a/assets/src_ranuts_binaryTree_index.md.DvglfkNJ.js
                  +++ b/assets/src_ranuts_binaryTree_index.md.TEku7NN9.js
                  @@ -1 +1 @@
                  -import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/binaryTree/index.md","filePath":"src/ranuts/binaryTree/index.md","lastUpdated":1728129125000}'),l={name:"src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

                  二叉树的定义

                  在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

                  二叉树的性质

                  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
                  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
                  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
                  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
                  • 二叉树的总结点数 n = n1 + n2 + n0
                  • 总连线数等于总节点数减一(B = n - 1)
                  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

                  二叉树的类型

                  满二叉树

                  一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

                  完全二叉树

                  一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

                  二叉搜索树

                  二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

                  平衡二叉树

                  平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

                  B 树

                  B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

                  B+树

                  B+树是 B 树的变体,也是一种多路搜索树。

                  B*树

                  B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

                  红黑树

                  红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

                  遍历

                  前序遍历

                  后序遍历

                  中序遍历

                  层序遍历

                  常见算法题

                  镜像二叉树

                  重建二叉树

                  二叉树深度

                  二叉树节点总数

                  判断二叉树子结构

                  输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

                  参考文档

                  1. 维基百科二叉树
                  2. 百度百科满二叉树
                  ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; +import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/binaryTree/index.md","filePath":"src/ranuts/binaryTree/index.md","lastUpdated":1728129344000}'),l={name:"src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

                  二叉树的定义

                  在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

                  二叉树的性质

                  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
                  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
                  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
                  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
                  • 二叉树的总结点数 n = n1 + n2 + n0
                  • 总连线数等于总节点数减一(B = n - 1)
                  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

                  二叉树的类型

                  满二叉树

                  一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

                  完全二叉树

                  一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

                  二叉搜索树

                  二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

                  平衡二叉树

                  平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

                  B 树

                  B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

                  B+树

                  B+树是 B 树的变体,也是一种多路搜索树。

                  B*树

                  B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

                  红黑树

                  红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

                  遍历

                  前序遍历

                  后序遍历

                  中序遍历

                  层序遍历

                  常见算法题

                  镜像二叉树

                  重建二叉树

                  二叉树深度

                  二叉树节点总数

                  判断二叉树子结构

                  输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

                  参考文档

                  1. 维基百科二叉树
                  2. 百度百科满二叉树
                  ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; diff --git a/assets/src_ranuts_binaryTree_index.md.DvglfkNJ.lean.js b/assets/src_ranuts_binaryTree_index.md.TEku7NN9.lean.js similarity index 99% rename from assets/src_ranuts_binaryTree_index.md.DvglfkNJ.lean.js rename to assets/src_ranuts_binaryTree_index.md.TEku7NN9.lean.js index d85cc0762a..46c3a79ccf 100644 --- a/assets/src_ranuts_binaryTree_index.md.DvglfkNJ.lean.js +++ b/assets/src_ranuts_binaryTree_index.md.TEku7NN9.lean.js @@ -1 +1 @@ -import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/binaryTree/index.md","filePath":"src/ranuts/binaryTree/index.md","lastUpdated":1728129125000}'),l={name:"src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

                  二叉树的定义

                  在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

                  二叉树的性质

                  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
                  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
                  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
                  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
                  • 二叉树的总结点数 n = n1 + n2 + n0
                  • 总连线数等于总节点数减一(B = n - 1)
                  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

                  二叉树的类型

                  满二叉树

                  一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

                  完全二叉树

                  一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

                  二叉搜索树

                  二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

                  平衡二叉树

                  平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

                  B 树

                  B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

                  B+树

                  B+树是 B 树的变体,也是一种多路搜索树。

                  B*树

                  B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

                  红黑树

                  红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

                  遍历

                  前序遍历

                  后序遍历

                  中序遍历

                  层序遍历

                  常见算法题

                  镜像二叉树

                  重建二叉树

                  二叉树深度

                  二叉树节点总数

                  判断二叉树子结构

                  输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

                  参考文档

                  1. 维基百科二叉树
                  2. 百度百科满二叉树
                  ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; +import{_ as e}from"./chunks/balanceTree.CCEoBiag.js";import{_ as r,o as i,c as t,a3 as h}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"二叉树的定义","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/binaryTree/index.md","filePath":"src/ranuts/binaryTree/index.md","lastUpdated":1728129344000}'),l={name:"src/ranuts/binaryTree/index.md"};function o(n,a,d,s,c,u){return i(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[h('

                  二叉树的定义

                  在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

                  二叉树的性质

                  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
                  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
                  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
                  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
                  • 二叉树的总结点数 n = n1 + n2 + n0
                  • 总连线数等于总节点数减一(B = n - 1)
                  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

                  二叉树的类型

                  满二叉树

                  一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

                  完全二叉树

                  一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

                  二叉搜索树

                  二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

                  平衡二叉树

                  平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

                  B 树

                  B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

                  B+树

                  B+树是 B 树的变体,也是一种多路搜索树。

                  B*树

                  B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

                  红黑树

                  红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

                  遍历

                  前序遍历

                  后序遍历

                  中序遍历

                  层序遍历

                  常见算法题

                  镜像二叉树

                  重建二叉树

                  二叉树深度

                  二叉树节点总数

                  判断二叉树子结构

                  输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

                  参考文档

                  1. 维基百科二叉树
                  2. 百度百科满二叉树
                  ',36)]))}const m=r(l,[["render",o]]);export{p as __pageData,m as default}; diff --git a/assets/src_ranuts_bundler_index.md.D3F9791C.js b/assets/src_ranuts_bundler_index.md.DzEPrDNS.js similarity index 96% rename from assets/src_ranuts_bundler_index.md.D3F9791C.js rename to assets/src_ranuts_bundler_index.md.DzEPrDNS.js index f74f3cd72f..7a53fed8e3 100644 --- a/assets/src_ranuts_bundler_index.md.D3F9791C.js +++ b/assets/src_ranuts_bundler_index.md.DzEPrDNS.js @@ -1,4 +1,4 @@ -import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/bundler/index.md","filePath":"src/ranuts/bundler/index.md","lastUpdated":1728129125000}'),l={name:"src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

                  Bundler

                  Bundler的使用: 传入 options 参数

                  function build(options: Options):Promise<Build> {
                  +import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/bundler/index.md","filePath":"src/ranuts/bundler/index.md","lastUpdated":1728129344000}'),l={name:"src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

                  Bundler

                  Bundler的使用: 传入 options 参数

                  function build(options: Options):Promise<Build> {
                     const bundle = new Bundle({
                       entry: options.input
                     });
                  diff --git a/assets/src_ranuts_bundler_index.md.D3F9791C.lean.js b/assets/src_ranuts_bundler_index.md.DzEPrDNS.lean.js
                  similarity index 96%
                  rename from assets/src_ranuts_bundler_index.md.D3F9791C.lean.js
                  rename to assets/src_ranuts_bundler_index.md.DzEPrDNS.lean.js
                  index f74f3cd72f..7a53fed8e3 100644
                  --- a/assets/src_ranuts_bundler_index.md.D3F9791C.lean.js
                  +++ b/assets/src_ranuts_bundler_index.md.DzEPrDNS.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/bundler/index.md","filePath":"src/ranuts/bundler/index.md","lastUpdated":1728129125000}'),l={name:"src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

                  Bundler

                  Bundler的使用: 传入 options 参数

                  function build(options: Options):Promise<Build> {
                  +import{_ as s}from"./chunks/bundle.BxrzsuA1.js";import{_ as a,o as e,c as p,a3 as t}from"./chunks/framework.eq-HTtE3.js";const h=JSON.parse('{"title":"Bundler","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/bundler/index.md","filePath":"src/ranuts/bundler/index.md","lastUpdated":1728129344000}'),l={name:"src/ranuts/bundler/index.md"};function r(i,n,d,o,c,u){return e(),p("div",{"data-pagefind-body":!0},n[0]||(n[0]=[t(`

                  Bundler

                  Bundler的使用: 传入 options 参数

                  function build(options: Options):Promise<Build> {
                     const bundle = new Bundle({
                       entry: options.input
                     });
                  diff --git a/assets/src_ranuts_file_appendFile.md.CN41s_o-.js b/assets/src_ranuts_file_appendFile.md.1-6oJJ-H.js
                  similarity index 96%
                  rename from assets/src_ranuts_file_appendFile.md.CN41s_o-.js
                  rename to assets/src_ranuts_file_appendFile.md.1-6oJJ-H.js
                  index afffcafb04..f47ed2ad09 100644
                  --- a/assets/src_ranuts_file_appendFile.md.CN41s_o-.js
                  +++ b/assets/src_ranuts_file_appendFile.md.1-6oJJ-H.js
                  @@ -1 +1 @@
                  -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/appendFile.md","filePath":"src/ranuts/file/appendFile.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  AppendFile

                  追加一些数据到文件内

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否追加成功booleantrue 追加成功 false 追加失败
                  data追加失败的原因,添加成功后的文件内容any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string

                  Example

                  ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/appendFile.md","filePath":"src/ranuts/file/appendFile.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  AppendFile

                  追加一些数据到文件内

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否追加成功booleantrue 追加成功 false 追加失败
                  data追加失败的原因,添加成功后的文件内容any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string

                  Example

                  ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; diff --git a/assets/src_ranuts_file_appendFile.md.CN41s_o-.lean.js b/assets/src_ranuts_file_appendFile.md.1-6oJJ-H.lean.js similarity index 96% rename from assets/src_ranuts_file_appendFile.md.CN41s_o-.lean.js rename to assets/src_ranuts_file_appendFile.md.1-6oJJ-H.lean.js index afffcafb04..f47ed2ad09 100644 --- a/assets/src_ranuts_file_appendFile.md.CN41s_o-.lean.js +++ b/assets/src_ranuts_file_appendFile.md.1-6oJJ-H.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/appendFile.md","filePath":"src/ranuts/file/appendFile.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  AppendFile

                  追加一些数据到文件内

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否追加成功booleantrue 追加成功 false 追加失败
                  data追加失败的原因,添加成功后的文件内容any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string

                  Example

                  ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"AppendFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/appendFile.md","filePath":"src/ranuts/file/appendFile.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/file/appendFile.md"};function o(i,t,l,h,s,p){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  AppendFile

                  追加一些数据到文件内

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否追加成功booleantrue 追加成功 false 追加失败
                  data追加失败的原因,添加成功后的文件内容any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string

                  Example

                  ',9)]))}const b=e(n,[["render",o]]);export{u as __pageData,b as default}; diff --git a/assets/src_ranuts_file_fileInfo.md.CZ2C_27B.js b/assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.js similarity index 96% rename from assets/src_ranuts_file_fileInfo.md.CZ2C_27B.js rename to assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.js index dbd734d9d9..c956c4931d 100644 --- a/assets/src_ranuts_file_fileInfo.md.CZ2C_27B.js +++ b/assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/fileInfo.md","filePath":"src/ranuts/file/fileInfo.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

                  QueryFileInfo

                  查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否检查成功booleantrue 成功 false 失败
                  data文件的信息,或者错误的原因Stats

                  Options

                  参数说明类型默认值
                  path文件路径,需要检查的文件路径stringundefined

                  Example

                  ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; +import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/fileInfo.md","filePath":"src/ranuts/file/fileInfo.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

                  QueryFileInfo

                  查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否检查成功booleantrue 成功 false 失败
                  data文件的信息,或者错误的原因Stats

                  Options

                  参数说明类型默认值
                  path文件路径,需要检查的文件路径stringundefined

                  Example

                  ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; diff --git a/assets/src_ranuts_file_fileInfo.md.CZ2C_27B.lean.js b/assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.lean.js similarity index 96% rename from assets/src_ranuts_file_fileInfo.md.CZ2C_27B.lean.js rename to assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.lean.js index dbd734d9d9..c956c4931d 100644 --- a/assets/src_ranuts_file_fileInfo.md.CZ2C_27B.lean.js +++ b/assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/fileInfo.md","filePath":"src/ranuts/file/fileInfo.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

                  QueryFileInfo

                  查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否检查成功booleantrue 成功 false 失败
                  data文件的信息,或者错误的原因Stats

                  Options

                  参数说明类型默认值
                  path文件路径,需要检查的文件路径stringundefined

                  Example

                  ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; +import{_ as e,o as a,c as r,a3 as d}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"QueryFileInfo","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/fileInfo.md","filePath":"src/ranuts/file/fileInfo.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/file/fileInfo.md"};function i(n,t,l,h,s,c){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[d('

                  QueryFileInfo

                  查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否检查成功booleantrue 成功 false 失败
                  data文件的信息,或者错误的原因Stats

                  Options

                  参数说明类型默认值
                  path文件路径,需要检查的文件路径stringundefined

                  Example

                  ',9)]))}const p=e(o,[["render",i]]);export{u as __pageData,p as default}; diff --git a/assets/src_ranuts_file_readDir.md.d3nM0oPh.js b/assets/src_ranuts_file_readDir.md.BvSC38m-.js similarity index 96% rename from assets/src_ranuts_file_readDir.md.d3nM0oPh.js rename to assets/src_ranuts_file_readDir.md.BvSC38m-.js index f6b3d4318a..2fb361cc9c 100644 --- a/assets/src_ranuts_file_readDir.md.d3nM0oPh.js +++ b/assets/src_ranuts_file_readDir.md.BvSC38m-.js @@ -1 +1 @@ -import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readDir.md","filePath":"src/ranuts/file/readDir.md","lastUpdated":1728129125000}'),i={name:"src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

                  ReadDir

                  读一个目录下的所有文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  result目录下所有文件的数组array传入函数的参数

                  Options

                  ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readDir.md","filePath":"src/ranuts/file/readDir.md","lastUpdated":1728129344000}'),i={name:"src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

                  ReadDir

                  读一个目录下的所有文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  result目录下所有文件的数组array传入函数的参数

                  Options

                  ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/src_ranuts_file_readDir.md.d3nM0oPh.lean.js b/assets/src_ranuts_file_readDir.md.BvSC38m-.lean.js similarity index 96% rename from assets/src_ranuts_file_readDir.md.d3nM0oPh.lean.js rename to assets/src_ranuts_file_readDir.md.BvSC38m-.lean.js index f6b3d4318a..2fb361cc9c 100644 --- a/assets/src_ranuts_file_readDir.md.d3nM0oPh.lean.js +++ b/assets/src_ranuts_file_readDir.md.BvSC38m-.lean.js @@ -1 +1 @@ -import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readDir.md","filePath":"src/ranuts/file/readDir.md","lastUpdated":1728129125000}'),i={name:"src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

                  ReadDir

                  读一个目录下的所有文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  result目录下所有文件的数组array传入函数的参数

                  Options

                  ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; +import{_ as e,o as r,c as l,a3 as d,j as t,a as n}from"./chunks/framework.eq-HTtE3.js";const m=JSON.parse('{"title":"ReadDir","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readDir.md","filePath":"src/ranuts/file/readDir.md","lastUpdated":1728129344000}'),i={name:"src/ranuts/file/readDir.md"};function o(s,a,h,u,c,p){return r(),l("div",{"data-pagefind-body":!0},a[0]||(a[0]=[d('

                  ReadDir

                  读一个目录下的所有文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  result目录下所有文件的数组array传入函数的参数

                  Options

                  ',7),t("table",{tabindex:"0"},[t("thead",null,[t("tr",null,[t("th",null,"参数"),t("th",null,"说明"),t("th",null,"类型"),t("th",null,"默认值")])]),t("tbody",null,[t("tr",null,[t("td",null,"options"),t("td",{dirPath:""}),t("td",null,[t("code",null,"object")]),t("td",null,"传入函数的参数")]),t("tr",null,[t("td",null,"dirPath"),t("td",null,"文件的路径"),t("td",null,[t("code",null,"Stats")]),t("td")])])],-1),t("h2",{id:"example",tabindex:"-1"},[n("Example "),t("a",{class:"header-anchor",href:"#example","aria-label":'Permalink to "Example"'},"​")],-1)]))}const f=e(i,[["render",o]]);export{m as __pageData,f as default}; diff --git a/assets/src_ranuts_file_readFile.md.CwPAyuVM.js b/assets/src_ranuts_file_readFile.md.Ac6LvpUh.js similarity index 96% rename from assets/src_ranuts_file_readFile.md.CwPAyuVM.js rename to assets/src_ranuts_file_readFile.md.Ac6LvpUh.js index 7701c4def3..a62ae0506f 100644 --- a/assets/src_ranuts_file_readFile.md.CwPAyuVM.js +++ b/assets/src_ranuts_file_readFile.md.Ac6LvpUh.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readFile.md","filePath":"src/ranuts/file/readFile.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  ReadFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  data文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20

                  Example

                  ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readFile.md","filePath":"src/ranuts/file/readFile.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  ReadFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  data文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20

                  Example

                  ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/src_ranuts_file_readFile.md.CwPAyuVM.lean.js b/assets/src_ranuts_file_readFile.md.Ac6LvpUh.lean.js similarity index 96% rename from assets/src_ranuts_file_readFile.md.CwPAyuVM.lean.js rename to assets/src_ranuts_file_readFile.md.Ac6LvpUh.lean.js index 7701c4def3..a62ae0506f 100644 --- a/assets/src_ranuts_file_readFile.md.CwPAyuVM.lean.js +++ b/assets/src_ranuts_file_readFile.md.Ac6LvpUh.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readFile.md","filePath":"src/ranuts/file/readFile.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  ReadFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  data文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20

                  Example

                  ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ReadFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/readFile.md","filePath":"src/ranuts/file/readFile.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/file/readFile.md"};function i(l,t,n,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  ReadFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  data文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20

                  Example

                  ',9)]))}const b=e(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/src_ranuts_file_watchFile.md.DYvq_aUK.js b/assets/src_ranuts_file_watchFile.md.i-gzm-Mr.js similarity index 96% rename from assets/src_ranuts_file_watchFile.md.DYvq_aUK.js rename to assets/src_ranuts_file_watchFile.md.i-gzm-Mr.js index b683246197..8fce6b1995 100644 --- a/assets/src_ranuts_file_watchFile.md.DYvq_aUK.js +++ b/assets/src_ranuts_file_watchFile.md.i-gzm-Mr.js @@ -1 +1 @@ -import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/watchFile.md","filePath":"src/ranuts/file/watchFile.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/file/watchFile.md"};function i(h,t,l,n,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WatchFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  status文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20
                  ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/watchFile.md","filePath":"src/ranuts/file/watchFile.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/file/watchFile.md"};function i(h,t,l,n,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WatchFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  status文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20
                  ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/src_ranuts_file_watchFile.md.DYvq_aUK.lean.js b/assets/src_ranuts_file_watchFile.md.i-gzm-Mr.lean.js similarity index 96% rename from assets/src_ranuts_file_watchFile.md.DYvq_aUK.lean.js rename to assets/src_ranuts_file_watchFile.md.i-gzm-Mr.lean.js index b683246197..8fce6b1995 100644 --- a/assets/src_ranuts_file_watchFile.md.DYvq_aUK.lean.js +++ b/assets/src_ranuts_file_watchFile.md.i-gzm-Mr.lean.js @@ -1 +1 @@ -import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/watchFile.md","filePath":"src/ranuts/file/watchFile.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/file/watchFile.md"};function i(h,t,l,n,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WatchFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  status文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20
                  ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; +import{_ as a,o as e,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WatchFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/watchFile.md","filePath":"src/ranuts/file/watchFile.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/file/watchFile.md"};function i(h,t,l,n,c,s){return e(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WatchFile

                  观察一个文件是否改变

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  status文件是否被改变booleantrue 文件改变 false 文件没变

                  Options

                  参数说明类型默认值
                  path文件路径,需要监听的文件stringundefined
                  interval监听文件改变的时间,单位毫秒。number20
                  ',8)]))}const b=a(o,[["render",i]]);export{p as __pageData,b as default}; diff --git a/assets/src_ranuts_file_writeFile.md.CdGN48zU.js b/assets/src_ranuts_file_writeFile.md.2z4SHchp.js similarity index 96% rename from assets/src_ranuts_file_writeFile.md.CdGN48zU.js rename to assets/src_ranuts_file_writeFile.md.2z4SHchp.js index 51f9d15f04..4ceb00f383 100644 --- a/assets/src_ranuts_file_writeFile.md.CdGN48zU.js +++ b/assets/src_ranuts_file_writeFile.md.2z4SHchp.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/writeFile.md","filePath":"src/ranuts/file/writeFile.md","lastUpdated":1728129125000}'),i={name:"src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WriteFile

                  将内容写入文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否写入成功booleantrue 成功 false 失败
                  data写入失败的原因,添加成功后的文件内容和文件路径any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string
                  ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/writeFile.md","filePath":"src/ranuts/file/writeFile.md","lastUpdated":1728129344000}'),i={name:"src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WriteFile

                  将内容写入文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否写入成功booleantrue 成功 false 失败
                  data写入失败的原因,添加成功后的文件内容和文件路径any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string
                  ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; diff --git a/assets/src_ranuts_file_writeFile.md.CdGN48zU.lean.js b/assets/src_ranuts_file_writeFile.md.2z4SHchp.lean.js similarity index 96% rename from assets/src_ranuts_file_writeFile.md.CdGN48zU.lean.js rename to assets/src_ranuts_file_writeFile.md.2z4SHchp.lean.js index 51f9d15f04..4ceb00f383 100644 --- a/assets/src_ranuts_file_writeFile.md.CdGN48zU.lean.js +++ b/assets/src_ranuts_file_writeFile.md.2z4SHchp.lean.js @@ -1 +1 @@ -import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/writeFile.md","filePath":"src/ranuts/file/writeFile.md","lastUpdated":1728129125000}'),i={name:"src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WriteFile

                  将内容写入文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否写入成功booleantrue 成功 false 失败
                  data写入失败的原因,添加成功后的文件内容和文件路径any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string
                  ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; +import{_ as e,o as a,c as d,a3 as r}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"WriteFile","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/file/writeFile.md","filePath":"src/ranuts/file/writeFile.md","lastUpdated":1728129344000}'),i={name:"src/ranuts/file/writeFile.md"};function o(n,t,l,h,s,c){return a(),d("div",{"data-pagefind-body":!0},t[0]||(t[0]=[r('

                  WriteFile

                  将内容写入文件

                  API

                  Return

                  • Promise
                  参数说明类型描述
                  success是否写入成功booleantrue 成功 false 失败
                  data写入失败的原因,添加成功后的文件内容和文件路径any

                  Options

                  参数说明类型默认值
                  path文件路径,需要追加的文件stringundefined
                  content需要追加的内容string
                  ',8)]))}const b=e(i,[["render",o]]);export{p as __pageData,b as default}; diff --git a/assets/src_ranuts_index.md.CHFYg7WB.js b/assets/src_ranuts_index.md.BJ436-Wa.js similarity index 97% rename from assets/src_ranuts_index.md.CHFYg7WB.js rename to assets/src_ranuts_index.md.BJ436-Wa.js index 208101c7ae..818974d02a 100644 --- a/assets/src_ranuts_index.md.CHFYg7WB.js +++ b/assets/src_ranuts_index.md.BJ436-Wa.js @@ -1 +1 @@ -import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/index.md","filePath":"src/ranuts/index.md","lastUpdated":1728129125000}'),h={name:"src/ranuts/index.md"};function s(n,t,f,m,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

                  ranuts overview

                  Method list

                  Methoddescriptiondetail
                  writeFileWrite to filewriteFile
                  readFileRead filereadFile
                  readDirRead the directory and get the names of all the files in the directoryreadDir
                  watchFileCheck whether the contents of the file have changedwatchFile
                  queryFileInfoQuery file informationqueryFileInfo
                  filterObjFilter objectfilterObj
                  EventEmitterPublish-subscribe classEventEmitter
                  str2XmlString is converted to xmlstr2Xml
                  getMimeGets the mime type based on the file format suffixgetMime
                  getCookieGets the value of the specified cookiewriteFile
                  formatJsonFormatted JSONformatJson

                  TOTP

                  ',4)),o(e)])}const v=r(h,[["render",s]]);export{p as __pageData,v as default}; +import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/index.md","filePath":"src/ranuts/index.md","lastUpdated":1728129344000}'),h={name:"src/ranuts/index.md"};function s(n,t,f,m,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

                  ranuts overview

                  Method list

                  Methoddescriptiondetail
                  writeFileWrite to filewriteFile
                  readFileRead filereadFile
                  readDirRead the directory and get the names of all the files in the directoryreadDir
                  watchFileCheck whether the contents of the file have changedwatchFile
                  queryFileInfoQuery file informationqueryFileInfo
                  filterObjFilter objectfilterObj
                  EventEmitterPublish-subscribe classEventEmitter
                  str2XmlString is converted to xmlstr2Xml
                  getMimeGets the mime type based on the file format suffixgetMime
                  getCookieGets the value of the specified cookiewriteFile
                  formatJsonFormatted JSONformatJson

                  TOTP

                  ',4)),o(e)])}const v=r(h,[["render",s]]);export{p as __pageData,v as default}; diff --git a/assets/src_ranuts_index.md.CHFYg7WB.lean.js b/assets/src_ranuts_index.md.BJ436-Wa.lean.js similarity index 97% rename from assets/src_ranuts_index.md.CHFYg7WB.lean.js rename to assets/src_ranuts_index.md.BJ436-Wa.lean.js index 208101c7ae..818974d02a 100644 --- a/assets/src_ranuts_index.md.CHFYg7WB.lean.js +++ b/assets/src_ranuts_index.md.BJ436-Wa.lean.js @@ -1 +1 @@ -import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/index.md","filePath":"src/ranuts/index.md","lastUpdated":1728129125000}'),h={name:"src/ranuts/index.md"};function s(n,t,f,m,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

                  ranuts overview

                  Method list

                  Methoddescriptiondetail
                  writeFileWrite to filewriteFile
                  readFileRead filereadFile
                  readDirRead the directory and get the names of all the files in the directoryreadDir
                  watchFileCheck whether the contents of the file have changedwatchFile
                  queryFileInfoQuery file informationqueryFileInfo
                  filterObjFilter objectfilterObj
                  EventEmitterPublish-subscribe classEventEmitter
                  str2XmlString is converted to xmlstr2Xml
                  getMimeGets the mime type based on the file format suffixgetMime
                  getCookieGets the value of the specified cookiewriteFile
                  formatJsonFormatted JSONformatJson

                  TOTP

                  ',4)),o(e)])}const v=r(h,[["render",s]]);export{p as __pageData,v as default}; +import{_ as r,o as d,c as a,a3 as i,G as o,B as l}from"./chunks/framework.eq-HTtE3.js";const p=JSON.parse('{"title":"ranuts overview","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/index.md","filePath":"src/ranuts/index.md","lastUpdated":1728129344000}'),h={name:"src/ranuts/index.md"};function s(n,t,f,m,c,u){const e=l("TOTP");return d(),a("div",{"data-pagefind-body":!0},[t[0]||(t[0]=i('

                  ranuts overview

                  Method list

                  Methoddescriptiondetail
                  writeFileWrite to filewriteFile
                  readFileRead filereadFile
                  readDirRead the directory and get the names of all the files in the directoryreadDir
                  watchFileCheck whether the contents of the file have changedwatchFile
                  queryFileInfoQuery file informationqueryFileInfo
                  filterObjFilter objectfilterObj
                  EventEmitterPublish-subscribe classEventEmitter
                  str2XmlString is converted to xmlstr2Xml
                  getMimeGets the mime type based on the file format suffixgetMime
                  getCookieGets the value of the specified cookiewriteFile
                  formatJsonFormatted JSONformatJson

                  TOTP

                  ',4)),o(e)])}const v=r(h,[["render",s]]);export{p as __pageData,v as default}; diff --git a/assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.js b/assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.js similarity index 98% rename from assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.js rename to assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.js index da0686bc19..068f961440 100644 --- a/assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.js +++ b/assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.js @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mimeType/mimeType.md","filePath":"src/ranuts/mimeType/mimeType.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  getMime

                  传入文件格式后缀,返回mime type

                  API

                  Return

                  参数说明类型
                  string返回mime typestring

                  Options

                  参数说明类型默认值
                  ext文件后缀格式string

                  Example

                  js
                  import { getMime } from 'ranuts';
                  +import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mimeType/mimeType.md","filePath":"src/ranuts/mimeType/mimeType.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  getMime

                  传入文件格式后缀,返回mime type

                  API

                  Return

                  参数说明类型
                  string返回mime typestring

                  Options

                  参数说明类型默认值
                  ext文件后缀格式string

                  Example

                  js
                  import { getMime } from 'ranuts';
                   
                   const result = getMime('.pptx');
                   console.log(result);
                  diff --git a/assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.lean.js b/assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.lean.js
                  rename to assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.lean.js
                  index da0686bc19..068f961440 100644
                  --- a/assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.lean.js
                  +++ b/assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mimeType/mimeType.md","filePath":"src/ranuts/mimeType/mimeType.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  getMime

                  传入文件格式后缀,返回mime type

                  API

                  Return

                  参数说明类型
                  string返回mime typestring

                  Options

                  参数说明类型默认值
                  ext文件后缀格式string

                  Example

                  js
                  import { getMime } from 'ranuts';
                  +import{_ as i,o as a,c as t,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getMime","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mimeType/mimeType.md","filePath":"src/ranuts/mimeType/mimeType.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/mimeType/mimeType.md"};function h(l,s,p,r,d,k){return a(),t("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  getMime

                  传入文件格式后缀,返回mime type

                  API

                  Return

                  参数说明类型
                  string返回mime typestring

                  Options

                  参数说明类型默认值
                  ext文件后缀格式string

                  Example

                  js
                  import { getMime } from 'ranuts';
                   
                   const result = getMime('.pptx');
                   console.log(result);
                  diff --git a/assets/src_ranuts_mode_subscribe.md.DmGp9s_W.js b/assets/src_ranuts_mode_subscribe.md.CBjAEwg0.js
                  similarity index 99%
                  rename from assets/src_ranuts_mode_subscribe.md.DmGp9s_W.js
                  rename to assets/src_ranuts_mode_subscribe.md.CBjAEwg0.js
                  index f1fba6b111..89a0077ed6 100644
                  --- a/assets/src_ranuts_mode_subscribe.md.DmGp9s_W.js
                  +++ b/assets/src_ranuts_mode_subscribe.md.CBjAEwg0.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mode/subscribe.md","filePath":"src/ranuts/mode/subscribe.md","lastUpdated":1728129125000}'),h={name:"src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  EventEmitter

                  发布订阅的类

                  Class

                  Methods

                  方法参数说明默认值
                  on订阅事件订阅事件,传入参数事件名,回调函数
                  once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
                  off取消订阅事件,传入参数事件名,回调函数取消订阅事件
                  emit触发事件,需要事件名触发事件

                  Example

                  js
                  import { Subscribe } from 'ranuts';
                  +import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mode/subscribe.md","filePath":"src/ranuts/mode/subscribe.md","lastUpdated":1728129344000}'),h={name:"src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  EventEmitter

                  发布订阅的类

                  Class

                  Methods

                  方法参数说明默认值
                  on订阅事件订阅事件,传入参数事件名,回调函数
                  once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
                  off取消订阅事件,传入参数事件名,回调函数取消订阅事件
                  emit触发事件,需要事件名触发事件

                  Example

                  js
                  import { Subscribe } from 'ranuts';
                   
                   const subscribe = new Subscribe();
                   
                  diff --git a/assets/src_ranuts_mode_subscribe.md.DmGp9s_W.lean.js b/assets/src_ranuts_mode_subscribe.md.CBjAEwg0.lean.js
                  similarity index 99%
                  rename from assets/src_ranuts_mode_subscribe.md.DmGp9s_W.lean.js
                  rename to assets/src_ranuts_mode_subscribe.md.CBjAEwg0.lean.js
                  index f1fba6b111..89a0077ed6 100644
                  --- a/assets/src_ranuts_mode_subscribe.md.DmGp9s_W.lean.js
                  +++ b/assets/src_ranuts_mode_subscribe.md.CBjAEwg0.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mode/subscribe.md","filePath":"src/ranuts/mode/subscribe.md","lastUpdated":1728129125000}'),h={name:"src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  EventEmitter

                  发布订阅的类

                  Class

                  Methods

                  方法参数说明默认值
                  on订阅事件订阅事件,传入参数事件名,回调函数
                  once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
                  off取消订阅事件,传入参数事件名,回调函数取消订阅事件
                  emit触发事件,需要事件名触发事件

                  Example

                  js
                  import { Subscribe } from 'ranuts';
                  +import{_ as i,o as a,c as n,a3 as t}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"EventEmitter","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/mode/subscribe.md","filePath":"src/ranuts/mode/subscribe.md","lastUpdated":1728129344000}'),h={name:"src/ranuts/mode/subscribe.md"};function l(k,s,p,e,E,r){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t(`

                  EventEmitter

                  发布订阅的类

                  Class

                  Methods

                  方法参数说明默认值
                  on订阅事件订阅事件,传入参数事件名,回调函数
                  once订阅一次事件,传入参数事件名,回调函数订阅一次事件,触发一次后不再会触发
                  off取消订阅事件,传入参数事件名,回调函数取消订阅事件
                  emit触发事件,需要事件名触发事件

                  Example

                  js
                  import { Subscribe } from 'ranuts';
                   
                   const subscribe = new Subscribe();
                   
                  diff --git a/assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.js b/assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.js
                  rename to assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.js
                  index fb91f3cc51..1706fa088e 100644
                  --- a/assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.js
                  +++ b/assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/convertImageToBase64.md","filePath":"src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/convertImageToBase64.md"};function r(o,t,d,h,l,c){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  convertImageToBase64

                  Picture turn 'base64'

                  API

                  Return

                  argumentInstructionstype
                  successWhether the conversion is successfulboolean
                  dataThe value after successful conversionstring,ArrayBuffer , null
                  messageThe reasons why the conversion succeeds or failsstring

                  Options

                  argumentInstructionstypeDefault value
                  fileIncoming fileFilenull

                  Example

                  js
                  import { convertImageToBase64 } from 'ranuts';
                  +import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/convertImageToBase64.md","filePath":"src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/convertImageToBase64.md"};function r(o,t,d,h,l,c){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  convertImageToBase64

                  Picture turn 'base64'

                  API

                  Return

                  argumentInstructionstype
                  successWhether the conversion is successfulboolean
                  dataThe value after successful conversionstring,ArrayBuffer , null
                  messageThe reasons why the conversion succeeds or failsstring

                  Options

                  argumentInstructionstypeDefault value
                  fileIncoming fileFilenull

                  Example

                  js
                  import { convertImageToBase64 } from 'ranuts';
                   
                   convertImageToBase64(file).then((res) => {
                     console.log(result);
                  diff --git a/assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.lean.js b/assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.lean.js
                  rename to assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.lean.js
                  index fb91f3cc51..1706fa088e 100644
                  --- a/assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.lean.js
                  +++ b/assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/convertImageToBase64.md","filePath":"src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/convertImageToBase64.md"};function r(o,t,d,h,l,c){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  convertImageToBase64

                  Picture turn 'base64'

                  API

                  Return

                  argumentInstructionstype
                  successWhether the conversion is successfulboolean
                  dataThe value after successful conversionstring,ArrayBuffer , null
                  messageThe reasons why the conversion succeeds or failsstring

                  Options

                  argumentInstructionstypeDefault value
                  fileIncoming fileFilenull

                  Example

                  js
                  import { convertImageToBase64 } from 'ranuts';
                  +import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"convertImageToBase64","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/convertImageToBase64.md","filePath":"src/ranuts/utils/convertImageToBase64.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/convertImageToBase64.md"};function r(o,t,d,h,l,c){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  convertImageToBase64

                  Picture turn 'base64'

                  API

                  Return

                  argumentInstructionstype
                  successWhether the conversion is successfulboolean
                  dataThe value after successful conversionstring,ArrayBuffer , null
                  messageThe reasons why the conversion succeeds or failsstring

                  Options

                  argumentInstructionstypeDefault value
                  fileIncoming fileFilenull

                  Example

                  js
                  import { convertImageToBase64 } from 'ranuts';
                   
                   convertImageToBase64(file).then((res) => {
                     console.log(result);
                  diff --git a/assets/src_ranuts_utils_filterObj.md.SUYinD61.js b/assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_filterObj.md.SUYinD61.js
                  rename to assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.js
                  index 2bc90f771a..3bc047a17d 100644
                  --- a/assets/src_ranuts_utils_filterObj.md.SUYinD61.js
                  +++ b/assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as t,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/filterObj.md","filePath":"src/ranuts/utils/filterObj.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/filterObj.md"};function l(h,s,r,p,d,k){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  filterObj

                  Filter the properties of the object, remove the properties of the object in the list array, return a new object, usually used to remove null characters and null

                  API

                  Return

                  argumentInstructionstype
                  ObjectReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  objObjects to be filteredobject
                  listFamiliar array to filterarray

                  Example

                  js
                  import { filterObj } from 'ranuts';
                  +import{_ as a,o as t,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/filterObj.md","filePath":"src/ranuts/utils/filterObj.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/filterObj.md"};function l(h,s,r,p,d,k){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  filterObj

                  Filter the properties of the object, remove the properties of the object in the list array, return a new object, usually used to remove null characters and null

                  API

                  Return

                  argumentInstructionstype
                  ObjectReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  objObjects to be filteredobject
                  listFamiliar array to filterarray

                  Example

                  js
                  import { filterObj } from 'ranuts';
                   
                   const obj = {
                     name: 'chaxus',
                  diff --git a/assets/src_ranuts_utils_filterObj.md.SUYinD61.lean.js b/assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_filterObj.md.SUYinD61.lean.js
                  rename to assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.lean.js
                  index 2bc90f771a..3bc047a17d 100644
                  --- a/assets/src_ranuts_utils_filterObj.md.SUYinD61.lean.js
                  +++ b/assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as t,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/filterObj.md","filePath":"src/ranuts/utils/filterObj.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/filterObj.md"};function l(h,s,r,p,d,k){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  filterObj

                  Filter the properties of the object, remove the properties of the object in the list array, return a new object, usually used to remove null characters and null

                  API

                  Return

                  argumentInstructionstype
                  ObjectReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  objObjects to be filteredobject
                  listFamiliar array to filterarray

                  Example

                  js
                  import { filterObj } from 'ranuts';
                  +import{_ as a,o as t,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"filterObj","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/filterObj.md","filePath":"src/ranuts/utils/filterObj.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/filterObj.md"};function l(h,s,r,p,d,k){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[e(`

                  filterObj

                  Filter the properties of the object, remove the properties of the object in the list array, return a new object, usually used to remove null characters and null

                  API

                  Return

                  argumentInstructionstype
                  ObjectReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  objObjects to be filteredobject
                  listFamiliar array to filterarray

                  Example

                  js
                  import { filterObj } from 'ranuts';
                   
                   const obj = {
                     name: 'chaxus',
                  diff --git a/assets/src_ranuts_utils_formatJson.md.BQjsxBql.js b/assets/src_ranuts_utils_formatJson.md.DQX6wWMq.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_formatJson.md.BQjsxBql.js
                  rename to assets/src_ranuts_utils_formatJson.md.DQX6wWMq.js
                  index f98147ae27..089355f2eb 100644
                  --- a/assets/src_ranuts_utils_formatJson.md.BQjsxBql.js
                  +++ b/assets/src_ranuts_utils_formatJson.md.DQX6wWMq.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/formatJson.md","filePath":"src/ranuts/utils/formatJson.md","lastUpdated":1728129125000}'),e={name:"src/ranuts/utils/formatJson.md"};function l(r,s,h,o,d,p){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  formatJson

                  Pass in a JSON or JSON string, add Spaces and newlines to return a formatted JSON string

                  API

                  Return

                  argumentInstructionstype
                  stringReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  jsonJSON objects that need to be formattedobject,stringnull
                  callbackError callback, optionalfunctionnull

                  Example

                  js
                  import { formatJson } from 'ranuts';
                  +import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/formatJson.md","filePath":"src/ranuts/utils/formatJson.md","lastUpdated":1728129344000}'),e={name:"src/ranuts/utils/formatJson.md"};function l(r,s,h,o,d,p){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  formatJson

                  Pass in a JSON or JSON string, add Spaces and newlines to return a formatted JSON string

                  API

                  Return

                  argumentInstructionstype
                  stringReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  jsonJSON objects that need to be formattedobject,stringnull
                  callbackError callback, optionalfunctionnull

                  Example

                  js
                  import { formatJson } from 'ranuts';
                   
                   const json = {
                     name: 'chaxus',
                  diff --git a/assets/src_ranuts_utils_formatJson.md.BQjsxBql.lean.js b/assets/src_ranuts_utils_formatJson.md.DQX6wWMq.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_formatJson.md.BQjsxBql.lean.js
                  rename to assets/src_ranuts_utils_formatJson.md.DQX6wWMq.lean.js
                  index f98147ae27..089355f2eb 100644
                  --- a/assets/src_ranuts_utils_formatJson.md.BQjsxBql.lean.js
                  +++ b/assets/src_ranuts_utils_formatJson.md.DQX6wWMq.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/formatJson.md","filePath":"src/ranuts/utils/formatJson.md","lastUpdated":1728129125000}'),e={name:"src/ranuts/utils/formatJson.md"};function l(r,s,h,o,d,p){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  formatJson

                  Pass in a JSON or JSON string, add Spaces and newlines to return a formatted JSON string

                  API

                  Return

                  argumentInstructionstype
                  stringReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  jsonJSON objects that need to be formattedobject,stringnull
                  callbackError callback, optionalfunctionnull

                  Example

                  js
                  import { formatJson } from 'ranuts';
                  +import{_ as a,o as t,c as i,a3 as n}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"formatJson","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/formatJson.md","filePath":"src/ranuts/utils/formatJson.md","lastUpdated":1728129344000}'),e={name:"src/ranuts/utils/formatJson.md"};function l(r,s,h,o,d,p){return t(),i("div",{"data-pagefind-body":!0},s[0]||(s[0]=[n(`

                  formatJson

                  Pass in a JSON or JSON string, add Spaces and newlines to return a formatted JSON string

                  API

                  Return

                  argumentInstructionstype
                  stringReturn an objectObject

                  Options

                  argumentInstructionstypeDefault value
                  jsonJSON objects that need to be formattedobject,stringnull
                  callbackError callback, optionalfunctionnull

                  Example

                  js
                  import { formatJson } from 'ranuts';
                   
                   const json = {
                     name: 'chaxus',
                  diff --git a/assets/src_ranuts_utils_getCookie.md.glwGRuBT.js b/assets/src_ranuts_utils_getCookie.md.CRjg9zYL.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_getCookie.md.glwGRuBT.js
                  rename to assets/src_ranuts_utils_getCookie.md.CRjg9zYL.js
                  index 1bb9b3aaf1..0ce574ed8b 100644
                  --- a/assets/src_ranuts_utils_getCookie.md.glwGRuBT.js
                  +++ b/assets/src_ranuts_utils_getCookie.md.CRjg9zYL.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/getCookie.md","filePath":"src/ranuts/utils/getCookie.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/getCookie.md"};function h(o,t,l,r,d,p){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  getCookie

                  Pass in a string to get the value of the cookie with the specified name

                  API

                  Return

                  argumentInstructionstype
                  stingReturns the value of a cookie with the specified namestring

                  Options

                  argumentInstructionstypeDefault value
                  nameSpecifies the value of the name that gets the cookieobject

                  Example

                  js
                  import { getCookie } from 'ranuts';
                  +import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/getCookie.md","filePath":"src/ranuts/utils/getCookie.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/getCookie.md"};function h(o,t,l,r,d,p){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  getCookie

                  Pass in a string to get the value of the cookie with the specified name

                  API

                  Return

                  argumentInstructionstype
                  stingReturns the value of a cookie with the specified namestring

                  Options

                  argumentInstructionstypeDefault value
                  nameSpecifies the value of the name that gets the cookieobject

                  Example

                  js
                  import { getCookie } from 'ranuts';
                   
                   const result = getCookie('name');
                   
                  diff --git a/assets/src_ranuts_utils_getCookie.md.glwGRuBT.lean.js b/assets/src_ranuts_utils_getCookie.md.CRjg9zYL.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_getCookie.md.glwGRuBT.lean.js
                  rename to assets/src_ranuts_utils_getCookie.md.CRjg9zYL.lean.js
                  index 1bb9b3aaf1..0ce574ed8b 100644
                  --- a/assets/src_ranuts_utils_getCookie.md.glwGRuBT.lean.js
                  +++ b/assets/src_ranuts_utils_getCookie.md.CRjg9zYL.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/getCookie.md","filePath":"src/ranuts/utils/getCookie.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/getCookie.md"};function h(o,t,l,r,d,p){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  getCookie

                  Pass in a string to get the value of the cookie with the specified name

                  API

                  Return

                  argumentInstructionstype
                  stingReturns the value of a cookie with the specified namestring

                  Options

                  argumentInstructionstypeDefault value
                  nameSpecifies the value of the name that gets the cookieobject

                  Example

                  js
                  import { getCookie } from 'ranuts';
                  +import{_ as a,o as e,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"getCookie","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/getCookie.md","filePath":"src/ranuts/utils/getCookie.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/getCookie.md"};function h(o,t,l,r,d,p){return e(),s("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  getCookie

                  Pass in a string to get the value of the cookie with the specified name

                  API

                  Return

                  argumentInstructionstype
                  stingReturns the value of a cookie with the specified namestring

                  Options

                  argumentInstructionstypeDefault value
                  nameSpecifies the value of the name that gets the cookieobject

                  Example

                  js
                  import { getCookie } from 'ranuts';
                   
                   const result = getCookie('name');
                   
                  diff --git a/assets/src_ranuts_utils_ocr.md.Bac0Ieci.js b/assets/src_ranuts_utils_ocr.md.COxYWSg4.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_ocr.md.Bac0Ieci.js
                  rename to assets/src_ranuts_utils_ocr.md.COxYWSg4.js
                  index 2b53b73626..b98b267724 100644
                  --- a/assets/src_ranuts_utils_ocr.md.Bac0Ieci.js
                  +++ b/assets/src_ranuts_utils_ocr.md.COxYWSg4.js
                  @@ -1,4 +1,4 @@
                  -import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/ocr.md","filePath":"src/ranuts/utils/ocr.md","lastUpdated":1728129125000}'),s={name:"src/ranuts/utils/ocr.md"};function e(n,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  OCR

                  Pass in the image and the corresponding language type, and return the text in the image.

                  API

                  Return

                  argumentInstructionstype
                  successWhether the resolution is successfulboolean
                  dataThe object is parsed successfullyobj
                  messageAnalyze the reasons for success or failurestring

                  Options

                  argumentInstructionstypeDefault value
                  imagesAn array of images, supporting 'url' and 'base64'Array<string>null
                  languageSpecify the language in which the text will be generatedlang-codestring
                  langPathWhen using it, you need to be able to access cdn.jsdelivr.net, which will download the corresponding language package, if you cannot access it, you can also put the language package locally, passing the corresponding directory pathstringThis parameter is optional. By default, download from the network

                  Example

                  js
                  import { ocr } from 'ranuts';
                  +import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/ocr.md","filePath":"src/ranuts/utils/ocr.md","lastUpdated":1728129344000}'),s={name:"src/ranuts/utils/ocr.md"};function e(n,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  OCR

                  Pass in the image and the corresponding language type, and return the text in the image.

                  API

                  Return

                  argumentInstructionstype
                  successWhether the resolution is successfulboolean
                  dataThe object is parsed successfullyobj
                  messageAnalyze the reasons for success or failurestring

                  Options

                  argumentInstructionstypeDefault value
                  imagesAn array of images, supporting 'url' and 'base64'Array<string>null
                  languageSpecify the language in which the text will be generatedlang-codestring
                  langPathWhen using it, you need to be able to access cdn.jsdelivr.net, which will download the corresponding language package, if you cannot access it, you can also put the language package locally, passing the corresponding directory pathstringThis parameter is optional. By default, download from the network

                  Example

                  js
                  import { ocr } from 'ranuts';
                   
                   const images = ['https://chaxus.github.io/ran/ocr/eng.png'];
                   const languages = 'eng';
                  diff --git a/assets/src_ranuts_utils_ocr.md.Bac0Ieci.lean.js b/assets/src_ranuts_utils_ocr.md.COxYWSg4.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_ocr.md.Bac0Ieci.lean.js
                  rename to assets/src_ranuts_utils_ocr.md.COxYWSg4.lean.js
                  index 2b53b73626..b98b267724 100644
                  --- a/assets/src_ranuts_utils_ocr.md.Bac0Ieci.lean.js
                  +++ b/assets/src_ranuts_utils_ocr.md.COxYWSg4.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/ocr.md","filePath":"src/ranuts/utils/ocr.md","lastUpdated":1728129125000}'),s={name:"src/ranuts/utils/ocr.md"};function e(n,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  OCR

                  Pass in the image and the corresponding language type, and return the text in the image.

                  API

                  Return

                  argumentInstructionstype
                  successWhether the resolution is successfulboolean
                  dataThe object is parsed successfullyobj
                  messageAnalyze the reasons for success or failurestring

                  Options

                  argumentInstructionstypeDefault value
                  imagesAn array of images, supporting 'url' and 'base64'Array<string>null
                  languageSpecify the language in which the text will be generatedlang-codestring
                  langPathWhen using it, you need to be able to access cdn.jsdelivr.net, which will download the corresponding language package, if you cannot access it, you can also put the language package locally, passing the corresponding directory pathstringThis parameter is optional. By default, download from the network

                  Example

                  js
                  import { ocr } from 'ranuts';
                  +import{_ as d,o as a,c as r,a3 as i}from"./chunks/framework.eq-HTtE3.js";const c=JSON.parse('{"title":"OCR","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/ocr.md","filePath":"src/ranuts/utils/ocr.md","lastUpdated":1728129344000}'),s={name:"src/ranuts/utils/ocr.md"};function e(n,t,l,h,o,p){return a(),r("div",{"data-pagefind-body":!0},t[0]||(t[0]=[i(`

                  OCR

                  Pass in the image and the corresponding language type, and return the text in the image.

                  API

                  Return

                  argumentInstructionstype
                  successWhether the resolution is successfulboolean
                  dataThe object is parsed successfullyobj
                  messageAnalyze the reasons for success or failurestring

                  Options

                  argumentInstructionstypeDefault value
                  imagesAn array of images, supporting 'url' and 'base64'Array<string>null
                  languageSpecify the language in which the text will be generatedlang-codestring
                  langPathWhen using it, you need to be able to access cdn.jsdelivr.net, which will download the corresponding language package, if you cannot access it, you can also put the language package locally, passing the corresponding directory pathstringThis parameter is optional. By default, download from the network

                  Example

                  js
                  import { ocr } from 'ranuts';
                   
                   const images = ['https://chaxus.github.io/ran/ocr/eng.png'];
                   const languages = 'eng';
                  diff --git a/assets/src_ranuts_utils_str2xml.md.BTPyA91O.js b/assets/src_ranuts_utils_str2xml.md.84gjZ6IG.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_str2xml.md.BTPyA91O.js
                  rename to assets/src_ranuts_utils_str2xml.md.84gjZ6IG.js
                  index 0c0df4ece9..a1e54bd11e 100644
                  --- a/assets/src_ranuts_utils_str2xml.md.BTPyA91O.js
                  +++ b/assets/src_ranuts_utils_str2xml.md.84gjZ6IG.js
                  @@ -1,4 +1,4 @@
                  -import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/str2xml.md","filePath":"src/ranuts/utils/str2xml.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/str2xml.md"};function l(r,t,h,d,o,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

                  str2Xml

                  Pass in a string and convert it to 'xml'

                  API

                  Return

                  argumentInstructionstype
                  HTMLElement返回一个HTMLElementHTMLElement

                  Options

                  argumentInstructionstypeDefault value
                  xmlStrIncoming parameterstringnull
                  formatSet the format to be converted. The default is' text/xml '' DOMParserSupportedType 'null

                  Example

                  For example, when doing icon library, we need to dynamically import all the 'ICONS' in the directory. In this case, strings are imported, but strings cannot be added to 'xml'. So we need to convert the string to 'xml', and then we can add it to 'xml'.

                  js
                  import { str2Xml } from 'ranuts';
                  +import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/str2xml.md","filePath":"src/ranuts/utils/str2xml.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/str2xml.md"};function l(r,t,h,d,o,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

                  str2Xml

                  Pass in a string and convert it to 'xml'

                  API

                  Return

                  argumentInstructionstype
                  HTMLElement返回一个HTMLElementHTMLElement

                  Options

                  argumentInstructionstypeDefault value
                  xmlStrIncoming parameterstringnull
                  formatSet the format to be converted. The default is' text/xml '' DOMParserSupportedType 'null

                  Example

                  For example, when doing icon library, we need to dynamically import all the 'ICONS' in the directory. In this case, strings are imported, but strings cannot be added to 'xml'. So we need to convert the string to 'xml', and then we can add it to 'xml'.

                  js
                  import { str2Xml } from 'ranuts';
                   
                   // import 'assets/*.svg'
                   const svg = \`<svg t="1667483498347" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8544" width="200" height="200"><path d="M858.5 763.6c-18.9-44.8-46.1-85-80.6-119.5-34.5-34.5-74.7-61.6-119.5-80.6-0.4-0.2-0.8-0.3-1.2-0.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-0.4 0.2-0.8 0.3-1.2 0.5-44.8 18.9-85 46-119.5 80.6-34.5 34.5-61.6 74.7-80.6 119.5C146.9 807.5 137 854 136 901.8c-0.1 4.5 3.5 8.2 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c0.1 4.4 3.6 7.8 8 7.8h60c4.5 0 8.1-3.7 8-8.2-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z" p-id="8545"></path></svg>\`;
                  diff --git a/assets/src_ranuts_utils_str2xml.md.BTPyA91O.lean.js b/assets/src_ranuts_utils_str2xml.md.84gjZ6IG.lean.js
                  similarity index 98%
                  rename from assets/src_ranuts_utils_str2xml.md.BTPyA91O.lean.js
                  rename to assets/src_ranuts_utils_str2xml.md.84gjZ6IG.lean.js
                  index 0c0df4ece9..a1e54bd11e 100644
                  --- a/assets/src_ranuts_utils_str2xml.md.BTPyA91O.lean.js
                  +++ b/assets/src_ranuts_utils_str2xml.md.84gjZ6IG.lean.js
                  @@ -1,4 +1,4 @@
                  -import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/str2xml.md","filePath":"src/ranuts/utils/str2xml.md","lastUpdated":1728129125000}'),n={name:"src/ranuts/utils/str2xml.md"};function l(r,t,h,d,o,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

                  str2Xml

                  Pass in a string and convert it to 'xml'

                  API

                  Return

                  argumentInstructionstype
                  HTMLElement返回一个HTMLElementHTMLElement

                  Options

                  argumentInstructionstypeDefault value
                  xmlStrIncoming parameterstringnull
                  formatSet the format to be converted. The default is' text/xml '' DOMParserSupportedType 'null

                  Example

                  For example, when doing icon library, we need to dynamically import all the 'ICONS' in the directory. In this case, strings are imported, but strings cannot be added to 'xml'. So we need to convert the string to 'xml', and then we can add it to 'xml'.

                  js
                  import { str2Xml } from 'ranuts';
                  +import{_ as s,o as a,c as i,a3 as e}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"str2Xml","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/str2xml.md","filePath":"src/ranuts/utils/str2xml.md","lastUpdated":1728129344000}'),n={name:"src/ranuts/utils/str2xml.md"};function l(r,t,h,d,o,p){return a(),i("div",{"data-pagefind-body":!0},t[0]||(t[0]=[e(`

                  str2Xml

                  Pass in a string and convert it to 'xml'

                  API

                  Return

                  argumentInstructionstype
                  HTMLElement返回一个HTMLElementHTMLElement

                  Options

                  argumentInstructionstypeDefault value
                  xmlStrIncoming parameterstringnull
                  formatSet the format to be converted. The default is' text/xml '' DOMParserSupportedType 'null

                  Example

                  For example, when doing icon library, we need to dynamically import all the 'ICONS' in the directory. In this case, strings are imported, but strings cannot be added to 'xml'. So we need to convert the string to 'xml', and then we can add it to 'xml'.

                  js
                  import { str2Xml } from 'ranuts';
                   
                   // import 'assets/*.svg'
                   const svg = \`<svg t="1667483498347" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8544" width="200" height="200"><path d="M858.5 763.6c-18.9-44.8-46.1-85-80.6-119.5-34.5-34.5-74.7-61.6-119.5-80.6-0.4-0.2-0.8-0.3-1.2-0.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-0.4 0.2-0.8 0.3-1.2 0.5-44.8 18.9-85 46-119.5 80.6-34.5 34.5-61.6 74.7-80.6 119.5C146.9 807.5 137 854 136 901.8c-0.1 4.5 3.5 8.2 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c0.1 4.4 3.6 7.8 8 7.8h60c4.5 0 8.1-3.7 8-8.2-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z" p-id="8545"></path></svg>\`;
                  diff --git a/assets/src_ranuts_utils_task.md.BA-6r3zJ.js b/assets/src_ranuts_utils_task.md.DEROvLOz.js
                  similarity index 99%
                  rename from assets/src_ranuts_utils_task.md.BA-6r3zJ.js
                  rename to assets/src_ranuts_utils_task.md.DEROvLOz.js
                  index 2ee77488c8..05829963f4 100644
                  --- a/assets/src_ranuts_utils_task.md.BA-6r3zJ.js
                  +++ b/assets/src_ranuts_utils_task.md.DEROvLOz.js
                  @@ -1 +1 @@
                  -import{_ as t,o as a,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"Statistical execution time","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/task.md","filePath":"src/ranuts/utils/task.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return a(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  Statistical execution time

                  Sometimes, we need statistics on the execution time of a function to analyze performance. Therefore, the 'startTask' and 'taskEnd' functions are wrapped. Three other statistical methods are also introduced

                  1. new Date().getTime(),
                  2. console.time() , console.timeEnd(),
                  3. performance.now()

                  一.startTask,taskEnd

                  1.startTask

                  Execute before the task begins

                  Return

                  参数说明类型
                  taskId任务标识unique symbol

                  2.taskEnd

                  任务结束的时候执行,需要传入startTask返回的任务标识

                  Options

                  参数说明类型默认值
                  taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

                  Return

                  参数说明类型
                  timetask执行的时间number

                  3.使用例子

                  js
                  const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

                  二.new Date().getTime()

                  new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

                  1. 某些情况下,毫秒级精度可能不够。
                  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

                    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

                  三.console.time(), console.timeEnd()

                  启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

                  四.performance.now()

                  performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

                  注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

                  ',24)]))}const m=t(o,[["render",n]]);export{k as __pageData,m as default}; +import{_ as t,o as a,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"Statistical execution time","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/task.md","filePath":"src/ranuts/utils/task.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return a(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  Statistical execution time

                  Sometimes, we need statistics on the execution time of a function to analyze performance. Therefore, the 'startTask' and 'taskEnd' functions are wrapped. Three other statistical methods are also introduced

                  1. new Date().getTime(),
                  2. console.time() , console.timeEnd(),
                  3. performance.now()

                  一.startTask,taskEnd

                  1.startTask

                  Execute before the task begins

                  Return

                  参数说明类型
                  taskId任务标识unique symbol

                  2.taskEnd

                  任务结束的时候执行,需要传入startTask返回的任务标识

                  Options

                  参数说明类型默认值
                  taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

                  Return

                  参数说明类型
                  timetask执行的时间number

                  3.使用例子

                  js
                  const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

                  二.new Date().getTime()

                  new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

                  1. 某些情况下,毫秒级精度可能不够。
                  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

                    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

                  三.console.time(), console.timeEnd()

                  启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

                  四.performance.now()

                  performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

                  注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

                  ',24)]))}const m=t(o,[["render",n]]);export{k as __pageData,m as default}; diff --git a/assets/src_ranuts_utils_task.md.BA-6r3zJ.lean.js b/assets/src_ranuts_utils_task.md.DEROvLOz.lean.js similarity index 99% rename from assets/src_ranuts_utils_task.md.BA-6r3zJ.lean.js rename to assets/src_ranuts_utils_task.md.DEROvLOz.lean.js index 2ee77488c8..05829963f4 100644 --- a/assets/src_ranuts_utils_task.md.BA-6r3zJ.lean.js +++ b/assets/src_ranuts_utils_task.md.DEROvLOz.lean.js @@ -1 +1 @@ -import{_ as t,o as a,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"Statistical execution time","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/task.md","filePath":"src/ranuts/utils/task.md","lastUpdated":1728129125000}'),o={name:"src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return a(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  Statistical execution time

                  Sometimes, we need statistics on the execution time of a function to analyze performance. Therefore, the 'startTask' and 'taskEnd' functions are wrapped. Three other statistical methods are also introduced

                  1. new Date().getTime(),
                  2. console.time() , console.timeEnd(),
                  3. performance.now()

                  一.startTask,taskEnd

                  1.startTask

                  Execute before the task begins

                  Return

                  参数说明类型
                  taskId任务标识unique symbol

                  2.taskEnd

                  任务结束的时候执行,需要传入startTask返回的任务标识

                  Options

                  参数说明类型默认值
                  taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

                  Return

                  参数说明类型
                  timetask执行的时间number

                  3.使用例子

                  js
                  const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

                  二.new Date().getTime()

                  new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

                  1. 某些情况下,毫秒级精度可能不够。
                  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

                    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

                  三.console.time(), console.timeEnd()

                  启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

                  四.performance.now()

                  performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

                  注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

                  ',24)]))}const m=t(o,[["render",n]]);export{k as __pageData,m as default}; +import{_ as t,o as a,c as s,a3 as i}from"./chunks/framework.eq-HTtE3.js";const k=JSON.parse('{"title":"Statistical execution time","description":"","frontmatter":{},"headers":[],"relativePath":"src/ranuts/utils/task.md","filePath":"src/ranuts/utils/task.md","lastUpdated":1728129344000}'),o={name:"src/ranuts/utils/task.md"};function n(d,e,r,c,l,h){return a(),s("div",{"data-pagefind-body":!0},e[0]||(e[0]=[i('

                  Statistical execution time

                  Sometimes, we need statistics on the execution time of a function to analyze performance. Therefore, the 'startTask' and 'taskEnd' functions are wrapped. Three other statistical methods are also introduced

                  1. new Date().getTime(),
                  2. console.time() , console.timeEnd(),
                  3. performance.now()

                  一.startTask,taskEnd

                  1.startTask

                  Execute before the task begins

                  Return

                  参数说明类型
                  taskId任务标识unique symbol

                  2.taskEnd

                  任务结束的时候执行,需要传入startTask返回的任务标识

                  Options

                  参数说明类型默认值
                  taskId任务标识unique symbol 无默认值,参数必传,否则无法识别是哪个任务

                  Return

                  参数说明类型
                  timetask执行的时间number

                  3.使用例子

                  js
                  const taskId = startTask();\n\n// do something\n\nconst time = taskEnd(taskId);\n\nconsole.log('task 执行花费的时间', time);

                  二.new Date().getTime()

                  new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

                  1. 某些情况下,毫秒级精度可能不够。
                  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

                    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

                  三.console.time(), console.timeEnd()

                  启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

                  四.performance.now()

                  performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

                  注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

                  ',24)]))}const m=t(o,[["render",n]]);export{k as __pageData,m as default}; diff --git "a/assets/src_types_TS\347\261\273\345\236\213.md.BAdYqP2z.js" "b/assets/src_types_TS\347\261\273\345\236\213.md.Du9AK3lr.js" similarity index 99% rename from "assets/src_types_TS\347\261\273\345\236\213.md.BAdYqP2z.js" rename to "assets/src_types_TS\347\261\273\345\236\213.md.Du9AK3lr.js" index 97517cafd2..02258339dd 100644 --- "a/assets/src_types_TS\347\261\273\345\236\213.md.BAdYqP2z.js" +++ "b/assets/src_types_TS\347\261\273\345\236\213.md.Du9AK3lr.js" @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/TS类型.md","filePath":"src/types/TS类型.md","lastUpdated":1728129125000}'),l={name:"src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  TypeScript 类型系统中的类型

                  1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
                  2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
                  3. 特殊的类型:void、never、any、unknown

                  Tuple

                  元组(Tuple)就是元素个数和类型固定的数组类型:

                  ts
                  type Tuple = [number, string];

                  Interface

                  接口(Interface)可以描述函数、对象、构造器的结构:

                  • 对象
                  ts
                  interface IPerson {
                  +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/TS类型.md","filePath":"src/types/TS类型.md","lastUpdated":1728129344000}'),l={name:"src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  TypeScript 类型系统中的类型

                  1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
                  2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
                  3. 特殊的类型:void、never、any、unknown

                  Tuple

                  元组(Tuple)就是元素个数和类型固定的数组类型:

                  ts
                  type Tuple = [number, string];

                  Interface

                  接口(Interface)可以描述函数、对象、构造器的结构:

                  • 对象
                  ts
                  interface IPerson {
                     name: string;
                     age: number;
                   }
                  diff --git "a/assets/src_types_TS\347\261\273\345\236\213.md.BAdYqP2z.lean.js" "b/assets/src_types_TS\347\261\273\345\236\213.md.Du9AK3lr.lean.js"
                  similarity index 99%
                  rename from "assets/src_types_TS\347\261\273\345\236\213.md.BAdYqP2z.lean.js"
                  rename to "assets/src_types_TS\347\261\273\345\236\213.md.Du9AK3lr.lean.js"
                  index 97517cafd2..02258339dd 100644
                  --- "a/assets/src_types_TS\347\261\273\345\236\213.md.BAdYqP2z.lean.js"
                  +++ "b/assets/src_types_TS\347\261\273\345\236\213.md.Du9AK3lr.lean.js"
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/TS类型.md","filePath":"src/types/TS类型.md","lastUpdated":1728129125000}'),l={name:"src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  TypeScript 类型系统中的类型

                  1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
                  2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
                  3. 特殊的类型:void、never、any、unknown

                  Tuple

                  元组(Tuple)就是元素个数和类型固定的数组类型:

                  ts
                  type Tuple = [number, string];

                  Interface

                  接口(Interface)可以描述函数、对象、构造器的结构:

                  • 对象
                  ts
                  interface IPerson {
                  +import{_ as i,o as a,c as n,a3 as h}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/TS类型.md","filePath":"src/types/TS类型.md","lastUpdated":1728129344000}'),l={name:"src/types/TS类型.md"};function t(p,s,k,e,r,d){return a(),n("div",{"data-pagefind-body":!0},s[0]||(s[0]=[h(`

                  TypeScript 类型系统中的类型

                  1. 基本类型: number、boolean、string、object、bigint、symbol、undefined、null
                  2. 复合类型: class、Array、元组(Tuple)、接口(Interface)、枚举(Enum)
                  3. 特殊的类型:void、never、any、unknown

                  Tuple

                  元组(Tuple)就是元素个数和类型固定的数组类型:

                  ts
                  type Tuple = [number, string];

                  Interface

                  接口(Interface)可以描述函数、对象、构造器的结构:

                  • 对象
                  ts
                  interface IPerson {
                     name: string;
                     age: number;
                   }
                  diff --git "a/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.CFNrWo4G.js" "b/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DO7GM6xX.js"
                  similarity index 99%
                  rename from "assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.CFNrWo4G.js"
                  rename to "assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DO7GM6xX.js"
                  index 543d65362a..3f7465712e 100644
                  --- "a/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.CFNrWo4G.js"
                  +++ "b/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DO7GM6xX.js"
                  @@ -1 +1 @@
                  -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/模式匹配.md","filePath":"src/types/模式匹配.md","lastUpdated":1728129125000}'),k={name:"src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  模式匹配

                  Typescript 的类型也同样可以做模式匹配。

                  比如这样一个 Promise 类型:

                  ts
                  type p = Promise<'value'>;

                  我们想提取 value 的类型,可以这样做:

                  ts
                  type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

                  通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

                  ts
                  type result = GetPromiseValue<Promise<'name'>>; // name

                  数组类型

                  数组类型想提取第一个元素的类型怎么做呢?

                  ts
                  type arr = [1, 2, 3];

                  用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

                  ts
                  type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

                  类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

                  any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

                  对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type result = GetArrayFirstItem<[1, 2, 3]>; // 1

                  当类型参数 Arr 为 [] 时:

                  ts
                  type result = GetArrayFirstItem<[]>; // never

                  可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

                  ts
                  type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

                  我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

                  ts
                  type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

                  如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type Result = Pop<[1, 2, 3]>; // [1,2]

                  当类型参数 Arr 为 [] 时:

                  ts
                  type Result = Pop<[]>; // []

                  同理可得 ShiftArr 的实现:

                  ts
                  type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

                  字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

                  判断字符串是否以某个前缀开头,也是通过模式匹配:

                  ts
                  type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

                  需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

                  用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

                  字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

                  比如实现字符串替换:

                  ts
                  type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

                  声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

                  用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

                  用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

                  Trim

                  能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

                  不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

                  先实现 TrimRight:

                  ts
                  type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

                  类型参数 Str 是要 Trim 的字符串。

                  如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

                  把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

                  同理可得 TrimLeft:

                  ts
                  type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

                  TrimRight 和 TrimLeft 结合就是 Trim:

                  ts
                  type Trim<S extends string> = TrimLeft<TrimRight<S>>;

                  函数

                  函数同样也可以做类型匹配,比如提取参数、返回值的类型。

                  GetParameters

                  函数类型可以通过模式匹配来提取参数的类型:

                  ts
                  type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

                  类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

                  Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

                  返回提取到的参数类型 Args。

                  GetReturnType

                  ts
                  type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

                  Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

                  参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

                  GetThisParameterType

                  方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

                  但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

                  这里的 this 类型同样也可以通过模式匹配提取出来:

                  ts
                  type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

                  构造器

                  构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

                  同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

                  GetInstanceType

                  构造器类型可以用 interface 声明,使用 new(): xx 的语法。

                  ts
                  interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

                  这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

                  ts
                  type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
                  ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/模式匹配.md","filePath":"src/types/模式匹配.md","lastUpdated":1728129344000}'),k={name:"src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  模式匹配

                  Typescript 的类型也同样可以做模式匹配。

                  比如这样一个 Promise 类型:

                  ts
                  type p = Promise<'value'>;

                  我们想提取 value 的类型,可以这样做:

                  ts
                  type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

                  通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

                  ts
                  type result = GetPromiseValue<Promise<'name'>>; // name

                  数组类型

                  数组类型想提取第一个元素的类型怎么做呢?

                  ts
                  type arr = [1, 2, 3];

                  用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

                  ts
                  type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

                  类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

                  any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

                  对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type result = GetArrayFirstItem<[1, 2, 3]>; // 1

                  当类型参数 Arr 为 [] 时:

                  ts
                  type result = GetArrayFirstItem<[]>; // never

                  可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

                  ts
                  type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

                  我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

                  ts
                  type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

                  如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type Result = Pop<[1, 2, 3]>; // [1,2]

                  当类型参数 Arr 为 [] 时:

                  ts
                  type Result = Pop<[]>; // []

                  同理可得 ShiftArr 的实现:

                  ts
                  type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

                  字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

                  判断字符串是否以某个前缀开头,也是通过模式匹配:

                  ts
                  type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

                  需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

                  用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

                  字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

                  比如实现字符串替换:

                  ts
                  type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

                  声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

                  用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

                  用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

                  Trim

                  能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

                  不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

                  先实现 TrimRight:

                  ts
                  type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

                  类型参数 Str 是要 Trim 的字符串。

                  如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

                  把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

                  同理可得 TrimLeft:

                  ts
                  type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

                  TrimRight 和 TrimLeft 结合就是 Trim:

                  ts
                  type Trim<S extends string> = TrimLeft<TrimRight<S>>;

                  函数

                  函数同样也可以做类型匹配,比如提取参数、返回值的类型。

                  GetParameters

                  函数类型可以通过模式匹配来提取参数的类型:

                  ts
                  type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

                  类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

                  Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

                  返回提取到的参数类型 Args。

                  GetReturnType

                  ts
                  type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

                  Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

                  参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

                  GetThisParameterType

                  方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

                  但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

                  这里的 this 类型同样也可以通过模式匹配提取出来:

                  ts
                  type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

                  构造器

                  构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

                  同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

                  GetInstanceType

                  构造器类型可以用 interface 声明,使用 new(): xx 的语法。

                  ts
                  interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

                  这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

                  ts
                  type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
                  ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git "a/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.CFNrWo4G.lean.js" "b/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DO7GM6xX.lean.js" similarity index 99% rename from "assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.CFNrWo4G.lean.js" rename to "assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DO7GM6xX.lean.js" index 543d65362a..3f7465712e 100644 --- "a/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.CFNrWo4G.lean.js" +++ "b/assets/src_types_\346\250\241\345\274\217\345\214\271\351\205\215.md.DO7GM6xX.lean.js" @@ -1 +1 @@ -import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/模式匹配.md","filePath":"src/types/模式匹配.md","lastUpdated":1728129125000}'),k={name:"src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  模式匹配

                  Typescript 的类型也同样可以做模式匹配。

                  比如这样一个 Promise 类型:

                  ts
                  type p = Promise<'value'>;

                  我们想提取 value 的类型,可以这样做:

                  ts
                  type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

                  通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

                  ts
                  type result = GetPromiseValue<Promise<'name'>>; // name

                  数组类型

                  数组类型想提取第一个元素的类型怎么做呢?

                  ts
                  type arr = [1, 2, 3];

                  用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

                  ts
                  type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

                  类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

                  any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

                  对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type result = GetArrayFirstItem<[1, 2, 3]>; // 1

                  当类型参数 Arr 为 [] 时:

                  ts
                  type result = GetArrayFirstItem<[]>; // never

                  可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

                  ts
                  type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

                  我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

                  ts
                  type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

                  如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type Result = Pop<[1, 2, 3]>; // [1,2]

                  当类型参数 Arr 为 [] 时:

                  ts
                  type Result = Pop<[]>; // []

                  同理可得 ShiftArr 的实现:

                  ts
                  type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

                  字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

                  判断字符串是否以某个前缀开头,也是通过模式匹配:

                  ts
                  type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

                  需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

                  用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

                  字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

                  比如实现字符串替换:

                  ts
                  type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

                  声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

                  用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

                  用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

                  Trim

                  能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

                  不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

                  先实现 TrimRight:

                  ts
                  type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

                  类型参数 Str 是要 Trim 的字符串。

                  如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

                  把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

                  同理可得 TrimLeft:

                  ts
                  type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

                  TrimRight 和 TrimLeft 结合就是 Trim:

                  ts
                  type Trim<S extends string> = TrimLeft<TrimRight<S>>;

                  函数

                  函数同样也可以做类型匹配,比如提取参数、返回值的类型。

                  GetParameters

                  函数类型可以通过模式匹配来提取参数的类型:

                  ts
                  type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

                  类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

                  Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

                  返回提取到的参数类型 Args。

                  GetReturnType

                  ts
                  type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

                  Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

                  参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

                  GetThisParameterType

                  方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

                  但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

                  这里的 this 类型同样也可以通过模式匹配提取出来:

                  ts
                  type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

                  构造器

                  构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

                  同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

                  GetInstanceType

                  构造器类型可以用 interface 声明,使用 new(): xx 的语法。

                  ts
                  interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

                  这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

                  ts
                  type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
                  ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; +import{_ as i,o as a,c as h,a3 as t}from"./chunks/framework.eq-HTtE3.js";const F=JSON.parse('{"title":"模式匹配","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/模式匹配.md","filePath":"src/types/模式匹配.md","lastUpdated":1728129344000}'),k={name:"src/types/模式匹配.md"};function n(p,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[t('

                  模式匹配

                  Typescript 的类型也同样可以做模式匹配。

                  比如这样一个 Promise 类型:

                  ts
                  type p = Promise<'value'>;

                  我们想提取 value 的类型,可以这样做:

                  ts
                  type GetPromiseValue<T> = T extends Promise<infer value> ? value : never;

                  通过 extends 对传入的类型参数 P 做模式匹配,其中值的类型是需要提取的,通过 infer 声明一个局部变量 Value 来保存,如果匹配,就返回匹配到的 Value,否则就返回 never 代表没匹配到。

                  ts
                  type result = GetPromiseValue<Promise<'name'>>; // name

                  数组类型

                  数组类型想提取第一个元素的类型怎么做呢?

                  ts
                  type arr = [1, 2, 3];

                  用它来匹配一个模式类型,提取第一个元素的类型到通过 infer 声明的局部变量里返回。

                  ts
                  type GetArrayFirstItem<T extends unknown[]> = T extends [infer value, ...unknown[]] ? value : never;

                  类型参数 Arr 通过 extends 约束为只能是数组类型,数组元素是 unknown 也就是可以是任何值。

                  any 和 unknown 的区别: any 和 unknown 都代表任意类型,但是 unknown 只能接收任意类型的值,而 any 除了可以接收任意类型的值,也可以赋值给任意类型(除了 never)。类型体操中经常用 unknown 接受和匹配任何类型,而很少把任何类型赋值给某个类型变量。

                  对 Arr 做模式匹配,把我们要提取的第一个元素的类型放到通过 infer 声明的 First 局部变量里,后面的元素可以是任何类型,用 unknown 接收,然后把局部变量 First 返回。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type result = GetArrayFirstItem<[1, 2, 3]>; // 1

                  当类型参数 Arr 为 [] 时:

                  ts
                  type result = GetArrayFirstItem<[]>; // never

                  可以提取第一个元素,当然也可以提取最后一个元素,修改下模式类型就行:

                  ts
                  type GetArrayLastItem<T extends unknown[]> = T extends [...unknown[],inter L] ? L : never

                  我们分别取了首尾元素,当然也可以取剩余的数组,比如取去掉了最后一个元素的数组:

                  ts
                  type Pop<T extends unknown[]> = T extends [] ? [] : T extends [...infer Rest, unknown] ? Rest : never;

                  如果是空数组,就直接返回,否则匹配剩余的元素,放到 infer 声明的局部变量 Rest 里,返回 Rest。

                  当类型参数 Arr 为 [1,2,3] 时:

                  ts
                  type Result = Pop<[1, 2, 3]>; // [1,2]

                  当类型参数 Arr 为 [] 时:

                  ts
                  type Result = Pop<[]>; // []

                  同理可得 ShiftArr 的实现:

                  ts
                  type Shift<T extends unknown[]> = T extends [] ? [] : T extends [unknown, ...infer Rest] ? Rest : never;

                  字符串类型也同样可以做模式匹配,匹配一个模式字符串,把需要提取的部分放到 infer 声明的局部变量里。

                  判断字符串是否以某个前缀开头,也是通过模式匹配:

                  ts
                  type StartWidth<S extends string, P extends string> = S extends `${P}${string}` ? true : false;

                  需要声明字符串 Str、匹配的前缀 Prefix 两个类型参数,它们都是 string。

                  用 Str 去匹配一个模式类型,模式类型的前缀是 Prefix,后面是任意的 string,如果匹配返回 true,否则返回 false。

                  字符串可以匹配一个模式类型,提取想要的部分,自然也可以用这些再构成一个新的类型。

                  比如实现字符串替换:

                  ts
                  type Replace<S extends string, F extends string, T extends string> = S extends `${infer P}${F}${infer L}`\n  ? `${P}${T}${L}`\n  : S;

                  声明要替换的字符串 Str、待替换的字符串 From、替换成的字符串 3 个类型参数,通过 extends 约束为都是 string 类型。

                  用 Str 去匹配模式串,模式串由 From 和之前之后的字符串构成,把之前之后的字符串放到通过 infer 声明的局部变量 Prefix、Suffix 里。

                  用 Prefix、Suffix 加上替换到的字符串 To 构造成新的字符串类型返回。

                  Trim

                  能够匹配和替换字符串,那也就能实现去掉空白字符的 Trim:

                  不过因为我们不知道有多少个空白字符,所以只能一个个匹配和去掉,需要递归。

                  先实现 TrimRight:

                  ts
                  type TrimRight<S extends string> = S extends `${infer Rest}${' ' | '\\n' | '\\t'}` ? TrimRight<Rest> : S;

                  类型参数 Str 是要 Trim 的字符串。

                  如果 Str 匹配字符串 + 空白字符 (空格、换行、制表符),那就把字符串放到 infer 声明的局部变量 Rest 里。

                  把 Rest 作为类型参数递归 TrimRight,直到不匹配,这时的类型参数 Str 就是处理结果。

                  同理可得 TrimLeft:

                  ts
                  type TrimLeft<S extends string> = S extends `${' ' | '\\n' | '\\t'}${infer Rest}` ? TrimLeft<Rest> : S;

                  TrimRight 和 TrimLeft 结合就是 Trim:

                  ts
                  type Trim<S extends string> = TrimLeft<TrimRight<S>>;

                  函数

                  函数同样也可以做类型匹配,比如提取参数、返回值的类型。

                  GetParameters

                  函数类型可以通过模式匹配来提取参数的类型:

                  ts
                  type GetParameters<T extends Function> = T extends (...args: infer A) => unknown ? A : never;

                  类型参数 Func 是要匹配的函数类型,通过 extends 约束为 Function。

                  Func 和模式类型做匹配,参数类型放到用 infer 声明的局部变量 Args 里,返回值可以是任何类型,用 unknown。

                  返回提取到的参数类型 Args。

                  GetReturnType

                  ts
                  type GetReturnType<T extends Function> = T extends (...args: unknown[]) => infer R ? R : never;

                  Func 和模式类型做匹配,提取返回值到通过 infer 声明的局部变量 ReturnType 里返回。

                  参数类型可以是任意类型,也就是 any[](注意,这里不能用 unknown,这里的解释涉及到参数的逆变性质,具体原因逆变那一节会解释)。

                  GetThisParameterType

                  方法里可以调用 this,用对象.方法名的方式调用的时候,this 就指向那个对象。

                  但是方法也可以用 call 或者 apply 调用,call 调用的时候,this 就变了,但这里却没有被检查出来 this 指向的错误。

                  这里的 this 类型同样也可以通过模式匹配提取出来:

                  ts
                  type GetThisParameterType<T extends Function> = T extends (this: infer H, ...args: unknown[]) => unknown ? H : unknown;

                  构造器

                  构造器和函数的区别是,构造器是用于创建对象的,所以可以被 new。

                  同样,我们也可以通过模式匹配提取构造器的参数和返回值的类型:

                  GetInstanceType

                  构造器类型可以用 interface 声明,使用 new(): xx 的语法。

                  ts
                  interface Person {\n  name: string;\n}\n\ninterface PersonConstructor {\n  new (name: string): Person;\n}

                  这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

                  ts
                  type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T\n  ? T\n  : unknown;
                  ',79)]))}const y=i(k,[["render",n]]);export{F as __pageData,y as default}; diff --git "a/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DitU5zkD.js" "b/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.D_oUktxL.js" similarity index 99% rename from "assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DitU5zkD.js" rename to "assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.D_oUktxL.js" index 284028e04b..ab267d43fb 100644 --- "a/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DitU5zkD.js" +++ "b/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.D_oUktxL.js" @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/类型运算.md","filePath":"src/types/类型运算.md","lastUpdated":1728129125000}'),n={name:"src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  TypeScript 类型系统中的类型运算

                  条件:extends ? :

                  TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;
                  +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/类型运算.md","filePath":"src/types/类型运算.md","lastUpdated":1728129344000}'),n={name:"src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  TypeScript 类型系统中的类型运算

                  条件:extends ? :

                  TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;
                   
                   type res = isTwo<1>; // true
                   type res2 = isTwo<2>; // false

                  这种类型也叫做高级类型。

                  高级类型的特点是传入类型参数,经过一系列类型运算逻辑后,返回新的类型。

                  推导:infer

                  如何提取类型的一部分呢?答案是 infer。

                  比如提取元组类型的第一个元素:

                  ts
                  type FirstTupleItem<Tuple extends unknown[]> = Tuple extends [infer T, ...inter R] ? T : never;
                  diff --git "a/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DitU5zkD.lean.js" "b/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.D_oUktxL.lean.js"
                  similarity index 99%
                  rename from "assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DitU5zkD.lean.js"
                  rename to "assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.D_oUktxL.lean.js"
                  index 284028e04b..ab267d43fb 100644
                  --- "a/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.DitU5zkD.lean.js"
                  +++ "b/assets/src_types_\347\261\273\345\236\213\350\277\220\347\256\227.md.D_oUktxL.lean.js"
                  @@ -1,4 +1,4 @@
                  -import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/类型运算.md","filePath":"src/types/类型运算.md","lastUpdated":1728129125000}'),n={name:"src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  TypeScript 类型系统中的类型运算

                  条件:extends ? :

                  TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;
                  +import{_ as i,o as a,c as h,a3 as k}from"./chunks/framework.eq-HTtE3.js";const g=JSON.parse('{"title":"TypeScript 类型系统中的类型运算","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/类型运算.md","filePath":"src/types/类型运算.md","lastUpdated":1728129344000}'),n={name:"src/types/类型运算.md"};function p(t,s,l,e,r,d){return a(),h("div",{"data-pagefind-body":!0},s[0]||(s[0]=[k(`

                  TypeScript 类型系统中的类型运算

                  条件:extends ? :

                  TypeScript 里的条件判断是 extends ? :,叫做条件类型(Conditional Type)比如:

                  ts
                  type isTwo<T> = T extends 2 ? true : false;
                   
                   type res = isTwo<1>; // true
                   type res2 = isTwo<2>; // false

                  这种类型也叫做高级类型。

                  高级类型的特点是传入类型参数,经过一系列类型运算逻辑后,返回新的类型。

                  推导:infer

                  如何提取类型的一部分呢?答案是 infer。

                  比如提取元组类型的第一个元素:

                  ts
                  type FirstTupleItem<Tuple extends unknown[]> = Tuple extends [infer T, ...inter R] ? T : never;
                  diff --git "a/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BcsCXIM4.js" "b/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.xpfmnY7g.js"
                  similarity index 98%
                  rename from "assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BcsCXIM4.js"
                  rename to "assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.xpfmnY7g.js"
                  index c79af61885..cec5d35aad 100644
                  --- "a/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BcsCXIM4.js"
                  +++ "b/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.xpfmnY7g.js"
                  @@ -1 +1 @@
                  -import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/高级类型.md","filePath":"src/types/高级类型.md","lastUpdated":1728129125000}'),o={name:"src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  TypeScript 内置的高级类型

                  Parameters

                  Parameters 用于提取函数类型的参数类型。

                  ReturnType

                  ReturnType 用于提取函数类型的返回值类型。

                  ConstructorParameters

                  构造器类型和函数类型的区别就是可以被 new。

                  Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

                  InstanceType

                  提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

                  ThisParameterType

                  OmitThisParameter

                  Partial

                  Required

                  Readonly

                  Pick

                  Record

                  Exclude

                  Extract

                  Omit

                  Awaited

                  NonNullable

                  Uppercase

                  Lowercase

                  Capitalize

                  Uncapitalize

                  总结

                  比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

                  用模式匹配 + 重新构造可以实现:OmitThisParameter

                  用重新构造可以实现:Partial、Required、Readonly、Pick、Record

                  用模式匹配 + 递归可以实现: Awaited

                  用联合类型在分布式条件类型的特性可以实现: Exclude

                  此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

                  ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; +import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/高级类型.md","filePath":"src/types/高级类型.md","lastUpdated":1728129344000}'),o={name:"src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  TypeScript 内置的高级类型

                  Parameters

                  Parameters 用于提取函数类型的参数类型。

                  ReturnType

                  ReturnType 用于提取函数类型的返回值类型。

                  ConstructorParameters

                  构造器类型和函数类型的区别就是可以被 new。

                  Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

                  InstanceType

                  提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

                  ThisParameterType

                  OmitThisParameter

                  Partial

                  Required

                  Readonly

                  Pick

                  Record

                  Exclude

                  Extract

                  Omit

                  Awaited

                  NonNullable

                  Uppercase

                  Lowercase

                  Capitalize

                  Uncapitalize

                  总结

                  比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

                  用模式匹配 + 重新构造可以实现:OmitThisParameter

                  用重新构造可以实现:Partial、Required、Readonly、Pick、Record

                  用模式匹配 + 递归可以实现: Awaited

                  用联合类型在分布式条件类型的特性可以实现: Exclude

                  此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

                  ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; diff --git "a/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BcsCXIM4.lean.js" "b/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.xpfmnY7g.lean.js" similarity index 98% rename from "assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BcsCXIM4.lean.js" rename to "assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.xpfmnY7g.lean.js" index c79af61885..cec5d35aad 100644 --- "a/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.BcsCXIM4.lean.js" +++ "b/assets/src_types_\351\253\230\347\272\247\347\261\273\345\236\213.md.xpfmnY7g.lean.js" @@ -1 +1 @@ -import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/高级类型.md","filePath":"src/types/高级类型.md","lastUpdated":1728129125000}'),o={name:"src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  TypeScript 内置的高级类型

                  Parameters

                  Parameters 用于提取函数类型的参数类型。

                  ReturnType

                  ReturnType 用于提取函数类型的返回值类型。

                  ConstructorParameters

                  构造器类型和函数类型的区别就是可以被 new。

                  Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

                  InstanceType

                  提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

                  ThisParameterType

                  OmitThisParameter

                  Partial

                  Required

                  Readonly

                  Pick

                  Record

                  Exclude

                  Extract

                  Omit

                  Awaited

                  NonNullable

                  Uppercase

                  Lowercase

                  Capitalize

                  Uncapitalize

                  总结

                  比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

                  用模式匹配 + 重新构造可以实现:OmitThisParameter

                  用重新构造可以实现:Partial、Required、Readonly、Pick、Record

                  用模式匹配 + 递归可以实现: Awaited

                  用联合类型在分布式条件类型的特性可以实现: Exclude

                  此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

                  ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; +import{_ as e,o as r,c as t,a3 as i}from"./chunks/framework.eq-HTtE3.js";const u=JSON.parse('{"title":"TypeScript 内置的高级类型","description":"","frontmatter":{},"headers":[],"relativePath":"src/types/高级类型.md","filePath":"src/types/高级类型.md","lastUpdated":1728129344000}'),o={name:"src/types/高级类型.md"};function l(n,a,h,c,s,d){return r(),t("div",{"data-pagefind-body":!0},a[0]||(a[0]=[i('

                  TypeScript 内置的高级类型

                  Parameters

                  Parameters 用于提取函数类型的参数类型。

                  ReturnType

                  ReturnType 用于提取函数类型的返回值类型。

                  ConstructorParameters

                  构造器类型和函数类型的区别就是可以被 new。

                  Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

                  InstanceType

                  提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

                  ThisParameterType

                  OmitThisParameter

                  Partial

                  Required

                  Readonly

                  Pick

                  Record

                  Exclude

                  Extract

                  Omit

                  Awaited

                  NonNullable

                  Uppercase

                  Lowercase

                  Capitalize

                  Uncapitalize

                  总结

                  比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

                  用模式匹配 + 重新构造可以实现:OmitThisParameter

                  用重新构造可以实现:Partial、Required、Readonly、Pick、Record

                  用模式匹配 + 递归可以实现: Awaited

                  用联合类型在分布式条件类型的特性可以实现: Exclude

                  此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

                  ',33)]))}const m=e(o,[["render",l]]);export{u as __pageData,m as default}; diff --git a/cn/index.html b/cn/index.html index af4c2c1b4a..34b85f7a73 100644 --- a/cn/index.html +++ b/cn/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - +
                  Skip to content

                  ran

                  风起于青萍之末

                  A ship in harbor is safe, but that is not what ships are built for.

                  logo

                  Released under the MIT License.

                  - + \ No newline at end of file diff --git a/cn/src/article/astParse/tokenizer.html b/cn/src/article/astParse/tokenizer.html index 61382eef22..d5f88eda77 100644 --- a/cn/src/article/astParse/tokenizer.html +++ b/cn/src/article/astParse/tokenizer.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -226,8 +226,8 @@ { type: 'RightParen', value: ')', start: 17, end: 18 }, { type: 'LeftCurly', value: '{', start: 19, end: 20 }, { type: 'RightCurly', value: '}', start: 20, end: 21 }, -];

                  一个简易版本的分词器已经被我们开发出来了,不过目前的分词器还比较简陋,仅仅支持有限的语法,不过在明确了核心的开发步骤之后,后面继续完善的过程就比较简单了。

                  四。编写语法分析器(Parser)

                  在解析出词法 token 之后,我们就可以进入语法分析阶段了。在这个阶段,我们会依次遍历 token ,对代码进行语法结构层面的分析,最后的目标是生成 AST 数据结构。至于代码的 AST 结构到底是什么样子,你可以去 AST Explorer 网站进行在线预览:

                  接下来,我们要做的就是将 token 数组转换为上图所示的 AST 数据。

                  开发步骤主要分为:

                  • 初始化类型声明

                  Released under the MIT License.

                  - +];

                  一个简易版本的分词器已经被我们开发出来了,不过目前的分词器还比较简陋,仅仅支持有限的语法,不过在明确了核心的开发步骤之后,后面继续完善的过程就比较简单了。

                  四。编写语法分析器(Parser)

                  在解析出词法 token 之后,我们就可以进入语法分析阶段了。在这个阶段,我们会依次遍历 token ,对代码进行语法结构层面的分析,最后的目标是生成 AST 数据结构。至于代码的 AST 结构到底是什么样子,你可以去 AST Explorer 网站进行在线预览:

                  接下来,我们要做的就是将 token 数组转换为上图所示的 AST 数据。

                  开发步骤主要分为:

                  • 初始化类型声明

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/babel.html b/cn/src/article/babel.html index 32a8160b1c..b05e0cc539 100644 --- a/cn/src/article/babel.html +++ b/cn/src/article/babel.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
                  Skip to content

                  Babel

                  babel 核心库主要是:

                  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
                  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
                  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
                  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
                  • @babel/code-frame 可以创建友好的报错信息
                  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
                  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。

                  Released under the MIT License.

                  - +
                  Skip to content

                  Babel

                  babel 核心库主要是:

                  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
                  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
                  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
                  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
                  • @babel/code-frame 可以创建友好的报错信息
                  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
                  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/bundle.html b/cn/src/article/bundle.html index 69024f62f8..87ebbfa262 100644 --- a/cn/src/article/bundle.html +++ b/cn/src/article/bundle.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
                  Skip to content

                  Bundle

                  Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:

                  • 编译能力
                  • 插件机制
                  • HMR
                  • cli 和命令行能力

                  Released under the MIT License.

                  - +
                  Skip to content

                  Bundle

                  Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:

                  • 编译能力
                  • 插件机制
                  • HMR
                  • cli 和命令行能力

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/designMode.html b/cn/src/article/designMode.html index 6d7efcbcc0..c33da7e611 100644 --- a/cn/src/article/designMode.html +++ b/cn/src/article/designMode.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -794,8 +794,8 @@ Visitor.push(a,1,2,3,4); Visitor.push(a,4,5,6); Visitor.pop(a); -Visitor.splice(a,2);

                  访问者模式解决了数据与数据的操作方法之间的耦合,让数据的操作方法独立于数据,使其可以自由演变。因此,访问者模式更适合于那些数据稳定、但数据的操作方法易变的环境下。

                  优点:

                  • 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。
                  • 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
                  • 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。

                  缺点:

                  • 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”的要求。
                  • 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。

                  总结

                  系统地学习设计模式后,你可以在过往的开发经历中发现,设计模式是无处不在的。在学习设计模式之前的很多时候我们是凭借过往经验和智慧来完善系统的设计,而这些经验很多和某个设计模式的思想不谋而合。

                  还有一些地方没有完全理解,文中有误之处还望不吝指出。

                  参考资料

                  Released under the MIT License.

                  - +Visitor.splice(a,2);

                  访问者模式解决了数据与数据的操作方法之间的耦合,让数据的操作方法独立于数据,使其可以自由演变。因此,访问者模式更适合于那些数据稳定、但数据的操作方法易变的环境下。

                  优点:

                  • 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。
                  • 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
                  • 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。

                  缺点:

                  • 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”的要求。
                  • 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。

                  总结

                  系统地学习设计模式后,你可以在过往的开发经历中发现,设计模式是无处不在的。在学习设计模式之前的很多时候我们是凭借过往经验和智慧来完善系统的设计,而这些经验很多和某个设计模式的思想不谋而合。

                  还有一些地方没有完全理解,文中有误之处还望不吝指出。

                  参考资料

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/docPreview.html b/cn/src/article/docPreview.html index 636b2f202a..57f7bc7ab4 100644 --- a/cn/src/article/docPreview.html +++ b/cn/src/article/docPreview.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -304,8 +304,8 @@ public static void main(String[] args) { convertToPDF("/Users/koolearn/Desktop/asdf.docx", "/Users/koolearn/Desktop/adsf.pdf"); } -}

                  2.kkFileView

                  github地址:https://github.com/kekingcn/kkFileView

                  支持的文件预览格式非常丰富 image.png

                  接下来是 从零到一 的启动步骤,按着步骤来,任何人都能搞定

                  1. 安装java:
                  sh
                  brew install java
                  1. 安装maven,java的包管理工具:
                  sh
                  brew install mvn
                  1. 检查是否安装成功

                  执行java --versionmvn -v。我这里遇到mvn找不到java home的报错。解决方式如下:

                  我用的是zsh,所以需要去.zshrc添加路径:

                  export JAVA_HOME=$(/usr/libexec/java_home)

                  添加完后,执行

                  source .zshrc
                  1. 安装下libreoffice :

                  kkFileView明确要求的额外依赖,否则无法启动

                  brew install libreoffice
                  1. mvn安装依赖

                  进入项目,在根目录执行依赖安装,同时清理缓存,跳过单测 (遇到了单测报错的问题)

                  mvn clean install -DskipTests
                  1. 启动项目

                  找到主文件,主函数mian,点击vscode上面的Run即可执行,路径如下图

                  image.png

                  1. 访问页面

                  启动完成后,点击终端输出的地址

                  image.png

                  1. 最终结果

                  最终展示如下,可以添加链接进行预览,也可以选择本地文件进行预览

                  image.png

                  预览效果非常好

                  3.onlyOffice

                  官网地址:https://www.onlyoffice.com/zh

                  github地址:https://github.com/ONLYOFFICE

                  开发者版本和社区版免费,企业版付费:https://www.onlyoffice.com/zh/docs-enterprise-prices.aspx

                  预览的文件种类没有kkFileView多,但对office三件套有很好的支持,甚至支持多人编辑。

                  四:总结

                  1. 外部服务,推荐微软的view.officeapps.live.com/op/view.aspx,但只建议预览一些互联网公开的文件,不建议使用在要求保密性和稳定性的文件。
                  2. 对保密性和稳定性有要求,且不差钱的,可以试试大厂服务,阿里云解决方案。
                  3. 服务端技术比较给力的,使用服务端预览方案。目前最好最全的效果是服务端预览方案。
                  4. 不想花钱,没有服务器的,使用前端预览方案,客户端渲染零成本。

                  五:参考文档:

                  1. 在 java 中如何使用 openOffice 进行格式转换
                  2. MAC 搭建 OpenOffice 完整教程 - 保姆级
                  3. 纯 js 实现 docx、xlsx、pdf 文件预览库,使用超简单
                  4. 前端实现 word、excel、pdf、ppt、mp4、图片、文本等文件的预览

                  Released under the MIT License.

                  - +}

                  2.kkFileView

                  github地址:https://github.com/kekingcn/kkFileView

                  支持的文件预览格式非常丰富 image.png

                  接下来是 从零到一 的启动步骤,按着步骤来,任何人都能搞定

                  1. 安装java:
                  sh
                  brew install java
                  1. 安装maven,java的包管理工具:
                  sh
                  brew install mvn
                  1. 检查是否安装成功

                  执行java --versionmvn -v。我这里遇到mvn找不到java home的报错。解决方式如下:

                  我用的是zsh,所以需要去.zshrc添加路径:

                  export JAVA_HOME=$(/usr/libexec/java_home)

                  添加完后,执行

                  source .zshrc
                  1. 安装下libreoffice :

                  kkFileView明确要求的额外依赖,否则无法启动

                  brew install libreoffice
                  1. mvn安装依赖

                  进入项目,在根目录执行依赖安装,同时清理缓存,跳过单测 (遇到了单测报错的问题)

                  mvn clean install -DskipTests
                  1. 启动项目

                  找到主文件,主函数mian,点击vscode上面的Run即可执行,路径如下图

                  image.png

                  1. 访问页面

                  启动完成后,点击终端输出的地址

                  image.png

                  1. 最终结果

                  最终展示如下,可以添加链接进行预览,也可以选择本地文件进行预览

                  image.png

                  预览效果非常好

                  3.onlyOffice

                  官网地址:https://www.onlyoffice.com/zh

                  github地址:https://github.com/ONLYOFFICE

                  开发者版本和社区版免费,企业版付费:https://www.onlyoffice.com/zh/docs-enterprise-prices.aspx

                  预览的文件种类没有kkFileView多,但对office三件套有很好的支持,甚至支持多人编辑。

                  四:总结

                  1. 外部服务,推荐微软的view.officeapps.live.com/op/view.aspx,但只建议预览一些互联网公开的文件,不建议使用在要求保密性和稳定性的文件。
                  2. 对保密性和稳定性有要求,且不差钱的,可以试试大厂服务,阿里云解决方案。
                  3. 服务端技术比较给力的,使用服务端预览方案。目前最好最全的效果是服务端预览方案。
                  4. 不想花钱,没有服务器的,使用前端预览方案,客户端渲染零成本。

                  五:参考文档:

                  1. 在 java 中如何使用 openOffice 进行格式转换
                  2. MAC 搭建 OpenOffice 完整教程 - 保姆级
                  3. 纯 js 实现 docx、xlsx、pdf 文件预览库,使用超简单
                  4. 前端实现 word、excel、pdf、ppt、mp4、图片、文本等文件的预览

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/functionalProgramming.html b/cn/src/article/functionalProgramming.html index e7a3af412a..ad5bf34114 100644 --- a/cn/src/article/functionalProgramming.html +++ b/cn/src/article/functionalProgramming.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -379,8 +379,8 @@ }; let r = readFile('package.json') //这里可以用 map 去处理内容 .flatMap(print) - .join();

                  参考资料

                  Released under the MIT License.

                  - + .join();

                  参考资料

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/imagemin.html b/cn/src/article/imagemin.html index 55d0e997ab..1e633a07f0 100644 --- a/cn/src/article/imagemin.html +++ b/cn/src/article/imagemin.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + - - + + \ No newline at end of file diff --git a/cn/src/article/javascript/domLoad.html b/cn/src/article/javascript/domLoad.html index 906f7c8766..4bb9b354b4 100644 --- a/cn/src/article/javascript/domLoad.html +++ b/cn/src/article/javascript/domLoad.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -44,8 +44,8 @@
                  Skip to content

                  页面加载完成后事件

                  window.onload

                  DOMContentLoaded

                  js
                  document.addEventListener('DOMContentLoaded', fun);

                  <body onload="fun()">

                  readyState

                  js
                  document.readyState;
                   
                  -document.onreadystatechange;

                  一个文档的 readyState 可以是以下之一:

                  • loading / 加载。document 仍在加载。
                  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
                  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。

                  Released under the MIT License.

                  - +document.onreadystatechange;

                  一个文档的 readyState 可以是以下之一:

                  • loading / 加载。document 仍在加载。
                  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
                  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/sort/bubble/index.html b/cn/src/article/sort/bubble/index.html index 48752d7e99..ffba83ce01 100644 --- a/cn/src/article/sort/bubble/index.html +++ b/cn/src/article/sort/bubble/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -55,8 +55,8 @@ } } return list; -};

                  Released under the MIT License.

                  - +};

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/sort/bucket/index.html b/cn/src/article/sort/bucket/index.html index 1233df4cbf..d08f7f7950 100644 --- a/cn/src/article/sort/bucket/index.html +++ b/cn/src/article/sort/bucket/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -98,8 +98,8 @@ list = list.concat(count(buckets[i])); } return list; -};

                  算法分析

                  桶排序最好情况下使用线性时间 O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为 O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

                  Released under the MIT License.

                  - +};

                  算法分析

                  桶排序最好情况下使用线性时间 O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为 O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/sort/count/index.html b/cn/src/article/sort/count/index.html index 76f210fc57..f87fce51ad 100644 --- a/cn/src/article/sort/count/index.html +++ b/cn/src/article/sort/count/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -76,8 +76,8 @@ } } return result; -};

                  算法分析

                  计数排序是一个稳定的排序算法。当输入的元素是 n 个 0 到 k 之间的整数时,时间复杂度是 O(n+k),空间复杂度也是 O(n+k),其排序速度快于任何比较排序算法。当 k 不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。

                  Released under the MIT License.

                  - +};

                  算法分析

                  计数排序是一个稳定的排序算法。当输入的元素是 n 个 0 到 k 之间的整数时,时间复杂度是 O(n+k),空间复杂度也是 O(n+k),其排序速度快于任何比较排序算法。当 k 不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。

                  Released under the MIT License.

                  + \ No newline at end of file diff --git a/cn/src/article/sort/heap/index.html b/cn/src/article/sort/heap/index.html index be772966b9..00eb4a13d1 100644 --- a/cn/src/article/sort/heap/index.html +++ b/cn/src/article/sort/heap/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -92,8 +92,8 @@ const heap = (list: Array<number>): Array<number> => { const { value } = new Heap(list); return value; -};

                Released under the MIT License.

            - +};

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/index.html b/cn/src/article/sort/index.html index c0c878129d..3b9ae65541 100644 --- a/cn/src/article/sort/index.html +++ b/cn/src/article/sort/index.html @@ -13,7 +13,7 @@ - + @@ -37,14 +37,14 @@ - + -
          Skip to content

          十大经典排序

          十种常见排序算法可以分为两大类:

          • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
          • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

          排序分类

          算法复杂度

          算法复杂度

          相关概念

          • 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
          • 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
          • 时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。
          • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。

          Released under the MIT License.

          - +
          Skip to content

          十大经典排序

          十种常见排序算法可以分为两大类:

          • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。
          • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

          排序分类

          算法复杂度

          算法复杂度

          相关概念

          • 稳定:如果 a 原本在 b 前面,而 a=b,排序之后 a 仍然在 b 的前面。
          • 不稳定:如果 a 原本在 b 的前面,而 a=b,排序之后 a 可能会出现在 b 的后面。
          • 时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。
          • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/insert/index.html b/cn/src/article/sort/insert/index.html index 068453c635..dcbba46e7a 100644 --- a/cn/src/article/sort/insert/index.html +++ b/cn/src/article/sort/insert/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -55,8 +55,8 @@ list[preIndex + 1] = current; } return list; -};

          算法分析

          插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

          Released under the MIT License.

          - +};

          算法分析

          插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/merge/index.html b/cn/src/article/sort/merge/index.html index 7cefdef9c4..95b756d3d6 100644 --- a/cn/src/article/sort/merge/index.html +++ b/cn/src/article/sort/merge/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -75,8 +75,8 @@ const left = list.slice(0, middle); const right = list.slice(middle); return combine(merge(left), merge(right)); -};

          算法分析

          归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn)的时间复杂度。代价是需要额外的内存空间。

          Released under the MIT License.

          - +};

          算法分析

          归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn)的时间复杂度。代价是需要额外的内存空间。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/quick/index.html b/cn/src/article/sort/quick/index.html index 6f685ad2b6..38f4a33ddb 100644 --- a/cn/src/article/sort/quick/index.html +++ b/cn/src/article/sort/quick/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -93,8 +93,8 @@ const quick = (list: number[] = []): number[] => { const size = list.length; return combine(list, 0, size - 1); -};

          Released under the MIT License.

          - +};

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/radix/index.html b/cn/src/article/sort/radix/index.html index 6b3703192f..d23f2b0a96 100644 --- a/cn/src/article/sort/radix/index.html +++ b/cn/src/article/sort/radix/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -78,8 +78,8 @@ list = list.concat(count(buckets[i])); } return list; -};

          算法分析

          基数排序基于分配排序,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要 O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要 O(n)的时间复杂度。假如待排数据可以分为 d 个关键字,则基数排序的时间复杂度将是 O(d*2n) ,当然 d 要远远小于 n,因此基本上还是线性级别的。

          基数排序的空间复杂度为 O(n+k),其中 k 为桶的数量。一般来说 n>>k,因此额外空间需要大概 n 个左右。

          Released under the MIT License.

          - +};

          算法分析

          基数排序基于分配排序,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要 O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要 O(n)的时间复杂度。假如待排数据可以分为 d 个关键字,则基数排序的时间复杂度将是 O(d*2n) ,当然 d 要远远小于 n,因此基本上还是线性级别的。

          基数排序的空间复杂度为 O(n+k),其中 k 为桶的数量。一般来说 n>>k,因此额外空间需要大概 n 个左右。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/select/index.html b/cn/src/article/sort/select/index.html index 3be33db8e8..e4331718f5 100644 --- a/cn/src/article/sort/select/index.html +++ b/cn/src/article/sort/select/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -59,8 +59,8 @@ } } return list; -};

          算法分析

          表现最稳定的排序算法之一,因为无论什么数据进去都是 O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。

          Released under the MIT License.

          - +};

          算法分析

          表现最稳定的排序算法之一,因为无论什么数据进去都是 O(n2)的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/sort/shell/index.html b/cn/src/article/sort/shell/index.html index 5a9f2c48f1..850ac88f6b 100644 --- a/cn/src/article/sort/shell/index.html +++ b/cn/src/article/sort/shell/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -62,8 +62,8 @@ } } return list; -};

          算法分析

          希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。动态定义间隔序列的算法是《算法(第 4 版)》的合著者 Robert Sedgewick 提出的。

          Released under the MIT License.

          - +};

          算法分析

          希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。动态定义间隔序列的算法是《算法(第 4 版)》的合著者 Robert Sedgewick 提出的。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/systemDesign.html b/cn/src/article/systemDesign.html index 835fe622fe..97f8f9d951 100644 --- a/cn/src/article/systemDesign.html +++ b/cn/src/article/systemDesign.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
          Skip to content

          如何处理一个系统设计

          系统设计是一个开放式的对话。他们期望你去主导这个对话。

          你可以使用下面的步骤来指引讨论。为了巩固这个过程,请使用下面的步骤完成系统设计的面试题和解答这个章节。

          第一步:描述使用场景,约束和假设

          把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。

          谁会使用它? 他们会怎样使用它? 有多少用户? 系统的作用是什么? 系统的输入输出分别是什么? 我们希望处理多少数据? 我们希望每秒钟处理多少请求? 我们希望的读写比率?

          第二步:创造一个高层级的设计

          使用所有重要的组件来描绘出一个高层级的设计。

          画出主要的组件和连接 证明你的想法

          第三步:设计核心组件

          对每一个核心组件进行详细深入的分析。举例来说,如果你被问到设计一个 url 缩写服务,开始讨论:

          生成并储存一个完整 url 的 hash MD5 和 Base62 Hash 碰撞 SQL 还是 NoSQL 数据库模型 将一个 hashed url 翻译成完整的 url 数据库查找 API 和面向对象设计

          第四步:扩展设计

          确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成扩展性的议题吗?

          负载均衡 水平扩展 缓存 数据库分片 论述可能的解决办法和代价。每件事情需要取舍。可以使用可扩展系统的设计原则来处理瓶颈。

          Released under the MIT License.

          - +
          Skip to content

          如何处理一个系统设计

          系统设计是一个开放式的对话。他们期望你去主导这个对话。

          你可以使用下面的步骤来指引讨论。为了巩固这个过程,请使用下面的步骤完成系统设计的面试题和解答这个章节。

          第一步:描述使用场景,约束和假设

          把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。

          谁会使用它? 他们会怎样使用它? 有多少用户? 系统的作用是什么? 系统的输入输出分别是什么? 我们希望处理多少数据? 我们希望每秒钟处理多少请求? 我们希望的读写比率?

          第二步:创造一个高层级的设计

          使用所有重要的组件来描绘出一个高层级的设计。

          画出主要的组件和连接 证明你的想法

          第三步:设计核心组件

          对每一个核心组件进行详细深入的分析。举例来说,如果你被问到设计一个 url 缩写服务,开始讨论:

          生成并储存一个完整 url 的 hash MD5 和 Base62 Hash 碰撞 SQL 还是 NoSQL 数据库模型 将一个 hashed url 翻译成完整的 url 数据库查找 API 和面向对象设计

          第四步:扩展设计

          确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成扩展性的议题吗?

          负载均衡 水平扩展 缓存 数据库分片 论述可能的解决办法和代价。每件事情需要取舍。可以使用可扩展系统的设计原则来处理瓶颈。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/typescript/calculate.html b/cn/src/article/typescript/calculate.html index a4257ebf09..1bbfd23dcd 100644 --- a/cn/src/article/typescript/calculate.html +++ b/cn/src/article/typescript/calculate.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -72,8 +72,8 @@ ? CurrentArr['length'] : FibonacciLoop<CurrentArr, [...PrevArr, ...CurrentArr], [...IndexArr, unknown], Num>; -type Fibonacci<Num extends number> = FibonacciLoop<[1], [], [], Num>;

          类型参数 PrevArr 是代表之前的累加值的数组。类型参数 CurrentArr 是代表当前数值的数组。

          类型参数 IndexArr 用于记录 index,每次递归加一,默认值是 [],代表从 0 开始。

          类型参数 Num 代表求数列的第几个数。

          判断当前 index 也就是 IndexArr['length'] 是否到了 Num,到了就返回当前的数值 CurrentArr['length']。

          否则求出当前 index 对应的数值,用之前的数加上当前的数 [...PrevArr, ... CurrentArr]。

          然后继续递归,index + 1,也就是 [...IndexArr, unknown]。

          这就是递归计算 Fibinacci 数列的数的过程。

          可以正确的算出第 8 个数是 21:

          Released under the MIT License.

          - +type Fibonacci<Num extends number> = FibonacciLoop<[1], [], [], Num>;

          类型参数 PrevArr 是代表之前的累加值的数组。类型参数 CurrentArr 是代表当前数值的数组。

          类型参数 IndexArr 用于记录 index,每次递归加一,默认值是 [],代表从 0 开始。

          类型参数 Num 代表求数列的第几个数。

          判断当前 index 也就是 IndexArr['length'] 是否到了 Num,到了就返回当前的数值 CurrentArr['length']。

          否则求出当前 index 对应的数值,用之前的数加上当前的数 [...PrevArr, ... CurrentArr]。

          然后继续递归,index + 1,也就是 [...IndexArr, unknown]。

          这就是递归计算 Fibinacci 数列的数的过程。

          可以正确的算出第 8 个数是 21:

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/typescript/index.html b/cn/src/article/typescript/index.html index 4faa25e475..bf945ac43e 100644 --- a/cn/src/article/typescript/index.html +++ b/cn/src/article/typescript/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -197,8 +197,8 @@ type TestAnyResult = TestAny<any>; // type TestAnyResult = 1 | 2

          联合类型、never、any 在作为条件类型的类型参数时的这些特殊情况,也会在后面的原理篇来解释原因。

          IsTuple

          元组类型怎么判断呢?它和数组有什么区别呢?

          元组类型的 length 是数字字面量,而数组的 length 是 number。

          ts
          type len

          UnionToIntersection

          类型之间是有父子关系的,更具体的那个是子类型,比如 A 和 B 的交叉类型 A & B 就是联合类型 A | B 的子类型,因为更具体。

          如果允许父类型赋值给子类型,就叫做逆变

          如果允许子类型赋值给父类型,就叫做协变

          (关于逆变、协变等概念的详细解释可以看原理篇)

          在 TypeScript 中有函数参数是有逆变的性质的,也就是如果参数可能是多个类型,参数类型会变成它们的交叉类型。

          所以联合转交叉可以这样实现 :

          ts
          type UnionToIntersection<U> = (U extends U ? (x: U) => unknown : never) extends (x: infer R) => unknown ? R : never;

          类型参数 U 是要转换的联合类型。

          U extends U 是为了触发联合类型的 distributive 的性质,让每个类型单独传入做计算,最后合并。

          利用 U 做为参数构造个函数,通过模式匹配取参数的类型。

          结果就是交叉类型

          函数参数的逆变性质一般就联合类型转交叉类型会用,记住就行。

          GetOptional

          如何提取索引类型中的可选索引呢?

          这也要利用可选索引的特性:可选索引的值为 undefined 和值类型的联合类型。

          过滤可选索引,就要构造一个新的索引类型,过程中做过滤:

          ts
          type GetOptional<Obj extends Record<string, any>> = {
             [Key in keyof Obj as {} extends Pick<Obj, Key> ? Key : never]: Obj[Key];
          -};

          类型参数 Obj 为待处理的索引类型,类型约束为索引为 string、值为任意类型的索引类型 Record<string, any>。

          用映射类型的语法重新构造索引类型,索引是之前的索引也就是 Key in keyof Obj,但要做一些过滤,也就是 as 之后的部分。

          过滤的方式就是单独取出该索引之后,判断空对象是否是其子类型。

          这里的 Pick 是 ts 提供的内置高级类型,就是取出某个 Key 构造新的索引类型:

          ts
          type Pick<T, K extends keyof T> = { [P in K]: T[P] };

          比如单独取出 age 构造的新的索引类型是这样的:

          可选的意思是这个索引可能没有,没有的时候,那 Pick<Obj, Key> 就是空的,所以 {} extends Pick<Obj, Key> 就能过滤出可选索引。

          值的类型依然是之前的,也就是 Obj[Key]。

          这样,就能过滤出所有可选索引,构造成新的索引类型:

          总结

          • any 类型与任何类型的交叉都是 any,也就是 1 & any 结果是 any,可以用这个特性判断 any 类型。
          • 联合类型作为类型参数出现在条件类型左侧时,会分散成单个类型传入,最后合并。
          • never 作为类型参数出现在条件类型左侧时,会直接返回 never。
          • any 作为类型参数出现在条件类型左侧时,会直接返回 trueType 和 falseType 的联合类型。
          • 元组类型也是数组类型,但 length 是数字字面量,而数组的 length 是 number。可以用来判断元组类型。
          • 函数参数处会发生逆变,可以用来实现联合类型转交叉类型。
          • 可选索引的索引可能没有,那 Pick 出来的就可能是 {},可以用来过滤可选索引,反过来也可以过滤非可选索引。
          • 索引类型的索引为字符串字面量类型,而可索引签名不是,可以用这个特性过滤掉可索引签名。
          • keyof 只能拿到 class 的 public 的索引,可以用来过滤出 public 的属性。
          • 默认推导出来的不是字面量类型,加上 as const 可以推导出字面量类型,但带有 readonly 修饰,这样模式匹配的时候也得加上 readonly 才行。

          Released under the MIT License.

          - +};

          类型参数 Obj 为待处理的索引类型,类型约束为索引为 string、值为任意类型的索引类型 Record<string, any>。

          用映射类型的语法重新构造索引类型,索引是之前的索引也就是 Key in keyof Obj,但要做一些过滤,也就是 as 之后的部分。

          过滤的方式就是单独取出该索引之后,判断空对象是否是其子类型。

          这里的 Pick 是 ts 提供的内置高级类型,就是取出某个 Key 构造新的索引类型:

          ts
          type Pick<T, K extends keyof T> = { [P in K]: T[P] };

          比如单独取出 age 构造的新的索引类型是这样的:

          可选的意思是这个索引可能没有,没有的时候,那 Pick<Obj, Key> 就是空的,所以 {} extends Pick<Obj, Key> 就能过滤出可选索引。

          值的类型依然是之前的,也就是 Obj[Key]。

          这样,就能过滤出所有可选索引,构造成新的索引类型:

          总结

          • any 类型与任何类型的交叉都是 any,也就是 1 & any 结果是 any,可以用这个特性判断 any 类型。
          • 联合类型作为类型参数出现在条件类型左侧时,会分散成单个类型传入,最后合并。
          • never 作为类型参数出现在条件类型左侧时,会直接返回 never。
          • any 作为类型参数出现在条件类型左侧时,会直接返回 trueType 和 falseType 的联合类型。
          • 元组类型也是数组类型,但 length 是数字字面量,而数组的 length 是 number。可以用来判断元组类型。
          • 函数参数处会发生逆变,可以用来实现联合类型转交叉类型。
          • 可选索引的索引可能没有,那 Pick 出来的就可能是 {},可以用来过滤可选索引,反过来也可以过滤非可选索引。
          • 索引类型的索引为字符串字面量类型,而可索引签名不是,可以用这个特性过滤掉可索引签名。
          • keyof 只能拿到 class 的 public 的索引,可以用来过滤出 public 的属性。
          • 默认推导出来的不是字面量类型,加上 as const 可以推导出字面量类型,但带有 readonly 修饰,这样模式匹配的时候也得加上 readonly 才行。

          Released under the MIT License.

          + \ No newline at end of file diff --git a/cn/src/article/typescript/pattern.html b/cn/src/article/typescript/pattern.html index c4d470ba6c..c62b830728 100644 --- a/cn/src/article/typescript/pattern.html +++ b/cn/src/article/typescript/pattern.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -134,8 +134,8 @@ : never : never;

          类型参数 Props 为待处理的类型。

          通过 keyof Props 取出 Props 的所有索引构成的联合类型,判断下 ref 是否在其中,也就是 'ref' extends keyof Props。

          为什么要做这个判断,上面注释里写了:

          在 ts3.0 里面如果没有对应的索引,Obj[Key] 返回的是 {} 而不是 never,所以这样做下兼容处理。

          如果有 ref 这个索引的话,就通过 infer 提取 Value 的类型返回,否则返回 never。

          ts
          type GetPropsRefResult = GetPropsRef<{ ref: 1; name: 'str' }>;
           // type GetPropsRefResult = 1

          当 ref 为 undefined 时:

          ts
          type GetPropsRefResult = GetPropsRef<{ ref: undefined; name: 'str' }>;
          -// type GetPropsRefResult = undefined

          Released under the MIT License.

          - +// type GetPropsRefResult = undefined

        Released under the MIT License.

    + \ No newline at end of file diff --git a/cn/src/article/typescript/reconstruction.html b/cn/src/article/typescript/reconstruction.html index 552f4c8665..a6001ffecc 100644 --- a/cn/src/article/typescript/reconstruction.html +++ b/cn/src/article/typescript/reconstruction.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -89,8 +89,8 @@ [Key in keyof T]-?: T[Key]; };

给索引类型 T 的索引去掉 ? 的修饰 ,其余保持不变。

FilterByValueType

可以在构造新索引类型的时候根据值的类型做下过滤:

ts
type FilterByValueType<Obj extends Record<string, any>, ValueType> = {
   [Key in keyof Obj as Obj[Key] extends ValueType ? Key : never]: Obj[Key];
-};

类型参数 Obj 为要处理的索引类型,通过 extends 约束为索引为 string,值为任意类型的索引类型 Record<string, any>。

类型参数 ValueType 为要过滤出的值的类型。

构造新的索引类型,索引为 Obj 的索引,也就是 Key in keyof Obj,但要做一些变换,也就是 as 之后的部分。

如果原来索引的值 Obj[Key] 是 ValueType 类型,索引依然为之前的索引 Key,否则索引设置为 never,never 的索引会在生成新的索引类型时被去掉。

值保持不变,依然为原来索引的值,也就是 Obj[Key]。

这样就达到了过滤索引类型的索引,产生新的索引类型的目的:

- +};

类型参数 Obj 为要处理的索引类型,通过 extends 约束为索引为 string,值为任意类型的索引类型 Record<string, any>。

类型参数 ValueType 为要过滤出的值的类型。

构造新的索引类型,索引为 Obj 的索引,也就是 Key in keyof Obj,但要做一些变换,也就是 as 之后的部分。

如果原来索引的值 Obj[Key] 是 ValueType 类型,索引依然为之前的索引 Key,否则索引设置为 never,never 的索引会在生成新的索引类型时被去掉。

值保持不变,依然为原来索引的值,也就是 Obj[Key]。

这样就达到了过滤索引类型的索引,产生新的索引类型的目的:

+ \ No newline at end of file diff --git a/cn/src/article/typescript/recursion.html b/cn/src/article/typescript/recursion.html index 2a36ba9bac..8392933944 100644 --- a/cn/src/article/typescript/recursion.html +++ b/cn/src/article/typescript/recursion.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -106,8 +106,8 @@ : DeepReadonly<Obj[Key]> : Obj[Key]; } - : never; - + : never; + \ No newline at end of file diff --git a/cn/src/article/typescript/unionType.html b/cn/src/article/typescript/unionType.html index 6b9af8cd74..de0d7a79e0 100644 --- a/cn/src/article/typescript/unionType.html +++ b/cn/src/article/typescript/unionType.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -62,8 +62,8 @@ > = `${Block}__${Element[number]}--${Modifiers[number]}`;

类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

ts
type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;
 // type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

可以看到,用好了联合类型,确实能简化类型编程逻辑。

AllCombinations

我们再来实现一个全组合的高级类型,也是联合类型相关的:

希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

任何两个类型的组合有四种:A、B、AB、BA

ts
type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

然后构造出来的字符串再和其他字符串组合。

所以全组合的高级类型就是这样:

ts
type AllCombinations<A extends string, B extends string = A> = A extends A
   ? Combination<A, AllCombinations<Exclude<B, A>>>
-  : never;

类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

总结

联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

有两点特别要注意:

我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

- + : never;

类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

总结

联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

有两点特别要注意:

我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

+ \ No newline at end of file diff --git a/cn/src/article/video.html b/cn/src/article/video.html index 06689d8f46..1aded2bf01 100644 --- a/cn/src/article/video.html +++ b/cn/src/article/video.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -326,8 +326,8 @@ display: none; }

由于播放器本身就是一个元素,那么可以任意的在里面添加元素,添加逻辑。

jsx
<r-player onChange={change} src="hls/example.m3u8">
   <div>111111</div>
-</r-player>

所以,这就解决配置项,长达好几页的问题,同时看到也就知道怎么配置,怎么开发了。

八:总结

目前已经从前后端的角度,实现了

  1. 视频的标准加密
  2. 视频的动态码率播放
  3. 视频的分片加载
  4. 可拖拽进度条
  5. 音量控制
  6. 手动清晰度切换
  7. 倍速播放
  8. 样式自定义覆盖
  9. 基于原生开发,可在所有框架运行,统一跨框架情况,各浏览器控件统一

这是demo和源码地址:

demo和文档地址:https://chaxus.github.io/ran/src/ranui/player/

源码地址:https://github.com/chaxus/ran

demo文档做了国际化,可切换到中文

- +</r-player>

所以,这就解决配置项,长达好几页的问题,同时看到也就知道怎么配置,怎么开发了。

八:总结

目前已经从前后端的角度,实现了

  1. 视频的标准加密
  2. 视频的动态码率播放
  3. 视频的分片加载
  4. 可拖拽进度条
  5. 音量控制
  6. 手动清晰度切换
  7. 倍速播放
  8. 样式自定义覆盖
  9. 基于原生开发,可在所有框架运行,统一跨框架情况,各浏览器控件统一

这是demo和源码地址:

demo和文档地址:https://chaxus.github.io/ran/src/ranui/player/

源码地址:https://github.com/chaxus/ran

demo文档做了国际化,可切换到中文

+ \ No newline at end of file diff --git a/cn/src/article/visual.html b/cn/src/article/visual.html index 75027916b1..e2fb5e28ab 100644 --- a/cn/src/article/visual.html +++ b/cn/src/article/visual.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -184,8 +184,8 @@ if (lineStyle.visible) { ctx.globalAlpha = lineStyle.alpha * this.worldAlpha; ctx.stroke(); -}

三 层级管理

在 canvas 绘图环境中,先绘制的图形会被后绘制的图形所覆盖,因此,层级的管理就自然地通过绘制顺序来实现。在这种情况下,最先被绘制的图形将位于最底层,而随后绘制的图形则逐层叠加,直至最上层。

我们已经实现了各种基础图形的绘制,接下来继续分离绘制时的数据和绘制操作。

我们需要一个容器类,在容器类中统一绘制所有的图形。

用一个 children 属性来添加所有的图形,在 render 方法中统一绘制。

同时,我们会根据 zIndex 属性来排序子元素,zIndex 越大的元素会被排在 children 数组的越后面,但是注意,排序的时候,要保证相同 zIndex 的相对顺序不变。

四 图形组的管理

我们会实现一个节点类 Vertex 这个类代表了最原始的‘节点’的概念,所有可以被展示到 canvas 画布上的、各种类型的节点都会继承于这个类,这是一个抽象类,我们并不会直接实例化这个类。

这个类上面挂载了‘节点’的各种属性,比如:父元素、透明度、旋转角度、缩放、平移、节点是否可见等。

为了进行图形组的管理,会继续实现一个容器类 Container,这个类代表了‘组’的概念,它提供了添加子元素,移除子元素等的方法;后续的要被渲染的一些类 (如 Graphics,Text,Sprite 等) 会继承于这个类;这个类本身不会被渲染 (因为它只是一个‘组’,它本身没有内容可以渲染)。

这个类继承于 Vertex 类,‘组’也算作‘节点’。

Graphics 这个类会用来构建一些几何图形元素;它会继承 Container 类。

在渲染引擎中,一切变换 (平移、旋转、缩放等) 都会转化成变换矩阵 (matrix),因为 canvas 只接受矩阵变换,虽然 canvas 为了开发的便捷,也提供了 ctx.rotate,ctx.scale 等操作,但是 canvas 中的这些操作会直接转换成变换矩阵,而不像 DOM 那样,有锚点的概念,所以 canvas 提供的 rotate,scale 等操作,和 DOM 提供的 rotate,scale 的表现是不一样的。

Matrix 类将会提供各种各样的与矩阵操作相关的函数 (矩阵相乘,矩阵求逆等),任何变换的叠加都将会转换成 matrix,方便我们调用 canvas 的指令。

Transform 类就类似 CSS 的 transform,它提供了一些更清晰、更符合人类直觉的变换,而不用直接使用矩阵变换,当然,这些变换最终会转换成矩阵变换。

参考资料:

  1. 如何通俗地讲解「仿射变换」?
  2. 仿射变换及其变换矩阵的理解
  3. 深入理解贝塞尔曲线
  4. 如何理解并应用贝塞尔曲线
- +}

三 层级管理

在 canvas 绘图环境中,先绘制的图形会被后绘制的图形所覆盖,因此,层级的管理就自然地通过绘制顺序来实现。在这种情况下,最先被绘制的图形将位于最底层,而随后绘制的图形则逐层叠加,直至最上层。

我们已经实现了各种基础图形的绘制,接下来继续分离绘制时的数据和绘制操作。

我们需要一个容器类,在容器类中统一绘制所有的图形。

用一个 children 属性来添加所有的图形,在 render 方法中统一绘制。

同时,我们会根据 zIndex 属性来排序子元素,zIndex 越大的元素会被排在 children 数组的越后面,但是注意,排序的时候,要保证相同 zIndex 的相对顺序不变。

四 图形组的管理

我们会实现一个节点类 Vertex 这个类代表了最原始的‘节点’的概念,所有可以被展示到 canvas 画布上的、各种类型的节点都会继承于这个类,这是一个抽象类,我们并不会直接实例化这个类。

这个类上面挂载了‘节点’的各种属性,比如:父元素、透明度、旋转角度、缩放、平移、节点是否可见等。

为了进行图形组的管理,会继续实现一个容器类 Container,这个类代表了‘组’的概念,它提供了添加子元素,移除子元素等的方法;后续的要被渲染的一些类 (如 Graphics,Text,Sprite 等) 会继承于这个类;这个类本身不会被渲染 (因为它只是一个‘组’,它本身没有内容可以渲染)。

这个类继承于 Vertex 类,‘组’也算作‘节点’。

Graphics 这个类会用来构建一些几何图形元素;它会继承 Container 类。

在渲染引擎中,一切变换 (平移、旋转、缩放等) 都会转化成变换矩阵 (matrix),因为 canvas 只接受矩阵变换,虽然 canvas 为了开发的便捷,也提供了 ctx.rotate,ctx.scale 等操作,但是 canvas 中的这些操作会直接转换成变换矩阵,而不像 DOM 那样,有锚点的概念,所以 canvas 提供的 rotate,scale 等操作,和 DOM 提供的 rotate,scale 的表现是不一样的。

Matrix 类将会提供各种各样的与矩阵操作相关的函数 (矩阵相乘,矩阵求逆等),任何变换的叠加都将会转换成 matrix,方便我们调用 canvas 的指令。

Transform 类就类似 CSS 的 transform,它提供了一些更清晰、更符合人类直觉的变换,而不用直接使用矩阵变换,当然,这些变换最终会转换成矩阵变换。

参考资料:

  1. 如何通俗地讲解「仿射变换」?
  2. 仿射变换及其变换矩阵的理解
  3. 深入理解贝塞尔曲线
  4. 如何理解并应用贝塞尔曲线
+ \ No newline at end of file diff --git a/cn/src/note/centos.html b/cn/src/note/centos.html index 392b07fdac..ad80b84b0b 100644 --- a/cn/src/note/centos.html +++ b/cn/src/note/centos.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -63,8 +63,8 @@ baseurl=http://vault.centos.org/8.5.2111/extras/$basearch/os/ gpgcheck=1 enabled=1 -gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

更新缓存并重试

  1. 清理缓存:yum clean all
  2. 生成新的缓存:yum makecache
  3. 更新系统:yum update
- +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

更新缓存并重试

  1. 清理缓存:yum clean all
  2. 生成新的缓存:yum makecache
  3. 更新系统:yum update
+ \ No newline at end of file diff --git a/cn/src/note/docker.html b/cn/src/note/docker.html index 8f9596067d..3e85fe7c44 100644 --- a/cn/src/note/docker.html +++ b/cn/src/note/docker.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -108,8 +108,8 @@ EXPOSR 3000 # 表示容器启动后自动执行 CMD node demos/01.js
shell
$ docker container run --rm - p 8000:3000 -it koa-demo:0.0.1

问题:

  1. 如果遇到 docker desktop 登录不上,可以尝试 docker login进行登录,命令行登录会输出错误的信息,如果发现是网络问题,换一个代理即可。
  2. Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
shell
# docker服务没有启动
-$ service docker start

参考资料

- +$ service docker start

参考资料

+ \ No newline at end of file diff --git a/cn/src/note/libreoffice2wasm.html b/cn/src/note/libreoffice2wasm.html index 5496c7e040..9705f72dad 100644 --- a/cn/src/note/libreoffice2wasm.html +++ b/cn/src/note/libreoffice2wasm.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -110,8 +110,8 @@ --disable-odk --disable-pch --disable-skia ---disable-scripting

通过这些步骤,你应该能够显著减小生成的 WebAssembly 文件的体积,同时保留将文件转换为 PDF 的功能。如果遇到具体问题,请提供详细的错误信息,以便更好地帮助你解决问题。

wasm-opt 是 Binaryen 项目的一部分,它是一个用于优化和处理 WebAssembly 二进制文件的工具。你可以在 Ubuntu 上通过以下步骤安装和使用 wasm-opt:

  1. 安装依赖项 首先,确保你的系统上已经安装了必要的依赖项:

sudo apt update sudo apt install cmake build-essential git 2. 下载和编译 Binaryen 接下来,从源码编译 Binaryen:

克隆 Binaryen 仓库

git clone https://github.com/WebAssembly/binaryen.git cd binaryen

创建并进入构建目录

mkdir build cd build

使用 CMake 配置构建

cmake ..

编译 Binaryen

make

安装 Binaryen

sudo make install 3. 使用 wasm-opt 安装完成后,你可以使用 wasm-opt 命令来优化 WebAssembly 文件。例如:

假设你有一个名为 original.wasm 的 WebAssembly 文件

wasm-opt -Oz -o optimized.wasm original.wasm 在这个命令中:

-Oz 表示进行最大化的尺寸优化。 -o optimized.wasm 指定输出文件名为 optimized.wasm。 original.wasm 是输入的 WebAssembly 文件。4. 验证安装 你可以通过运行以下命令来验证 wasm-opt 是否正确安装:

wasm-opt --version 这将输出 wasm-opt 的版本信息,确认它已成功安装并可以使用。

示例 假设你有一个名为 example.wasm 的 WebAssembly 文件,你可以通过以下命令优化它:

wasm-opt -Oz -o example_optimized.wasm example.wasm 这将生成一个优化后的 WebAssembly 文件 example_optimized.wasm,其体积通常会显著减小。

通过这些步骤,你应该能够在 Ubuntu 上成功安装和使用 wasm-opt 来优化你的 WebAssembly 文件。如果在安装或使用过程中遇到任何问题,请提供详细的错误信息,以便进一步帮助你解决问题。

--disable-debug --enable-sal-log --disable-crashdump --host=wasm32-local-emscripten --disable-gui --with-main-module=writer --with-package-format=emscripten --disable-dbus --disable-odk --disable-postgresql-sdbc --disable-firebird-sdbc --disable-coinmp --disable-cve-tests --disable-gtk3 --disable-gstreamer-1-0 --disable-kf5 --disable-scripting-beanshell --disable-scripting-javascript --disable-extensions --disable-epm --disable-online-update --disable-python --disable-pdfimport --disable-lpsolve --disable-sdremote --disable-scripting --disable-avmedia --disable-coinmp --disable-report-builder --disable-odk --disable-pch --disable-skia --disable-scripting

步骤 1:下载最新的 OpenSSL 源代码 首先,确保你在一个干净的目录中,然后下载最新版本的 OpenSSL 源代码。你可以访问 OpenSSL 官方网站 查看最新版本。假设最新版本是 openssl-3.0.9:

wget https://www.openssl.org/source/openssl-3.0.9.tar.gz tar -xzvf openssl-3.0.9.tar.gz cd openssl-3.0.9 步骤 2:编译和安装 OpenSSL 接下来,编译并安装 OpenSSL:

./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib make make test make install 步骤 3:确认 OpenSSL 安装路径 确保 OpenSSL 的头文件和库文件已正确安装:

ls /usr/local/ssl/include ls /usr/local/ssl/lib 步骤 4:设置环境变量 设置正确的环境变量,以便编译时能找到 OpenSSL:

export PKG_CONFIG_PATH="/usr/local/ssl/lib/pkgconfig" export CFLAGS="-I/usr/local/ssl/include" export LDFLAGS="-L/usr/local/ssl/lib -lssl -lcrypto" 步骤 5:清理之前的配置 在重新运行 configure 脚本之前,清理之前的配置:

make clean 步骤 6:重新运行 configure 脚本 重新运行 configure 脚本,以确保它能找到新的 OpenSSL 版本:

emconfigure ./configure --disable-debug --enable-sal-log --disable-crashdump --host=wasm32-local-emscripten --disable-gui --with-main-module=writer --with-package-format=emscripten --disable-dbus --disable-odk --disable-postgresql-sdbc --disable-firebird-sdbc --disable-coinmp --disable-cve-tests --disable-gtk3 --disable-gstreamer-1-0 --disable-kf5 --disable-scripting-beanshell --disable-scripting-javascript --disable-extensions --disable-epm --disable-online-update --disable-python --disable-pdfimport --disable-lpsolve --disable-sdremote --disable-scripting --disable-avmedia --disable-coinmp --disable-report-builder --disable-odk --disable-pch --disable-skia --disable-scripting --srcdir=/home/core --enable-option-checking=fatal --disable-pthreads 步骤 7:检查 config.log 如果配置仍然失败,请检查 config.log 文件以获取更多详细信息:

cat config.log | grep openssl 通过确保 OpenSSL 已正确安装并设置正确的环境变量,我们应该能够解决配置过程中遇到的问题。如果问题仍然存在,请提供 config.log 文件中与 OpenSSL 相关的更多详细信息,以便进一步诊断和解决问题。

emconfigure ./configure --disable-debug --enable-sal-log --disable-crashdump --host=wasm32-local-emscripten --disable-gui --with-main-module=writer --with-package-format=emscripten --disable-dbus --disable-odk --disable-postgresql-sdbc --disable-firebird-sdbc --disable-coinmp --disable-cve-tests --disable-gtk3 --disable-gstreamer-1-0 --disable-kf5 --disable-scripting-beanshell --disable-scripting-javascript --disable-extensions --disable-epm --disable-online-update --disable-python --disable-pdfimport --disable-lpsolve --disable-sdremote --disable-scripting --disable-avmedia --disable-coinmp --disable-report-builder --disable-odk --disable-pch --disable-skia --disable-scripting --srcdir=/home/core --enable-option-checking=fatal

emmake make CXXFLAGS="-I/usr/local/ssl/include -O3 -g0 -msimd128" LDFLAGS="-L/usr/local/ssl/lib -lssl -lcrypto -O3 -g0"

emconfigure ./configure --disable-cups --disable-dbus --without-system-fontconfig --with-system-zlib --disable-dynamic-loading --disable-gui CXXFLAGS=-std=c++20 --host=wasm32-unknown-emscripten

root@8cb3480a4441:/home/core/instsetoo_native# vim CustomTarget_emscripten-install.mk

使用 file_packager.py 工具将文件预加载到虚拟文件系统中。确保 Emscripten 的环境变量已经正确设置,然后运行以下命令:

python3 /home/emsdk/upstream/emscripten/tools/file_packager.py preload.data --preload /home/core/instdir/share@/instdir/share --js-output=preload.js

- +--disable-scripting

通过这些步骤,你应该能够显著减小生成的 WebAssembly 文件的体积,同时保留将文件转换为 PDF 的功能。如果遇到具体问题,请提供详细的错误信息,以便更好地帮助你解决问题。

wasm-opt 是 Binaryen 项目的一部分,它是一个用于优化和处理 WebAssembly 二进制文件的工具。你可以在 Ubuntu 上通过以下步骤安装和使用 wasm-opt:

  1. 安装依赖项 首先,确保你的系统上已经安装了必要的依赖项:

sudo apt update sudo apt install cmake build-essential git 2. 下载和编译 Binaryen 接下来,从源码编译 Binaryen:

克隆 Binaryen 仓库

git clone https://github.com/WebAssembly/binaryen.git cd binaryen

创建并进入构建目录

mkdir build cd build

使用 CMake 配置构建

cmake ..

编译 Binaryen

make

安装 Binaryen

sudo make install 3. 使用 wasm-opt 安装完成后,你可以使用 wasm-opt 命令来优化 WebAssembly 文件。例如:

假设你有一个名为 original.wasm 的 WebAssembly 文件

wasm-opt -Oz -o optimized.wasm original.wasm 在这个命令中:

-Oz 表示进行最大化的尺寸优化。 -o optimized.wasm 指定输出文件名为 optimized.wasm。 original.wasm 是输入的 WebAssembly 文件。4. 验证安装 你可以通过运行以下命令来验证 wasm-opt 是否正确安装:

wasm-opt --version 这将输出 wasm-opt 的版本信息,确认它已成功安装并可以使用。

示例 假设你有一个名为 example.wasm 的 WebAssembly 文件,你可以通过以下命令优化它:

wasm-opt -Oz -o example_optimized.wasm example.wasm 这将生成一个优化后的 WebAssembly 文件 example_optimized.wasm,其体积通常会显著减小。

通过这些步骤,你应该能够在 Ubuntu 上成功安装和使用 wasm-opt 来优化你的 WebAssembly 文件。如果在安装或使用过程中遇到任何问题,请提供详细的错误信息,以便进一步帮助你解决问题。

--disable-debug --enable-sal-log --disable-crashdump --host=wasm32-local-emscripten --disable-gui --with-main-module=writer --with-package-format=emscripten --disable-dbus --disable-odk --disable-postgresql-sdbc --disable-firebird-sdbc --disable-coinmp --disable-cve-tests --disable-gtk3 --disable-gstreamer-1-0 --disable-kf5 --disable-scripting-beanshell --disable-scripting-javascript --disable-extensions --disable-epm --disable-online-update --disable-python --disable-pdfimport --disable-lpsolve --disable-sdremote --disable-scripting --disable-avmedia --disable-coinmp --disable-report-builder --disable-odk --disable-pch --disable-skia --disable-scripting

步骤 1:下载最新的 OpenSSL 源代码 首先,确保你在一个干净的目录中,然后下载最新版本的 OpenSSL 源代码。你可以访问 OpenSSL 官方网站 查看最新版本。假设最新版本是 openssl-3.0.9:

wget https://www.openssl.org/source/openssl-3.0.9.tar.gz tar -xzvf openssl-3.0.9.tar.gz cd openssl-3.0.9 步骤 2:编译和安装 OpenSSL 接下来,编译并安装 OpenSSL:

./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib make make test make install 步骤 3:确认 OpenSSL 安装路径 确保 OpenSSL 的头文件和库文件已正确安装:

ls /usr/local/ssl/include ls /usr/local/ssl/lib 步骤 4:设置环境变量 设置正确的环境变量,以便编译时能找到 OpenSSL:

export PKG_CONFIG_PATH="/usr/local/ssl/lib/pkgconfig" export CFLAGS="-I/usr/local/ssl/include" export LDFLAGS="-L/usr/local/ssl/lib -lssl -lcrypto" 步骤 5:清理之前的配置 在重新运行 configure 脚本之前,清理之前的配置:

make clean 步骤 6:重新运行 configure 脚本 重新运行 configure 脚本,以确保它能找到新的 OpenSSL 版本:

emconfigure ./configure --disable-debug --enable-sal-log --disable-crashdump --host=wasm32-local-emscripten --disable-gui --with-main-module=writer --with-package-format=emscripten --disable-dbus --disable-odk --disable-postgresql-sdbc --disable-firebird-sdbc --disable-coinmp --disable-cve-tests --disable-gtk3 --disable-gstreamer-1-0 --disable-kf5 --disable-scripting-beanshell --disable-scripting-javascript --disable-extensions --disable-epm --disable-online-update --disable-python --disable-pdfimport --disable-lpsolve --disable-sdremote --disable-scripting --disable-avmedia --disable-coinmp --disable-report-builder --disable-odk --disable-pch --disable-skia --disable-scripting --srcdir=/home/core --enable-option-checking=fatal --disable-pthreads 步骤 7:检查 config.log 如果配置仍然失败,请检查 config.log 文件以获取更多详细信息:

cat config.log | grep openssl 通过确保 OpenSSL 已正确安装并设置正确的环境变量,我们应该能够解决配置过程中遇到的问题。如果问题仍然存在,请提供 config.log 文件中与 OpenSSL 相关的更多详细信息,以便进一步诊断和解决问题。

emconfigure ./configure --disable-debug --enable-sal-log --disable-crashdump --host=wasm32-local-emscripten --disable-gui --with-main-module=writer --with-package-format=emscripten --disable-dbus --disable-odk --disable-postgresql-sdbc --disable-firebird-sdbc --disable-coinmp --disable-cve-tests --disable-gtk3 --disable-gstreamer-1-0 --disable-kf5 --disable-scripting-beanshell --disable-scripting-javascript --disable-extensions --disable-epm --disable-online-update --disable-python --disable-pdfimport --disable-lpsolve --disable-sdremote --disable-scripting --disable-avmedia --disable-coinmp --disable-report-builder --disable-odk --disable-pch --disable-skia --disable-scripting --srcdir=/home/core --enable-option-checking=fatal

emmake make CXXFLAGS="-I/usr/local/ssl/include -O3 -g0 -msimd128" LDFLAGS="-L/usr/local/ssl/lib -lssl -lcrypto -O3 -g0"

emconfigure ./configure --disable-cups --disable-dbus --without-system-fontconfig --with-system-zlib --disable-dynamic-loading --disable-gui CXXFLAGS=-std=c++20 --host=wasm32-unknown-emscripten

root@8cb3480a4441:/home/core/instsetoo_native# vim CustomTarget_emscripten-install.mk

使用 file_packager.py 工具将文件预加载到虚拟文件系统中。确保 Emscripten 的环境变量已经正确设置,然后运行以下命令:

python3 /home/emsdk/upstream/emscripten/tools/file_packager.py preload.data --preload /home/core/instdir/share@/instdir/share --js-output=preload.js

+ \ No newline at end of file diff --git a/cn/src/note/ubuntu.html b/cn/src/note/ubuntu.html index f6480c1c1c..0de8eb5369 100644 --- a/cn/src/note/ubuntu.html +++ b/cn/src/note/ubuntu.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -44,8 +44,8 @@
Skip to content

ubuntu

sh
apt update
 
-apt install openjdk-11-jdk  wget curl junit4 ant libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgtk-3-dev libglib2.0-dev libatk1.0-dev libcairo2-dev zip flex uuid-runtime bison libxrandr-dev libxrender-dev libxext-dev libnss3-dev libnspr4-dev libkrb5-dev python3 python3-pip libxml2-utils xsltproc libxslt1-dev gperf libfontconfig1-dev libcups2-dev gcc make autoconf pkg-config automake

Last updated:

Released under the MIT License.

- +apt install openjdk-11-jdk wget curl junit4 ant libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgtk-3-dev libglib2.0-dev libatk1.0-dev libcairo2-dev zip flex uuid-runtime bison libxrandr-dev libxrender-dev libxext-dev libnss3-dev libnspr4-dev libkrb5-dev python3 python3-pip libxml2-utils xsltproc libxslt1-dev gperf libfontconfig1-dev libcups2-dev gcc make autoconf pkg-config automake + \ No newline at end of file diff --git a/cn/src/ranui/button/index.html b/cn/src/ranui/button/index.html index ea5ba50167..6f22e77016 100644 --- a/cn/src/ranui/button/index.html +++ b/cn/src/ranui/button/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -50,8 +50,8 @@ <r-button type="text" disabled>文本按钮</r-button> <r-button disabled>默认按钮</r-button>

图标icon

当需要在 Button 内嵌入 Icon 时,可以设置 icon 属性,或者直接在 Button 内使用 Icon 组件。

如果想控制 Icon 具体的位置,只能直接使用 Icon 组件,而非 icon 属性。

默认按钮
主要按钮
xml
<r-button type="default" icon="user">默认按钮</r-button>
 <r-button type="primary" icon="home">主要按钮</r-button>

特效 effect

如果需要纯净的 Button,可以加上 effect = false,屏蔽点击时候的水波纹特效

默认按钮主要按钮
xml
<r-button type="default" icon="user">默认按钮</r-button>
-<r-button type="primary" icon="home">主要按钮</r-button>
- +<r-button type="primary" icon="home">主要按钮</r-button> + \ No newline at end of file diff --git a/cn/src/ranui/checkbox/index.html b/cn/src/ranui/checkbox/index.html index 91d3e0947d..a5cf6abae8 100644 --- a/cn/src/ranui/checkbox/index.html +++ b/cn/src/ranui/checkbox/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -45,8 +45,8 @@
Skip to content

CheckBox 多选框

代码演示

xml
 <r-checkbox ></r-checkbox>

属性

checked

xml
 <r-checkbox checked="true"></r-checkbox>
  <r-checkbox checked="false"></r-checkbox>

disabled

xml
 <r-checkbox checked="true" disabled></r-checkbox>
  <r-checkbox checked="false" disabled></r-checkbox>

事件event

改变的时候触发。

onchange

xml
 <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
- <r-checkbox onchange="console.log(this.checked)"></r-checkbox>

Released under the MIT License.

- + <r-checkbox onchange="console.log(this.checked)"></r-checkbox> + \ No newline at end of file diff --git a/cn/src/ranui/icon/index.html b/cn/src/ranui/icon/index.html index 7cd66ee207..719e71f4e2 100644 --- a/cn/src/ranui/icon/index.html +++ b/cn/src/ranui/icon/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -53,8 +53,8 @@ <r-icon name="lock" size="50" color="#F44336"></r-icon> <r-icon name="lock" size="50" color="#3F51B5"></r-icon>

旋转spin

设置 spin 开启旋转,传入数字控制旋转的速度,数字越小旋转越快

html
<r-icon name="loading" size="50" color="#1E90FF" spin="0.7"></r-icon>
 <r-icon name="loading" size="50" color="#1E90FF" spin></r-icon>
-<r-icon name="loading" size="50" color="#1E90FF" spin="5"></r-icon>

图标列表

- +<r-icon name="loading" size="50" color="#1E90FF" spin="5"></r-icon>

图标列表

+ \ No newline at end of file diff --git a/cn/src/ranui/image/index.html b/cn/src/ranui/image/index.html index eb4c052aa8..68361626ca 100644 --- a/cn/src/ranui/image/index.html +++ b/cn/src/ranui/image/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

Image 图片

代码演示

xml
 <r-img src="" fallback=""></r-img>

属性

图片加载地址src

图片的地址

图片加载失败fallback

src配置的图片加载失败,兜底的图片地址,下面是默认加载失败图片

Released under the MIT License.

- +
Skip to content

Image 图片

代码演示

xml
 <r-img src="" fallback=""></r-img>

属性

图片加载地址src

图片的地址

图片加载失败fallback

src配置的图片加载失败,兜底的图片地址,下面是默认加载失败图片

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranui/index.html b/cn/src/ranui/index.html index ace5331055..9c57860f75 100644 --- a/cn/src/ranui/index.html +++ b/cn/src/ranui/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -128,8 +128,8 @@ percent="0.70" type="drag" style="--ran-progress-wrap-background:linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);" -></r-progress>

具体css3变量名称可以参考每个组件的介绍和说明

Compatibility 兼容性

Contributors 贡献者

Other 相关资源

  1. 优秀的组件设计
  2. 在线生成 CSS 渐变色
  3. 优秀设计作品,有 psd 和 sketch
  4. 3D UI 设计,类似于 3D 版的 figma
  5. 设计规范
  6. 优秀设计作品
  7. element UI 中文网
  8. Ant design 中文网
  9. 在线绘制 CSS 动画
  10. tailwindcss 组件库
  11. animate css 非常优秀的 css 动画
  12. can i use 检测兼容性 API 网站
  13. figma

协议和标准

  1. RFCs
  2. ECMA
  3. w3c
- +></r-progress>

具体css3变量名称可以参考每个组件的介绍和说明

Compatibility 兼容性

Contributors 贡献者

Other 相关资源

  1. 优秀的组件设计
  2. 在线生成 CSS 渐变色
  3. 优秀设计作品,有 psd 和 sketch
  4. 3D UI 设计,类似于 3D 版的 figma
  5. 设计规范
  6. 优秀设计作品
  7. element UI 中文网
  8. Ant design 中文网
  9. 在线绘制 CSS 动画
  10. tailwindcss 组件库
  11. animate css 非常优秀的 css 动画
  12. can i use 检测兼容性 API 网站
  13. figma

协议和标准

  1. RFCs
  2. ECMA
  3. w3c
+ \ No newline at end of file diff --git a/cn/src/ranui/input/index.html b/cn/src/ranui/input/index.html index b6e6a7059b..95622b419b 100644 --- a/cn/src/ranui/input/index.html +++ b/cn/src/ranui/input/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -53,8 +53,8 @@ const func = (e) => { console.log(e); }; -input.addEventListener('input', func);

事件的e参数结构 input方法

- +input.addEventListener('input', func);

事件的e参数结构 input方法

+ \ No newline at end of file diff --git a/cn/src/ranui/loading/index.html b/cn/src/ranui/loading/index.html index 5faf687364..161ec159ab 100644 --- a/cn/src/ranui/loading/index.html +++ b/cn/src/ranui/loading/index.html @@ -14,7 +14,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -47,8 +47,8 @@
Skip to content

Loading

一些好看的 loading

Code demo

xml
<r-loading name="circle"></r-loading>

属性

name

这里有很多好看的 loading,可供选择

xml
<r-loading name="double-bounce"></r-loading>
 <r-loading name="rotate"></r-loading>
 <r-loading name="stretch"></r-loading>
-<r-loading name="cube"></r-loading>

Loading list

Move the mouse over the icon to see the loading animation

stretch
rotate
double-bounce
cube
dot
triple-bounce
scale-out
circle
circle-line
square
pulse
solar
cube-fold
circle-fold
cube-grid
circle-turn
circle-rotate
circle-spin
dot-bar
dot-circle
line
dot-pulse
line-scale
text
cube-dim
dot-line
arc
drop
pacman

Released under the MIT License.

- +<r-loading name="cube"></r-loading>

Loading list

Move the mouse over the icon to see the loading animation

stretch
rotate
double-bounce
cube
dot
triple-bounce
scale-out
circle
circle-line
square
pulse
solar
cube-fold
circle-fold
cube-grid
circle-turn
circle-rotate
circle-spin
dot-bar
dot-circle
line
dot-pulse
line-scale
text
cube-dim
dot-line
arc
drop
pacman
+ \ No newline at end of file diff --git a/cn/src/ranui/math/index.html b/cn/src/ranui/math/index.html index 7f8c8c637b..d11ff80f03 100644 --- a/cn/src/ranui/math/index.html +++ b/cn/src/ranui/math/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

math 数学公式

HTML 页面中高质量展示 LaTeX 数学公式

代码演示

xml
<r-math latex="\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1 \quad (a > b > 0)"></r-math>

属性

latex string

xml
  <r-math latex="x = {-b \pm \sqrt{b^2-4ac} \over 2a}"></r-math>

Released under the MIT License.

- +
Skip to content

math 数学公式

HTML 页面中高质量展示 LaTeX 数学公式

代码演示

xml
<r-math latex="\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1 \quad (a > b > 0)"></r-math>

属性

latex string

xml
  <r-math latex="x = {-b \pm \sqrt{b^2-4ac} \over 2a}"></r-math>

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranui/message/index.html b/cn/src/ranui/message/index.html index cd4bf3ab76..a3bb7408e2 100644 --- a/cn/src/ranui/message/index.html +++ b/cn/src/ranui/message/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -46,8 +46,8 @@ <r-button onclick="message.warning('这是一条提示')">警告提示</r-button> <r-button onclick="message.error('这是一条提示')">错误提示</r-button> <r-button onclick="message.success('这是一条提示')">成功提示</r-button> -<r-button onclick="message.toast('这是一条提示')">toast提示</r-button>

方法

组件提供了一些静态方法,使用方式和参数如下:

  1. 可以只传一个参数,提示的内容,默认提示 3000 毫秒

message.info('这是一条提示')

message.warning('这是一条提示')

message.error('这是一条提示')

message.success('这是一条提示')

message.toast('这是一条提示')"

  1. 也可以传一个对象,设置提示内容,关闭延时,关闭时触发的回调函数

message.info({content:'这是一条提示', duration: 2000, close: () => {}})

message.warning({content:'这是一条提示', duration: 2000, close: () => {}})

message.error({content:'这是一条提示', duration: 2000, close: () => {}})

message.success({content:'这是一条提示', duration: 2000, close: () => {}})

message.toast({content:'这是一条提示', duration: 2000, close: () => {}})

参数说明类型
content提示内容string
duration自动关闭的延时,单位毫秒。默认 3000 毫秒number
close关闭时触发的回调函数() => void
- +<r-button onclick="message.toast('这是一条提示')">toast提示</r-button>

方法

组件提供了一些静态方法,使用方式和参数如下:

  1. 可以只传一个参数,提示的内容,默认提示 3000 毫秒

message.info('这是一条提示')

message.warning('这是一条提示')

message.error('这是一条提示')

message.success('这是一条提示')

message.toast('这是一条提示')"

  1. 也可以传一个对象,设置提示内容,关闭延时,关闭时触发的回调函数

message.info({content:'这是一条提示', duration: 2000, close: () => {}})

message.warning({content:'这是一条提示', duration: 2000, close: () => {}})

message.error({content:'这是一条提示', duration: 2000, close: () => {}})

message.success({content:'这是一条提示', duration: 2000, close: () => {}})

message.toast({content:'这是一条提示', duration: 2000, close: () => {}})

参数说明类型
content提示内容string
duration自动关闭的延时,单位毫秒。默认 3000 毫秒number
close关闭时触发的回调函数() => void
+ \ No newline at end of file diff --git a/cn/src/ranui/modal/index.html b/cn/src/ranui/modal/index.html index ad0a84fb26..9ee4b97528 100644 --- a/cn/src/ranui/modal/index.html +++ b/cn/src/ranui/modal/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

Released under the MIT License.

- +
Skip to content

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranui/player/index.html b/cn/src/ranui/player/index.html index e77fb4ec3f..2f8292fff7 100644 --- a/cn/src/ranui/player/index.html +++ b/cn/src/ranui/player/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

r-player 视频播放器

基于hlsjsweb components,让原生的标签r-player拥有统一的视频控件。 不采用new Player(options)的方式挂载到指定dom,视图的归视图,逻辑的归逻辑,所见及所得,更加直观。

  1. 可拖拽进度条
  2. 音量控制
  3. 根据当前带宽自适应码率切换
  4. 手动清晰度切换
  5. 倍速播放
  6. 样式自定义覆盖
  7. hls协议标准加密视频播放
  8. 基于原生开发,可在所有框架运行,统一跨框架情况
  9. 各浏览器控件统一

代码演示

xml
  <r-player src="/ran/hls/example.m3u8"></r-player>

属性

src

视频的资源地址

volume

设置初始音量,默认 0.5

currentTime

设置初始播放时间,默认从头开始播放

playbackRate

设置倍速,默认 1.0

debug

控制台会打印输出一些信息

事件event

onchange

监听任何播放器发生的变化,返回的值如下。

可通过这个方法获得播放器的实例

活着通过type判断不同的事件类型,进行不同的操作

属性说明类型
type发生变化的事件类型string
data事件的值Object
currentTime播放的当前时间number
duration视频的总时长number
tag播放器的实例Element

其中type类型有

名称说明
canplay浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。
canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。
completeOfflineAudioContext 渲染完成。
durationchangeduration 属性的值改变时触发。
emptied媒体内容变为空;例如,当这个 media 已经加载完成(或者部分加载完成),则发送此事件,并调用 load() 方法重新加载它。
ended视频停止播放,因为 media 已经到达结束点。
loadedmetadata已加载元数据。
progress在浏览器加载资源时周期性触发。
ratechange播放速率发生变化。
seeked跳帧(seek)操作完成。
seeking跳帧(seek)操作开始。
stalled用户代理(user agent)正在尝试获取媒体数据,但数据意外未出现。
suspend媒体数据加载已暂停。
loadeddatamedia 中的首帧已经完成加载。
timeupdatecurrentTime 属性指定的时间发生变化。
volumechange音量发生变化。
waiting由于暂时缺少数据,播放已停止。
play播放已开始。
playing由于缺乏数据而暂停或延迟后,播放准备开始。
pause播放已暂停。
volume音量发生变化。
fullscreen触发全屏事件

Released under the MIT License.

- +
Skip to content

r-player 视频播放器

基于hlsjsweb components,让原生的标签r-player拥有统一的视频控件。 不采用new Player(options)的方式挂载到指定dom,视图的归视图,逻辑的归逻辑,所见及所得,更加直观。

  1. 可拖拽进度条
  2. 音量控制
  3. 根据当前带宽自适应码率切换
  4. 手动清晰度切换
  5. 倍速播放
  6. 样式自定义覆盖
  7. hls协议标准加密视频播放
  8. 基于原生开发,可在所有框架运行,统一跨框架情况
  9. 各浏览器控件统一

代码演示

xml
  <r-player src="/ran/hls/example.m3u8"></r-player>

属性

src

视频的资源地址

volume

设置初始音量,默认 0.5

currentTime

设置初始播放时间,默认从头开始播放

playbackRate

设置倍速,默认 1.0

debug

控制台会打印输出一些信息

事件event

onchange

监听任何播放器发生的变化,返回的值如下。

可通过这个方法获得播放器的实例

活着通过type判断不同的事件类型,进行不同的操作

属性说明类型
type发生变化的事件类型string
data事件的值Object
currentTime播放的当前时间number
duration视频的总时长number
tag播放器的实例Element

其中type类型有

名称说明
canplay浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。
canplaythrough浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束。
completeOfflineAudioContext 渲染完成。
durationchangeduration 属性的值改变时触发。
emptied媒体内容变为空;例如,当这个 media 已经加载完成(或者部分加载完成),则发送此事件,并调用 load() 方法重新加载它。
ended视频停止播放,因为 media 已经到达结束点。
loadedmetadata已加载元数据。
progress在浏览器加载资源时周期性触发。
ratechange播放速率发生变化。
seeked跳帧(seek)操作完成。
seeking跳帧(seek)操作开始。
stalled用户代理(user agent)正在尝试获取媒体数据,但数据意外未出现。
suspend媒体数据加载已暂停。
loadeddatamedia 中的首帧已经完成加载。
timeupdatecurrentTime 属性指定的时间发生变化。
volumechange音量发生变化。
waiting由于暂时缺少数据,播放已停止。
play播放已开始。
playing由于缺乏数据而暂停或延迟后,播放准备开始。
pause播放已暂停。
volume音量发生变化。
fullscreen触发全屏事件

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranui/popover/index.html b/cn/src/ranui/popover/index.html index ccf5332afc..50c0d68be8 100644 --- a/cn/src/ranui/popover/index.html +++ b/cn/src/ranui/popover/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -67,8 +67,8 @@ <r-content> <div>top</div> </r-content> - </r-popover> - + </r-popover> + \ No newline at end of file diff --git a/cn/src/ranui/preview/index.html b/cn/src/ranui/preview/index.html index a1c83d091b..1b91b5c75e 100644 --- a/cn/src/ranui/preview/index.html +++ b/cn/src/ranui/preview/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -61,8 +61,8 @@ } }; }; -</script>

属性

资源地址src

src 地址即可打开弹窗,没有src就不展示

html
<r-preview src=""></r-preview>

是否可关闭closeable

closeable 默认为 true ,可以关闭,设置成 false 时, 表示不可关闭,将不会展示右上角的关闭按钮

html
<r-preview closeable="false"></r-preview>
- +</script>

属性

资源地址src

src 地址即可打开弹窗,没有src就不展示

html
<r-preview src=""></r-preview>

是否可关闭closeable

closeable 默认为 true ,可以关闭,设置成 false 时, 表示不可关闭,将不会展示右上角的关闭按钮

html
<r-preview closeable="false"></r-preview>
+ \ No newline at end of file diff --git a/cn/src/ranui/progress/index.html b/cn/src/ranui/progress/index.html index a3ffcc3502..247ad75407 100644 --- a/cn/src/ranui/progress/index.html +++ b/cn/src/ranui/progress/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,8 +48,8 @@ <r-progress type="primary" percent="40%"></r-progress> <r-progress type="primary" percent="100%"></r-progress>

进度条的点dot

默认展示,设置成false可隐藏

html
<r-progress type="drag" percent="30%" dot="false"></r-progress>
 <r-progress type="primary" percent="40%" dot="true"></r-progress>
-<r-progress type="primary" percent="40%"></r-progress>

类型type

html
<r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

方法

change

percenttotal属性发生变化时,触发change事件。

属性说明类型
value当前进度string|number
percent当前进度string|number
total总进度string|number
- +<r-progress type="primary" percent="40%"></r-progress>

类型type

html
<r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

方法

change

percenttotal属性发生变化时,触发change事件。

属性说明类型
value当前进度string|number
percent当前进度string|number
total总进度string|number
+ \ No newline at end of file diff --git a/cn/src/ranui/radar/index.html b/cn/src/ranui/radar/index.html index b13a4cbc25..5279b9f3cc 100644 --- a/cn/src/ranui/radar/index.html +++ b/cn/src/ranui/radar/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -96,8 +96,8 @@ "abilityName": "暴击伤害", "scoreRate": "50" } -] - +] + \ No newline at end of file diff --git a/cn/src/ranui/select/index.html b/cn/src/ranui/select/index.html index 00758e2169..bc18ec657a 100644 --- a/cn/src/ranui/select/index.html +++ b/cn/src/ranui/select/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -83,8 +83,8 @@ <r-option value="185">Mike</r-option> <r-option value="186">Tom</r-option> <r-option value="187">Lucy</r-option> - </r-select> - + </r-select> + \ No newline at end of file diff --git a/cn/src/ranui/skeleton/index.html b/cn/src/ranui/skeleton/index.html index 169bf36cb6..8fa432fe52 100644 --- a/cn/src/ranui/skeleton/index.html +++ b/cn/src/ranui/skeleton/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

skeleton 骨架屏

在需要等待加载内容的位置提供一个占位图形组合。

代码演示

骨架长度跟随父级元素的长度

xml
<r-skeleton ></r-skeleton>

Released under the MIT License.

- +
Skip to content

skeleton 骨架屏

在需要等待加载内容的位置提供一个占位图形组合。

代码演示

骨架长度跟随父级元素的长度

xml
<r-skeleton ></r-skeleton>

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranui/tab/index.html b/cn/src/ranui/tab/index.html index ac69cdc0be..e70349fbd7 100644 --- a/cn/src/ranui/tab/index.html +++ b/cn/src/ranui/tab/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -84,8 +84,8 @@ <r-tab label="tab1">11111</r-tab> <r-tab label="tab2">22222</r-tab> <r-tab label="tab3">33333</r-tab> - </r-tabs> - + </r-tabs> + \ No newline at end of file diff --git a/cn/src/ranui/tabs/index.html b/cn/src/ranui/tabs/index.html index 297dd028ed..bed5460cf3 100644 --- a/cn/src/ranui/tabs/index.html +++ b/cn/src/ranui/tabs/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -66,8 +66,8 @@ <r-tab icon="home" iconSize="22">tab1</r-tab> <r-tab icon="message" iconSize="22">tab2</r-tab> <r-tab icon="user" iconSize="22">tab3</r-tab> -</r-tabs>

风格type

风格有 text,clean,

对齐align

事件event

onchange

切换完成时触发。

- +</r-tabs>

风格type

风格有 text,clean,

对齐align

事件event

onchange

切换完成时触发。

+ \ No newline at end of file diff --git a/cn/src/ranuts/binaryTree/index.html b/cn/src/ranuts/binaryTree/index.html index 9bf83c6d07..43e043179a 100644 --- a/cn/src/ranuts/binaryTree/index.html +++ b/cn/src/ranuts/binaryTree/index.html @@ -13,7 +13,7 @@ - + @@ -37,14 +37,14 @@ - + -
Skip to content

二叉树的定义

在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

二叉树的性质

  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
  • 二叉树的总结点数 n = n1 + n2 + n0
  • 总连线数等于总节点数减一(B = n - 1)
  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

二叉树的类型

满二叉树

一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

完全二叉树

一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

二叉搜索树

二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

平衡二叉树

平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

B 树

B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

B+树

B+树是 B 树的变体,也是一种多路搜索树。

B*树

B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

红黑树

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

遍历

前序遍历

后序遍历

中序遍历

层序遍历

常见算法题

镜像二叉树

重建二叉树

二叉树深度

二叉树节点总数

判断二叉树子结构

输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

参考文档

  1. 维基百科二叉树
  2. 百度百科满二叉树

Released under the MIT License.

- +
Skip to content

二叉树的定义

在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

二叉树的性质

  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
  • 二叉树的总结点数 n = n1 + n2 + n0
  • 总连线数等于总节点数减一(B = n - 1)
  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

二叉树的类型

满二叉树

一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

完全二叉树

一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

二叉搜索树

二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

平衡二叉树

平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

B 树

B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

B+树

B+树是 B 树的变体,也是一种多路搜索树。

B*树

B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

红黑树

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

遍历

前序遍历

后序遍历

中序遍历

层序遍历

常见算法题

镜像二叉树

重建二叉树

二叉树深度

二叉树节点总数

判断二叉树子结构

输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

参考文档

  1. 维基百科二叉树
  2. 百度百科满二叉树

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/bundler/index.html b/cn/src/ranuts/bundler/index.html index 78c4cba931..b5234b4a55 100644 --- a/cn/src/ranuts/bundler/index.html +++ b/cn/src/ranuts/bundler/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -52,8 +52,8 @@ generate: () => bundle.render() }; }); -}

架构图

- +}

架构图

+ \ No newline at end of file diff --git a/cn/src/ranuts/file/appendFile.html b/cn/src/ranuts/file/appendFile.html index f8f38d359c..68b0986d86 100644 --- a/cn/src/ranuts/file/appendFile.html +++ b/cn/src/ranuts/file/appendFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

AppendFile

追加一些数据到文件内

API

Return

  • Promise
参数说明类型描述
success是否追加成功booleantrue 追加成功 false 追加失败
data追加失败的原因,添加成功后的文件内容any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Example

Released under the MIT License.

- +
Skip to content

AppendFile

追加一些数据到文件内

API

Return

  • Promise
参数说明类型描述
success是否追加成功booleantrue 追加成功 false 追加失败
data追加失败的原因,添加成功后的文件内容any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/file/fileInfo.html b/cn/src/ranuts/file/fileInfo.html index 5244645e67..bfc7393c5d 100644 --- a/cn/src/ranuts/file/fileInfo.html +++ b/cn/src/ranuts/file/fileInfo.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

QueryFileInfo

查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

API

Return

  • Promise
参数说明类型描述
success是否检查成功booleantrue 成功 false 失败
data文件的信息,或者错误的原因Stats

Options

参数说明类型默认值
path文件路径,需要检查的文件路径stringundefined

Example

Released under the MIT License.

- +
Skip to content

QueryFileInfo

查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

API

Return

  • Promise
参数说明类型描述
success是否检查成功booleantrue 成功 false 失败
data文件的信息,或者错误的原因Stats

Options

参数说明类型默认值
path文件路径,需要检查的文件路径stringundefined

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/file/readDir.html b/cn/src/ranuts/file/readDir.html index 27259a14c1..767d2f696c 100644 --- a/cn/src/ranuts/file/readDir.html +++ b/cn/src/ranuts/file/readDir.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

ReadDir

读一个目录下的所有文件

API

Return

  • Promise
参数说明类型描述
result目录下所有文件的数组array传入函数的参数

Options

参数说明类型默认值
optionsobject传入函数的参数
dirPath文件的路径Stats

Example

Released under the MIT License.

- +
Skip to content

ReadDir

读一个目录下的所有文件

API

Return

  • Promise
参数说明类型描述
result目录下所有文件的数组array传入函数的参数

Options

参数说明类型默认值
optionsobject传入函数的参数
dirPath文件的路径Stats

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/file/readFile.html b/cn/src/ranuts/file/readFile.html index 2f054d6506..c37fc7c769 100644 --- a/cn/src/ranuts/file/readFile.html +++ b/cn/src/ranuts/file/readFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

ReadFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
data文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Example

Released under the MIT License.

- +
Skip to content

ReadFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
data文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/file/watchFile.html b/cn/src/ranuts/file/watchFile.html index 460a356f78..4e68e93de1 100644 --- a/cn/src/ranuts/file/watchFile.html +++ b/cn/src/ranuts/file/watchFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

WatchFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
status文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Released under the MIT License.

- +
Skip to content

WatchFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
status文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/file/writeFile.html b/cn/src/ranuts/file/writeFile.html index f8dbf2620c..747038f42b 100644 --- a/cn/src/ranuts/file/writeFile.html +++ b/cn/src/ranuts/file/writeFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

WriteFile

将内容写入文件

API

Return

  • Promise
参数说明类型描述
success是否写入成功booleantrue 成功 false 失败
data写入失败的原因,添加成功后的文件内容和文件路径any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Released under the MIT License.

- +
Skip to content

WriteFile

将内容写入文件

API

Return

  • Promise
参数说明类型描述
success是否写入成功booleantrue 成功 false 失败
data写入失败的原因,添加成功后的文件内容和文件路径any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/index.html b/cn/src/ranuts/index.html index 9647a77b8a..e05e045f7d 100644 --- a/cn/src/ranuts/index.html +++ b/cn/src/ranuts/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

ranuts overview

方法列表

方法说明详细内容
writeFile写入文件writeFile
readFile读取文件readFile
readDir读取目录,获取目录下所有文件的名字readDir
watchFile观察文件的内容是否发生变化watchFile
queryFileInfo查询文件信息queryFileInfo
filterObj过滤对象filterObj
EventEmitter发布订阅类EventEmitter
str2Xml字符串转成xmlstr2Xml
getMime根据文件格式后缀获取 mime typegetMime
getCookie获取指定 cookie 的值writeFile
formatJson格式化 JSONformatJson

TOTP

2FA Verification
Generate
code:
expires:

Released under the MIT License.

- +
Skip to content

ranuts overview

方法列表

方法说明详细内容
writeFile写入文件writeFile
readFile读取文件readFile
readDir读取目录,获取目录下所有文件的名字readDir
watchFile观察文件的内容是否发生变化watchFile
queryFileInfo查询文件信息queryFileInfo
filterObj过滤对象filterObj
EventEmitter发布订阅类EventEmitter
str2Xml字符串转成xmlstr2Xml
getMime根据文件格式后缀获取 mime typegetMime
getCookie获取指定 cookie 的值writeFile
formatJson格式化 JSONformatJson

TOTP

2FA Verification
Generate
code:
expires:

Released under the MIT License.

+ \ No newline at end of file diff --git a/cn/src/ranuts/mimeType/mimeType.html b/cn/src/ranuts/mimeType/mimeType.html index 921db0c1f7..0bcf49eae4 100644 --- a/cn/src/ranuts/mimeType/mimeType.html +++ b/cn/src/ranuts/mimeType/mimeType.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -49,8 +49,8 @@ // 'application/vnd.openxmlformats-officedocument.presentationml.presentation const res = getMime('.txt'); console.log(result); -// text/plain - +// text/plain + \ No newline at end of file diff --git a/cn/src/ranuts/mode/subscribe.html b/cn/src/ranuts/mode/subscribe.html index 8244db4620..119fc4ac1f 100644 --- a/cn/src/ranuts/mode/subscribe.html +++ b/cn/src/ranuts/mode/subscribe.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -73,8 +73,8 @@ // 订阅一次,触发一次自动取消 subscribe.once('other', () => { console.log(5); -}); - +}); + \ No newline at end of file diff --git a/cn/src/ranuts/utils/convertImageToBase64.html b/cn/src/ranuts/utils/convertImageToBase64.html index 1135fd6a52..6caa804f6e 100644 --- a/cn/src/ranuts/utils/convertImageToBase64.html +++ b/cn/src/ranuts/utils/convertImageToBase64.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -46,8 +46,8 @@ convertImageToBase64(file).then((res) => { console.log(result); -}); - +}); + \ No newline at end of file diff --git a/cn/src/ranuts/utils/filterObj.html b/cn/src/ranuts/utils/filterObj.html index 5d4eef67be..32b6dde2de 100644 --- a/cn/src/ranuts/utils/filterObj.html +++ b/cn/src/ranuts/utils/filterObj.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -54,8 +54,8 @@ console.log(result); -// { age:10 } - +// { age:10 } + \ No newline at end of file diff --git a/cn/src/ranuts/utils/formatJson.html b/cn/src/ranuts/utils/formatJson.html index 6fd3e0ec41..49c53a6512 100644 --- a/cn/src/ranuts/utils/formatJson.html +++ b/cn/src/ranuts/utils/formatJson.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -51,8 +51,8 @@ const result = formatJson(json); -console.log(result); - +console.log(result); + \ No newline at end of file diff --git a/cn/src/ranuts/utils/getCookie.html b/cn/src/ranuts/utils/getCookie.html index 4f243957b5..828b24cfb5 100644 --- a/cn/src/ranuts/utils/getCookie.html +++ b/cn/src/ranuts/utils/getCookie.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,8 +48,8 @@ console.log(result); -// '' - +// '' + \ No newline at end of file diff --git a/cn/src/ranuts/utils/ocr.html b/cn/src/ranuts/utils/ocr.html index 1c173c86eb..84e4d81ea7 100644 --- a/cn/src/ranuts/utils/ocr.html +++ b/cn/src/ranuts/utils/ocr.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -56,8 +56,8 @@ // And when thou lovest thy pale orb to shroud // Behind the gather’d blackness lost on high; // And when thou dartest from the wind-rent cloud -// Thy placid lightning o’er the awaken’d sky.

Lang Code

Lang CodeLanguage
afrAfrikaans
amhAmharic
araArabic
asmAssamese
azeAzerbaijani
aze_cyrlAzerbaijani - Cyrillic
belBelarusian
benBengali
bodTibetan
bosBosnian
bulBulgarian
catCatalan; Valencian
cebCebuano
cesCzech
chi_simChinese - Simplified
chi_traChinese - Traditional
chrCherokee
cymWelsh
danDanish
deuGerman
dzoDzongkha
ellGreek, Modern (1453-)
engEnglish
enmEnglish, Middle (1100-1500)
epoEsperanto
estEstonian
eusBasque
fasPersian
finFinnish
fraFrench
frkGerman Fraktur
frmFrench, Middle (ca. 1400-1600)
gleIrish
glgGalician
grcGreek, Ancient (-1453)
gujGujarati
hatHaitian; Haitian Creole
hebHebrew
hinHindi
hrvCroatian
hunHungarian
ikuInuktitut
indIndonesian
islIcelandic
itaItalian
ita_oldItalian - Old
javJavanese
jpnJapanese
kanKannada
katGeorgian
kat_oldGeorgian - Old
kazKazakh
khmCentral Khmer
kirKirghiz; Kyrgyz
korKorean
kurKurdish
laoLao
latLatin
lavLatvian
litLithuanian
malMalayalam
marMarathi
mkdMacedonian
mltMaltese
msaMalay
myaBurmese
nepNepali
nldDutch; Flemish
norNorwegian
oriOriya
panPanjabi; Punjabi
polPolish
porPortuguese
pusPushto; Pashto
ronRomanian; Moldavian; Moldovan
rusRussian
sanSanskrit
sinSinhala; Sinhalese
slkSlovak
slvSlovenian
spaSpanish; Castilian
spa_oldSpanish; Castilian - Old
sqiAlbanian
srpSerbian
srp_latnSerbian - Latin
swaSwahili
sweSwedish
syrSyriac
tamTamil
telTelugu
tgkTajik
tglTagalog
thaThai
tirTigrinya
turTurkish
uigUighur; Uyghur
ukrUkrainian
urdUrdu
uzbUzbek
uzb_cyrlUzbek - Cyrillic
vieVietnamese
yidYiddish
- +// Thy placid lightning o’er the awaken’d sky.

Lang Code

Lang CodeLanguage
afrAfrikaans
amhAmharic
araArabic
asmAssamese
azeAzerbaijani
aze_cyrlAzerbaijani - Cyrillic
belBelarusian
benBengali
bodTibetan
bosBosnian
bulBulgarian
catCatalan; Valencian
cebCebuano
cesCzech
chi_simChinese - Simplified
chi_traChinese - Traditional
chrCherokee
cymWelsh
danDanish
deuGerman
dzoDzongkha
ellGreek, Modern (1453-)
engEnglish
enmEnglish, Middle (1100-1500)
epoEsperanto
estEstonian
eusBasque
fasPersian
finFinnish
fraFrench
frkGerman Fraktur
frmFrench, Middle (ca. 1400-1600)
gleIrish
glgGalician
grcGreek, Ancient (-1453)
gujGujarati
hatHaitian; Haitian Creole
hebHebrew
hinHindi
hrvCroatian
hunHungarian
ikuInuktitut
indIndonesian
islIcelandic
itaItalian
ita_oldItalian - Old
javJavanese
jpnJapanese
kanKannada
katGeorgian
kat_oldGeorgian - Old
kazKazakh
khmCentral Khmer
kirKirghiz; Kyrgyz
korKorean
kurKurdish
laoLao
latLatin
lavLatvian
litLithuanian
malMalayalam
marMarathi
mkdMacedonian
mltMaltese
msaMalay
myaBurmese
nepNepali
nldDutch; Flemish
norNorwegian
oriOriya
panPanjabi; Punjabi
polPolish
porPortuguese
pusPushto; Pashto
ronRomanian; Moldavian; Moldovan
rusRussian
sanSanskrit
sinSinhala; Sinhalese
slkSlovak
slvSlovenian
spaSpanish; Castilian
spa_oldSpanish; Castilian - Old
sqiAlbanian
srpSerbian
srp_latnSerbian - Latin
swaSwahili
sweSwedish
syrSyriac
tamTamil
telTelugu
tgkTajik
tglTagalog
thaThai
tirTigrinya
turTurkish
uigUighur; Uyghur
ukrUkrainian
urdUrdu
uzbUzbek
uzb_cyrlUzbek - Cyrillic
vieVietnamese
yidYiddish
+ \ No newline at end of file diff --git a/cn/src/ranuts/utils/str2xml.html b/cn/src/ranuts/utils/str2xml.html index cbda97c908..33b67af298 100644 --- a/cn/src/ranuts/utils/str2xml.html +++ b/cn/src/ranuts/utils/str2xml.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -49,8 +49,8 @@ const icon = str2Xml(svg, 'image/svg+xml'); -document.body.appendChild(icon); - +document.body.appendChild(icon); + \ No newline at end of file diff --git a/cn/src/ranuts/utils/task.html b/cn/src/ranuts/utils/task.html index 93db1fcfd8..bbcc103ef5 100644 --- a/cn/src/ranuts/utils/task.html +++ b/cn/src/ranuts/utils/task.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,8 +48,8 @@ const time = taskEnd(taskId); -console.log('task 执行花费的时间', time);

二.new Date().getTime()

new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

  1. 某些情况下,毫秒级精度可能不够。
  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

三.console.time(), console.timeEnd()

启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

四.performance.now()

performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

- +console.log('task 执行花费的时间', time);

二.new Date().getTime()

new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

  1. 某些情况下,毫秒级精度可能不够。
  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

三.console.time(), console.timeEnd()

启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

四.performance.now()

performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

+ \ No newline at end of file diff --git "a/cn/src/types/TS\347\261\273\345\236\213.html" "b/cn/src/types/TS\347\261\273\345\236\213.html" index c1ae5ed2e1..f09f5b28ce 100644 --- "a/cn/src/types/TS\347\261\273\345\236\213.html" +++ "b/cn/src/types/TS\347\261\273\345\236\213.html" @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -89,8 +89,8 @@ age?: number; } -type tuple = [string, number?]; - +type tuple = [string, number?]; + \ No newline at end of file diff --git "a/cn/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" "b/cn/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" index 39472747a4..7d73330ae5 100644 --- "a/cn/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" +++ "b/cn/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -52,8 +52,8 @@ new (name: string): Person; }

这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

ts
type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T
   ? T
-  : unknown;
- + : unknown; + \ No newline at end of file diff --git "a/cn/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" "b/cn/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" index b910ca3d13..8507ff49c9 100644 --- "a/cn/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" +++ "b/cn/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -68,8 +68,8 @@ // type res = { // aaa:[1,1,1] // bbb:[2,2,2] -// }

这里的 & string 可能大家会迷惑,解释一下:

因为索引类型(对象、class 等)可以用 string、number 和 symbol 作为 key,这里 keyof T 取出的索引就是 string | number | symbol 的联合类型,和 string 取交叉部分就只剩下 string 了。就像前面所说,交叉类型会把同一类型做合并,不同类型舍弃。

因为 js 处理对象比较多,所以索引类型的映射比较重要。

- +// }

这里的 & string 可能大家会迷惑,解释一下:

因为索引类型(对象、class 等)可以用 string、number 和 symbol 作为 key,这里 keyof T 取出的索引就是 string | number | symbol 的联合类型,和 string 取交叉部分就只剩下 string 了。就像前面所说,交叉类型会把同一类型做合并,不同类型舍弃。

因为 js 处理对象比较多,所以索引类型的映射比较重要。

+ \ No newline at end of file diff --git "a/cn/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" "b/cn/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" index 9fdab44938..2bc7986262 100644 --- "a/cn/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" +++ "b/cn/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

TypeScript 内置的高级类型

Parameters

Parameters 用于提取函数类型的参数类型。

ReturnType

ReturnType 用于提取函数类型的返回值类型。

ConstructorParameters

构造器类型和函数类型的区别就是可以被 new。

Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

InstanceType

提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

ThisParameterType

OmitThisParameter

Partial

Required

Readonly

Pick

Record

Exclude

Extract

Omit

Awaited

NonNullable

Uppercase

Lowercase

Capitalize

Uncapitalize

总结

比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

用模式匹配 + 重新构造可以实现:OmitThisParameter

用重新构造可以实现:Partial、Required、Readonly、Pick、Record

用模式匹配 + 递归可以实现: Awaited

用联合类型在分布式条件类型的特性可以实现: Exclude

此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

Last updated:

Released under the MIT License.

- +
Skip to content

TypeScript 内置的高级类型

Parameters

Parameters 用于提取函数类型的参数类型。

ReturnType

ReturnType 用于提取函数类型的返回值类型。

ConstructorParameters

构造器类型和函数类型的区别就是可以被 new。

Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

InstanceType

提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

ThisParameterType

OmitThisParameter

Partial

Required

Readonly

Pick

Record

Exclude

Extract

Omit

Awaited

NonNullable

Uppercase

Lowercase

Capitalize

Uncapitalize

总结

比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

用模式匹配 + 重新构造可以实现:OmitThisParameter

用重新构造可以实现:Partial、Required、Readonly、Pick、Record

用模式匹配 + 递归可以实现: Awaited

用联合类型在分布式条件类型的特性可以实现: Exclude

此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

Last updated:

Released under the MIT License.

+ \ No newline at end of file diff --git a/hashmap.json b/hashmap.json index de6a44af9f..080ecc3d78 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"cn_index.md":"CC940uHT","cn_src_article_astparse_tokenizer.md":"CbTsq3qg","cn_src_article_babel.md":"Bb4oNZ50","cn_src_article_bundle.md":"BgkzbBow","cn_src_article_designmode.md":"BzjcnDMg","cn_src_article_docpreview.md":"Bk6_K5Up","cn_src_article_functionalprogramming.md":"BI-pI2Tm","cn_src_article_imagemin.md":"CEVub_ZQ","cn_src_article_javascript_domload.md":"CI-VlQc8","cn_src_article_sort_bubble_index.md":"Cr8QMhiM","cn_src_article_sort_bucket_index.md":"Dl_rJ3sv","cn_src_article_sort_count_index.md":"BwHTgoYv","cn_src_article_sort_heap_index.md":"oxRUNOKZ","cn_src_article_sort_index.md":"V1uGWtUp","cn_src_article_sort_insert_index.md":"BPxYKrUM","cn_src_article_sort_merge_index.md":"Dz-ycn2t","cn_src_article_sort_quick_index.md":"Dm-fpFZL","cn_src_article_sort_radix_index.md":"CmXyO2h4","cn_src_article_sort_select_index.md":"C6f3fqev","cn_src_article_sort_shell_index.md":"B0DKjSD6","cn_src_article_systemdesign.md":"Oy3scZvv","cn_src_article_typescript_calculate.md":"DPFdz5ZN","cn_src_article_typescript_index.md":"CaFWWXxk","cn_src_article_typescript_pattern.md":"GG3CpSHa","cn_src_article_typescript_reconstruction.md":"B-6dp4-w","cn_src_article_typescript_recursion.md":"NrD8NlPF","cn_src_article_typescript_uniontype.md":"BgeL6nAW","cn_src_article_video.md":"Cz2oSvp1","cn_src_article_visual.md":"Be4MMnab","cn_src_note_centos.md":"BcqWCqVs","cn_src_note_docker.md":"BeX3ffKU","cn_src_note_libreoffice2wasm.md":"DATIsYiT","cn_src_note_ubuntu.md":"C1lhavpN","cn_src_ranui_button_index.md":"B-YucaRE","cn_src_ranui_checkbox_index.md":"7OhoHSmh","cn_src_ranui_icon_index.md":"7jVbQ82_","cn_src_ranui_image_index.md":"CEdjKNte","cn_src_ranui_index.md":"Cv0LhI4v","cn_src_ranui_input_index.md":"CKpr9bUR","cn_src_ranui_loading_index.md":"CcRWELn0","cn_src_ranui_math_index.md":"nmeuJ7af","cn_src_ranui_message_index.md":"OWf7PDqT","cn_src_ranui_modal_index.md":"C5SgGuYU","cn_src_ranui_player_index.md":"DXjOFy1h","cn_src_ranui_popover_index.md":"YQ2K5eFD","cn_src_ranui_preview_index.md":"MwV2D84f","cn_src_ranui_progress_index.md":"BhrzleOl","cn_src_ranui_radar_index.md":"JUC5Tblz","cn_src_ranui_select_index.md":"CqrSnS3F","cn_src_ranui_skeleton_index.md":"DfgRdP5t","cn_src_ranui_tab_index.md":"BlcshkmY","cn_src_ranui_tabs_index.md":"BnLsjdMq","cn_src_ranuts_binarytree_index.md":"CoTPSG40","cn_src_ranuts_bundler_index.md":"Bui4hlUf","cn_src_ranuts_file_appendfile.md":"CYzaDkUO","cn_src_ranuts_file_fileinfo.md":"cszSvf9s","cn_src_ranuts_file_readdir.md":"DVHZJj5k","cn_src_ranuts_file_readfile.md":"CMWdjeNp","cn_src_ranuts_file_watchfile.md":"5h3FtZpC","cn_src_ranuts_file_writefile.md":"DLZe7Swq","cn_src_ranuts_index.md":"B_7KgyI-","cn_src_ranuts_mimetype_mimetype.md":"nxjk8xi_","cn_src_ranuts_mode_subscribe.md":"BpJB8REw","cn_src_ranuts_utils_convertimagetobase64.md":"BQFuSXTt","cn_src_ranuts_utils_filterobj.md":"R6pWg6_Y","cn_src_ranuts_utils_formatjson.md":"w3L-Rhdf","cn_src_ranuts_utils_getcookie.md":"ChQkIFFD","cn_src_ranuts_utils_ocr.md":"CCSas4Mw","cn_src_ranuts_utils_str2xml.md":"CgTEUJ7O","cn_src_ranuts_utils_task.md":"BLYztfpY","cn_src_types_ts类型.md":"ctrtnDXL","cn_src_types_模式匹配.md":"DWHjoF0h","cn_src_types_类型运算.md":"BALncgle","cn_src_types_高级类型.md":"C_sodMsN","index.md":"C1mV7KLR","src_article_astparse_tokenizer.md":"m5lyUfzX","src_article_babel.md":"D0mGQXMo","src_article_bundle.md":"BvqfF4Lk","src_article_designmode.md":"DWnxNFS_","src_article_functionalprogramming.md":"Bbl-kdPi","src_article_imagemin.md":"CNzOGYN8","src_article_javascript_domload.md":"f1BWKaWm","src_article_sort_bubble_index.md":"DM2Rnnat","src_article_sort_bucket_index.md":"BTbi9aJ6","src_article_sort_count_index.md":"Ch5HRRp8","src_article_sort_heap_index.md":"D-XARjAx","src_article_sort_index.md":"LNhlg9Oa","src_article_sort_insert_index.md":"B-oNm95-","src_article_sort_merge_index.md":"D6h05wbs","src_article_sort_quick_index.md":"C9uemKEa","src_article_sort_radix_index.md":"Ca-wVT5Q","src_article_sort_select_index.md":"CgrU_Nnu","src_article_sort_shell_index.md":"DOUhMGGU","src_article_typescript_calculate.md":"CVW8Xao2","src_article_typescript_index.md":"CZyb-Nhf","src_article_typescript_pattern.md":"dRLdKmiq","src_article_typescript_reconstruction.md":"RJXBSd9W","src_article_typescript_recursion.md":"CxwYW6pd","src_article_typescript_uniontype.md":"lwThafAB","src_ranui_button_index.md":"Bv4CarK8","src_ranui_checkbox_index.md":"D6UQK38M","src_ranui_icon_index.md":"Cada4EMN","src_ranui_image_index.md":"iND1p-rT","src_ranui_index.md":"BQzsJSIN","src_ranui_input_index.md":"dqo--3Vk","src_ranui_loading_index.md":"Cr0qirnC","src_ranui_math_index.md":"CAvnqA_Q","src_ranui_message_index.md":"Dr_PZANG","src_ranui_modal_index.md":"BChGxskG","src_ranui_player_index.md":"DSXQVeNM","src_ranui_popover_index.md":"4cp6AOCz","src_ranui_preview_index.md":"GrIEkeLk","src_ranui_progress_index.md":"Be_sOmHB","src_ranui_radar_index.md":"CkBeOv58","src_ranui_select_index.md":"DgN8GI_q","src_ranui_skeleton_index.md":"DnZOMEV1","src_ranui_tab_index.md":"CciVbuSi","src_ranui_tabs_index.md":"CkEcQ38o","src_ranuts_binarytree_index.md":"DvglfkNJ","src_ranuts_bundler_index.md":"D3F9791C","src_ranuts_file_appendfile.md":"CN41s_o-","src_ranuts_file_fileinfo.md":"CZ2C_27B","src_ranuts_file_readdir.md":"d3nM0oPh","src_ranuts_file_readfile.md":"CwPAyuVM","src_ranuts_file_watchfile.md":"DYvq_aUK","src_ranuts_file_writefile.md":"CdGN48zU","src_ranuts_index.md":"CHFYg7WB","src_ranuts_mimetype_mimetype.md":"Bp_Buc7N","src_ranuts_mode_subscribe.md":"DmGp9s_W","src_ranuts_utils_convertimagetobase64.md":"CQdNGUWw","src_ranuts_utils_filterobj.md":"SUYinD61","src_ranuts_utils_formatjson.md":"BQjsxBql","src_ranuts_utils_getcookie.md":"glwGRuBT","src_ranuts_utils_ocr.md":"Bac0Ieci","src_ranuts_utils_str2xml.md":"BTPyA91O","src_ranuts_utils_task.md":"BA-6r3zJ","src_types_ts类型.md":"BAdYqP2z","src_types_模式匹配.md":"CFNrWo4G","src_types_类型运算.md":"DitU5zkD","src_types_高级类型.md":"BcsCXIM4"} +{"cn_index.md":"BLhfTKfk","cn_src_article_astparse_tokenizer.md":"fRsPjbrs","cn_src_article_babel.md":"igUT7dgP","cn_src_article_bundle.md":"DQFZ3YRD","cn_src_article_designmode.md":"B2Mi_NTb","cn_src_article_docpreview.md":"BpvHIdsV","cn_src_article_functionalprogramming.md":"B0WkJbH2","cn_src_article_imagemin.md":"O-LRpAFt","cn_src_article_javascript_domload.md":"Cc9Twcl2","cn_src_article_sort_bubble_index.md":"BL_0xBSg","cn_src_article_sort_bucket_index.md":"CInFho5i","cn_src_article_sort_count_index.md":"CLrg_eAi","cn_src_article_sort_heap_index.md":"CjUwiufT","cn_src_article_sort_index.md":"BhyMit8Y","cn_src_article_sort_insert_index.md":"C4G7YeWl","cn_src_article_sort_merge_index.md":"CrLtJpMk","cn_src_article_sort_quick_index.md":"CmIbA4AB","cn_src_article_sort_radix_index.md":"vBHH0tHh","cn_src_article_sort_select_index.md":"Cd2SKviA","cn_src_article_sort_shell_index.md":"CQc6o0U8","cn_src_article_systemdesign.md":"hF7LXas7","cn_src_article_typescript_calculate.md":"DFF0Jmnv","cn_src_article_typescript_index.md":"BE6MDNaH","cn_src_article_typescript_pattern.md":"ClYdggen","cn_src_article_typescript_reconstruction.md":"CwRrki5l","cn_src_article_typescript_recursion.md":"DXrQrnQX","cn_src_article_typescript_uniontype.md":"BEJLXxlO","cn_src_article_video.md":"D_lFXyPJ","cn_src_article_visual.md":"DseWRfx9","cn_src_note_centos.md":"DNIN5Wz6","cn_src_note_docker.md":"Dwk9bEUN","cn_src_note_libreoffice2wasm.md":"DG6uOQ65","cn_src_note_ubuntu.md":"boe-scbt","cn_src_ranui_button_index.md":"ByX0UCnc","cn_src_ranui_checkbox_index.md":"Csmdnt0O","cn_src_ranui_icon_index.md":"VgKq_hwc","cn_src_ranui_image_index.md":"DTxkaIXi","cn_src_ranui_index.md":"DbAIrpTI","cn_src_ranui_input_index.md":"Xj5VXLle","cn_src_ranui_loading_index.md":"Cd9-GnXr","cn_src_ranui_math_index.md":"vkrzCmwa","cn_src_ranui_message_index.md":"DQaOXw4A","cn_src_ranui_modal_index.md":"Ct_5BTpn","cn_src_ranui_player_index.md":"ClG2z7Rd","cn_src_ranui_popover_index.md":"CKCBCC0z","cn_src_ranui_preview_index.md":"HNgH1cdk","cn_src_ranui_progress_index.md":"B6p9KAvb","cn_src_ranui_radar_index.md":"D7lwGVbW","cn_src_ranui_select_index.md":"x3hqXtMG","cn_src_ranui_skeleton_index.md":"CxsOeOpu","cn_src_ranui_tab_index.md":"_Yrr2Do-","cn_src_ranui_tabs_index.md":"Y6X6hlvp","cn_src_ranuts_binarytree_index.md":"B0ahr_Mn","cn_src_ranuts_bundler_index.md":"DwAuuXbu","cn_src_ranuts_file_appendfile.md":"B63KmkE8","cn_src_ranuts_file_fileinfo.md":"CqJn8JuV","cn_src_ranuts_file_readdir.md":"BAFE9wq0","cn_src_ranuts_file_readfile.md":"_sEkTrwK","cn_src_ranuts_file_watchfile.md":"DizkqDrO","cn_src_ranuts_file_writefile.md":"Bu4zBA8r","cn_src_ranuts_index.md":"Br1tGjNi","cn_src_ranuts_mimetype_mimetype.md":"D8QLJam4","cn_src_ranuts_mode_subscribe.md":"Dfqta45A","cn_src_ranuts_utils_convertimagetobase64.md":"Baw_0VwK","cn_src_ranuts_utils_filterobj.md":"CxRU9J7j","cn_src_ranuts_utils_formatjson.md":"BPEAnSbs","cn_src_ranuts_utils_getcookie.md":"Cn937pro","cn_src_ranuts_utils_ocr.md":"DDRRaGe9","cn_src_ranuts_utils_str2xml.md":"Bde3XsNH","cn_src_ranuts_utils_task.md":"vM2hJa_Z","cn_src_types_ts类型.md":"BqYfr0BF","cn_src_types_模式匹配.md":"Cv2DtzlL","cn_src_types_类型运算.md":"DMkykj3f","cn_src_types_高级类型.md":"BDtDighX","index.md":"D7BH-fMN","src_article_astparse_tokenizer.md":"CuCr3w1T","src_article_babel.md":"Cy_u0Zgd","src_article_bundle.md":"CJFsaLBq","src_article_designmode.md":"CsQyDQ9L","src_article_functionalprogramming.md":"BPj6iTX9","src_article_imagemin.md":"F6ka3QYU","src_article_javascript_domload.md":"ISLuBQzT","src_article_sort_bubble_index.md":"DmhPntEW","src_article_sort_bucket_index.md":"1nocTUlS","src_article_sort_count_index.md":"Du6QhWUu","src_article_sort_heap_index.md":"DUjhT9HH","src_article_sort_index.md":"Day6RJ-s","src_article_sort_insert_index.md":"Cmba0n6W","src_article_sort_merge_index.md":"BYCAvkHG","src_article_sort_quick_index.md":"BVXZPTlC","src_article_sort_radix_index.md":"CZgriu-L","src_article_sort_select_index.md":"DVQbDLxi","src_article_sort_shell_index.md":"CGmp_Zfr","src_article_typescript_calculate.md":"vojGlJJE","src_article_typescript_index.md":"CkcO1_RK","src_article_typescript_pattern.md":"CLkB9hDT","src_article_typescript_reconstruction.md":"6hei5wds","src_article_typescript_recursion.md":"Cklrk5ux","src_article_typescript_uniontype.md":"DWIWcdin","src_ranui_button_index.md":"xyRWgUv8","src_ranui_checkbox_index.md":"DZWEhA_6","src_ranui_icon_index.md":"CCbuLCdk","src_ranui_image_index.md":"DWE25Dbt","src_ranui_index.md":"CLoF5T0G","src_ranui_input_index.md":"BYzbt7Jg","src_ranui_loading_index.md":"CPbTFkjm","src_ranui_math_index.md":"BEaDYiBh","src_ranui_message_index.md":"C9TUA1qN","src_ranui_modal_index.md":"CeMIN_J0","src_ranui_player_index.md":"DwTQwuCV","src_ranui_popover_index.md":"PFeffqLM","src_ranui_preview_index.md":"hkKqqHBY","src_ranui_progress_index.md":"CubSOb3E","src_ranui_radar_index.md":"DVubbf7p","src_ranui_select_index.md":"DQq3MVHp","src_ranui_skeleton_index.md":"BUf_Burp","src_ranui_tab_index.md":"DSSJuz31","src_ranui_tabs_index.md":"Y_dSHlvB","src_ranuts_binarytree_index.md":"TEku7NN9","src_ranuts_bundler_index.md":"DzEPrDNS","src_ranuts_file_appendfile.md":"1-6oJJ-H","src_ranuts_file_fileinfo.md":"C2z4D1Q8","src_ranuts_file_readdir.md":"BvSC38m-","src_ranuts_file_readfile.md":"Ac6LvpUh","src_ranuts_file_watchfile.md":"i-gzm-Mr","src_ranuts_file_writefile.md":"2z4SHchp","src_ranuts_index.md":"BJ436-Wa","src_ranuts_mimetype_mimetype.md":"CgbW52XX","src_ranuts_mode_subscribe.md":"CBjAEwg0","src_ranuts_utils_convertimagetobase64.md":"COMNqW1t","src_ranuts_utils_filterobj.md":"Q_-NUV8p","src_ranuts_utils_formatjson.md":"DQX6wWMq","src_ranuts_utils_getcookie.md":"CRjg9zYL","src_ranuts_utils_ocr.md":"COxYWSg4","src_ranuts_utils_str2xml.md":"84gjZ6IG","src_ranuts_utils_task.md":"DEROvLOz","src_types_ts类型.md":"Du9AK3lr","src_types_模式匹配.md":"DO7GM6xX","src_types_类型运算.md":"D_oUktxL","src_types_高级类型.md":"xpfmnY7g"} diff --git a/index.html b/index.html index 4719896641..4786167b26 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - +
Skip to content

ran

A Troupe of little vagrants of the world , leave your footprints in my words .

logo

Released under the MIT License.

- + \ No newline at end of file diff --git a/pagefind/fragment/en_1f28c1c.pf_fragment b/pagefind/fragment/en_1f28c1c.pf_fragment deleted file mode 100644 index 840f271ccc..0000000000 Binary files a/pagefind/fragment/en_1f28c1c.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_30709d8.pf_fragment b/pagefind/fragment/en_30709d8.pf_fragment new file mode 100644 index 0000000000..ba76ba37bf Binary files /dev/null and b/pagefind/fragment/en_30709d8.pf_fragment differ diff --git a/pagefind/fragment/en_324b2ed.pf_fragment b/pagefind/fragment/en_324b2ed.pf_fragment deleted file mode 100644 index f3b373ff21..0000000000 Binary files a/pagefind/fragment/en_324b2ed.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_3541bef.pf_fragment b/pagefind/fragment/en_3541bef.pf_fragment deleted file mode 100644 index 03dd4b5647..0000000000 Binary files a/pagefind/fragment/en_3541bef.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_38d8359.pf_fragment b/pagefind/fragment/en_38d8359.pf_fragment new file mode 100644 index 0000000000..5c2c188f62 Binary files /dev/null and b/pagefind/fragment/en_38d8359.pf_fragment differ diff --git a/pagefind/fragment/en_3df5c5f.pf_fragment b/pagefind/fragment/en_3df5c5f.pf_fragment new file mode 100644 index 0000000000..d6abce515f Binary files /dev/null and b/pagefind/fragment/en_3df5c5f.pf_fragment differ diff --git a/pagefind/fragment/en_6e7d47a.pf_fragment b/pagefind/fragment/en_449b12a.pf_fragment similarity index 96% rename from pagefind/fragment/en_6e7d47a.pf_fragment rename to pagefind/fragment/en_449b12a.pf_fragment index 7e5c912beb..d8d658a1eb 100644 Binary files a/pagefind/fragment/en_6e7d47a.pf_fragment and b/pagefind/fragment/en_449b12a.pf_fragment differ diff --git a/pagefind/fragment/en_34b032a.pf_fragment b/pagefind/fragment/en_5094588.pf_fragment similarity index 78% rename from pagefind/fragment/en_34b032a.pf_fragment rename to pagefind/fragment/en_5094588.pf_fragment index 8340088203..113e7aa7d7 100644 Binary files a/pagefind/fragment/en_34b032a.pf_fragment and b/pagefind/fragment/en_5094588.pf_fragment differ diff --git a/pagefind/fragment/en_5861ab4.pf_fragment b/pagefind/fragment/en_5861ab4.pf_fragment new file mode 100644 index 0000000000..4ff421494b Binary files /dev/null and b/pagefind/fragment/en_5861ab4.pf_fragment differ diff --git a/pagefind/fragment/en_5eef384.pf_fragment b/pagefind/fragment/en_5eef384.pf_fragment new file mode 100644 index 0000000000..d771fdda3b Binary files /dev/null and b/pagefind/fragment/en_5eef384.pf_fragment differ diff --git a/pagefind/fragment/en_63cffab.pf_fragment b/pagefind/fragment/en_63cffab.pf_fragment new file mode 100644 index 0000000000..cc8475888d Binary files /dev/null and b/pagefind/fragment/en_63cffab.pf_fragment differ diff --git a/pagefind/fragment/en_63f4b5c.pf_fragment b/pagefind/fragment/en_63f4b5c.pf_fragment deleted file mode 100644 index 21cdcf409e..0000000000 Binary files a/pagefind/fragment/en_63f4b5c.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_6a9915b.pf_fragment b/pagefind/fragment/en_6a9915b.pf_fragment new file mode 100644 index 0000000000..909fc9e67c Binary files /dev/null and b/pagefind/fragment/en_6a9915b.pf_fragment differ diff --git a/pagefind/fragment/en_7118fbe.pf_fragment b/pagefind/fragment/en_7118fbe.pf_fragment new file mode 100644 index 0000000000..bf0f8c233f Binary files /dev/null and b/pagefind/fragment/en_7118fbe.pf_fragment differ diff --git a/pagefind/fragment/en_78f477e.pf_fragment b/pagefind/fragment/en_78f477e.pf_fragment deleted file mode 100644 index c2db080521..0000000000 Binary files a/pagefind/fragment/en_78f477e.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_815cbc8.pf_fragment b/pagefind/fragment/en_815cbc8.pf_fragment new file mode 100644 index 0000000000..a69f5bd3c0 Binary files /dev/null and b/pagefind/fragment/en_815cbc8.pf_fragment differ diff --git a/pagefind/fragment/en_aab3547.pf_fragment b/pagefind/fragment/en_aab3547.pf_fragment deleted file mode 100644 index 884682c597..0000000000 Binary files a/pagefind/fragment/en_aab3547.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_af4a67a.pf_fragment b/pagefind/fragment/en_af4a67a.pf_fragment deleted file mode 100644 index c8db2dd51d..0000000000 Binary files a/pagefind/fragment/en_af4a67a.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_b110a5b.pf_fragment b/pagefind/fragment/en_b110a5b.pf_fragment new file mode 100644 index 0000000000..6fa8e10706 Binary files /dev/null and b/pagefind/fragment/en_b110a5b.pf_fragment differ diff --git a/pagefind/fragment/en_7fffa0e.pf_fragment b/pagefind/fragment/en_b24f7ef.pf_fragment similarity index 82% rename from pagefind/fragment/en_7fffa0e.pf_fragment rename to pagefind/fragment/en_b24f7ef.pf_fragment index 4b740b9a5c..ee42cfbfbf 100644 Binary files a/pagefind/fragment/en_7fffa0e.pf_fragment and b/pagefind/fragment/en_b24f7ef.pf_fragment differ diff --git a/pagefind/fragment/en_16d01f2.pf_fragment b/pagefind/fragment/en_b7c8c16.pf_fragment similarity index 87% rename from pagefind/fragment/en_16d01f2.pf_fragment rename to pagefind/fragment/en_b7c8c16.pf_fragment index 7b212ebfee..ee0ad4524c 100644 Binary files a/pagefind/fragment/en_16d01f2.pf_fragment and b/pagefind/fragment/en_b7c8c16.pf_fragment differ diff --git a/pagefind/fragment/en_6b4c4a9.pf_fragment b/pagefind/fragment/en_c2412d4.pf_fragment similarity index 76% rename from pagefind/fragment/en_6b4c4a9.pf_fragment rename to pagefind/fragment/en_c2412d4.pf_fragment index 5a527618a0..e1b6d78e20 100644 Binary files a/pagefind/fragment/en_6b4c4a9.pf_fragment and b/pagefind/fragment/en_c2412d4.pf_fragment differ diff --git a/pagefind/fragment/en_dccebd7.pf_fragment b/pagefind/fragment/en_dccebd7.pf_fragment deleted file mode 100644 index 2dedeba66b..0000000000 Binary files a/pagefind/fragment/en_dccebd7.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_edda142.pf_fragment b/pagefind/fragment/en_edda142.pf_fragment deleted file mode 100644 index c78a163277..0000000000 Binary files a/pagefind/fragment/en_edda142.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/en_70bee3b.pf_fragment b/pagefind/fragment/en_ee9f170.pf_fragment similarity index 95% rename from pagefind/fragment/en_70bee3b.pf_fragment rename to pagefind/fragment/en_ee9f170.pf_fragment index 3b0dab07cc..c1a284967c 100644 Binary files a/pagefind/fragment/en_70bee3b.pf_fragment and b/pagefind/fragment/en_ee9f170.pf_fragment differ diff --git a/pagefind/fragment/en_f575e71.pf_fragment b/pagefind/fragment/en_f575e71.pf_fragment deleted file mode 100644 index 52fe3656c0..0000000000 Binary files a/pagefind/fragment/en_f575e71.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_8c8e8be.pf_fragment b/pagefind/fragment/zh-cn_0ea6667.pf_fragment similarity index 88% rename from pagefind/fragment/zh-cn_8c8e8be.pf_fragment rename to pagefind/fragment/zh-cn_0ea6667.pf_fragment index 6ead354b91..8e618b4ec4 100644 Binary files a/pagefind/fragment/zh-cn_8c8e8be.pf_fragment and b/pagefind/fragment/zh-cn_0ea6667.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_154849c.pf_fragment b/pagefind/fragment/zh-cn_154849c.pf_fragment new file mode 100644 index 0000000000..14865e5c35 Binary files /dev/null and b/pagefind/fragment/zh-cn_154849c.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_99e62b8.pf_fragment b/pagefind/fragment/zh-cn_27c99c4.pf_fragment similarity index 88% rename from pagefind/fragment/zh-cn_99e62b8.pf_fragment rename to pagefind/fragment/zh-cn_27c99c4.pf_fragment index b0949ec6aa..c7f11c2768 100644 Binary files a/pagefind/fragment/zh-cn_99e62b8.pf_fragment and b/pagefind/fragment/zh-cn_27c99c4.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_2bc07af.pf_fragment b/pagefind/fragment/zh-cn_2bc07af.pf_fragment deleted file mode 100644 index e7181b7eca..0000000000 Binary files a/pagefind/fragment/zh-cn_2bc07af.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_88af4d0.pf_fragment b/pagefind/fragment/zh-cn_2c21dbd.pf_fragment similarity index 79% rename from pagefind/fragment/zh-cn_88af4d0.pf_fragment rename to pagefind/fragment/zh-cn_2c21dbd.pf_fragment index d9e4c6f64d..3e55f5f087 100644 Binary files a/pagefind/fragment/zh-cn_88af4d0.pf_fragment and b/pagefind/fragment/zh-cn_2c21dbd.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_39da2fe.pf_fragment b/pagefind/fragment/zh-cn_39da2fe.pf_fragment new file mode 100644 index 0000000000..0a318978ff Binary files /dev/null and b/pagefind/fragment/zh-cn_39da2fe.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_51bdb63.pf_fragment b/pagefind/fragment/zh-cn_51bdb63.pf_fragment new file mode 100644 index 0000000000..3036fcb37f Binary files /dev/null and b/pagefind/fragment/zh-cn_51bdb63.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_5584627.pf_fragment b/pagefind/fragment/zh-cn_5584627.pf_fragment new file mode 100644 index 0000000000..749afc2366 Binary files /dev/null and b/pagefind/fragment/zh-cn_5584627.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_b2e449c.pf_fragment b/pagefind/fragment/zh-cn_565fd27.pf_fragment similarity index 92% rename from pagefind/fragment/zh-cn_b2e449c.pf_fragment rename to pagefind/fragment/zh-cn_565fd27.pf_fragment index 85cb234633..827b2d30a8 100644 Binary files a/pagefind/fragment/zh-cn_b2e449c.pf_fragment and b/pagefind/fragment/zh-cn_565fd27.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_af6e5c4.pf_fragment b/pagefind/fragment/zh-cn_5ab33e7.pf_fragment similarity index 82% rename from pagefind/fragment/zh-cn_af6e5c4.pf_fragment rename to pagefind/fragment/zh-cn_5ab33e7.pf_fragment index 30c445ab99..9a331c2a56 100644 Binary files a/pagefind/fragment/zh-cn_af6e5c4.pf_fragment and b/pagefind/fragment/zh-cn_5ab33e7.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_5f3328b.pf_fragment b/pagefind/fragment/zh-cn_5f3328b.pf_fragment deleted file mode 100644 index 0602e96d92..0000000000 Binary files a/pagefind/fragment/zh-cn_5f3328b.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_68b3659.pf_fragment b/pagefind/fragment/zh-cn_68b3659.pf_fragment new file mode 100644 index 0000000000..ee6c2a3b13 Binary files /dev/null and b/pagefind/fragment/zh-cn_68b3659.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_69610a7.pf_fragment b/pagefind/fragment/zh-cn_69610a7.pf_fragment deleted file mode 100644 index 36093500dd..0000000000 Binary files a/pagefind/fragment/zh-cn_69610a7.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_217f9f1.pf_fragment b/pagefind/fragment/zh-cn_6df03b5.pf_fragment similarity index 96% rename from pagefind/fragment/zh-cn_217f9f1.pf_fragment rename to pagefind/fragment/zh-cn_6df03b5.pf_fragment index d7a59ac231..9b0d106996 100644 Binary files a/pagefind/fragment/zh-cn_217f9f1.pf_fragment and b/pagefind/fragment/zh-cn_6df03b5.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_8998463.pf_fragment b/pagefind/fragment/zh-cn_8998463.pf_fragment new file mode 100644 index 0000000000..92dab622e6 Binary files /dev/null and b/pagefind/fragment/zh-cn_8998463.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_8dcd37a.pf_fragment b/pagefind/fragment/zh-cn_8dcd37a.pf_fragment deleted file mode 100644 index 1b0dd27ad0..0000000000 Binary files a/pagefind/fragment/zh-cn_8dcd37a.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_964a4fd.pf_fragment b/pagefind/fragment/zh-cn_964a4fd.pf_fragment deleted file mode 100644 index 46270b9d83..0000000000 Binary files a/pagefind/fragment/zh-cn_964a4fd.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_9879172.pf_fragment b/pagefind/fragment/zh-cn_9879172.pf_fragment new file mode 100644 index 0000000000..3c10697231 Binary files /dev/null and b/pagefind/fragment/zh-cn_9879172.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_ad77d75.pf_fragment b/pagefind/fragment/zh-cn_ad77d75.pf_fragment deleted file mode 100644 index 03d7817dbc..0000000000 Binary files a/pagefind/fragment/zh-cn_ad77d75.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_bc27649.pf_fragment b/pagefind/fragment/zh-cn_bc27649.pf_fragment deleted file mode 100644 index 8b6226e438..0000000000 Binary files a/pagefind/fragment/zh-cn_bc27649.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_82b59c2.pf_fragment b/pagefind/fragment/zh-cn_c5d845d.pf_fragment similarity index 85% rename from pagefind/fragment/zh-cn_82b59c2.pf_fragment rename to pagefind/fragment/zh-cn_c5d845d.pf_fragment index 1a0fe3a398..bd31831cc0 100644 Binary files a/pagefind/fragment/zh-cn_82b59c2.pf_fragment and b/pagefind/fragment/zh-cn_c5d845d.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_c999918.pf_fragment b/pagefind/fragment/zh-cn_c999918.pf_fragment deleted file mode 100644 index af9850b644..0000000000 Binary files a/pagefind/fragment/zh-cn_c999918.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_d388141.pf_fragment b/pagefind/fragment/zh-cn_d388141.pf_fragment new file mode 100644 index 0000000000..cd99585f27 Binary files /dev/null and b/pagefind/fragment/zh-cn_d388141.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_20933c3.pf_fragment b/pagefind/fragment/zh-cn_e6dde39.pf_fragment similarity index 94% rename from pagefind/fragment/zh-cn_20933c3.pf_fragment rename to pagefind/fragment/zh-cn_e6dde39.pf_fragment index f803a8c39d..36e823a91c 100644 Binary files a/pagefind/fragment/zh-cn_20933c3.pf_fragment and b/pagefind/fragment/zh-cn_e6dde39.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_f260254.pf_fragment b/pagefind/fragment/zh-cn_e831ca8.pf_fragment similarity index 79% rename from pagefind/fragment/zh-cn_f260254.pf_fragment rename to pagefind/fragment/zh-cn_e831ca8.pf_fragment index 0d4818200f..bf3f816d9f 100644 Binary files a/pagefind/fragment/zh-cn_f260254.pf_fragment and b/pagefind/fragment/zh-cn_e831ca8.pf_fragment differ diff --git a/pagefind/fragment/zh-cn_ec79f02.pf_fragment b/pagefind/fragment/zh-cn_ec79f02.pf_fragment deleted file mode 100644 index 7779eac22d..0000000000 Binary files a/pagefind/fragment/zh-cn_ec79f02.pf_fragment and /dev/null differ diff --git a/pagefind/fragment/zh-cn_f2527de.pf_fragment b/pagefind/fragment/zh-cn_f2527de.pf_fragment new file mode 100644 index 0000000000..14794f1e70 Binary files /dev/null and b/pagefind/fragment/zh-cn_f2527de.pf_fragment differ diff --git a/pagefind/pagefind-entry.json b/pagefind/pagefind-entry.json index 54dc367086..57c76a9a20 100644 --- a/pagefind/pagefind-entry.json +++ b/pagefind/pagefind-entry.json @@ -1 +1 @@ -{"version":"1.1.1","languages":{"zh-cn":{"hash":"zh-cn_5630655c2f3e8","wasm":null,"page_count":72},"en":{"hash":"en_9aedcc98b3","wasm":"en","page_count":64}}} \ No newline at end of file +{"version":"1.1.1","languages":{"zh-cn":{"hash":"zh-cn_b71a7d8847108","wasm":null,"page_count":72},"en":{"hash":"en_853ef581ec","wasm":"en","page_count":64}}} \ No newline at end of file diff --git a/pagefind/pagefind.en_853ef581ec.pf_meta b/pagefind/pagefind.en_853ef581ec.pf_meta new file mode 100644 index 0000000000..3a4f8d4bdb Binary files /dev/null and b/pagefind/pagefind.en_853ef581ec.pf_meta differ diff --git a/pagefind/pagefind.en_9aedcc98b3.pf_meta b/pagefind/pagefind.en_9aedcc98b3.pf_meta deleted file mode 100644 index 44831f6697..0000000000 Binary files a/pagefind/pagefind.en_9aedcc98b3.pf_meta and /dev/null differ diff --git a/pagefind/pagefind.zh-cn_5630655c2f3e8.pf_meta b/pagefind/pagefind.zh-cn_5630655c2f3e8.pf_meta deleted file mode 100644 index 583acb1d8b..0000000000 Binary files a/pagefind/pagefind.zh-cn_5630655c2f3e8.pf_meta and /dev/null differ diff --git a/pagefind/pagefind.zh-cn_b71a7d8847108.pf_meta b/pagefind/pagefind.zh-cn_b71a7d8847108.pf_meta new file mode 100644 index 0000000000..e34db43d36 Binary files /dev/null and b/pagefind/pagefind.zh-cn_b71a7d8847108.pf_meta differ diff --git a/src/article/astParse/tokenizer.html b/src/article/astParse/tokenizer.html index 8175858f83..677535b32d 100644 --- a/src/article/astParse/tokenizer.html +++ b/src/article/astParse/tokenizer.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -226,8 +226,8 @@ { type: 'RightParen', value: ')', start: 17, end: 18 }, { type: 'LeftCurly', value: '{', start: 19, end: 20 }, { type: 'RightCurly', value: '}', start: 20, end: 21 }, -];

一个简易版本的分词器已经被我们开发出来了,不过目前的分词器还比较简陋,仅仅支持有限的语法,不过在明确了核心的开发步骤之后,后面继续完善的过程就比较简单了。

四。编写语法分析器(Parser)

在解析出词法 token 之后,我们就可以进入语法分析阶段了。在这个阶段,我们会依次遍历 token ,对代码进行语法结构层面的分析,最后的目标是生成 AST 数据结构。至于代码的 AST 结构到底是什么样子,你可以去 AST Explorer 网站进行在线预览:

接下来,我们要做的就是将 token 数组转换为上图所示的 AST 数据。

开发步骤主要分为:

- +];

一个简易版本的分词器已经被我们开发出来了,不过目前的分词器还比较简陋,仅仅支持有限的语法,不过在明确了核心的开发步骤之后,后面继续完善的过程就比较简单了。

四。编写语法分析器(Parser)

在解析出词法 token 之后,我们就可以进入语法分析阶段了。在这个阶段,我们会依次遍历 token ,对代码进行语法结构层面的分析,最后的目标是生成 AST 数据结构。至于代码的 AST 结构到底是什么样子,你可以去 AST Explorer 网站进行在线预览:

接下来,我们要做的就是将 token 数组转换为上图所示的 AST 数据。

开发步骤主要分为:

+ \ No newline at end of file diff --git a/src/article/babel.html b/src/article/babel.html index 60263c81af..d7d41d5d93 100644 --- a/src/article/babel.html +++ b/src/article/babel.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

Babel

babel 核心库主要是:

  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • @babel/code-frame 可以创建友好的报错信息
  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。

Released under the MIT License.

- +
Skip to content

Babel

babel 核心库主要是:

  • @babel/parser 对源码进行 parse,可以通过 plugins、sourceType 等来指定 parse 语法,功能是把源码转成 AST。
  • @babel/traverse 通过 visitor 函数对遍历到的 ast 进行处理,分为 enter 和 exit 两个阶段,具体操作 AST 使用 path 的 api,还可以通过 state 来在遍历过程中传递一些数据
  • @babel/types 用于创建、判断 AST 节点,提供了 xxx、isXxx、assertXxx 的 api
  • @babel/template 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • @babel/code-frame 可以创建友好的报错信息
  • @babel/generator 打印 AST 成目标代码字符串,支持 comments、minified、sourceMaps 等选项。
  • @babel/core 基于上面的包来完成 babel 的编译流程,并应用 plugin 和 preset。

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/article/bundle.html b/src/article/bundle.html index 4d96e17b57..59456ff8e4 100644 --- a/src/article/bundle.html +++ b/src/article/bundle.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

Bundle

Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:

  • 编译能力
  • 插件机制
  • HMR
  • cli 和命令行能力

Released under the MIT License.

- +
Skip to content

Bundle

Bundle 的本质就是输入,转换,输出。在机器上直接运行的代码,往往都难以维护和理解,我们需要将开发者方便理解和维护的代码,通过打包等工具转换成方便机器或者程序使用的代码。对于 web 前端来说,打包工具,至少需要以下功能:

  • 编译能力
  • 插件机制
  • HMR
  • cli 和命令行能力

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/article/designMode.html b/src/article/designMode.html index 6083b1c39d..76baa3d90c 100644 --- a/src/article/designMode.html +++ b/src/article/designMode.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -785,8 +785,8 @@ Visitor.push(a,1,2,3,4); Visitor.push(a,4,5,6); Visitor.pop(a); -Visitor.splice(a,2);

The visitor pattern solves the coupling between the data and the manipulation of the data, making the manipulation of the data independent of the data, so that it can freely evolve. Therefore, the visitor pattern is more suitable for those environments where the data is stable but the data manipulation method is variable.

Advantages:

Disadvantage:

VII.Sum up

After systematically studying design patterns, you can see in your past development experience that design patterns are everywhere. Before learning design patterns, we often rely on past experience and wisdom to improve the design of a system, and many of these experiences coincide with the idea of a certain design pattern.

There are still some places that are not fully understood, and I would like to point out the mistakes in the article.

VIII.Reference material

- +Visitor.splice(a,2);

The visitor pattern solves the coupling between the data and the manipulation of the data, making the manipulation of the data independent of the data, so that it can freely evolve. Therefore, the visitor pattern is more suitable for those environments where the data is stable but the data manipulation method is variable.

Advantages:

Disadvantage:

VII.Sum up

After systematically studying design patterns, you can see in your past development experience that design patterns are everywhere. Before learning design patterns, we often rely on past experience and wisdom to improve the design of a system, and many of these experiences coincide with the idea of a certain design pattern.

There are still some places that are not fully understood, and I would like to point out the mistakes in the article.

VIII.Reference material

+ \ No newline at end of file diff --git a/src/article/functionalProgramming.html b/src/article/functionalProgramming.html index 5805f46104..59c664cc0c 100644 --- a/src/article/functionalProgramming.html +++ b/src/article/functionalProgramming.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -379,8 +379,8 @@ }; let r = readFile('package.json') //这里可以用 map 去处理内容 .flatMap(print) - .join();

参考资料

- + .join();

参考资料

+ \ No newline at end of file diff --git a/src/article/imagemin.html b/src/article/imagemin.html index 14d15d4d3b..bdbc6525bd 100644 --- a/src/article/imagemin.html +++ b/src/article/imagemin.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

imagemin 图片压缩源码分析

Released under the MIT License.

- +
Skip to content

imagemin 图片压缩源码分析

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/article/javascript/domLoad.html b/src/article/javascript/domLoad.html index da462d36dc..27a24d8837 100644 --- a/src/article/javascript/domLoad.html +++ b/src/article/javascript/domLoad.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -44,8 +44,8 @@
Skip to content

页面加载完成后事件

window.onload

DOMContentLoaded

js
document.addEventListener('DOMContentLoaded', fun);

<body onload="fun()">

readyState

js
document.readyState;
 
-document.onreadystatechange;

一个文档的 readyState 可以是以下之一:

  • loading / 加载。document 仍在加载。
  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
  • complete / 完成。T 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。

Released under the MIT License.

- +document.onreadystatechange;

一个文档的 readyState 可以是以下之一:

+ \ No newline at end of file diff --git a/src/article/sort/bubble/index.html b/src/article/sort/bubble/index.html index 44d227bf6a..e412a39667 100644 --- a/src/article/sort/bubble/index.html +++ b/src/article/sort/bubble/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -55,8 +55,8 @@ } } return list; -}; - +}; + \ No newline at end of file diff --git a/src/article/sort/bucket/index.html b/src/article/sort/bucket/index.html index 8ad27403d9..3a2be3391a 100644 --- a/src/article/sort/bucket/index.html +++ b/src/article/sort/bucket/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -98,8 +98,8 @@ list = list.concat(count(buckets[i])); } return list; -};

Algorithm analysis

Bucket sorting best uses linear time O(n), and the time complexity of bucket sorting depends on the time complexity of sorting data between buckets, because the time complexity of other parts is O(n). Obviously, the smaller the buckets, the less data there is between them, and the less time it takes to sort them. But the corresponding space consumption will increase.

- +};

Algorithm analysis

Bucket sorting best uses linear time O(n), and the time complexity of bucket sorting depends on the time complexity of sorting data between buckets, because the time complexity of other parts is O(n). Obviously, the smaller the buckets, the less data there is between them, and the less time it takes to sort them. But the corresponding space consumption will increase.

+ \ No newline at end of file diff --git a/src/article/sort/count/index.html b/src/article/sort/count/index.html index 47e9c98029..a4af5dd577 100644 --- a/src/article/sort/count/index.html +++ b/src/article/sort/count/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -76,8 +76,8 @@ } } return result; -};

Algorithm analysis

Counting sort is a stable sorting algorithm. When the input elements are n integers between 0 and k, the time complexity is O(n+k) and the space complexity is also O(n+k), which sorts faster than any comparison sorting algorithm. Counting sort is an efficient sorting algorithm when k is not large and the sequence is concentrated.

- +};

Algorithm analysis

Counting sort is a stable sorting algorithm. When the input elements are n integers between 0 and k, the time complexity is O(n+k) and the space complexity is also O(n+k), which sorts faster than any comparison sorting algorithm. Counting sort is an efficient sorting algorithm when k is not large and the sequence is concentrated.

+ \ No newline at end of file diff --git a/src/article/sort/heap/index.html b/src/article/sort/heap/index.html index ae492d6fe1..0f808761f6 100644 --- a/src/article/sort/heap/index.html +++ b/src/article/sort/heap/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -92,8 +92,8 @@ const heap = (list: Array<number>): Array<number> => { const { value } = new Heap(list); return value; -}; - +}; + \ No newline at end of file diff --git a/src/article/sort/index.html b/src/article/sort/index.html index c8a7dbe26e..9036a18a97 100644 --- a/src/article/sort/index.html +++ b/src/article/sort/index.html @@ -13,7 +13,7 @@ - + @@ -37,14 +37,14 @@ - + -
Skip to content

Ten classic sorting algorithms

The ten common sorting algorithms can be divided into two broad categories:

  • Comparator sort:By comparison to determine the relative order between elements, because its time complexity can not break through O(nlogn), so it is also called nonlinear time comparison class sorting.
  • Non-comparison sort:Instead of determining the relative order between elements by comparison, it can break through the time lower bound based on comparison sort and run in linear time, so it is also called linear time non-comparison sort.

Sorting algorithm classification

Algorithm complexity

Algorithm complexity

  • Stable: If a was originally in front of b and a=b, a is still in front of b after sorting.
  • Unstable: If a originally comes before b and a=b, a may come after b after sorting.
  • Time complexity: The total number of operations on sorted data. Reflects what rule the number of operations presents when n changes.
  • Spatial complexity: refers to the measure of the storage space required when the algorithm is executed in the computer, and it is also a function of the data size n.

Released under the MIT License.

- +
Skip to content

Ten classic sorting algorithms

The ten common sorting algorithms can be divided into two broad categories:

  • Comparator sort:By comparison to determine the relative order between elements, because its time complexity can not break through O(nlogn), so it is also called nonlinear time comparison class sorting.
  • Non-comparison sort:Instead of determining the relative order between elements by comparison, it can break through the time lower bound based on comparison sort and run in linear time, so it is also called linear time non-comparison sort.

Sorting algorithm classification

Algorithm complexity

Algorithm complexity

  • Stable: If a was originally in front of b and a=b, a is still in front of b after sorting.
  • Unstable: If a originally comes before b and a=b, a may come after b after sorting.
  • Time complexity: The total number of operations on sorted data. Reflects what rule the number of operations presents when n changes.
  • Spatial complexity: refers to the measure of the storage space required when the algorithm is executed in the computer, and it is also a function of the data size n.

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/article/sort/insert/index.html b/src/article/sort/insert/index.html index 72ba3c3918..6c7d61cb0d 100644 --- a/src/article/sort/insert/index.html +++ b/src/article/sort/insert/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -55,8 +55,8 @@ list[preIndex + 1] = current; } return list; -};

Algorithm analysis

Insertion sort in the implementation, usually use in-place sort (that is, only need to use O(1) of the additional space of the sort), so in the process of scanning from back to forward, the sorted elements need to be repeatedly moved backward step by step, to provide insertion space for the latest elements.

- +};

Algorithm analysis

Insertion sort in the implementation, usually use in-place sort (that is, only need to use O(1) of the additional space of the sort), so in the process of scanning from back to forward, the sorted elements need to be repeatedly moved backward step by step, to provide insertion space for the latest elements.

+ \ No newline at end of file diff --git a/src/article/sort/merge/index.html b/src/article/sort/merge/index.html index 1c81b489fe..78b32b3428 100644 --- a/src/article/sort/merge/index.html +++ b/src/article/sort/merge/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -75,8 +75,8 @@ const left = list.slice(0, middle); const right = list.slice(middle); return combine(merge(left), merge(right)); -};

Algorithm analysis

Merge sort is a stable sort method. Like select sort, merge sort performs independently of the input data, but performs much better than select sort because it is always O(nlogn) in time complexity. The trade-off is extra memory space.

- +};

Algorithm analysis

Merge sort is a stable sort method. Like select sort, merge sort performs independently of the input data, but performs much better than select sort because it is always O(nlogn) in time complexity. The trade-off is extra memory space.

+ \ No newline at end of file diff --git a/src/article/sort/quick/index.html b/src/article/sort/quick/index.html index b4dcc14440..e2ee11b0e6 100644 --- a/src/article/sort/quick/index.html +++ b/src/article/sort/quick/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -93,8 +93,8 @@ const quick = (list: number[] = []): number[] => { const size = list.length; return combine(list, 0, size - 1); -}; - +}; + \ No newline at end of file diff --git a/src/article/sort/radix/index.html b/src/article/sort/radix/index.html index 568dd5e3ab..9b2fd2fbbc 100644 --- a/src/article/sort/radix/index.html +++ b/src/article/sort/radix/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -78,8 +78,8 @@ list = list.concat(count(buckets[i])); } return list; -};

Algorithm analysis

Radix sort is based on distributive sort, so it is stable. But the performance of radix sort is slightly worse than bucket sort, each keyword bucket allocation requires O(n) time complexity, and after allocation to get a new keyword sequence requires O(n) time complexity. If the data to be sorted can be divided into d keywords, the time complexity of radix sort will be O(d*2n), of course, d is far less than n, so it is basically linear level.

The spatial complexity of radix sort is O(n+k), where k is the number of buckets. Generally speaking, n> > k, so you need about n extra Spaces.

- +};

Algorithm analysis

Radix sort is based on distributive sort, so it is stable. But the performance of radix sort is slightly worse than bucket sort, each keyword bucket allocation requires O(n) time complexity, and after allocation to get a new keyword sequence requires O(n) time complexity. If the data to be sorted can be divided into d keywords, the time complexity of radix sort will be O(d*2n), of course, d is far less than n, so it is basically linear level.

The spatial complexity of radix sort is O(n+k), where k is the number of buckets. Generally speaking, n> > k, so you need about n extra Spaces.

+ \ No newline at end of file diff --git a/src/article/sort/select/index.html b/src/article/sort/select/index.html index 5f63135344..0f4edf6cf1 100644 --- a/src/article/sort/select/index.html +++ b/src/article/sort/select/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -59,8 +59,8 @@ } } return list; -};

Algorithm analysis

One of the most stable sorting algorithms, because whatever data goes in is O(n2) time complexity, so when using it, the smaller the data size, the better. The only benefit may be that it doesn't take up extra memory space. In theory, selection sorting may also be the most common sorting method that people think of.

- +};

Algorithm analysis

One of the most stable sorting algorithms, because whatever data goes in is O(n2) time complexity, so when using it, the smaller the data size, the better. The only benefit may be that it doesn't take up extra memory space. In theory, selection sorting may also be the most common sorting method that people think of.

+ \ No newline at end of file diff --git a/src/article/sort/shell/index.html b/src/article/sort/shell/index.html index b2f19a44df..8ba47bcbbe 100644 --- a/src/article/sort/shell/index.html +++ b/src/article/sort/shell/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -62,8 +62,8 @@ } } return list; -};

Algorithm analysis

The core of Hill sort is the setting of interval sequence. The interval sequence can be set in advance, and the interval sequence can be defined dynamically. The algorithm for dynamically defining interval sequences was developed by Robert Sedgewick, co-author of Algorithms (4th Edition).

- +};

Algorithm analysis

The core of Hill sort is the setting of interval sequence. The interval sequence can be set in advance, and the interval sequence can be defined dynamically. The algorithm for dynamically defining interval sequences was developed by Robert Sedgewick, co-author of Algorithms (4th Edition).

+ \ No newline at end of file diff --git a/src/article/typescript/calculate.html b/src/article/typescript/calculate.html index 9fc4eb5252..274410251e 100644 --- a/src/article/typescript/calculate.html +++ b/src/article/typescript/calculate.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -72,8 +72,8 @@ ? CurrentArr['length'] : FibonacciLoop<CurrentArr, [...PrevArr, ...CurrentArr], [...IndexArr, unknown], Num>; -type Fibonacci<Num extends number> = FibonacciLoop<[1], [], [], Num>;

类型参数 PrevArr 是代表之前的累加值的数组。类型参数 CurrentArr 是代表当前数值的数组。

类型参数 IndexArr 用于记录 index,每次递归加一,默认值是 [],代表从 0 开始。

类型参数 Num 代表求数列的第几个数。

判断当前 index 也就是 IndexArr['length'] 是否到了 Num,到了就返回当前的数值 CurrentArr['length']。

否则求出当前 index 对应的数值,用之前的数加上当前的数 [...PrevArr, ... CurrentArr]。

然后继续递归,index + 1,也就是 [...IndexArr, unknown]。

这就是递归计算 Fibinacci 数列的数的过程。

可以正确的算出第 8 个数是 21:

- +type Fibonacci<Num extends number> = FibonacciLoop<[1], [], [], Num>;

类型参数 PrevArr 是代表之前的累加值的数组。类型参数 CurrentArr 是代表当前数值的数组。

类型参数 IndexArr 用于记录 index,每次递归加一,默认值是 [],代表从 0 开始。

类型参数 Num 代表求数列的第几个数。

判断当前 index 也就是 IndexArr['length'] 是否到了 Num,到了就返回当前的数值 CurrentArr['length']。

否则求出当前 index 对应的数值,用之前的数加上当前的数 [...PrevArr, ... CurrentArr]。

然后继续递归,index + 1,也就是 [...IndexArr, unknown]。

这就是递归计算 Fibinacci 数列的数的过程。

可以正确的算出第 8 个数是 21:

+ \ No newline at end of file diff --git a/src/article/typescript/index.html b/src/article/typescript/index.html index 05d942046b..51850f86f5 100644 --- a/src/article/typescript/index.html +++ b/src/article/typescript/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -197,8 +197,8 @@ type TestAnyResult = TestAny<any>; // type TestAnyResult = 1 | 2

联合类型、never、any 在作为条件类型的类型参数时的这些特殊情况,也会在后面的原理篇来解释原因。

IsTuple

元组类型怎么判断呢?它和数组有什么区别呢?

元组类型的 length 是数字字面量,而数组的 length 是 number。

ts
type len

UnionToIntersection

类型之间是有父子关系的,更具体的那个是子类型,比如 A 和 B 的交叉类型 A & B 就是联合类型 A | B 的子类型,因为更具体。

如果允许父类型赋值给子类型,就叫做逆变

如果允许子类型赋值给父类型,就叫做协变

(关于逆变、协变等概念的详细解释可以看原理篇)

在 TypeScript 中有函数参数是有逆变的性质的,也就是如果参数可能是多个类型,参数类型会变成它们的交叉类型。

所以联合转交叉可以这样实现:

ts
type UnionToIntersection<U> = (U extends U ? (x: U) => unknown : never) extends (x: infer R) => unknown ? R : never;

类型参数 U 是要转换的联合类型。

U extends U 是为了触发联合类型的 distributive 的性质,让每个类型单独传入做计算,最后合并。

利用 U 做为参数构造个函数,通过模式匹配取参数的类型。

结果就是交叉类型

函数参数的逆变性质一般就联合类型转交叉类型会用,记住就行。

GetOptional

如何提取索引类型中的可选索引呢?

这也要利用可选索引的特性:可选索引的值为 undefined 和值类型的联合类型。

过滤可选索引,就要构造一个新的索引类型,过程中做过滤:

ts
type GetOptional<Obj extends Record<string, any>> = {
   [Key in keyof Obj as {} extends Pick<Obj, Key> ? Key : never]: Obj[Key];
-};

类型参数 Obj 为待处理的索引类型,类型约束为索引为 string、值为任意类型的索引类型 Record<string, any>。

用映射类型的语法重新构造索引类型,索引是之前的索引也就是 Key in keyof Obj,但要做一些过滤,也就是 as 之后的部分。

过滤的方式就是单独取出该索引之后,判断空对象是否是其子类型。

这里的 Pick 是 ts 提供的内置高级类型,就是取出某个 Key 构造新的索引类型:

ts
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

比如单独取出 age 构造的新的索引类型是这样的:

可选的意思是这个索引可能没有,没有的时候,那 Pick<Obj, Key> 就是空的,所以 {} extends Pick<Obj, Key> 就能过滤出可选索引。

值的类型依然是之前的,也就是 Obj[Key]。

这样,就能过滤出所有可选索引,构造成新的索引类型:

总结

- +};

类型参数 Obj 为待处理的索引类型,类型约束为索引为 string、值为任意类型的索引类型 Record<string, any>。

用映射类型的语法重新构造索引类型,索引是之前的索引也就是 Key in keyof Obj,但要做一些过滤,也就是 as 之后的部分。

过滤的方式就是单独取出该索引之后,判断空对象是否是其子类型。

这里的 Pick 是 ts 提供的内置高级类型,就是取出某个 Key 构造新的索引类型:

ts
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

比如单独取出 age 构造的新的索引类型是这样的:

可选的意思是这个索引可能没有,没有的时候,那 Pick<Obj, Key> 就是空的,所以 {} extends Pick<Obj, Key> 就能过滤出可选索引。

值的类型依然是之前的,也就是 Obj[Key]。

这样,就能过滤出所有可选索引,构造成新的索引类型:

总结

+ \ No newline at end of file diff --git a/src/article/typescript/pattern.html b/src/article/typescript/pattern.html index a58b00796d..d683739feb 100644 --- a/src/article/typescript/pattern.html +++ b/src/article/typescript/pattern.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -134,8 +134,8 @@ : never : never;

类型参数 Props 为待处理的类型。

通过 keyof Props 取出 Props 的所有索引构成的联合类型,判断下 ref 是否在其中,也就是 'ref' extends keyof Props。

为什么要做这个判断,上面注释里写了:

在 ts3.0 里面如果没有对应的索引,Obj[Key] 返回的是 {} 而不是 never,所以这样做下兼容处理。

如果有 ref 这个索引的话,就通过 infer 提取 Value 的类型返回,否则返回 never。

ts
type GetPropsRefResult = GetPropsRef<{ ref: 1; name: 'str' }>;
 // type GetPropsRefResult = 1

当 ref 为 undefined 时:

ts
type GetPropsRefResult = GetPropsRef<{ ref: undefined; name: 'str' }>;
-// type GetPropsRefResult = undefined
- +// type GetPropsRefResult = undefined + \ No newline at end of file diff --git a/src/article/typescript/reconstruction.html b/src/article/typescript/reconstruction.html index eb4512b7d7..51e9b3f545 100644 --- a/src/article/typescript/reconstruction.html +++ b/src/article/typescript/reconstruction.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -89,8 +89,8 @@ [Key in keyof T]-?: T[Key]; };

给索引类型 T 的索引去掉 ? 的修饰,其余保持不变。

FilterByValueType

可以在构造新索引类型的时候根据值的类型做下过滤:

ts
type FilterByValueType<Obj extends Record<string, any>, ValueType> = {
   [Key in keyof Obj as Obj[Key] extends ValueType ? Key : never]: Obj[Key];
-};

类型参数 Obj 为要处理的索引类型,通过 extends 约束为索引为 string,值为任意类型的索引类型 Record<string, any>。

类型参数 ValueType 为要过滤出的值的类型。

构造新的索引类型,索引为 Obj 的索引,也就是 Key in keyof Obj,但要做一些变换,也就是 as 之后的部分。

如果原来索引的值 Obj[Key] 是 ValueType 类型,索引依然为之前的索引 Key,否则索引设置为 never,never 的索引会在生成新的索引类型时被去掉。

值保持不变,依然为原来索引的值,也就是 Obj[Key]。

这样就达到了过滤索引类型的索引,产生新的索引类型的目的:

- +};

类型参数 Obj 为要处理的索引类型,通过 extends 约束为索引为 string,值为任意类型的索引类型 Record<string, any>。

类型参数 ValueType 为要过滤出的值的类型。

构造新的索引类型,索引为 Obj 的索引,也就是 Key in keyof Obj,但要做一些变换,也就是 as 之后的部分。

如果原来索引的值 Obj[Key] 是 ValueType 类型,索引依然为之前的索引 Key,否则索引设置为 never,never 的索引会在生成新的索引类型时被去掉。

值保持不变,依然为原来索引的值,也就是 Obj[Key]。

这样就达到了过滤索引类型的索引,产生新的索引类型的目的:

+ \ No newline at end of file diff --git a/src/article/typescript/recursion.html b/src/article/typescript/recursion.html index d0129f8b7a..92f3bcc446 100644 --- a/src/article/typescript/recursion.html +++ b/src/article/typescript/recursion.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -106,8 +106,8 @@ : DeepReadonly<Obj[Key]> : Obj[Key]; } - : never; - + : never; + \ No newline at end of file diff --git a/src/article/typescript/unionType.html b/src/article/typescript/unionType.html index 4dae48235b..66ceeecaf3 100644 --- a/src/article/typescript/unionType.html +++ b/src/article/typescript/unionType.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -62,8 +62,8 @@ > = `${Block}__${Element[number]}--${Modifiers[number]}`;

类型参数 Block、Element、Modifiers 分别是 bem 规范的三部分,其中 Element 和 Modifiers 都可能多个,约束为 string[]。

构造一个字符串类型,其中 Element 和 Modifiers 通过索引访问来变为联合类型。

字符串类型中遇到联合类型的时候,会每个元素单独传入计算,也就是这样的效果:

ts
type RemResult = BEM<'a', ['b', 'c'], ['d', 'e']>;
 // type RemResult = 'a__b--d' | 'a__b--e' | 'a__c--d' | 'a__b--e'

可以看到,用好了联合类型,确实能简化类型编程逻辑。

AllCombinations

我们再来实现一个全组合的高级类型,也是联合类型相关的:

希望传入 'A' | 'B' 的时候,能够返回所有的组合: 'A' | 'B' | 'BA' | 'AB'。

这种全组合问题的实现思路就是两两组合,组合出的字符串再和其他字符串两两组和:

比如 'A' | 'B' | 'c',就是 A 和 B、C 组合,B 和 A、C 组合,C 和 A、B 组合。然后组合出来的字符串再和其他字符串组合。

任何两个类型的组合有四种:A、B、AB、BA

ts
type Combination<A extends string, B extends string> = A | B | `${A}${B}` | `${B}${A}`;

然后构造出来的字符串再和其他字符串组合。

所以全组合的高级类型就是这样:

ts
type AllCombinations<A extends string, B extends string = A> = A extends A
   ? Combination<A, AllCombinations<Exclude<B, A>>>
-  : never;

类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

总结

联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

有两点特别要注意:

我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

- + : never;

类型参数 A、B 是待组合的两个联合类型,B 默认是 A 也就是同一个。

A extends A 的意义就是让联合类型每个类型单独传入做处理,上面我们刚学会。

A 的处理就是 A 和 B 中去掉 A 以后的所有类型组合,也就是 Combination<A, B 去掉 A 以后的所有组合>。

而 B 去掉 A 以后的所有组合就是 AllCombinations<Exclude<B, A>>,所以全组合就是 Combination<A, AllCombinations<Exclude<B, A>>>。

总结

联合类型中的每个类型都是相互独立的,TypeScript 对它做了特殊处理,也就是遇到字符串类型、条件类型的时候会把每个类型单独传入做计算,最后把每个类型的计算结果合并成联合类型。

条件类型左边是联合类型的时候就会触法这种处理,叫做分布式条件类型。

有两点特别要注意:

我们后面做了一些案例,发现联合类型的这种 distributive 的特性确实能简化类型编程,但是也增加了认知成本,不过这也是不可避免的事。

+ \ No newline at end of file diff --git a/src/ranui/button/index.html b/src/ranui/button/index.html index 6d38b60886..482941ae34 100644 --- a/src/ranui/button/index.html +++ b/src/ranui/button/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -50,8 +50,8 @@ <r-button type="text" disabled>Text button</r-button> <r-button disabled>Default button</r-button>

icon

When you need to embed an Icon inside a Button, you can set the icon property or use the Icon component directly inside a Button.

If you want to control the specific position of the Icon, you can only use the Icon component directly, not the icon property.

Default button
Primary button
xml
<r-button type="default" icon="user">Default button</r-button>
 <r-button type="primary" icon="home">Primary button</r-button>

特效 effect

If you want a pure Button, you can add effect = false to block the water ripple effect when clicked

Default buttonPrimary button
xml
<r-button type="default" icon="user">Default button</r-button>
-<r-button type="primary" icon="home">Primary button</r-button>
- +<r-button type="primary" icon="home">Primary button</r-button> + \ No newline at end of file diff --git a/src/ranui/checkbox/index.html b/src/ranui/checkbox/index.html index 8fffd7d004..75ea088027 100644 --- a/src/ranui/checkbox/index.html +++ b/src/ranui/checkbox/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -45,8 +45,8 @@
Skip to content

CheckBox

Code demo

xml
 <r-checkbox ></r-checkbox>

Attribute

checked

xml
 <r-checkbox checked="true"></r-checkbox>
  <r-checkbox checked="false"></r-checkbox>

disabled

xml
 <r-checkbox checked="true" disabled></r-checkbox>
  <r-checkbox checked="false" disabled></r-checkbox>

event

Common callback events.

onchange

xml
 <r-checkbox onchange="console.log(this.checked)"></r-checkbox>
- <r-checkbox onchange="console.log(this.checked)"></r-checkbox>

Released under the MIT License.

- + <r-checkbox onchange="console.log(this.checked)"></r-checkbox> + \ No newline at end of file diff --git a/src/ranui/icon/index.html b/src/ranui/icon/index.html index 9d4b5c025a..486c7bd271 100644 --- a/src/ranui/icon/index.html +++ b/src/ranui/icon/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -53,8 +53,8 @@ <r-icon name="lock" size="50" color="#F44336"></r-icon> <r-icon name="lock" size="50" color="#3F51B5"></r-icon>

spin

Set spin to turn on the rotation, and pass in a number to control the rotation speed. The smaller the number, the faster the rotation

html
<r-icon name="loading" size="50" color="#1E90FF" spin="0.7"></r-icon>
 <r-icon name="loading" size="50" color="#1E90FF" spin></r-icon>
-<r-icon name="loading" size="50" color="#1E90FF" spin="5"></r-icon>

Icon list

- +<r-icon name="loading" size="50" color="#1E90FF" spin="5"></r-icon>

Icon list

+ \ No newline at end of file diff --git a/src/ranui/image/index.html b/src/ranui/image/index.html index 4bdafd2ac0..99503a6c29 100644 --- a/src/ranui/image/index.html +++ b/src/ranui/image/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

Image

Code demo

xml
 <r-img src="" fallback=""></r-img>

Attribute

src

Image address

Image loading failurefallback

srcConfiguration of the picture loading failure, the bottom of the picture address, the following is the default loading failure picture

Released under the MIT License.

- +
Skip to content

Image

Code demo

xml
 <r-img src="" fallback=""></r-img>

Attribute

src

Image address

Image loading failurefallback

srcConfiguration of the picture loading failure, the bottom of the picture address, the following is the default loading failure picture

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranui/index.html b/src/ranui/index.html index 28cbbb9905..7ef87dda34 100644 --- a/src/ranui/index.html +++ b/src/ranui/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -127,8 +127,8 @@ percent="0.70" type="drag" style="--ran-progress-wrap-background:linear-gradient(to right, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000);" -></r-progress>

For specific CSS3 variable names, refer to the introduction and description of each component.

Compatibility

Contributors

Other

  1. 优秀的组件设计
  2. 在线生成 CSS 渐变色
  3. 优秀设计作品,有 psd 和 sketch
  4. 3D UI 设计,类似于 3D 版的 figma
  5. 设计规范
  6. 优秀设计作品
  7. element UI 中文网
  8. Ant design 中文网
  9. 在线绘制 CSS 动画
  10. tailwindcss
  11. animate css
  12. can i use
  13. figma

Protocols and standards

  1. RFCs
  2. ECMA
  3. w3c
- +></r-progress>

For specific CSS3 variable names, refer to the introduction and description of each component.

Compatibility

Contributors

Other

  1. 优秀的组件设计
  2. 在线生成 CSS 渐变色
  3. 优秀设计作品,有 psd 和 sketch
  4. 3D UI 设计,类似于 3D 版的 figma
  5. 设计规范
  6. 优秀设计作品
  7. element UI 中文网
  8. Ant design 中文网
  9. 在线绘制 CSS 动画
  10. tailwindcss
  11. animate css
  12. can i use
  13. figma

Protocols and standards

  1. RFCs
  2. ECMA
  3. w3c
+ \ No newline at end of file diff --git a/src/ranui/input/index.html b/src/ranui/input/index.html index 49ffc6a23e..a64501d762 100644 --- a/src/ranui/input/index.html +++ b/src/ranui/input/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -53,8 +53,8 @@ const func = (e) => { console.log(e); }; -input.addEventListener('input', func);

The e parameter structure of the event

input method

- +input.addEventListener('input', func);

The e parameter structure of the event

input method

+ \ No newline at end of file diff --git a/src/ranui/loading/index.html b/src/ranui/loading/index.html index e3f69d9583..5c632d4a2a 100644 --- a/src/ranui/loading/index.html +++ b/src/ranui/loading/index.html @@ -14,7 +14,7 @@ - + @@ -38,7 +38,7 @@ - + @@ -47,8 +47,8 @@
Skip to content

Loading

Some nice looking loading.

Code demo

xml
<r-loading name="circle"></r-loading>

Attribute

name

There's a lot of different loading here

xml
<r-loading name="double-bounce"></r-loading>
 <r-loading name="rotate"></r-loading>
 <r-loading name="stretch"></r-loading>
-<r-loading name="cube"></r-loading>

Loading list

Move the mouse over the icon to see the loading animation

stretch
rotate
double-bounce
cube
dot
triple-bounce
scale-out
circle
circle-line
square
pulse
solar
cube-fold
circle-fold
cube-grid
circle-turn
circle-rotate
circle-spin
dot-bar
dot-circle
line
dot-pulse
line-scale
text
cube-dim
dot-line
arc
drop
pacman

Released under the MIT License.

- +<r-loading name="cube"></r-loading>

Loading list

Move the mouse over the icon to see the loading animation

stretch
rotate
double-bounce
cube
dot
triple-bounce
scale-out
circle
circle-line
square
pulse
solar
cube-fold
circle-fold
cube-grid
circle-turn
circle-rotate
circle-spin
dot-bar
dot-circle
line
dot-pulse
line-scale
text
cube-dim
dot-line
arc
drop
pacman
+ \ No newline at end of file diff --git a/src/ranui/math/index.html b/src/ranui/math/index.html index 0adeaaee81..bb74c07e59 100644 --- a/src/ranui/math/index.html +++ b/src/ranui/math/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

math

High-quality display of LaTeX in HTML pages

Code demo

xml
<r-math latex="\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1 \quad (a > b > 0)"></r-math>

Attribute

latex string

xml
  <r-math latex="x = {-b \pm \sqrt{b^2-4ac} \over 2a}"></r-math>

Released under the MIT License.

- +
Skip to content

math

High-quality display of LaTeX in HTML pages

Code demo

xml
<r-math latex="\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1 \quad (a > b > 0)"></r-math>

Attribute

latex string

xml
  <r-math latex="x = {-b \pm \sqrt{b^2-4ac} \over 2a}"></r-math>

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranui/message/index.html b/src/ranui/message/index.html index cd0bf9b2b3..0d3b059588 100644 --- a/src/ranui/message/index.html +++ b/src/ranui/message/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -46,8 +46,8 @@ <r-button onclick="message.warning('This is a hint')">Warning prompt</r-button> <r-button onclick="message.error('This is a hint')">Error prompt</r-button> <r-button onclick="message.success('This is a hint')">Success tip</r-button> -<r-button onclick="message.toast('This is a hint')">toast tip</r-button>

Method

The component provides a number of static methods, using the following methods and parameters:

  1. You can pass only one parameter, prompt the content, the default prompt 3000 milliseconds

message.info('This is a hint')

message.warning('This is a hint')

message.error('This is a hint')

message.success('This is a hint')

message.toast('This is a hint')"

  1. You can also pass an object, set the prompt content, turn off the delay, and trigger the callback function when you close it

message.info({content:'This is a hint', duration: 2000, close: () => {}})

message.warning({content:'This is a hint', duration: 2000, close: () => {}})

message.error({content:'This is a hint', duration: 2000, close: () => {}})

message.success({content:'This is a hint', duration: 2000, close: () => {}})

message.toast({content:'This is a hint', duration: 2000, close: () => {}})

参数说明类型
contentPrompt contentstring
durationAutomatic shutdown delay, in milliseconds. Default 3000 msnumber
closeCallback function triggered when closed() => void
- +<r-button onclick="message.toast('This is a hint')">toast tip</r-button>

Method

The component provides a number of static methods, using the following methods and parameters:

  1. You can pass only one parameter, prompt the content, the default prompt 3000 milliseconds

message.info('This is a hint')

message.warning('This is a hint')

message.error('This is a hint')

message.success('This is a hint')

message.toast('This is a hint')"

  1. You can also pass an object, set the prompt content, turn off the delay, and trigger the callback function when you close it

message.info({content:'This is a hint', duration: 2000, close: () => {}})

message.warning({content:'This is a hint', duration: 2000, close: () => {}})

message.error({content:'This is a hint', duration: 2000, close: () => {}})

message.success({content:'This is a hint', duration: 2000, close: () => {}})

message.toast({content:'This is a hint', duration: 2000, close: () => {}})

参数说明类型
contentPrompt contentstring
durationAutomatic shutdown delay, in milliseconds. Default 3000 msnumber
closeCallback function triggered when closed() => void
+ \ No newline at end of file diff --git a/src/ranui/modal/index.html b/src/ranui/modal/index.html index 17193f982f..d7b0daca37 100644 --- a/src/ranui/modal/index.html +++ b/src/ranui/modal/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

Released under the MIT License.

- +
Skip to content

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranui/player/index.html b/src/ranui/player/index.html index 4ad5168d6e..a5ce0ee554 100644 --- a/src/ranui/player/index.html +++ b/src/ranui/player/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

r-player

Video player

Based on 'hlsjs' and' web components', let the native tag 'r-player' have a unified video control. Do not use the 'new Player(options)' way to mount to the specified 'dom', view return view, logic return logic, see and get, more intuitive.

  1. Drag and drop the progress bar
  2. Volume control
  3. The bitrate is automatically switched based on the current bandwidth
  4. Manual definition switch
  5. Play at double speed
  6. Style custom overlay
  7. 'hls' protocol standard encryption video playback
  8. Based on native development, it can run in all frameworks and unify the cross-framework situation
  9. Unified browser controls

Code demo

xml
  <r-player src="/ran/hls/example.m3u8"></r-player>

Attribute

src

Resource address of the video

volume

Set the initial volume. The default is 0.5

currentTime

Set the initial playback time. By default, the playback starts from the beginning

playbackRate

Set the double speed. The default is 1.0

debug

console.log some info

event

onchange

Listen for any player changes, and the value returned is as follows.

An 'instance of the player' can be obtained through this method.

Live by 'type' to judge different event types, perform different operations

propertyexplains thatis of type
typeIndicates the type of the changed event'string'
dataThe value of the'Object'
currentTimeThe current playback time'number'
durationTotal duration of videos'number'
tagAn example of the player'Element'

Where 'type' type has

NameDescription
canplayYour browser is ready to play the media file, but it probably doesn't have enough data to play it to the end without pausing to buffer the content further.
canplaythroughThe browser estimates that it canplay media until the end without stopping content buffering.
completeOfflineAudioContext The rendering is complete.
durationchangeduration is triggered when the value of the duration property changes.
emptiedMedia content emptied; For example, when the media is already loaded (or partially loaded), this event is sent and the load() method is called to reload it.
endedThe video stops playing because the media has reached the end point.
loadedmetadataThe metadata is loaded.
progressis triggered periodically when the browser loads the resource.
ratechangeThe play rate changes.
seekedThe seek operation is complete.
seekingseek begins.
stalledThe user agent is trying to obtain media data but it has not appeared unexpectedly.
suspendMedia data loading has been suspended.
loadeddataThe first frame in media has been added. media has loaded.
timeupdateThe time specified by the currentTime property changes. currentTime attribute has changed.
volumechangeThe volume changes.
waitingPlaying has stopped due to lack of data.
playPlayback has started.
playingAfter a pause or delay due to lack of data, playback is ready to begin.
pausePlay is paused.
volumeThe volume changes.
fullscreenTriggers a full-screen event

Released under the MIT License.

- +
Skip to content

r-player

Video player

Based on 'hlsjs' and' web components', let the native tag 'r-player' have a unified video control. Do not use the 'new Player(options)' way to mount to the specified 'dom', view return view, logic return logic, see and get, more intuitive.

  1. Drag and drop the progress bar
  2. Volume control
  3. The bitrate is automatically switched based on the current bandwidth
  4. Manual definition switch
  5. Play at double speed
  6. Style custom overlay
  7. 'hls' protocol standard encryption video playback
  8. Based on native development, it can run in all frameworks and unify the cross-framework situation
  9. Unified browser controls

Code demo

xml
  <r-player src="/ran/hls/example.m3u8"></r-player>

Attribute

src

Resource address of the video

volume

Set the initial volume. The default is 0.5

currentTime

Set the initial playback time. By default, the playback starts from the beginning

playbackRate

Set the double speed. The default is 1.0

debug

console.log some info

event

onchange

Listen for any player changes, and the value returned is as follows.

An 'instance of the player' can be obtained through this method.

Live by 'type' to judge different event types, perform different operations

propertyexplains thatis of type
typeIndicates the type of the changed event'string'
dataThe value of the'Object'
currentTimeThe current playback time'number'
durationTotal duration of videos'number'
tagAn example of the player'Element'

Where 'type' type has

NameDescription
canplayYour browser is ready to play the media file, but it probably doesn't have enough data to play it to the end without pausing to buffer the content further.
canplaythroughThe browser estimates that it canplay media until the end without stopping content buffering.
completeOfflineAudioContext The rendering is complete.
durationchangeduration is triggered when the value of the duration property changes.
emptiedMedia content emptied; For example, when the media is already loaded (or partially loaded), this event is sent and the load() method is called to reload it.
endedThe video stops playing because the media has reached the end point.
loadedmetadataThe metadata is loaded.
progressis triggered periodically when the browser loads the resource.
ratechangeThe play rate changes.
seekedThe seek operation is complete.
seekingseek begins.
stalledThe user agent is trying to obtain media data but it has not appeared unexpectedly.
suspendMedia data loading has been suspended.
loadeddataThe first frame in media has been added. media has loaded.
timeupdateThe time specified by the currentTime property changes. currentTime attribute has changed.
volumechangeThe volume changes.
waitingPlaying has stopped due to lack of data.
playPlayback has started.
playingAfter a pause or delay due to lack of data, playback is ready to begin.
pausePlay is paused.
volumeThe volume changes.
fullscreenTriggers a full-screen event

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranui/popover/index.html b/src/ranui/popover/index.html index 0eed0c36ab..28680ec7f4 100644 --- a/src/ranui/popover/index.html +++ b/src/ranui/popover/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -67,8 +67,8 @@ <r-content> <div>top</div> </r-content> - </r-popover> - + </r-popover> + \ No newline at end of file diff --git a/src/ranui/preview/index.html b/src/ranui/preview/index.html index 402d55a365..c4892c0ead 100644 --- a/src/ranui/preview/index.html +++ b/src/ranui/preview/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -61,8 +61,8 @@ } }; }; -</script>

Attribute

src

If there is a 'src' address, the popup window will be opened, and if there is no 'src', it will not be displayed

html
<r-preview src=""></r-preview>

closeable

'closeable' defaults to 'true' and can be closed, when set to 'false', it cannot be closed, and the close button in the upper right corner will not be displayed

html
<r-preview closeable="false"></r-preview>
- +</script>

Attribute

src

If there is a 'src' address, the popup window will be opened, and if there is no 'src', it will not be displayed

html
<r-preview src=""></r-preview>

closeable

'closeable' defaults to 'true' and can be closed, when set to 'false', it cannot be closed, and the close button in the upper right corner will not be displayed

html
<r-preview closeable="false"></r-preview>
+ \ No newline at end of file diff --git a/src/ranui/progress/index.html b/src/ranui/progress/index.html index 39071867e2..a7be3588a3 100644 --- a/src/ranui/progress/index.html +++ b/src/ranui/progress/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,8 +48,8 @@ <r-progress type="primary" percent="40%"></r-progress> <r-progress type="primary" percent="100%"></r-progress>

dot

Point of the progress bar, Default display, set to 'false' can be hidden

html
<r-progress type="drag" percent="30%" dot="false"></r-progress>
 <r-progress type="primary" percent="40%" dot="true"></r-progress>
-<r-progress type="primary" percent="40%"></r-progress>

type

html
<r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

Method

change

The 'change' event is triggered when the 'percent' and 'total' properties change.

propertyexplains thattype
valueCurrent progress'string or number'
percentCurrent progress'string or number'
totalTotal progress'string or number'
- +<r-progress type="primary" percent="40%"></r-progress>

type

html
<r-progress type="drag" percent="30%"></r-progress> <r-progress type="primary" percent="40%"></r-progress>

Method

change

The 'change' event is triggered when the 'percent' and 'total' properties change.

propertyexplains thattype
valueCurrent progress'string or number'
percentCurrent progress'string or number'
totalTotal progress'string or number'
+ \ No newline at end of file diff --git a/src/ranui/radar/index.html b/src/ranui/radar/index.html index 13be0cb53d..44618b9109 100644 --- a/src/ranui/radar/index.html +++ b/src/ranui/radar/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -96,8 +96,8 @@ "abilityName": "Critical hit damage", "scoreRate": "50" } -] - +] + \ No newline at end of file diff --git a/src/ranui/select/index.html b/src/ranui/select/index.html index 2060f25b0a..b70e8f13eb 100644 --- a/src/ranui/select/index.html +++ b/src/ranui/select/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -83,8 +83,8 @@ <r-option value="185">Mike</r-option> <r-option value="186">Tom</r-option> <r-option value="187">Lucy</r-option> - </r-select> - + </r-select> + \ No newline at end of file diff --git a/src/ranui/skeleton/index.html b/src/ranui/skeleton/index.html index 46cf71fb5c..7495766a10 100644 --- a/src/ranui/skeleton/index.html +++ b/src/ranui/skeleton/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

skeleton

Provides a combination of placeholder graphics where you need to wait for content to load.

Code demo

The skeleton length follows the length of the parent element

xml
<r-skeleton ></r-skeleton>

Released under the MIT License.

- +
Skip to content

skeleton

Provides a combination of placeholder graphics where you need to wait for content to load.

Code demo

The skeleton length follows the length of the parent element

xml
<r-skeleton ></r-skeleton>

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranui/tab/index.html b/src/ranui/tab/index.html index 5c4aaa2cc7..1d6830de55 100644 --- a/src/ranui/tab/index.html +++ b/src/ranui/tab/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -84,8 +84,8 @@ <r-tab label="tab1">11111</r-tab> <r-tab label="tab2">22222</r-tab> <r-tab label="tab3">33333</r-tab> - </r-tabs> - + </r-tabs> + \ No newline at end of file diff --git a/src/ranui/tabs/index.html b/src/ranui/tabs/index.html index 2a38fcc0e7..2be69fd25e 100644 --- a/src/ranui/tabs/index.html +++ b/src/ranui/tabs/index.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -66,8 +66,8 @@ <r-tab icon="home" iconSize="22">tab1</r-tab> <r-tab icon="message" iconSize="22">tab2</r-tab> <r-tab icon="user" iconSize="22">tab3</r-tab> -</r-tabs>

'type'

The style is text, clean,

event '

onchange

Triggered when the switch is complete.

- +</r-tabs>

'type'

The style is text, clean,

event '

onchange

Triggered when the switch is complete.

+ \ No newline at end of file diff --git a/src/ranuts/binaryTree/index.html b/src/ranuts/binaryTree/index.html index ce348c73b5..2b1d82cf01 100644 --- a/src/ranuts/binaryTree/index.html +++ b/src/ranuts/binaryTree/index.html @@ -13,7 +13,7 @@ - + @@ -37,14 +37,14 @@ - + -
Skip to content

二叉树的定义

在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

二叉树的性质

  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
  • 二叉树的总结点数 n = n1 + n2 + n0
  • 总连线数等于总节点数减一(B = n - 1)
  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

二叉树的类型

满二叉树

一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

完全二叉树

一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

二叉搜索树

二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

平衡二叉树

平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

B 树

B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

B+树

B+树是 B 树的变体,也是一种多路搜索树。

B*树

B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

红黑树

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

遍历

前序遍历

后序遍历

中序遍历

层序遍历

常见算法题

镜像二叉树

重建二叉树

二叉树深度

二叉树节点总数

判断二叉树子结构

输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

参考文档

  1. 维基百科二叉树
  2. 百度百科满二叉树

Released under the MIT License.

- +
Skip to content

二叉树的定义

在计算机科学中,二叉树(Binary tree)是每个节点最多只有两个分支(即不存在分支度大于 2 的节点)的树结构。通常分支被称作“左子树”或“右子树”。二叉树的分支具有左右次序,不能随意颠倒[1]。。

二叉树的性质

  • 在二叉树的第 i 层上最多有 2^(i-1)个结点(i>=1)
  • 深度为 h 的二叉树,最多有 2^h-1 个结点,最少有 h 个结点(h>=1)
  • 包含 n 个结点的二叉树的高度至少为(log2n)+1
  • 非空的二叉树,分支度为 0 的总数为 n0,分支度为 2 的总数为 n2,则 n0=n2+1
  • 二叉树的总结点数 n = n1 + n2 + n0
  • 总连线数等于总节点数减一(B = n - 1)
  • 总连线数等于分支度为 2 的节点的两倍加上分支度为 1 的节点(B = n2 _ 2 + n1 _ 1)

二叉树的类型

满二叉树

一棵深度为 k 且有 2k-1 个节点的二叉树称为满二叉树。 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树[2]

完全二叉树

一棵深度为 k 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i(1≤i≤n)的结点与满二叉树中编号为 i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

二叉搜索树

二叉搜索树(BST)又称二叉查找树或二叉排序树。它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

平衡二叉树

平衡二叉树(AVL)一定是二叉搜索树,且左子树和右子树的高度差的绝对值不超过 1。 平衡二叉树

B 树

B 树属于多叉树又名平衡多路查找树(查找路径不只两个)

B+树

B+树是 B 树的变体,也是一种多路搜索树。

B*树

B* 树是 B+树的变体,在 B+树的非根和非叶子结点再增加指向兄弟的指针;B* 树定义了非叶子结点关键字个数至少为(2/3)M,即块的最低使用率为 2/3(代替 B+树的 1/2)。B 树分配新结点的概率比 B+树要低,空间使用率更高;

红黑树

红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但对它进行平衡的代价较低, 其平均统计性能要强于 AVL 。

遍历

前序遍历

后序遍历

中序遍历

层序遍历

常见算法题

镜像二叉树

重建二叉树

二叉树深度

二叉树节点总数

判断二叉树子结构

输入两棵二叉树 A 和 B,判断 B 是不是 A 的子结构。(ps:约定空树不是任意一个树的子结构)

参考文档

  1. 维基百科二叉树
  2. 百度百科满二叉树

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/bundler/index.html b/src/ranuts/bundler/index.html index 39c70a07ea..4bc611e0d7 100644 --- a/src/ranuts/bundler/index.html +++ b/src/ranuts/bundler/index.html @@ -13,7 +13,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -52,8 +52,8 @@ generate: () => bundle.render() }; }); -}

架构图

- +}

架构图

+ \ No newline at end of file diff --git a/src/ranuts/file/appendFile.html b/src/ranuts/file/appendFile.html index f8c9628382..bcf479e7c0 100644 --- a/src/ranuts/file/appendFile.html +++ b/src/ranuts/file/appendFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

AppendFile

追加一些数据到文件内

API

Return

  • Promise
参数说明类型描述
success是否追加成功booleantrue 追加成功 false 追加失败
data追加失败的原因,添加成功后的文件内容any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Example

Released under the MIT License.

- +
Skip to content

AppendFile

追加一些数据到文件内

API

Return

  • Promise
参数说明类型描述
success是否追加成功booleantrue 追加成功 false 追加失败
data追加失败的原因,添加成功后的文件内容any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/file/fileInfo.html b/src/ranuts/file/fileInfo.html index b47af150d1..ad33bf4c74 100644 --- a/src/ranuts/file/fileInfo.html +++ b/src/ranuts/file/fileInfo.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

QueryFileInfo

查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

API

Return

  • Promise
参数说明类型描述
success是否检查成功booleantrue 成功 false 失败
data文件的信息,或者错误的原因Stats

Options

参数说明类型默认值
path文件路径,需要检查的文件路径stringundefined

Example

Released under the MIT License.

- +
Skip to content

QueryFileInfo

查询一个文件的详细信息,一般用于区分文件还是目录,可以通过返回的 data 来判断(data.isDirectory())

API

Return

  • Promise
参数说明类型描述
success是否检查成功booleantrue 成功 false 失败
data文件的信息,或者错误的原因Stats

Options

参数说明类型默认值
path文件路径,需要检查的文件路径stringundefined

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/file/readDir.html b/src/ranuts/file/readDir.html index c85fb623de..039454498c 100644 --- a/src/ranuts/file/readDir.html +++ b/src/ranuts/file/readDir.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

ReadDir

读一个目录下的所有文件

API

Return

  • Promise
参数说明类型描述
result目录下所有文件的数组array传入函数的参数

Options

参数说明类型默认值
optionsobject传入函数的参数
dirPath文件的路径Stats

Example

Released under the MIT License.

- +
Skip to content

ReadDir

读一个目录下的所有文件

API

Return

  • Promise
参数说明类型描述
result目录下所有文件的数组array传入函数的参数

Options

参数说明类型默认值
optionsobject传入函数的参数
dirPath文件的路径Stats

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/file/readFile.html b/src/ranuts/file/readFile.html index b5d5ca7973..76d9255c2a 100644 --- a/src/ranuts/file/readFile.html +++ b/src/ranuts/file/readFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

ReadFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
data文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Example

Released under the MIT License.

- +
Skip to content

ReadFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
data文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Example

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/file/watchFile.html b/src/ranuts/file/watchFile.html index c41d4c619a..ab39bd024c 100644 --- a/src/ranuts/file/watchFile.html +++ b/src/ranuts/file/watchFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

WatchFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
status文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Released under the MIT License.

- +
Skip to content

WatchFile

观察一个文件是否改变

API

Return

  • Promise
参数说明类型描述
status文件是否被改变booleantrue 文件改变 false 文件没变

Options

参数说明类型默认值
path文件路径,需要监听的文件stringundefined
interval监听文件改变的时间,单位毫秒。number20

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/file/writeFile.html b/src/ranuts/file/writeFile.html index d42557eab6..31270b6526 100644 --- a/src/ranuts/file/writeFile.html +++ b/src/ranuts/file/writeFile.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

WriteFile

将内容写入文件

API

Return

  • Promise
参数说明类型描述
success是否写入成功booleantrue 成功 false 失败
data写入失败的原因,添加成功后的文件内容和文件路径any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Released under the MIT License.

- +
Skip to content

WriteFile

将内容写入文件

API

Return

  • Promise
参数说明类型描述
success是否写入成功booleantrue 成功 false 失败
data写入失败的原因,添加成功后的文件内容和文件路径any

Options

参数说明类型默认值
path文件路径,需要追加的文件stringundefined
content需要追加的内容string

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/index.html b/src/ranuts/index.html index f6be7ae85f..6a2f4eb034 100644 --- a/src/ranuts/index.html +++ b/src/ranuts/index.html @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

ranuts overview

Method list

Methoddescriptiondetail
writeFileWrite to filewriteFile
readFileRead filereadFile
readDirRead the directory and get the names of all the files in the directoryreadDir
watchFileCheck whether the contents of the file have changedwatchFile
queryFileInfoQuery file informationqueryFileInfo
filterObjFilter objectfilterObj
EventEmitterPublish-subscribe classEventEmitter
str2XmlString is converted to xmlstr2Xml
getMimeGets the mime type based on the file format suffixgetMime
getCookieGets the value of the specified cookiewriteFile
formatJsonFormatted JSONformatJson

TOTP

2FA Verification
Generate
code:
expires:

Released under the MIT License.

- +
Skip to content

ranuts overview

Method list

Methoddescriptiondetail
writeFileWrite to filewriteFile
readFileRead filereadFile
readDirRead the directory and get the names of all the files in the directoryreadDir
watchFileCheck whether the contents of the file have changedwatchFile
queryFileInfoQuery file informationqueryFileInfo
filterObjFilter objectfilterObj
EventEmitterPublish-subscribe classEventEmitter
str2XmlString is converted to xmlstr2Xml
getMimeGets the mime type based on the file format suffixgetMime
getCookieGets the value of the specified cookiewriteFile
formatJsonFormatted JSONformatJson

TOTP

2FA Verification
Generate
code:
expires:

Released under the MIT License.

+ \ No newline at end of file diff --git a/src/ranuts/mimeType/mimeType.html b/src/ranuts/mimeType/mimeType.html index bc2a130cd5..39f234b6bc 100644 --- a/src/ranuts/mimeType/mimeType.html +++ b/src/ranuts/mimeType/mimeType.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -49,8 +49,8 @@ // 'application/vnd.openxmlformats-officedocument.presentationml.presentation const res = getMime('.txt'); console.log(result); -// text/plain - +// text/plain + \ No newline at end of file diff --git a/src/ranuts/mode/subscribe.html b/src/ranuts/mode/subscribe.html index e05211023a..f1f5753453 100644 --- a/src/ranuts/mode/subscribe.html +++ b/src/ranuts/mode/subscribe.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -73,8 +73,8 @@ // 订阅一次,触发一次自动取消 subscribe.once('other', () => { console.log(5); -}); - +}); + \ No newline at end of file diff --git a/src/ranuts/utils/convertImageToBase64.html b/src/ranuts/utils/convertImageToBase64.html index ec517b9d95..099befffc1 100644 --- a/src/ranuts/utils/convertImageToBase64.html +++ b/src/ranuts/utils/convertImageToBase64.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -46,8 +46,8 @@ convertImageToBase64(file).then((res) => { console.log(result); -}); - +}); + \ No newline at end of file diff --git a/src/ranuts/utils/filterObj.html b/src/ranuts/utils/filterObj.html index 1d041a734d..bb9cb67c72 100644 --- a/src/ranuts/utils/filterObj.html +++ b/src/ranuts/utils/filterObj.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -54,8 +54,8 @@ console.log(result); -// { age:10 } - +// { age:10 } + \ No newline at end of file diff --git a/src/ranuts/utils/formatJson.html b/src/ranuts/utils/formatJson.html index 2ff781d064..4a24c22106 100644 --- a/src/ranuts/utils/formatJson.html +++ b/src/ranuts/utils/formatJson.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -51,8 +51,8 @@ const result = formatJson(json); -console.log(result); - +console.log(result); + \ No newline at end of file diff --git a/src/ranuts/utils/getCookie.html b/src/ranuts/utils/getCookie.html index fd8aec6676..40a9e14d31 100644 --- a/src/ranuts/utils/getCookie.html +++ b/src/ranuts/utils/getCookie.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,8 +48,8 @@ console.log(result); -// '' - +// '' + \ No newline at end of file diff --git a/src/ranuts/utils/ocr.html b/src/ranuts/utils/ocr.html index 2ceaf9238d..889042570d 100644 --- a/src/ranuts/utils/ocr.html +++ b/src/ranuts/utils/ocr.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -56,8 +56,8 @@ // And when thou lovest thy pale orb to shroud // Behind the gather’d blackness lost on high; // And when thou dartest from the wind-rent cloud -// Thy placid lightning o’er the awaken’d sky.

Lang Code

Lang CodeLanguage
afrAfrikaans
amhAmharic
araArabic
asmAssamese
azeAzerbaijani
aze_cyrlAzerbaijani - Cyrillic
belBelarusian
benBengali
bodTibetan
bosBosnian
bulBulgarian
catCatalan; Valencian
cebCebuano
cesCzech
chi_simChinese - Simplified
chi_traChinese - Traditional
chrCherokee
cymWelsh
danDanish
deuGerman
dzoDzongkha
ellGreek, Modern (1453-)
engEnglish
enmEnglish, Middle (1100-1500)
epoEsperanto
estEstonian
eusBasque
fasPersian
finFinnish
fraFrench
frkGerman Fraktur
frmFrench, Middle (ca. 1400-1600)
gleIrish
glgGalician
grcGreek, Ancient (-1453)
gujGujarati
hatHaitian; Haitian Creole
hebHebrew
hinHindi
hrvCroatian
hunHungarian
ikuInuktitut
indIndonesian
islIcelandic
itaItalian
ita_oldItalian - Old
javJavanese
jpnJapanese
kanKannada
katGeorgian
kat_oldGeorgian - Old
kazKazakh
khmCentral Khmer
kirKirghiz; Kyrgyz
korKorean
kurKurdish
laoLao
latLatin
lavLatvian
litLithuanian
malMalayalam
marMarathi
mkdMacedonian
mltMaltese
msaMalay
myaBurmese
nepNepali
nldDutch; Flemish
norNorwegian
oriOriya
panPanjabi; Punjabi
polPolish
porPortuguese
pusPushto; Pashto
ronRomanian; Moldavian; Moldovan
rusRussian
sanSanskrit
sinSinhala; Sinhalese
slkSlovak
slvSlovenian
spaSpanish; Castilian
spa_oldSpanish; Castilian - Old
sqiAlbanian
srpSerbian
srp_latnSerbian - Latin
swaSwahili
sweSwedish
syrSyriac
tamTamil
telTelugu
tgkTajik
tglTagalog
thaThai
tirTigrinya
turTurkish
uigUighur; Uyghur
ukrUkrainian
urdUrdu
uzbUzbek
uzb_cyrlUzbek - Cyrillic
vieVietnamese
yidYiddish
- +// Thy placid lightning o’er the awaken’d sky.

Lang Code

Lang CodeLanguage
afrAfrikaans
amhAmharic
araArabic
asmAssamese
azeAzerbaijani
aze_cyrlAzerbaijani - Cyrillic
belBelarusian
benBengali
bodTibetan
bosBosnian
bulBulgarian
catCatalan; Valencian
cebCebuano
cesCzech
chi_simChinese - Simplified
chi_traChinese - Traditional
chrCherokee
cymWelsh
danDanish
deuGerman
dzoDzongkha
ellGreek, Modern (1453-)
engEnglish
enmEnglish, Middle (1100-1500)
epoEsperanto
estEstonian
eusBasque
fasPersian
finFinnish
fraFrench
frkGerman Fraktur
frmFrench, Middle (ca. 1400-1600)
gleIrish
glgGalician
grcGreek, Ancient (-1453)
gujGujarati
hatHaitian; Haitian Creole
hebHebrew
hinHindi
hrvCroatian
hunHungarian
ikuInuktitut
indIndonesian
islIcelandic
itaItalian
ita_oldItalian - Old
javJavanese
jpnJapanese
kanKannada
katGeorgian
kat_oldGeorgian - Old
kazKazakh
khmCentral Khmer
kirKirghiz; Kyrgyz
korKorean
kurKurdish
laoLao
latLatin
lavLatvian
litLithuanian
malMalayalam
marMarathi
mkdMacedonian
mltMaltese
msaMalay
myaBurmese
nepNepali
nldDutch; Flemish
norNorwegian
oriOriya
panPanjabi; Punjabi
polPolish
porPortuguese
pusPushto; Pashto
ronRomanian; Moldavian; Moldovan
rusRussian
sanSanskrit
sinSinhala; Sinhalese
slkSlovak
slvSlovenian
spaSpanish; Castilian
spa_oldSpanish; Castilian - Old
sqiAlbanian
srpSerbian
srp_latnSerbian - Latin
swaSwahili
sweSwedish
syrSyriac
tamTamil
telTelugu
tgkTajik
tglTagalog
thaThai
tirTigrinya
turTurkish
uigUighur; Uyghur
ukrUkrainian
urdUrdu
uzbUzbek
uzb_cyrlUzbek - Cyrillic
vieVietnamese
yidYiddish
+ \ No newline at end of file diff --git a/src/ranuts/utils/str2xml.html b/src/ranuts/utils/str2xml.html index ea6117de2d..9f8942d83c 100644 --- a/src/ranuts/utils/str2xml.html +++ b/src/ranuts/utils/str2xml.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -49,8 +49,8 @@ const icon = str2Xml(svg, 'image/svg+xml'); -document.body.appendChild(icon); - +document.body.appendChild(icon); + \ No newline at end of file diff --git a/src/ranuts/utils/task.html b/src/ranuts/utils/task.html index 421b8e74cc..66d60251db 100644 --- a/src/ranuts/utils/task.html +++ b/src/ranuts/utils/task.html @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -48,8 +48,8 @@ const time = taskEnd(taskId); -console.log('task 执行花费的时间', time);

二.new Date().getTime()

new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

  1. 某些情况下,毫秒级精度可能不够。
  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

三.console.time(), console.timeEnd()

启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

四.performance.now()

performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

- +console.log('task 执行花费的时间', time);

二.new Date().getTime()

new Date().getTime() 返回一个数值,表示从 1970 年 1 月 1 日 0 时 0 分 0 秒(UTC,即协调世界时)距离该日期对象所代表时间的毫秒数。用来计算 JS 执行时间会有两个问题:

  1. 某些情况下,毫秒级精度可能不够。
  2. new Date() 解析的时间在不同浏览器,或者不同设备上可能并不一致。MDN 说明

    由于浏览器之间的差异与不一致性,强烈不推荐使用 Date 构造函数来解析日期字符串 (或使用与其等价的 Date.parse)。对 RFC 2822 格式的日期仅有约定俗成的支持。对 ISO 8601 格式的支持中,仅有日期的串 (例如 "1970-01-01") 会被处理为 UTC 而不是本地时间,与其他格式的串的处理不同。

三.console.time(), console.timeEnd()

启动一个计时器来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字,页面中最多能同时运行 10,000 个计时器。当以此计时器名字为参数调用 console.timeEnd() 时,浏览器将以毫秒为单位,输出对应计时器所经过的时间。比起new Date().getTime(),统计时间更加精确,可以统计到 0.001 毫秒(比如:0.134ms)

四.performance.now()

performance.now()返回的时间精度最高可达微秒级,且不会受到系统时间的影响(系统时钟可能会被手动调整或被 NTP 等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。因此对于统计 JS 执行耗时方面,更推荐使用performance.now()

注意:为了提供对定时攻击和指纹的保护,performance.now() 的精度可能会根据浏览器的设置而被舍弃。 在 Firefox 中,privacy.reduceTimerPrecision 偏好是默认启用的,默认值为 1ms。可以启用 privacy.resistFingerprinting 这将精度改为 100ms 或privacy.resistFingerprinting.reduceTimerPrecision.microseconds 的值,以较大者为准。

+ \ No newline at end of file diff --git "a/src/types/TS\347\261\273\345\236\213.html" "b/src/types/TS\347\261\273\345\236\213.html" index c48c525f29..32e5bd7e32 100644 --- "a/src/types/TS\347\261\273\345\236\213.html" +++ "b/src/types/TS\347\261\273\345\236\213.html" @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -89,8 +89,8 @@ age?: number; } -type tuple = [string, number?]; - +type tuple = [string, number?]; + \ No newline at end of file diff --git "a/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" "b/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" index 747ebd983c..982253488d 100644 --- "a/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" +++ "b/src/types/\346\250\241\345\274\217\345\214\271\351\205\215.html" @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -52,8 +52,8 @@ new (name: string): Person; }

这里的 PersonConstructor 返回的是 Person 类型的实例对象,这个也可以通过模式匹配取出来。

ts
type GetInstanceType<C extends new (...args: unknown[]) => unknown> = C extends new (...args: unknown[]) => infer T
   ? T
-  : unknown;
- + : unknown; + \ No newline at end of file diff --git "a/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" "b/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" index 711a5141f0..7190bf77e5 100644 --- "a/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" +++ "b/src/types/\347\261\273\345\236\213\350\277\220\347\256\227.html" @@ -12,7 +12,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -68,8 +68,8 @@ // type res = { // aaa:[1,1,1] // bbb:[2,2,2] -// }

这里的 & string 可能大家会迷惑,解释一下:

因为索引类型(对象、class 等)可以用 string、number 和 symbol 作为 key,这里 keyof T 取出的索引就是 string | number | symbol 的联合类型,和 string 取交叉部分就只剩下 string 了。就像前面所说,交叉类型会把同一类型做合并,不同类型舍弃。

因为 js 处理对象比较多,所以索引类型的映射比较重要。

- +// }

这里的 & string 可能大家会迷惑,解释一下:

因为索引类型(对象、class 等)可以用 string、number 和 symbol 作为 key,这里 keyof T 取出的索引就是 string | number | symbol 的联合类型,和 string 取交叉部分就只剩下 string 了。就像前面所说,交叉类型会把同一类型做合并,不同类型舍弃。

因为 js 处理对象比较多,所以索引类型的映射比较重要。

+ \ No newline at end of file diff --git "a/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" "b/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" index 41cc2f1e37..aa5db608f4 100644 --- "a/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" +++ "b/src/types/\351\253\230\347\272\247\347\261\273\345\236\213.html" @@ -12,7 +12,7 @@ - + @@ -36,14 +36,14 @@ - + -
Skip to content

TypeScript 内置的高级类型

Parameters

Parameters 用于提取函数类型的参数类型。

ReturnType

ReturnType 用于提取函数类型的返回值类型。

ConstructorParameters

构造器类型和函数类型的区别就是可以被 new。

Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

InstanceType

提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

ThisParameterType

OmitThisParameter

Partial

Required

Readonly

Pick

Record

Exclude

Extract

Omit

Awaited

NonNullable

Uppercase

Lowercase

Capitalize

Uncapitalize

总结

比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

用模式匹配 + 重新构造可以实现:OmitThisParameter

用重新构造可以实现:Partial、Required、Readonly、Pick、Record

用模式匹配 + 递归可以实现: Awaited

用联合类型在分布式条件类型的特性可以实现: Exclude

此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

Last updated:

Released under the MIT License.

- +
Skip to content

TypeScript 内置的高级类型

Parameters

Parameters 用于提取函数类型的参数类型。

ReturnType

ReturnType 用于提取函数类型的返回值类型。

ConstructorParameters

构造器类型和函数类型的区别就是可以被 new。

Parameters 用于提取函数参数的类型,而 ConstructorParameters 用于提取构造器参数的类型。

InstanceType

提取了构造器参数的类型,自然也可以提取构造器返回值的类型,就是 InstanceType。

ThisParameterType

OmitThisParameter

Partial

Required

Readonly

Pick

Record

Exclude

Extract

Omit

Awaited

NonNullable

Uppercase

Lowercase

Capitalize

Uncapitalize

总结

比如用模式匹配可以实现:Parameters、ReturnType、ConstructorParameters、InstanceType、ThisParameterType。

用模式匹配 + 重新构造可以实现:OmitThisParameter

用重新构造可以实现:Partial、Required、Readonly、Pick、Record

用模式匹配 + 递归可以实现: Awaited

用联合类型在分布式条件类型的特性可以实现: Exclude

此外还有 NonNullable 和四个编译器内部实现的类型:Uppercase、Lowercase、Capitalize、Uncapitalize。

Last updated:

Released under the MIT License.

+ \ No newline at end of file diff --git a/sw1728129561.js b/sw1728129796.js similarity index 56% rename from sw1728129561.js rename to sw1728129796.js index 8b61b7b58a..9608251571 100644 --- a/sw1728129561.js +++ b/sw1728129796.js @@ -67,204 +67,209 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/icon_72.png", "/ran/404.html", "/ran/home.svg", -"/ran/assets/src_article_sort_radix_index.md.Ca-wVT5Q.js", -"/ran/assets/cn_src_ranui_tab_index.md.BlcshkmY.js", -"/ran/assets/cn_src_ranui_message_index.md.OWf7PDqT.lean.js", +"/ran/assets/src_ranui_icon_index.md.CCbuLCdk.js", "/ran/assets/Statement.9lGuRes5.jpeg", -"/ran/assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.lean.js", -"/ran/assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.js", -"/ran/assets/src_ranuts_utils_formatJson.md.BQjsxBql.lean.js", -"/ran/assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.lean.js", -"/ran/assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.lean.js", -"/ran/assets/src_ranuts_file_watchFile.md.DYvq_aUK.js", -"/ran/assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.js", -"/ran/assets/cn_index.md.CC940uHT.lean.js", +"/ran/assets/src_ranui_message_index.md.C9TUA1qN.js", +"/ran/assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.js", +"/ran/assets/src_ranui_icon_index.md.CCbuLCdk.lean.js", +"/ran/assets/cn_src_article_bundle.md.DQFZ3YRD.js", +"/ran/assets/cn_src_article_designMode.md.B2Mi_NTb.lean.js", +"/ran/assets/src_article_astParse_tokenizer.md.CuCr3w1T.lean.js", +"/ran/assets/cn_src_ranui_progress_index.md.B6p9KAvb.js", "/ran/assets/inter-roman-greek.BBVDIX6e.woff2", "/ran/assets/extra.Da45cF33.jpeg", -"/ran/assets/src_article_sort_radix_index.md.Ca-wVT5Q.lean.js", -"/ran/assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.js", -"/ran/assets/src_article_typescript_pattern.md.dRLdKmiq.lean.js", -"/ran/assets/src_ranuts_utils_str2xml.md.BTPyA91O.lean.js", -"/ran/assets/cn_src_article_docPreview.md.Bk6_K5Up.js", -"/ran/assets/src_article_typescript_recursion.md.CxwYW6pd.js", +"/ran/assets/cn_src_article_typescript_index.md.BE6MDNaH.js", +"/ran/assets/cn_src_types_TS类型.md.BqYfr0BF.lean.js", +"/ran/assets/src_article_sort_quick_index.md.BVXZPTlC.lean.js", +"/ran/assets/src_ranui_select_index.md.DQq3MVHp.js", "/ran/assets/export.ne8l5ppO.jpeg", -"/ran/assets/src_ranui_checkbox_index.md.D6UQK38M.js", "/ran/assets/bili_demo_url.BQDGtHUp.webp", "/ran/assets/ms_ppt.C2zYd_IC.webp", -"/ran/assets/src_ranuts_utils_str2xml.md.BTPyA91O.js", -"/ran/assets/src_ranuts_utils_filterObj.md.SUYinD61.lean.js", -"/ran/assets/cn_src_article_typescript_recursion.md.NrD8NlPF.lean.js", +"/ran/assets/cn_src_ranuts_index.md.Br1tGjNi.lean.js", +"/ran/assets/src_ranui_skeleton_index.md.BUf_Burp.js", +"/ran/assets/src_ranui_player_index.md.DwTQwuCV.lean.js", +"/ran/assets/src_ranuts_utils_task.md.DEROvLOz.js", +"/ran/assets/cn_src_article_sort_shell_index.md.CQc6o0U8.lean.js", "/ran/assets/inter-italic-cyrillic.By2_1cv3.woff2", "/ran/assets/bilibili_video_code.DgWtgAEf.webp", -"/ran/assets/cn_src_ranui_player_index.md.DXjOFy1h.lean.js", +"/ran/assets/src_ranui_modal_index.md.CeMIN_J0.js", +"/ran/assets/src_article_babel.md.Cy_u0Zgd.js", "/ran/assets/app.BN0mb3GM.js", "/ran/assets/抽象工厂.DlwNEriZ.png", -"/ran/assets/cn_src_article_typescript_calculate.md.DPFdz5ZN.js", -"/ran/assets/cn_src_article_typescript_index.md.CaFWWXxk.lean.js", "/ran/assets/享元.EIifVVTy.png", "/ran/assets/适配器.C2VH4lXy.png", -"/ran/assets/cn_src_ranuts_file_appendFile.md.CYzaDkUO.lean.js", -"/ran/assets/src_ranui_button_index.md.Bv4CarK8.lean.js", +"/ran/assets/src_ranui_player_index.md.DwTQwuCV.js", +"/ran/assets/src_article_typescript_pattern.md.CLkB9hDT.lean.js", "/ran/assets/File.BiH2GpuW.jpeg", -"/ran/assets/cn_src_article_visual.md.Be4MMnab.lean.js", +"/ran/assets/src_article_sort_select_index.md.DVQbDLxi.lean.js", "/ran/assets/import.BTsVI5Tc.jpeg", -"/ran/assets/src_ranui_tabs_index.md.CkEcQ38o.lean.js", "/ran/assets/xgplayer_docs.CpHb1Wan.webp", "/ran/assets/inter-roman-latin.Di8DUHzh.woff2", -"/ran/assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.js", -"/ran/assets/src_ranui_tab_index.md.CciVbuSi.lean.js", -"/ran/assets/src_ranuts_utils_ocr.md.Bac0Ieci.js", -"/ran/assets/cn_src_ranui_tabs_index.md.BnLsjdMq.js", -"/ran/assets/src_types_模式匹配.md.CFNrWo4G.js", -"/ran/assets/cn_src_article_typescript_unionType.md.BgeL6nAW.lean.js", -"/ran/assets/src_ranui_popover_index.md.4cp6AOCz.lean.js", -"/ran/assets/src_article_functionalProgramming.md.Bbl-kdPi.lean.js", +"/ran/assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.js", +"/ran/assets/cn_src_article_typescript_unionType.md.BEJLXxlO.js", +"/ran/assets/cn_src_ranui_image_index.md.DTxkaIXi.lean.js", +"/ran/assets/src_article_sort_bucket_index.md.1nocTUlS.js", +"/ran/assets/src_ranuts_utils_task.md.DEROvLOz.lean.js", +"/ran/assets/src_article_sort_index.md.Day6RJ-s.lean.js", +"/ran/assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.lean.js", +"/ran/assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.js", +"/ran/assets/cn_src_article_sort_bucket_index.md.CInFho5i.lean.js", +"/ran/assets/src_article_imagemin.md.F6ka3QYU.lean.js", +"/ran/assets/cn_src_ranui_preview_index.md.HNgH1cdk.js", +"/ran/assets/cn_index.md.BLhfTKfk.js", +"/ran/assets/src_article_typescript_unionType.md.DWIWcdin.lean.js", "/ran/assets/ow365.CXWyYekS.webp", -"/ran/assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.lean.js", -"/ran/assets/src_ranui_input_index.md.dqo--3Vk.lean.js", -"/ran/assets/cn_src_ranui_modal_index.md.C5SgGuYU.lean.js", -"/ran/assets/src_ranui_preview_index.md.GrIEkeLk.js", -"/ran/assets/src_ranui_button_index.md.Bv4CarK8.js", -"/ran/assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.js", -"/ran/assets/cn_src_ranui_popover_index.md.YQ2K5eFD.js", +"/ran/assets/src_ranuts_bundler_index.md.DzEPrDNS.js", +"/ran/assets/src_ranui_progress_index.md.CubSOb3E.js", +"/ran/assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.js", +"/ran/assets/cn_src_ranui_modal_index.md.Ct_5BTpn.js", +"/ran/assets/src_ranui_input_index.md.BYzbt7Jg.js", +"/ran/assets/cn_src_article_visual.md.DseWRfx9.lean.js", +"/ran/assets/cn_src_ranui_image_index.md.DTxkaIXi.js", +"/ran/assets/cn_src_article_video.md.D_lFXyPJ.js", +"/ran/assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.lean.js", "/ran/assets/heap.D4myjC6C.gif", +"/ran/assets/src_article_javascript_domLoad.md.ISLuBQzT.js", +"/ran/assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.js", +"/ran/assets/src_ranuts_mode_subscribe.md.CBjAEwg0.lean.js", "/ran/assets/bubble.Csp5B4TH.gif", +"/ran/assets/cn_src_article_functionalProgramming.md.B0WkJbH2.lean.js", "/ran/assets/状态.Bt_a2OKX.png", -"/ran/assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.lean.js", -"/ran/assets/cn_src_ranuts_file_fileInfo.md.cszSvf9s.js", +"/ran/assets/cn_src_article_sort_radix_index.md.vBHH0tHh.js", "/ran/assets/wps_office.DiET-U8x.webp", -"/ran/assets/src_ranuts_file_appendFile.md.CN41s_o-.js", +"/ran/assets/src_ranuts_file_watchFile.md.i-gzm-Mr.js", "/ran/assets/ali_doc.CYo30EHy.webp", -"/ran/assets/cn_src_note_centos.md.BcqWCqVs.lean.js", +"/ran/assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.lean.js", "/ran/assets/ms_answer.Cgv6ylFF.webp", -"/ran/assets/src_ranuts_utils_filterObj.md.SUYinD61.js", -"/ran/assets/src_article_imagemin.md.CNzOGYN8.lean.js", -"/ran/assets/cn_src_ranuts_bundler_index.md.Bui4hlUf.lean.js", -"/ran/assets/cn_src_ranui_input_index.md.CKpr9bUR.lean.js", -"/ran/assets/cn_src_note_ubuntu.md.C1lhavpN.lean.js", -"/ran/assets/cn_src_article_visual.md.Be4MMnab.js", -"/ran/assets/cn_src_note_docker.md.BeX3ffKU.lean.js", +"/ran/assets/src_article_designMode.md.CsQyDQ9L.js", +"/ran/assets/src_ranuts_file_watchFile.md.i-gzm-Mr.lean.js", +"/ran/assets/src_ranui_loading_index.md.CPbTFkjm.lean.js", +"/ran/assets/cn_src_note_ubuntu.md.boe-scbt.lean.js", +"/ran/assets/cn_src_article_designMode.md.B2Mi_NTb.js", +"/ran/assets/src_ranuts_binaryTree_index.md.TEku7NN9.js", "/ran/assets/inter-italic-vietnamese.BSbpV94h.woff2", +"/ran/assets/cn_src_article_typescript_recursion.md.DXrQrnQX.lean.js", +"/ran/assets/src_article_sort_bubble_index.md.DmhPntEW.lean.js", "/ran/assets/Literal.Dl-JxujV.jpeg", -"/ran/assets/src_article_typescript_reconstruction.md.RJXBSd9W.lean.js", -"/ran/assets/src_article_typescript_calculate.md.CVW8Xao2.lean.js", -"/ran/assets/src_article_sort_merge_index.md.D6h05wbs.lean.js", -"/ran/assets/index.md.C1mV7KLR.lean.js", -"/ran/assets/src_article_sort_heap_index.md.D-XARjAx.js", -"/ran/assets/src_ranuts_utils_task.md.BA-6r3zJ.lean.js", +"/ran/assets/cn_src_ranui_icon_index.md.VgKq_hwc.js", +"/ran/assets/cn_src_article_sort_select_index.md.Cd2SKviA.js", +"/ran/assets/src_types_高级类型.md.xpfmnY7g.lean.js", +"/ran/assets/cn_src_article_sort_heap_index.md.CjUwiufT.js", "/ran/assets/firefox_support_media.BLmSeBNy.webp", "/ran/assets/inter-italic-latin.C2AdPX0b.woff2", -"/ran/assets/src_article_astParse_tokenizer.md.m5lyUfzX.lean.js", +"/ran/assets/cn_src_ranuts_mode_subscribe.md.Dfqta45A.lean.js", +"/ran/assets/src_article_astParse_tokenizer.md.CuCr3w1T.js", +"/ran/assets/src_article_typescript_calculate.md.vojGlJJE.lean.js", +"/ran/assets/cn_src_article_bundle.md.DQFZ3YRD.lean.js", "/ran/assets/video_format.CRMCTmKB.webp", -"/ran/assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.lean.js", -"/ran/assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.lean.js", -"/ran/assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.js", -"/ran/assets/src_ranuts_utils_task.md.BA-6r3zJ.js", -"/ran/assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.lean.js", -"/ran/assets/src_ranuts_index.md.CHFYg7WB.js", -"/ran/assets/index.md.C1mV7KLR.js", -"/ran/assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.js", +"/ran/assets/cn_src_ranui_button_index.md.ByX0UCnc.lean.js", +"/ran/assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.lean.js", +"/ran/assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.js", +"/ran/assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.js", +"/ran/assets/cn_src_article_imagemin.md.O-LRpAFt.lean.js", "/ran/assets/bundle.bky0NmdF.png", -"/ran/assets/src_article_typescript_index.md.CZyb-Nhf.lean.js", -"/ran/assets/cn_src_ranui_button_index.md.B-YucaRE.js", -"/ran/assets/src_ranui_progress_index.md.Be_sOmHB.lean.js", -"/ran/assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.js", -"/ran/assets/cn_src_types_高级类型.md.C_sodMsN.lean.js", +"/ran/assets/cn_src_ranui_input_index.md.Xj5VXLle.lean.js", +"/ran/assets/src_article_sort_quick_index.md.BVXZPTlC.js", +"/ran/assets/src_article_typescript_calculate.md.vojGlJJE.js", +"/ran/assets/src_article_sort_insert_index.md.Cmba0n6W.js", +"/ran/assets/cn_src_ranuts_index.md.Br1tGjNi.js", +"/ran/assets/src_article_bundle.md.CJFsaLBq.js", +"/ran/assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.lean.js", +"/ran/assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.lean.js", +"/ran/assets/cn_src_article_typescript_recursion.md.DXrQrnQX.js", +"/ran/assets/src_ranuts_file_writeFile.md.2z4SHchp.js", +"/ran/assets/cn_src_article_docPreview.md.BpvHIdsV.js", +"/ran/assets/src_article_sort_index.md.Day6RJ-s.js", "/ran/assets/count.CWSWBe_h.gif", +"/ran/assets/src_article_sort_count_index.md.Du6QhWUu.js", "/ran/assets/google_doc_view.BknjnOKw.webp", -"/ran/assets/cn_src_article_sort_merge_index.md.Dz-ycn2t.lean.js", +"/ran/assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.js", "/ran/assets/merge.Bguw-KQu.gif", -"/ran/assets/src_ranuts_index.md.CHFYg7WB.lean.js", -"/ran/assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.js", -"/ran/assets/cn_src_article_designMode.md.BzjcnDMg.lean.js", -"/ran/assets/cn_src_ranui_tab_index.md.BlcshkmY.lean.js", -"/ran/assets/src_ranui_player_index.md.DSXQVeNM.js", -"/ran/assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.js", -"/ran/assets/src_article_sort_count_index.md.Ch5HRRp8.lean.js", -"/ran/assets/src_ranuts_bundler_index.md.D3F9791C.lean.js", +"/ran/assets/cn_src_article_imagemin.md.O-LRpAFt.js", +"/ran/assets/src_ranui_checkbox_index.md.DZWEhA_6.lean.js", +"/ran/assets/src_ranuts_utils_filterObj.md.Q_-NUV8p.js", +"/ran/assets/src_ranui_image_index.md.DWE25Dbt.js", +"/ran/assets/src_ranuts_utils_str2xml.md.84gjZ6IG.js", "/ran/assets/继承.Dta6_xdc.png", +"/ran/assets/cn_src_article_sort_merge_index.md.CrLtJpMk.lean.js", +"/ran/assets/cn_src_article_sort_shell_index.md.CQc6o0U8.js", "/ran/assets/red_book.DiBEvryP.webp", +"/ran/assets/cn_src_ranui_select_index.md.x3hqXtMG.js", "/ran/assets/inter-italic-greek-ext.1u6EdAuj.woff2", +"/ran/assets/src_types_模式匹配.md.DO7GM6xX.js", "/ran/assets/radix.Bwrylu8F.gif", -"/ran/assets/src_ranuts_bundler_index.md.D3F9791C.js", -"/ran/assets/cn_src_ranuts_index.md.B_7KgyI-.js", -"/ran/assets/src_ranui_tabs_index.md.CkEcQ38o.js", -"/ran/assets/cn_src_article_typescript_unionType.md.BgeL6nAW.js", +"/ran/assets/src_ranuts_file_fileInfo.md.C2z4D1Q8.js", "/ran/assets/解释器.DymUKGTa.jpg", -"/ran/assets/src_ranui_progress_index.md.Be_sOmHB.js", -"/ran/assets/src_ranuts_file_readDir.md.d3nM0oPh.js", +"/ran/assets/src_ranuts_utils_getCookie.md.CRjg9zYL.js", +"/ran/assets/src_types_TS类型.md.Du9AK3lr.lean.js", +"/ran/assets/cn_src_article_sort_insert_index.md.C4G7YeWl.js", "/ran/assets/complexity.DSLVsjHt.png", -"/ran/assets/src_ranui_skeleton_index.md.DnZOMEV1.lean.js", "/ran/assets/ms_file_not.DgOrkG3n.webp", -"/ran/assets/cn_src_ranui_icon_index.md.7jVbQ82_.js", -"/ran/assets/src_ranui_loading_index.md.Cr0qirnC.lean.js", "/ran/assets/xml.NyzOyhr4.webp", -"/ran/assets/src_types_高级类型.md.BcsCXIM4.js", -"/ran/assets/src_article_sort_insert_index.md.B-oNm95-.lean.js", -"/ran/assets/src_ranui_player_index.md.DSXQVeNM.lean.js", -"/ran/assets/src_ranui_message_index.md.Dr_PZANG.lean.js", -"/ran/assets/cn_src_types_高级类型.md.C_sodMsN.js", -"/ran/assets/cn_src_article_typescript_index.md.CaFWWXxk.js", -"/ran/assets/cn_src_ranui_progress_index.md.BhrzleOl.lean.js", -"/ran/assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.lean.js", +"/ran/assets/cn_src_ranui_select_index.md.x3hqXtMG.lean.js", +"/ran/assets/cn_src_article_visual.md.DseWRfx9.js", +"/ran/assets/cn_src_ranui_player_index.md.ClG2z7Rd.js", +"/ran/assets/cn_src_article_sort_select_index.md.Cd2SKviA.lean.js", "/ran/assets/备忘录.meL0YZxn.jpg", -"/ran/assets/src_ranuts_binaryTree_index.md.DvglfkNJ.js", +"/ran/assets/src_ranui_button_index.md.xyRWgUv8.lean.js", +"/ran/assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.js", "/ran/assets/select.B8GwndZy.gif", -"/ran/assets/src_types_TS类型.md.BAdYqP2z.js", -"/ran/assets/src_types_TS类型.md.BAdYqP2z.lean.js", -"/ran/assets/src_article_sort_bucket_index.md.BTbi9aJ6.js", -"/ran/assets/src_ranuts_file_watchFile.md.DYvq_aUK.lean.js", -"/ran/assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.lean.js", -"/ran/assets/src_ranui_preview_index.md.GrIEkeLk.lean.js", -"/ran/assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.js", -"/ran/assets/src_article_sort_index.md.LNhlg9Oa.js", -"/ran/assets/cn_src_article_sort_bucket_index.md.Dl_rJ3sv.js", +"/ran/assets/src_article_typescript_unionType.md.DWIWcdin.js", +"/ran/assets/src_ranuts_file_readFile.md.Ac6LvpUh.js", +"/ran/assets/src_ranui_radar_index.md.DVubbf7p.js", +"/ran/assets/src_ranuts_mode_subscribe.md.CBjAEwg0.js", +"/ran/assets/src_article_typescript_index.md.CkcO1_RK.js", +"/ran/assets/cn_src_article_typescript_pattern.md.ClYdggen.js", "/ran/assets/sort.CSVZS1AV.png", -"/ran/assets/src_article_astParse_tokenizer.md.m5lyUfzX.js", +"/ran/assets/cn_src_ranui_tab_index.md._Yrr2Do-.lean.js", +"/ran/assets/src_ranui_index.md.CLoF5T0G.lean.js", "/ran/assets/quick.DD28bswc.gif", -"/ran/assets/cn_src_ranuts_mimeType_mimeType.md.nxjk8xi_.js", -"/ran/assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.lean.js", +"/ran/assets/src_ranui_tabs_index.md.Y_dSHlvB.js", +"/ran/assets/cn_src_article_sort_index.md.BhyMit8Y.lean.js", +"/ran/assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.js", +"/ran/assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.lean.js", +"/ran/assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.js", "/ran/assets/kkfile_des.CENUMtpY.webp", -"/ran/assets/cn_src_ranui_math_index.md.nmeuJ7af.lean.js", -"/ran/assets/src_article_typescript_calculate.md.CVW8Xao2.js", -"/ran/assets/cn_src_article_sort_select_index.md.C6f3fqev.lean.js", -"/ran/assets/src_article_javascript_domLoad.md.f1BWKaWm.lean.js", -"/ran/assets/src_article_javascript_domLoad.md.f1BWKaWm.js", -"/ran/assets/cn_src_ranuts_file_writeFile.md.DLZe7Swq.js", -"/ran/assets/src_ranui_message_index.md.Dr_PZANG.js", -"/ran/assets/src_ranuts_file_fileInfo.md.CZ2C_27B.lean.js", -"/ran/assets/cn_src_ranui_loading_index.md.CcRWELn0.js", +"/ran/assets/cn_src_article_sort_bucket_index.md.CInFho5i.js", +"/ran/assets/src_ranuts_utils_convertImageToBase64.md.COMNqW1t.js", +"/ran/assets/cn_src_types_模式匹配.md.Cv2DtzlL.lean.js", +"/ran/assets/cn_src_article_babel.md.igUT7dgP.lean.js", +"/ran/assets/src_article_sort_shell_index.md.CGmp_Zfr.lean.js", +"/ran/assets/cn_src_article_typescript_index.md.BE6MDNaH.lean.js", +"/ran/assets/cn_src_ranui_message_index.md.DQaOXw4A.js", "/ran/assets/bilibili_video_m4s.BccuY7bk.webp", +"/ran/assets/cn_src_article_typescript_unionType.md.BEJLXxlO.lean.js", +"/ran/assets/cn_src_ranui_index.md.DbAIrpTI.lean.js", "/ran/assets/ExpressionStatement.zaIHlhIF.jpeg", "/ran/assets/xdoc.CeowcMPq.webp", +"/ran/assets/cn_src_article_typescript_pattern.md.ClYdggen.lean.js", "/ran/assets/外观.Cm0-J0eF.png", +"/ran/assets/src_ranuts_file_readFile.md.Ac6LvpUh.lean.js", "/ran/assets/rplayer_demo.CoJ7kuJt.webp", -"/ran/assets/src_article_sort_select_index.md.CgrU_Nnu.lean.js", -"/ran/assets/cn_src_ranui_message_index.md.OWf7PDqT.js", -"/ran/assets/cn_src_ranui_progress_index.md.BhrzleOl.js", -"/ran/assets/cn_src_ranui_preview_index.md.MwV2D84f.lean.js", -"/ran/assets/src_article_functionalProgramming.md.Bbl-kdPi.js", -"/ran/assets/cn_src_ranui_image_index.md.CEdjKNte.js", -"/ran/assets/cn_src_ranui_radar_index.md.JUC5Tblz.js", -"/ran/assets/src_article_sort_bucket_index.md.BTbi9aJ6.lean.js", -"/ran/assets/cn_src_article_sort_select_index.md.C6f3fqev.js", -"/ran/assets/src_ranui_icon_index.md.Cada4EMN.js", +"/ran/assets/src_ranui_tabs_index.md.Y_dSHlvB.lean.js", +"/ran/assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.js", +"/ran/assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.js", +"/ran/assets/cn_src_note_centos.md.DNIN5Wz6.js", +"/ran/assets/cn_src_article_typescript_reconstruction.md.CwRrki5l.lean.js", "/ran/assets/组合.StqZ1pDc.png", -"/ran/assets/src_ranuts_utils_formatJson.md.BQjsxBql.js", -"/ran/assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.lean.js", -"/ran/assets/src_ranui_index.md.BQzsJSIN.js", -"/ran/assets/cn_src_ranui_modal_index.md.C5SgGuYU.js", -"/ran/assets/cn_src_ranuts_file_readDir.md.DVHZJj5k.lean.js", +"/ran/assets/cn_src_article_systemDesign.md.hF7LXas7.lean.js", +"/ran/assets/cn_src_ranuts_file_appendFile.md.B63KmkE8.lean.js", +"/ran/assets/cn_src_ranuts_utils_task.md.vM2hJa_Z.lean.js", +"/ran/assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.lean.js", +"/ran/assets/src_article_sort_count_index.md.Du6QhWUu.lean.js", +"/ran/assets/index.md.D7BH-fMN.lean.js", +"/ran/assets/src_ranuts_file_readDir.md.BvSC38m-.js", +"/ran/assets/src_ranuts_file_appendFile.md.1-6oJJ-H.js", "/ran/assets/safari_support_media.Bafr23bR.webp", -"/ran/assets/cn_src_ranui_button_index.md.B-YucaRE.lean.js", -"/ran/assets/cn_src_ranui_radar_index.md.JUC5Tblz.lean.js", -"/ran/assets/cn_src_article_sort_index.md.V1uGWtUp.lean.js", -"/ran/assets/src_ranui_popover_index.md.4cp6AOCz.js", -"/ran/assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.js", -"/ran/assets/src_ranui_loading_index.md.Cr0qirnC.js", -"/ran/assets/cn_src_note_docker.md.BeX3ffKU.js", -"/ran/assets/cn_src_article_bundle.md.BgkzbBow.js", -"/ran/assets/cn_src_article_sort_count_index.md.BwHTgoYv.js", +"/ran/assets/src_ranuts_binaryTree_index.md.TEku7NN9.lean.js", +"/ran/assets/cn_src_article_sort_count_index.md.CLrg_eAi.lean.js", +"/ran/assets/src_ranuts_index.md.BJ436-Wa.js", +"/ran/assets/src_ranui_button_index.md.xyRWgUv8.js", +"/ran/assets/cn_src_article_sort_index.md.BhyMit8Y.js", +"/ran/assets/cn_src_ranui_tab_index.md._Yrr2Do-.js", +"/ran/assets/src_article_functionalProgramming.md.BPj6iTX9.lean.js", +"/ran/assets/src_article_typescript_reconstruction.md.6hei5wds.lean.js", +"/ran/assets/src_ranuts_file_readDir.md.BvSC38m-.lean.js", "/ran/assets/chunks/index.CafPLwUV.js", "/ran/assets/chunks/warning-circle-DDUgEDIv.1BX6MOiy.js", "/ran/assets/chunks/warning-circle-fill-lODUKz0i.7RyGfSeR.js", @@ -321,191 +326,185 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/assets/chunks/input-input.MnARRJC6.js", "/ran/assets/chunks/index-Ba501-HG.DLGTjfcz.js", "/ran/assets/chunks/quick.WcLzRUPH.js", -"/ran/assets/src_article_sort_quick_index.md.C9uemKEa.js", +"/ran/assets/src_article_imagemin.md.F6ka3QYU.js", +"/ran/assets/cn_src_ranuts_bundler_index.md.DwAuuXbu.js", +"/ran/assets/src_ranuts_utils_str2xml.md.84gjZ6IG.lean.js", +"/ran/assets/src_article_sort_insert_index.md.Cmba0n6W.lean.js", "/ran/assets/kkfile_doc.CPfEUxoD.webp", -"/ran/assets/cn_src_ranui_select_index.md.CqrSnS3F.js", -"/ran/assets/src_ranuts_file_writeFile.md.CdGN48zU.js", "/ran/assets/ms_excel.CJXFi2bf.webp", +"/ran/assets/cn_src_ranuts_file_readFile.md._sEkTrwK.js", +"/ran/assets/cn_src_article_sort_insert_index.md.C4G7YeWl.lean.js", "/ran/assets/原型.DYbH0CSA.jpg", -"/ran/assets/src_ranui_select_index.md.DgN8GI_q.js", -"/ran/assets/src_article_sort_insert_index.md.B-oNm95-.js", -"/ran/assets/src_ranuts_utils_convertImageToBase64.md.CQdNGUWw.lean.js", -"/ran/assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.lean.js", -"/ran/assets/cn_src_article_designMode.md.BzjcnDMg.js", -"/ran/assets/src_types_高级类型.md.BcsCXIM4.lean.js", -"/ran/assets/src_ranui_radar_index.md.CkBeOv58.lean.js", -"/ran/assets/src_ranui_select_index.md.DgN8GI_q.lean.js", -"/ran/assets/src_article_typescript_recursion.md.CxwYW6pd.lean.js", -"/ran/assets/cn_src_article_sort_radix_index.md.CmXyO2h4.js", -"/ran/assets/cn_src_article_sort_insert_index.md.BPxYKrUM.js", -"/ran/assets/cn_src_ranuts_file_watchFile.md.5h3FtZpC.lean.js", -"/ran/assets/cn_src_article_sort_shell_index.md.B0DKjSD6.js", +"/ran/assets/cn_src_article_babel.md.igUT7dgP.js", +"/ran/assets/src_ranui_select_index.md.DQq3MVHp.lean.js", +"/ran/assets/cn_src_article_sort_merge_index.md.CrLtJpMk.js", +"/ran/assets/src_ranui_tab_index.md.DSSJuz31.lean.js", +"/ran/assets/cn_src_article_sort_quick_index.md.CmIbA4AB.lean.js", +"/ran/assets/cn_src_article_docPreview.md.BpvHIdsV.lean.js", +"/ran/assets/src_types_高级类型.md.xpfmnY7g.js", +"/ran/assets/src_types_模式匹配.md.DO7GM6xX.lean.js", +"/ran/assets/src_article_sort_merge_index.md.BYCAvkHG.js", +"/ran/assets/cn_src_ranuts_utils_formatJson.md.BPEAnSbs.js", +"/ran/assets/cn_src_article_sort_radix_index.md.vBHH0tHh.lean.js", +"/ran/assets/src_ranuts_utils_ocr.md.COxYWSg4.js", +"/ran/assets/src_ranui_index.md.CLoF5T0G.js", +"/ran/assets/cn_src_article_typescript_calculate.md.DFF0Jmnv.lean.js", +"/ran/assets/cn_src_ranui_popover_index.md.CKCBCC0z.js", "/ran/assets/inter-roman-latin-ext.4ZJIpNVo.woff2", -"/ran/assets/src_article_typescript_index.md.CZyb-Nhf.js", -"/ran/assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.lean.js", -"/ran/assets/src_article_imagemin.md.CNzOGYN8.js", -"/ran/assets/cn_src_article_sort_insert_index.md.BPxYKrUM.lean.js", +"/ran/assets/src_ranuts_utils_getCookie.md.CRjg9zYL.lean.js", +"/ran/assets/cn_src_ranui_input_index.md.Xj5VXLle.js", +"/ran/assets/cn_src_ranui_math_index.md.vkrzCmwa.lean.js", "/ran/assets/ms_word.3k-0mjp0.webp", "/ran/assets/inter-italic-greek.DJ8dCoTZ.woff2", -"/ran/assets/cn_src_types_类型运算.md.BALncgle.lean.js", -"/ran/assets/cn_src_article_docPreview.md.Bk6_K5Up.lean.js", -"/ran/assets/src_ranuts_mode_subscribe.md.DmGp9s_W.lean.js", "/ran/assets/tiktik_video_demo.mxxBzLay.webp", -"/ran/assets/src_ranui_image_index.md.iND1p-rT.js", "/ran/assets/inter-italic-cyrillic-ext.r48I6akx.woff2", -"/ran/assets/src_ranuts_file_readDir.md.d3nM0oPh.lean.js", "/ran/assets/inter-italic-latin-ext.CN1xVJS-.woff2", -"/ran/assets/cn_src_ranui_index.md.Cv0LhI4v.js", -"/ran/assets/src_article_sort_count_index.md.Ch5HRRp8.js", -"/ran/assets/src_ranuts_file_fileInfo.md.CZ2C_27B.js", -"/ran/assets/cn_src_ranui_math_index.md.nmeuJ7af.js", -"/ran/assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.lean.js", -"/ran/assets/cn_src_ranuts_binaryTree_index.md.CoTPSG40.js", +"/ran/assets/src_article_sort_select_index.md.DVQbDLxi.js", +"/ran/assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.js", +"/ran/assets/cn_src_ranuts_utils_getCookie.md.Cn937pro.lean.js", +"/ran/assets/src_article_babel.md.Cy_u0Zgd.lean.js", +"/ran/assets/index.md.D7BH-fMN.js", +"/ran/assets/cn_src_ranui_popover_index.md.CKCBCC0z.lean.js", "/ran/assets/style.D7_Ytddv.css", -"/ran/assets/cn_src_note_libreoffice2wasm.md.DATIsYiT.js", -"/ran/assets/src_article_sort_index.md.LNhlg9Oa.lean.js", -"/ran/assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.js", +"/ran/assets/cn_src_article_sort_heap_index.md.CjUwiufT.lean.js", +"/ran/assets/src_article_sort_shell_index.md.CGmp_Zfr.js", "/ran/assets/Expression.DczLaznn.jpeg", -"/ran/assets/src_article_designMode.md.DWnxNFS_.lean.js", -"/ran/assets/src_ranui_modal_index.md.BChGxskG.lean.js", -"/ran/assets/cn_src_article_babel.md.Bb4oNZ50.lean.js", -"/ran/assets/cn_src_article_typescript_reconstruction.md.B-6dp4-w.js", -"/ran/assets/cn_src_note_ubuntu.md.C1lhavpN.js", -"/ran/assets/cn_index.md.CC940uHT.js", +"/ran/assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.js", +"/ran/assets/cn_src_note_ubuntu.md.boe-scbt.js", +"/ran/assets/cn_src_ranui_checkbox_index.md.Csmdnt0O.lean.js", +"/ran/assets/cn_src_ranui_modal_index.md.Ct_5BTpn.lean.js", "/ran/assets/customElements.DbqgaaNb.png", -"/ran/assets/cn_src_ranui_popover_index.md.YQ2K5eFD.lean.js", -"/ran/assets/cn_src_ranuts_utils_convertImageToBase64.md.BQFuSXTt.lean.js", +"/ran/assets/src_types_类型运算.md.D_oUktxL.js", +"/ran/assets/src_article_sort_heap_index.md.DUjhT9HH.lean.js", +"/ran/assets/src_ranuts_file_writeFile.md.2z4SHchp.lean.js", +"/ran/assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.js", +"/ran/assets/src_article_functionalProgramming.md.BPj6iTX9.js", +"/ran/assets/cn_src_ranui_loading_index.md.Cd9-GnXr.js", "/ran/assets/wps_office_price.ler9htjh.webp", "/ran/assets/kkfile_output.DD8iZdbo.webp", -"/ran/assets/src_article_typescript_reconstruction.md.RJXBSd9W.js", -"/ran/assets/src_ranuts_file_readFile.md.CwPAyuVM.lean.js", -"/ran/assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.lean.js", -"/ran/assets/src_article_typescript_unionType.md.lwThafAB.lean.js", -"/ran/assets/cn_src_article_sort_bubble_index.md.Cr8QMhiM.js", -"/ran/assets/cn_src_ranui_preview_index.md.MwV2D84f.js", -"/ran/assets/src_ranui_radar_index.md.CkBeOv58.js", -"/ran/assets/src_ranui_skeleton_index.md.DnZOMEV1.js", -"/ran/assets/src_ranui_icon_index.md.Cada4EMN.lean.js", -"/ran/assets/src_ranuts_utils_getCookie.md.glwGRuBT.lean.js", -"/ran/assets/src_article_bundle.md.BvqfF4Lk.js", +"/ran/assets/cn_src_ranui_player_index.md.ClG2z7Rd.lean.js", +"/ran/assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.lean.js", +"/ran/assets/cn_src_types_高级类型.md.BDtDighX.js", +"/ran/assets/cn_src_article_sort_bubble_index.md.BL_0xBSg.js", +"/ran/assets/cn_src_types_模式匹配.md.Cv2DtzlL.js", +"/ran/assets/src_ranui_checkbox_index.md.DZWEhA_6.js", +"/ran/assets/src_ranui_message_index.md.C9TUA1qN.lean.js", +"/ran/assets/src_ranuts_bundler_index.md.DzEPrDNS.lean.js", "/ran/assets/ts_demo.D_l_mv9k.webp", "/ran/assets/装饰.CuuWN9YK.jpg", +"/ran/assets/cn_src_note_docker.md.Dwk9bEUN.lean.js", "/ran/assets/桥接.DX0mO5JC.png", +"/ran/assets/cn_src_ranuts_file_readDir.md.BAFE9wq0.js", "/ran/assets/Program.BBf_t-me.jpeg", -"/ran/assets/cn_src_article_bundle.md.BgkzbBow.lean.js", -"/ran/assets/src_article_sort_bubble_index.md.DM2Rnnat.lean.js", -"/ran/assets/src_article_sort_heap_index.md.D-XARjAx.lean.js", -"/ran/assets/cn_src_article_sort_quick_index.md.Dm-fpFZL.js", +"/ran/assets/cn_src_ranuts_utils_ocr.md.DDRRaGe9.lean.js", +"/ran/assets/src_ranui_preview_index.md.hkKqqHBY.js", +"/ran/assets/cn_src_ranui_loading_index.md.Cd9-GnXr.lean.js", "/ran/assets/Comment.BYtNY-L1.jpeg", -"/ran/assets/src_ranui_modal_index.md.BChGxskG.js", "/ran/assets/inter-roman-greek-ext.CqjqNYQ-.woff2", -"/ran/assets/cn_src_article_typescript_pattern.md.GG3CpSHa.lean.js", +"/ran/assets/src_article_typescript_recursion.md.Cklrk5ux.lean.js", "/ran/assets/策略.BAijEgGz.png", -"/ran/assets/src_ranui_index.md.BQzsJSIN.lean.js", +"/ran/assets/src_article_sort_bucket_index.md.1nocTUlS.lean.js", "/ran/assets/axtexplorer.D7PG-3cx.jpeg", "/ran/assets/ms_answer_2.D-D9H1v0.webp", "/ran/assets/inter-roman-cyrillic.C5lxZ8CY.woff2", -"/ran/assets/cn_src_article_systemDesign.md.Oy3scZvv.js", "/ran/assets/Identifier.lJSxyFTe.jpeg", -"/ran/assets/cn_src_article_imagemin.md.CEVub_ZQ.lean.js", -"/ran/assets/src_article_babel.md.D0mGQXMo.lean.js", -"/ran/assets/src_types_类型运算.md.DitU5zkD.js", -"/ran/assets/cn_src_ranuts_file_readFile.md.CMWdjeNp.lean.js", -"/ran/assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.js", +"/ran/assets/cn_src_article_video.md.D_lFXyPJ.lean.js", +"/ran/assets/cn_src_article_functionalProgramming.md.B0WkJbH2.js", +"/ran/assets/src_ranui_skeleton_index.md.BUf_Burp.lean.js", +"/ran/assets/src_article_sort_merge_index.md.BYCAvkHG.lean.js", "/ran/assets/aqiyi_demo.s0swGzvF.webp", -"/ran/assets/cn_src_article_sort_index.md.V1uGWtUp.js", "/ran/assets/input-input.1X1aE5oH.jpg", -"/ran/assets/cn_src_article_babel.md.Bb4oNZ50.js", -"/ran/assets/cn_src_article_astParse_tokenizer.md.CbTsq3qg.js", -"/ran/assets/cn_src_article_functionalProgramming.md.BI-pI2Tm.lean.js", -"/ran/assets/cn_src_ranuts_mode_subscribe.md.BpJB8REw.lean.js", -"/ran/assets/cn_src_ranui_checkbox_index.md.7OhoHSmh.lean.js", -"/ran/assets/cn_src_types_模式匹配.md.DWHjoF0h.lean.js", -"/ran/assets/src_article_bundle.md.BvqfF4Lk.lean.js", +"/ran/assets/cn_src_article_systemDesign.md.hF7LXas7.js", +"/ran/assets/src_ranuts_utils_ocr.md.COxYWSg4.lean.js", +"/ran/assets/cn_src_types_高级类型.md.BDtDighX.lean.js", +"/ran/assets/src_ranui_loading_index.md.CPbTFkjm.js", +"/ran/assets/cn_src_ranui_radar_index.md.D7lwGVbW.js", +"/ran/assets/src_article_typescript_reconstruction.md.6hei5wds.js", +"/ran/assets/cn_src_types_TS类型.md.BqYfr0BF.js", +"/ran/assets/cn_src_ranui_progress_index.md.B6p9KAvb.lean.js", +"/ran/assets/src_types_TS类型.md.Du9AK3lr.js", +"/ran/assets/cn_src_ranuts_utils_convertImageToBase64.md.Baw_0VwK.lean.js", +"/ran/assets/src_article_sort_heap_index.md.DUjhT9HH.js", +"/ran/assets/src_ranuts_mimeType_mimeType.md.CgbW52XX.lean.js", +"/ran/assets/src_ranui_progress_index.md.CubSOb3E.lean.js", +"/ran/assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.js", "/ran/assets/访问者.aEI4m-5a.png", -"/ran/assets/src_ranui_image_index.md.iND1p-rT.lean.js", -"/ran/assets/cn_src_types_类型运算.md.BALncgle.js", -"/ran/assets/src_article_typescript_unionType.md.lwThafAB.js", -"/ran/assets/src_article_typescript_pattern.md.dRLdKmiq.js", -"/ran/assets/src_article_sort_shell_index.md.DOUhMGGU.lean.js", -"/ran/assets/cn_src_ranuts_utils_getCookie.md.ChQkIFFD.lean.js", +"/ran/assets/cn_src_ranui_tabs_index.md.Y6X6hlvp.lean.js", +"/ran/assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.lean.js", "/ran/assets/insert.gf3GhDvq.gif", +"/ran/assets/src_article_sort_radix_index.md.CZgriu-L.js", +"/ran/assets/src_article_bundle.md.CJFsaLBq.lean.js", +"/ran/assets/cn_src_ranuts_file_readFile.md._sEkTrwK.lean.js", "/ran/assets/bili_demo.CI8Ur8IA.webp", -"/ran/assets/cn_src_ranui_tabs_index.md.BnLsjdMq.lean.js", -"/ran/assets/src_ranui_math_index.md.CAvnqA_Q.lean.js", -"/ran/assets/cn_src_article_sort_radix_index.md.CmXyO2h4.lean.js", -"/ran/assets/cn_src_ranui_select_index.md.CqrSnS3F.lean.js", -"/ran/assets/cn_src_ranuts_utils_filterObj.md.R6pWg6_Y.lean.js", -"/ran/assets/cn_src_article_sort_heap_index.md.oxRUNOKZ.js", -"/ran/assets/cn_src_article_imagemin.md.CEVub_ZQ.js", -"/ran/assets/cn_src_ranuts_utils_ocr.md.CCSas4Mw.js", -"/ran/assets/src_article_sort_merge_index.md.D6h05wbs.js", -"/ran/assets/cn_src_article_javascript_domLoad.md.CI-VlQc8.js", -"/ran/assets/cn_src_ranui_loading_index.md.CcRWELn0.lean.js", -"/ran/assets/cn_src_article_sort_shell_index.md.B0DKjSD6.lean.js", -"/ran/assets/src_article_designMode.md.DWnxNFS_.js", -"/ran/assets/src_ranuts_mode_subscribe.md.DmGp9s_W.js", -"/ran/assets/cn_src_types_TS类型.md.ctrtnDXL.lean.js", -"/ran/assets/cn_src_article_video.md.Cz2oSvp1.lean.js", -"/ran/assets/cn_src_article_typescript_pattern.md.GG3CpSHa.js", -"/ran/assets/cn_src_ranuts_index.md.B_7KgyI-.lean.js", +"/ran/assets/cn_src_types_类型运算.md.DMkykj3f.js", +"/ran/assets/cn_src_ranui_message_index.md.DQaOXw4A.lean.js", +"/ran/assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.js", +"/ran/assets/cn_src_ranuts_utils_str2xml.md.Bde3XsNH.lean.js", +"/ran/assets/cn_src_ranuts_file_writeFile.md.Bu4zBA8r.lean.js", "/ran/assets/tiktok_video.DuwKyIdt.webp", -"/ran/assets/cn_src_article_systemDesign.md.Oy3scZvv.lean.js", -"/ran/assets/src_article_sort_select_index.md.CgrU_Nnu.js", -"/ran/assets/cn_src_ranui_skeleton_index.md.DfgRdP5t.js", -"/ran/assets/src_types_模式匹配.md.CFNrWo4G.lean.js", -"/ran/assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.lean.js", -"/ran/assets/src_ranuts_file_writeFile.md.CdGN48zU.lean.js", -"/ran/assets/src_article_sort_bubble_index.md.DM2Rnnat.js", -"/ran/assets/src_ranuts_mimeType_mimeType.md.Bp_Buc7N.js", -"/ran/assets/src_ranui_math_index.md.CAvnqA_Q.js", +"/ran/assets/src_ranui_tab_index.md.DSSJuz31.js", +"/ran/assets/cn_src_ranui_math_index.md.vkrzCmwa.js", +"/ran/assets/cn_src_ranui_radar_index.md.D7lwGVbW.lean.js", +"/ran/assets/cn_src_note_centos.md.DNIN5Wz6.lean.js", +"/ran/assets/src_article_javascript_domLoad.md.ISLuBQzT.lean.js", +"/ran/assets/src_ranuts_index.md.BJ436-Wa.lean.js", +"/ran/assets/cn_src_ranui_skeleton_index.md.CxsOeOpu.lean.js", "/ran/assets/mdn_pdf.DbNmeC8H.webp", -"/ran/assets/cn_src_ranui_input_index.md.CKpr9bUR.js", -"/ran/assets/cn_src_ranuts_utils_formatJson.md.w3L-Rhdf.js", +"/ran/assets/src_ranui_radar_index.md.DVubbf7p.lean.js", +"/ran/assets/src_article_typescript_recursion.md.Cklrk5ux.js", +"/ran/assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.lean.js", +"/ran/assets/src_ranui_popover_index.md.PFeffqLM.lean.js", +"/ran/assets/cn_src_ranuts_file_watchFile.md.DizkqDrO.js", +"/ran/assets/src_article_sort_bubble_index.md.DmhPntEW.js", +"/ran/assets/cn_src_types_类型运算.md.DMkykj3f.lean.js", "/ran/assets/inter-roman-vietnamese.BjW4sHH5.woff2", -"/ran/assets/cn_src_types_模式匹配.md.DWHjoF0h.js", -"/ran/assets/src_ranui_input_index.md.dqo--3Vk.js", -"/ran/assets/src_article_sort_shell_index.md.DOUhMGGU.js", -"/ran/assets/cn_src_ranui_image_index.md.CEdjKNte.lean.js", -"/ran/assets/src_types_类型运算.md.DitU5zkD.lean.js", +"/ran/assets/cn_src_ranui_button_index.md.ByX0UCnc.js", +"/ran/assets/src_ranui_popover_index.md.PFeffqLM.js", +"/ran/assets/src_ranui_math_index.md.BEaDYiBh.js", +"/ran/assets/cn_src_ranuts_mimeType_mimeType.md.D8QLJam4.lean.js", +"/ran/assets/cn_src_note_libreoffice2wasm.md.DG6uOQ65.lean.js", +"/ran/assets/src_ranui_modal_index.md.CeMIN_J0.lean.js", +"/ran/assets/src_ranui_input_index.md.BYzbt7Jg.lean.js", "/ran/assets/Class.Cx5QD1OX.jpeg", "/ran/assets/shell.CZ-z1IVg.gif", "/ran/assets/balanceTree.DP_9yIkO.png", -"/ran/assets/cn_src_ranui_player_index.md.DXjOFy1h.js", +"/ran/assets/cn_index.md.BLhfTKfk.lean.js", "/ran/assets/建造者.B6neb_7R.jpeg", +"/ran/assets/cn_src_ranui_index.md.DbAIrpTI.js", +"/ran/assets/cn_src_ranuts_binaryTree_index.md.B0ahr_Mn.js", "/ran/assets/kkfile.X1tAqvNa.webp", -"/ran/assets/src_ranuts_binaryTree_index.md.DvglfkNJ.lean.js", -"/ran/assets/cn_src_article_video.md.Cz2oSvp1.js", -"/ran/assets/src_ranui_checkbox_index.md.D6UQK38M.lean.js", +"/ran/assets/src_types_类型运算.md.D_oUktxL.lean.js", +"/ran/assets/src_article_typescript_pattern.md.CLkB9hDT.js", +"/ran/assets/src_ranui_math_index.md.BEaDYiBh.lean.js", +"/ran/assets/cn_src_article_astParse_tokenizer.md.fRsPjbrs.js", +"/ran/assets/cn_src_ranuts_utils_filterObj.md.CxRU9J7j.lean.js", "/ran/assets/Declaration.CplvpFd-.jpeg", "/ran/assets/ms_support.CaZSSiRt.webp", -"/ran/assets/src_ranuts_file_readFile.md.CwPAyuVM.js", -"/ran/assets/cn_src_article_sort_count_index.md.BwHTgoYv.lean.js", -"/ran/assets/cn_src_types_TS类型.md.ctrtnDXL.js", +"/ran/assets/cn_src_ranuts_file_fileInfo.md.CqJn8JuV.js", +"/ran/assets/src_ranuts_utils_formatJson.md.DQX6wWMq.lean.js", +"/ran/assets/src_ranui_image_index.md.DWE25Dbt.lean.js", +"/ran/assets/cn_src_ranui_icon_index.md.VgKq_hwc.lean.js", +"/ran/assets/src_article_sort_radix_index.md.CZgriu-L.lean.js", +"/ran/assets/cn_src_article_sort_quick_index.md.CmIbA4AB.js", +"/ran/assets/cn_src_article_javascript_domLoad.md.Cc9Twcl2.lean.js", "/ran/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2", -"/ran/assets/src_ranui_tab_index.md.CciVbuSi.js", -"/ran/assets/cn_src_note_centos.md.BcqWCqVs.js", -"/ran/assets/cn_src_ranuts_utils_task.md.BLYztfpY.js", -"/ran/assets/cn_src_ranui_index.md.Cv0LhI4v.lean.js", -"/ran/assets/cn_src_article_typescript_recursion.md.NrD8NlPF.js", -"/ran/assets/src_ranuts_file_appendFile.md.CN41s_o-.lean.js", -"/ran/assets/src_ranuts_utils_getCookie.md.glwGRuBT.js", -"/ran/assets/cn_src_ranuts_utils_str2xml.md.CgTEUJ7O.lean.js", -"/ran/assets/cn_src_ranuts_utils_task.md.BLYztfpY.lean.js", -"/ran/assets/cn_src_ranui_icon_index.md.7jVbQ82_.lean.js", +"/ran/assets/cn_src_note_docker.md.Dwk9bEUN.js", +"/ran/assets/src_ranuts_file_appendFile.md.1-6oJJ-H.lean.js", +"/ran/assets/src_ranuts_utils_formatJson.md.DQX6wWMq.js", +"/ran/assets/cn_src_article_sort_count_index.md.CLrg_eAi.js", +"/ran/assets/cn_src_ranui_preview_index.md.HNgH1cdk.lean.js", +"/ran/assets/src_article_designMode.md.CsQyDQ9L.lean.js", "/ran/assets/单例.B4dKFqxx.jpg", -"/ran/assets/src_ranuts_utils_ocr.md.Bac0Ieci.lean.js", -"/ran/assets/src_article_sort_quick_index.md.C9uemKEa.lean.js", -"/ran/assets/src_article_babel.md.D0mGQXMo.js", +"/ran/assets/src_ranui_preview_index.md.hkKqqHBY.lean.js", +"/ran/assets/src_article_typescript_index.md.CkcO1_RK.lean.js", "/ran/assets/axtexplorerSave.a5hTrvd2.jpeg", "/ran/assets/firefox_pdf.BWQR-ogn.webp", -"/ran/sw1728129561.js", "/ran/icon_48.png", "/ran/icon_144.png", "/ran/pagefind/pagefind-entry.json", "/ran/pagefind/pagefind-modular-ui.js", -"/ran/pagefind/pagefind.en_9aedcc98b3.pf_meta", -"/ran/pagefind/pagefind.zh-cn_5630655c2f3e8.pf_meta", +"/ran/pagefind/pagefind.zh-cn_b71a7d8847108.pf_meta", +"/ran/pagefind/pagefind.en_853ef581ec.pf_meta", "/ran/pagefind/pagefind-ui.js", "/ran/pagefind/pagefind-modular-ui.css", "/ran/pagefind/wasm.unknown.pagefind", @@ -530,20 +529,18 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/pagefind/fragment/zh-cn_f9c4333.pf_fragment", "/ran/pagefind/fragment/zh-cn_489c27d.pf_fragment", "/ran/pagefind/fragment/zh-cn_4152685.pf_fragment", -"/ran/pagefind/fragment/en_6b4c4a9.pf_fragment", "/ran/pagefind/fragment/en_349bd7e.pf_fragment", "/ran/pagefind/fragment/zh-cn_c1151c9.pf_fragment", -"/ran/pagefind/fragment/zh-cn_5f3328b.pf_fragment", +"/ran/pagefind/fragment/zh-cn_5584627.pf_fragment", "/ran/pagefind/fragment/en_9755f3c.pf_fragment", "/ran/pagefind/fragment/zh-cn_e64f93e.pf_fragment", +"/ran/pagefind/fragment/zh-cn_0ea6667.pf_fragment", "/ran/pagefind/fragment/en_c8893d7.pf_fragment", "/ran/pagefind/fragment/en_7b5a911.pf_fragment", -"/ran/pagefind/fragment/en_aab3547.pf_fragment", "/ran/pagefind/fragment/en_7a4eb73.pf_fragment", -"/ran/pagefind/fragment/zh-cn_82b59c2.pf_fragment", +"/ran/pagefind/fragment/zh-cn_6df03b5.pf_fragment", "/ran/pagefind/fragment/zh-cn_7d7b75a.pf_fragment", -"/ran/pagefind/fragment/en_edda142.pf_fragment", -"/ran/pagefind/fragment/en_dccebd7.pf_fragment", +"/ran/pagefind/fragment/en_b7c8c16.pf_fragment", "/ran/pagefind/fragment/zh-cn_5d1b782.pf_fragment", "/ran/pagefind/fragment/zh-cn_f09c929.pf_fragment", "/ran/pagefind/fragment/en_2771bf1.pf_fragment", @@ -551,65 +548,72 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/pagefind/fragment/en_27ced55.pf_fragment", "/ran/pagefind/fragment/zh-cn_50313a9.pf_fragment", "/ran/pagefind/fragment/zh-cn_75a264c.pf_fragment", +"/ran/pagefind/fragment/zh-cn_154849c.pf_fragment", "/ran/pagefind/fragment/zh-cn_5bab657.pf_fragment", "/ran/pagefind/fragment/zh-cn_67d6c7a.pf_fragment", -"/ran/pagefind/fragment/en_70bee3b.pf_fragment", +"/ran/pagefind/fragment/zh-cn_d388141.pf_fragment", "/ran/pagefind/fragment/zh-cn_544fd92.pf_fragment", "/ran/pagefind/fragment/en_c2a5686.pf_fragment", +"/ran/pagefind/fragment/zh-cn_2c21dbd.pf_fragment", "/ran/pagefind/fragment/en_e34b39a.pf_fragment", "/ran/pagefind/fragment/zh-cn_d98338a.pf_fragment", +"/ran/pagefind/fragment/en_7118fbe.pf_fragment", "/ran/pagefind/fragment/en_fba3fcb.pf_fragment", "/ran/pagefind/fragment/en_7755d1b.pf_fragment", +"/ran/pagefind/fragment/en_449b12a.pf_fragment", "/ran/pagefind/fragment/en_229996f.pf_fragment", "/ran/pagefind/fragment/en_65ccfe9.pf_fragment", +"/ran/pagefind/fragment/en_38d8359.pf_fragment", "/ran/pagefind/fragment/zh-cn_e88bc3f.pf_fragment", "/ran/pagefind/fragment/zh-cn_3de2e5c.pf_fragment", "/ran/pagefind/fragment/zh-cn_c4a5919.pf_fragment", -"/ran/pagefind/fragment/en_63f4b5c.pf_fragment", "/ran/pagefind/fragment/en_738928a.pf_fragment", +"/ran/pagefind/fragment/zh-cn_565fd27.pf_fragment", "/ran/pagefind/fragment/en_12451bd.pf_fragment", "/ran/pagefind/fragment/en_a034a97.pf_fragment", "/ran/pagefind/fragment/zh-cn_5f4f874.pf_fragment", "/ran/pagefind/fragment/en_b7a3e23.pf_fragment", -"/ran/pagefind/fragment/en_f575e71.pf_fragment", +"/ran/pagefind/fragment/zh-cn_e6dde39.pf_fragment", "/ran/pagefind/fragment/en_f5a1553.pf_fragment", -"/ran/pagefind/fragment/zh-cn_f260254.pf_fragment", "/ran/pagefind/fragment/zh-cn_ac92f78.pf_fragment", "/ran/pagefind/fragment/en_b1c2dff.pf_fragment", -"/ran/pagefind/fragment/zh-cn_8c8e8be.pf_fragment", +"/ran/pagefind/fragment/zh-cn_27c99c4.pf_fragment", "/ran/pagefind/fragment/en_91e701d.pf_fragment", "/ran/pagefind/fragment/zh-cn_5bb9206.pf_fragment", +"/ran/pagefind/fragment/zh-cn_9879172.pf_fragment", "/ran/pagefind/fragment/en_39ca4e7.pf_fragment", "/ran/pagefind/fragment/zh-cn_f379427.pf_fragment", "/ran/pagefind/fragment/en_bc653ea.pf_fragment", "/ran/pagefind/fragment/zh-cn_5df12ef.pf_fragment", "/ran/pagefind/fragment/zh-cn_c2e89f3.pf_fragment", -"/ran/pagefind/fragment/zh-cn_c999918.pf_fragment", -"/ran/pagefind/fragment/zh-cn_69610a7.pf_fragment", +"/ran/pagefind/fragment/en_b110a5b.pf_fragment", "/ran/pagefind/fragment/zh-cn_e347438.pf_fragment", +"/ran/pagefind/fragment/en_b24f7ef.pf_fragment", "/ran/pagefind/fragment/zh-cn_542bc65.pf_fragment", +"/ran/pagefind/fragment/zh-cn_e831ca8.pf_fragment", "/ran/pagefind/fragment/zh-cn_3e43b1f.pf_fragment", "/ran/pagefind/fragment/zh-cn_44fa7df.pf_fragment", "/ran/pagefind/fragment/en_a3a3739.pf_fragment", "/ran/pagefind/fragment/en_422fcc1.pf_fragment", -"/ran/pagefind/fragment/zh-cn_bc27649.pf_fragment", +"/ran/pagefind/fragment/zh-cn_5ab33e7.pf_fragment", "/ran/pagefind/fragment/zh-cn_cfbcb39.pf_fragment", +"/ran/pagefind/fragment/en_5eef384.pf_fragment", "/ran/pagefind/fragment/zh-cn_c4f682e.pf_fragment", +"/ran/pagefind/fragment/en_30709d8.pf_fragment", "/ran/pagefind/fragment/zh-cn_5ca356d.pf_fragment", "/ran/pagefind/fragment/zh-cn_a7edffe.pf_fragment", -"/ran/pagefind/fragment/en_16d01f2.pf_fragment", "/ran/pagefind/fragment/en_6a66788.pf_fragment", -"/ran/pagefind/fragment/zh-cn_b2e449c.pf_fragment", -"/ran/pagefind/fragment/zh-cn_af6e5c4.pf_fragment", "/ran/pagefind/fragment/zh-cn_58279de.pf_fragment", "/ran/pagefind/fragment/zh-cn_61c329a.pf_fragment", +"/ran/pagefind/fragment/zh-cn_39da2fe.pf_fragment", +"/ran/pagefind/fragment/zh-cn_68b3659.pf_fragment", "/ran/pagefind/fragment/en_1d9d6e5.pf_fragment", "/ran/pagefind/fragment/en_3f75462.pf_fragment", "/ran/pagefind/fragment/en_1fd6515.pf_fragment", "/ran/pagefind/fragment/zh-cn_f7dcce8.pf_fragment", "/ran/pagefind/fragment/en_106ee7d.pf_fragment", +"/ran/pagefind/fragment/en_815cbc8.pf_fragment", "/ran/pagefind/fragment/en_7322c43.pf_fragment", -"/ran/pagefind/fragment/en_3541bef.pf_fragment", "/ran/pagefind/fragment/en_b2b1326.pf_fragment", "/ran/pagefind/fragment/en_19ba7e6.pf_fragment", "/ran/pagefind/fragment/zh-cn_59bdbd3.pf_fragment", @@ -618,42 +622,37 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/pagefind/fragment/zh-cn_c551e89.pf_fragment", "/ran/pagefind/fragment/en_b46590e.pf_fragment", "/ran/pagefind/fragment/zh-cn_b7b2564.pf_fragment", +"/ran/pagefind/fragment/en_3df5c5f.pf_fragment", "/ran/pagefind/fragment/zh-cn_5190b59.pf_fragment", "/ran/pagefind/fragment/en_e2846bf.pf_fragment", -"/ran/pagefind/fragment/en_af4a67a.pf_fragment", +"/ran/pagefind/fragment/zh-cn_f2527de.pf_fragment", "/ran/pagefind/fragment/zh-cn_3fb29d3.pf_fragment", "/ran/pagefind/fragment/zh-cn_e6b1657.pf_fragment", -"/ran/pagefind/fragment/zh-cn_88af4d0.pf_fragment", -"/ran/pagefind/fragment/en_324b2ed.pf_fragment", +"/ran/pagefind/fragment/en_c2412d4.pf_fragment", +"/ran/pagefind/fragment/en_5094588.pf_fragment", +"/ran/pagefind/fragment/en_6a9915b.pf_fragment", "/ran/pagefind/fragment/en_919ab78.pf_fragment", "/ran/pagefind/fragment/zh-cn_715eacf.pf_fragment", "/ran/pagefind/fragment/en_b5a7787.pf_fragment", -"/ran/pagefind/fragment/zh-cn_ad77d75.pf_fragment", "/ran/pagefind/fragment/en_27a0b4a.pf_fragment", -"/ran/pagefind/fragment/en_34b032a.pf_fragment", -"/ran/pagefind/fragment/en_78f477e.pf_fragment", +"/ran/pagefind/fragment/zh-cn_c5d845d.pf_fragment", +"/ran/pagefind/fragment/zh-cn_8998463.pf_fragment", +"/ran/pagefind/fragment/en_5861ab4.pf_fragment", +"/ran/pagefind/fragment/en_ee9f170.pf_fragment", "/ran/pagefind/fragment/en_222d98b.pf_fragment", "/ran/pagefind/fragment/zh-cn_51dafe6.pf_fragment", -"/ran/pagefind/fragment/zh-cn_20933c3.pf_fragment", -"/ran/pagefind/fragment/en_7fffa0e.pf_fragment", "/ran/pagefind/fragment/en_55a7cf9.pf_fragment", -"/ran/pagefind/fragment/zh-cn_964a4fd.pf_fragment", "/ran/pagefind/fragment/en_13157a1.pf_fragment", "/ran/pagefind/fragment/zh-cn_32113cf.pf_fragment", +"/ran/pagefind/fragment/zh-cn_51bdb63.pf_fragment", "/ran/pagefind/fragment/zh-cn_5042f4c.pf_fragment", "/ran/pagefind/fragment/zh-cn_677ff04.pf_fragment", "/ran/pagefind/fragment/en_dd6246f.pf_fragment", -"/ran/pagefind/fragment/zh-cn_217f9f1.pf_fragment", "/ran/pagefind/fragment/zh-cn_8a3cd5f.pf_fragment", -"/ran/pagefind/fragment/zh-cn_99e62b8.pf_fragment", -"/ran/pagefind/fragment/zh-cn_ec79f02.pf_fragment", -"/ran/pagefind/fragment/en_6e7d47a.pf_fragment", "/ran/pagefind/fragment/zh-cn_a1e1243.pf_fragment", "/ran/pagefind/fragment/en_346b990.pf_fragment", -"/ran/pagefind/fragment/zh-cn_8dcd37a.pf_fragment", +"/ran/pagefind/fragment/en_63cffab.pf_fragment", "/ran/pagefind/fragment/en_b5741b3.pf_fragment", -"/ran/pagefind/fragment/zh-cn_2bc07af.pf_fragment", -"/ran/pagefind/fragment/en_1f28c1c.pf_fragment", "/ran/pagefind/fragment/en_5ff5133.pf_fragment", "/ran/pagefind/fragment/zh-cn_0ef213d.pf_fragment", "/ran/pagefind/fragment/zh-cn_3ad6344.pf_fragment", @@ -662,6 +661,7 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/ocr/eng.png", "/ran/screenshots_2560x1440.jpg", "/ran/screenshots_748x1340.jpg", +"/ran/sw1728129796.js", "/ran/hashmap.json", "/ran/icon_168.png", "/ran/cn/src/types/高级类型.html", @@ -763,7 +763,7 @@ const SERVICE_WORK_CACHE_FILE_PATHS = [ "/ran/manifest.json", "/ran/icon_192.png", ]; -const VERSION = "1728129561"; +const VERSION = "1728129796"; const CACHE_NAME = 'chaxus_ran_' + VERSION const IGNORE_REQUEST_LIST = [