diff --git a/404.html b/404.html index 6370c3e8..b14af5de 100644 --- a/404.html +++ b/404.html @@ -34,10 +34,10 @@ } - + -
跳至主要內容

404

页面不存在

这 是 四 零 四 !

- +
跳至主要內容

404

页面不存在

这 是 四 零 四 !

+ diff --git a/about.html b/about.html index 80352741..6fb38c59 100644 --- a/about.html +++ b/about.html @@ -5,7 +5,7 @@ - 关于 | levy @@ -34,10 +34,10 @@ } - + -
跳至主要內容
关于

关于


关于

从前端到后端,似乎成了全栈。

其实个人的定位更倾向于 Software Engineer。

在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。

上次编辑于:
贡献者: levy
- +
跳至主要內容
关于

关于


关于

从前端到后端,似乎成了全栈。

其实个人的定位更倾向于 Software Engineer。

在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。

上次编辑于:
贡献者: levy
+ diff --git a/article/index.html b/article/index.html index 7c16a9f5..1c63d515 100644 --- a/article/index.html +++ b/article/index.html @@ -34,10 +34,16 @@ } - + -
跳至主要內容
使用Ragas评估LLM应用

使用Ragas评估LLM应用

+
跳至主要內容
大语言模型赋能备案审查

大语言模型赋能备案审查

+

业务背景

+

备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
+在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
+因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

+

核心流程

+

levy大约 2 分钟llm
使用Ragas评估LLM应用

使用Ragas评估LLM应用

说明

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

@@ -104,11 +110,8 @@

前言

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

-

levy大约 2 分钟Daily
技术点评:别每张表都加tenant_id

技术点评:别每张表都加tenant_id

-

前言

-

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

-

levy大约 3 分钟DesignDaily
2
3
4
5
...
8
- +

levy大约 2 分钟Daily
2
3
4
5
...
8
+ diff --git a/assets/404.html-a05e94f9.js b/assets/404.html-a8fb429e.js similarity index 71% rename from assets/404.html-a05e94f9.js rename to assets/404.html-a8fb429e.js index 80998e87..31bdeb2e 100644 --- a/assets/404.html-a05e94f9.js +++ b/assets/404.html-a8fb429e.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(_,n){return t(),c("div")}const f=e(o,[["render",r],["__file","404.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(_,n){return t(),c("div")}const f=e(o,[["render",r],["__file","404.html.vue"]]);export{f as default}; diff --git a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-3148a9c9.js b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-6b396fa0.js similarity index 98% rename from assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-3148a9c9.js rename to assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-6b396fa0.js index 4261150f..d88283a8 100644 --- a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-3148a9c9.js +++ b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-6b396fa0.js @@ -1,4 +1,4 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as s,c as d,a as e,b as a,d as r,e as i}from"./app-ee4d23bf.js";const c={},m=e("h1",{id:"idea常见问题与解决方案",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#idea常见问题与解决方案","aria-hidden":"true"},"#"),a(" IDEA常见问题与解决方案")],-1),p=e("h2",{id:"启动参数过长",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#启动参数过长","aria-hidden":"true"},"#"),a(" 启动参数过长")],-1),h=e("p",null,[a("Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun."),e("br"),a(" 解决方案:")],-1),g=e("li",null,"编辑 .idea/workspace.xml",-1),u=e("li",null,[a("找到 "),e("code",null,"PropertiesComponent")],-1),b=i(`

或者这样:

"dynamic.classpath": "true",
+import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as s,c as d,a as e,b as a,d as r,f as i}from"./app-dcf5468f.js";const c={},m=e("h1",{id:"idea常见问题与解决方案",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#idea常见问题与解决方案","aria-hidden":"true"},"#"),a(" IDEA常见问题与解决方案")],-1),p=e("h2",{id:"启动参数过长",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#启动参数过长","aria-hidden":"true"},"#"),a(" 启动参数过长")],-1),h=e("p",null,[a("Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun."),e("br"),a(" 解决方案:")],-1),g=e("li",null,"编辑 .idea/workspace.xml",-1),u=e("li",null,[a("找到 "),e("code",null,"PropertiesComponent")],-1),b=i(`

或者这样:

"dynamic.classpath": "true",
 

设置JDK版本

相关报错:

`,4),f={href:"https://blog.csdn.net/qq_32452623/article/details/106141126",target:"_blank",rel:"noopener noreferrer"},_=e("li",null,"Cannot resolve jdk.tools:jdk.tools:1.7",-1),v=i('

解决方案如下。

1.先确保已安装 jdk。

2.修改运行设置


3.修改外部依赖设置

lombok 编译报错

',4),k={href:"https://blog.csdn.net/weixin_42440768/article/details/107999786",target:"_blank",rel:"noopener noreferrer"},x=e("br",null,null,-1),w={href:"https://stackoverflow.com/questions/66801256/java-lang-illegalaccesserror-class-lombok-javac-apt-lombokprocessor-cannot-acce",target:"_blank",rel:"noopener noreferrer"},j=i(`

解决方案:找到相应的 pom.xml,更新依赖版本(如果没有,则添加依赖)

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
diff --git a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-f4cd0189.js b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-c9321106.js
similarity index 73%
rename from assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-f4cd0189.js
rename to assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-c9321106.js
index 21e613c6..23b06cd2 100644
--- a/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-f4cd0189.js
+++ b/assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-c9321106.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-86a15cb6","path":"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html","title":"IDEA常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-11-25T00:00:00.000Z","tag":["Java","Daily"],"description":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"IDEA常见问题与解决方案"}],["meta",{"property":"og:description","content":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-11-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"IDEA常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-11-25T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"启动参数过长","slug":"启动参数过长","link":"#启动参数过长","children":[]},{"level":2,"title":"设置JDK版本","slug":"设置jdk版本","link":"#设置jdk版本","children":[]},{"level":2,"title":"lombok 编译报错","slug":"lombok-编译报错","link":"#lombok-编译报错","children":[]},{"level":2,"title":"设置启动参数","slug":"设置启动参数","link":"#设置启动参数","children":[]},{"level":2,"title":"栈溢出","slug":"栈溢出","link":"#栈溢出","children":[]},{"level":2,"title":"内存不足","slug":"内存不足","link":"#内存不足","children":[]},{"level":2,"title":"热加载","slug":"热加载","link":"#热加载","children":[]},{"level":2,"title":"终端加载环境变量","slug":"终端加载环境变量","link":"#终端加载环境变量","children":[]},{"level":2,"title":"添加外部jar作为依赖","slug":"添加外部jar作为依赖","link":"#添加外部jar作为依赖","children":[]},{"level":2,"title":"文件找不到——依赖冲突","slug":"文件找不到——依赖冲突","link":"#文件找不到——依赖冲突","children":[]},{"level":2,"title":"自动import","slug":"自动import","link":"#自动import","children":[]},{"level":2,"title":"文件乱码","slug":"文件乱码","link":"#文件乱码","children":[]},{"level":2,"title":"autowired 提示变量未赋值","slug":"autowired-提示变量未赋值","link":"#autowired-提示变量未赋值","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.76,"words":827},"filePathRelative":"java/Resolving-Common-Problems-in-IntelliJ-IDEA.md","localizedDate":"2022年11月25日","excerpt":"

IDEA常见问题与解决方案

\\n

启动参数过长

\\n

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
\\n解决方案:

\\n
    \\n
  1. 编辑 .idea/workspace.xml
  2. \\n
  3. 找到 PropertiesComponent
  4. \\n
  5. 添加:
  6. \\n
\\n

或者这样:
\\n\\"\\"

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-86a15cb6","path":"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html","title":"IDEA常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-11-25T00:00:00.000Z","tag":["Java","Daily"],"description":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"IDEA常见问题与解决方案"}],["meta",{"property":"og:description","content":"IDEA常见问题与解决方案 启动参数过长 Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun. 解决方案: 编辑 .idea/workspace.xml 找到 PropertiesComponent 添加: 或者这样:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-11-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"IDEA常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-11-25T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"启动参数过长","slug":"启动参数过长","link":"#启动参数过长","children":[]},{"level":2,"title":"设置JDK版本","slug":"设置jdk版本","link":"#设置jdk版本","children":[]},{"level":2,"title":"lombok 编译报错","slug":"lombok-编译报错","link":"#lombok-编译报错","children":[]},{"level":2,"title":"设置启动参数","slug":"设置启动参数","link":"#设置启动参数","children":[]},{"level":2,"title":"栈溢出","slug":"栈溢出","link":"#栈溢出","children":[]},{"level":2,"title":"内存不足","slug":"内存不足","link":"#内存不足","children":[]},{"level":2,"title":"热加载","slug":"热加载","link":"#热加载","children":[]},{"level":2,"title":"终端加载环境变量","slug":"终端加载环境变量","link":"#终端加载环境变量","children":[]},{"level":2,"title":"添加外部jar作为依赖","slug":"添加外部jar作为依赖","link":"#添加外部jar作为依赖","children":[]},{"level":2,"title":"文件找不到——依赖冲突","slug":"文件找不到——依赖冲突","link":"#文件找不到——依赖冲突","children":[]},{"level":2,"title":"自动import","slug":"自动import","link":"#自动import","children":[]},{"level":2,"title":"文件乱码","slug":"文件乱码","link":"#文件乱码","children":[]},{"level":2,"title":"autowired 提示变量未赋值","slug":"autowired-提示变量未赋值","link":"#autowired-提示变量未赋值","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.76,"words":827},"filePathRelative":"java/Resolving-Common-Problems-in-IntelliJ-IDEA.md","localizedDate":"2022年11月25日","excerpt":"

IDEA常见问题与解决方案

\\n

启动参数过长

\\n

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
\\n解决方案:

\\n
    \\n
  1. 编辑 .idea/workspace.xml
  2. \\n
  3. 找到 PropertiesComponent
  4. \\n
  5. 添加:
  6. \\n
\\n

或者这样:
\\n\\"\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/Resolving-Common-Problems-in-Maven.md.html-018ae313.js b/assets/Resolving-Common-Problems-in-Maven.md.html-3d4fa9ab.js similarity index 69% rename from assets/Resolving-Common-Problems-in-Maven.md.html-018ae313.js rename to assets/Resolving-Common-Problems-in-Maven.md.html-3d4fa9ab.js index d83d6712..c5e186f6 100644 --- a/assets/Resolving-Common-Problems-in-Maven.md.html-018ae313.js +++ b/assets/Resolving-Common-Problems-in-Maven.md.html-3d4fa9ab.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-6dc18ec6","path":"/java/Resolving-Common-Problems-in-Maven.md.html","title":"Maven常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-12-09T00:00:00.000Z","tag":["Java","Daily"],"description":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-Maven.md.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Maven常见问题与解决方案"}],["meta",{"property":"og:description","content":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-12-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Maven常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-12-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"运行 class 找不到主类","slug":"运行-class-找不到主类","link":"#运行-class-找不到主类","children":[]},{"level":2,"title":"运行 jar 找不到主类","slug":"运行-jar-找不到主类","link":"#运行-jar-找不到主类","children":[]},{"level":2,"title":"编译时找不到主类","slug":"编译时找不到主类","link":"#编译时找不到主类","children":[]},{"level":2,"title":"设置Maven目录","slug":"设置maven目录","link":"#设置maven目录","children":[]},{"level":2,"title":"无法识别 Maven 项目","slug":"无法识别-maven-项目","link":"#无法识别-maven-项目","children":[]},{"level":2,"title":"使用了不想要的镜像源","slug":"使用了不想要的镜像源","link":"#使用了不想要的镜像源","children":[]},{"level":2,"title":"下载 jar 失败","slug":"下载-jar-失败","link":"#下载-jar-失败","children":[]},{"level":2,"title":"私服认证401","slug":"私服认证401","link":"#私服认证401","children":[]},{"level":2,"title":"避免缓存","slug":"避免缓存","link":"#避免缓存","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.25,"words":976},"filePathRelative":"java/Resolving-Common-Problems-in-Maven.md.md","localizedDate":"2022年12月9日","excerpt":"

Maven常见问题与解决方案

\\n

运行 class 找不到主类

\\n
maven compile\\n

得到 class 文件后

\\n
cd /my-app/target/com/mycompany/app\\njava App\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-6dc18ec6","path":"/java/Resolving-Common-Problems-in-Maven.md.html","title":"Maven常见问题与解决方案","lang":"zh-CN","frontmatter":{"date":"2022-12-09T00:00:00.000Z","tag":["Java","Daily"],"description":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/Resolving-Common-Problems-in-Maven.md.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Maven常见问题与解决方案"}],["meta",{"property":"og:description","content":"Maven常见问题与解决方案 运行 class 找不到主类 maven compile 得到 class 文件后 cd /my-app/target/com/mycompany/app java App"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-12-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Maven常见问题与解决方案\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-12-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"运行 class 找不到主类","slug":"运行-class-找不到主类","link":"#运行-class-找不到主类","children":[]},{"level":2,"title":"运行 jar 找不到主类","slug":"运行-jar-找不到主类","link":"#运行-jar-找不到主类","children":[]},{"level":2,"title":"编译时找不到主类","slug":"编译时找不到主类","link":"#编译时找不到主类","children":[]},{"level":2,"title":"设置Maven目录","slug":"设置maven目录","link":"#设置maven目录","children":[]},{"level":2,"title":"无法识别 Maven 项目","slug":"无法识别-maven-项目","link":"#无法识别-maven-项目","children":[]},{"level":2,"title":"使用了不想要的镜像源","slug":"使用了不想要的镜像源","link":"#使用了不想要的镜像源","children":[]},{"level":2,"title":"下载 jar 失败","slug":"下载-jar-失败","link":"#下载-jar-失败","children":[]},{"level":2,"title":"私服认证401","slug":"私服认证401","link":"#私服认证401","children":[]},{"level":2,"title":"避免缓存","slug":"避免缓存","link":"#避免缓存","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.25,"words":976},"filePathRelative":"java/Resolving-Common-Problems-in-Maven.md.md","localizedDate":"2022年12月9日","excerpt":"

Maven常见问题与解决方案

\\n

运行 class 找不到主类

\\n
maven compile\\n

得到 class 文件后

\\n
cd /my-app/target/com/mycompany/app\\njava App\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/Resolving-Common-Problems-in-Maven.md.html-1c6cb245.js b/assets/Resolving-Common-Problems-in-Maven.md.html-d159f2ba.js similarity index 99% rename from assets/Resolving-Common-Problems-in-Maven.md.html-1c6cb245.js rename to assets/Resolving-Common-Problems-in-Maven.md.html-d159f2ba.js index 7004e3f6..e1db998e 100644 --- a/assets/Resolving-Common-Problems-in-Maven.md.html-1c6cb245.js +++ b/assets/Resolving-Common-Problems-in-Maven.md.html-d159f2ba.js @@ -1,4 +1,4 @@ -import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c as i,a as n,b as a,d as e,e as t}from"./app-ee4d23bf.js";const c={},r=t(`

Maven常见问题与解决方案

运行 class 找不到主类

maven compile
+import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c as i,a as n,b as a,d as e,f as t}from"./app-dcf5468f.js";const c={},r=t(`

Maven常见问题与解决方案

运行 class 找不到主类

maven compile
 

得到 class 文件后

cd /my-app/target/com/mycompany/app
 java App
 

报错:

错误: 找不到或无法加载主类 App
原因: java.lang.NoClassDefFoundError: com/mycompany/app/App (wrong name: App)

这是因为主类并非在默认包下,故需要在正确的路径下调用全限定名。

cd /my-app/target
diff --git a/assets/a-vuepress2-entertaining-video.html-13144ff8.js b/assets/a-vuepress2-entertaining-video.html-0e2f1f03.js
similarity index 64%
rename from assets/a-vuepress2-entertaining-video.html-13144ff8.js
rename to assets/a-vuepress2-entertaining-video.html-0e2f1f03.js
index c45e862b..903f7ff5 100644
--- a/assets/a-vuepress2-entertaining-video.html-13144ff8.js
+++ b/assets/a-vuepress2-entertaining-video.html-0e2f1f03.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-30a50b00","path":"/daily/a-vuepress2-entertaining-video.html","title":"VuePress2 娱乐视频","lang":"zh-CN","frontmatter":{"date":"2023-08-01T00:00:00.000Z","tag":["Daily","Frontend","Video"],"description":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-vuepress2-entertaining-video.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"VuePress2 娱乐视频"}],["meta",{"property":"og:description","content":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"VuePress2 娱乐视频\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-01T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.11,"words":34},"filePathRelative":"daily/a-vuepress2-entertaining-video.md","localizedDate":"2023年8月1日","excerpt":"

VuePress2 娱乐视频

\\n

参考《原神,启动》的梗,做的一个娱乐向视频。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-30a50b00","path":"/daily/a-vuepress2-entertaining-video.html","title":"VuePress2 娱乐视频","lang":"zh-CN","frontmatter":{"date":"2023-08-01T00:00:00.000Z","tag":["Daily","Frontend","Video"],"description":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-vuepress2-entertaining-video.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"VuePress2 娱乐视频"}],["meta",{"property":"og:description","content":"VuePress2 娱乐视频 参考《原神,启动》的梗,做的一个娱乐向视频。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"VuePress2 娱乐视频\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-01T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.11,"words":34},"filePathRelative":"daily/a-vuepress2-entertaining-video.md","localizedDate":"2023年8月1日","excerpt":"

VuePress2 娱乐视频

\\n

参考《原神,启动》的梗,做的一个娱乐向视频。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/a-vuepress2-entertaining-video.html-13910c5b.js b/assets/a-vuepress2-entertaining-video.html-9eef7c60.js similarity index 89% rename from assets/a-vuepress2-entertaining-video.html-13910c5b.js rename to assets/a-vuepress2-entertaining-video.html-9eef7c60.js index acf8107a..1830aa6d 100644 --- a/assets/a-vuepress2-entertaining-video.html-13910c5b.js +++ b/assets/a-vuepress2-entertaining-video.html-9eef7c60.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o,c as a,d as n,a as e,b as i}from"./app-ee4d23bf.js";const c={},d=e("h1",{id:"vuepress2-娱乐视频",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress2-娱乐视频","aria-hidden":"true"},"#"),i(" VuePress2 娱乐视频")],-1),_=e("p",null,"参考《原神,启动》的梗,做的一个娱乐向视频。",-1);function l(p,u){const s=t("BiliBili");return o(),a("div",null,[d,_,n(s,{bvid:"BV1r14y167c2"})])}const f=r(c,[["render",l],["__file","a-vuepress2-entertaining-video.html.vue"]]);export{f as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o,c as a,d as n,a as e,b as i}from"./app-dcf5468f.js";const c={},d=e("h1",{id:"vuepress2-娱乐视频",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vuepress2-娱乐视频","aria-hidden":"true"},"#"),i(" VuePress2 娱乐视频")],-1),_=e("p",null,"参考《原神,启动》的梗,做的一个娱乐向视频。",-1);function l(p,u){const s=t("BiliBili");return o(),a("div",null,[d,_,n(s,{bvid:"BV1r14y167c2"})])}const f=r(c,[["render",l],["__file","a-vuepress2-entertaining-video.html.vue"]]);export{f as default}; diff --git a/assets/a-warning-from-navicat.html-0caac0ee.js b/assets/a-warning-from-navicat.html-0b2038a8.js similarity index 91% rename from assets/a-warning-from-navicat.html-0caac0ee.js rename to assets/a-warning-from-navicat.html-0b2038a8.js index 685e0117..a6965ab3 100644 --- a/assets/a-warning-from-navicat.html-0caac0ee.js +++ b/assets/a-warning-from-navicat.html-0b2038a8.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as i,d as c,a as e,b as r}from"./app-ee4d23bf.js";const s={},_=e("h1",{id:"来自navicat的侵权警告",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#来自navicat的侵权警告","aria-hidden":"true"},"#"),r(" 来自Navicat的侵权警告")],-1),l=e("p",null,"公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。",-1),d=e("p",null,"另外,开发者常用的软件的合法替代品,视频中也有推荐。",-1);function m(h,f){const a=n("BiliBili");return o(),i("div",null,[_,l,d,c(a,{bvid:"BV1L94y1e7sx"})])}const u=t(s,[["render",m],["__file","a-warning-from-navicat.html.vue"]]);export{u as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as i,d as c,a as e,b as r}from"./app-dcf5468f.js";const s={},_=e("h1",{id:"来自navicat的侵权警告",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#来自navicat的侵权警告","aria-hidden":"true"},"#"),r(" 来自Navicat的侵权警告")],-1),l=e("p",null,"公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。",-1),d=e("p",null,"另外,开发者常用的软件的合法替代品,视频中也有推荐。",-1);function m(h,f){const a=n("BiliBili");return o(),i("div",null,[_,l,d,c(a,{bvid:"BV1L94y1e7sx"})])}const u=t(s,[["render",m],["__file","a-warning-from-navicat.html.vue"]]);export{u as default}; diff --git a/assets/a-warning-from-navicat.html-32552aa3.js b/assets/a-warning-from-navicat.html-a042c9b4.js similarity index 65% rename from assets/a-warning-from-navicat.html-32552aa3.js rename to assets/a-warning-from-navicat.html-a042c9b4.js index f082ed61..d527b77a 100644 --- a/assets/a-warning-from-navicat.html-32552aa3.js +++ b/assets/a-warning-from-navicat.html-a042c9b4.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-0481cc80","path":"/daily/a-warning-from-navicat.html","title":"来自Navicat的侵权警告","lang":"zh-CN","frontmatter":{"date":"2023-07-31T00:00:00.000Z","tag":["Daily","Video"],"description":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-warning-from-navicat.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"来自Navicat的侵权警告"}],["meta",{"property":"og:description","content":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-31T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"来自Navicat的侵权警告\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-31T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.27,"words":81},"filePathRelative":"daily/a-warning-from-navicat.md","localizedDate":"2023年7月31日","excerpt":"

来自Navicat的侵权警告

\\n

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

\\n

另外,开发者常用的软件的合法替代品,视频中也有推荐。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-0481cc80","path":"/daily/a-warning-from-navicat.html","title":"来自Navicat的侵权警告","lang":"zh-CN","frontmatter":{"date":"2023-07-31T00:00:00.000Z","tag":["Daily","Video"],"description":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/a-warning-from-navicat.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"来自Navicat的侵权警告"}],["meta",{"property":"og:description","content":"来自Navicat的侵权警告 公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。 另外,开发者常用的软件的合法替代品,视频中也有推荐。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-31T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"来自Navicat的侵权警告\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-31T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.27,"words":81},"filePathRelative":"daily/a-warning-from-navicat.md","localizedDate":"2023年7月31日","excerpt":"

来自Navicat的侵权警告

\\n

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

\\n

另外,开发者常用的软件的合法替代品,视频中也有推荐。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/about-arm-things-you-need-to-know.html-cff9d934.js b/assets/about-arm-things-you-need-to-know.html-8264be5b.js similarity index 87% rename from assets/about-arm-things-you-need-to-know.html-cff9d934.js rename to assets/about-arm-things-you-need-to-know.html-8264be5b.js index ee32946f..5686073a 100644 --- a/assets/about-arm-things-you-need-to-know.html-cff9d934.js +++ b/assets/about-arm-things-you-need-to-know.html-8264be5b.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as i,c as a,d as t,e as c}from"./app-ee4d23bf.js";const m={},l=c('

关于 Arm 你需要了解的三件事

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

跟我们有什么关系呢?

  1. MacOS 的 M1 芯片是基于 Arm 的
  2. 云厂商及生态都在积极与 Arm 进行合作
  3. Docker 镜像的构建有注意事项

构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。

解决办法就是,想办法把相关命令前置,提前执行,再构建镜像。

如注释掉 Dockerfile 里的 Run chmod 777,改成在构建镜像前执行。

视频里有更详细的讲解:

',8);function n(d,s){const e=o("BiliBili");return i(),a("div",null,[l,t(e,{bvid:"BV1Eu4y1m75X"})])}const _=r(m,[["render",n],["__file","about-arm-things-you-need-to-know.html.vue"]]);export{_ as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as i,c as a,d as t,f as c}from"./app-dcf5468f.js";const m={},l=c('

关于 Arm 你需要了解的三件事

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

跟我们有什么关系呢?

  1. MacOS 的 M1 芯片是基于 Arm 的
  2. 云厂商及生态都在积极与 Arm 进行合作
  3. Docker 镜像的构建有注意事项

构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。

解决办法就是,想办法把相关命令前置,提前执行,再构建镜像。

如注释掉 Dockerfile 里的 Run chmod 777,改成在构建镜像前执行。

视频里有更详细的讲解:

',8);function n(d,s){const e=o("BiliBili");return i(),a("div",null,[l,t(e,{bvid:"BV1Eu4y1m75X"})])}const f=r(m,[["render",n],["__file","about-arm-things-you-need-to-know.html.vue"]]);export{f as default}; diff --git a/assets/about-arm-things-you-need-to-know.html-a6dfc5b5.js b/assets/about-arm-things-you-need-to-know.html-978abb78.js similarity index 61% rename from assets/about-arm-things-you-need-to-know.html-a6dfc5b5.js rename to assets/about-arm-things-you-need-to-know.html-978abb78.js index 99c41468..1b6cdee3 100644 --- a/assets/about-arm-things-you-need-to-know.html-a6dfc5b5.js +++ b/assets/about-arm-things-you-need-to-know.html-978abb78.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-3f274907","path":"/devops/about-arm-things-you-need-to-know.html","title":"关于 Arm 你需要了解的三件事","lang":"zh-CN","frontmatter":{"date":"2023-08-02T00:00:00.000Z","tag":["Daily","DevOps","Linux"],"description":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项 构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error。 这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/about-arm-things-you-need-to-know.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于 Arm 你需要了解的三件事"}],["meta",{"property":"og:description","content":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项 构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error。 这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:published_time","content":"2023-08-02T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"关于 Arm 你需要了解的三件事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-02T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.72,"words":217},"filePathRelative":"devops/about-arm-things-you-need-to-know.md","localizedDate":"2023年8月2日","excerpt":"

关于 Arm 你需要了解的三件事

\\n

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

\\n

跟我们有什么关系呢?

\\n
    \\n
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. \\n
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. \\n
  5. Docker 镜像的构建有注意事项
  6. \\n
\\n

构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
\\n这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。
\\n\\"\\"

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-3f274907","path":"/devops/about-arm-things-you-need-to-know.html","title":"关于 Arm 你需要了解的三件事","lang":"zh-CN","frontmatter":{"date":"2023-08-02T00:00:00.000Z","tag":["Daily","DevOps","Linux"],"description":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项 构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error。 这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/about-arm-things-you-need-to-know.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于 Arm 你需要了解的三件事"}],["meta",{"property":"og:description","content":"关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项 构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error。 这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:published_time","content":"2023-08-02T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"关于 Arm 你需要了解的三件事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-02T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.72,"words":217},"filePathRelative":"devops/about-arm-things-you-need-to-know.md","localizedDate":"2023年8月2日","excerpt":"

关于 Arm 你需要了解的三件事

\\n

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

\\n

跟我们有什么关系呢?

\\n
    \\n
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. \\n
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. \\n
  5. Docker 镜像的构建有注意事项
  6. \\n
\\n

构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
\\n这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。
\\n\\"\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/about.html-c47dcdc2.js b/assets/about.html-162be796.js similarity index 69% rename from assets/about.html-c47dcdc2.js rename to assets/about.html-162be796.js index 7fc0bcc2..baf0c45f 100644 --- a/assets/about.html-c47dcdc2.js +++ b/assets/about.html-162be796.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-22a39d25","path":"/about.html","title":"关于","lang":"zh-CN","frontmatter":{"icon":"circle-info","cover":"/assets/images/cover3.jpg","article":false,"description":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。","head":[["meta",{"property":"og:url","content":"https://levy.vip/about.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于"}],["meta",{"property":"og:description","content":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:image","content":"https://levy.vip/assets/images/cover3.jpg"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:alt","content":"关于"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"关于\\",\\"description\\":\\"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。\\"}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"about.md","localizedDate":"2024年4月29日","excerpt":"

关于

\\n

从前端到后端,似乎成了全栈。

\\n

其实个人的定位更倾向于 Software Engineer。

\\n

在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-22a39d25","path":"/about.html","title":"关于","lang":"zh-CN","frontmatter":{"icon":"circle-info","cover":"/assets/images/cover3.jpg","article":false,"description":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。","head":[["meta",{"property":"og:url","content":"https://levy.vip/about.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"关于"}],["meta",{"property":"og:description","content":"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:image","content":"https://levy.vip/assets/images/cover3.jpg"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:alt","content":"关于"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"关于\\",\\"description\\":\\"关于 从前端到后端,似乎成了全栈。 其实个人的定位更倾向于 Software Engineer。 在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。\\"}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"about.md","localizedDate":"2024年5月28日","excerpt":"

关于

\\n

从前端到后端,似乎成了全栈。

\\n

其实个人的定位更倾向于 Software Engineer。

\\n

在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/about.html-d0965c3f.js b/assets/about.html-9f67323b.js similarity index 90% rename from assets/about.html-d0965c3f.js rename to assets/about.html-9f67323b.js index 7875155e..1497397f 100644 --- a/assets/about.html-d0965c3f.js +++ b/assets/about.html-9f67323b.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o,c as a,a as e,b as n}from"./app-ee4d23bf.js";const s={},c=e("h1",{id:"关于",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于","aria-hidden":"true"},"#"),n(" 关于")],-1),r=e("p",null,"从前端到后端,似乎成了全栈。",-1),_=e("p",null,"其实个人的定位更倾向于 Software Engineer。",-1),d=e("p",null,"在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。",-1),i=[c,r,_,d];function l(h,u){return o(),a("div",null,i)}const p=t(s,[["render",l],["__file","about.html.vue"]]);export{p as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o,c as a,a as e,b as n}from"./app-dcf5468f.js";const s={},c=e("h1",{id:"关于",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于","aria-hidden":"true"},"#"),n(" 关于")],-1),r=e("p",null,"从前端到后端,似乎成了全栈。",-1),_=e("p",null,"其实个人的定位更倾向于 Software Engineer。",-1),d=e("p",null,"在新技术层出不穷、旧技术不断被淘汰的时代,我期望记录一些能经得起时间考验的东西。",-1),i=[c,r,_,d];function l(h,u){return o(),a("div",null,i)}const p=t(s,[["render",l],["__file","about.html.vue"]]);export{p as default}; diff --git a/assets/add-logging-for-llm-app.html-b2dd1309.js b/assets/add-logging-for-llm-app.html-6b7e5c8b.js similarity index 70% rename from assets/add-logging-for-llm-app.html-b2dd1309.js rename to assets/add-logging-for-llm-app.html-6b7e5c8b.js index 621cd423..2520c268 100644 --- a/assets/add-logging-for-llm-app.html-b2dd1309.js +++ b/assets/add-logging-for-llm-app.html-6b7e5c8b.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-de221860","path":"/python/add-logging-for-llm-app.html","title":"给LLM应用添加日志","lang":"zh-CN","frontmatter":{"date":"2023-12-04T00:00:00.000Z","tag":["Python"],"description":"给LLM应用添加日志 logging替代print 目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。 print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/add-logging-for-llm-app.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"给LLM应用添加日志"}],["meta",{"property":"og:description","content":"给LLM应用添加日志 logging替代print 目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。 print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-12-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"给LLM应用添加日志\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"logging替代print","slug":"logging替代print","link":"#logging替代print","children":[]},{"level":2,"title":"何时打印日志","slug":"何时打印日志","link":"#何时打印日志","children":[{"level":3,"title":"外部调用","slug":"外部调用","link":"#外部调用","children":[]},{"level":3,"title":"异常捕获","slug":"异常捕获","link":"#异常捕获","children":[]},{"level":3,"title":"提前返回","slug":"提前返回","link":"#提前返回","children":[]},{"level":3,"title":"复杂或特殊的if-else","slug":"复杂或特殊的if-else","link":"#复杂或特殊的if-else","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.3,"words":989},"filePathRelative":"python/add-logging-for-llm-app.md","localizedDate":"2023年12月4日","excerpt":"

给LLM应用添加日志

\\n

logging替代print

\\n

目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。

\\n

print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。

\\n","autoDesc":true}');export{t as data}; +const e=JSON.parse('{"key":"v-de221860","path":"/python/add-logging-for-llm-app.html","title":"给LLM应用添加日志","lang":"zh-CN","frontmatter":{"date":"2023-12-04T00:00:00.000Z","tag":["Python"],"description":"给LLM应用添加日志 logging替代print 目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。 print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/add-logging-for-llm-app.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"给LLM应用添加日志"}],["meta",{"property":"og:description","content":"给LLM应用添加日志 logging替代print 目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。 print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-12-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"给LLM应用添加日志\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"logging替代print","slug":"logging替代print","link":"#logging替代print","children":[]},{"level":2,"title":"何时打印日志","slug":"何时打印日志","link":"#何时打印日志","children":[{"level":3,"title":"外部调用","slug":"外部调用","link":"#外部调用","children":[]},{"level":3,"title":"异常捕获","slug":"异常捕获","link":"#异常捕获","children":[]},{"level":3,"title":"提前返回","slug":"提前返回","link":"#提前返回","children":[]},{"level":3,"title":"复杂或特殊的if-else","slug":"复杂或特殊的if-else","link":"#复杂或特殊的if-else","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.3,"words":989},"filePathRelative":"python/add-logging-for-llm-app.md","localizedDate":"2023年12月4日","excerpt":"

给LLM应用添加日志

\\n

logging替代print

\\n

目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。

\\n

print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/add-logging-for-llm-app.html-afc22b3f.js b/assets/add-logging-for-llm-app.html-7e982a8e.js similarity index 99% rename from assets/add-logging-for-llm-app.html-afc22b3f.js rename to assets/add-logging-for-llm-app.html-7e982a8e.js index 85d9fee3..e249e233 100644 --- a/assets/add-logging-for-llm-app.html-afc22b3f.js +++ b/assets/add-logging-for-llm-app.html-7e982a8e.js @@ -1,4 +1,4 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,f as u,a as n,b as s,d as t,e as p}from"./app-ee4d23bf.js";const l={},k=n("h1",{id:"给llm应用添加日志",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#给llm应用添加日志","aria-hidden":"true"},"#"),s(" 给LLM应用添加日志")],-1),d=n("h2",{id:"logging替代print",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#logging替代print","aria-hidden":"true"},"#"),s(" logging替代print")],-1),r=n("p",null,"目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。",-1),v=n("p",null,"print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。",-1),m={href:"https://stackoverflow.com/questions/29663459/why-doesnt-python-app-print-anything-when-run-in-a-detached-docker-container",target:"_blank",rel:"noopener noreferrer"},g={href:"https://stackoverflow.com/questions/24617397/how-do-i-print-to-console-in-pytest",target:"_blank",rel:"noopener noreferrer"},b=p(`

使用 Python 内置的日志模块 logging,直接 import 即可使用:

import logging
+import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,e as u,a as n,b as s,d as t,f as p}from"./app-dcf5468f.js";const l={},k=n("h1",{id:"给llm应用添加日志",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#给llm应用添加日志","aria-hidden":"true"},"#"),s(" 给LLM应用添加日志")],-1),d=n("h2",{id:"logging替代print",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#logging替代print","aria-hidden":"true"},"#"),s(" logging替代print")],-1),r=n("p",null,"目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。",-1),v=n("p",null,"print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。",-1),m={href:"https://stackoverflow.com/questions/29663459/why-doesnt-python-app-print-anything-when-run-in-a-detached-docker-container",target:"_blank",rel:"noopener noreferrer"},g={href:"https://stackoverflow.com/questions/24617397/how-do-i-print-to-console-in-pytest",target:"_blank",rel:"noopener noreferrer"},b=p(`

使用 Python 内置的日志模块 logging,直接 import 即可使用:

import logging
 # 在入口函数添加以下一行
 logging.basicConfig(level=logging.INFO)
 
diff --git a/assets/app-dcf5468f.js b/assets/app-dcf5468f.js
new file mode 100644
index 00000000..79155e01
--- /dev/null
+++ b/assets/app-dcf5468f.js
@@ -0,0 +1,484 @@
+var Cu=Object.defineProperty;var Su=(e,t,l)=>t in e?Cu(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l;var oa=(e,t,l)=>(Su(e,typeof t!="symbol"?t+"":t,l),l);const Du="modulepreload",Mu=function(e){return"/"+e},Xi={},g=function(t,l,n){if(!l||l.length===0)return t();const a=document.getElementsByTagName("link");return Promise.all(l.map(i=>{if(i=Mu(i),i in Xi)return;Xi[i]=!0;const r=i.endsWith(".css"),o=r?'[rel="stylesheet"]':"";if(!!n)for(let d=a.length-1;d>=0;d--){const p=a[d];if(p.href===i&&(!r||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${o}`))return;const u=document.createElement("link");if(u.rel=r?"stylesheet":Du,r||(u.as="script",u.crossOrigin=""),u.href=i,document.head.appendChild(u),r)return new Promise((d,p)=>{u.addEventListener("load",d),u.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t()).catch(i=>{const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=i,window.dispatchEvent(r),!r.defaultPrevented)throw i})};function ei(e,t){const l=Object.create(null),n=e.split(",");for(let a=0;a!!l[a.toLowerCase()]:a=>!!l[a]}const Le={},cl=[],st=()=>{},$u=()=>!1,Vu=/^on[^a-z]/,Zl=e=>Vu.test(e),ti=e=>e.startsWith("onUpdate:"),Oe=Object.assign,li=(e,t)=>{const l=e.indexOf(t);l>-1&&e.splice(l,1)},Fu=Object.prototype.hasOwnProperty,oe=(e,t)=>Fu.call(e,t),X=Array.isArray,Dl=e=>Gn(e)==="[object Map]",Nu=e=>Gn(e)==="[object Set]",te=e=>typeof e=="function",ae=e=>typeof e=="string",ni=e=>typeof e=="symbol",Te=e=>e!==null&&typeof e=="object",As=e=>Te(e)&&te(e.then)&&te(e.catch),ju=Object.prototype.toString,Gn=e=>ju.call(e),Bu=e=>Gn(e).slice(8,-1),Hu=e=>Gn(e)==="[object Object]",ai=e=>ae(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Ml=ei(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Un=e=>{const t=Object.create(null);return l=>t[l]||(t[l]=e(l))},zu=/-(\w)/g,tt=Un(e=>e.replace(zu,(t,l)=>l?l.toUpperCase():"")),qu=/\B([A-Z])/g,wl=Un(e=>e.replace(qu,"-$1").toLowerCase()),en=Un(e=>e.charAt(0).toUpperCase()+e.slice(1)),ca=Un(e=>e?`on${en(e)}`:""),Hl=(e,t)=>!Object.is(e,t),ua=(e,t)=>{for(let l=0;l{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:l})},Gu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Uu=e=>{const t=ae(e)?Number(e):NaN;return isNaN(t)?e:t};let Zi;const Ia=()=>Zi||(Zi=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ii(e){if(X(e)){const t={};for(let l=0;l{if(l){const n=l.split(Ku);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function ri(e){let t="";if(ae(e))t=e;else if(X(e))for(let l=0;l{const t=new Set(e);return t.w=0,t.n=0,t},Is=e=>(e.w&Nt)>0,Rs=e=>(e.n&Nt)>0,ld=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let l=0;for(let n=0;n{(d==="length"||d>=c)&&o.push(u)})}else switch(l!==void 0&&o.push(r.get(l)),t){case"add":X(e)?ai(l)&&o.push(r.get("length")):(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ca)));break;case"delete":X(e)||(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ca)));break;case"set":Dl(e)&&o.push(r.get(Jt));break}if(o.length===1)o[0]&&Sa(o[0]);else{const c=[];for(const u of o)u&&c.push(...u);Sa(si(c))}}function Sa(e,t){const l=X(e)?e:[...e];for(const n of l)n.computed&&tr(n);for(const n of l)n.computed||tr(n)}function tr(e,t){(e!==at||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function ad(e,t){var l;return(l=Dn.get(e))==null?void 0:l.get(t)}const id=ei("__proto__,__v_isRef,__isVue"),Ds=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ni)),rd=ci(),sd=ci(!1,!0),od=ci(!0),lr=cd();function cd(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...l){const n=re(this);for(let i=0,r=this.length;i{e[t]=function(...l){El();const n=re(this)[t].apply(this,l);return Ll(),n}}),e}function ud(e){const t=re(this);return qe(t,"has",e),t.hasOwnProperty(e)}function ci(e=!1,t=!1){return function(n,a,i){if(a==="__v_isReactive")return!e;if(a==="__v_isReadonly")return e;if(a==="__v_isShallow")return t;if(a==="__v_raw"&&i===(e?t?Td:Ns:t?Fs:Vs).get(n))return n;const r=X(n);if(!e){if(r&&oe(lr,a))return Reflect.get(lr,a,i);if(a==="hasOwnProperty")return ud}const o=Reflect.get(n,a,i);return(ni(a)?Ds.has(a):id(a))||(e||qe(n,"get",a),t)?o:Ie(o)?r&&ai(a)?o:o.value:Te(o)?e?Yt(o):tn(o):o}}const dd=Ms(),pd=Ms(!0);function Ms(e=!1){return function(l,n,a,i){let r=l[n];if(vl(r)&&Ie(r)&&!Ie(a))return!1;if(!e&&(!Mn(a)&&!vl(a)&&(r=re(r),a=re(a)),!X(l)&&Ie(r)&&!Ie(a)))return r.value=a,!0;const o=X(l)&&ai(n)?Number(n)e,Wn=e=>Reflect.getPrototypeOf(e);function yn(e,t,l=!1,n=!1){e=e.__v_raw;const a=re(e),i=re(t);l||(t!==i&&qe(a,"get",t),qe(a,"get",i));const{has:r}=Wn(a),o=n?ui:l?hi:zl;if(r.call(a,t))return o(e.get(t));if(r.call(a,i))return o(e.get(i));e!==a&&e.get(t)}function bn(e,t=!1){const l=this.__v_raw,n=re(l),a=re(e);return t||(e!==a&&qe(n,"has",e),qe(n,"has",a)),e===a?l.has(e):l.has(e)||l.has(a)}function _n(e,t=!1){return e=e.__v_raw,!t&&qe(re(e),"iterate",Jt),Reflect.get(e,"size",e)}function nr(e){e=re(e);const t=re(this);return Wn(t).has.call(t,e)||(t.add(e),xt(t,"add",e,e)),this}function ar(e,t){t=re(t);const l=re(this),{has:n,get:a}=Wn(l);let i=n.call(l,e);i||(e=re(e),i=n.call(l,e));const r=a.call(l,e);return l.set(e,t),i?Hl(t,r)&&xt(l,"set",e,t):xt(l,"add",e,t),this}function ir(e){const t=re(this),{has:l,get:n}=Wn(t);let a=l.call(t,e);a||(e=re(e),a=l.call(t,e)),n&&n.call(t,e);const i=t.delete(e);return a&&xt(t,"delete",e,void 0),i}function rr(){const e=re(this),t=e.size!==0,l=e.clear();return t&&xt(e,"clear",void 0,void 0),l}function kn(e,t){return function(n,a){const i=this,r=i.__v_raw,o=re(r),c=t?ui:e?hi:zl;return!e&&qe(o,"iterate",Jt),r.forEach((u,d)=>n.call(a,c(u),c(d),i))}}function wn(e,t,l){return function(...n){const a=this.__v_raw,i=re(a),r=Dl(i),o=e==="entries"||e===Symbol.iterator&&r,c=e==="keys"&&r,u=a[e](...n),d=l?ui:t?hi:zl;return!t&&qe(i,"iterate",c?Ca:Jt),{next(){const{value:p,done:h}=u.next();return h?{value:p,done:h}:{value:o?[d(p[0]),d(p[1])]:d(p),done:h}},[Symbol.iterator](){return this}}}}function Pt(e){return function(...t){return e==="delete"?!1:this}}function yd(){const e={get(i){return yn(this,i)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!1)},t={get(i){return yn(this,i,!1,!0)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!0)},l={get(i){return yn(this,i,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!1)},n={get(i){return yn(this,i,!0,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=wn(i,!1,!1),l[i]=wn(i,!0,!1),t[i]=wn(i,!1,!0),n[i]=wn(i,!0,!0)}),[e,l,t,n]}const[bd,_d,kd,wd]=yd();function di(e,t){const l=t?e?wd:kd:e?_d:bd;return(n,a,i)=>a==="__v_isReactive"?!e:a==="__v_isReadonly"?e:a==="__v_raw"?n:Reflect.get(oe(l,a)&&a in n?l:n,a,i)}const Ed={get:di(!1,!1)},Ld={get:di(!1,!0)},xd={get:di(!0,!1)},Vs=new WeakMap,Fs=new WeakMap,Ns=new WeakMap,Td=new WeakMap;function Ad(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Od(e){return e.__v_skip||!Object.isExtensible(e)?0:Ad(Bu(e))}function tn(e){return vl(e)?e:pi(e,!1,$s,Ed,Vs)}function js(e){return pi(e,!1,md,Ld,Fs)}function Yt(e){return pi(e,!0,gd,xd,Ns)}function pi(e,t,l,n,a){if(!Te(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=a.get(e);if(i)return i;const r=Od(e);if(r===0)return e;const o=new Proxy(e,r===2?n:l);return a.set(e,o),o}function ul(e){return vl(e)?ul(e.__v_raw):!!(e&&e.__v_isReactive)}function vl(e){return!!(e&&e.__v_isReadonly)}function Mn(e){return!!(e&&e.__v_isShallow)}function Bs(e){return ul(e)||vl(e)}function re(e){const t=e&&e.__v_raw;return t?re(t):e}function Hs(e){return Sn(e,"__v_skip",!0),e}const zl=e=>Te(e)?tn(e):e,hi=e=>Te(e)?Yt(e):e;function vi(e){$t&&at&&(e=re(e),Ss(e.dep||(e.dep=si())))}function fi(e,t){e=re(e);const l=e.dep;l&&Sa(l)}function Ie(e){return!!(e&&e.__v_isRef===!0)}function W(e){return zs(e,!1)}function Ge(e){return zs(e,!0)}function zs(e,t){return Ie(e)?e:new Pd(e,t)}class Pd{constructor(t,l){this.__v_isShallow=l,this.dep=void 0,this.__v_isRef=!0,this._rawValue=l?t:re(t),this._value=l?t:zl(t)}get value(){return vi(this),this._value}set value(t){const l=this.__v_isShallow||Mn(t)||vl(t);t=l?t:re(t),Hl(t,this._rawValue)&&(this._rawValue=t,this._value=l?t:zl(t),fi(this))}}function it(e){return Ie(e)?e.value:e}const Id={get:(e,t,l)=>it(Reflect.get(e,t,l)),set:(e,t,l,n)=>{const a=e[t];return Ie(a)&&!Ie(l)?(a.value=l,!0):Reflect.set(e,t,l,n)}};function qs(e){return ul(e)?e:new Proxy(e,Id)}class Rd{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:l,set:n}=t(()=>vi(this),()=>fi(this));this._get=l,this._set=n}get value(){return this._get()}set value(t){this._set(t)}}function Cd(e){return new Rd(e)}function Sd(e){const t=X(e)?new Array(e.length):{};for(const l in e)t[l]=Gs(e,l);return t}class Dd{constructor(t,l,n){this._object=t,this._key=l,this._defaultValue=n,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return ad(re(this._object),this._key)}}class Md{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function xl(e,t,l){return Ie(e)?e:te(e)?new Md(e):Te(e)&&arguments.length>1?Gs(e,t,l):W(e)}function Gs(e,t,l){const n=e[t];return Ie(n)?n:new Dd(e,t,l)}class $d{constructor(t,l,n,a){this._setter=l,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new oi(t,()=>{this._dirty||(this._dirty=!0,fi(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!a,this.__v_isReadonly=n}get value(){const t=re(this);return vi(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function Vd(e,t,l=!1){let n,a;const i=te(e);return i?(n=e,a=st):(n=e.get,a=e.set),new $d(n,a,i||!a,l)}function Vt(e,t,l,n){let a;try{a=n?e(...n):e()}catch(i){ln(i,t,l)}return a}function Ze(e,t,l,n){if(te(e)){const i=Vt(e,t,l,n);return i&&As(i)&&i.catch(r=>{ln(r,t,l)}),i}const a=[];for(let i=0;i>>1;Gl(Me[n])ft&&Me.splice(t,1)}function Bd(e){X(e)?dl.push(...e):(!Lt||!Lt.includes(e,e.allowRecurse?Ut+1:Ut))&&dl.push(e),Ws()}function sr(e,t=ql?ft+1:0){for(;tGl(l)-Gl(n)),Ut=0;Ute.id==null?1/0:e.id,Hd=(e,t)=>{const l=Gl(e)-Gl(t);if(l===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return l};function Ks(e){Da=!1,ql=!0,Me.sort(Hd);const t=st;try{for(ft=0;ftae(v)?v.trim():v)),p&&(a=l.map(Gu))}let o,c=n[o=ca(t)]||n[o=ca(tt(t))];!c&&i&&(c=n[o=ca(wl(t))]),c&&Ze(c,e,6,a);const u=n[o+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[o])return;e.emitted[o]=!0,Ze(u,e,6,a)}}function Js(e,t,l=!1){const n=t.emitsCache,a=n.get(e);if(a!==void 0)return a;const i=e.emits;let r={},o=!1;if(!te(e)){const c=u=>{const d=Js(u,t,!0);d&&(o=!0,Oe(r,d))};!l&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!i&&!o?(Te(e)&&n.set(e,null),null):(X(i)?i.forEach(c=>r[c]=null):Oe(r,i),Te(e)&&n.set(e,r),r)}function Jn(e,t){return!e||!Zl(t)?!1:(t=t.slice(2).replace(/Once$/,""),oe(e,t[0].toLowerCase()+t.slice(1))||oe(e,wl(t))||oe(e,t))}let Xe=null,Qs=null;function Vn(e){const t=Xe;return Xe=e,Qs=e&&e.type.__scopeId||null,t}function qd(e,t=Xe,l){if(!t||e._n)return e;const n=(...a)=>{n._d&&br(-1);const i=Vn(t);let r;try{r=e(...a)}finally{Vn(i),n._d&&br(1)}return r};return n._n=!0,n._c=!0,n._d=!0,n}function da(e){const{type:t,vnode:l,proxy:n,withProxy:a,props:i,propsOptions:[r],slots:o,attrs:c,emit:u,render:d,renderCache:p,data:h,setupState:v,ctx:b,inheritAttrs:w}=e;let L,m;const _=Vn(e);try{if(l.shapeFlag&4){const R=a||n;L=nt(d.call(R,R,p,i,v,h,b)),m=c}else{const R=t;L=nt(R.length>1?R(i,{attrs:c,slots:o,emit:u}):R(i,null)),m=t.props?c:Gd(c)}}catch(R){Nl.length=0,ln(R,e,1),L=Ae(et)}let I=L;if(m&&w!==!1){const R=Object.keys(m),{shapeFlag:B}=I;R.length&&B&7&&(r&&R.some(ti)&&(m=Ud(m,r)),I=jt(I,m))}return l.dirs&&(I=jt(I),I.dirs=I.dirs?I.dirs.concat(l.dirs):l.dirs),l.transition&&(I.transition=l.transition),L=I,Vn(_),L}const Gd=e=>{let t;for(const l in e)(l==="class"||l==="style"||Zl(l))&&((t||(t={}))[l]=e[l]);return t},Ud=(e,t)=>{const l={};for(const n in e)(!ti(n)||!(n.slice(9)in t))&&(l[n]=e[n]);return l};function Wd(e,t,l){const{props:n,children:a,component:i}=e,{props:r,children:o,patchFlag:c}=t,u=i.emitsOptions;if(t.dirs||t.transition)return!0;if(l&&c>=0){if(c&1024)return!0;if(c&16)return n?or(n,r,u):!!r;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Ys(e,t){t&&t.pendingBranch?X(e)?t.effects.push(...e):t.effects.push(e):Bd(e)}function Xs(e,t){return mi(e,null,t)}const En={};function ce(e,t,l){return mi(e,t,l)}function mi(e,t,{immediate:l,deep:n,flush:a,onTrack:i,onTrigger:r}=Le){var o;const c=Ps()===((o=Re)==null?void 0:o.scope)?Re:null;let u,d=!1,p=!1;if(Ie(e)?(u=()=>e.value,d=Mn(e)):ul(e)?(u=()=>e,n=!0):X(e)?(p=!0,d=e.some(R=>ul(R)||Mn(R)),u=()=>e.map(R=>{if(Ie(R))return R.value;if(ul(R))return sl(R);if(te(R))return Vt(R,c,2)})):te(e)?t?u=()=>Vt(e,c,2):u=()=>{if(!(c&&c.isUnmounted))return h&&h(),Ze(e,c,3,[v])}:u=st,t&&n){const R=u;u=()=>sl(R())}let h,v=R=>{h=_.onStop=()=>{Vt(R,c,4)}},b;if(ml)if(v=st,t?l&&Ze(t,c,3,[u(),p?[]:void 0,v]):u(),a==="sync"){const R=qp();b=R.__watcherHandles||(R.__watcherHandles=[])}else return st;let w=p?new Array(e.length).fill(En):En;const L=()=>{if(_.active)if(t){const R=_.run();(n||d||(p?R.some((B,C)=>Hl(B,w[C])):Hl(R,w)))&&(h&&h(),Ze(t,c,3,[R,w===En?void 0:p&&w[0]===En?[]:w,v]),w=R)}else _.run()};L.allowRecurse=!!t;let m;a==="sync"?m=L:a==="post"?m=()=>Be(L,c&&c.suspense):(L.pre=!0,c&&(L.id=c.uid),m=()=>Kn(L));const _=new oi(u,m);t?l?L():w=_.run():a==="post"?Be(_.run.bind(_),c&&c.suspense):_.run();const I=()=>{_.stop(),c&&c.scope&&li(c.scope.effects,_)};return b&&b.push(I),I}function Qd(e,t,l){const n=this.proxy,a=ae(e)?e.includes(".")?Zs(n,e):()=>n[e]:e.bind(n,n);let i;te(t)?i=t:(i=t.handler,l=t);const r=Re;gl(this);const o=mi(a,i.bind(n),l);return r?gl(r):Qt(),o}function Zs(e,t){const l=t.split(".");return()=>{let n=e;for(let a=0;a{sl(l,t)});else if(Hu(e))for(const l in e)sl(e[l],t);return e}function vt(e,t,l,n){const a=e.dirs,i=t&&t.dirs;for(let r=0;r{e.isMounted=!0}),Yn(()=>{e.isUnmounting=!0}),e}const Qe=[Function,Array],to={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Qe,onEnter:Qe,onAfterEnter:Qe,onEnterCancelled:Qe,onBeforeLeave:Qe,onLeave:Qe,onAfterLeave:Qe,onLeaveCancelled:Qe,onBeforeAppear:Qe,onAppear:Qe,onAfterAppear:Qe,onAppearCancelled:Qe},Yd={name:"BaseTransition",props:to,setup(e,{slots:t}){const l=Xt(),n=eo();let a;return()=>{const i=t.default&&yi(t.default(),!0);if(!i||!i.length)return;let r=i[0];if(i.length>1){for(const w of i)if(w.type!==et){r=w;break}}const o=re(e),{mode:c}=o;if(n.isLeaving)return pa(r);const u=cr(r);if(!u)return pa(r);const d=Ul(u,o,n,l);Wl(u,d);const p=l.subTree,h=p&&cr(p);let v=!1;const{getTransitionKey:b}=u.type;if(b){const w=b();a===void 0?a=w:w!==a&&(a=w,v=!0)}if(h&&h.type!==et&&(!Wt(u,h)||v)){const w=Ul(h,o,n,l);if(Wl(h,w),c==="out-in")return n.isLeaving=!0,w.afterLeave=()=>{n.isLeaving=!1,l.update.active!==!1&&l.update()},pa(r);c==="in-out"&&u.type!==et&&(w.delayLeave=(L,m,_)=>{const I=lo(n,h);I[String(h.key)]=h,L._leaveCb=()=>{m(),L._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=_})}return r}}},Xd=Yd;function lo(e,t){const{leavingVNodes:l}=e;let n=l.get(t.type);return n||(n=Object.create(null),l.set(t.type,n)),n}function Ul(e,t,l,n){const{appear:a,mode:i,persisted:r=!1,onBeforeEnter:o,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:h,onAfterLeave:v,onLeaveCancelled:b,onBeforeAppear:w,onAppear:L,onAfterAppear:m,onAppearCancelled:_}=t,I=String(e.key),R=lo(l,e),B=(A,q)=>{A&&Ze(A,n,9,q)},C=(A,q)=>{const Y=q[1];B(A,q),X(A)?A.every(ne=>ne.length<=1)&&Y():A.length<=1&&Y()},V={mode:i,persisted:r,beforeEnter(A){let q=o;if(!l.isMounted)if(a)q=w||o;else return;A._leaveCb&&A._leaveCb(!0);const Y=R[I];Y&&Wt(e,Y)&&Y.el._leaveCb&&Y.el._leaveCb(),B(q,[A])},enter(A){let q=c,Y=u,ne=d;if(!l.isMounted)if(a)q=L||c,Y=m||u,ne=_||d;else return;let z=!1;const ee=A._enterCb=U=>{z||(z=!0,U?B(ne,[A]):B(Y,[A]),V.delayedLeave&&V.delayedLeave(),A._enterCb=void 0)};q?C(q,[A,ee]):ee()},leave(A,q){const Y=String(e.key);if(A._enterCb&&A._enterCb(!0),l.isUnmounting)return q();B(p,[A]);let ne=!1;const z=A._leaveCb=ee=>{ne||(ne=!0,q(),ee?B(b,[A]):B(v,[A]),A._leaveCb=void 0,R[Y]===e&&delete R[Y])};R[Y]=e,h?C(h,[A,z]):z()},clone(A){return Ul(A,t,l,n)}};return V}function pa(e){if(nn(e))return e=jt(e),e.children=null,e}function cr(e){return nn(e)?e.children?e.children[0]:void 0:e}function Wl(e,t){e.shapeFlag&6&&e.component?Wl(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function yi(e,t=!1,l){let n=[],a=0;for(let i=0;i1)for(let i=0;iOe({name:e.name},t,{setup:e}))():e}const $l=e=>!!e.type.__asyncLoader;function T(e){te(e)&&(e={loader:e});const{loader:t,loadingComponent:l,errorComponent:n,delay:a=200,timeout:i,suspensible:r=!0,onError:o}=e;let c=null,u,d=0;const p=()=>(d++,c=null,h()),h=()=>{let v;return c||(v=c=t().catch(b=>{if(b=b instanceof Error?b:new Error(String(b)),o)return new Promise((w,L)=>{o(b,()=>w(p()),()=>L(b),d+1)});throw b}).then(b=>v!==c&&c?c:(b&&(b.__esModule||b[Symbol.toStringTag]==="Module")&&(b=b.default),u=b,b)))};return $({name:"AsyncComponentWrapper",__asyncLoader:h,get __asyncResolved(){return u},setup(){const v=Re;if(u)return()=>ha(u,v);const b=_=>{c=null,ln(_,v,13,!n)};if(r&&v.suspense||ml)return h().then(_=>()=>ha(_,v)).catch(_=>(b(_),()=>n?Ae(n,{error:_}):null));const w=W(!1),L=W(),m=W(!!a);return a&&setTimeout(()=>{m.value=!1},a),i!=null&&setTimeout(()=>{if(!w.value&&!L.value){const _=new Error(`Async component timed out after ${i}ms.`);b(_),L.value=_}},i),h().then(()=>{w.value=!0,v.parent&&nn(v.parent.vnode)&&Kn(v.parent.update)}).catch(_=>{b(_),L.value=_}),()=>{if(w.value&&u)return ha(u,v);if(L.value&&n)return Ae(n,{error:L.value});if(l&&!m.value)return Ae(l)}}})}function ha(e,t){const{ref:l,props:n,children:a,ce:i}=t.vnode,r=Ae(e,n,a);return r.ref=l,r.ce=i,delete t.vnode.ce,r}const nn=e=>e.type.__isKeepAlive;function Zd(e,t){no(e,"a",t)}function ep(e,t){no(e,"da",t)}function no(e,t,l=Re){const n=e.__wdc||(e.__wdc=()=>{let a=l;for(;a;){if(a.isDeactivated)return;a=a.parent}return e()});if(Qn(t,n,l),l){let a=l.parent;for(;a&&a.parent;)nn(a.parent.vnode)&&tp(n,t,l,a),a=a.parent}}function tp(e,t,l,n){const a=Qn(t,e,n,!0);an(()=>{li(n[t],a)},l)}function Qn(e,t,l=Re,n=!1){if(l){const a=l[e]||(l[e]=[]),i=t.__weh||(t.__weh=(...r)=>{if(l.isUnmounted)return;El(),gl(l);const o=Ze(t,l,e,r);return Qt(),Ll(),o});return n?a.unshift(i):a.push(i),i}}const At=e=>(t,l=Re)=>(!ml||e==="sp")&&Qn(e,(...n)=>t(...n),l),lp=At("bm"),ke=At("m"),np=At("bu"),ao=At("u"),Yn=At("bum"),an=At("um"),ap=At("sp"),ip=At("rtg"),rp=At("rtc");function sp(e,t=Re){Qn("ec",e,t)}const io="components";function Je(e,t){return cp(io,e,!0,t)||e}const op=Symbol.for("v-ndc");function cp(e,t,l=!0,n=!1){const a=Xe||Re;if(a){const i=a.type;if(e===io){const o=Bp(i,!1);if(o&&(o===t||o===tt(t)||o===en(tt(t))))return i}const r=ur(a[e]||i[e],t)||ur(a.appContext[e],t);return!r&&n?i:r}}function ur(e,t){return e&&(e[t]||e[tt(t)]||e[en(tt(t))])}const Ma=e=>e?bo(e)?Ei(e)||e.proxy:Ma(e.parent):null,Vl=Oe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Ma(e.parent),$root:e=>Ma(e.root),$emit:e=>e.emit,$options:e=>bi(e),$forceUpdate:e=>e.f||(e.f=()=>Kn(e.update)),$nextTick:e=>e.n||(e.n=Tl.bind(e.proxy)),$watch:e=>Qd.bind(e)}),va=(e,t)=>e!==Le&&!e.__isScriptSetup&&oe(e,t),up={get({_:e},t){const{ctx:l,setupState:n,data:a,props:i,accessCache:r,type:o,appContext:c}=e;let u;if(t[0]!=="$"){const v=r[t];if(v!==void 0)switch(v){case 1:return n[t];case 2:return a[t];case 4:return l[t];case 3:return i[t]}else{if(va(n,t))return r[t]=1,n[t];if(a!==Le&&oe(a,t))return r[t]=2,a[t];if((u=e.propsOptions[0])&&oe(u,t))return r[t]=3,i[t];if(l!==Le&&oe(l,t))return r[t]=4,l[t];$a&&(r[t]=0)}}const d=Vl[t];let p,h;if(d)return t==="$attrs"&&qe(e,"get",t),d(e);if((p=o.__cssModules)&&(p=p[t]))return p;if(l!==Le&&oe(l,t))return r[t]=4,l[t];if(h=c.config.globalProperties,oe(h,t))return h[t]},set({_:e},t,l){const{data:n,setupState:a,ctx:i}=e;return va(a,t)?(a[t]=l,!0):n!==Le&&oe(n,t)?(n[t]=l,!0):oe(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=l,!0)},has({_:{data:e,setupState:t,accessCache:l,ctx:n,appContext:a,propsOptions:i}},r){let o;return!!l[r]||e!==Le&&oe(e,r)||va(t,r)||(o=i[0])&&oe(o,r)||oe(n,r)||oe(Vl,r)||oe(a.config.globalProperties,r)},defineProperty(e,t,l){return l.get!=null?e._.accessCache[t]=0:oe(l,"value")&&this.set(e,t,l.value,null),Reflect.defineProperty(e,t,l)}};function dr(e){return X(e)?e.reduce((t,l)=>(t[l]=null,t),{}):e}let $a=!0;function dp(e){const t=bi(e),l=e.proxy,n=e.ctx;$a=!1,t.beforeCreate&&pr(t.beforeCreate,e,"bc");const{data:a,computed:i,methods:r,watch:o,provide:c,inject:u,created:d,beforeMount:p,mounted:h,beforeUpdate:v,updated:b,activated:w,deactivated:L,beforeDestroy:m,beforeUnmount:_,destroyed:I,unmounted:R,render:B,renderTracked:C,renderTriggered:V,errorCaptured:A,serverPrefetch:q,expose:Y,inheritAttrs:ne,components:z,directives:ee,filters:U}=t;if(u&&pp(u,n,null),r)for(const _e in r){const ve=r[_e];te(ve)&&(n[_e]=ve.bind(l))}if(a){const _e=a.call(l,l);Te(_e)&&(e.data=tn(_e))}if($a=!0,i)for(const _e in i){const ve=i[_e],bt=te(ve)?ve.bind(l,l):te(ve.get)?ve.get.bind(l,l):st,Ot=!te(ve)&&te(ve.set)?ve.set.bind(l):st,pt=E({get:bt,set:Ot});Object.defineProperty(n,_e,{enumerable:!0,configurable:!0,get:()=>pt.value,set:je=>pt.value=je})}if(o)for(const _e in o)ro(o[_e],n,l,_e);if(c){const _e=te(c)?c.call(l):c;Reflect.ownKeys(_e).forEach(ve=>{ot(ve,_e[ve])})}d&&pr(d,e,"c");function he(_e,ve){X(ve)?ve.forEach(bt=>_e(bt.bind(l))):ve&&_e(ve.bind(l))}if(he(lp,p),he(ke,h),he(np,v),he(ao,b),he(Zd,w),he(ep,L),he(sp,A),he(rp,C),he(ip,V),he(Yn,_),he(an,R),he(ap,q),X(Y))if(Y.length){const _e=e.exposed||(e.exposed={});Y.forEach(ve=>{Object.defineProperty(_e,ve,{get:()=>l[ve],set:bt=>l[ve]=bt})})}else e.exposed||(e.exposed={});B&&e.render===st&&(e.render=B),ne!=null&&(e.inheritAttrs=ne),z&&(e.components=z),ee&&(e.directives=ee)}function pp(e,t,l=st){X(e)&&(e=Va(e));for(const n in e){const a=e[n];let i;Te(a)?"default"in a?i=me(a.from||n,a.default,!0):i=me(a.from||n):i=me(a),Ie(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:r=>i.value=r}):t[n]=i}}function pr(e,t,l){Ze(X(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,l)}function ro(e,t,l,n){const a=n.includes(".")?Zs(l,n):()=>l[n];if(ae(e)){const i=t[e];te(i)&&ce(a,i)}else if(te(e))ce(a,e.bind(l));else if(Te(e))if(X(e))e.forEach(i=>ro(i,t,l,n));else{const i=te(e.handler)?e.handler.bind(l):t[e.handler];te(i)&&ce(a,i,e)}}function bi(e){const t=e.type,{mixins:l,extends:n}=t,{mixins:a,optionsCache:i,config:{optionMergeStrategies:r}}=e.appContext,o=i.get(t);let c;return o?c=o:!a.length&&!l&&!n?c=t:(c={},a.length&&a.forEach(u=>Fn(c,u,r,!0)),Fn(c,t,r)),Te(t)&&i.set(t,c),c}function Fn(e,t,l,n=!1){const{mixins:a,extends:i}=t;i&&Fn(e,i,l,!0),a&&a.forEach(r=>Fn(e,r,l,!0));for(const r in t)if(!(n&&r==="expose")){const o=hp[r]||l&&l[r];e[r]=o?o(e[r],t[r]):t[r]}return e}const hp={data:hr,props:vr,emits:vr,methods:Sl,computed:Sl,beforeCreate:Fe,created:Fe,beforeMount:Fe,mounted:Fe,beforeUpdate:Fe,updated:Fe,beforeDestroy:Fe,beforeUnmount:Fe,destroyed:Fe,unmounted:Fe,activated:Fe,deactivated:Fe,errorCaptured:Fe,serverPrefetch:Fe,components:Sl,directives:Sl,watch:fp,provide:hr,inject:vp};function hr(e,t){return t?e?function(){return Oe(te(e)?e.call(this,this):e,te(t)?t.call(this,this):t)}:t:e}function vp(e,t){return Sl(Va(e),Va(t))}function Va(e){if(X(e)){const t={};for(let l=0;l1)return l&&te(t)?t.call(n&&n.proxy):t}}function yp(e,t,l,n=!1){const a={},i={};Sn(i,Xn,1),e.propsDefaults=Object.create(null),oo(e,t,a,i);for(const r in e.propsOptions[0])r in a||(a[r]=void 0);l?e.props=n?a:js(a):e.type.props?e.props=a:e.props=i,e.attrs=i}function bp(e,t,l,n){const{props:a,attrs:i,vnode:{patchFlag:r}}=e,o=re(a),[c]=e.propsOptions;let u=!1;if((n||r>0)&&!(r&16)){if(r&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[h,v]=co(p,t,!0);Oe(r,h),v&&o.push(...v)};!l&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!c)return Te(e)&&n.set(e,cl),cl;if(X(i))for(let d=0;d-1,v[1]=w<0||b-1||oe(v,"default"))&&o.push(p)}}}const u=[r,o];return Te(e)&&n.set(e,u),u}function fr(e){return e[0]!=="$"}function gr(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function mr(e,t){return gr(e)===gr(t)}function yr(e,t){return X(t)?t.findIndex(l=>mr(l,e)):te(t)&&mr(t,e)?0:-1}const uo=e=>e[0]==="_"||e==="$stable",_i=e=>X(e)?e.map(nt):[nt(e)],_p=(e,t,l)=>{if(t._n)return t;const n=qd((...a)=>_i(t(...a)),l);return n._c=!1,n},po=(e,t,l)=>{const n=e._ctx;for(const a in e){if(uo(a))continue;const i=e[a];if(te(i))t[a]=_p(a,i,n);else if(i!=null){const r=_i(i);t[a]=()=>r}}},ho=(e,t)=>{const l=_i(t);e.slots.default=()=>l},kp=(e,t)=>{if(e.vnode.shapeFlag&32){const l=t._;l?(e.slots=re(t),Sn(t,"_",l)):po(t,e.slots={})}else e.slots={},t&&ho(e,t);Sn(e.slots,Xn,1)},wp=(e,t,l)=>{const{vnode:n,slots:a}=e;let i=!0,r=Le;if(n.shapeFlag&32){const o=t._;o?l&&o===1?i=!1:(Oe(a,t),!l&&o===1&&delete a._):(i=!t.$stable,po(t,a)),r=t}else t&&(ho(e,t),r={default:1});if(i)for(const o in a)!uo(o)&&!(o in r)&&delete a[o]};function jn(e,t,l,n,a=!1){if(X(e)){e.forEach((h,v)=>jn(h,t&&(X(t)?t[v]:t),l,n,a));return}if($l(n)&&!a)return;const i=n.shapeFlag&4?Ei(n.component)||n.component.proxy:n.el,r=a?null:i,{i:o,r:c}=e,u=t&&t.r,d=o.refs===Le?o.refs={}:o.refs,p=o.setupState;if(u!=null&&u!==c&&(ae(u)?(d[u]=null,oe(p,u)&&(p[u]=null)):Ie(u)&&(u.value=null)),te(c))Vt(c,o,12,[r,d]);else{const h=ae(c),v=Ie(c);if(h||v){const b=()=>{if(e.f){const w=h?oe(p,c)?p[c]:d[c]:c.value;a?X(w)&&li(w,i):X(w)?w.includes(i)||w.push(i):h?(d[c]=[i],oe(p,c)&&(p[c]=d[c])):(c.value=[i],e.k&&(d[e.k]=c.value))}else h?(d[c]=r,oe(p,c)&&(p[c]=r)):v&&(c.value=r,e.k&&(d[e.k]=r))};r?(b.id=-1,Be(b,l)):b()}}}let It=!1;const Ln=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",xn=e=>e.nodeType===8;function Ep(e){const{mt:t,p:l,o:{patchProp:n,createText:a,nextSibling:i,parentNode:r,remove:o,insert:c,createComment:u}}=e,d=(m,_)=>{if(!_.hasChildNodes()){l(null,m,_),$n(),_._vnode=m;return}It=!1,p(_.firstChild,m,null,null,null),$n(),_._vnode=m,It&&console.error("Hydration completed but contains mismatches.")},p=(m,_,I,R,B,C=!1)=>{const V=xn(m)&&m.data==="[",A=()=>w(m,_,I,R,B,V),{type:q,ref:Y,shapeFlag:ne,patchFlag:z}=_;let ee=m.nodeType;_.el=m,z===-2&&(C=!1,_.dynamicChildren=null);let U=null;switch(q){case fl:ee!==3?_.children===""?(c(_.el=a(""),r(m),m),U=m):U=A():(m.data!==_.children&&(It=!0,m.data=_.children),U=i(m));break;case et:ee!==8||V?U=A():U=i(m);break;case Fl:if(V&&(m=i(m),ee=m.nodeType),ee===1||ee===3){U=m;const Se=!_.children.length;for(let he=0;he<_.staticCount;he++)Se&&(_.children+=U.nodeType===1?U.outerHTML:U.data),he===_.staticCount-1&&(_.anchor=U),U=i(U);return V?i(U):U}else A();break;case Ke:V?U=b(m,_,I,R,B,C):U=A();break;default:if(ne&1)ee!==1||_.type.toLowerCase()!==m.tagName.toLowerCase()?U=A():U=h(m,_,I,R,B,C);else if(ne&6){_.slotScopeIds=B;const Se=r(m);if(t(_,Se,null,I,R,Ln(Se),C),U=V?L(m):i(m),U&&xn(U)&&U.data==="teleport end"&&(U=i(U)),$l(_)){let he;V?(he=Ae(Ke),he.anchor=U?U.previousSibling:Se.lastChild):he=m.nodeType===3?yo(""):Ae("div"),he.el=m,_.component.subTree=he}}else ne&64?ee!==8?U=A():U=_.type.hydrate(m,_,I,R,B,C,e,v):ne&128&&(U=_.type.hydrate(m,_,I,R,Ln(r(m)),B,C,e,p))}return Y!=null&&jn(Y,null,R,_),U},h=(m,_,I,R,B,C)=>{C=C||!!_.dynamicChildren;const{type:V,props:A,patchFlag:q,shapeFlag:Y,dirs:ne}=_,z=V==="input"&&ne||V==="option";if(z||q!==-1){if(ne&&vt(_,null,I,"created"),A)if(z||!C||q&48)for(const U in A)(z&&U.endsWith("value")||Zl(U)&&!Ml(U))&&n(m,U,null,A[U],!1,void 0,I);else A.onClick&&n(m,"onClick",null,A.onClick,!1,void 0,I);let ee;if((ee=A&&A.onVnodeBeforeMount)&&Ye(ee,I,_),ne&&vt(_,null,I,"beforeMount"),((ee=A&&A.onVnodeMounted)||ne)&&Ys(()=>{ee&&Ye(ee,I,_),ne&&vt(_,null,I,"mounted")},R),Y&16&&!(A&&(A.innerHTML||A.textContent))){let U=v(m.firstChild,_,m,I,R,B,C);for(;U;){It=!0;const Se=U;U=U.nextSibling,o(Se)}}else Y&8&&m.textContent!==_.children&&(It=!0,m.textContent=_.children)}return m.nextSibling},v=(m,_,I,R,B,C,V)=>{V=V||!!_.dynamicChildren;const A=_.children,q=A.length;for(let Y=0;Y{const{slotScopeIds:V}=_;V&&(B=B?B.concat(V):V);const A=r(m),q=v(i(m),_,A,I,R,B,C);return q&&xn(q)&&q.data==="]"?i(_.anchor=q):(It=!0,c(_.anchor=u("]"),A,q),q)},w=(m,_,I,R,B,C)=>{if(It=!0,_.el=null,C){const q=L(m);for(;;){const Y=i(m);if(Y&&Y!==q)o(Y);else break}}const V=i(m),A=r(m);return o(m),l(null,_,A,V,I,R,Ln(A),B),V},L=m=>{let _=0;for(;m;)if(m=i(m),m&&xn(m)&&(m.data==="["&&_++,m.data==="]")){if(_===0)return i(m);_--}return m};return[d,p]}const Be=Ys;function Lp(e){return xp(e,Ep)}function xp(e,t){const l=Ia();l.__VUE__=!0;const{insert:n,remove:a,patchProp:i,createElement:r,createText:o,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:h,setScopeId:v=st,insertStaticContent:b}=e,w=(f,y,k,x=null,P=null,S=null,j=!1,M=null,F=!!y.dynamicChildren)=>{if(f===y)return;f&&!Wt(f,y)&&(x=O(f),je(f,P,S,!0),f=null),y.patchFlag===-2&&(F=!1,y.dynamicChildren=null);const{type:D,ref:J,shapeFlag:G}=y;switch(D){case fl:L(f,y,k,x);break;case et:m(f,y,k,x);break;case Fl:f==null&&_(y,k,x,j);break;case Ke:z(f,y,k,x,P,S,j,M,F);break;default:G&1?B(f,y,k,x,P,S,j,M,F):G&6?ee(f,y,k,x,P,S,j,M,F):(G&64||G&128)&&D.process(f,y,k,x,P,S,j,M,F,N)}J!=null&&P&&jn(J,f&&f.ref,S,y||f,!y)},L=(f,y,k,x)=>{if(f==null)n(y.el=o(y.children),k,x);else{const P=y.el=f.el;y.children!==f.children&&u(P,y.children)}},m=(f,y,k,x)=>{f==null?n(y.el=c(y.children||""),k,x):y.el=f.el},_=(f,y,k,x)=>{[f.el,f.anchor]=b(f.children,y,k,x,f.el,f.anchor)},I=({el:f,anchor:y},k,x)=>{let P;for(;f&&f!==y;)P=h(f),n(f,k,x),f=P;n(y,k,x)},R=({el:f,anchor:y})=>{let k;for(;f&&f!==y;)k=h(f),a(f),f=k;a(y)},B=(f,y,k,x,P,S,j,M,F)=>{j=j||y.type==="svg",f==null?C(y,k,x,P,S,j,M,F):q(f,y,P,S,j,M,F)},C=(f,y,k,x,P,S,j,M)=>{let F,D;const{type:J,props:G,shapeFlag:Q,transition:Z,dirs:le}=f;if(F=f.el=r(f.type,S,G&&G.is,G),Q&8?d(F,f.children):Q&16&&A(f.children,F,null,x,P,S&&J!=="foreignObject",j,M),le&&vt(f,null,x,"created"),V(F,f,f.scopeId,j,x),G){for(const ye in G)ye!=="value"&&!Ml(ye)&&i(F,ye,null,G[ye],S,f.children,x,P,De);"value"in G&&i(F,"value",null,G.value),(D=G.onVnodeBeforeMount)&&Ye(D,x,f)}le&&vt(f,null,x,"beforeMount");const we=(!P||P&&!P.pendingBranch)&&Z&&!Z.persisted;we&&Z.beforeEnter(F),n(F,y,k),((D=G&&G.onVnodeMounted)||we||le)&&Be(()=>{D&&Ye(D,x,f),we&&Z.enter(F),le&&vt(f,null,x,"mounted")},P)},V=(f,y,k,x,P)=>{if(k&&v(f,k),x)for(let S=0;S{for(let D=F;D{const M=y.el=f.el;let{patchFlag:F,dynamicChildren:D,dirs:J}=y;F|=f.patchFlag&16;const G=f.props||Le,Q=y.props||Le;let Z;k&&qt(k,!1),(Z=Q.onVnodeBeforeUpdate)&&Ye(Z,k,y,f),J&&vt(y,f,k,"beforeUpdate"),k&&qt(k,!0);const le=P&&y.type!=="foreignObject";if(D?Y(f.dynamicChildren,D,M,k,x,le,S):j||ve(f,y,M,null,k,x,le,S,!1),F>0){if(F&16)ne(M,y,G,Q,k,x,P);else if(F&2&&G.class!==Q.class&&i(M,"class",null,Q.class,P),F&4&&i(M,"style",G.style,Q.style,P),F&8){const we=y.dynamicProps;for(let ye=0;ye{Z&&Ye(Z,k,y,f),J&&vt(y,f,k,"updated")},x)},Y=(f,y,k,x,P,S,j)=>{for(let M=0;M{if(k!==x){if(k!==Le)for(const M in k)!Ml(M)&&!(M in x)&&i(f,M,k[M],null,j,y.children,P,S,De);for(const M in x){if(Ml(M))continue;const F=x[M],D=k[M];F!==D&&M!=="value"&&i(f,M,D,F,j,y.children,P,S,De)}"value"in x&&i(f,"value",k.value,x.value)}},z=(f,y,k,x,P,S,j,M,F)=>{const D=y.el=f?f.el:o(""),J=y.anchor=f?f.anchor:o("");let{patchFlag:G,dynamicChildren:Q,slotScopeIds:Z}=y;Z&&(M=M?M.concat(Z):Z),f==null?(n(D,k,x),n(J,k,x),A(y.children,k,J,P,S,j,M,F)):G>0&&G&64&&Q&&f.dynamicChildren?(Y(f.dynamicChildren,Q,k,P,S,j,M),(y.key!=null||P&&y===P.subTree)&&vo(f,y,!0)):ve(f,y,k,J,P,S,j,M,F)},ee=(f,y,k,x,P,S,j,M,F)=>{y.slotScopeIds=M,f==null?y.shapeFlag&512?P.ctx.activate(y,k,x,j,F):U(y,k,x,P,S,j,F):Se(f,y,F)},U=(f,y,k,x,P,S,j)=>{const M=f.component=$p(f,x,P);if(nn(f)&&(M.ctx.renderer=N),Vp(M),M.asyncDep){if(P&&P.registerDep(M,he),!f.el){const F=M.subTree=Ae(et);m(null,F,y,k)}return}he(M,f,y,k,P,S,j)},Se=(f,y,k)=>{const x=y.component=f.component;if(Wd(f,y,k))if(x.asyncDep&&!x.asyncResolved){_e(x,y,k);return}else x.next=y,jd(x.update),x.update();else y.el=f.el,x.vnode=y},he=(f,y,k,x,P,S,j)=>{const M=()=>{if(f.isMounted){let{next:J,bu:G,u:Q,parent:Z,vnode:le}=f,we=J,ye;qt(f,!1),J?(J.el=le.el,_e(f,J,j)):J=le,G&&ua(G),(ye=J.props&&J.props.onVnodeBeforeUpdate)&&Ye(ye,Z,J,le),qt(f,!0);const Pe=da(f),lt=f.subTree;f.subTree=Pe,w(lt,Pe,p(lt.el),O(lt),f,P,S),J.el=Pe.el,we===null&&Kd(f,Pe.el),Q&&Be(Q,P),(ye=J.props&&J.props.onVnodeUpdated)&&Be(()=>Ye(ye,Z,J,le),P)}else{let J;const{el:G,props:Q}=y,{bm:Z,m:le,parent:we}=f,ye=$l(y);if(qt(f,!1),Z&&ua(Z),!ye&&(J=Q&&Q.onVnodeBeforeMount)&&Ye(J,we,y),qt(f,!0),G&&fe){const Pe=()=>{f.subTree=da(f),fe(G,f.subTree,f,P,null)};ye?y.type.__asyncLoader().then(()=>!f.isUnmounted&&Pe()):Pe()}else{const Pe=f.subTree=da(f);w(null,Pe,k,x,f,P,S),y.el=Pe.el}if(le&&Be(le,P),!ye&&(J=Q&&Q.onVnodeMounted)){const Pe=y;Be(()=>Ye(J,we,Pe),P)}(y.shapeFlag&256||we&&$l(we.vnode)&&we.vnode.shapeFlag&256)&&f.a&&Be(f.a,P),f.isMounted=!0,y=k=x=null}},F=f.effect=new oi(M,()=>Kn(D),f.scope),D=f.update=()=>F.run();D.id=f.uid,qt(f,!0),D()},_e=(f,y,k)=>{y.component=f;const x=f.vnode.props;f.vnode=y,f.next=null,bp(f,y.props,x,k),wp(f,y.children,k),El(),sr(),Ll()},ve=(f,y,k,x,P,S,j,M,F=!1)=>{const D=f&&f.children,J=f?f.shapeFlag:0,G=y.children,{patchFlag:Q,shapeFlag:Z}=y;if(Q>0){if(Q&128){Ot(D,G,k,x,P,S,j,M,F);return}else if(Q&256){bt(D,G,k,x,P,S,j,M,F);return}}Z&8?(J&16&&De(D,P,S),G!==D&&d(k,G)):J&16?Z&16?Ot(D,G,k,x,P,S,j,M,F):De(D,P,S,!0):(J&8&&d(k,""),Z&16&&A(G,k,x,P,S,j,M,F))},bt=(f,y,k,x,P,S,j,M,F)=>{f=f||cl,y=y||cl;const D=f.length,J=y.length,G=Math.min(D,J);let Q;for(Q=0;QJ?De(f,P,S,!0,!1,G):A(y,k,x,P,S,j,M,F,G)},Ot=(f,y,k,x,P,S,j,M,F)=>{let D=0;const J=y.length;let G=f.length-1,Q=J-1;for(;D<=G&&D<=Q;){const Z=f[D],le=y[D]=F?St(y[D]):nt(y[D]);if(Wt(Z,le))w(Z,le,k,null,P,S,j,M,F);else break;D++}for(;D<=G&&D<=Q;){const Z=f[G],le=y[Q]=F?St(y[Q]):nt(y[Q]);if(Wt(Z,le))w(Z,le,k,null,P,S,j,M,F);else break;G--,Q--}if(D>G){if(D<=Q){const Z=Q+1,le=ZQ)for(;D<=G;)je(f[D],P,S,!0),D++;else{const Z=D,le=D,we=new Map;for(D=le;D<=Q;D++){const Ue=y[D]=F?St(y[D]):nt(y[D]);Ue.key!=null&&we.set(Ue.key,D)}let ye,Pe=0;const lt=Q-le+1;let nl=!1,Ji=0;const Pl=new Array(lt);for(D=0;D=lt){je(Ue,P,S,!0);continue}let ht;if(Ue.key!=null)ht=we.get(Ue.key);else for(ye=le;ye<=Q;ye++)if(Pl[ye-le]===0&&Wt(Ue,y[ye])){ht=ye;break}ht===void 0?je(Ue,P,S,!0):(Pl[ht-le]=D+1,ht>=Ji?Ji=ht:nl=!0,w(Ue,y[ht],k,null,P,S,j,M,F),Pe++)}const Qi=nl?Tp(Pl):cl;for(ye=Qi.length-1,D=lt-1;D>=0;D--){const Ue=le+D,ht=y[Ue],Yi=Ue+1{const{el:S,type:j,transition:M,children:F,shapeFlag:D}=f;if(D&6){pt(f.component.subTree,y,k,x);return}if(D&128){f.suspense.move(y,k,x);return}if(D&64){j.move(f,y,k,N);return}if(j===Ke){n(S,y,k);for(let G=0;GM.enter(S),P);else{const{leave:G,delayLeave:Q,afterLeave:Z}=M,le=()=>n(S,y,k),we=()=>{G(S,()=>{le(),Z&&Z()})};Q?Q(S,le,we):we()}else n(S,y,k)},je=(f,y,k,x=!1,P=!1)=>{const{type:S,props:j,ref:M,children:F,dynamicChildren:D,shapeFlag:J,patchFlag:G,dirs:Q}=f;if(M!=null&&jn(M,null,k,f,!0),J&256){y.ctx.deactivate(f);return}const Z=J&1&&Q,le=!$l(f);let we;if(le&&(we=j&&j.onVnodeBeforeUnmount)&&Ye(we,y,f),J&6)mn(f.component,k,x);else{if(J&128){f.suspense.unmount(k,x);return}Z&&vt(f,null,y,"beforeUnmount"),J&64?f.type.remove(f,y,k,P,N,x):D&&(S!==Ke||G>0&&G&64)?De(D,y,k,!1,!0):(S===Ke&&G&384||!P&&J&16)&&De(F,y,k),x&&tl(f)}(le&&(we=j&&j.onVnodeUnmounted)||Z)&&Be(()=>{we&&Ye(we,y,f),Z&&vt(f,null,y,"unmounted")},k)},tl=f=>{const{type:y,el:k,anchor:x,transition:P}=f;if(y===Ke){ll(k,x);return}if(y===Fl){R(f);return}const S=()=>{a(k),P&&!P.persisted&&P.afterLeave&&P.afterLeave()};if(f.shapeFlag&1&&P&&!P.persisted){const{leave:j,delayLeave:M}=P,F=()=>j(k,S);M?M(f.el,S,F):F()}else S()},ll=(f,y)=>{let k;for(;f!==y;)k=h(f),a(f),f=k;a(y)},mn=(f,y,k)=>{const{bum:x,scope:P,update:S,subTree:j,um:M}=f;x&&ua(x),P.stop(),S&&(S.active=!1,je(j,f,y,k)),M&&Be(M,y),Be(()=>{f.isUnmounted=!0},y),y&&y.pendingBranch&&!y.isUnmounted&&f.asyncDep&&!f.asyncResolved&&f.suspenseId===y.pendingId&&(y.deps--,y.deps===0&&y.resolve())},De=(f,y,k,x=!1,P=!1,S=0)=>{for(let j=S;jf.shapeFlag&6?O(f.component.subTree):f.shapeFlag&128?f.suspense.next():h(f.anchor||f.el),H=(f,y,k)=>{f==null?y._vnode&&je(y._vnode,null,null,!0):w(y._vnode||null,f,y,null,null,null,k),sr(),$n(),y._vnode=f},N={p:w,um:je,m:pt,r:tl,mt:U,mc:A,pc:ve,pbc:Y,n:O,o:e};let K,fe;return t&&([K,fe]=t(N)),{render:H,hydrate:K,createApp:mp(H,K)}}function qt({effect:e,update:t},l){e.allowRecurse=t.allowRecurse=l}function vo(e,t,l=!1){const n=e.children,a=t.children;if(X(n)&&X(a))for(let i=0;i>1,e[l[o]]0&&(t[n]=l[i-1]),l[i]=n)}}for(i=l.length,r=l[i-1];i-- >0;)l[i]=r,r=t[r];return l}const Ap=e=>e.__isTeleport,Ke=Symbol.for("v-fgt"),fl=Symbol.for("v-txt"),et=Symbol.for("v-cmt"),Fl=Symbol.for("v-stc"),Nl=[];let rt=null;function Op(e=!1){Nl.push(rt=e?null:[])}function Pp(){Nl.pop(),rt=Nl[Nl.length-1]||null}let Kl=1;function br(e){Kl+=e}function fo(e){return e.dynamicChildren=Kl>0?rt||cl:null,Pp(),Kl>0&&rt&&rt.push(e),e}function ig(e,t,l,n,a,i){return fo(mo(e,t,l,n,a,i,!0))}function Ip(e,t,l,n,a){return fo(Ae(e,t,l,n,a,!0))}function Na(e){return e?e.__v_isVNode===!0:!1}function Wt(e,t){return e.type===t.type&&e.key===t.key}const Xn="__vInternal",go=({key:e})=>e??null,Cn=({ref:e,ref_key:t,ref_for:l})=>(typeof e=="number"&&(e=""+e),e!=null?ae(e)||Ie(e)||te(e)?{i:Xe,r:e,k:t,f:!!l}:e:null);function mo(e,t=null,l=null,n=0,a=null,i=e===Ke?0:1,r=!1,o=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&go(t),ref:t&&Cn(t),scopeId:Qs,slotScopeIds:null,children:l,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:n,dynamicProps:a,dynamicChildren:null,appContext:null,ctx:Xe};return o?(ki(c,l),i&128&&e.normalize(c)):l&&(c.shapeFlag|=ae(l)?8:16),Kl>0&&!r&&rt&&(c.patchFlag>0||i&6)&&c.patchFlag!==32&&rt.push(c),c}const Ae=Rp;function Rp(e,t=null,l=null,n=0,a=null,i=!1){if((!e||e===op)&&(e=et),Na(e)){const o=jt(e,t,!0);return l&&ki(o,l),Kl>0&&!i&&rt&&(o.shapeFlag&6?rt[rt.indexOf(e)]=o:rt.push(o)),o.patchFlag|=-2,o}if(Hp(e)&&(e=e.__vccOpts),t){t=Cp(t);let{class:o,style:c}=t;o&&!ae(o)&&(t.class=ri(o)),Te(c)&&(Bs(c)&&!X(c)&&(c=Oe({},c)),t.style=ii(c))}const r=ae(e)?1:Jd(e)?128:Ap(e)?64:Te(e)?4:te(e)?2:0;return mo(e,t,l,n,a,r,i,!0)}function Cp(e){return e?Bs(e)||Xn in e?Oe({},e):e:null}function jt(e,t,l=!1){const{props:n,ref:a,patchFlag:i,children:r}=e,o=t?Sp(n||{},t):n;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:o,key:o&&go(o),ref:t&&t.ref?l&&a?X(a)?a.concat(Cn(t)):[a,Cn(t)]:Cn(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:r,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Ke?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&jt(e.ssContent),ssFallback:e.ssFallback&&jt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function yo(e=" ",t=0){return Ae(fl,null,e,t)}function rg(e,t){const l=Ae(Fl,null,e);return l.staticCount=t,l}function sg(e="",t=!1){return t?(Op(),Ip(et,null,e)):Ae(et,null,e)}function nt(e){return e==null||typeof e=="boolean"?Ae(et):X(e)?Ae(Ke,null,e.slice()):typeof e=="object"?St(e):Ae(fl,null,String(e))}function St(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:jt(e)}function ki(e,t){let l=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(X(t))l=16;else if(typeof t=="object")if(n&65){const a=t.default;a&&(a._c&&(a._d=!1),ki(e,a()),a._c&&(a._d=!0));return}else{l=32;const a=t._;!a&&!(Xn in t)?t._ctx=Xe:a===3&&Xe&&(Xe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else te(t)?(t={default:t,_ctx:Xe},l=32):(t=String(t),n&64?(l=16,t=[yo(t)]):l=8);e.children=t,e.shapeFlag|=l}function Sp(...e){const t={};for(let l=0;lRe||Xe;let wi,al,_r="__VUE_INSTANCE_SETTERS__";(al=Ia()[_r])||(al=Ia()[_r]=[]),al.push(e=>Re=e),wi=e=>{al.length>1?al.forEach(t=>t(e)):al[0](e)};const gl=e=>{wi(e),e.scope.on()},Qt=()=>{Re&&Re.scope.off(),wi(null)};function bo(e){return e.vnode.shapeFlag&4}let ml=!1;function Vp(e,t=!1){ml=t;const{props:l,children:n}=e.vnode,a=bo(e);yp(e,l,a,t),kp(e,n);const i=a?Fp(e,t):void 0;return ml=!1,i}function Fp(e,t){const l=e.type;e.accessCache=Object.create(null),e.proxy=Hs(new Proxy(e.ctx,up));const{setup:n}=l;if(n){const a=e.setupContext=n.length>1?jp(e):null;gl(e),El();const i=Vt(n,e,0,[e.props,a]);if(Ll(),Qt(),As(i)){if(i.then(Qt,Qt),t)return i.then(r=>{kr(e,r,t)}).catch(r=>{ln(r,e,0)});e.asyncDep=i}else kr(e,i,t)}else _o(e,t)}function kr(e,t,l){te(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Te(t)&&(e.setupState=qs(t)),_o(e,l)}let wr;function _o(e,t,l){const n=e.type;if(!e.render){if(!t&&wr&&!n.render){const a=n.template||bi(e).template;if(a){const{isCustomElement:i,compilerOptions:r}=e.appContext.config,{delimiters:o,compilerOptions:c}=n,u=Oe(Oe({isCustomElement:i,delimiters:o},r),c);n.render=wr(a,u)}}e.render=n.render||st}gl(e),El(),dp(e),Ll(),Qt()}function Np(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,l){return qe(e,"get","$attrs"),t[l]}}))}function jp(e){const t=l=>{e.exposed=l||{}};return{get attrs(){return Np(e)},slots:e.slots,emit:e.emit,expose:t}}function Ei(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(qs(Hs(e.exposed)),{get(t,l){if(l in t)return t[l];if(l in Vl)return Vl[l](e)},has(t,l){return l in t||l in Vl}}))}function Bp(e,t=!0){return te(e)?e.displayName||e.name:e.name||t&&e.__name}function Hp(e){return te(e)&&"__vccOpts"in e}const E=(e,t)=>Vd(e,t,ml);function s(e,t,l){const n=arguments.length;return n===2?Te(t)&&!X(t)?Na(t)?Ae(e,null,[t]):Ae(e,t):Ae(e,null,t):(n>3?l=Array.prototype.slice.call(arguments,2):n===3&&Na(l)&&(l=[l]),Ae(e,t,l))}const zp=Symbol.for("v-scx"),qp=()=>me(zp),Gp="3.3.4",Up="http://www.w3.org/2000/svg",Kt=typeof document<"u"?document:null,Er=Kt&&Kt.createElement("template"),Wp={insert:(e,t,l)=>{t.insertBefore(e,l||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,l,n)=>{const a=t?Kt.createElementNS(Up,e):Kt.createElement(e,l?{is:l}:void 0);return e==="select"&&n&&n.multiple!=null&&a.setAttribute("multiple",n.multiple),a},createText:e=>Kt.createTextNode(e),createComment:e=>Kt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Kt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,l,n,a,i){const r=l?l.previousSibling:t.lastChild;if(a&&(a===i||a.nextSibling))for(;t.insertBefore(a.cloneNode(!0),l),!(a===i||!(a=a.nextSibling)););else{Er.innerHTML=n?`${e}`:e;const o=Er.content;if(n){const c=o.firstChild;for(;c.firstChild;)o.appendChild(c.firstChild);o.removeChild(c)}t.insertBefore(o,l)}return[r?r.nextSibling:t.firstChild,l?l.previousSibling:t.lastChild]}};function Kp(e,t,l){const n=e._vtc;n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):l?e.setAttribute("class",t):e.className=t}function Jp(e,t,l){const n=e.style,a=ae(l);if(l&&!a){if(t&&!ae(t))for(const i in t)l[i]==null&&ja(n,i,"");for(const i in l)ja(n,i,l[i])}else{const i=n.display;a?t!==l&&(n.cssText=l):t&&e.removeAttribute("style"),"_vod"in e&&(n.display=i)}}const Lr=/\s*!important$/;function ja(e,t,l){if(X(l))l.forEach(n=>ja(e,t,n));else if(l==null&&(l=""),t.startsWith("--"))e.setProperty(t,l);else{const n=Qp(e,t);Lr.test(l)?e.setProperty(wl(n),l.replace(Lr,""),"important"):e[n]=l}}const xr=["Webkit","Moz","ms"],fa={};function Qp(e,t){const l=fa[t];if(l)return l;let n=tt(t);if(n!=="filter"&&n in e)return fa[t]=n;n=en(n);for(let a=0;aga||(n0.then(()=>ga=0),ga=Date.now());function i0(e,t){const l=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=l.attached)return;Ze(r0(n,l.value),t,5,[n])};return l.value=e,l.attached=a0(),l}function r0(e,t){if(X(t)){const l=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{l.call(e),e._stopped=!0},t.map(n=>a=>!a._stopped&&n&&n(a))}else return t}const Or=/^on[a-z]/,s0=(e,t,l,n,a=!1,i,r,o,c)=>{t==="class"?Kp(e,n,a):t==="style"?Jp(e,l,n):Zl(t)?ti(t)||t0(e,t,l,n,r):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):o0(e,t,n,a))?Xp(e,t,n,i,r,o,c):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Yp(e,t,n,a))};function o0(e,t,l,n){return n?!!(t==="innerHTML"||t==="textContent"||t in e&&Or.test(t)&&te(l)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Or.test(t)&&ae(l)?!1:t in e}const Rt="transition",Il="animation",Bt=(e,{slots:t})=>s(Xd,wo(e),t);Bt.displayName="Transition";const ko={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},c0=Bt.props=Oe({},to,ko),Gt=(e,t=[])=>{X(e)?e.forEach(l=>l(...t)):e&&e(...t)},Pr=e=>e?X(e)?e.some(t=>t.length>1):e.length>1:!1;function wo(e){const t={};for(const z in e)z in ko||(t[z]=e[z]);if(e.css===!1)return t;const{name:l="v",type:n,duration:a,enterFromClass:i=`${l}-enter-from`,enterActiveClass:r=`${l}-enter-active`,enterToClass:o=`${l}-enter-to`,appearFromClass:c=i,appearActiveClass:u=r,appearToClass:d=o,leaveFromClass:p=`${l}-leave-from`,leaveActiveClass:h=`${l}-leave-active`,leaveToClass:v=`${l}-leave-to`}=e,b=u0(a),w=b&&b[0],L=b&&b[1],{onBeforeEnter:m,onEnter:_,onEnterCancelled:I,onLeave:R,onLeaveCancelled:B,onBeforeAppear:C=m,onAppear:V=_,onAppearCancelled:A=I}=t,q=(z,ee,U)=>{Ct(z,ee?d:o),Ct(z,ee?u:r),U&&U()},Y=(z,ee)=>{z._isLeaving=!1,Ct(z,p),Ct(z,v),Ct(z,h),ee&&ee()},ne=z=>(ee,U)=>{const Se=z?V:_,he=()=>q(ee,z,U);Gt(Se,[ee,he]),Ir(()=>{Ct(ee,z?c:i),kt(ee,z?d:o),Pr(Se)||Rr(ee,n,w,he)})};return Oe(t,{onBeforeEnter(z){Gt(m,[z]),kt(z,i),kt(z,r)},onBeforeAppear(z){Gt(C,[z]),kt(z,c),kt(z,u)},onEnter:ne(!1),onAppear:ne(!0),onLeave(z,ee){z._isLeaving=!0;const U=()=>Y(z,ee);kt(z,p),Lo(),kt(z,h),Ir(()=>{z._isLeaving&&(Ct(z,p),kt(z,v),Pr(R)||Rr(z,n,L,U))}),Gt(R,[z,U])},onEnterCancelled(z){q(z,!1),Gt(I,[z])},onAppearCancelled(z){q(z,!0),Gt(A,[z])},onLeaveCancelled(z){Y(z),Gt(B,[z])}})}function u0(e){if(e==null)return null;if(Te(e))return[ma(e.enter),ma(e.leave)];{const t=ma(e);return[t,t]}}function ma(e){return Uu(e)}function kt(e,t){t.split(/\s+/).forEach(l=>l&&e.classList.add(l)),(e._vtc||(e._vtc=new Set)).add(t)}function Ct(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const{_vtc:l}=e;l&&(l.delete(t),l.size||(e._vtc=void 0))}function Ir(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let d0=0;function Rr(e,t,l,n){const a=e._endId=++d0,i=()=>{a===e._endId&&n()};if(l)return setTimeout(i,l);const{type:r,timeout:o,propCount:c}=Eo(e,t);if(!r)return n();const u=r+"end";let d=0;const p=()=>{e.removeEventListener(u,h),i()},h=v=>{v.target===e&&++d>=c&&p()};setTimeout(()=>{d(l[b]||"").split(", "),a=n(`${Rt}Delay`),i=n(`${Rt}Duration`),r=Cr(a,i),o=n(`${Il}Delay`),c=n(`${Il}Duration`),u=Cr(o,c);let d=null,p=0,h=0;t===Rt?r>0&&(d=Rt,p=r,h=i.length):t===Il?u>0&&(d=Il,p=u,h=c.length):(p=Math.max(r,u),d=p>0?r>u?Rt:Il:null,h=d?d===Rt?i.length:c.length:0);const v=d===Rt&&/\b(transform|all)(,|$)/.test(n(`${Rt}Property`).toString());return{type:d,timeout:p,propCount:h,hasTransform:v}}function Cr(e,t){for(;e.lengthSr(l)+Sr(e[n])))}function Sr(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function Lo(){return document.body.offsetHeight}const xo=new WeakMap,To=new WeakMap,Ao={name:"TransitionGroup",props:Oe({},c0,{tag:String,moveClass:String}),setup(e,{slots:t}){const l=Xt(),n=eo();let a,i;return ao(()=>{if(!a.length)return;const r=e.moveClass||`${e.name||"v"}-move`;if(!m0(a[0].el,l.vnode.el,r))return;a.forEach(v0),a.forEach(f0);const o=a.filter(g0);Lo(),o.forEach(c=>{const u=c.el,d=u.style;kt(u,r),d.transform=d.webkitTransform=d.transitionDuration="";const p=u._moveCb=h=>{h&&h.target!==u||(!h||/transform$/.test(h.propertyName))&&(u.removeEventListener("transitionend",p),u._moveCb=null,Ct(u,r))};u.addEventListener("transitionend",p)})}),()=>{const r=re(e),o=wo(r);let c=r.tag||Ke;a=i,i=t.default?yi(t.default()):[];for(let u=0;udelete e.mode;Ao.props;const h0=Ao;function v0(e){const t=e.el;t._moveCb&&t._moveCb(),t._enterCb&&t._enterCb()}function f0(e){To.set(e,e.el.getBoundingClientRect())}function g0(e){const t=xo.get(e),l=To.get(e),n=t.left-l.left,a=t.top-l.top;if(n||a){const i=e.el.style;return i.transform=i.webkitTransform=`translate(${n}px,${a}px)`,i.transitionDuration="0s",e}}function m0(e,t,l){const n=e.cloneNode();e._vtc&&e._vtc.forEach(r=>{r.split(/\s+/).forEach(o=>o&&n.classList.remove(o))}),l.split(/\s+/).forEach(r=>r&&n.classList.add(r)),n.style.display="none";const a=t.nodeType===1?t:t.parentNode;a.appendChild(n);const{hasTransform:i}=Eo(n);return a.removeChild(n),i}const y0=Oe({patchProp:s0},Wp);let ya,Dr=!1;function b0(){return ya=Dr?ya:Lp(y0),Dr=!0,ya}const _0=(...e)=>{const t=b0().createApp(...e),{mount:l}=t;return t.mount=n=>{const a=k0(n);if(a)return l(a,!0,a instanceof SVGElement)},t};function k0(e){return ae(e)?document.querySelector(e):e}const w0={"v-8daa1a0e":()=>g(()=>import("./index.html-593aec50.js"),[]).then(({data:e})=>e),"v-22a39d25":()=>g(()=>import("./about.html-162be796.js"),[]).then(({data:e})=>e),"v-30a50b00":()=>g(()=>import("./a-vuepress2-entertaining-video.html-0e2f1f03.js"),[]).then(({data:e})=>e),"v-0481cc80":()=>g(()=>import("./a-warning-from-navicat.html-a042c9b4.js"),[]).then(({data:e})=>e),"v-79e139f8":()=>g(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-89862497.js"),[]).then(({data:e})=>e),"v-7a74360a":()=>g(()=>import("./claude-ai-in-action-extract-info-from-html.html-454eb9b7.js"),[]).then(({data:e})=>e),"v-76f251d2":()=>g(()=>import("./copy-code-may-not-be-guilty.html-f5f78127.js"),[]).then(({data:e})=>e),"v-f47f129e":()=>g(()=>import("./dont-try-to-argue-with-a-sb.html-7b0b7267.js"),[]).then(({data:e})=>e),"v-fd7b7f92":()=>g(()=>import("./iteration-retrospective-of-sanyuan.html-774f41bd.js"),[]).then(({data:e})=>e),"v-81a71778":()=>g(()=>import("./leverage-ai-to-boost-coding-productivity.html-f7cbca20.js"),[]).then(({data:e})=>e),"v-5c48d497":()=>g(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-9956b49d.js"),[]).then(({data:e})=>e),"v-4f919602":()=>g(()=>import("./testing-environments-should-be-consistent-with-production-environments.html-429ac068.js"),[]).then(({data:e})=>e),"v-1e305501":()=>g(()=>import("./things-I-have-to-vent-about-vue.html-9d87b484.js"),[]).then(({data:e})=>e),"v-404740fa":()=>g(()=>import("./use-claude2-instead-of-chatgpt.html-22369824.js"),[]).then(({data:e})=>e),"v-f1efc11c":()=>g(()=>import("./vim-creator-pass-away.html-d16d8ec0.js"),[]).then(({data:e})=>e),"v-bd2b5fe8":()=>g(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-c6128cbb.js"),[]).then(({data:e})=>e),"v-971cc7fe":()=>g(()=>import("./contemporary-college-english-1.html-d0fabfd1.js"),[]).then(({data:e})=>e),"v-93b316c0":()=>g(()=>import("./contemporary-college-english-2.html-b3e5d0ca.js"),[]).then(({data:e})=>e),"v-90496582":()=>g(()=>import("./contemporary-college-english-3.html-cbd18de0.js"),[]).then(({data:e})=>e),"v-8cdfb444":()=>g(()=>import("./contemporary-college-english-4.html-fe4a2c8d.js"),[]).then(({data:e})=>e),"v-89760306":()=>g(()=>import("./contemporary-college-english-5.html-a86ee847.js"),[]).then(({data:e})=>e),"v-860c51c8":()=>g(()=>import("./contemporary-college-english-6.html-97ed8266.js"),[]).then(({data:e})=>e),"v-246f4f17":()=>g(()=>import("./everyone-can-learn-english-1-overview.html-38f7a62a.js"),[]).then(({data:e})=>e),"v-3ac0474c":()=>g(()=>import("./everyone-can-learn-english-2-pronunciation.html-7684ead6.js"),[]).then(({data:e})=>e),"v-58a409d7":()=>g(()=>import("./everyone-can-learn-english-3-words.html-69cddd91.js"),[]).then(({data:e})=>e),"v-788d194a":()=>g(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-33930ae5.js"),[]).then(({data:e})=>e),"v-ae153a4e":()=>g(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-c9f3798a.js"),[]).then(({data:e})=>e),"v-d42db13c":()=>g(()=>import("./how-to-self-evaluate-english-level.html-3db293dc.js"),[]).then(({data:e})=>e),"v-6ed7d996":()=>g(()=>import("./learning-7000-words-task-completed.html-5cd182e1.js"),[]).then(({data:e})=>e),"v-221efd1f":()=>g(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-7a6652d7.js"),[]).then(({data:e})=>e),"v-3f274907":()=>g(()=>import("./about-arm-things-you-need-to-know.html-978abb78.js"),[]).then(({data:e})=>e),"v-6614b1a1":()=>g(()=>import("./common-solutions-of-object-storage-for-static-assets.html-288ae794.js"),[]).then(({data:e})=>e),"v-2e81d6a4":()=>g(()=>import("./docker-build-and-push-script.html-3e6607f0.js"),[]).then(({data:e})=>e),"v-3c0c097a":()=>g(()=>import("./reduce-python-image-size.html-53326baa.js"),[]).then(({data:e})=>e),"v-daf302c6":()=>g(()=>import("./what-is-the-difference-between-sh-and-bash.html-d2f76510.js"),[]).then(({data:e})=>e),"v-72e84a92":()=>g(()=>import("./old-articles.html-6516a558.js"),[]).then(({data:e})=>e),"v-7320140c":()=>g(()=>import("./performance-optimization-in-action.html-e09ad751.js"),[]).then(({data:e})=>e),"v-1e5872c4":()=>g(()=>import("./git-best-pratices.html-a5f43181.js"),[]).then(({data:e})=>e),"v-48e494ef":()=>g(()=>import("./git-definitive-guide-to-merge-code.html-710385ae.js"),[]).then(({data:e})=>e),"v-60021cbc":()=>g(()=>import("./git-history-two-tricks-in-idea.html-5d7d3044.js"),[]).then(({data:e})=>e),"v-642eaaea":()=>g(()=>import("./git-useful-commands.html-1d5e62e9.js"),[]).then(({data:e})=>e),"v-4008ff77":()=>g(()=>import("./gitlab-ci.html-783ced8a.js"),[]).then(({data:e})=>e),"v-0fbf5fdc":()=>g(()=>import("./rethinking-git-flow.html-59994a2c.js"),[]).then(({data:e})=>e),"v-071be141":()=>g(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-faa5903e.js"),[]).then(({data:e})=>e),"v-86a15cb6":()=>g(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-c9321106.js"),[]).then(({data:e})=>e),"v-6dc18ec6":()=>g(()=>import("./Resolving-Common-Problems-in-Maven.md.html-3d4fa9ab.js"),[]).then(({data:e})=>e),"v-538a98d7":()=>g(()=>import("./avoid-sending-password-in-plaintext.html-eee63490.js"),[]).then(({data:e})=>e),"v-2f6ae09c":()=>g(()=>import("./check-if-name-exists.html-4984a2ab.js"),[]).then(({data:e})=>e),"v-b5a78a7a":()=>g(()=>import("./common-practices-for-handling-excel.html-18fd5108.js"),[]).then(({data:e})=>e),"v-100d1814":()=>g(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-298785a8.js"),[]).then(({data:e})=>e),"v-d767e98e":()=>g(()=>import("./recommend-practices-for-collections-naming-convention.html-41ae727d.js"),[]).then(({data:e})=>e),"v-f5471cb0":()=>g(()=>import("./recommend-practices-for-query-by-date-range.html-9ac25fe6.js"),[]).then(({data:e})=>e),"v-57d9b582":()=>g(()=>import("./recommend-practices-for-writing-good-functions.html-fb82c5c2.js"),[]).then(({data:e})=>e),"v-2ba0b01e":()=>g(()=>import("./using-enum-in-java.html-7c6ed84b.js"),[]).then(({data:e})=>e),"v-386e94f1":()=>g(()=>import("./which-one-is-better-Boolean-or-boolean.html-749fb6a0.js"),[]).then(({data:e})=>e),"v-0d6828c2":()=>g(()=>import("./which-one-is-better-forEach-or-map.html-0aa9847c.js"),[]).then(({data:e})=>e),"v-1aafac08":()=>g(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-e3333c98.js"),[]).then(({data:e})=>e),"v-ca672354":()=>g(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-f5a86225.js"),[]).then(({data:e})=>e),"v-8903fc5e":()=>g(()=>import("./evaluate-llm-app-with-ragas.html-9ec1f483.js"),[]).then(({data:e})=>e),"v-5d5f3b26":()=>g(()=>import("./llm-with-recordation-review-of-regulations.html-46d4f78c.js"),[]).then(({data:e})=>e),"v-143a8bce":()=>g(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-d41bb3be.js"),[]).then(({data:e})=>e),"v-7e2c7a0c":()=>g(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-c5a554fd.js"),[]).then(({data:e})=>e),"v-f297935a":()=>g(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-29a4e446.js"),[]).then(({data:e})=>e),"v-de221860":()=>g(()=>import("./add-logging-for-llm-app.html-6b7e5c8b.js"),[]).then(({data:e})=>e),"v-5b37b3c6":()=>g(()=>import("./export-mysql-table-into-excel.html-90242810.js"),[]).then(({data:e})=>e),"v-966d933e":()=>g(()=>import("./mr.py.html-c2cab192.js"),[]).then(({data:e})=>e),"v-113531b4":()=>g(()=>import("./unit-testing-overview.html-d4b6190c.js"),[]).then(({data:e})=>e),"v-65b23736":()=>g(()=>import("./use-RestAssured-for-api-testing.html-69c1f333.js"),[]).then(({data:e})=>e),"v-c488ac58":()=>g(()=>import("./use-cypress-for-e2e-testing.html-4617f19d.js"),[]).then(({data:e})=>e),"v-efcacba2":()=>g(()=>import("./use-jest-for-test-driven-development.html-5db91cb1.js"),[]).then(({data:e})=>e),"v-0be1af08":()=>g(()=>import("./use-playwright-for-ui-testing.html-62764232.js"),[]).then(({data:e})=>e),"v-75616b85":()=>g(()=>import("./use-postman-for-api-testing.html-2115de7b.js"),[]).then(({data:e})=>e),"v-7ba1021b":()=>g(()=>import("./use-pytest-for-regression-testing-in-llm-app.html-372308ae.js"),[]).then(({data:e})=>e),"v-17809471":()=>g(()=>import("./how-to-connect-to-internet.html-ae1c63f6.js"),[]).then(({data:e})=>e),"v-3706649a":()=>g(()=>import("./404.html-f2d5dd87.js"),[]).then(({data:e})=>e),"v-79c9f96f":()=>g(()=>import("./index.html-6a79c9ec.js"),[]).then(({data:e})=>e),"v-43539db8":()=>g(()=>import("./index.html-8779bfb9.js"),[]).then(({data:e})=>e),"v-71fde78e":()=>g(()=>import("./index.html-ff411f50.js"),[]).then(({data:e})=>e),"v-06198984":()=>g(()=>import("./index.html-6039efb3.js"),[]).then(({data:e})=>e),"v-74473916":()=>g(()=>import("./index.html-b483266e.js"),[]).then(({data:e})=>e),"v-14c69af4":()=>g(()=>import("./index.html-08f7d006.js"),[]).then(({data:e})=>e),"v-7449895b":()=>g(()=>import("./index.html-7ae7db32.js"),[]).then(({data:e})=>e),"v-eb072ff4":()=>g(()=>import("./index.html-bdb5e891.js"),[]).then(({data:e})=>e),"v-63cd5dba":()=>g(()=>import("./index.html-6866fdfe.js"),[]).then(({data:e})=>e),"v-0df55bac":()=>g(()=>import("./index.html-b3692dcf.js"),[]).then(({data:e})=>e),"v-d440f426":()=>g(()=>import("./index.html-aff33757.js"),[]).then(({data:e})=>e),"v-5bc93818":()=>g(()=>import("./index.html-fe376c9d.js"),[]).then(({data:e})=>e),"v-744d024e":()=>g(()=>import("./index.html-7b393b72.js"),[]).then(({data:e})=>e),"v-e52c881c":()=>g(()=>import("./index.html-1bb998c5.js"),[]).then(({data:e})=>e),"v-154dc4c4":()=>g(()=>import("./index.html-fa3db4ea.js"),[]).then(({data:e})=>e),"v-01560935":()=>g(()=>import("./index.html-c98c047d.js"),[]).then(({data:e})=>e),"v-3d5315f8":()=>g(()=>import("./index.html-e4ea5f08.js"),[]).then(({data:e})=>e),"v-1b3ae9cf":()=>g(()=>import("./index.html-db9a5c1a.js"),[]).then(({data:e})=>e),"v-007c0ae2":()=>g(()=>import("./index.html-8811bd65.js"),[]).then(({data:e})=>e),"v-1bee38ca":()=>g(()=>import("./index.html-c50be8cb.js"),[]).then(({data:e})=>e),"v-0da0abf9":()=>g(()=>import("./index.html-90b5caba.js"),[]).then(({data:e})=>e),"v-5a3e80fc":()=>g(()=>import("./index.html-96e262ec.js"),[]).then(({data:e})=>e),"v-01c1de5b":()=>g(()=>import("./index.html-3d1d7f95.js"),[]).then(({data:e})=>e),"v-29350809":()=>g(()=>import("./index.html-fa48630e.js"),[]).then(({data:e})=>e),"v-50d6e023":()=>g(()=>import("./index.html-62782fea.js"),[]).then(({data:e})=>e),"v-0ca0efe6":()=>g(()=>import("./index.html-dc40d5ff.js"),[]).then(({data:e})=>e),"v-51040344":()=>g(()=>import("./index.html-634a0594.js"),[]).then(({data:e})=>e),"v-211f44ee":()=>g(()=>import("./index.html-5fa1740f.js"),[]).then(({data:e})=>e),"v-0da0e901":()=>g(()=>import("./index.html-47577403.js"),[]).then(({data:e})=>e),"v-b309c306":()=>g(()=>import("./index.html-84db287f.js"),[]).then(({data:e})=>e),"v-b3094364":()=>g(()=>import("./index.html-4a669a47.js"),[]).then(({data:e})=>e),"v-245f5676":()=>g(()=>import("./index.html-44e80ad7.js"),[]).then(({data:e})=>e),"v-b310d42a":()=>g(()=>import("./index.html-5b2edaa1.js"),[]).then(({data:e})=>e),"v-13275df4":()=>g(()=>import("./index.html-15cad1de.js"),[]).then(({data:e})=>e),"v-28a1d8bf":()=>g(()=>import("./index.html-c22f0db4.js"),[]).then(({data:e})=>e),"v-60379330":()=>g(()=>import("./index.html-6d3cb5cc.js"),[]).then(({data:e})=>e),"v-3b951558":()=>g(()=>import("./index.html-db761428.js"),[]).then(({data:e})=>e),"v-b30c33a0":()=>g(()=>import("./index.html-fda8af0e.js"),[]).then(({data:e})=>e),"v-48b3e46d":()=>g(()=>import("./index.html-4736b52e.js"),[]).then(({data:e})=>e)},E0=JSON.parse(`{"base":"/","lang":"zh-CN","title":"levy","description":"levy's blog","head":[["meta",{"name":"google-site-verification","content":"XSoaUnV59ACn-fVEvYre2y_5mka_7o_wEoMPBQpwo2M"}],["script",{"async":true,"src":"https://www.googletagmanager.com/gtag/js?id=G-6HEW6B1S6B"}],["script",{},["window.dataLayer = window.dataLayer || [];\\nfunction gtag(){dataLayer.push(arguments);}\\ngtag('js', new Date());\\ngtag('config','G-6HEW6B1S6B');"]],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://levy.vip/rss.xml","title":"levy RSS Feed"}]],"locales":{}}`);var L0=([e,t,l])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,l]),x0=e=>{const t=new Set,l=[];return e.forEach(n=>{const a=L0(n);t.has(a)||(t.add(a),l.push(n))}),l},T0=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,A0=e=>e.startsWith("ftp://"),Zt=e=>/^(https?:)?\/\//.test(e),O0=/.md((\?|#).*)?$/,Bn=(e,t="/")=>!!(Zt(e)||A0(e)||e.startsWith("/")&&!e.startsWith(t)&&!O0.test(e)),Oo=e=>/^mailto:/.test(e),P0=e=>/^tel:/.test(e),Li=e=>Object.prototype.toString.call(e)==="[object Object]",xi=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Po=e=>e[0]==="/"?e.slice(1):e,I0=(e,t)=>{const l=Object.keys(e).sort((n,a)=>{const i=a.split("/").length-n.split("/").length;return i!==0?i:a.length-n.length});for(const n of l)if(t.startsWith(n))return n;return"/"};const Io={"v-8daa1a0e":T(()=>g(()=>import("./index.html-204d82a3.js"),["assets/index.html-204d82a3.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-22a39d25":T(()=>g(()=>import("./about.html-9f67323b.js"),["assets/about.html-9f67323b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-30a50b00":T(()=>g(()=>import("./a-vuepress2-entertaining-video.html-9eef7c60.js"),["assets/a-vuepress2-entertaining-video.html-9eef7c60.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0481cc80":T(()=>g(()=>import("./a-warning-from-navicat.html-0b2038a8.js"),["assets/a-warning-from-navicat.html-0b2038a8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79e139f8":T(()=>g(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-948c7db2.js"),["assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-948c7db2.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7a74360a":T(()=>g(()=>import("./claude-ai-in-action-extract-info-from-html.html-b23660e4.js"),["assets/claude-ai-in-action-extract-info-from-html.html-b23660e4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-76f251d2":T(()=>g(()=>import("./copy-code-may-not-be-guilty.html-8aca485c.js"),["assets/copy-code-may-not-be-guilty.html-8aca485c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f47f129e":T(()=>g(()=>import("./dont-try-to-argue-with-a-sb.html-c362fa80.js"),["assets/dont-try-to-argue-with-a-sb.html-c362fa80.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-fd7b7f92":T(()=>g(()=>import("./iteration-retrospective-of-sanyuan.html-e6a9971f.js"),["assets/iteration-retrospective-of-sanyuan.html-e6a9971f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-81a71778":T(()=>g(()=>import("./leverage-ai-to-boost-coding-productivity.html-306f73df.js"),["assets/leverage-ai-to-boost-coding-productivity.html-306f73df.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5c48d497":T(()=>g(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-59308fd7.js"),["assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-59308fd7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4f919602":T(()=>g(()=>import("./testing-environments-should-be-consistent-with-production-environments.html-1e136f28.js"),["assets/testing-environments-should-be-consistent-with-production-environments.html-1e136f28.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e305501":T(()=>g(()=>import("./things-I-have-to-vent-about-vue.html-569980a1.js"),["assets/things-I-have-to-vent-about-vue.html-569980a1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-404740fa":T(()=>g(()=>import("./use-claude2-instead-of-chatgpt.html-e336cea0.js"),["assets/use-claude2-instead-of-chatgpt.html-e336cea0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f1efc11c":T(()=>g(()=>import("./vim-creator-pass-away.html-62bd3587.js"),["assets/vim-creator-pass-away.html-62bd3587.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-bd2b5fe8":T(()=>g(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-dd070117.js"),["assets/you-dont-need-to-add-tenant_id-to-every-table.html-dd070117.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-971cc7fe":T(()=>g(()=>import("./contemporary-college-english-1.html-5b89cc6a.js"),["assets/contemporary-college-english-1.html-5b89cc6a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-93b316c0":T(()=>g(()=>import("./contemporary-college-english-2.html-8021f470.js"),["assets/contemporary-college-english-2.html-8021f470.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-90496582":T(()=>g(()=>import("./contemporary-college-english-3.html-e7cb754f.js"),["assets/contemporary-college-english-3.html-e7cb754f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-8cdfb444":T(()=>g(()=>import("./contemporary-college-english-4.html-0f930a4d.js"),["assets/contemporary-college-english-4.html-0f930a4d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-89760306":T(()=>g(()=>import("./contemporary-college-english-5.html-3b490327.js"),["assets/contemporary-college-english-5.html-3b490327.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-860c51c8":T(()=>g(()=>import("./contemporary-college-english-6.html-1c25b40f.js"),["assets/contemporary-college-english-6.html-1c25b40f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-246f4f17":T(()=>g(()=>import("./everyone-can-learn-english-1-overview.html-44c5fd6a.js"),["assets/everyone-can-learn-english-1-overview.html-44c5fd6a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3ac0474c":T(()=>g(()=>import("./everyone-can-learn-english-2-pronunciation.html-27d767db.js"),["assets/everyone-can-learn-english-2-pronunciation.html-27d767db.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-58a409d7":T(()=>g(()=>import("./everyone-can-learn-english-3-words.html-25949b28.js"),["assets/everyone-can-learn-english-3-words.html-25949b28.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-788d194a":T(()=>g(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-4c61079e.js"),["assets/everyone-can-learn-english-4-listening-and-speaking.html-4c61079e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ae153a4e":T(()=>g(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-fa801eaa.js"),["assets/everyone-can-learn-english-5-reading-and-writing.html-fa801eaa.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d42db13c":T(()=>g(()=>import("./how-to-self-evaluate-english-level.html-839828a6.js"),["assets/how-to-self-evaluate-english-level.html-839828a6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6ed7d996":T(()=>g(()=>import("./learning-7000-words-task-completed.html-bce0206f.js"),["assets/learning-7000-words-task-completed.html-bce0206f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-221efd1f":T(()=>g(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-058401da.js"),["assets/let-chatgpt-be-your-foreign-language-teacher.html-058401da.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3f274907":T(()=>g(()=>import("./about-arm-things-you-need-to-know.html-8264be5b.js"),["assets/about-arm-things-you-need-to-know.html-8264be5b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6614b1a1":T(()=>g(()=>import("./common-solutions-of-object-storage-for-static-assets.html-5016aa62.js"),["assets/common-solutions-of-object-storage-for-static-assets.html-5016aa62.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2e81d6a4":T(()=>g(()=>import("./docker-build-and-push-script.html-634e2d74.js"),["assets/docker-build-and-push-script.html-634e2d74.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3c0c097a":T(()=>g(()=>import("./reduce-python-image-size.html-5940c01d.js"),["assets/reduce-python-image-size.html-5940c01d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-daf302c6":T(()=>g(()=>import("./what-is-the-difference-between-sh-and-bash.html-f0872766.js"),["assets/what-is-the-difference-between-sh-and-bash.html-f0872766.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-72e84a92":T(()=>g(()=>import("./old-articles.html-68158fb3.js"),["assets/old-articles.html-68158fb3.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7320140c":T(()=>g(()=>import("./performance-optimization-in-action.html-c864ffc7.js"),["assets/performance-optimization-in-action.html-c864ffc7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e5872c4":T(()=>g(()=>import("./git-best-pratices.html-4df1a641.js"),["assets/git-best-pratices.html-4df1a641.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48e494ef":T(()=>g(()=>import("./git-definitive-guide-to-merge-code.html-aa033054.js"),["assets/git-definitive-guide-to-merge-code.html-aa033054.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60021cbc":T(()=>g(()=>import("./git-history-two-tricks-in-idea.html-12d7b14c.js"),["assets/git-history-two-tricks-in-idea.html-12d7b14c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-642eaaea":T(()=>g(()=>import("./git-useful-commands.html-e051d6dd.js"),["assets/git-useful-commands.html-e051d6dd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4008ff77":T(()=>g(()=>import("./gitlab-ci.html-e3af106a.js"),["assets/gitlab-ci.html-e3af106a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0fbf5fdc":T(()=>g(()=>import("./rethinking-git-flow.html-f432c019.js"),["assets/rethinking-git-flow.html-f432c019.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-071be141":T(()=>g(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-0a2b5bae.js"),["assets/use-command-line-tool-to-manage-gitlab-merge-request.html-0a2b5bae.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-86a15cb6":T(()=>g(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-6b396fa0.js"),["assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-6b396fa0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6dc18ec6":T(()=>g(()=>import("./Resolving-Common-Problems-in-Maven.md.html-d159f2ba.js"),["assets/Resolving-Common-Problems-in-Maven.md.html-d159f2ba.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-538a98d7":T(()=>g(()=>import("./avoid-sending-password-in-plaintext.html-0a23e59b.js"),["assets/avoid-sending-password-in-plaintext.html-0a23e59b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2f6ae09c":T(()=>g(()=>import("./check-if-name-exists.html-0118cdf7.js"),["assets/check-if-name-exists.html-0118cdf7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b5a78a7a":T(()=>g(()=>import("./common-practices-for-handling-excel.html-148e90f5.js"),["assets/common-practices-for-handling-excel.html-148e90f5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-100d1814":T(()=>g(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-f9bf0899.js"),["assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9bf0899.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d767e98e":T(()=>g(()=>import("./recommend-practices-for-collections-naming-convention.html-b81f48ef.js"),["assets/recommend-practices-for-collections-naming-convention.html-b81f48ef.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f5471cb0":T(()=>g(()=>import("./recommend-practices-for-query-by-date-range.html-e11d2f0c.js"),["assets/recommend-practices-for-query-by-date-range.html-e11d2f0c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-57d9b582":T(()=>g(()=>import("./recommend-practices-for-writing-good-functions.html-413d3f52.js"),["assets/recommend-practices-for-writing-good-functions.html-413d3f52.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2ba0b01e":T(()=>g(()=>import("./using-enum-in-java.html-a110114b.js"),["assets/using-enum-in-java.html-a110114b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-386e94f1":T(()=>g(()=>import("./which-one-is-better-Boolean-or-boolean.html-98455639.js"),["assets/which-one-is-better-Boolean-or-boolean.html-98455639.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0d6828c2":T(()=>g(()=>import("./which-one-is-better-forEach-or-map.html-c0f0caf5.js"),["assets/which-one-is-better-forEach-or-map.html-c0f0caf5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1aafac08":T(()=>g(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-89359d71.js"),["assets/why-i-prefer-fastjson-instead-of-jackson.html-89359d71.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ca672354":T(()=>g(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-e251503b.js"),["assets/why-is-it-so-hard-to-upgrade-dependencies.html-e251503b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-8903fc5e":T(()=>g(()=>import("./evaluate-llm-app-with-ragas.html-45c81a74.js"),["assets/evaluate-llm-app-with-ragas.html-45c81a74.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5d5f3b26":T(()=>g(()=>import("./llm-with-recordation-review-of-regulations.html-4ba9dc58.js"),["assets/llm-with-recordation-review-of-regulations.html-4ba9dc58.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-143a8bce":T(()=>g(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-fb973b11.js"),["assets/mysql-backup-case-study-mysqldump-in-action.html-fb973b11.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7e2c7a0c":T(()=>g(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-17bba961.js"),["assets/mysql-data-migration-case-study-add-auto-increment.html-17bba961.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f297935a":T(()=>g(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-47e85e35.js"),["assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-47e85e35.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-de221860":T(()=>g(()=>import("./add-logging-for-llm-app.html-7e982a8e.js"),["assets/add-logging-for-llm-app.html-7e982a8e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5b37b3c6":T(()=>g(()=>import("./export-mysql-table-into-excel.html-14d9fea6.js"),["assets/export-mysql-table-into-excel.html-14d9fea6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-966d933e":T(()=>g(()=>import("./mr.py.html-1198e72d.js"),["assets/mr.py.html-1198e72d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-113531b4":T(()=>g(()=>import("./unit-testing-overview.html-8f4834a6.js"),["assets/unit-testing-overview.html-8f4834a6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-65b23736":T(()=>g(()=>import("./use-RestAssured-for-api-testing.html-9aa3329d.js"),["assets/use-RestAssured-for-api-testing.html-9aa3329d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-c488ac58":T(()=>g(()=>import("./use-cypress-for-e2e-testing.html-9e7fedb6.js"),["assets/use-cypress-for-e2e-testing.html-9e7fedb6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-efcacba2":T(()=>g(()=>import("./use-jest-for-test-driven-development.html-be06b9cd.js"),["assets/use-jest-for-test-driven-development.html-be06b9cd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0be1af08":T(()=>g(()=>import("./use-playwright-for-ui-testing.html-893da16f.js"),["assets/use-playwright-for-ui-testing.html-893da16f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-75616b85":T(()=>g(()=>import("./use-postman-for-api-testing.html-2175ffaf.js"),["assets/use-postman-for-api-testing.html-2175ffaf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7ba1021b":T(()=>g(()=>import("./use-pytest-for-regression-testing-in-llm-app.html-14ddbd85.js"),["assets/use-pytest-for-regression-testing-in-llm-app.html-14ddbd85.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-17809471":T(()=>g(()=>import("./how-to-connect-to-internet.html-c6a7d2c0.js"),["assets/how-to-connect-to-internet.html-c6a7d2c0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3706649a":T(()=>g(()=>import("./404.html-a8fb429e.js"),["assets/404.html-a8fb429e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79c9f96f":T(()=>g(()=>import("./index.html-5b00eb2a.js"),["assets/index.html-5b00eb2a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-43539db8":T(()=>g(()=>import("./index.html-ce33adeb.js"),["assets/index.html-ce33adeb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-71fde78e":T(()=>g(()=>import("./index.html-e82db359.js"),["assets/index.html-e82db359.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-06198984":T(()=>g(()=>import("./index.html-c2feef9e.js"),["assets/index.html-c2feef9e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-74473916":T(()=>g(()=>import("./index.html-9e4080be.js"),["assets/index.html-9e4080be.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-14c69af4":T(()=>g(()=>import("./index.html-c023e196.js"),["assets/index.html-c023e196.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7449895b":T(()=>g(()=>import("./index.html-fa943177.js"),["assets/index.html-fa943177.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-eb072ff4":T(()=>g(()=>import("./index.html-55f7638e.js"),["assets/index.html-55f7638e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-63cd5dba":T(()=>g(()=>import("./index.html-9c9d5c68.js"),["assets/index.html-9c9d5c68.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0df55bac":T(()=>g(()=>import("./index.html-1eefb1bb.js"),["assets/index.html-1eefb1bb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d440f426":T(()=>g(()=>import("./index.html-f4dbc26f.js"),["assets/index.html-f4dbc26f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5bc93818":T(()=>g(()=>import("./index.html-e3868ed7.js"),["assets/index.html-e3868ed7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-744d024e":T(()=>g(()=>import("./index.html-890f2f8a.js"),["assets/index.html-890f2f8a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-e52c881c":T(()=>g(()=>import("./index.html-99c4dd42.js"),["assets/index.html-99c4dd42.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-154dc4c4":T(()=>g(()=>import("./index.html-120f4470.js"),["assets/index.html-120f4470.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01560935":T(()=>g(()=>import("./index.html-ccf4e4b1.js"),["assets/index.html-ccf4e4b1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3d5315f8":T(()=>g(()=>import("./index.html-3c69023e.js"),["assets/index.html-3c69023e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1b3ae9cf":T(()=>g(()=>import("./index.html-103d33d4.js"),["assets/index.html-103d33d4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-007c0ae2":T(()=>g(()=>import("./index.html-eda7fbed.js"),["assets/index.html-eda7fbed.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1bee38ca":T(()=>g(()=>import("./index.html-a0bca378.js"),["assets/index.html-a0bca378.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0da0abf9":T(()=>g(()=>import("./index.html-98cb1677.js"),["assets/index.html-98cb1677.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5a3e80fc":T(()=>g(()=>import("./index.html-9ac6dabf.js"),["assets/index.html-9ac6dabf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01c1de5b":T(()=>g(()=>import("./index.html-27179df5.js"),["assets/index.html-27179df5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-29350809":T(()=>g(()=>import("./index.html-53073c6a.js"),["assets/index.html-53073c6a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-50d6e023":T(()=>g(()=>import("./index.html-c3cc9dcf.js"),["assets/index.html-c3cc9dcf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0ca0efe6":T(()=>g(()=>import("./index.html-83502a15.js"),["assets/index.html-83502a15.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-51040344":T(()=>g(()=>import("./index.html-cafd4adb.js"),["assets/index.html-cafd4adb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-211f44ee":T(()=>g(()=>import("./index.html-18766f96.js"),["assets/index.html-18766f96.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0da0e901":T(()=>g(()=>import("./index.html-2a23ac5a.js"),["assets/index.html-2a23ac5a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b309c306":T(()=>g(()=>import("./index.html-fd0e4a7f.js"),["assets/index.html-fd0e4a7f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b3094364":T(()=>g(()=>import("./index.html-17f35239.js"),["assets/index.html-17f35239.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-245f5676":T(()=>g(()=>import("./index.html-93fd52bc.js"),["assets/index.html-93fd52bc.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b310d42a":T(()=>g(()=>import("./index.html-8758c156.js"),["assets/index.html-8758c156.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-13275df4":T(()=>g(()=>import("./index.html-27e28243.js"),["assets/index.html-27e28243.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-28a1d8bf":T(()=>g(()=>import("./index.html-f661c803.js"),["assets/index.html-f661c803.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60379330":T(()=>g(()=>import("./index.html-1e5dd181.js"),["assets/index.html-1e5dd181.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3b951558":T(()=>g(()=>import("./index.html-1461acaf.js"),["assets/index.html-1461acaf.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b30c33a0":T(()=>g(()=>import("./index.html-673c18e7.js"),["assets/index.html-673c18e7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48b3e46d":T(()=>g(()=>import("./index.html-c44e132b.js"),["assets/index.html-c44e132b.js","assets/plugin-vue_export-helper-c27b6911.js"]))};var R0=Symbol(""),C0=W(w0),Ro=Yt({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Dt=W(Ro),ue=()=>Dt,Co=Symbol(""),Ee=()=>{const e=me(Co);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},So=Symbol(""),S0=()=>{const e=me(So);if(!e)throw new Error("usePageHead() is called without provider.");return e},D0=Symbol(""),Do=Symbol(""),Mo=()=>{const e=me(Do);if(!e)throw new Error("usePageLang() is called without provider.");return e},$o=Symbol(""),M0=()=>{const e=me($o);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Ti=Symbol(""),mt=()=>{const e=me(Ti);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},ol=W(E0),Vo=()=>ol,Fo=Symbol(""),rn=()=>{const e=me(Fo);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},$0=Symbol(""),V0="Layout",F0="NotFound",wt=tn({resolveLayouts:e=>e.reduce((t,l)=>({...t,...l.layouts}),{}),resolvePageData:async e=>{const t=C0.value[e];return await(t==null?void 0:t())??Ro},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,l)=>{const n=ae(t.description)?t.description:l.description,a=[...X(t.head)?t.head:[],...l.head,["title",{},e],["meta",{name:"description",content:n}]];return x0(a)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(l=>!!l).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let l;if(e.path){const n=e.frontmatter.layout;ae(n)?l=n:l=V0}else l=F0;return t[l]},resolveRouteLocale:(e,t)=>I0(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),Zn=$({name:"ClientOnly",setup(e,t){const l=W(!1);return ke(()=>{l.value=!0}),()=>{var n,a;return l.value?(a=(n=t.slots).default)==null?void 0:a.call(n):null}}}),No=$({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=ue(),l=E(()=>Io[e.pageKey||t.value.key]);return()=>l.value?s(l.value):s("div","404 Not Found")}}),dt=(e={})=>e,xe=e=>Zt(e)?e:`/${Po(e)}`;const N0={};/*!
+  * vue-router v4.2.4
+  * (c) 2023 Eduardo San Martin Morote
+  * @license MIT
+  */const rl=typeof window<"u";function j0(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ge=Object.assign;function ba(e,t){const l={};for(const n in t){const a=t[n];l[n]=ut(a)?a.map(e):e(a)}return l}const jl=()=>{},ut=Array.isArray,B0=/\/$/,H0=e=>e.replace(B0,"");function _a(e,t,l="/"){let n,a={},i="",r="";const o=t.indexOf("#");let c=t.indexOf("?");return o=0&&(c=-1),c>-1&&(n=t.slice(0,c),i=t.slice(c+1,o>-1?o:t.length),a=e(i)),o>-1&&(n=n||t.slice(0,o),r=t.slice(o,t.length)),n=U0(n??t,l),{fullPath:n+(i&&"?")+i+r,path:n,query:a,hash:r}}function z0(e,t){const l=t.query?e(t.query):"";return t.path+(l&&"?")+l+(t.hash||"")}function Mr(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function q0(e,t,l){const n=t.matched.length-1,a=l.matched.length-1;return n>-1&&n===a&&yl(t.matched[n],l.matched[a])&&jo(t.params,l.params)&&e(t.query)===e(l.query)&&t.hash===l.hash}function yl(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function jo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const l in e)if(!G0(e[l],t[l]))return!1;return!0}function G0(e,t){return ut(e)?$r(e,t):ut(t)?$r(t,e):e===t}function $r(e,t){return ut(t)?e.length===t.length&&e.every((l,n)=>l===t[n]):e.length===1&&e[0]===t}function U0(e,t){if(e.startsWith("/"))return e;if(!e)return t;const l=t.split("/"),n=e.split("/"),a=n[n.length-1];(a===".."||a===".")&&n.push("");let i=l.length-1,r,o;for(r=0;r1&&i--;else break;return l.slice(0,i).join("/")+"/"+n.slice(r-(r===n.length?1:0)).join("/")}var Jl;(function(e){e.pop="pop",e.push="push"})(Jl||(Jl={}));var Bl;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Bl||(Bl={}));function W0(e){if(!e)if(rl){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),H0(e)}const K0=/^[^#]+#/;function J0(e,t){return e.replace(K0,"#")+t}function Q0(e,t){const l=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-l.left-(t.left||0),top:n.top-l.top-(t.top||0)}}const ea=()=>({left:window.pageXOffset,top:window.pageYOffset});function Y0(e){let t;if("el"in e){const l=e.el,n=typeof l=="string"&&l.startsWith("#"),a=typeof l=="string"?n?document.getElementById(l.slice(1)):document.querySelector(l):l;if(!a)return;t=Q0(a,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Vr(e,t){return(history.state?history.state.position-t:-1)+e}const Ba=new Map;function X0(e,t){Ba.set(e,t)}function Z0(e){const t=Ba.get(e);return Ba.delete(e),t}let eh=()=>location.protocol+"//"+location.host;function Bo(e,t){const{pathname:l,search:n,hash:a}=t,i=e.indexOf("#");if(i>-1){let o=a.includes(e.slice(i))?e.slice(i).length:1,c=a.slice(o);return c[0]!=="/"&&(c="/"+c),Mr(c,"")}return Mr(l,e)+n+a}function th(e,t,l,n){let a=[],i=[],r=null;const o=({state:h})=>{const v=Bo(e,location),b=l.value,w=t.value;let L=0;if(h){if(l.value=v,t.value=h,r&&r===b){r=null;return}L=w?h.position-w.position:0}else n(v);a.forEach(m=>{m(l.value,b,{delta:L,type:Jl.pop,direction:L?L>0?Bl.forward:Bl.back:Bl.unknown})})};function c(){r=l.value}function u(h){a.push(h);const v=()=>{const b=a.indexOf(h);b>-1&&a.splice(b,1)};return i.push(v),v}function d(){const{history:h}=window;h.state&&h.replaceState(ge({},h.state,{scroll:ea()}),"")}function p(){for(const h of i)h();i=[],window.removeEventListener("popstate",o),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",o),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:c,listen:u,destroy:p}}function Fr(e,t,l,n=!1,a=!1){return{back:e,current:t,forward:l,replaced:n,position:window.history.length,scroll:a?ea():null}}function lh(e){const{history:t,location:l}=window,n={value:Bo(e,l)},a={value:t.state};a.value||i(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(c,u,d){const p=e.indexOf("#"),h=p>-1?(l.host&&document.querySelector("base")?e:e.slice(p))+c:eh()+e+c;try{t[d?"replaceState":"pushState"](u,"",h),a.value=u}catch(v){console.error(v),l[d?"replace":"assign"](h)}}function r(c,u){const d=ge({},t.state,Fr(a.value.back,c,a.value.forward,!0),u,{position:a.value.position});i(c,d,!0),n.value=c}function o(c,u){const d=ge({},a.value,t.state,{forward:c,scroll:ea()});i(d.current,d,!0);const p=ge({},Fr(n.value,c,null),{position:d.position+1},u);i(c,p,!1),n.value=c}return{location:n,state:a,push:o,replace:r}}function nh(e){e=W0(e);const t=lh(e),l=th(e,t.state,t.location,t.replace);function n(i,r=!0){r||l.pauseListeners(),history.go(i)}const a=ge({location:"",base:e,go:n,createHref:J0.bind(null,e)},t,l);return Object.defineProperty(a,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(a,"state",{enumerable:!0,get:()=>t.state.value}),a}function ah(e){return typeof e=="string"||e&&typeof e=="object"}function Ho(e){return typeof e=="string"||typeof e=="symbol"}const Et={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},zo=Symbol("");var Nr;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Nr||(Nr={}));function bl(e,t){return ge(new Error,{type:e,[zo]:!0},t)}function _t(e,t){return e instanceof Error&&zo in e&&(t==null||!!(e.type&t))}const jr="[^/]+?",ih={sensitive:!1,strict:!1,start:!0,end:!0},rh=/[.+*?^${}()[\]/\\]/g;function sh(e,t){const l=ge({},ih,t),n=[];let a=l.start?"^":"";const i=[];for(const u of e){const d=u.length?[]:[90];l.strict&&!u.length&&(a+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function ch(e,t){let l=0;const n=e.score,a=t.score;for(;l0&&t[t.length-1]<0}const uh={type:0,value:""},dh=/[a-zA-Z0-9_]/;function ph(e){if(!e)return[[]];if(e==="/")return[[uh]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(v){throw new Error(`ERR (${l})/"${u}": ${v}`)}let l=0,n=l;const a=[];let i;function r(){i&&a.push(i),i=[]}let o=0,c,u="",d="";function p(){u&&(l===0?i.push({type:0,value:u}):l===1||l===2||l===3?(i.length>1&&(c==="*"||c==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:u,regexp:d,repeatable:c==="*"||c==="+",optional:c==="*"||c==="?"})):t("Invalid state to consume buffer"),u="")}function h(){u+=c}for(;o{r(_)}:jl}function r(d){if(Ho(d)){const p=n.get(d);p&&(n.delete(d),l.splice(l.indexOf(p),1),p.children.forEach(r),p.alias.forEach(r))}else{const p=l.indexOf(d);p>-1&&(l.splice(p,1),d.record.name&&n.delete(d.record.name),d.children.forEach(r),d.alias.forEach(r))}}function o(){return l}function c(d){let p=0;for(;p=0&&(d.record.path!==l[p].record.path||!qo(d,l[p]));)p++;l.splice(p,0,d),d.record.name&&!zr(d)&&n.set(d.record.name,d)}function u(d,p){let h,v={},b,w;if("name"in d&&d.name){if(h=n.get(d.name),!h)throw bl(1,{location:d});w=h.record.name,v=ge(Hr(p.params,h.keys.filter(_=>!_.optional).map(_=>_.name)),d.params&&Hr(d.params,h.keys.map(_=>_.name))),b=h.stringify(v)}else if("path"in d)b=d.path,h=l.find(_=>_.re.test(b)),h&&(v=h.parse(b),w=h.record.name);else{if(h=p.name?n.get(p.name):l.find(_=>_.re.test(p.path)),!h)throw bl(1,{location:d,currentLocation:p});w=h.record.name,v=ge({},p.params,d.params),b=h.stringify(v)}const L=[];let m=h;for(;m;)L.unshift(m.record),m=m.parent;return{name:w,path:b,params:v,matched:L,meta:mh(L)}}return e.forEach(d=>i(d)),{addRoute:i,resolve:u,removeRoute:r,getRoutes:o,getRecordMatcher:a}}function Hr(e,t){const l={};for(const n of t)n in e&&(l[n]=e[n]);return l}function fh(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:gh(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function gh(e){const t={},l=e.props||!1;if("component"in e)t.default=l;else for(const n in e.components)t[n]=typeof l=="object"?l[n]:l;return t}function zr(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function mh(e){return e.reduce((t,l)=>ge(t,l.meta),{})}function qr(e,t){const l={};for(const n in e)l[n]=n in t?t[n]:e[n];return l}function qo(e,t){return t.children.some(l=>l===e||qo(e,l))}const Go=/#/g,yh=/&/g,bh=/\//g,_h=/=/g,kh=/\?/g,Uo=/\+/g,wh=/%5B/g,Eh=/%5D/g,Wo=/%5E/g,Lh=/%60/g,Ko=/%7B/g,xh=/%7C/g,Jo=/%7D/g,Th=/%20/g;function Ai(e){return encodeURI(""+e).replace(xh,"|").replace(wh,"[").replace(Eh,"]")}function Ah(e){return Ai(e).replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function Ha(e){return Ai(e).replace(Uo,"%2B").replace(Th,"+").replace(Go,"%23").replace(yh,"%26").replace(Lh,"`").replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function Oh(e){return Ha(e).replace(_h,"%3D")}function Ph(e){return Ai(e).replace(Go,"%23").replace(kh,"%3F")}function Ih(e){return e==null?"":Ph(e).replace(bh,"%2F")}function Hn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Rh(e){const t={};if(e===""||e==="?")return t;const n=(e[0]==="?"?e.slice(1):e).split("&");for(let a=0;ai&&Ha(i)):[n&&Ha(n)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+l,i!=null&&(t+="="+i))})}return t}function Ch(e){const t={};for(const l in e){const n=e[l];n!==void 0&&(t[l]=ut(n)?n.map(a=>a==null?null:""+a):n==null?n:""+n)}return t}const Sh=Symbol(""),Ur=Symbol(""),ta=Symbol(""),Oi=Symbol(""),za=Symbol("");function Rl(){let e=[];function t(n){return e.push(n),()=>{const a=e.indexOf(n);a>-1&&e.splice(a,1)}}function l(){e=[]}return{add:t,list:()=>e.slice(),reset:l}}function Mt(e,t,l,n,a){const i=n&&(n.enterCallbacks[a]=n.enterCallbacks[a]||[]);return()=>new Promise((r,o)=>{const c=p=>{p===!1?o(bl(4,{from:l,to:t})):p instanceof Error?o(p):ah(p)?o(bl(2,{from:t,to:p})):(i&&n.enterCallbacks[a]===i&&typeof p=="function"&&i.push(p),r())},u=e.call(n&&n.instances[a],t,l,c);let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch(p=>o(p))})}function ka(e,t,l,n){const a=[];for(const i of e)for(const r in i.components){let o=i.components[r];if(!(t!=="beforeRouteEnter"&&!i.instances[r]))if(Dh(o)){const u=(o.__vccOpts||o)[t];u&&a.push(Mt(u,l,n,i,r))}else{let c=o();a.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${r}" at "${i.path}"`));const d=j0(u)?u.default:u;i.components[r]=d;const h=(d.__vccOpts||d)[t];return h&&Mt(h,l,n,i,r)()}))}}return a}function Dh(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function qa(e){const t=me(ta),l=me(Oi),n=E(()=>t.resolve(it(e.to))),a=E(()=>{const{matched:c}=n.value,{length:u}=c,d=c[u-1],p=l.matched;if(!d||!p.length)return-1;const h=p.findIndex(yl.bind(null,d));if(h>-1)return h;const v=Wr(c[u-2]);return u>1&&Wr(d)===v&&p[p.length-1].path!==v?p.findIndex(yl.bind(null,c[u-2])):h}),i=E(()=>a.value>-1&&Fh(l.params,n.value.params)),r=E(()=>a.value>-1&&a.value===l.matched.length-1&&jo(l.params,n.value.params));function o(c={}){return Vh(c)?t[it(e.replace)?"replace":"push"](it(e.to)).catch(jl):Promise.resolve()}return{route:n,href:E(()=>n.value.href),isActive:i,isExactActive:r,navigate:o}}const Mh=$({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:qa,setup(e,{slots:t}){const l=tn(qa(e)),{options:n}=me(ta),a=E(()=>({[Kr(e.activeClass,n.linkActiveClass,"router-link-active")]:l.isActive,[Kr(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:l.isExactActive}));return()=>{const i=t.default&&t.default(l);return e.custom?i:s("a",{"aria-current":l.isExactActive?e.ariaCurrentValue:null,href:l.href,onClick:l.navigate,class:a.value},i)}}}),$h=Mh;function Vh(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Fh(e,t){for(const l in t){const n=t[l],a=e[l];if(typeof n=="string"){if(n!==a)return!1}else if(!ut(a)||a.length!==n.length||n.some((i,r)=>i!==a[r]))return!1}return!0}function Wr(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Kr=(e,t,l)=>e??t??l,Nh=$({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:l}){const n=me(za),a=E(()=>e.route||n.value),i=me(Ur,0),r=E(()=>{let u=it(i);const{matched:d}=a.value;let p;for(;(p=d[u])&&!p.components;)u++;return u}),o=E(()=>a.value.matched[r.value]);ot(Ur,E(()=>r.value+1)),ot(Sh,o),ot(za,a);const c=W();return ce(()=>[c.value,o.value,e.name],([u,d,p],[h,v,b])=>{d&&(d.instances[p]=u,v&&v!==d&&u&&u===h&&(d.leaveGuards.size||(d.leaveGuards=v.leaveGuards),d.updateGuards.size||(d.updateGuards=v.updateGuards))),u&&d&&(!v||!yl(d,v)||!h)&&(d.enterCallbacks[p]||[]).forEach(w=>w(u))},{flush:"post"}),()=>{const u=a.value,d=e.name,p=o.value,h=p&&p.components[d];if(!h)return Jr(l.default,{Component:h,route:u});const v=p.props[d],b=v?v===!0?u.params:typeof v=="function"?v(u):v:null,L=s(h,ge({},b,t,{onVnodeUnmounted:m=>{m.component.isUnmounted&&(p.instances[d]=null)},ref:c}));return Jr(l.default,{Component:L,route:u})||L}}});function Jr(e,t){if(!e)return null;const l=e(t);return l.length===1?l[0]:l}const Qo=Nh;function jh(e){const t=vh(e.routes,e),l=e.parseQuery||Rh,n=e.stringifyQuery||Gr,a=e.history,i=Rl(),r=Rl(),o=Rl(),c=Ge(Et);let u=Et;rl&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=ba.bind(null,O=>""+O),p=ba.bind(null,Ih),h=ba.bind(null,Hn);function v(O,H){let N,K;return Ho(O)?(N=t.getRecordMatcher(O),K=H):K=O,t.addRoute(K,N)}function b(O){const H=t.getRecordMatcher(O);H&&t.removeRoute(H)}function w(){return t.getRoutes().map(O=>O.record)}function L(O){return!!t.getRecordMatcher(O)}function m(O,H){if(H=ge({},H||c.value),typeof O=="string"){const k=_a(l,O,H.path),x=t.resolve({path:k.path},H),P=a.createHref(k.fullPath);return ge(k,x,{params:h(x.params),hash:Hn(k.hash),redirectedFrom:void 0,href:P})}let N;if("path"in O)N=ge({},O,{path:_a(l,O.path,H.path).path});else{const k=ge({},O.params);for(const x in k)k[x]==null&&delete k[x];N=ge({},O,{params:p(k)}),H.params=p(H.params)}const K=t.resolve(N,H),fe=O.hash||"";K.params=d(h(K.params));const f=z0(n,ge({},O,{hash:Ah(fe),path:K.path})),y=a.createHref(f);return ge({fullPath:f,hash:fe,query:n===Gr?Ch(O.query):O.query||{}},K,{redirectedFrom:void 0,href:y})}function _(O){return typeof O=="string"?_a(l,O,c.value.path):ge({},O)}function I(O,H){if(u!==O)return bl(8,{from:H,to:O})}function R(O){return V(O)}function B(O){return R(ge(_(O),{replace:!0}))}function C(O){const H=O.matched[O.matched.length-1];if(H&&H.redirect){const{redirect:N}=H;let K=typeof N=="function"?N(O):N;return typeof K=="string"&&(K=K.includes("?")||K.includes("#")?K=_(K):{path:K},K.params={}),ge({query:O.query,hash:O.hash,params:"path"in K?{}:O.params},K)}}function V(O,H){const N=u=m(O),K=c.value,fe=O.state,f=O.force,y=O.replace===!0,k=C(N);if(k)return V(ge(_(k),{state:typeof k=="object"?ge({},fe,k.state):fe,force:f,replace:y}),H||N);const x=N;x.redirectedFrom=H;let P;return!f&&q0(n,K,N)&&(P=bl(16,{to:x,from:K}),pt(K,K,!0,!1)),(P?Promise.resolve(P):Y(x,K)).catch(S=>_t(S)?_t(S,2)?S:Ot(S):ve(S,x,K)).then(S=>{if(S){if(_t(S,2))return V(ge({replace:y},_(S.to),{state:typeof S.to=="object"?ge({},fe,S.to.state):fe,force:f}),H||x)}else S=z(x,K,!0,y,fe);return ne(x,K,S),S})}function A(O,H){const N=I(O,H);return N?Promise.reject(N):Promise.resolve()}function q(O){const H=ll.values().next().value;return H&&typeof H.runWithContext=="function"?H.runWithContext(O):O()}function Y(O,H){let N;const[K,fe,f]=Bh(O,H);N=ka(K.reverse(),"beforeRouteLeave",O,H);for(const k of K)k.leaveGuards.forEach(x=>{N.push(Mt(x,O,H))});const y=A.bind(null,O,H);return N.push(y),De(N).then(()=>{N=[];for(const k of i.list())N.push(Mt(k,O,H));return N.push(y),De(N)}).then(()=>{N=ka(fe,"beforeRouteUpdate",O,H);for(const k of fe)k.updateGuards.forEach(x=>{N.push(Mt(x,O,H))});return N.push(y),De(N)}).then(()=>{N=[];for(const k of f)if(k.beforeEnter)if(ut(k.beforeEnter))for(const x of k.beforeEnter)N.push(Mt(x,O,H));else N.push(Mt(k.beforeEnter,O,H));return N.push(y),De(N)}).then(()=>(O.matched.forEach(k=>k.enterCallbacks={}),N=ka(f,"beforeRouteEnter",O,H),N.push(y),De(N))).then(()=>{N=[];for(const k of r.list())N.push(Mt(k,O,H));return N.push(y),De(N)}).catch(k=>_t(k,8)?k:Promise.reject(k))}function ne(O,H,N){o.list().forEach(K=>q(()=>K(O,H,N)))}function z(O,H,N,K,fe){const f=I(O,H);if(f)return f;const y=H===Et,k=rl?history.state:{};N&&(K||y?a.replace(O.fullPath,ge({scroll:y&&k&&k.scroll},fe)):a.push(O.fullPath,fe)),c.value=O,pt(O,H,N,y),Ot()}let ee;function U(){ee||(ee=a.listen((O,H,N)=>{if(!mn.listening)return;const K=m(O),fe=C(K);if(fe){V(ge(fe,{replace:!0}),K).catch(jl);return}u=K;const f=c.value;rl&&X0(Vr(f.fullPath,N.delta),ea()),Y(K,f).catch(y=>_t(y,12)?y:_t(y,2)?(V(y.to,K).then(k=>{_t(k,20)&&!N.delta&&N.type===Jl.pop&&a.go(-1,!1)}).catch(jl),Promise.reject()):(N.delta&&a.go(-N.delta,!1),ve(y,K,f))).then(y=>{y=y||z(K,f,!1),y&&(N.delta&&!_t(y,8)?a.go(-N.delta,!1):N.type===Jl.pop&&_t(y,20)&&a.go(-1,!1)),ne(K,f,y)}).catch(jl)}))}let Se=Rl(),he=Rl(),_e;function ve(O,H,N){Ot(O);const K=he.list();return K.length?K.forEach(fe=>fe(O,H,N)):console.error(O),Promise.reject(O)}function bt(){return _e&&c.value!==Et?Promise.resolve():new Promise((O,H)=>{Se.add([O,H])})}function Ot(O){return _e||(_e=!O,U(),Se.list().forEach(([H,N])=>O?N(O):H()),Se.reset()),O}function pt(O,H,N,K){const{scrollBehavior:fe}=e;if(!rl||!fe)return Promise.resolve();const f=!N&&Z0(Vr(O.fullPath,0))||(K||!N)&&history.state&&history.state.scroll||null;return Tl().then(()=>fe(O,H,f)).then(y=>y&&Y0(y)).catch(y=>ve(y,O,H))}const je=O=>a.go(O);let tl;const ll=new Set,mn={currentRoute:c,listening:!0,addRoute:v,removeRoute:b,hasRoute:L,getRoutes:w,resolve:m,options:e,push:R,replace:B,go:je,back:()=>je(-1),forward:()=>je(1),beforeEach:i.add,beforeResolve:r.add,afterEach:o.add,onError:he.add,isReady:bt,install(O){const H=this;O.component("RouterLink",$h),O.component("RouterView",Qo),O.config.globalProperties.$router=H,Object.defineProperty(O.config.globalProperties,"$route",{enumerable:!0,get:()=>it(c)}),rl&&!tl&&c.value===Et&&(tl=!0,R(a.location).catch(fe=>{}));const N={};for(const fe in Et)Object.defineProperty(N,fe,{get:()=>c.value[fe],enumerable:!0});O.provide(ta,H),O.provide(Oi,js(N)),O.provide(za,c);const K=O.unmount;ll.add(O),O.unmount=function(){ll.delete(O),ll.size<1&&(u=Et,ee&&ee(),ee=null,c.value=Et,tl=!1,_e=!1),K()}}};function De(O){return O.reduce((H,N)=>H.then(()=>q(N)),Promise.resolve())}return mn}function Bh(e,t){const l=[],n=[],a=[],i=Math.max(t.matched.length,e.matched.length);for(let r=0;ryl(u,o))?n.push(o):l.push(o));const c=e.matched[r];c&&(t.matched.find(u=>yl(u,c))||a.push(c))}return[l,n,a]}function Ve(){return me(ta)}function yt(){return me(Oi)}const de=({name:e="",color:t="currentColor"},{slots:l})=>{var n;return s("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(n=l.default)==null?void 0:n.call(l))};de.displayName="IconBase";const Yo=({size:e=48,stroke:t=4,wrapper:l=!0,height:n=2*e})=>{const a=s("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[s("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),s("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[s("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),s("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return l?s("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${n}px`},a):a};Yo.displayName="LoadingIcon";const Xo=(e,{slots:t})=>{var l;return(l=t.default)==null?void 0:l.call(t)},Pi=(e="")=>{if(e){if(typeof e=="number")return new Date(e);const t=Date.parse(e.toString());if(!Number.isNaN(t))return new Date(t)}return null},la=(e,t)=>{let l=1;for(let n=0;n>6;return l+=l<<3,l^=l>>11,l%t},Zo=Array.isArray,Hh=e=>typeof e=="function",zh=e=>typeof e=="string";var qh=e=>e.startsWith("ftp://"),Ii=e=>/^(https?:)?\/\//.test(e),Gh=/.md((\?|#).*)?$/,Uh=(e,t="/")=>!!(Ii(e)||qh(e)||e.startsWith("/")&&!e.startsWith(t)&&!Gh.test(e)),ec=e=>Object.prototype.toString.call(e)==="[object Object]";function Wh(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function Kh(e){return Wh(),E(()=>!!e())}const Tt=e=>typeof e=="string",Ql=(e,t)=>Tt(e)&&e.startsWith(t),il=(e,t)=>Tt(e)&&e.endsWith(t),sn=Object.entries,Jh=Object.fromEntries,gt=Object.keys,Qh=e=>(e.endsWith(".md")&&(e=`${e.slice(0,-3)}.html`),!e.endsWith("/")&&!e.endsWith(".html")&&(e=`${e}.html`),e=e.replace(/(^|\/)(?:README|index).html$/i,"$1"),e),tc=e=>{const[t,l=""]=e.split("#");return t?`${Qh(t)}${l?`#${l}`:""}`:e},Qr=e=>ec(e)&&Tt(e.name),Yl=(e,t=!1)=>e?Zo(e)?e.map(l=>Tt(l)?{name:l}:Qr(l)?l:null).filter(l=>l!==null):Tt(e)?[{name:e}]:Qr(e)?[e]:(console.error(`Expect "author" to be \`AuthorInfo[] | AuthorInfo | string[] | string ${t?"":"| false"} | undefined\`, but got`,e),[]):[],lc=(e,t)=>{if(e){if(Zo(e)&&e.every(Tt))return e;if(Tt(e))return[e];console.error(`Expect ${t||"value"} to be \`string[] | string | undefined\`, but got`,e)}return[]},nc=e=>lc(e,"category"),ac=e=>lc(e,"tag"),na=e=>Ql(e,"/");let Yh=class{constructor(){oa(this,"containerElement");oa(this,"messageElements",{});const t="message-container",l=document.getElementById(t);l?this.containerElement=l:(this.containerElement=document.createElement("div"),this.containerElement.id=t,document.body.appendChild(this.containerElement))}pop(t,l=2e3){const n=document.createElement("div"),a=Date.now();return n.className="message move-in",n.innerHTML=t,this.containerElement.appendChild(n),this.messageElements[a]=n,l>0&&setTimeout(()=>{this.close(a)},l),a}close(t){if(t){const l=this.messageElements[t];l.classList.remove("move-in"),l.classList.add("move-out"),l.addEventListener("animationend",()=>{l.remove(),delete this.messageElements[t]})}else gt(this.messageElements).forEach(l=>this.close(Number(l)))}destroy(){document.body.removeChild(this.containerElement)}};const ic=/#.*$/u,Xh=e=>{const t=ic.exec(e);return t?t[0]:""},Yr=e=>decodeURI(e).replace(ic,"").replace(/(index)?\.(md|html)$/,""),Ri=(e,t)=>{if(t===void 0)return!1;const l=Yr(e.path),n=Yr(t),a=Xh(t);return a?a===e.hash&&(!n||l===n):l===n},Zh=e=>Ii(e)?e:`https://github.com/${e}`,rc=e=>!Ii(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,_l=(e,...t)=>{const l=e.resolve(...t),n=l.matched[l.matched.length-1];if(!(n!=null&&n.redirect))return l;const{redirect:a}=n,i=Hh(a)?a(l):a,r=zh(i)?{path:i}:i;return _l(e,{hash:l.hash,query:l.query,params:l.params,...r})},e1=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},Ce=({to:e=""},{slots:t})=>{var l;const n=Ve(),a=(i={})=>e1(i)?n.push(e).catch():Promise.resolve();return s("a",{class:"vp-link",href:xe(tc(e)),onClick:a},(l=t.default)==null?void 0:l.call(t))};Ce.displayName="VPLink";const sc=()=>s(de,{name:"github"},()=>s("path",{d:"M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"}));sc.displayName="GitHubIcon";const oc=()=>s(de,{name:"gitlab"},()=>s("path",{d:"M229.333 78.688C223.52 62 199.895 62 193.895 78.688L87.958 406.438h247.5c-.188 0-106.125-327.75-106.125-327.75zM33.77 571.438c-4.875 15 .563 31.687 13.313 41.25l464.812 345L87.77 406.438zm301.5-165 176.813 551.25 176.812-551.25zm655.125 165-54-165-424.312 551.25 464.812-345c12.938-9.563 18.188-26.25 13.5-41.25zM830.27 78.688c-5.812-16.688-29.437-16.688-35.437 0l-106.125 327.75h247.5z"}));oc.displayName="GitLabIcon";const cc=()=>s(de,{name:"gitee"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"}));cc.displayName="GiteeIcon";const uc=()=>s(de,{name:"bitbucket"},()=>s("path",{d:"M575.256 490.862c6.29 47.981-52.005 85.723-92.563 61.147-45.714-20.004-45.714-92.562-1.133-113.152 38.29-23.442 93.696 7.424 93.696 52.005zm63.451-11.996c-10.276-81.152-102.29-134.839-177.152-101.156-47.433 21.138-79.433 71.424-77.129 124.562 2.853 69.705 69.157 126.866 138.862 120.576S647.3 548.571 638.708 478.83zm136.558-309.723c-25.161-33.134-67.986-38.839-105.728-45.13-106.862-17.151-216.576-17.7-323.438 1.134-35.438 5.706-75.447 11.996-97.719 43.996 36.572 34.304 88.576 39.424 135.424 45.129 84.553 10.862 171.447 11.447 256 .585 47.433-5.705 99.987-10.276 135.424-45.714zm32.585 591.433c-16.018 55.99-6.839 131.438-66.304 163.986-102.29 56.576-226.304 62.867-338.87 42.862-59.43-10.862-129.135-29.696-161.72-85.723-14.3-54.858-23.442-110.848-32.585-166.84l3.438-9.142 10.276-5.157c170.277 112.567 408.576 112.567 579.438 0 26.844 8.01 6.84 40.558 6.29 60.014zm103.424-549.157c-19.42 125.148-41.728 249.71-63.415 374.272-6.29 36.572-41.728 57.162-71.424 72.558-106.862 53.724-231.424 62.866-348.562 50.286-79.433-8.558-160.585-29.696-225.134-79.433-30.28-23.443-30.28-63.415-35.986-97.134-20.005-117.138-42.862-234.277-57.161-352.585 6.839-51.42 64.585-73.728 107.447-89.71 57.16-21.138 118.272-30.866 178.87-36.571 129.134-12.58 261.157-8.01 386.304 28.562 44.581 13.13 92.563 31.415 122.844 69.705 13.714 17.7 9.143 40.01 6.29 60.014z"}));uc.displayName="BitbucketIcon";const dc=()=>s(de,{name:"source"},()=>s("path",{d:"M601.92 475.2c0 76.428-8.91 83.754-28.512 99.594-14.652 11.88-43.956 14.058-78.012 16.434-18.81 1.386-40.392 2.97-62.172 6.534-18.612 2.97-36.432 9.306-53.064 17.424V299.772c37.818-21.978 63.36-62.766 63.36-109.692 0-69.894-56.826-126.72-126.72-126.72S190.08 120.186 190.08 190.08c0 46.926 25.542 87.714 63.36 109.692v414.216c-37.818 21.978-63.36 62.766-63.36 109.692 0 69.894 56.826 126.72 126.72 126.72s126.72-56.826 126.72-126.72c0-31.086-11.286-59.598-29.7-81.576 13.266-9.504 27.522-17.226 39.996-19.206 16.038-2.574 32.868-3.762 50.688-5.148 48.312-3.366 103.158-7.326 148.896-44.55 61.182-49.698 74.25-103.158 75.24-187.902V475.2h-126.72zM316.8 126.72c34.848 0 63.36 28.512 63.36 63.36s-28.512 63.36-63.36 63.36-63.36-28.512-63.36-63.36 28.512-63.36 63.36-63.36zm0 760.32c-34.848 0-63.36-28.512-63.36-63.36s28.512-63.36 63.36-63.36 63.36 28.512 63.36 63.36-28.512 63.36-63.36 63.36zM823.68 158.4h-95.04V63.36h-126.72v95.04h-95.04v126.72h95.04v95.04h126.72v-95.04h95.04z"}));dc.displayName="SourceIcon";const ct=(e,t)=>{const l=t?t._instance:Xt();return ec(l==null?void 0:l.appContext.components)&&(e in l.appContext.components||tt(e)in l.appContext.components||en(tt(e))in l.appContext.components)},t1=()=>Kh(()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator),l1=()=>{const e=t1();return E(()=>e.value&&/\b(?:Android|iPhone)/i.test(navigator.userAgent))},on=e=>{const t=mt();return E(()=>e[t.value])};var n1=Object.defineProperty,a1=Object.defineProperties,i1=Object.getOwnPropertyDescriptors,Xr=Object.getOwnPropertySymbols,r1=Object.prototype.hasOwnProperty,s1=Object.prototype.propertyIsEnumerable,Zr=(e,t,l)=>t in e?n1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,o1=(e,t)=>{for(var l in t||(t={}))r1.call(t,l)&&Zr(e,l,t[l]);if(Xr)for(var l of Xr(t))s1.call(t,l)&&Zr(e,l,t[l]);return e},c1=(e,t)=>a1(e,i1(t));function es(e,t){var l;const n=Ge();return Xs(()=>{n.value=e()},c1(o1({},t),{flush:(l=t==null?void 0:t.flush)!=null?l:"sync"})),Yt(n)}function Al(e){return Ps()?(td(e),!0):!1}function He(e){return typeof e=="function"?e():it(e)}const cn=typeof window<"u",Xl=()=>{},ts=u1();function u1(){var e;return cn&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent)}function pc(e,t){function l(...n){return new Promise((a,i)=>{Promise.resolve(e(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(a).catch(i)})}return l}const hc=e=>e();function d1(e,t=!0,l=!0,n=!1){let a=0,i,r=!0,o=Xl,c;const u=()=>{i&&(clearTimeout(i),i=void 0,o(),o=Xl)};return p=>{const h=He(e),v=Date.now()-a,b=()=>c=p();return u(),h<=0?(a=Date.now(),b()):(v>h&&(l||!r)?(a=Date.now(),b()):t&&(c=new Promise((w,L)=>{o=n?L:w,i=setTimeout(()=>{a=Date.now(),r=!0,w(b()),u()},Math.max(0,h-v))})),!l&&!i&&(i=setTimeout(()=>r=!0,h)),r=!1,c)}}function p1(e=hc){const t=W(!0);function l(){t.value=!1}function n(){t.value=!0}const a=(...i)=>{t.value&&e(...i)};return{isActive:Yt(t),pause:l,resume:n,eventFilter:a}}function h1(...e){if(e.length!==1)return xl(...e);const t=e[0];return typeof t=="function"?Yt(Cd(()=>({get:t,set:Xl}))):W(t)}function v1(e,t=200,l=!1,n=!0,a=!1){return pc(d1(t,l,n,a),e)}function vc(e,t=!0){Xt()?ke(e):t?e():Tl(e)}function f1(e){Xt()&&an(e)}function g1(e,t,l={}){const{immediate:n=!0}=l,a=W(!1);let i=null;function r(){i&&(clearTimeout(i),i=null)}function o(){a.value=!1,r()}function c(...u){r(),a.value=!0,i=setTimeout(()=>{a.value=!1,i=null,e(...u)},He(t))}return n&&(a.value=!0,cn&&c()),Al(o),{isPending:Yt(a),start:c,stop:o}}function ls(e=!1,t={}){const{truthyValue:l=!0,falsyValue:n=!1}=t,a=Ie(e),i=W(e);function r(o){if(arguments.length)return i.value=o,i.value;{const c=He(l);return i.value=i.value===c?He(n):c,i.value}}return a?r:[i,r]}var ns=Object.getOwnPropertySymbols,m1=Object.prototype.hasOwnProperty,y1=Object.prototype.propertyIsEnumerable,b1=(e,t)=>{var l={};for(var n in e)m1.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&ns)for(var n of ns(e))t.indexOf(n)<0&&y1.call(e,n)&&(l[n]=e[n]);return l};function _1(e,t,l={}){const n=l,{eventFilter:a=hc}=n,i=b1(n,["eventFilter"]);return ce(e,pc(a,t),i)}var k1=Object.defineProperty,w1=Object.defineProperties,E1=Object.getOwnPropertyDescriptors,zn=Object.getOwnPropertySymbols,fc=Object.prototype.hasOwnProperty,gc=Object.prototype.propertyIsEnumerable,as=(e,t,l)=>t in e?k1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,L1=(e,t)=>{for(var l in t||(t={}))fc.call(t,l)&&as(e,l,t[l]);if(zn)for(var l of zn(t))gc.call(t,l)&&as(e,l,t[l]);return e},x1=(e,t)=>w1(e,E1(t)),T1=(e,t)=>{var l={};for(var n in e)fc.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&zn)for(var n of zn(e))t.indexOf(n)<0&&gc.call(e,n)&&(l[n]=e[n]);return l};function A1(e,t,l={}){const n=l,{eventFilter:a}=n,i=T1(n,["eventFilter"]),{eventFilter:r,pause:o,resume:c,isActive:u}=p1(a);return{stop:_1(e,t,x1(L1({},i),{eventFilter:r})),pause:o,resume:c,isActive:u}}function Ft(e){var t;const l=He(e);return(t=l==null?void 0:l.$el)!=null?t:l}const Ht=cn?window:void 0,mc=cn?window.document:void 0,O1=cn?window.navigator:void 0;function $e(...e){let t,l,n,a;if(typeof e[0]=="string"||Array.isArray(e[0])?([l,n,a]=e,t=Ht):[t,l,n,a]=e,!t)return Xl;Array.isArray(l)||(l=[l]),Array.isArray(n)||(n=[n]);const i=[],r=()=>{i.forEach(d=>d()),i.length=0},o=(d,p,h,v)=>(d.addEventListener(p,h,v),()=>d.removeEventListener(p,h,v)),c=ce(()=>[Ft(t),He(a)],([d,p])=>{r(),d&&i.push(...l.flatMap(h=>n.map(v=>o(d,h,v,p))))},{immediate:!0,flush:"post"}),u=()=>{c(),r()};return Al(u),u}function P1(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function aa(e){const t=P1();return E(()=>(t.value,!!e()))}function yc(e,t={}){const{window:l=Ht}=t,n=aa(()=>l&&"matchMedia"in l&&typeof l.matchMedia=="function");let a;const i=W(!1),r=u=>{i.value=u.matches},o=()=>{a&&("removeEventListener"in a?a.removeEventListener("change",r):a.removeListener(r))},c=Xs(()=>{n.value&&(o(),a=l.matchMedia(He(e)),"addEventListener"in a?a.addEventListener("change",r):a.addListener(r),i.value=a.matches)});return Al(()=>{c(),o(),a=void 0}),i}function I1(e={}){const{navigator:t=O1,read:l=!1,source:n,copiedDuring:a=1500,legacy:i=!1}=e,r=aa(()=>t&&"clipboard"in t),o=E(()=>r.value||i),c=W(""),u=W(!1),d=g1(()=>u.value=!1,a);function p(){r.value?t.clipboard.readText().then(w=>{c.value=w}):c.value=b()}o.value&&l&&$e(["copy","cut"],p);async function h(w=He(n)){o.value&&w!=null&&(r.value?await t.clipboard.writeText(w):v(w),c.value=w,u.value=!0,d.start())}function v(w){const L=document.createElement("textarea");L.value=w??"",L.style.position="absolute",L.style.opacity="0",document.body.appendChild(L),L.select(),document.execCommand("copy"),L.remove()}function b(){var w,L,m;return(m=(L=(w=document==null?void 0:document.getSelection)==null?void 0:w.call(document))==null?void 0:L.toString())!=null?m:""}return{isSupported:o,text:c,copied:u,copy:h}}const Tn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},An="__vueuse_ssr_handlers__",R1=C1();function C1(){return An in Tn||(Tn[An]=Tn[An]||{}),Tn[An]}function S1(e,t){return R1[e]||t}function D1(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}var M1=Object.defineProperty,is=Object.getOwnPropertySymbols,$1=Object.prototype.hasOwnProperty,V1=Object.prototype.propertyIsEnumerable,rs=(e,t,l)=>t in e?M1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,ss=(e,t)=>{for(var l in t||(t={}))$1.call(t,l)&&rs(e,l,t[l]);if(is)for(var l of is(t))V1.call(t,l)&&rs(e,l,t[l]);return e};const F1={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},os="vueuse-storage";function Ci(e,t,l,n={}){var a;const{flush:i="pre",deep:r=!0,listenToStorageChanges:o=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:p=Ht,eventFilter:h,onError:v=A=>{console.error(A)}}=n,b=(d?Ge:W)(t);if(!l)try{l=S1("getDefaultStorage",()=>{var A;return(A=Ht)==null?void 0:A.localStorage})()}catch(A){v(A)}if(!l)return b;const w=He(t),L=D1(w),m=(a=n.serializer)!=null?a:F1[L],{pause:_,resume:I}=A1(b,()=>R(b.value),{flush:i,deep:r,eventFilter:h});return p&&o&&($e(p,"storage",V),$e(p,os,C)),V(),b;function R(A){try{if(A==null)l.removeItem(e);else{const q=m.write(A),Y=l.getItem(e);Y!==q&&(l.setItem(e,q),p&&p.dispatchEvent(new CustomEvent(os,{detail:{key:e,oldValue:Y,newValue:q,storageArea:l}})))}}catch(q){v(q)}}function B(A){const q=A?A.newValue:l.getItem(e);if(q==null)return c&&w!==null&&l.setItem(e,m.write(w)),w;if(!A&&u){const Y=m.read(q);return typeof u=="function"?u(Y,w):L==="object"&&!Array.isArray(Y)?ss(ss({},w),Y):Y}else return typeof q!="string"?q:m.read(q)}function C(A){V(A.detail)}function V(A){if(!(A&&A.storageArea!==l)){if(A&&A.key==null){b.value=w;return}if(!(A&&A.key!==e)){_();try{b.value=B(A)}catch(q){v(q)}finally{A?Tl(I):I()}}}}}function N1(e){return yc("(prefers-color-scheme: dark)",e)}var cs=Object.getOwnPropertySymbols,j1=Object.prototype.hasOwnProperty,B1=Object.prototype.propertyIsEnumerable,H1=(e,t)=>{var l={};for(var n in e)j1.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&cs)for(var n of cs(e))t.indexOf(n)<0&&B1.call(e,n)&&(l[n]=e[n]);return l};function z1(e,t,l={}){const n=l,{window:a=Ht}=n,i=H1(n,["window"]);let r;const o=aa(()=>a&&"ResizeObserver"in a),c=()=>{r&&(r.disconnect(),r=void 0)},u=E(()=>Array.isArray(e)?e.map(h=>Ft(h)):[Ft(e)]),d=ce(u,h=>{if(c(),o.value&&a){r=new ResizeObserver(t);for(const v of h)v&&r.observe(v,i)}},{immediate:!0,flush:"post",deep:!0}),p=()=>{c(),d()};return Al(p),{isSupported:o,stop:p}}function q1(e,t={width:0,height:0},l={}){const{window:n=Ht,box:a="content-box"}=l,i=E(()=>{var c,u;return(u=(c=Ft(e))==null?void 0:c.namespaceURI)==null?void 0:u.includes("svg")}),r=W(t.width),o=W(t.height);return z1(e,([c])=>{const u=a==="border-box"?c.borderBoxSize:a==="content-box"?c.contentBoxSize:c.devicePixelContentBoxSize;if(n&&i.value){const d=Ft(e);if(d){const p=n.getComputedStyle(d);r.value=Number.parseFloat(p.width),o.value=Number.parseFloat(p.height)}}else if(u){const d=Array.isArray(u)?u:[u];r.value=d.reduce((p,{inlineSize:h})=>p+h,0),o.value=d.reduce((p,{blockSize:h})=>p+h,0)}else r.value=c.contentRect.width,o.value=c.contentRect.height},l),ce(()=>Ft(e),c=>{r.value=c?t.width:0,o.value=c?t.height:0}),{width:r,height:o}}const us=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function Si(e,t={}){const{document:l=mc,autoExit:n=!1}=t,a=E(()=>{var m;return(m=Ft(e))!=null?m:l==null?void 0:l.querySelector("html")}),i=W(!1),r=E(()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find(m=>l&&m in l||a.value&&m in a.value)),o=E(()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find(m=>l&&m in l||a.value&&m in a.value)),c=E(()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find(m=>l&&m in l||a.value&&m in a.value)),u=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find(m=>l&&m in l),d=aa(()=>a.value&&l&&r.value!==void 0&&o.value!==void 0&&c.value!==void 0),p=()=>u?(l==null?void 0:l[u])===a.value:!1,h=()=>{if(c.value){if(l&&l[c.value]!=null)return l[c.value];{const m=a.value;if((m==null?void 0:m[c.value])!=null)return!!m[c.value]}}return!1};async function v(){if(!(!d.value||!i.value)){if(o.value)if((l==null?void 0:l[o.value])!=null)await l[o.value]();else{const m=a.value;(m==null?void 0:m[o.value])!=null&&await m[o.value]()}i.value=!1}}async function b(){if(!d.value||i.value)return;h()&&await v();const m=a.value;r.value&&(m==null?void 0:m[r.value])!=null&&(await m[r.value](),i.value=!0)}async function w(){await(i.value?v():b())}const L=()=>{const m=h();(!m||m&&p())&&(i.value=m)};return $e(l,us,L,!1),$e(()=>Ft(a),us,L,!1),n&&Al(v),{isSupported:d,isFullscreen:i,enter:b,exit:v,toggle:w}}function wa(e,t=Xl,l={}){const{immediate:n=!0,manual:a=!1,type:i="text/javascript",async:r=!0,crossOrigin:o,referrerPolicy:c,noModule:u,defer:d,document:p=mc,attrs:h={}}=l,v=W(null);let b=null;const w=_=>new Promise((I,R)=>{const B=A=>(v.value=A,I(A),A);if(!p){I(!1);return}let C=!1,V=p.querySelector(`script[src="${He(e)}"]`);V?V.hasAttribute("data-loaded")&&B(V):(V=p.createElement("script"),V.type=i,V.async=r,V.src=He(e),d&&(V.defer=d),o&&(V.crossOrigin=o),u&&(V.noModule=u),c&&(V.referrerPolicy=c),Object.entries(h).forEach(([A,q])=>V==null?void 0:V.setAttribute(A,q)),C=!0),V.addEventListener("error",A=>R(A)),V.addEventListener("abort",A=>R(A)),V.addEventListener("load",()=>{V.setAttribute("data-loaded","true"),t(V),B(V)}),C&&(V=p.head.appendChild(V)),_||B(V)}),L=(_=!0)=>(b||(b=w(_)),b),m=()=>{if(!p)return;b=null,v.value&&(v.value=null);const _=p.querySelector(`script[src="${He(e)}"]`);_&&p.head.removeChild(_)};return n&&!a&&vc(L),a||f1(m),{scriptTag:v,load:L,unload:m}}function bc(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}function _c(e,t=!1){const l=W(t);let n=null,a;ce(h1(e),o=>{if(o){const c=o;a=c.style.overflow,l.value&&(c.style.overflow="hidden")}},{immediate:!0});const i=()=>{const o=He(e);!o||l.value||(ts&&(n=$e(o,"touchmove",c=>{G1(c)},{passive:!1})),o.style.overflow="hidden",l.value=!0)},r=()=>{const o=He(e);!o||!l.value||(ts&&(n==null||n()),o.style.overflow=a,l.value=!1)};return Al(r),E({get(){return l.value},set(o){o?i():r()}})}function U1({window:e=Ht}={}){if(!e)return{x:W(0),y:W(0)};const t=W(e.scrollX),l=W(e.scrollY);return $e(e,"scroll",()=>{t.value=e.scrollX,l.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:l}}function W1(e={}){const{window:t=Ht,initialWidth:l=Number.POSITIVE_INFINITY,initialHeight:n=Number.POSITIVE_INFINITY,listenOrientation:a=!0,includeScrollbar:i=!0}=e,r=W(l),o=W(n),c=()=>{t&&(i?(r.value=t.innerWidth,o.value=t.innerHeight):(r.value=t.document.documentElement.clientWidth,o.value=t.document.documentElement.clientHeight))};if(c(),vc(c),$e("resize",c,{passive:!0}),a){const u=yc("(orientation: portrait)");ce(u,()=>c())}return{width:r,height:o}}var K1=$({name:"FontIcon",props:{icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""}},setup(e){const t=E(()=>{const n=["font-icon icon"],a=`fas fa-${e.icon}`;return n.push("fa-fw fa-sm"),n.push(e.icon.includes(" ")?e.icon:a),n}),l=E(()=>{const n={};return e.color&&(n.color=e.color),e.size&&(n["font-size"]=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),gt(n).length?n:null});return()=>e.icon?s("span",{key:e.icon,class:t.value,style:l.value}):null}});const J1="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture",ds=e=>ae(e)?e:`${e}px`,Q1=(e,t=0)=>{const l=Ge(),n=E(()=>ds(it(e.width)||"100%")),a=W("auto"),i=c=>{if(ae(c)){const[u,d]=c.split(":"),p=Number(u)/Number(d);if(!Number.isNaN(p))return p}return typeof c=="number"?c:16/9},r=c=>{const u=it(e.height),d=i(it(e.ratio));return u?ds(u):`${Number(c)/d+it(t)}px`},o=()=>{l.value&&(a.value=r(l.value.clientWidth))};return ke(()=>{o(),Ie(t)&&ce(t,()=>o()),$e("orientationchange",()=>o()),$e("resize",()=>o())}),{el:l,width:n,height:a}},ps="https://player.bilibili.com/player.html";var Y1=$({name:"BiliBili",props:{bvid:{type:String,default:""},aid:{type:String,default:""},cid:{type:String,default:""},title:{type:String,default:"A BiliBili video"},page:{type:[String,Number],default:1},width:{type:[String,Number],default:"100%"},height:{type:[String,Number],default:void 0},ratio:{type:[String,Number],default:16/9},time:{type:[String,Number],default:0},autoplay:Boolean},setup(e){const{el:t,width:l,height:n}=Q1(e),a=W(!1),i=E(()=>{const{aid:r,bvid:o,cid:c,autoplay:u,time:d,page:p}=e;return r&&c?`${ps}?aid=${r}&cid=${c}&t=${d}&autoplay=${u?1:0}&page=${p}`:o?`${ps}?bvid=${o}&t=${d}&autoplay=${u?1:0}`:null});return()=>i.value?[s("div",{class:"bilibili-desc"},s("a",{class:"sr-only",href:i.value},e.title)),s("iframe",{ref:t,src:i.value,title:e.title,class:"bilibili-iframe",allow:J1,style:{width:l.value,height:a.value?n.value:0},onLoad:()=>{a.value=!0}}),a.value?null:s(Yo)]:[]}});const kc=()=>s(de,{name:"back-to-top"},()=>[s("path",{d:"M512 843.2c-36.2 0-66.4-13.6-85.8-21.8-10.8-4.6-22.6 3.6-21.8 15.2l7 102c.4 6.2 7.6 9.4 12.6 5.6l29-22c3.6-2.8 9-1.8 11.4 2l41 64.2c3 4.8 10.2 4.8 13.2 0l41-64.2c2.4-3.8 7.8-4.8 11.4-2l29 22c5 3.8 12.2.6 12.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6 8.2-49.6 21.8-85.8 21.8z"}),s("path",{d:"m795.4 586.2-96-98.2C699.4 172 513 32 513 32S324.8 172 324.8 488l-96 98.2c-3.6 3.6-5.2 9-4.4 14.2L261.2 824c1.8 11.4 14.2 17 23.6 10.8L419 744s41.4 40 94.2 40c52.8 0 92.2-40 92.2-40l134.2 90.8c9.2 6.2 21.6.6 23.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14zM513 384c-34 0-61.4-28.6-61.4-64s27.6-64 61.4-64c34 0 61.4 28.6 61.4 64S547 384 513 384z"})]);kc.displayName="BackToTopIcon";var X1=$({name:"BackToTop",props:{threshold:{type:Number,default:100},noProgress:Boolean},setup(e){const t=Ee(),l=on({"/":{backToTop:"返回顶部"}}),n=Ge(),{height:a}=q1(n),{height:i}=W1(),{y:r}=U1(),o=E(()=>t.value.backToTop!==!1&&r.value>e.threshold),c=E(()=>r.value/(a.value-i.value));return ke(()=>{n.value=document.body}),()=>s(Bt,{name:"fade"},()=>o.value?s("button",{type:"button",class:"vp-back-to-top-button","aria-label":l.value.backToTop,"data-balloon-pos":"left",onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[e.noProgress?null:s("svg",{class:"vp-scroll-progress"},s("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*c.value*100}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}})),s(kc)]):null)}});const Z1=dt({enhance:({app:e})=>{ct("FontIcon")||e.component("FontIcon",K1),ct("BiliBili")||e.component("BiliBili",Y1)},setup:()=>{wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/brands.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/solid.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/fontawesome.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}})},rootComponents:[()=>s(X1,{})]});function ev(e,t,l){var n,a,i;t===void 0&&(t=50),l===void 0&&(l={});var r=(n=l.isImmediate)!=null&&n,o=(a=l.callback)!=null&&a,c=l.maxWait,u=Date.now(),d=[];function p(){if(c!==void 0){var v=Date.now()-u;if(v+t>=c)return c-v}return t}var h=function(){var v=[].slice.call(arguments),b=this;return new Promise(function(w,L){var m=r&&i===void 0;if(i!==void 0&&clearTimeout(i),i=setTimeout(function(){if(i=void 0,u=Date.now(),!r){var I=e.apply(b,v);o&&o(I),d.forEach(function(R){return(0,R.resolve)(I)}),d=[]}},p()),m){var _=e.apply(b,v);return o&&o(_),w(_)}d.push({resolve:w,reject:L})})};return h.cancel=function(v){i!==void 0&&clearTimeout(i),d.forEach(function(b){return(0,b.reject)(v)}),d=[]},h}const tv=({headerLinkSelector:e,headerAnchorSelector:t,delay:l,offset:n=5})=>{const a=Ve(),r=ev(()=>{var w,L;const o=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(o-0)h.some(_=>_.hash===m.hash));for(let m=0;m=(((w=_.parentElement)==null?void 0:w.offsetTop)??0)-n,B=!I||o<(((L=I.parentElement)==null?void 0:L.offsetTop)??0)-n;if(!(R&&B))continue;const V=decodeURIComponent(a.currentRoute.value.hash),A=decodeURIComponent(_.hash);if(V===A)return;if(p){for(let q=m+1;q{window.addEventListener("scroll",r)}),Yn(()=>{window.removeEventListener("scroll",r)})},hs=async(e,t)=>{const{scrollBehavior:l}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=l)},lv=".vp-sidebar-link, .toc-link",nv=".header-anchor",av=200,iv=5,rv=dt({setup(){tv({headerLinkSelector:lv,headerAnchorSelector:nv,delay:av,offset:iv})}});let wc=()=>null;const Ec=Symbol(""),sv=e=>{wc=e},ov=()=>me(Ec),cv=e=>{e.provide(Ec,wc)};var uv=$({name:"AutoCatalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean},setup(e){const t=ov(),l=on({"/":{title:"目录",empty:"暂无目录"}}),n=ue(),a=Ve(),i=Vo(),r=u=>{const d=u.I;return typeof d>"u"||d},o=()=>{const u=e.base||n.value.path.replace(/\/[^/]+$/,"/"),d=a.getRoutes(),p=[];return d.filter(({meta:h,path:v})=>{if(!Ql(v,u)||v===u)return!1;if(u==="/"){const b=gt(i.value.locales).filter(w=>w!=="/");if(v==="/404.html"||b.some(w=>Ql(v,w)))return!1}return(il(v,".html")&&!il(v,"/index.html")||il(v,"/"))&&r(h)}).map(({path:h,meta:v})=>{const b=h.substring(u.length).split("/").length;return{title:v.t||"",icon:v.i,base:h.replace(/\/[^/]+\/?$/,"/"),order:v.O||null,level:il(h,"/")?b-1:b,path:h}}).filter(({title:h,level:v})=>h&&v<=e.level).sort(({title:h,level:v,path:b,order:w},{title:L,level:m,path:_,order:I})=>v-m||(il(b,"/index.html")?-1:il(_,"/index.html")?1:w===null?I===null?h.localeCompare(L):I:I===null?w:w>0?I>0?w-I:-1:I<0?w-I:1)).forEach(h=>{var v;const{base:b,level:w}=h;switch(w){case 1:p.push(h);break;case 2:{const L=p.find(m=>m.path===b);L&&(L.children??(L.children=[])).push(h);break}default:{const L=p.find(m=>m.path===b.replace(/\/[^/]+\/$/,"/"));if(L){const m=(v=L.children)==null?void 0:v.find(_=>_.path===b);m&&(m.children??(m.children=[])).push(h)}}}}),p},c=E(()=>o());return()=>s("div",{class:"vp-catalog"},[s("h2",{class:"vp-catalog-main-title"},l.value.title),c.value.length?c.value.map(({children:u=[],icon:d,path:p,title:h},v)=>[s("h3",{id:h,class:["vp-catalog-child-title",{"has-children":u.length}]},[s("a",{href:`#${h}`,class:"header-anchor","aria-hidden":!0},"#"),s(Ce,{class:"vp-catalog-title",to:p},()=>[e.index?`${v+1}.`:null,d&&t?s(t,{icon:d}):null,h||p])]),u.length?s("ul",{class:"vp-catalog-child-catalogs"},u.map(({children:b=[],icon:w,path:L,title:m},_)=>s("li",{class:"vp-child-catalog"},[s("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[s("a",{href:`#${m}`,class:"header-anchor"},"#"),s(Ce,{class:"vp-catalog-title",to:L},()=>[e.index?`${v+1}.${_+1}`:null,w&&t?s(t,{icon:w}):null,m||L])]),b.length?s("div",{class:"v-sub-catalogs"},b.map(({icon:I,path:R,title:B},C)=>s(Ce,{class:"vp-sub-catalog",to:R},()=>[e.index?`${v+1}.${_+1}.${C+1}`:null,I&&t?s(t,{icon:I}):null,B||R]))):null]))):null]):s("p",{class:"vp-empty-catalog"},l.value.empty)])}}),dv=dt({enhance:({app:e})=>{cv(e),ct("AutoCatalog",e)||e.component("AutoCatalog",uv)}});const pv=s("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[s("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),s("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),Lc=$({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=mt(),l=E(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>s("span",[pv,s("span",{class:"external-link-icon-sr-only"},l.value.openInNewWindow)])}}),hv={},vv=dt({enhance({app:e}){e.component("ExternalLinkIcon",s(Lc,{locales:hv}))}});/**
+ * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
+ * @license MIT
+ */const se={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=se.isStarted();e=Ea(e,se.settings.minimum,1),se.status=e===1?null:e;const l=se.render(!t),n=l.querySelector(se.settings.barSelector),a=se.settings.speed,i=se.settings.easing;return l.offsetWidth,fv(r=>{On(n,{transform:"translate3d("+vs(e)+"%,0,0)",transition:"all "+a+"ms "+i}),e===1?(On(l,{transition:"none",opacity:"1"}),l.offsetWidth,setTimeout(function(){On(l,{transition:"all "+a+"ms linear",opacity:"0"}),setTimeout(function(){se.remove(),r()},a)},a)):setTimeout(()=>r(),a)}),se},isStarted:()=>typeof se.status=="number",start:()=>{se.status||se.set(0);const e=()=>{setTimeout(()=>{se.status&&(se.trickle(),e())},se.settings.trickleSpeed)};return se.settings.trickle&&e(),se},done:e=>!e&&!se.status?se:se.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=se.status;return t?(typeof e!="number"&&(e=(1-t)*Ea(Math.random()*t,.1,.95)),t=Ea(t+e,0,.994),se.set(t)):se.start()},trickle:()=>se.inc(Math.random()*se.settings.trickleRate),render:e=>{if(se.isRendered())return document.getElementById("nprogress");fs(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=se.settings.template;const l=t.querySelector(se.settings.barSelector),n=e?"-100":vs(se.status||0),a=document.querySelector(se.settings.parent);return On(l,{transition:"all 0 linear",transform:"translate3d("+n+"%,0,0)"}),a!==document.body&&fs(a,"nprogress-custom-parent"),a==null||a.appendChild(t),t},remove:()=>{gs(document.documentElement,"nprogress-busy"),gs(document.querySelector(se.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&gv(e)},isRendered:()=>!!document.getElementById("nprogress")},Ea=(e,t,l)=>el?l:e,vs=e=>(-1+e)*100,fv=function(){const e=[];function t(){const l=e.shift();l&&l(t)}return function(l){e.push(l),e.length===1&&t()}}(),On=function(){const e=["Webkit","O","Moz","ms"],t={};function l(r){return r.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(o,c){return c.toUpperCase()})}function n(r){const o=document.body.style;if(r in o)return r;let c=e.length;const u=r.charAt(0).toUpperCase()+r.slice(1);let d;for(;c--;)if(d=e[c]+u,d in o)return d;return r}function a(r){return r=l(r),t[r]??(t[r]=n(r))}function i(r,o,c){o=a(o),r.style[o]=c}return function(r,o){for(const c in o){const u=o[c];u!==void 0&&Object.prototype.hasOwnProperty.call(o,c)&&i(r,c,u)}}}(),xc=(e,t)=>(typeof e=="string"?e:Di(e)).indexOf(" "+t+" ")>=0,fs=(e,t)=>{const l=Di(e),n=l+t;xc(l,t)||(e.className=n.substring(1))},gs=(e,t)=>{const l=Di(e);if(!xc(e,t))return;const n=l.replace(" "+t+" "," ");e.className=n.substring(1,n.length-1)},Di=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),gv=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const mv=()=>{ke(()=>{const e=Ve(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(l=>{t.has(l.path)||se.start()}),e.afterEach(l=>{t.add(l.path),se.done()})})},yv=dt({setup(){mv()}}),bv=JSON.parse('{"encrypt":{},"author":{"name":"levy"},"pageInfo":["Date","Tag"],"logo":"/logo.png","repo":"levy9527/blog","docsDir":"src","displayFooter":true,"editLink":false,"darkmode":"disable","blog":{"description":"Javascript/Java/Python\\n都能撸的“全干工程师”","avatar":"https://avatars.githubusercontent.com/u/9384365?v=4","roundAvatar":true,"intro":"/about.html","medias":{"GitHub":"https://github.com/levy9527","BiliBili":"https://search.bilibili.com/video?keyword=levydotvip&from_source=webtop_search","Email":"mailto:info@897895407@qq.com","Rss":"https://levy.vip/rss.xml"}},"locales":{"/":{"lang":"zh-CN","navbarLocales":{"langName":"简体中文","selectLangAriaLabel":"选择语言"},"metaLocales":{"author":"作者","date":"写作日期","origin":"原创","views":"访问量","category":"分类","tag":"标签","readingTime":"阅读时间","words":"字数","toc":"此页内容","prev":"上一页","next":"下一页","lastUpdated":"上次编辑于","contributors":"贡献者","editLink":"编辑此页","print":"打印"},"blogLocales":{"article":"文章","articleList":"文章列表","category":"分类","tag":"标签","timeline":"时间轴","timelineTitle":"昨日不在","all":"全部","intro":"个人介绍","star":"收藏"},"paginationLocales":{"prev":"上一页","next":"下一页","navigate":"跳转到","action":"前往","errorText":"请输入 1 到 $page 之前的页码!"},"outlookLocales":{"themeColor":"主题色","darkmode":"外观","fullscreen":"全屏"},"routeLocales":{"skipToContent":"跳至主要內容","notFoundTitle":"页面不存在","notFoundMsg":["这里什么也没有","我们是怎么来到这儿的?","这 是 四 零 四 !","看起来你访问了一个失效的链接"],"back":"返回上一页","home":"带我回家","openInNewWindow":"Open in new window"},"navbar":[{"text":"首页","icon":"house","link":"/"},{"text":"分类","icon":"list","children":[{"text":"软件研发","icon":"code","link":"/tools/how-to-connect-to-internet"},{"text":"工作日常","icon":"briefcase","link":"/daily"},{"text":"英语学习","icon":"globe","link":"/english"}]},"/about"],"sidebar":{"/daily/":"structure","/english":["let-chatgpt-be-your-foreign-language-teacher","how-to-self-evaluate-english-level","learning-7000-words-task-completed","everyone-can-learn-english-1-overview","everyone-can-learn-english-2-pronunciation","everyone-can-learn-english-3-words","everyone-can-learn-english-4-listening-and-speaking","everyone-can-learn-english-5-reading-and-writing","contemporary-college-english-1","contemporary-college-english-2","contemporary-college-english-3","contemporary-college-english-4","contemporary-college-english-5","contemporary-college-english-6"],"/":[{"text":"实用工具","prefix":"/tools/","children":["how-to-connect-to-internet"]},{"text":"LLM","prefix":"/llm","children":["evaluate-llm-app-with-ragas","llm-with-recordation-review-of-regulations"]},{"text":"Python","prefix":"/python","children":["add-logging-for-llm-app","mr.py","export-mysql-table-into-excel"]},{"text":"Git相关","prefix":"/git/","children":["git-useful-commands","git-definitive-guide-to-merge-code","git-history-two-tricks-in-idea","gitlab-ci","rethinking-git-flow","git-best-pratices","use-command-line-tool-to-manage-gitlab-merge-request"]},{"text":"软件测试","prefix":"/software-testing/","children":["unit-testing-overview","use-postman-for-api-testing","use-RestAssured-for-api-testing","use-pytest-for-regression-testing-in-llm-app","use-jest-for-test-driven-development","use-playwright-for-ui-testing","use-cypress-for-e2e-testing"]},{"text":"DevOps","prefix":"/devops","children":["docker-build-and-push-script","reduce-python-image-size","what-is-the-difference-between-sh-and-bash","about-arm-things-you-need-to-know","common-solutions-of-object-storage-for-static-assets"]},{"text":"Java","prefix":"/java","children":["Resolving-Common-Problems-in-IntelliJ-IDEA","Resolving-Common-Problems-in-Maven.md","using-enum-in-java","which-one-is-better-Boolean-or-boolean","which-one-is-better-forEach-or-map","recommend-practices-for-collections-naming-convention","recommend-practices-for-writing-good-functions","avoid-sending-password-in-plaintext","recommend-practices-for-query-by-date-range","common-practices-for-handling-excel","check-if-name-exists","how-to-convert-snapshot-into-release-jar-without-source-code","why-i-prefer-fastjson-instead-of-jackson","why-is-it-so-hard-to-upgrade-dependencies"]},{"text":"MySQL","prefix":"/mysql","children":["mysql-backup-case-study-mysqldump-in-action","mysql-data-migration-case-study-add-auto-increment","mysql-details-you-should-know-when-execute-sql-in-command-line"]},{"text":"前端技术","prefix":"/frontend","children":["old-articles","performance-optimization-in-action"]}]}}}}'),_v=W(bv),Tc=()=>_v,Ac=Symbol(""),kv=()=>{const e=me(Ac);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},wv=(e,t)=>{const{locales:l,...n}=e;return{...n,...l==null?void 0:l[t]}},Ev=dt({enhance({app:e}){const t=Tc(),l=e._context.provides[Ti],n=E(()=>wv(t.value,l.value));e.provide(Ac,n),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return n.value}}})}});const Lv=800,xv=2e3,Tv={"/":{copy:"复制代码",copied:"已复制",hint:"复制成功"}},Av=!1,Ov=['.theme-hope-content div[class*="language-"] pre'],ms=!1,La=new Map,Pv=()=>{const{copy:e}=I1({legacy:!0}),t=on(Tv),l=ue(),n=l1(),a=o=>{if(!o.hasAttribute("copy-code-registered")){const c=document.createElement("button");c.type="button",c.classList.add("copy-code-button"),c.innerHTML='
',c.setAttribute("aria-label",t.value.copy),c.setAttribute("data-copied",t.value.copied),o.parentElement&&o.parentElement.insertBefore(c,o),o.setAttribute("copy-code-registered","")}},i=()=>Tl().then(()=>new Promise(o=>{setTimeout(()=>{Ov.forEach(c=>{document.querySelectorAll(c).forEach(a)}),o()},Lv)})),r=(o,c,u)=>{let{innerText:d=""}=c;/language-(shellscript|shell|bash|sh|zsh)/.test(o.classList.toString())&&(d=d.replace(/^ *(\$|>) /gm,"")),e(d).then(()=>{u.classList.add("copied"),clearTimeout(La.get(u));const p=setTimeout(()=>{u.classList.remove("copied"),u.blur(),La.delete(u)},xv);La.set(u,p)})};ke(()=>{(!n.value||ms)&&i(),$e("click",o=>{const c=o.target;if(c.matches('div[class*="language-"] > button.copy')){const u=c.parentElement,d=c.nextElementSibling;d&&r(u,d,c)}else if(c.matches('div[class*="language-"] div.copy-icon')){const u=c.parentElement,d=u.parentElement,p=u.nextElementSibling;p&&r(d,p,u)}}),ce(()=>l.value.path,()=>{(!n.value||ms)&&i()})})};var Iv=dt({setup:()=>{Pv()}});const Pn=Ci("VUEPRESS_CODE_TAB_STORE",{});var Rv=$({name:"CodeTabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ge([]),a=()=>{e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>Pn.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>Pn.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-code-tabs"},[s("div",{class:"vp-code-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-code-tab-nav",{active:p}],role:"tab","aria-controls":`codetab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-code-tab",{active:p}],id:`codetab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Oc=({active:e=!1},{slots:t})=>{var l;return s("div",{class:["code-group-item",{active:e}],"aria-selected":e},(l=t.default)==null?void 0:l.call(t))};Oc.displayName="CodeGroupItem";const Cv=$({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const l=W(-1),n=Ge([]),a=(o=l.value)=>{l.value=o{l.value=o>0?o-1:n.value.length-1,n.value[l.value].focus()},r=(o,c)=>{o.key===" "||o.key==="Enter"?(o.preventDefault(),l.value=c):o.key==="ArrowRight"?(o.preventDefault(),a(c)):o.key==="ArrowLeft"&&(o.preventDefault(),i(c))};return()=>{var o;const c=(((o=t.default)==null?void 0:o.call(t))||[]).filter(u=>u.type.name==="CodeGroupItem").map(u=>(u.props===null&&(u.props={}),u));return c.length===0?null:(l.value<0||l.value>c.length-1?(l.value=c.findIndex(u=>"active"in u.props),l.value===-1&&(l.value=0)):c.forEach((u,d)=>{u.props.active=d===l.value}),s("div",{class:"code-group"},[s("div",{class:"code-group-nav"},c.map((u,d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["code-group-nav-tab",{active:p}],"aria-pressed":p,"aria-expanded":p,onClick:()=>{l.value=d},onKeydown:h=>r(h,d)},u.props.title)})),c]))}}});const xa=Ci("VUEPRESS_TAB_STORE",{});var Sv=$({name:"Tabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ge([]),a=()=>{e.tabId&&(xa.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),a()},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>xa.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>xa.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-tabs"},[s("div",{class:"vp-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-tab-nav",{active:p}],role:"tab","aria-controls":`tab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-tab",{active:p}],id:`tab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Dv=dt({enhance:({app:e})=>{e.component("CodeTabs",Rv),ct("CodeGroup",e)||e.component("CodeGroup",Cv),ct("CodeGroupItem",e)||e.component("CodeGroupItem",Oc),e.component("Tabs",Sv)},setup:()=>{}});let Mv={};const Pc=Symbol(""),$v=()=>me(Pc),Vv=e=>{e.provide(Pc,Mv)};const Fv=".theme-hope-content :not(a) > img:not([no-view])",Nv={"/":{closeTitle:"关闭",downloadTitle:"下载图片",fullscreenTitle:"切换全屏",zoomTitle:"缩放",arrowPrevTitle:"上一个 (左箭头)",arrowNextTitle:"下一个 (右箭头)"}},jv=800,Bv='
',Hv=e=>ae(e)?Array.from(document.querySelectorAll(e)):e.map(t=>Array.from(document.querySelectorAll(t))).flat(),Ic=e=>new Promise((t,l)=>{e.complete?t({type:"image",element:e,src:e.src,width:e.naturalWidth,height:e.naturalHeight,alt:e.alt,msrc:e.src}):(e.onload=()=>t(Ic(e)),e.onerror=n=>l(n))}),zv=()=>{const{isSupported:e,toggle:t}=Si(),l=$v(),n=on(Nv),a=ue();let i;const r=c=>{c.on("uiRegister",()=>{e&&c.ui.registerElement({name:"fullscreen",order:7,isButton:!0,html:'',onClick:()=>{t()}}),c.ui.registerElement({name:"download",order:8,isButton:!0,tagName:"a",html:{isCustomSVG:!0,inner:'',outlineID:"pswp__icn-download"},onInit:(u,d)=>{u.setAttribute("download",""),u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"),d.on("change",()=>{u.setAttribute("href",d.currSlide.data.src)})}}),c.ui.registerElement({name:"bulletsIndicator",className:"photo-swipe-bullets-indicator",appendTo:"wrapper",onInit:(u,d)=>{const p=[];let h=-1;for(let v=0;v{d.goTo(p.indexOf(w.target))},p.push(b),u.appendChild(b)}d.on("change",()=>{h>=0&&p[h].classList.remove("active"),p[d.currIndex].classList.add("active"),h=d.currIndex})}})})},o=()=>Promise.all([g(()=>import("./photoswipe.esm-5794cde2.js"),[]),Tl().then(()=>new Promise(c=>setTimeout(c,jv)).then(()=>Hv(Fv)))]).then(([{default:c},u])=>{const d=u.map(p=>({html:Bv,element:p,msrc:p.src}));u.forEach((p,h)=>{const v=()=>{i=new c({preloaderDelay:0,showHideAnimationType:"zoom",...n.value,...l,dataSource:d,index:h,closeOnVerticalDrag:!0,wheelToZoom:!1}),r(i),i.addFilter("thumbEl",()=>p),i.addFilter("placeholderSrc",()=>p.src),i.init()};p.style.cursor="zoom-in",p.addEventListener("click",()=>{v()}),p.addEventListener("keypress",({key:b})=>{b==="Enter"&&v()})}),u.forEach((p,h)=>{Ic(p).then(v=>{d.splice(h,1,v),i==null||i.refreshSlideContent(h)})})});ke(()=>{$e("wheel",()=>{i==null||i.close()}),o(),ce(()=>a.value.path,()=>o())})};var qv=dt({enhance:({app:e})=>{Vv(e)},setup:()=>{zv()}});const Rc=()=>{const e=ue();return E(()=>e.value.readingTime??null)},Ga=typeof{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}}>"u"?null:{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}},Cc=(e,t)=>{const{minutes:l,words:n}=e,{less1Minute:a,word:i,time:r}=t;return{time:l<1?a:r.replace("$time",Math.round(l).toString()),words:i.replace("$word",n.toString())}},ys={words:"",time:""},Sc=()=>Ga?on(Ga):E(()=>null),Gv=()=>{if(typeof Ga>"u")return E(()=>ys);const e=Rc(),t=Sc();return E(()=>e.value&&t.value?Cc(e.value,t.value):ys)},el=()=>Tc(),ie=()=>kv(),Ol=()=>E(()=>!!el().value.pure);var Ta=$({name:"EmptyComponent",setup:()=>()=>null});const Uv="719px",Wv="1440px",Kv="false",Mi={mobileBreakPoint:Uv,pcBreakPoint:Wv,enableThemeColor:Kv},$i={"/daily/":["testing-environments-should-be-consistent-with-production-environments","copy-code-may-not-be-guilty","you-dont-need-to-add-tenant_id-to-every-table","reflections-on-a-speech-by-cto-of-microsoft-china","iteration-retrospective-of-sanyuan","leverage-ai-to-boost-coding-productivity","claude-ai-in-action-extract-info-from-html","beyond-utf8-do-you-know-utf8mb4-and-collation","use-claude2-instead-of-chatgpt","vim-creator-pass-away","dont-try-to-argue-with-a-sb","a-vuepress2-entertaining-video","a-warning-from-navicat","things-I-have-to-vent-about-vue"]},Dc=e=>{const{icon:t="",color:l,size:n}=e,a={};return l&&(a.color=l),n&&(a.height=Number.isNaN(Number(n))?n:`${n}px`),Zt(t)?s("img",{class:"icon",src:t,"no-view":"",style:a}):na(t)?s("img",{class:"icon",src:xe(t),"no-view":"",style:a}):s(Je("FontIcon"),e)};Dc.displayName="HopeIcon";var Ne=Dc,be=(e=>(e.type="y",e.title="t",e.shortTitle="s",e.icon="i",e.author="a",e.date="d",e.localizedDate="l",e.category="c",e.tag="g",e.isEncrypted="n",e.isOriginal="o",e.readingTime="r",e.excerpt="e",e.sticky="u",e.cover="v",e.index="I",e.order="O",e))(be||{}),Mc=(e=>(e.article="a",e.home="h",e.slide="s",e.page="p",e))(Mc||{});const pl=(e,t,l=!1)=>{const n=encodeURI(t);let a=_l(e,tc(n));a.name==="404"&&(a=_l(e,n));const{fullPath:i,meta:r,name:o}=a;return{text:!l&&r[be.shortTitle]?r[be.shortTitle]:r[be.title]||t,link:o==="404"?t:i,...r[be.icon]?{icon:r[be.icon]}:{}}},un=()=>{const e=Ve(),t=yt();return l=>{if(l)if(na(l))t.path!==l&&e.push(l);else if(Zt(l)||Oo(l))window&&window.open(l);else{const n=t.path.slice(0,t.path.lastIndexOf("/"));e.push(`${n}/${encodeURI(l)}`)}}},$c=()=>{const e=ie(),t=Ee();return E(()=>{const{author:l}=t.value;return l?Yl(l):l===!1?[]:Yl(e.value.author,!1)})},Jv=()=>{const e=Ee();return E(()=>nc(e.value.category).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("categoryMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Qv=()=>{const e=Ee();return E(()=>ac(e.value.tag).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("tagMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Yv=()=>{const e=Ee(),t=ue();return E(()=>{const l=Pi(e.value.date);if(l)return l;const{createdTime:n}=t.value.git||{};return n?new Date(n):null})},Xv=()=>{const e=ie(),t=ue(),l=Ee(),n=$c(),a=Jv(),i=Qv(),r=Yv(),o=Rc(),c=Gv(),u=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value.localizedDate,tag:i.value,isOriginal:l.value.isOriginal||!1,readingTime:o.value,readingTimeLocale:c.value,pageview:"pageview"in l.value?l.value.pageview:!0})),d=E(()=>"pageInfo"in l.value?l.value.pageInfo:"pageInfo"in e.value?e.value.pageInfo:null);return{info:u,items:d}},{mobileBreakPoint:Zv,pcBreakPoint:ef}=Mi,bs=e=>e.endsWith("px")?Number(e.slice(0,-2)):null,dn=()=>{const e=W(!1),t=W(!1),l=()=>{e.value=window.innerWidth<=(bs(Zv)??719),t.value=window.innerWidth>=(bs(ef)??1440)};return ke(()=>{l(),$e("resize",l,!1),$e("orientationchange",l,!1)}),{isMobile:e,isPC:t}},Vc=Symbol(""),pn=()=>{const e=me(Vc);if(!e)throw new Error("useDarkmode() is called without provider.");return e},tf=e=>{const t=el(),l=N1(),n=Ci("vuepress-theme-hope-scheme","auto"),a=E(()=>t.value.darkmode||"switch"),i=E(()=>{const o=a.value;return o==="disable"?!1:o==="enable"?!0:o==="auto"?l.value:o==="toggle"?n.value==="dark":n.value==="dark"||n.value==="auto"&&l.value}),r=E(()=>{const o=a.value;return o==="switch"||o==="toggle"});e.provide(Vc,{canToggle:r,config:a,isDarkmode:i,status:n}),Object.defineProperties(e.config.globalProperties,{$isDarkmode:{get:()=>i.value}})},lf=()=>{const{isDarkmode:e}=pn(),t=(l=e.value)=>document.documentElement.setAttribute("data-theme",l?"dark":"light");ke(()=>{ce(e,t,{immediate:!0})})};var ze=$({name:"AutoLink",inheritAttrs:!1,props:{config:{type:Object,required:!0},exact:Boolean,noExternalLinkIcon:Boolean},emits:["focusout"],slots:Object,setup(e,{attrs:t,emit:l,slots:n}){const a=yt(),i=Vo(),r=xl(e,"config"),o=E(()=>Zt(r.value.link)),c=E(()=>Oo(r.value.link)||P0(r.value.link)),u=E(()=>c.value?void 0:r.value.target||(o.value?"_blank":void 0)),d=E(()=>u.value==="_blank"),p=E(()=>!o.value&&!c.value&&!d.value),h=E(()=>c.value?void 0:r.value.rel||(d.value?"noopener noreferrer":void 0)),v=E(()=>r.value.ariaLabel||r.value.text),b=E(()=>{if(e.exact)return!1;const L=gt(i.value.locales);return L.length?L.every(m=>m!==r.value.link):r.value.link!=="/"}),w=E(()=>p.value?r.value.activeMatch?new RegExp(r.value.activeMatch).test(a.path):b.value?Ql(a.path,r.value.link):a.path===r.value.link:!1);return()=>{const{before:L,after:m,default:_}=n,{text:I,icon:R,link:B}=r.value;return p.value?s(Ce,{to:B,"aria-label":v.value,...t,class:["nav-link",{active:w.value},t.class],onFocusout:()=>l("focusout")},()=>_?_():[L?L():s(Ne,{icon:R}),I,m==null?void 0:m()]):s("a",{href:B,rel:h.value,target:u.value,"aria-label":v.value,...t,class:["nav-link",t.class],onFocusout:()=>l("focusout")},_?_():[L?L():s(Ne,{icon:R}),I,e.noExternalLinkIcon?null:s(Lc),m==null?void 0:m()])}}});const kl=(e,t,l=!1)=>"activeMatch"in t?new RegExp(t.activeMatch).test(e.path):Ri(e,t.link)?!0:t.children&&!l?t.children.some(n=>kl(e,n)):!1,Fc=(e,t)=>t.type==="group"?t.children.some(l=>l.type==="group"?Fc(e,l):l.type==="page"&&kl(e,l,!0))||"prefix"in t&&Ri(e,t.prefix):!1,Nc=(e,t)=>ae(e.link)?s(ze,{...t,config:e}):s("p",t,[s(Ne,{icon:e.icon}),e.text]),jc=e=>{const t=yt();return e?s("ul",{class:"vp-sidebar-sub-headers"},e.map(l=>{const n=kl(t,l,!0);return s("li",{class:"vp-sidebar-sub-header"},[Nc(l,{class:["vp-sidebar-link","vp-heading",{active:n}]}),jc(l.children)])})):null},Aa=(e="",t="")=>na(t)?t:`${T0(e)}${t}`,nf=(e,t)=>{const l=ue();return{type:"heading",text:e.title,link:`${l.value.path}#${e.slug}`,children:Vi(e.children,t)}},Vi=(e,t)=>t>0?e.map(l=>nf(l,t-1)):[],Bc=e=>{const t=ue();return Vi(t.value.headers,e)},Ua=(e,t,l="")=>{const n=Ve(),a=ue(),i=(r,o=l)=>{var c;const u=ae(r)?pl(n,Aa(o,r)):r.link?{...r,...Bn(r.link)?{}:{link:pl(n,Aa(o,r.link)).link}}:r;if("children"in u){const d=Aa(o,u.prefix),p=u.children==="structure"?$i[d]:u.children;return{type:"group",...u,prefix:d,children:p.map(h=>i(h,d))}}return{type:"page",...u,children:u.link===a.value.path?Vi(((c=a.value.headers[0])==null?void 0:c.level)===1?a.value.headers[0].children:a.value.headers,t):[]}};return e.map(r=>i(r))},af=(e,t)=>{const l=ue(),n=gt(e).sort((a,i)=>i.length-a.length);for(const a of n)if(Ql(decodeURI(l.value.path),a)){const i=e[a];return i?Ua(i==="structure"?$i[a]:i==="heading"?Bc(t):i,t,a):[]}return console.warn(`${l.value.path} is missing sidebar config.`),[]},rf=(e,t)=>{const l=mt();return e===!1?[]:e==="heading"?Bc(t):e==="structure"?Ua($i[l.value],t,l.value):X(e)?Ua(e,t):Li(e)?af(e,t):[]},Hc=Symbol(""),sf=()=>{const e=Ee(),t=ie(),l=E(()=>e.value.home?!1:e.value.sidebar??t.value.sidebar??"structure"),n=E(()=>e.value.headerDepth??t.value.headerDepth??2),a=E(()=>rf(l.value,n.value));ot(Hc,a)},Fi=()=>{const e=me(Hc);if(!e)throw new Error("useSidebarItems() is called without provider.");return e};var of=$({name:"PageFooter",setup(){const e=Ee(),t=ie(),l=$c(),n=E(()=>{const{copyright:r,footer:o}=e.value;return o!==!1&&!!(r||o||t.value.displayFooter)}),a=E(()=>{const{footer:r}=e.value;return r===!1?!1:ae(r)?r:t.value.footer||""}),i=E(()=>"copyright"in e.value?e.value.copyright:"copyright"in t.value?t.value.copyright:l.value.length?`Copyright © ${new Date().getFullYear()} ${l.value[0].name}`:!1);return()=>n.value?s("footer",{class:"vp-footer-wrapper"},[a.value?s("div",{class:"vp-footer",innerHTML:a.value}):null,i.value?s("div",{class:"vp-copyright",innerHTML:i.value}):null]):null}}),cf=$({name:"NavbarDropdownLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const l=ue(),n=xl(e,"config"),a=E(()=>n.value.ariaLabel||n.value.text),i=W(!1);ce(()=>l.value.path,()=>{i.value=!1});const r=o=>{o.detail===0&&(i.value=!i.value)};return()=>{var o;return s("div",{class:["dropdown-wrapper",{open:i.value}]},[s("button",{type:"button",class:"dropdown-title","aria-label":a.value,onClick:r},[((o=t.title)==null?void 0:o.call(t))||s("span",{class:"title"},[s(Ne,{icon:n.value.icon}),e.config.text]),s("span",{class:"arrow"}),s("ul",{class:"nav-dropdown"},n.value.children.map((c,u)=>{const d=u===n.value.children.length-1;return s("li",{class:"dropdown-item"},"children"in c?[s("h4",{class:"dropdown-subtitle"},c.link?s(ze,{config:c,onFocusout:()=>{c.children.length===0&&d&&(i.value=!1)}}):s("span",c.text)),s("ul",{class:"dropdown-subitem-wrapper"},c.children.map((p,h)=>s("li",{class:"dropdown-subitem"},s(ze,{config:p,onFocusout:()=>{h===c.children.length-1&&d&&(i.value=!1)}}))))]:s(ze,{config:c,onFocusout:()=>{d&&(i.value=!1)}}))}))])])}}});const zc=(e,t,l="")=>ae(t)?pl(e,`${l}${t}`):"children"in t?{...t,...t.link&&!Bn(t.link)?pl(e,`${l}${t.link}`):{},children:t.children.map(n=>zc(e,n,`${l}${t.prefix||""}`))}:{...t,link:Bn(t.link)?t.link:pl(e,`${l}${t.link}`).link},qc=()=>{const e=ie(),t=Ve(),l=()=>(e.value.navbar||[]).map(a=>zc(t,a)),n=W(l());return ce(e,()=>{n.value=l()}),n},uf=()=>{const e=ie(),t=E(()=>e.value.repo||null),l=E(()=>t.value?Zh(t.value):null),n=E(()=>t.value?rc(t.value):null),a=E(()=>l.value?e.value.repoLabel??(n.value===null?"Source":n.value):null);return E(()=>!l.value||!a.value||e.value.repoDisplay===!1?null:{type:n.value||"Source",label:a.value,link:l.value})};var df=$({name:"NavScreenDropdown",props:{config:{type:Object,required:!0}},setup(e){const t=ue(),l=xl(e,"config"),n=E(()=>l.value.ariaLabel||l.value.text),a=W(!1);ce(()=>t.value.path,()=>{a.value=!1});const i=(r,o)=>o[o.length-1]===r;return()=>[s("button",{type:"button",class:["nav-screen-dropdown-title",{active:a.value}],"aria-label":n.value,onClick:()=>{a.value=!a.value}},[s("span",{class:"title"},[s(Ne,{icon:l.value.icon}),e.config.text]),s("span",{class:["arrow",a.value?"down":"end"]})]),s("ul",{class:["nav-screen-dropdown",{hide:!a.value}]},l.value.children.map(r=>s("li",{class:"dropdown-item"},"children"in r?[s("h4",{class:"dropdown-subtitle"},r.link?s(ze,{config:r,onFocusout:()=>{i(r,l.value.children)&&r.children.length===0&&(a.value=!1)}}):s("span",r.text)),s("ul",{class:"dropdown-subitem-wrapper"},r.children.map(o=>s("li",{class:"dropdown-subitem"},s(ze,{config:o,onFocusout:()=>{i(o,r.children)&&i(r,l.value.children)&&(a.value=!1)}}))))]:s(ze,{config:r,onFocusout:()=>{i(r,l.value.children)&&(a.value=!1)}}))))]}}),pf=$({name:"NavScreenLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"nav-screen-links"},e.value.map(t=>s("div",{class:"navbar-links-item"},"children"in t?s(df,{config:t}):s(ze,{config:t})))):null}});const Gc=()=>s(de,{name:"dark"},()=>s("path",{d:"M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"}));Gc.displayName="DarkIcon";const Uc=()=>s(de,{name:"light"},()=>s("path",{d:"M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"}));Uc.displayName="LightIcon";const Wc=()=>s(de,{name:"auto"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"}));Wc.displayName="AutoIcon";const Kc=()=>s(de,{name:"enter-fullscreen"},()=>s("path",{d:"M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z"}));Kc.displayName="EnterFullScreenIcon";const Jc=()=>s(de,{name:"cancel-fullscreen"},()=>s("path",{d:"M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z"}));Jc.displayName="CancelFullScreenIcon";const Qc=()=>s(de,{name:"outlook"},()=>[s("path",{d:"M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"})]);Qc.displayName="OutlookIcon";var Yc=$({name:"AppearanceSwitch",setup(){const{config:e,status:t}=pn(),l=()=>{e.value==="switch"?t.value={light:"dark",dark:"auto",auto:"light"}[t.value]:t.value=t.value==="light"?"dark":"light"};return()=>s("button",{type:"button",id:"appearance-switch",onClick:()=>l()},[s(Wc,{style:{display:t.value==="auto"?"block":"none"}}),s(Gc,{style:{display:t.value==="dark"?"block":"none"}}),s(Uc,{style:{display:t.value==="light"?"block":"none"}})])}}),hf=$({name:"AppearanceMode",setup(){const e=ie(),{canToggle:t}=pn(),l=E(()=>e.value.outlookLocales.darkmode);return()=>t.value?s("div",{class:"appearance-wrapper"},[s("label",{class:"appearance-title",for:"appearance-switch"},l.value),s(Yc)]):null}});const Oa="VUEPRESS_THEME_COLOR";var vf=$({name:"ThemeColorPicker",props:{themeColor:{type:Object,required:!0}},setup(e){const t=(l="")=>{const n=document.documentElement.classList,a=gt(e.themeColor);if(!l){localStorage.removeItem(Oa),n.remove(...a);return}n.remove(...a.filter(i=>i!==l)),n.add(l),localStorage.setItem(Oa,l)};return ke(()=>{const l=localStorage.getItem(Oa);l&&t(l)}),()=>s("ul",{id:"theme-color-picker"},[s("li",s("span",{class:"theme-color",onClick:()=>t()})),sn(e.themeColor).map(([l,n])=>s("li",s("span",{style:{background:n},onClick:()=>t(l)})))])}});const hl=Mi.enableThemeColor==="true",ff=hl?Jh(sn(Mi).filter(([e])=>e.startsWith("theme-"))):{};var gf=$({name:"ThemeColor",setup(){const e=ie(),t=E(()=>e.value.outlookLocales.themeColor);return()=>hl?s("div",{class:"theme-color-wrapper"},[s("label",{class:"theme-color-title",for:"theme-color-picker"},t.value),s(vf,{themeColor:ff})]):null}}),Xc=$({name:"ToggleFullScreenButton",setup(){const e=ie(),{isSupported:t,isFullscreen:l,toggle:n}=Si(),a=E(()=>e.value.outlookLocales.fullscreen);return()=>t?s("div",{class:"full-screen-wrapper"},[s("label",{class:"full-screen-title",for:"full-screen-switch"},a.value),s("button",{type:"button",id:"full-screen-switch",class:"full-screen",ariaPressed:l.value,onClick:()=>n()},l.value?s(Jc):s(Kc))]):null}}),Zc=$({name:"OutlookSettings",setup(){const e=el(),t=Ol(),l=E(()=>!t.value&&e.value.fullscreen);return()=>s(Zn,()=>[hl?s(gf):null,s(hf),l.value?s(Xc):null])}}),mf=$({name:"NavScreen",props:{show:Boolean},emits:["close"],slots:Object,setup(e,{emit:t,slots:l}){const n=ue(),{isMobile:a}=dn(),i=Ge(),r=_c(i);return ke(()=>{i.value=document.body,ce(a,o=>{!o&&e.show&&(r.value=!1,t("close"))}),ce(()=>n.value.path,()=>{r.value=!1,t("close")})}),an(()=>{r.value=!1}),()=>s(Bt,{name:"fade",onEnter:()=>{r.value=!0},onAfterLeave:()=>{r.value=!1}},()=>{var o,c;return e.show?s("div",{id:"nav-screen"},s("div",{class:"vp-nav-screen-container"},[(o=l.before)==null?void 0:o.call(l),s(pf),s("div",{class:"vp-outlook-wrapper"},s(Zc)),(c=l.after)==null?void 0:c.call(l)])):null})}}),yf=$({name:"NavbarBrand",setup(){const e=mt(),t=rn(),l=ie(),n=E(()=>l.value.home||e.value),a=E(()=>t.value.title),i=E(()=>l.value.navTitle??a.value),r=E(()=>l.value.logo?xe(l.value.logo):null),o=E(()=>l.value.logoDark?xe(l.value.logoDark):null);return()=>s(Ce,{to:n.value,class:"vp-brand"},()=>[r.value?s("img",{class:["vp-nav-logo",{light:!!o.value}],src:r.value,alt:a.value}):null,o.value?s("img",{class:["vp-nav-logo dark"],src:o.value,alt:a.value}):null,i.value?s("span",{class:["vp-site-name",{"hide-in-pad":r.value&&l.value.hideSiteNameOnMobile!==!1}]},i.value):null])}}),bf=$({name:"NavbarLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"vp-nav-links"},e.value.map(t=>s("div",{class:"nav-item hide-in-mobile"},"children"in t?s(cf,{config:t}):s(ze,{config:t})))):null}}),_f=$({name:"RepoLink",components:{BitbucketIcon:uc,GiteeIcon:cc,GitHubIcon:sc,GitLabIcon:oc,SourceIcon:dc},setup(){const e=uf();return()=>e.value?s("div",{class:"nav-item vp-repo"},s("a",{class:"vp-repo-link",href:e.value.link,target:"_blank",rel:"noopener noreferrer","aria-label":e.value.label},s(Je(`${e.value.type}Icon`),{style:{width:"1.25rem",height:"1.25rem",verticalAlign:"middle"}}))):null}});const eu=({active:e=!1},{emit:t})=>s("button",{type:"button",class:["vp-toggle-navbar-button",{"is-active":e}],"aria-label":"Toggle Navbar","aria-expanded":e,"aria-controls":"nav-screen",onClick:()=>t("toggle")},s("span",[s("span",{class:"vp-top"}),s("span",{class:"vp-middle"}),s("span",{class:"vp-bottom"})]));eu.displayName="ToggleNavbarButton";var kf=eu;const Wa=(e,{emit:t})=>s("button",{type:"button",class:"vp-toggle-sidebar-button",title:"Toggle Sidebar",onClick:()=>t("toggle")},s("span",{class:"icon"}));Wa.displayName="ToggleSidebarButton",Wa.emits=["toggle"];var wf=Wa,Ef=$({name:"OutlookButton",setup(){const{isSupported:e}=Si(),t=el(),l=Ol(),n=ue(),{canToggle:a}=pn(),i=W(!1),r=E(()=>!l.value&&t.value.fullscreen&&e);return ce(()=>n.value.path,()=>{i.value=!1}),()=>a.value||r.value||hl?s("div",{class:"nav-item hide-in-mobile"},a.value&&!r.value&&!hl?s(Yc):r.value&&!a.value&&!hl?s(Xc):s("button",{type:"button",class:["outlook-button",{open:i.value}],tabindex:"-1","aria-hidden":!0},[s(Qc),s("div",{class:"outlook-dropdown"},s(Zc))])):null}}),Lf=$({name:"NavBar",emits:["toggleSidebar"],slots:Object,setup(e,{emit:t,slots:l}){const n=ie(),{isMobile:a}=dn(),i=W(!1),r=E(()=>{const{navbarAutoHide:d="mobile"}=n.value;return d!=="none"&&(d==="always"||a.value)}),o=E(()=>n.value.navbarLayout||{start:["Brand"],center:["Links"],end:["Language","Repo","Outlook","Search"]}),c={Brand:yf,Language:Ta,Links:bf,Repo:_f,Outlook:Ef,Search:ct("Docsearch")?Je("Docsearch"):ct("SearchBox")?Je("SearchBox"):Ta},u=d=>c[d]??(ct(d)?Je(d):Ta);return()=>{var d,p,h,v,b,w;return[s("header",{id:"navbar",class:["vp-navbar",{"auto-hide":r.value,"hide-icon":n.value.navbarIcon===!1}]},[s("div",{class:"vp-navbar-start"},[s(wf,{onToggle:()=>{i.value&&(i.value=!1),t("toggleSidebar")}}),(d=l.startBefore)==null?void 0:d.call(l),(o.value.start||[]).map(L=>s(u(L))),(p=l.startAfter)==null?void 0:p.call(l)]),s("div",{class:"vp-navbar-center"},[(h=l.centerBefore)==null?void 0:h.call(l),(o.value.center||[]).map(L=>s(u(L))),(v=l.centerAfter)==null?void 0:v.call(l)]),s("div",{class:"vp-navbar-end"},[(b=l.endBefore)==null?void 0:b.call(l),(o.value.end||[]).map(L=>s(u(L))),(w=l.endAfter)==null?void 0:w.call(l),s(kf,{active:i.value,onToggle:()=>{i.value=!i.value}})])]),s(mf,{show:i.value,onClose:()=>{i.value=!1}},{before:()=>{var L;return(L=l.screenTop)==null?void 0:L.call(l)},after:()=>{var L;return(L=l.screenBottom)==null?void 0:L.call(l)}})]}}}),xf=$({name:"SidebarChild",props:{config:{type:Object,required:!0}},setup(e){const t=yt();return()=>[Nc(e.config,{class:["vp-sidebar-link",`vp-sidebar-${e.config.type}`,{active:kl(t,e.config,!0)}],exact:!0}),jc(e.config.children)]}}),Tf=$({name:"SidebarGroup",props:{config:{type:Object,required:!0},open:{type:Boolean,required:!0}},emits:["toggle"],setup(e,{emit:t}){const l=yt(),n=E(()=>kl(l,e.config)),a=E(()=>kl(l,e.config,!0));return()=>{const{collapsible:i,children:r=[],icon:o,prefix:c,link:u,text:d}=e.config;return s("section",{class:"vp-sidebar-group"},[s(i?"button":"p",{class:["vp-sidebar-heading",{clickable:i||u,exact:a.value,active:n.value}],...i?{type:"button",onClick:()=>t("toggle"),onKeydown:p=>{p.key==="Enter"&&t("toggle")}}:{}},[s(Ne,{icon:o}),u?s(ze,{class:"vp-sidebar-title",config:{text:d,link:u},noExternalLinkIcon:!0}):s("span",{class:"vp-sidebar-title"},d),i?s("span",{class:["vp-arrow",e.open?"down":"end"]}):null]),e.open||!i?s(tu,{key:c,config:r}):null])}}}),tu=$({name:"SidebarLinks",props:{config:{type:Array,required:!0}},setup(e){const t=yt(),l=W(-1),n=a=>{l.value=a===l.value?-1:a};return ce(()=>t.path,()=>{const a=e.config.findIndex(i=>Fc(t,i));l.value=a},{immediate:!0,flush:"post"}),()=>s("ul",{class:"vp-sidebar-links"},e.config.map((a,i)=>s("li",a.type==="group"?s(Tf,{config:a,open:i===l.value,onToggle:()=>n(i)}):s(xf,{config:a}))))}}),Af=$({name:"SideBar",slots:Object,setup(e,{slots:t}){const l=yt(),n=ie(),a=Fi(),i=Ge();return ke(()=>{ce(()=>l.hash,r=>{const o=document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${l.path}${r}"]`);if(!o)return;const{top:c,height:u}=i.value.getBoundingClientRect(),{top:d,height:p}=o.getBoundingClientRect();dc+u&&o.scrollIntoView(!1)})}),()=>{var r,o,c;return s("aside",{ref:i,id:"sidebar",class:["vp-sidebar",{"hide-icon":n.value.sidebarIcon===!1}]},[(r=t.top)==null?void 0:r.call(t),((o=t.default)==null?void 0:o.call(t))||s(tu,{config:a.value}),(c=t.bottom)==null?void 0:c.call(t)])}}}),Ni=$({name:"CommonWrapper",props:{containerClass:{type:String,default:""},noNavbar:Boolean,noSidebar:Boolean,noToc:Boolean},slots:Object,setup(e,{slots:t}){const l=Ve(),n=ue(),a=Ee(),i=ie(),{isMobile:r,isPC:o}=dn(),[c,u]=ls(!1),[d,p]=ls(!1),h=Fi(),v=W(!1),b=E(()=>e.noNavbar||a.value.navbar===!1||i.value.navbar===!1?!1:!!(n.value.title||i.value.logo||i.value.repo||i.value.navbar)),w=E(()=>e.noSidebar?!1:a.value.sidebar!==!1&&h.value.length!==0&&!a.value.home),L=E(()=>e.noToc||a.value.home?!1:a.value.toc||i.value.toc!==!1&&a.value.toc!==!1),m={x:0,y:0},_=C=>{m.x=C.changedTouches[0].clientX,m.y=C.changedTouches[0].clientY},I=C=>{const V=C.changedTouches[0].clientX-m.x,A=C.changedTouches[0].clientY-m.y;Math.abs(V)>Math.abs(A)*1.5&&Math.abs(V)>40&&(V>0&&m.x<=80?u(!0):u(!1))},R=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;let B=0;return $e("scroll",v1(()=>{const C=R();C<=58||C{C||u(!1)}),ke(()=>{const C=_c(document.body);ce(c,A=>{C.value=A});const V=l.afterEach(()=>{u(!1)});an(()=>{C.value=!1,V()})}),()=>s(ct("GlobalEncrypt")?Je("GlobalEncrypt"):Xo,()=>s("div",{class:["theme-container",{"no-navbar":!b.value,"no-sidebar":!w.value&&!(t.sidebar||t.sidebarTop||t.sidebarBottom),"has-toc":L.value,"hide-navbar":v.value,"sidebar-collapsed":!r.value&&!o.value&&d.value,"sidebar-open":r.value&&c.value},e.containerClass,a.value.containerClass||""],onTouchStart:_,onTouchEnd:I},[b.value?s(Lf,{onToggleSidebar:()=>u()},{startBefore:()=>{var C;return(C=t.navbarStartBefore)==null?void 0:C.call(t)},startAfter:()=>{var C;return(C=t.navbarStartAfter)==null?void 0:C.call(t)},centerBefore:()=>{var C;return(C=t.navbarCenterBefore)==null?void 0:C.call(t)},centerAfter:()=>{var C;return(C=t.navbarCenterAfter)==null?void 0:C.call(t)},endBefore:()=>{var C;return(C=t.navbarEndBefore)==null?void 0:C.call(t)},endAfter:()=>{var C;return(C=t.navbarEndAfter)==null?void 0:C.call(t)},screenTop:()=>{var C;return(C=t.navScreenTop)==null?void 0:C.call(t)},screenBottom:()=>{var C;return(C=t.navScreenBottom)==null?void 0:C.call(t)}}):null,s(Bt,{name:"fade"},()=>c.value?s("div",{class:"vp-sidebar-mask",onClick:()=>u(!1)}):null),s(Bt,{name:"fade"},()=>r.value?null:s("div",{class:"toggle-sidebar-wrapper",onClick:()=>p()},s("span",{class:["arrow",d.value?"end":"start"]}))),s(Af,{},{...t.sidebar?{default:()=>t.sidebar()}:{},top:()=>{var C;return(C=t.sidebarTop)==null?void 0:C.call(t)},bottom:()=>{var C;return(C=t.sidebarBottom)==null?void 0:C.call(t)}}),t.default(),s(of)]))}}),pe=$({name:"DropTransition",props:{type:{type:String,default:"single"},delay:{type:Number,default:0},duration:{type:Number,default:.25},appear:Boolean},slots:Object,setup(e,{slots:t}){const l=a=>{a.style.transition=`transform ${e.duration}s ease-in-out ${e.delay}s, opacity ${e.duration}s ease-in-out ${e.delay}s`,a.style.transform="translateY(-20px)",a.style.opacity="0"},n=a=>{a.style.transform="translateY(0)",a.style.opacity="1"};return()=>s(e.type==="single"?Bt:h0,{name:"drop",appear:e.appear,onAppear:l,onAfterAppear:n,onEnter:l,onAfterEnter:n,onBeforeLeave:l},()=>t.default())}});const Ka=({custom:e})=>s(No,{class:["theme-hope-content",{custom:e}]});Ka.displayName="MarkdownContent",Ka.props={custom:Boolean};var ji=Ka;const lu=()=>s(de,{name:"author"},()=>s("path",{d:"M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"}));lu.displayName="AuthorIcon";const nu=()=>s(de,{name:"calendar"},()=>s("path",{d:"M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"}));nu.displayName="CalendarIcon";const au=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));au.displayName="CategoryIcon";const iu=()=>s(de,{name:"print"},()=>s("path",{d:"M819.2 364.8h-44.8V128c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v236.8h-44.8C145.067 364.8 96 413.867 96 473.6v192c0 59.733 49.067 108.8 108.8 108.8h44.8V896c0 17.067 14.933 32 32 32h460.8c17.067 0 32-14.933 32-32V774.4h44.8c59.733 0 108.8-49.067 108.8-108.8v-192c0-59.733-49.067-108.8-108.8-108.8zM313.6 160h396.8v204.8H313.6V160zm396.8 704H313.6V620.8h396.8V864zM864 665.6c0 25.6-19.2 44.8-44.8 44.8h-44.8V588.8c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v121.6h-44.8c-25.6 0-44.8-19.2-44.8-44.8v-192c0-25.6 19.2-44.8 44.8-44.8h614.4c25.6 0 44.8 19.2 44.8 44.8v192z"}));iu.displayName="PrintIcon";const ru=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));ru.displayName="TagIcon";const su=()=>s(de,{name:"timer"},()=>s("path",{d:"M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"}));su.displayName="TimerIcon";const ou=()=>s(de,{name:"word"},()=>[s("path",{d:"M518.217 432.64V73.143A73.143 73.143 0 01603.43 1.097a512 512 0 01419.474 419.474 73.143 73.143 0 01-72.046 85.212H591.36a73.143 73.143 0 01-73.143-73.143z"}),s("path",{d:"M493.714 566.857h340.297a73.143 73.143 0 0173.143 85.577A457.143 457.143 0 11371.566 117.76a73.143 73.143 0 0185.577 73.143v339.383a36.571 36.571 0 0036.571 36.571z"})]);ou.displayName="WordIcon";const zt=()=>{const e=ie();return E(()=>e.value.metaLocales)};var Of=$({name:"AuthorInfo",inheritAttrs:!1,props:{author:{type:Array,required:!0},pure:Boolean},setup(e){const t=zt();return()=>e.author.length?s("span",{class:"page-author-info","aria-label":`${t.value.author}${e.pure?"":"🖊"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(lu),s("span",e.author.map(l=>l.url?s("a",{class:"page-author-item",href:l.url,target:"_blank",rel:"noopener noreferrer"},l.name):s("span",{class:"page-author-item"},l.name))),s("span",{property:"author",content:e.author.map(l=>l.name).join(", ")})]):null}}),Pf=$({name:"CategoryInfo",inheritAttrs:!1,props:{category:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=zt(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.category.length?s("span",{class:"page-category-info","aria-label":`${n.value.category}${e.pure?"":"🌈"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(au),e.category.map(({name:i,path:r})=>s("span",{class:["page-category-item",{[`category${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"articleSection",content:e.category.map(({name:i})=>i).join(",")})]):null}}),If=$({name:"DateInfo",inheritAttrs:!1,props:{date:{type:Object,default:null},localizedDate:{type:String,default:""},pure:Boolean},setup(e){const t=Mo(),l=zt();return()=>e.date?s("span",{class:"page-date-info","aria-label":`${l.value.date}${e.pure?"":"📅"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(nu),s("span",s(Zn,()=>e.localizedDate||e.date.toLocaleDateString(t.value))),s("meta",{property:"datePublished",content:e.date.toISOString()||""})]):null}}),Rf=$({name:"OriginalInfo",inheritAttrs:!1,props:{isOriginal:Boolean},setup(e){const t=zt();return()=>e.isOriginal?s("span",{class:"page-original-info"},t.value.origin):null}}),Cf=$({name:"ReadingTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=zt(),l=E(()=>{if(!e.readingTime)return null;const{minutes:n}=e.readingTime;return n<1?"PT1M":`PT${Math.round(n)}M`});return()=>{var n,a;return(n=e.readingTimeLocale)!=null&&n.time?s("span",{class:"page-reading-time-info","aria-label":`${t.value.readingTime}${e.pure?"":"⌛"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(su),s("span",(a=e.readingTimeLocale)==null?void 0:a.time),s("meta",{property:"timeRequired",content:l.value})]):null}}}),Sf=$({name:"TagInfo",inheritAttrs:!1,props:{tag:{type:Array,default:()=>[]},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=zt(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.tag.length?s("span",{class:"page-tag-info","aria-label":`${n.value.tag}${e.pure?"":"🏷"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ru),e.tag.map(({name:i,path:r})=>s("span",{class:["page-tag-item",{[`tag${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"keywords",content:e.tag.map(({name:i})=>i).join(",")})]):null}}),Df=$({name:"ReadTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=zt();return()=>{var l,n,a;return(l=e.readingTimeLocale)!=null&&l.words?s("span",{class:"page-word-info","aria-label":`${t.value.words}${e.pure?"":"🔠"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ou),s("span",(n=e.readingTimeLocale)==null?void 0:n.words),s("meta",{property:"wordCount",content:(a=e.readingTime)==null?void 0:a.words})]):null}}}),cu=$({name:"PageInfo",components:{AuthorInfo:Of,CategoryInfo:Pf,DateInfo:If,OriginalInfo:Rf,PageViewInfo:()=>null,ReadingTimeInfo:Cf,TagInfo:Sf,WordInfo:Df},props:{items:{type:[Array,Boolean],default:()=>["Author","Original","Date","PageView","ReadingTime","Category","Tag"]},info:{type:Object,required:!0}},setup(e){const t=Ol();return()=>e.items?s("div",{class:"page-info"},e.items.map(l=>s(Je(`${l}Info`),{...e.info,pure:t.value}))):null}}),Mf=$({name:"PrintButton",setup(){const e=el(),t=ie();return()=>e.value.print===!1?null:s("button",{type:"button",class:"print-button",title:t.value.metaLocales.print,onClick:()=>{window.print()}},s(iu))}});const $f=({title:e,level:t,slug:l})=>s(Ce,{to:`#${l}`,class:["toc-link",`level${t}`]},()=>e),Ja=(e,t)=>{const l=yt();return e.length&&t>0?s("ul",{class:"toc-list"},e.map(n=>{const a=Ja(n.children,t-1);return[s("li",{class:["toc-item",{active:Ri(l,`#${n.slug}`)}]},$f(n)),a?s("li",a):null]})):null};var uu=$({name:"TOC",props:{items:{type:Array,default:()=>[]},headerDepth:{type:Number,default:2}},slots:Object,setup(e,{slots:t}){const l=yt(),n=ue(),a=zt(),i=Ge(),r=W("-1.7rem"),o=u=>{var d;(d=i.value)==null||d.scrollTo({top:u,behavior:"smooth"})},c=()=>{if(i.value){const u=document.querySelector(".toc-item.active");u?r.value=`${u.getBoundingClientRect().top-i.value.getBoundingClientRect().top+i.value.scrollTop}px`:r.value="-1.7rem"}else r.value="-1.7rem"};return ke(()=>{ce(()=>l.hash,u=>{if(i.value){const d=document.querySelector(`#toc a.toc-link[href$="${u}"]`);if(!d)return;const{top:p,height:h}=i.value.getBoundingClientRect(),{top:v,height:b}=d.getBoundingClientRect();vp+h&&o(i.value.scrollTop+v+b-p-h)}}),ce(()=>l.fullPath,()=>c(),{flush:"post",immediate:!0})}),()=>{var u,d;const p=e.items.length?Ja(e.items,e.headerDepth):n.value.headers?Ja(n.value.headers,e.headerDepth):null;return p?s("div",{class:"toc-place-holder"},[s("aside",{id:"toc"},[(u=t.before)==null?void 0:u.call(t),s("div",{class:"toc-header"},[a.value.toc,s(Mf)]),s("div",{class:"toc-wrapper",ref:i},[p,s("div",{class:"toc-marker",style:{top:r.value}})]),(d=t.after)==null?void 0:d.call(t)])]):null}}}),Bi=$({name:"SkipLink",props:{content:{type:String,default:"main-content"}},setup(e){const t=ue(),l=ie(),n=Ge(),a=({target:i})=>{const r=document.querySelector(i.hash);if(r){const o=()=>{r.removeAttribute("tabindex"),r.removeEventListener("blur",o)};r.setAttribute("tabindex","-1"),r.addEventListener("blur",o),r.focus(),window.scrollTo(0,0)}};return ke(()=>{ce(()=>t.value.path,()=>n.value.focus())}),()=>[s("span",{ref:n,tabindex:"-1"}),s("a",{href:`#${e.content}`,class:"vp-skip-link sr-only",onClick:a},l.value.routeLocales.skipToContent)]}});let Pa=null,In=null;const Vf={wait:()=>Pa,pending:()=>{Pa=new Promise(e=>In=e)},resolve:()=>{In==null||In(),Pa=null,In=null}},du=()=>Vf;var Ff=$({name:"FadeSlideY",slots:Object,setup(e,{slots:t}){const{resolve:l,pending:n}=du();return()=>s(Bt,{name:"fade-slide-y",mode:"out-in",onBeforeEnter:l,onBeforeLeave:n},()=>{var a;return(a=t.default)==null?void 0:a.call(t)})}});const Nf=(e,t)=>{const l=e.replace(t,"/").split("/"),n=[];let a=xi(t);return l.forEach((i,r)=>{r!==l.length-1?(a+=`${i}/`,n.push({link:a,name:i||"Home"})):i!==""&&(a+=i,n.push({link:a,name:i}))}),n},pu=(e,{slots:t})=>{var l,n;const{bgImage:a,bgImageDark:i,bgImageStyle:r,color:o,description:c,image:u,imageDark:d,header:p,features:h=[]}=e;return s("div",{class:"vp-feature-wrapper"},[a?s("div",{class:["vp-feature-bg",{light:i}],style:[{"background-image":`url(${a})`},r]}):null,i?s("div",{class:"vp-feature-bg dark",style:[{"background-image":`url(${i})`},r]}):null,s("div",{class:"vp-feature",style:o?{color:o}:{}},[((l=t.image)==null?void 0:l.call(t,e))||[u?s("img",{class:["vp-feature-image",{light:d}],src:xe(u),alt:p}):null,d?s("img",{class:"vp-feature-image dark",src:xe(d),alt:p}):null],((n=t.info)==null?void 0:n.call(t,e))||[p?s("h2",{class:"vp-feature-header"},p):null,c?s("p",{class:"vp-feature-description",innerHTML:c}):null],h.length?s("div",{class:"vp-features"},h.map(({icon:v,title:b,details:w,link:L})=>{const m=[s("h3",{class:"vp-feature-title"},[s(Ne,{icon:v}),s("span",{innerHTML:b})]),s("p",{class:"vp-feature-details",innerHTML:w})];return L?Bn(L)?s("a",{class:"vp-feature-item link",href:L,role:"navigation","aria-label":b,target:"_blank"},m):s(Ce,{class:"vp-feature-item link",to:L,role:"navigation","aria-label":b},()=>m):s("div",{class:"vp-feature-item"},m)})):null])])};pu.displayName="FeaturePanel";var _s=pu,jf=$({name:"HeroInfo",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=E(()=>l.value.heroFullScreen??!1),i=E(()=>{const{heroText:u,tagline:d}=l.value;return{text:u??n.value.title??"Hello",tagline:d??n.value.description??"",isFullScreen:a.value}}),r=E(()=>{const{heroText:u,heroImage:d,heroImageDark:p,heroAlt:h,heroImageStyle:v}=l.value;return{image:d?xe(d):null,imageDark:p?xe(p):null,heroStyle:v,alt:h||u||"hero image",isFullScreen:a.value}}),o=E(()=>{const{bgImage:u,bgImageDark:d,bgImageStyle:p}=l.value;return{image:Tt(u)?xe(u):null,imageDark:Tt(d)?xe(d):null,bgStyle:p,isFullScreen:a.value}}),c=E(()=>l.value.actions??[]);return()=>{var u,d,p;return s("header",{class:["vp-hero-info-wrapper",{fullscreen:a.value}]},[((u=t.heroBg)==null?void 0:u.call(t,o.value))||[o.value.image?s("div",{class:["vp-hero-mask",{light:o.value.imageDark}],style:[{"background-image":`url(${o.value.image})`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-hero-mask dark",style:[{"background-image":`url(${o.value.imageDark})`},o.value.bgStyle]}):null],s("div",{class:"vp-hero-info"},[((d=t.heroImage)==null?void 0:d.call(t,r.value))||s(pe,{appear:!0,type:"group"},()=>[r.value.image?s("img",{key:"light",class:["vp-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),((p=t.heroInfo)==null?void 0:p.call(t,i.value))??s("div",{class:"vp-hero-infos"},[i.value.text?s(pe,{appear:!0,delay:.04},()=>s("h1",{id:"main-title"},i.value.text)):null,i.value.tagline?s(pe,{appear:!0,delay:.08},()=>s("p",{class:"vp-description",innerHTML:i.value.tagline})):null,c.value.length?s(pe,{appear:!0,delay:.12},()=>s("p",{class:"vp-actions"},c.value.map(h=>s(ze,{class:["vp-action",h.type||"default"],config:h,noExternalLinkIcon:!0})))):null])])])}}});const hu=(e,{slots:t})=>{var l,n,a;const{bgImage:i,bgImageDark:r,bgImageStyle:o,color:c,description:u,image:d,imageDark:p,header:h,highlights:v=[],type:b="un-order"}=e;return s("div",{class:"vp-highlight-wrapper",style:c?{color:c}:{}},[i?s("div",{class:["vp-highlight-bg",{light:r}],style:[{"background-image":`url(${i})`},o]}):null,r?s("div",{class:"vp-highlight-bg dark",style:[{"background-image":`url(${r})`},o]}):null,s("div",{class:"vp-highlight"},[((l=t.image)==null?void 0:l.call(t,e))||[d?s("img",{class:["vp-highlight-image",{light:p}],src:xe(d),alt:h}):null,p?s("img",{class:"vp-highlight-image dark",src:xe(p),alt:h}):null],((n=t.info)==null?void 0:n.call(t,e))||[s("div",{class:"vp-highlight-info-wrapper"},s("div",{class:"vp-highlight-info"},[h?s("h2",{class:"vp-highlight-header",innerHTML:h}):null,u?s("p",{class:"vp-highlight-description",innerHTML:u}):null,((a=t.highlights)==null?void 0:a.call(t,v))||s(b==="order"?"ol":b==="no-order"?"dl":"ul",{class:"vp-highlights"},v.map(({icon:w,title:L,details:m,link:_})=>{const I=[s(b==="no-order"?"dt":"h3",{class:"vp-highlight-title"},[w?s(Ne,{class:"vp-highlight-icon",icon:w}):null,s("span",{innerHTML:L})]),m?s(b==="no-order"?"dd":"p",{class:"vp-highlight-details",innerHTML:m}):null];return s(b==="no-order"?"div":"li",{class:["vp-highlight-item-wrapper",{link:_}]},_?Uh(_)?s("a",{class:"vp-highlight-item link",href:_,role:"navigation","aria-label":L,target:"_blank"},I):s(Ce,{class:"vp-highlight-item link",to:_,role:"navigation","aria-label":L},()=>I):s("div",{class:"vp-highlight-item"},I))}))]))]])])};hu.displayName="HighlightPanel";var Bf=hu,Hf=$({name:"HomePage",slots:Object,setup(e,{slots:t}){const l=Ol(),n=Ee(),a=E(()=>{const{features:r}=n.value;return X(r)?r:null}),i=E(()=>{const{highlights:r}=n.value;return X(r)?r:null});return()=>{var r,o,c,u;return s("main",{id:"main-content",class:["vp-project-home ",{pure:l.value}],"aria-labelledby":n.value.heroText===null?"":"main-title"},[(r=t.top)==null?void 0:r.call(t),s(jf),((o=i.value)==null?void 0:o.map(d=>"features"in d?s(_s,d):s(Bf,d)))||(a.value?s(pe,{appear:!0,delay:.24},()=>s(_s,{features:a.value})):null),(c=t.center)==null?void 0:c.call(t),s(pe,{appear:!0,delay:.32},()=>s(ji)),(u=t.bottom)==null?void 0:u.call(t)])}}}),zf=$({name:"BreadCrumb",setup(){const e=Ve(),t=ue(),l=mt(),n=Ee(),a=ie(),i=Ge([]),r=E(()=>(n.value.breadcrumb||n.value.breadcrumb!==!1&&a.value.breadcrumb!==!1)&&i.value.length>1),o=E(()=>n.value.breadcrumbIcon||n.value.breadcrumbIcon!==!1&&a.value.breadcrumbIcon!==!1),c=()=>{const u=e.getRoutes(),d=Nf(t.value.path,l.value).map(({link:p,name:h})=>{const v=u.find(b=>b.path===p);if(v){const{meta:b,path:w}=_l(e,v.path);return{title:b[be.shortTitle]||b[be.title]||h,icon:b[be.icon],path:w}}return null}).filter(p=>p!==null);d.length>1&&(i.value=d)};return ke(()=>{c(),ce(()=>t.value.path,c)}),()=>s("nav",{class:["vp-breadcrumb",{disable:!r.value}]},r.value?s("ol",{vocab:"https://schema.org/",typeof:"BreadcrumbList"},i.value.map((u,d)=>s("li",{class:{"is-active":i.value.length-1===d},property:"itemListElement",typeof:"ListItem"},[s(Ce,{to:u.path,property:"item",typeof:"WebPage"},()=>[o.value?s(Ne,{icon:u.icon}):null,s("span",{property:"name"},u.title||"Unknown")]),s("meta",{property:"position",content:d+1})]))):[])}});const ks=e=>{const t=Ve();return e===!1?!1:ae(e)?pl(t,e,!0):Li(e)?e:null},Qa=(e,t,l)=>{const n=e.findIndex(a=>a.link===t);if(n!==-1){const a=e[n+l];return a!=null&&a.link?a:null}for(const a of e)if(a.children){const i=Qa(a.children,t,l);if(i)return i}return null};var qf=$({name:"PageNav",setup(){const e=ie(),t=Ee(),l=Fi(),n=ue(),a=un(),i=E(()=>{const o=ks(t.value.prev);return o===!1?null:o||(e.value.prevLink===!1?null:Qa(l.value,n.value.path,-1))}),r=E(()=>{const o=ks(t.value.next);return o===!1?null:o||(e.value.nextLink===!1?null:Qa(l.value,n.value.path,1))});return $e("keydown",o=>{o.altKey&&(o.key==="ArrowRight"?r.value&&(a(r.value.link),o.preventDefault()):o.key==="ArrowLeft"&&i.value&&(a(i.value.link),o.preventDefault()))}),()=>i.value||r.value?s("nav",{class:"vp-page-nav"},[i.value?s(ze,{class:"prev",config:i.value},()=>{var o,c;return[s("div",{class:"hint"},[s("span",{class:"arrow start"}),e.value.metaLocales.prev]),s("div",{class:"link"},[s(Ne,{icon:(o=i.value)==null?void 0:o.icon}),(c=i.value)==null?void 0:c.text])]}):null,r.value?s(ze,{class:"next",config:r.value},()=>{var o,c;return[s("div",{class:"hint"},[e.value.metaLocales.next,s("span",{class:"arrow end"})]),s("div",{class:"link"},[(o=r.value)==null?void 0:o.text,s(Ne,{icon:(c=r.value)==null?void 0:c.icon})])]}):null]):null}});const Gf={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},Uf=({docsRepo:e,docsBranch:t,docsDir:l,filePathRelative:n,editLinkPattern:a})=>{if(!n)return null;const i=rc(e);let r;return a?r=a:i!==null&&(r=Gf[i]),r?r.replace(/:repo/,Zt(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Po(`${xi(l)}/${n}`)):null},Wf=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{const{repo:n,docsRepo:a=n,docsBranch:i="main",docsDir:r="",editLink:o,editLinkPattern:c=""}=e.value;if(!(l.value.editLink??o??!0)||!a)return null;const u=Uf({docsRepo:a,docsBranch:i,docsDir:r,editLinkPattern:c,filePathRelative:t.value.filePathRelative});return u?{text:e.value.metaLocales.editLink,link:u}:null})},Kf=()=>{const e=rn(),t=ie(),l=ue(),n=Ee();return E(()=>{var a,i;return!(n.value.lastUpdated??t.value.lastUpdated??!0)||!((a=l.value.git)!=null&&a.updatedTime)?null:new Date((i=l.value.git)==null?void 0:i.updatedTime).toLocaleString(e.value.lang)})},Jf=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{var n;return l.value.contributors??e.value.contributors??!0?((n=t.value.git)==null?void 0:n.contributors)??null:null})};var Qf=$({name:"PageTitle",setup(){const e=ue(),t=Ee(),l=ie(),{info:n,items:a}=Xv();return()=>s("div",{class:"vp-page-title"},[s("h1",[l.value.titleIcon===!1?null:s(Ne,{icon:t.value.icon}),e.value.title]),s(cu,{info:n.value,...a.value===null?{}:{items:a.value}}),s("hr")])}});const vu=()=>s(de,{name:"edit"},()=>[s("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),s("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})]);vu.displayName="EditIcon";var Yf=$({name:"PageMeta",setup(){const e=ie(),t=Wf(),l=Kf(),n=Jf();return()=>{const{metaLocales:a}=e.value;return s("footer",{class:"page-meta"},[t.value?s("div",{class:"meta-item edit-link"},s(ze,{class:"label",config:t.value},{before:()=>s(vu)})):null,s("div",{class:"meta-item git-info"},[l.value?s("div",{class:"update-time"},[s("span",{class:"label"},`${a.lastUpdated}: `),s(Zn,()=>s("span",{class:"info"},l.value))]):null,n.value&&n.value.length?s("div",{class:"contributors"},[s("span",{class:"label"},`${a.contributors}: `),n.value.map(({email:i,name:r},o)=>[s("span",{class:"contributor",title:`email: ${i}`},r),o!==n.value.length-1?",":""])]):null])])}}}),Xf=$({name:"NormalPage",slots:Object,setup(e,{slots:t}){const l=Ee(),n=ue(),{isDarkmode:a}=pn(),i=ie(),r=E(()=>l.value.toc||l.value.toc!==!1&&i.value.toc!==!1);return()=>s("main",{id:"main-content",class:"vp-page"},s(ct("LocalEncrypt")?Je("LocalEncrypt"):Xo,()=>{var o,c,u,d;return[(o=t.top)==null?void 0:o.call(t),l.value.cover?s("img",{class:"page-cover",src:xe(l.value.cover),alt:n.value.title,"no-view":""}):null,s(zf),s(Qf),r.value?s(uu,{headerDepth:l.value.headerDepth??i.value.headerDepth??2},{before:()=>{var p;return(p=t.tocBefore)==null?void 0:p.call(t)},after:()=>{var p;return(p=t.tocAfter)==null?void 0:p.call(t)}}):null,(c=t.contentBefore)==null?void 0:c.call(t),s(ji),(u=t.contentAfter)==null?void 0:u.call(t),s(Yf),s(qf),ct("CommentService")?s(Je("CommentService"),{darkmode:a.value}):null,(d=t.bottom)==null?void 0:d.call(t)]}))}}),Zf=$({name:"Layout",setup(){const e=el(),t=ie(),l=ue(),n=Ee(),{isMobile:a}=dn(),i=E(()=>{var r,o;return((r=t.value.blog)==null?void 0:r.sidebarDisplay)||((o=e.value.blog)==null?void 0:o.sidebarDisplay)||"mobile"});return()=>[s(Bi),s(Ni,{},{default:()=>n.value.home?s(Hf):s(Ff,()=>s(Xf,{key:l.value.path})),...i.value!=="none"?{navScreenBottom:()=>s(Je("BloggerInfo"))}:{},...!a.value&&i.value==="always"?{sidebar:()=>s(Je("BloggerInfo"))}:{}})]}}),e2=$({name:"NotFoundHint",setup(){const e=ie(),t=()=>{const l=e.value.routeLocales.notFoundMsg;return l[Math.floor(Math.random()*l.length)]};return()=>s("div",{class:"not-found-hint"},[s("p",{class:"error-code"},"404"),s("h1",{class:"error-title"},e.value.routeLocales.notFoundTitle),s("p",{class:"error-hint"},t())])}}),t2=$({name:"NotFound",slots:Object,setup(e,{slots:t}){const l=mt(),n=ie(),{navigate:a}=qa({to:n.value.home??l.value});return()=>[s(Bi),s(Ni,{noSidebar:!0},()=>{var i;return s("main",{id:"main-content",class:"vp-page not-found"},((i=t.default)==null?void 0:i.call(t))||[s(e2),s("div",{class:"actions"},[s("button",{type:"button",class:"action-button",onClick:()=>{window.history.go(-1)}},n.value.routeLocales.back),s("button",{type:"button",class:"action-button",onClick:()=>a()},n.value.routeLocales.home)])])})]}});const l2={GitHub:'',BiliBili:'',Email:'',Rss:''},n2={category:{"/":{path:"/category/",map:{}}},tag:{"/":{path:"/tag/",map:{Daily:{path:"/tag/daily/",keys:["v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-f1efc11c","v-f47f129e","v-3f274907","v-30a50b00","v-0481cc80","v-1e305501","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-386e94f1","v-d767e98e","v-0d6828c2"]},Frontend:{path:"/tag/frontend/",keys:["v-30a50b00","v-1e305501","v-7320140c","v-72e84a92","v-6614b1a1"]},Video:{path:"/tag/video/",keys:["v-5c48d497","v-ca672354","v-7a74360a","v-1aafac08","v-79e139f8","v-404740fa","v-f1efc11c","v-f47f129e","v-daf302c6","v-30a50b00","v-0481cc80","v-1e305501"]},MySQL:{path:"/tag/mysql/",keys:["v-2f6ae09c","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8"]},AI:{path:"/tag/ai/",keys:["v-81a71778","v-7a74360a","v-404740fa","v-221efd1f"]},Emotion:{path:"/tag/emotion/",keys:["v-f47f129e"]},"Working Experience":{path:"/tag/working-experience/",keys:["v-fd7b7f92","v-f47f129e"]},Tool:{path:"/tag/tool/",keys:["v-404740fa","v-17809471"]},Design:{path:"/tag/design/",keys:["v-bd2b5fe8"]},English:{path:"/tag/english/",keys:["v-221efd1f","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-93b316c0","v-971cc7fe"]},DevOps:{path:"/tag/devops/",keys:["v-3c0c097a","v-2e81d6a4","v-daf302c6","v-3f274907","v-6614b1a1"]},Linux:{path:"/tag/linux/",keys:["v-2e81d6a4","v-daf302c6","v-3f274907"]},S3:{path:"/tag/s3/",keys:["v-6614b1a1"]},OBS:{path:"/tag/obs/",keys:["v-6614b1a1"]},OSS:{path:"/tag/oss/",keys:["v-6614b1a1"]},Python:{path:"/tag/python/",keys:["v-8903fc5e","v-3c0c097a","v-7ba1021b","v-de221860","v-966d933e","v-0be1af08","v-071be141","v-5b37b3c6"]},Git:{path:"/tag/git/",keys:["v-60021cbc","v-966d933e","v-071be141","v-4008ff77","v-48e494ef","v-0fbf5fdc","v-1e5872c4","v-642eaaea"]},GitLab:{path:"/tag/gitlab/",keys:["v-071be141","v-4008ff77"]},Java:{path:"/tag/java/",keys:["v-538a98d7","v-2f6ae09c","v-f5471cb0","v-b5a78a7a","v-ca672354","v-1aafac08","v-100d1814","v-65b23736","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-386e94f1","v-d767e98e","v-0d6828c2"]},"Node.js":{path:"/tag/node.js/",keys:["v-75616b85","v-0be1af08","v-4008ff77","v-c488ac58","v-efcacba2"]},JavaScript:{path:"/tag/javascript/",keys:["v-538a98d7"]},llm:{path:"/tag/llm/",keys:["v-5d5f3b26","v-8903fc5e"]},Testing:{path:"/tag/testing/",keys:["v-7ba1021b","v-113531b4","v-65b23736","v-0be1af08","v-c488ac58","v-efcacba2"]},Gitlab:{path:"/tag/gitlab/",keys:["v-7ba1021b"]}}}}},a2={article:{"/":{path:"/article/",keys:["v-5d5f3b26","v-8903fc5e","v-3c0c097a","v-2e81d6a4","v-7ba1021b","v-de221860","v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-daf302c6","v-3f274907","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-966d933e","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-386e94f1","v-d767e98e","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-0d6828c2","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2","v-6614b1a1"]}},star:{"/":{path:"/star/",keys:[]}},timeline:{"/":{path:"/timeline/",keys:["v-5d5f3b26","v-8903fc5e","v-3c0c097a","v-2e81d6a4","v-7ba1021b","v-de221860","v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-daf302c6","v-3f274907","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-966d933e","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-386e94f1","v-d767e98e","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-0d6828c2","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2","v-6614b1a1"]}}},ws=W(n2),fu=(e="")=>{const t=ue(),l=Ve(),n=mt();return E(()=>{var a;const i=e||((a=Ee().value.blog)==null?void 0:a.key)||"";if(!i)return console.warn("useBlogCategory: key not found"),{path:"/",map:{}};const r=l.getRoutes();if(!ws.value[i])throw new Error(`useBlogCategory: key ${i} is invalid`);const o=ws.value[i][n.value],c={path:o.path,map:{}};for(const u in o.map){const d=o.map[u];c.map[u]={path:d.path,items:[]};for(const p of d.keys){const h=r.find(({name:v})=>v===p);if(h){const v=_l(l,h.path);c.map[u].items.push({path:v.path,info:v.meta})}}t.value.path===d.path&&(c.currentItems=c.map[u].items)}return c})},Es=W(a2),ia=(e="")=>{const t=Ve(),l=mt();return E(()=>{var n;const a=e||((n=Ee().value.blog)==null?void 0:n.key)||"";if(!a)return console.warn("useBlogType: key not found"),{path:"/",items:[]};if(!Es.value[a])throw new Error(`useBlogType: key ${e} is invalid`);const i=t.getRoutes(),r=Es.value[a][l.value],o={path:r.path,items:[]};for(const c of r.keys){const u=i.find(({name:d})=>d===c);if(u){const d=_l(t,u.path);o.items.push({path:d.path,info:d.meta})}}return o})};const i2="/assets/hero-197a9d2d.jpg",gu=Symbol.for("categoryMap"),hn=()=>{const e=me(gu);if(!e)throw new Error("useCategoryMap() is called without provider.");return e},r2=()=>{const e=fu("category");ot(gu,e)},vn=()=>{const e=el(),t=ie();return E(()=>({...e.value.blog,...t.value.blog}))},mu=Symbol.for("tagMap"),fn=()=>{const e=me(mu);if(!e)throw new Error("useTagMap() is called without provider.");return e},s2=()=>{const e=fu("tag");ot(mu,e)},o2=e=>{const t=ie();return E(()=>{const{[be.author]:l}=e.value;return l?Yl(l):l===!1?[]:Yl(t.value.author,!1)})},c2=e=>{const t=hn();return E(()=>nc(e.value[be.category]).map(l=>({name:l,path:t.value.map[l].path})))},u2=e=>{const t=fn();return E(()=>ac(e.value[be.tag]).map(l=>({name:l,path:t.value.map[l].path})))},d2=e=>E(()=>{const{[be.date]:t}=e.value;return Pi(t)}),p2=e=>{const t=xl(e,"info"),l=vn(),n=o2(t),a=c2(t),i=u2(t),r=d2(t),o=Sc(),c=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value[be.localizedDate]||"",tag:i.value,isOriginal:t.value[be.isOriginal]||!1,readingTime:t.value[be.readingTime]||null,readingTimeLocale:t.value[be.readingTime]&&o.value?Cc(t.value[be.readingTime],o.value):null,pageview:e.path})),u=E(()=>l.value.articleInfo);return{info:c,items:u}},yu=Symbol(""),gn=()=>{const e=me(yu);if(!e)throw new Error("useArticles() is called without provider.");return e},h2=()=>{const e=ia("article");ot(yu,e)},bu=Symbol(""),Hi=()=>{const e=me(bu);if(!e)throw new Error("useStars() is called without provider.");return e},v2=()=>{const e=ia("star");ot(bu,e)},_u=Symbol(""),zi=()=>{const e=me(_u);if(!e)throw new Error("useTimelines() is called without provider.");return e},f2=()=>{const e=ia("timeline"),t=E(()=>{const l=[];return e.value.items.forEach(({info:n,path:a})=>{const i=Pi(n[be.date]),r=i==null?void 0:i.getFullYear(),o=i?i.getMonth()+1:null,c=i==null?void 0:i.getDate();r&&o&&c&&((!l[0]||l[0].year!==r)&&l.unshift({year:r,items:[]}),l[0].items.push({date:`${o}/${c}`,info:n,path:a}))}),{...e.value,config:l.reverse()}});ot(_u,t)},g2=()=>{h2(),r2(),v2(),s2(),f2()};var m2=$({name:"SocialMedia",setup(){const e=vn(),t=Ol(),l=E(()=>{const n=e.value.medias;return n?sn(n).map(([a,i])=>({name:a,icon:l2[a],url:i})):[]});return()=>l.value.length?s("div",{class:"vp-social-medias"},l.value.map(({name:n,icon:a,url:i})=>s("a",{class:"vp-social-media",href:i,rel:"noopener noreferrer",target:"_blank","aria-label":n,...t.value?{}:{"data-balloon-pos":"up"},innerHTML:a}))):null}}),qi=$({name:"BloggerInfo",setup(){const e=vn(),t=rn(),l=ie(),n=gn(),a=hn(),i=fn(),r=zi(),o=un(),c=E(()=>{var h;return e.value.name||((h=Yl(l.value.author)[0])==null?void 0:h.name)||t.value.title}),u=E(()=>e.value.avatar||l.value.logo),d=E(()=>l.value.blogLocales),p=E(()=>e.value.intro);return()=>{const{article:h,category:v,tag:b,timeline:w}=d.value,L=[[n.value.path,n.value.items.length,h],[a.value.path,gt(a.value.map).length,v],[i.value.path,gt(i.value.map).length,b],[r.value.path,r.value.items.length,w]];return s("div",{class:"vp-blogger-info",vocab:"https://schema.org/",typeof:"Person"},[s("div",{class:"vp-blogger",...p.value?{style:{cursor:"pointer"},"aria-label":d.value.intro,"data-balloon-pos":"down",role:"navigation",onClick:()=>o(p.value)}:{}},[u.value?s("img",{class:["vp-blogger-avatar",{round:e.value.roundAvatar}],src:xe(u.value),property:"image",alt:"Blogger Avatar"}):null,c.value?s("div",{class:"vp-blogger-name",property:"name"},c.value):null,e.value.description?s("div",{class:"vp-blogger-description",innerHTML:e.value.description}):null,p.value?s("meta",{property:"url",content:xe(p.value)}):null]),s("div",{class:"vp-blog-counts"},L.map(([m,_,I])=>s(Ce,{class:"vp-blog-count",to:m},()=>[s("div",{class:"count"},_),s("div",I)]))),s(m2)])}}});const Ya=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));Ya.displayName="CategoryIcon";const Xa=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));Xa.displayName="TagIcon";const Gi=()=>s(de,{name:"timeline"},()=>s("path",{d:"M511.997 70.568c-243.797 0-441.429 197.633-441.429 441.435 0 243.797 197.632 441.429 441.43 441.429S953.431 755.8 953.431 512.002c0-243.796-197.637-441.434-441.435-441.434zm150.158 609.093-15.605 15.61c-8.621 8.615-22.596 8.615-31.215 0L472.197 552.126c-4.95-4.944-4.34-14.888-4.34-24.677V247.14c0-12.19 9.882-22.07 22.07-22.07h22.07c12.19 0 22.07 9.882 22.07 22.07v273.218l128.088 128.088c8.62 8.62 8.62 22.595 0 31.215zm0 0"}));Gi.displayName="TimelineIcon";const ku=()=>s(de,{name:"slides"},()=>s("path",{d:"M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"}));ku.displayName="SlideIcon";const wu=()=>s(de,{name:"sticky"},()=>[s("path",{d:"m381.3 733.8l-161.9 118c-5.9 4.5-13.2 6.6-20.1 6.6-8.7 0-17.7-3.4-24.3-10-12.2-12.2-13.9-31.3-3.5-45.2l144.5-195.5-113.6-112.9c-11.1-11.1-13.2-28.4-5.5-42 5.5-8.7 52.1-76.4 155.5-51 1.8 0.3 3.5 0.3 5.6 0.7 4.2 0.3 9 0.7 14.2 1.7 21.9 3.5 60.8-13.9 94.5-42.7 32.3-27.5 53.1-59.4 53.1-81.6 0-5.2 0-10.8-0.3-16-0.7-20.8-2.1-52.8 21.5-76.4 28.1-28.1 72.9-30.6 103.9-5.2 0.6 0.3 1 1 1.7 1.7 16.7 16.3 187.5 187.2 189.3 188.9 14.5 14.6 22.9 34.4 22.9 55.3 0 20.8-8 40.2-22.9 54.8-23.7 23.6-56 22.6-77.1 21.6-4.9 0-10.5-0.4-15.7-0.4-20.8 0-45.8 14.6-70.5 41.3-34.3 37.5-55.5 85.8-53.8 107.7 0.7 6.9 2.1 19.1 2.4 20.8 25 101.4-42.7 147.6-50.7 152.8-13.9 8.4-31.6 6.3-42.7-4.8l-112.1-112.2z"})]);wu.displayName="StickyIcon";const qn=()=>s(de,{name:"article"},()=>s("path",{d:"M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"}));qn.displayName="ArticleIcon";const Eu=()=>s(de,{name:"book"},()=>s("path",{d:"M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"}));Eu.displayName="BookIcon";const Lu=()=>s(de,{name:"link"},()=>s("path",{d:"M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"}));Lu.displayName="LinkIcon";const xu=()=>s(de,{name:"project"},()=>s("path",{d:"M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"}));xu.displayName="ProjectIcon";const Tu=()=>s(de,{name:"friend"},()=>s("path",{d:"M860.16 213.333A268.373 268.373 0 0 0 512 186.027a267.52 267.52 0 0 0-348.16 404.48L428.8 855.893a118.613 118.613 0 0 0 166.4 0l264.96-265.386a267.52 267.52 0 0 0 0-377.174zM800 531.627l-264.96 264.96a32.427 32.427 0 0 1-46.08 0L224 530.347a183.04 183.04 0 0 1 0-256 182.187 182.187 0 0 1 256 0 42.667 42.667 0 0 0 60.587 0 182.187 182.187 0 0 1 256 0 183.04 183.04 0 0 1 3.413 256z"}));Tu.displayName="FriendIcon";const Za=()=>s(de,{name:"slide-down"},()=>s("path",{d:"M108.775 312.23c13.553 0 27.106 3.734 39.153 11.806l375.205 250.338 363.641-252.808c32.587-21.624 76.499-12.83 98.123 19.757 21.685 32.467 12.95 76.56-19.576 98.184l-402.854 278.89c-23.733 15.901-54.694 15.962-78.547.12L69.501 442.097c-32.647-21.685-41.441-65.777-19.817-98.304 13.734-20.54 36.201-31.563 59.09-31.563Z"}));Za.displayName="SlideDownIcon";const Au=()=>s("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:"empty-icon",viewBox:"0 0 1024 1024",innerHTML:''});Au.displayName="EmptyIcon";const Ou=()=>s(de,{name:"lock"},()=>s("path",{d:"M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"}));Ou.displayName="LockIcon";var y2=$({name:"ArticleItem",props:{info:{type:Object,required:!0},path:{type:String,required:!0}},slots:Object,setup(e,{slots:t}){const l=xl(e,"info"),{info:n,items:a}=p2(e);return()=>{var i,r,o;const{[be.title]:c,[be.type]:u,[be.isEncrypted]:d=!1,[be.cover]:p,[be.excerpt]:h,[be.sticky]:v}=l.value,b=n.value;return s("div",{class:"vp-article-wrapper"},s("article",{class:"vp-article-item",vocab:"https://schema.org/",typeof:"Article"},[((i=t.cover)==null?void 0:i.call(t,{cover:p}))||(p?[s("img",{class:"vp-article-cover",src:xe(p)}),s("meta",{property:"image",content:xe(p)})]:[]),v?s(wu):null,s(Ce,{to:e.path},()=>{var w;return((w=t.title)==null?void 0:w.call(t,{title:c,isEncrypted:d,type:u}))||s("header",{class:"vp-article-title"},[d?s(Ou):null,u===Mc.slide?s(ku):null,s("span",{property:"headline"},c)])}),((r=t.excerpt)==null?void 0:r.call(t,{excerpt:h}))||(h?s("div",{class:"vp-article-excerpt",innerHTML:h}):null),s("hr",{class:"vp-article-hr"}),((o=t.info)==null?void 0:o.call(t,{info:b}))||s(cu,{info:b,...a.value?{items:a.value}:{}})]))}}}),b2=$({name:"Pagination",props:{total:{type:Number,default:10},perPage:{type:Number,default:10},current:{type:Number,default:1}},emits:["updateCurrentPage"],setup(e,{emit:t}){let l;const n=ie(),a=W(""),i=E(()=>n.value.paginationLocales),r=E(()=>Math.ceil(e.total/e.perPage)),o=E(()=>!!r.value&&r.value!==1),c=E(()=>r.value<7?!1:e.current>4),u=E(()=>r.value<7?!1:e.current{const{current:v}=e;let b=1,w=r.value;const L=[];r.value>=7&&(v<=4&&v4&&v>=r.value-3?(w=r.value,b=r.value-4):r.value>7&&(b=v-2,w=v+2));for(let m=b;m<=w;m++)L.push(m);return L}),p=v=>t("updateCurrentPage",v),h=v=>{const b=parseInt(v);b<=r.value&&b>0?p(b):l.pop(`${i.value.errorText.replace(/\$page/g,r.value.toString())}`)};return ke(()=>{l=new Yh}),()=>s("div",{class:"vp-pagination"},o.value?s("div",{class:"vp-pagination-list"},[s("div",{class:"vp-pagination-number "},[e.current>1?s("div",{class:"prev",role:"navigation",unselectable:"on",onClick:()=>p(e.current-1)},i.value.prev):null,c.value?[s("div",{role:"navigation",onClick:()=>p(1)},1),s("div",{class:"ellipsis"},"...")]:null,d.value.map(v=>s("div",{key:v,class:{active:e.current===v},role:"navigation",onClick:()=>p(v)},v)),u.value?[s("div",{class:"ellipsis"},"..."),s("div",{role:"navigation",onClick:()=>p(r.value)},r.value)]:null,e.currentp(e.current+1)},i.value.next):null]),s("div",{class:"vp-pagination-nav"},[s("label",{for:"navigation-text"},`${i.value.navigate}: `),s("input",{id:"navigation-text",value:a.value,onInput:({target:v})=>{a.value=v.value},onKeydown:v=>{v.key==="Enter"&&(v.preventDefault(),h(a.value))}}),s("button",{class:"vp-pagination-button",role:"navigation",title:i.value.action,onClick:()=>h(a.value)},i.value.action)])]):[])}}),Ui=$({name:"ArticleList",props:{items:{type:Array,default:()=>[]}},setup(e){const t=yt(),l=Ve(),n=vn(),a=W(1),i=E(()=>n.value.articlePerPage||10),r=E(()=>e.items.slice((a.value-1)*i.value,a.value*i.value)),o=c=>{a.value=c;const u={...t.query};u.page===c.toString()||c===1&&!u.page||(c===1?delete u.page:u.page=c.toString(),l.push({path:t.path,query:u}))};return ke(()=>{const{page:c}=t.query;o(c?Number(c):1),ce(a,()=>{const u=document.querySelector("#article-list").getBoundingClientRect().top+window.scrollY;setTimeout(()=>{window.scrollTo(0,u)},100)}),ce(()=>t.query,({page:u})=>{o(u?Number(u):1)})}),()=>s("div",{id:"article-list",class:"vp-article-list"},r.value.length?[...r.value.map(({info:c,path:u},d)=>s(pe,{appear:!0,delay:d*.04},()=>s(y2,{key:u,info:c,path:u}))),s(b2,{current:a.value,perPage:i.value,total:e.items.length,onUpdateCurrentPage:o})]:s(Au))}}),Wi=$({name:"CategoryList",setup(){const e=ue(),t=hn();return()=>s("ul",{class:"vp-category-list"},sn(t.value.map).map(([l,{path:n,items:a}])=>s("li",{class:["vp-category",`vp-category${la(l,9)}`,{active:n===e.value.path}]},s(Ce,{to:n},()=>[l,s("span",{class:"count"},a.length)]))))}}),Ki=$({name:"TagList",setup(){const e=Ee(),t=fn(),l=n=>{var a;return n===((a=e.value.blog)==null?void 0:a.name)};return()=>s("ul",{class:"tag-list-wrapper"},sn(t.value.map).map(([n,{path:a,items:i}])=>s("li",{class:["tag",`tag${la(n,9)}`,{active:l(n)}]},s(Ce,{to:a},()=>[n,s("span",{class:"tag-num"},i.length)]))))}}),_2=$({name:"TimelineList",setup(){const e=ie(),t=zi(),l=un(),n=E(()=>e.value.blogLocales.timeline);return()=>s("div",{class:"timeline-list-wrapper"},[s("div",{class:"timeline-list-title",onClick:()=>l(t.value.path)},[s(Gi),s("span",{class:"num"},t.value.items.length),n.value]),s("hr"),s("div",{class:"timeline-content"},s("ul",{class:"timeline-list"},t.value.config.map(({year:a,items:i},r)=>s(pe,{appear:!0,delay:.08*(r+1)},()=>s("li",[s("h3",{class:"timeline-year"},a),s("ul",{class:"timeline-year-wrapper"},i.map(({date:o,info:c,path:u})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},o),s(Ce,{class:"timeline-title",to:u},()=>c[be.title])])))])))))])}}),Pu=$({name:"InfoList",setup(){const e=ie(),t=gn(),l=hn(),n=E(()=>gt(l.value.map).length),a=Hi(),i=fn(),r=E(()=>gt(i.value.map).length),o=un(),c=W("article"),u=E(()=>e.value.blogLocales),d=[["article",qn],["category",Ya],["tag",Xa],["timeline",Gi]];return()=>s("div",{class:"vp-blog-infos"},[s("div",{class:"vp-blog-type-switcher"},d.map(([p,h])=>s("button",{type:"button",class:"vp-blog-type-button",onClick:()=>{c.value=p}},s("div",{class:["icon-wrapper",{active:c.value===p}],"aria-label":u.value[p],"data-balloon-pos":"up"},s(h))))),s(pe,()=>c.value==="article"?s("div",{class:"vp-sticky-article-wrapper"},[s("div",{class:"title",onClick:()=>o(t.value.path)},[s(qn),s("span",{class:"num"},t.value.items.length),u.value.article]),s("hr"),s("ul",{class:"vp-sticky-articles"},a.value.items.map(({info:p,path:h},v)=>s(pe,{appear:!0,delay:.08*(v+1)},()=>s("li",{class:"vp-sticky-article"},s(Ce,{to:h},()=>p[be.title])))))]):c.value==="category"?s("div",{class:"vp-category-wrapper"},[n.value?s("div",{class:"title",onClick:()=>o(l.value.path)},[s(Ya),s("span",{class:"num"},n.value),u.value.category]):null,s("hr"),s(pe,{delay:.04},()=>s(Wi))]):c.value==="tag"?s("div",{class:"vp-tag-wrapper"},[r.value?s("div",{class:"title",onClick:()=>o(i.value.path)},[s(Xa),s("span",{class:"num"},r.value),u.value.tag]):null,s("hr"),s(pe,{delay:.04},()=>s(Ki))]):s(pe,()=>s(_2)))])}}),ra=$({name:"BlogWrapper",slots:Object,setup(e,{slots:t}){const{isMobile:l}=dn();return()=>[s(Bi),s(Ni,{noSidebar:!0,noToc:!0},{default:()=>t.default(),navScreenBottom:()=>s(qi),...l.value?{sidebar:()=>s(Pu)}:{}})]}});const Iu=()=>s("aside",{class:"vp-blog-info-wrapper"},[s(pe,()=>s(qi)),s(pe,{delay:.04},()=>s(Pu))]);Iu.displayName="InfoPanel";var sa=Iu,k2=$({name:"BlogPage",components:{CategoryList:Wi,TagList:Ki},setup(){const e=ue(),t=Ee(),l=hn(),n=fn(),a=E(()=>t.value.blog||{}),i=E(()=>{const{key:o=""}=a.value;return o==="category"?"CategoryList":o==="tag"?"TagList":null}),r=E(()=>{const{name:o="",key:c=""}=a.value;return c==="category"?o?l.value.map[o].items:[]:c==="tag"?o?n.value.map[o].items:[]:[]});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>i.value?s(Je(i.value)):null),a.value.name?s(pe,{appear:!0,delay:.24},()=>s(Ui,{key:e.value.path,items:r.value})):null]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),w2=$({name:"BlogHero",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=Ge(),i=E(()=>l.value.heroFullScreen??!1),r=E(()=>{const{heroText:c,heroImage:u,heroImageDark:d,heroAlt:p,heroImageStyle:h,tagline:v}=l.value;return{text:c??n.value.title??"Hello",image:u?xe(u):null,imageDark:d?xe(d):null,heroStyle:h,alt:p||c||"hero image",tagline:v??"",isFullScreen:i.value}}),o=E(()=>{const{bgImage:c,bgImageDark:u,bgImageStyle:d}=l.value;return{image:ae(c)?xe(c):c===!1?null:i2,imageDark:ae(u)?xe(u):null,bgStyle:d,isFullScreen:i.value}});return()=>{var c,u;return l.value.hero===!1?null:s("div",{ref:a,class:["vp-blog-hero",{fullscreen:i.value,"no-bg":!o.value.image}]},[((c=t.heroBg)==null?void 0:c.call(t,o.value))||[o.value.image?s("div",{class:["vp-blog-mask",{light:o.value.imageDark}],style:[{background:`url(${o.value.image}) center/cover no-repeat`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-blog-mask dark",style:[{background:`url(${o.value.imageDark}) center/cover no-repeat`},o.value.bgStyle]}):null],((u=t.heroInfo)==null?void 0:u.call(t,r.value))||[s(pe,{appear:!0,type:"group",delay:.04},()=>[r.value.image?s("img",{key:"light",class:["vp-blog-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-blog-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),s(pe,{appear:!0,delay:.08},()=>r.value.text?s("h1",{class:"vp-blog-hero-title"},r.value.text):null),s(pe,{appear:!0,delay:.12},()=>r.value.tagline?s("p",{class:"vp-blog-hero-description",innerHTML:r.value.tagline}):null)],r.value.isFullScreen?s("button",{type:"button",class:"slide-down-button",onClick:()=>{window.scrollTo({top:a.value.clientHeight,behavior:"smooth"})}},[s(Za),s(Za)]):null])}}});const E2=["link","article","book","project","friend"];var L2=$({name:"ProjectPanel",components:{ArticleIcon:qn,BookIcon:Eu,FriendIcon:Tu,LinkIcon:Lu,ProjectIcon:xu},setup(){const e=Ee(),t=Ol(),l=un(),n=(a="",i="icon")=>E2.includes(a)?s(Je(`${a}-icon`)):Zt(a)?s("img",{class:"vp-project-image",src:a,alt:i}):na(a)?s("img",{class:"vp-project-image",src:xe(a),alt:i}):s(Ne,{icon:a});return()=>{var a;return(a=e.value.projects)!=null&&a.length?s("div",{class:"vp-project-panel"},e.value.projects.map(({icon:i,link:r,name:o,desc:c},u)=>s("div",{class:["vp-project-card",{[`project${u%9}`]:!t.value}],onClick:()=>l(r)},[n(i,o),s("div",{class:"vp-project-name"},o),s("div",{class:"vp-project-desc"},c)]))):null}}}),x2=$({name:"BlogHome",setup(){const e=gn();return()=>s("div",{class:"vp-page vp-blog"},[s(w2),s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.16},()=>s(L2)),s(pe,{appear:!0,delay:.24},()=>s(Ui,{items:e.value.items}))]),s(pe,{appear:!0,delay:.16},()=>s(sa,{key:"blog"}))]),s(pe,{appear:!0,delay:.28},()=>s(ji))])}}),T2=$({name:"BlogHome",setup(){return()=>s(ra,()=>s(x2))}}),Ru=$({name:"ArticleType",setup(){const e=ue(),t=mt(),l=ie(),n=gn(),a=Hi(),i=E(()=>{const r=l.value.blogLocales;return[{text:r.all,path:n.value.path},{text:r.star,path:a.value.path},...[].map(({key:o,path:c})=>({text:r[o],path:c.replace(/^\//,t.value)}))]});return()=>s("ul",{class:"vp-article-type-wrapper"},i.value.map(r=>s("li",{class:["vp-article-type",{active:r.path===e.value.path}]},s(Ce,{to:r.path},()=>r.text))))}}),A2=$({name:"BlogPage",setup(){const e=ia(),t=Ee(),l=ue(),n=gn(),a=Hi(),i=E(()=>{const{key:r="",type:o}=t.value.blog||{};return r==="star"?a.value.items:o==="type"&&r?e.value.items:n.value.items});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>s(Ru)),s(pe,{appear:!0,delay:.24},()=>s(Ui,{key:l.value.path,items:i.value}))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),O2=$({name:"TimelineItems",setup(){const e=vn(),t=ie(),l=zi(),n=E(()=>e.value.timeline||t.value.blogLocales.timelineTitle),a=E(()=>l.value.config.map(({year:i})=>({title:i.toString(),level:2,slug:i.toString(),children:[]})));return()=>s("div",{class:"timeline-wrapper"},s("ul",{class:"timeline-content"},[s(pe,()=>s("li",{class:"motto"},n.value)),s(uu,{items:a.value}),l.value.config.map(({year:i,items:r},o)=>s(pe,{appear:!0,delay:.08*(o+1),type:"group"},()=>[s("h3",{key:"title",id:i,class:"timeline-year-title"},s("span",i)),s("li",{key:"content",class:"timeline-year-list"},[s("ul",{class:"timeline-year-wrapper"},r.map(({date:c,info:u,path:d})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},c),s(Ce,{class:"timeline-title",to:d},()=>u[be.title])])))])]))]))}}),P2=$({name:"Timeline",components:{ArticleType:Ru,CategoryList:Wi,TagList:Ki},setup(){return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.24},()=>s(O2))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}});sv(Ne);const I2=dt({enhance:({app:e,router:t})=>{const{scrollBehavior:l}=t.options;t.options.scrollBehavior=async(...n)=>(await du().wait(),l(...n)),tf(e),e.component("HopeIcon",Ne),e.component("VPLink",Ce),e.component("BloggerInfo",qi)},setup:()=>{lf(),sf(),g2()},layouts:{Layout:Zf,NotFound:t2,BlogCategory:k2,BlogHome:T2,BlogType:A2,Timeline:P2}}),R2=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,C2=(e,t)=>t.some(l=>{if(ae(l))return l===e.key;const{key:n,ctrl:a=!1,shift:i=!1,alt:r=!1}=l;return n===e.key&&a===e.ctrlKey&&i===e.shiftKey&&r===e.altKey}),S2=/[^\x00-\x7F]/,D2=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),Ls=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),xs=(e,t)=>{const l=t.join(" "),n=D2(e);if(S2.test(e))return n.some(r=>l.toLowerCase().indexOf(r)>-1);const a=e.endsWith(" ");return new RegExp(n.map((r,o)=>n.length===o+1&&!a?`(?=.*\\b${Ls(r)})`:`(?=.*\\b${Ls(r)}\\b)`).join("")+".+","gi").test(l)},M2=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const l=n=>{e.value&&C2(n,t.value)&&!R2(n.target)&&(n.preventDefault(),e.value.focus())};ke(()=>{document.addEventListener("keydown",l)}),Yn(()=>{document.removeEventListener("keydown",l)})},$2=[{title:"levy's blog",headers:[],path:"/",pathLocale:"/",extraFields:[]},{title:"关于",headers:[],path:"/about.html",pathLocale:"/",extraFields:[]},{title:"VuePress2 娱乐视频",headers:[],path:"/daily/a-vuepress2-entertaining-video.html",pathLocale:"/",extraFields:[]},{title:"来自Navicat的侵权警告",headers:[],path:"/daily/a-warning-from-navicat.html",pathLocale:"/",extraFields:[]},{title:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",headers:[{level:2,title:"Background",slug:"background",link:"#background",children:[]},{level:2,title:"utf8mb4(UTF-8 MultiByte 4-Byte)",slug:"utf8mb4-utf-8-multibyte-4-byte",link:"#utf8mb4-utf-8-multibyte-4-byte",children:[]},{level:2,title:"utf8mb4_unicode_ci",slug:"utf8mb4-unicode-ci",link:"#utf8mb4-unicode-ci",children:[]},{level:2,title:"Some tips",slug:"some-tips",link:"#some-tips",children:[]}],path:"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",pathLocale:"/",extraFields:[]},{title:"Claude AI应用案例,从HTML中抽取文本",headers:[],path:"/daily/claude-ai-in-action-extract-info-from-html.html",pathLocale:"/",extraFields:[]},{title:"复制代码也许不是罪",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/copy-code-may-not-be-guilty.html",pathLocale:"/",extraFields:[]},{title:"不要与傻逼进行争吵",headers:[],path:"/daily/dont-try-to-argue-with-a-sb.html",pathLocale:"/",extraFields:[]},{title:"迭代复盘之三员管理",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"动手前,至少梳理出接口清单",slug:"动手前-至少梳理出接口清单",link:"#动手前-至少梳理出接口清单",children:[]},{level:2,title:"迁移时,采取结队编程",slug:"迁移时-采取结队编程",link:"#迁移时-采取结队编程",children:[]},{level:2,title:"迁移后,需要对自己负责的功能设计测试用例",slug:"迁移后-需要对自己负责的功能设计测试用例",link:"#迁移后-需要对自己负责的功能设计测试用例",children:[]}],path:"/daily/iteration-retrospective-of-sanyuan.html",pathLocale:"/",extraFields:[]},{title:"都什么年代了,还在用传统方式写代码?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"开发流程",slug:"开发流程",link:"#开发流程",children:[]},{level:2,title:"程序设计",slug:"程序设计",link:"#程序设计",children:[]},{level:2,title:"代码编写",slug:"代码编写",link:"#代码编写",children:[{level:3,title:"生成真实代码",slug:"生成真实代码",link:"#生成真实代码",children:[]},{level:3,title:"辅助编程工具",slug:"辅助编程工具",link:"#辅助编程工具",children:[]}]},{level:2,title:"软件测试",slug:"软件测试",link:"#软件测试",children:[]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"附:CodeWhisperer 安装",slug:"附-codewhisperer-安装",link:"#附-codewhisperer-安装",children:[]}],path:"/daily/leverage-ai-to-boost-coding-productivity.html",pathLocale:"/",extraFields:[]},{title:"微软中国CTO演讲观后感",headers:[],path:"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",pathLocale:"/",extraFields:[]},{title:"生产教训:测试环境要与生产环境一致",headers:[{level:2,title:"事件还原",slug:"事件还原",link:"#事件还原",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/daily/testing-environments-should-be-consistent-with-production-environments.html",pathLocale:"/",extraFields:[]},{title:"对Vue不得不吐槽的事",headers:[],path:"/daily/things-I-have-to-vent-about-vue.html",pathLocale:"/",extraFields:[]},{title:"再见ChatGPT,我选择Claude2!",headers:[],path:"/daily/use-claude2-instead-of-chatgpt.html",pathLocale:"/",extraFields:[]},{title:"Vim 作者离世",headers:[],path:"/daily/vim-creator-pass-away.html",pathLocale:"/",extraFields:[]},{title:"技术点评:别每张表都加tenant_id",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/you-dont-need-to-add-tenant_id-to-every-table.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第一册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-1.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第二册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-2.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第三册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-3.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第四册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-4.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第五册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"Who Are You and What Are You Doing Here",slug:"who-are-you-and-what-are-you-doing-here",link:"#who-are-you-and-what-are-you-doing-here",children:[]},{level:2,title:"Two Kinds",slug:"two-kinds",link:"#two-kinds",children:[]},{level:2,title:"Love is a Fallacy",slug:"love-is-a-fallacy",link:"#love-is-a-fallacy",children:[]},{level:2,title:"Rewriting American History",slug:"rewriting-american-history",link:"#rewriting-american-history",children:[]},{level:2,title:"Nobel Peace Price About Global Warming",slug:"nobel-peace-price-about-global-warming",link:"#nobel-peace-price-about-global-warming",children:[]},{level:2,title:"The Bluest Eyes",slug:"the-bluest-eyes",link:"#the-bluest-eyes",children:[]},{level:2,title:"How News Becomes Options and Opinions Off-Limits",slug:"how-news-becomes-options-and-opinions-off-limits",link:"#how-news-becomes-options-and-opinions-off-limits",children:[]},{level:2,title:"The Indispensable Opposition",slug:"the-indispensable-opposition",link:"#the-indispensable-opposition",children:[]},{level:2,title:"The Danger of a Single Story",slug:"the-danger-of-a-single-story",link:"#the-danger-of-a-single-story",children:[]},{level:2,title:"Come Rain or Come Shine",slug:"come-rain-or-come-shine",link:"#come-rain-or-come-shine",children:[]},{level:2,title:"Invisible Man",slug:"invisible-man",link:"#invisible-man",children:[]},{level:2,title:"You've Got to Find What You Love",slug:"you-ve-got-to-find-what-you-love",link:"#you-ve-got-to-find-what-you-love",children:[]},{level:2,title:"Where Do We Go from Here",slug:"where-do-we-go-from-here",link:"#where-do-we-go-from-here",children:[]}],path:"/english/contemporary-college-english-5.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第六册",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"Paper Tigers",slug:"paper-tigers",link:"#paper-tigers",children:[]},{level:2,title:"What Is News",slug:"what-is-news",link:"#what-is-news",children:[]},{level:2,title:"At War with the Planet",slug:"at-war-with-the-planet",link:"#at-war-with-the-planet",children:[]},{level:2,title:"How to Get the Poor off Our Conscience",slug:"how-to-get-the-poor-off-our-conscience",link:"#how-to-get-the-poor-off-our-conscience",children:[]},{level:2,title:"Housewifely Arts",slug:"housewifely-arts",link:"#housewifely-arts",children:[]},{level:2,title:"The One Against The Many",slug:"the-one-against-the-many",link:"#the-one-against-the-many",children:[]},{level:2,title:"Notes on the English Character",slug:"notes-on-the-english-character",link:"#notes-on-the-english-character",children:[]},{level:2,title:"The Death of a Pig",slug:"the-death-of-a-pig",link:"#the-death-of-a-pig",children:[]},{level:2,title:"Don't Eat Fortune's Cookie",slug:"don-t-eat-fortune-s-cookie",link:"#don-t-eat-fortune-s-cookie",children:[]},{level:2,title:"The Accidental Universe",slug:"the-accidental-universe",link:"#the-accidental-universe",children:[]},{level:2,title:"Rowling's Speech at Harvard",slug:"rowling-s-speech-at-harvard",link:"#rowling-s-speech-at-harvard",children:[]}],path:"/english/contemporary-college-english-6.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语1:开篇",headers:[{level:2,title:"为什么学",slug:"为什么学",link:"#为什么学",children:[]},{level:2,title:"怎么学",slug:"怎么学",link:"#怎么学",children:[{level:3,title:"建设心态",slug:"建设心态",link:"#建设心态",children:[]},{level:3,title:"提升认知",slug:"提升认知",link:"#提升认知",children:[]},{level:3,title:"明确意义",slug:"明确意义",link:"#明确意义",children:[]},{level:3,title:"制定计划",slug:"制定计划",link:"#制定计划",children:[]},{level:3,title:"培养习惯",slug:"培养习惯",link:"#培养习惯",children:[]},{level:3,title:"注重方法",slug:"注重方法",link:"#注重方法",children:[]}]}],path:"/english/everyone-can-learn-english-1-overview.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语2:音标",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"讨论",slug:"讨论",link:"#讨论",children:[{level:3,title:"建议买实体书",slug:"建议买实体书",link:"#建议买实体书",children:[]},{level:3,title:"美语是主流",slug:"美语是主流",link:"#美语是主流",children:[]},{level:3,title:"不要纠结口音",slug:"不要纠结口音",link:"#不要纠结口音",children:[]}]}],path:"/english/everyone-can-learn-english-2-pronunciation.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语3:单词",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"提醒",slug:"提醒",link:"#提醒",children:[]}],path:"/english/everyone-can-learn-english-3-words.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语4:听说",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"方法",slug:"方法",link:"#方法",children:[{level:3,title:"1.建立信心",slug:"_1-建立信心",link:"#_1-建立信心",children:[]},{level:3,title:"2.明确方向",slug:"_2-明确方向",link:"#_2-明确方向",children:[]},{level:3,title:"3.坚持输入并输出",slug:"_3-坚持输入并输出",link:"#_3-坚持输入并输出",children:[]}]}],path:"/english/everyone-can-learn-english-4-listening-and-speaking.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语5:读写",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"阅读能力升级之旅",slug:"阅读能力升级之旅",link:"#阅读能力升级之旅",children:[]},{level:2,title:"阅读方法",slug:"阅读方法",link:"#阅读方法",children:[{level:3,title:"建立信心",slug:"建立信心",link:"#建立信心",children:[]},{level:3,title:"根据蓝思值选书",slug:"根据蓝思值选书",link:"#根据蓝思值选书",children:[]},{level:3,title:"巧查生词",slug:"巧查生词",link:"#巧查生词",children:[]},{level:3,title:"阅读材料推荐",slug:"阅读材料推荐",link:"#阅读材料推荐",children:[]}]},{level:2,title:"写作",slug:"写作",link:"#写作",children:[]}],path:"/english/everyone-can-learn-english-5-reading-and-writing.html",pathLocale:"/",extraFields:[]},{title:"英文能力评测手把手教学",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"单词量",slug:"单词量",link:"#单词量",children:[]},{level:2,title:"阅读能力",slug:"阅读能力",link:"#阅读能力",children:[]}],path:"/english/how-to-self-evaluate-english-level.html",pathLocale:"/",extraFields:[]},{title:"完成刷7k单词任务",headers:[],path:"/english/learning-7000-words-task-completed.html",pathLocale:"/",extraFields:[]},{title:"让 ChatGPT 成为你的外语私教",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"准备工作",slug:"准备工作",link:"#准备工作",children:[]},{level:2,title:"常用Prompt",slug:"常用prompt",link:"#常用prompt",children:[]},{level:2,title:"进行对话",slug:"进行对话",link:"#进行对话",children:[]},{level:2,title:"记录回答",slug:"记录回答",link:"#记录回答",children:[]}],path:"/english/let-chatgpt-be-your-foreign-language-teacher.html",pathLocale:"/",extraFields:[]},{title:"关于 Arm 你需要了解的三件事",headers:[],path:"/devops/about-arm-things-you-need-to-know.html",pathLocale:"/",extraFields:[]},{title:"对象存储静态资源常见操作",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"阿里云OSS",slug:"阿里云oss",link:"#阿里云oss",children:[{level:3,title:"绑定域名",slug:"绑定域名",link:"#绑定域名",children:[]},{level:3,title:"CNAME设置",slug:"cname设置",link:"#cname设置",children:[]},{level:3,title:"HTTPS证书托管",slug:"https证书托管",link:"#https证书托管",children:[]},{level:3,title:"公共读",slug:"公共读",link:"#公共读",children:[]},{level:3,title:"CORS跨域设置",slug:"cors跨域设置",link:"#cors跨域设置",children:[]}]},{level:2,title:"华为云OBS",slug:"华为云obs",link:"#华为云obs",children:[{level:3,title:"跨域设置",slug:"跨域设置",link:"#跨域设置",children:[]}]}],path:"/devops/common-solutions-of-object-storage-for-static-assets.html",pathLocale:"/",extraFields:[]},{title:"Docker 构建镜像、推送、启动实用脚本",headers:[{level:2,title:"misc",slug:"misc",link:"#misc",children:[]},{level:2,title:"get-version.sh",slug:"get-version-sh",link:"#get-version-sh",children:[]},{level:2,title:"build-image.sh",slug:"build-image-sh",link:"#build-image-sh",children:[]},{level:2,title:"startup.sh",slug:"startup-sh",link:"#startup-sh",children:[]}],path:"/devops/docker-build-and-push-script.html",pathLocale:"/",extraFields:[]},{title:"缩减Python应用的镜像体积",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"原始Dockerfile",slug:"原始dockerfile",link:"#原始dockerfile",children:[]},{level:2,title:"使用slim镜像",slug:"使用slim镜像",link:"#使用slim镜像",children:[]},{level:2,title:"减少层数、取消本地缓存",slug:"减少层数、取消本地缓存",link:"#减少层数、取消本地缓存",children:[]},{level:2,title:"优化效果",slug:"优化效果",link:"#优化效果",children:[]},{level:2,title:"参考",slug:"参考",link:"#参考",children:[]}],path:"/devops/reduce-python-image-size.html",pathLocale:"/",extraFields:[]},{title:"sh与bash的区别",headers:[],path:"/devops/what-is-the-difference-between-sh-and-bash.html",pathLocale:"/",extraFields:[]},{title:"旧文章精选",headers:[],path:"/frontend/old-articles.html",pathLocale:"/",extraFields:[]},{title:"前端项目性能优化实战",headers:[{level:2,title:"检测",slug:"检测",link:"#检测",children:[]},{level:2,title:"图片优化",slug:"图片优化",link:"#图片优化",children:[]},{level:2,title:"提高TTFB时间",slug:"提高ttfb时间",link:"#提高ttfb时间",children:[]},{level:2,title:"移除未使用的 Javascript",slug:"移除未使用的-javascript",link:"#移除未使用的-javascript",children:[]},{level:2,title:"延迟静态资源的加载",slug:"延迟静态资源的加载",link:"#延迟静态资源的加载",children:[]},{level:2,title:"启用文本压缩",slug:"启用文本压缩",link:"#启用文本压缩",children:[]},{level:2,title:"优化缓存策略",slug:"优化缓存策略",link:"#优化缓存策略",children:[]}],path:"/frontend/performance-optimization-in-action.html",pathLocale:"/",extraFields:[]},{title:"Git最佳实践",headers:[{level:2,title:"精简提交",slug:"精简提交",link:"#精简提交",children:[]},{level:2,title:"频繁提交",slug:"频繁提交",link:"#频繁提交",children:[]},{level:2,title:"不要提交不完整的改动",slug:"不要提交不完整的改动",link:"#不要提交不完整的改动",children:[]},{level:2,title:"提交前测试那些改动",slug:"提交前测试那些改动",link:"#提交前测试那些改动",children:[]},{level:2,title:"版本控制不是备份系统",slug:"版本控制不是备份系统",link:"#版本控制不是备份系统",children:[]},{level:2,title:"Github实例",slug:"github实例",link:"#github实例",children:[{level:3,title:"一个功能对应一个分支",slug:"一个功能对应一个分支",link:"#一个功能对应一个分支",children:[]},{level:3,title:"提交“瘦”的PR",slug:"提交-瘦-的pr",link:"#提交-瘦-的pr",children:[]},{level:3,title:"使用正确的标题",slug:"使用正确的标题",link:"#使用正确的标题",children:[]},{level:3,title:"根据模板填写PR描述",slug:"根据模板填写pr描述",link:"#根据模板填写pr描述",children:[]},{level:3,title:"自动关闭issue",slug:"自动关闭issue",link:"#自动关闭issue",children:[]},{level:3,title:"1+2 review 规则",slug:"_1-2-review-规则",link:"#_1-2-review-规则",children:[]},{level:3,title:"礼貌提问",slug:"礼貌提问",link:"#礼貌提问",children:[]}]},{level:2,title:"学习资源",slug:"学习资源",link:"#学习资源",children:[]}],path:"/git/git-best-pratices.html",pathLocale:"/",extraFields:[]},{title:"Git代码合并指南",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"功能分支合并长驻分支冲突",slug:"功能分支合并长驻分支冲突",link:"#功能分支合并长驻分支冲突",children:[{level:3,title:"解决思路",slug:"解决思路",link:"#解决思路",children:[]},{level:3,title:"操作步骤",slug:"操作步骤",link:"#操作步骤",children:[]}]},{level:2,title:"功能分支被污染",slug:"功能分支被污染",link:"#功能分支被污染",children:[{level:3,title:"解决思路",slug:"解决思路-1",link:"#解决思路-1",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-1",link:"#操作步骤-1",children:[]}]},{level:2,title:"挑选别的分支部分代码合并",slug:"挑选别的分支部分代码合并",link:"#挑选别的分支部分代码合并",children:[{level:3,title:"解决思路",slug:"解决思路-2",link:"#解决思路-2",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-2",link:"#操作步骤-2",children:[]}]}],path:"/git/git-definitive-guide-to-merge-code.html",pathLocale:"/",extraFields:[]},{title:"Git查看历史记录小技巧",headers:[],path:"/git/git-history-two-tricks-in-idea.html",pathLocale:"/",extraFields:[]},{title:"Git常用命令",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[]},{level:2,title:"增强",slug:"增强",link:"#增强",children:[]},{level:2,title:"记住账号密码",slug:"记住账号密码",link:"#记住账号密码",children:[]},{level:2,title:"初始化",slug:"初始化",link:"#初始化",children:[]},{level:2,title:"本地提交",slug:"本地提交",link:"#本地提交",children:[{level:3,title:"取消未暂存的修改",slug:"取消未暂存的修改",link:"#取消未暂存的修改",children:[]},{level:3,title:"取消add",slug:"取消add",link:"#取消add",children:[]},{level:3,title:"取消提交",slug:"取消提交",link:"#取消提交",children:[]},{level:3,title:"修正提交",slug:"修正提交",link:"#修正提交",children:[]},{level:3,title:"stash修改",slug:"stash修改",link:"#stash修改",children:[]},{level:3,title:"恢复stash",slug:"恢复stash",link:"#恢复stash",children:[]}]},{level:2,title:"分支管理",slug:"分支管理",link:"#分支管理",children:[{level:3,title:"创建分支",slug:"创建分支",link:"#创建分支",children:[]},{level:3,title:"查看远程分支",slug:"查看远程分支",link:"#查看远程分支",children:[]},{level:3,title:"创建干净历史分支",slug:"创建干净历史分支",link:"#创建干净历史分支",children:[]},{level:3,title:"删除分支",slug:"删除分支",link:"#删除分支",children:[]}]},{level:2,title:"远程仓库",slug:"远程仓库",link:"#远程仓库",children:[{level:3,title:"远程仓库管理",slug:"远程仓库管理",link:"#远程仓库管理",children:[]},{level:3,title:"浅克隆",slug:"浅克隆",link:"#浅克隆",children:[]},{level:3,title:"克隆指定分支",slug:"克隆指定分支",link:"#克隆指定分支",children:[]},{level:3,title:"克隆失败因为文件名太长",slug:"克隆失败因为文件名太长",link:"#克隆失败因为文件名太长",children:[]},{level:3,title:"强行推送",slug:"强行推送",link:"#强行推送",children:[]},{level:3,title:"取消错误的推送",slug:"取消错误的推送",link:"#取消错误的推送",children:[]}]},{level:2,title:"标签管理",slug:"标签管理",link:"#标签管理",children:[{level:3,title:"新建本地标签",slug:"新建本地标签",link:"#新建本地标签",children:[]},{level:3,title:"删除本地标签",slug:"删除本地标签",link:"#删除本地标签",children:[]},{level:3,title:"查看本地所有标签",slug:"查看本地所有标签",link:"#查看本地所有标签",children:[]},{level:3,title:"推送本地标签",slug:"推送本地标签",link:"#推送本地标签",children:[]},{level:3,title:"获取远程标签",slug:"获取远程标签",link:"#获取远程标签",children:[]},{level:3,title:"删除远程标签",slug:"删除远程标签",link:"#删除远程标签",children:[]}]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"cherry-pick",slug:"cherry-pick",link:"#cherry-pick",children:[]},{level:3,title:"merge unrelated histories",slug:"merge-unrelated-histories",link:"#merge-unrelated-histories",children:[]},{level:3,title:"git log 丢失最新提交",slug:"git-log-丢失最新提交",link:"#git-log-丢失最新提交",children:[]},{level:3,title:"查看分支创建时间",slug:"查看分支创建时间",link:"#查看分支创建时间",children:[]},{level:3,title:"根据文件搜索历史",slug:"根据文件搜索历史",link:"#根据文件搜索历史",children:[]},{level:3,title:"从所有提交中删除一个文件",slug:"从所有提交中删除一个文件",link:"#从所有提交中删除一个文件",children:[]}]}],path:"/git/git-useful-commands.html",pathLocale:"/",extraFields:[]},{title:"GitLab CI",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装与配置",slug:"安装与配置",link:"#安装与配置",children:[{level:3,title:"GitLab Runner 安装",slug:"gitlab-runner-安装",link:"#gitlab-runner-安装",children:[]},{level:3,title:"GitLab Runner 注册",slug:"gitlab-runner-注册",link:"#gitlab-runner-注册",children:[]},{level:3,title:"提交.gitlab-ci.yml",slug:"提交-gitlab-ci-yml",link:"#提交-gitlab-ci-yml",children:[]}]},{level:2,title:"合并代码前进行检查",slug:"合并代码前进行检查",link:"#合并代码前进行检查",children:[{level:3,title:"背景",slug:"背景",link:"#背景",children:[]},{level:3,title:"设置MR检查",slug:"设置mr检查",link:"#设置mr检查",children:[]},{level:3,title:".gitlab-ci.yml 示例",slug:"gitlab-ci-yml-示例",link:"#gitlab-ci-yml-示例",children:[]},{level:3,title:"效果",slug:"效果",link:"#效果",children:[]}]},{level:2,title:"集成单元测试",slug:"集成单元测试",link:"#集成单元测试",children:[]},{level:2,title:"线上发布 jar",slug:"线上发布-jar",link:"#线上发布-jar",children:[{level:3,title:"Maven配置",slug:"maven配置",link:"#maven配置",children:[]},{level:3,title:".gitlab-ci.yml 配置",slug:"gitlab-ci-yml-配置",link:"#gitlab-ci-yml-配置",children:[]},{level:3,title:"拉取最新的jar",slug:"拉取最新的jar",link:"#拉取最新的jar",children:[]}]},{level:2,title:"保存中间产物",slug:"保存中间产物",link:"#保存中间产物",children:[]},{level:2,title:"其他问题与解决方案",slug:"其他问题与解决方案",link:"#其他问题与解决方案",children:[{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"Node.js",slug:"node-js",link:"#node-js",children:[]},{level:3,title:"创建不了容器",slug:"创建不了容器",link:"#创建不了容器",children:[]},{level:3,title:"本地成功,流水线失败",slug:"本地成功-流水线失败",link:"#本地成功-流水线失败",children:[]}]},{level:2,title:"参考文档",slug:"参考文档",link:"#参考文档",children:[]}],path:"/git/gitlab-ci.html",pathLocale:"/",extraFields:[]},{title:"再论Git Flow",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"动机",slug:"动机",link:"#动机",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[{level:3,title:"剔除代码",slug:"剔除代码",link:"#剔除代码",children:[]},{level:3,title:"再次提交",slug:"再次提交",link:"#再次提交",children:[]},{level:3,title:"比较优劣",slug:"比较优劣",link:"#比较优劣",children:[]}]},{level:2,title:"实例",slug:"实例",link:"#实例",children:[{level:3,title:"分支模型",slug:"分支模型",link:"#分支模型",children:[]},{level:3,title:"功能提交",slug:"功能提交",link:"#功能提交",children:[]},{level:3,title:"功能回撤",slug:"功能回撤",link:"#功能回撤",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/git/rethinking-git-flow.html",pathLocale:"/",extraFields:[]},{title:"操作 Gitlab MR 的命令行工具",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[{level:3,title:"解压zip",slug:"解压zip",link:"#解压zip",children:[]},{level:3,title:"安装git bash",slug:"安装git-bash",link:"#安装git-bash",children:[]}]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[{level:3,title:"gitlab_token",slug:"gitlab-token",link:"#gitlab-token",children:[]},{level:3,title:"codebases",slug:"codebases",link:"#codebases",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"IDEA",slug:"idea",link:"#idea",children:[]}]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"创建MR",slug:"创建mr",link:"#创建mr",children:[]},{level:3,title:"查看MR",slug:"查看mr",link:"#查看mr",children:[]},{level:3,title:"合并MR",slug:"合并mr",link:"#合并mr",children:[]},{level:3,title:"冲突处理",slug:"冲突处理",link:"#冲突处理",children:[]}]}],path:"/git/use-command-line-tool-to-manage-gitlab-merge-request.html",pathLocale:"/",extraFields:[]},{title:"IDEA常见问题与解决方案",headers:[{level:2,title:"启动参数过长",slug:"启动参数过长",link:"#启动参数过长",children:[]},{level:2,title:"设置JDK版本",slug:"设置jdk版本",link:"#设置jdk版本",children:[]},{level:2,title:"lombok 编译报错",slug:"lombok-编译报错",link:"#lombok-编译报错",children:[]},{level:2,title:"设置启动参数",slug:"设置启动参数",link:"#设置启动参数",children:[]},{level:2,title:"栈溢出",slug:"栈溢出",link:"#栈溢出",children:[]},{level:2,title:"内存不足",slug:"内存不足",link:"#内存不足",children:[]},{level:2,title:"热加载",slug:"热加载",link:"#热加载",children:[]},{level:2,title:"终端加载环境变量",slug:"终端加载环境变量",link:"#终端加载环境变量",children:[]},{level:2,title:"添加外部jar作为依赖",slug:"添加外部jar作为依赖",link:"#添加外部jar作为依赖",children:[]},{level:2,title:"文件找不到——依赖冲突",slug:"文件找不到——依赖冲突",link:"#文件找不到——依赖冲突",children:[]},{level:2,title:"自动import",slug:"自动import",link:"#自动import",children:[]},{level:2,title:"文件乱码",slug:"文件乱码",link:"#文件乱码",children:[]},{level:2,title:"autowired 提示变量未赋值",slug:"autowired-提示变量未赋值",link:"#autowired-提示变量未赋值",children:[]}],path:"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",pathLocale:"/",extraFields:[]},{title:"Maven常见问题与解决方案",headers:[{level:2,title:"运行 class 找不到主类",slug:"运行-class-找不到主类",link:"#运行-class-找不到主类",children:[]},{level:2,title:"运行 jar 找不到主类",slug:"运行-jar-找不到主类",link:"#运行-jar-找不到主类",children:[]},{level:2,title:"编译时找不到主类",slug:"编译时找不到主类",link:"#编译时找不到主类",children:[]},{level:2,title:"设置Maven目录",slug:"设置maven目录",link:"#设置maven目录",children:[]},{level:2,title:"无法识别 Maven 项目",slug:"无法识别-maven-项目",link:"#无法识别-maven-项目",children:[]},{level:2,title:"使用了不想要的镜像源",slug:"使用了不想要的镜像源",link:"#使用了不想要的镜像源",children:[]},{level:2,title:"下载 jar 失败",slug:"下载-jar-失败",link:"#下载-jar-失败",children:[]},{level:2,title:"私服认证401",slug:"私服认证401",link:"#私服认证401",children:[]},{level:2,title:"避免缓存",slug:"避免缓存",link:"#避免缓存",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/Resolving-Common-Problems-in-Maven.md.html",pathLocale:"/",extraFields:[]},{title:"避免密码明文传输",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"前端代码",slug:"前端代码",link:"#前端代码",children:[]},{level:2,title:"后端代码",slug:"后端代码",link:"#后端代码",children:[]}],path:"/java/avoid-sending-password-in-plaintext.html",pathLocale:"/",extraFields:[]},{title:"检查名字是否重复",headers:[{level:2,title:"推荐做法",slug:"推荐做法",link:"#推荐做法",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]}],path:"/java/check-if-name-exists.html",pathLocale:"/",extraFields:[]},{title:"Excel处理常用实践",headers:[{level:2,title:"基础知识",slug:"基础知识",link:"#基础知识",children:[]},{level:2,title:"应用框架",slug:"应用框架",link:"#应用框架",children:[{level:3,title:"导出",slug:"导出",link:"#导出",children:[]},{level:3,title:"导入",slug:"导入",link:"#导入",children:[]}]},{level:2,title:"常见问题与解决方案",slug:"常见问题与解决方案",link:"#常见问题与解决方案",children:[{level:3,title:"浏览器下载",slug:"浏览器下载",link:"#浏览器下载",children:[]},{level:3,title:"上传文件大小限制",slug:"上传文件大小限制",link:"#上传文件大小限制",children:[]},{level:3,title:"缺少字体",slug:"缺少字体",link:"#缺少字体",children:[]},{level:3,title:"序列化失败",slug:"序列化失败",link:"#序列化失败",children:[]}]}],path:"/java/common-practices-for-handling-excel.html",pathLocale:"/",extraFields:[]},{title:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"下载",slug:"下载",link:"#下载",children:[]},{level:2,title:"修改",slug:"修改",link:"#修改",children:[]},{level:2,title:"上传",slug:"上传",link:"#上传",children:[]}],path:"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",pathLocale:"/",extraFields:[]},{title:"集合命名推荐",headers:[{level:2,title:"概述",slug:"概述",link:"#概述",children:[]},{level:2,title:"List",slug:"list",link:"#list",children:[]},{level:2,title:"Set",slug:"set",link:"#set",children:[]},{level:2,title:"Map",slug:"map",link:"#map",children:[]}],path:"/java/recommend-practices-for-collections-naming-convention.html",pathLocale:"/",extraFields:[]},{title:"根据时间范围查询推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"实现",slug:"实现",link:"#实现",children:[{level:3,title:"MySQL",slug:"mysql",link:"#mysql",children:[]},{level:3,title:"MyBatis",slug:"mybatis",link:"#mybatis",children:[]},{level:3,title:"LoxalDate",slug:"loxaldate",link:"#loxaldate",children:[]},{level:3,title:"Jackson",slug:"jackson",link:"#jackson",children:[]}]},{level:2,title:"结语",slug:"结语",link:"#结语",children:[]}],path:"/java/recommend-practices-for-query-by-date-range.html",pathLocale:"/",extraFields:[]},{title:"编写函数的最佳实践",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[{level:3,title:"减少重复",slug:"减少重复",link:"#减少重复",children:[]},{level:3,title:"隐藏细节",slug:"隐藏细节",link:"#隐藏细节",children:[]}]},{level:2,title:"建议",slug:"建议",link:"#建议",children:[{level:3,title:"优先根据业务命名",slug:"优先根据业务命名",link:"#优先根据业务命名",children:[]},{level:3,title:"一个函数只做一件事",slug:"一个函数只做一件事",link:"#一个函数只做一件事",children:[]},{level:3,title:"优先使用纯函数",slug:"优先使用纯函数",link:"#优先使用纯函数",children:[]},{level:3,title:"编写不需要返回值的函数",slug:"编写不需要返回值的函数",link:"#编写不需要返回值的函数",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/recommend-practices-for-writing-good-functions.html",pathLocale:"/",extraFields:[]},{title:"枚举的推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"Java",slug:"java",link:"#java",children:[{level:3,title:"极简实现",slug:"极简实现",link:"#极简实现",children:[]},{level:3,title:"常见实现",slug:"常见实现",link:"#常见实现",children:[]},{level:3,title:"更好的方式",slug:"更好的方式",link:"#更好的方式",children:[]},{level:3,title:"为什么还要定义数字?",slug:"为什么还要定义数字",link:"#为什么还要定义数字",children:[]}]},{level:2,title:"数据库",slug:"数据库",link:"#数据库",children:[{level:3,title:"排序特点",slug:"排序特点",link:"#排序特点",children:[]},{level:3,title:"添加新值",slug:"添加新值",link:"#添加新值",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/using-enum-in-java.html",pathLocale:"/",extraFields:[]},{title:"Boolean 还是 boolean?",headers:[{level:2,title:"结论",slug:"结论",link:"#结论",children:[]},{level:2,title:"争议",slug:"争议",link:"#争议",children:[]},{level:2,title:"实战",slug:"实战",link:"#实战",children:[{level:3,title:"简单例子",slug:"简单例子",link:"#简单例子",children:[]},{level:3,title:"复杂例子",slug:"复杂例子",link:"#复杂例子",children:[]}]},{level:2,title:"附",slug:"附",link:"#附",children:[]}],path:"/java/which-one-is-better-Boolean-or-boolean.html",pathLocale:"/",extraFields:[]},{title:"forEach 还是 map?",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]},{level:2,title:"解析",slug:"解析",link:"#解析",children:[]},{level:2,title:"实战",slug:"实战",link:"#实战",children:[]}],path:"/java/which-one-is-better-forEach-or-map.html",pathLocale:"/",extraFields:[]},{title:"Jackson 经典异常 UnrecognizedPropertyException",headers:[],path:"/java/why-i-prefer-fastjson-instead-of-jackson.html",pathLocale:"/",extraFields:[]},{title:"升个jar版本,怎么这么难?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"接口调用示意图",slug:"接口调用示意图",link:"#接口调用示意图",children:[]},{level:2,title:"已知信息",slug:"已知信息",link:"#已知信息",children:[]},{level:2,title:"问题",slug:"问题",link:"#问题",children:[]},{level:2,title:"复盘",slug:"复盘",link:"#复盘",children:[]}],path:"/java/why-is-it-so-hard-to-upgrade-dependencies.html",pathLocale:"/",extraFields:[]},{title:"使用Ragas评估LLM应用",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"数据说明",slug:"数据说明",link:"#数据说明",children:[]},{level:2,title:"正确性❌",slug:"正确性❌",link:"#正确性❌",children:[]},{level:2,title:"忠实度✔",slug:"忠实度✔",link:"#忠实度✔",children:[]},{level:2,title:"实战演示",slug:"实战演示",link:"#实战演示",children:[{level:3,title:"准备好样例问题",slug:"准备好样例问题",link:"#准备好样例问题",children:[]},{level:3,title:"准备好正确答案",slug:"准备好正确答案",link:"#准备好正确答案",children:[]},{level:3,title:"编写回答函数",slug:"编写回答函数",link:"#编写回答函数",children:[]},{level:3,title:"进行答案评估",slug:"进行答案评估",link:"#进行答案评估",children:[]}]}],path:"/llm/evaluate-llm-app-with-ragas.html",pathLocale:"/",extraFields:[]},{title:"大语言模型赋能备案审查",headers:[{level:2,title:"业务背景",slug:"业务背景",link:"#业务背景",children:[]},{level:2,title:"核心流程",slug:"核心流程",link:"#核心流程",children:[]},{level:2,title:"项目难点",slug:"项目难点",link:"#项目难点",children:[]},{level:2,title:"经验总结",slug:"经验总结",link:"#经验总结",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/llm/llm-with-recordation-review-of-regulations.html",pathLocale:"/",extraFields:[]},{title:"数据备份案例:mysqldump实战",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"架构",slug:"架构",link:"#架构",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"导出",slug:"导出",link:"#导出",children:[{level:3,title:"--set-gtid-purged=OFF",slug:"set-gtid-purged-off",link:"#set-gtid-purged-off",children:[]},{level:3,title:"--ignore-table",slug:"ignore-table",link:"#ignore-table",children:[]}]},{level:2,title:"导入",slug:"导入",link:"#导入",children:[]},{level:2,title:"为什么不?",slug:"为什么不",link:"#为什么不",children:[]}],path:"/mysql/mysql-backup-case-study-mysqldump-in-action.html",pathLocale:"/",extraFields:[]},{title:"数据迁移案例:表AUTO_INCREMENT加10w",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"备份",slug:"备份",link:"#备份",children:[]},{level:2,title:"SQL编写",slug:"sql编写",link:"#sql编写",children:[]},{level:2,title:"存储过程(可复用",slug:"存储过程-可复用",link:"#存储过程-可复用",children:[]}],path:"/mysql/mysql-data-migration-case-study-add-auto-increment.html",pathLocale:"/",extraFields:[]},{title:"MySQL 命令行执行SQL的细节",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"环境说明",slug:"环境说明",link:"#环境说明",children:[]},{level:2,title:"执行SQL文件",slug:"执行sql文件",link:"#执行sql文件",children:[]},{level:2,title:"复制粘贴执行",slug:"复制粘贴执行",link:"#复制粘贴执行",children:[]},{level:2,title:"如果要删除错误的数据怎么办?",slug:"如果要删除错误的数据怎么办",link:"#如果要删除错误的数据怎么办",children:[]}],path:"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",pathLocale:"/",extraFields:[]},{title:"给LLM应用添加日志",headers:[{level:2,title:"logging替代print",slug:"logging替代print",link:"#logging替代print",children:[]},{level:2,title:"何时打印日志",slug:"何时打印日志",link:"#何时打印日志",children:[{level:3,title:"外部调用",slug:"外部调用",link:"#外部调用",children:[]},{level:3,title:"异常捕获",slug:"异常捕获",link:"#异常捕获",children:[]},{level:3,title:"提前返回",slug:"提前返回",link:"#提前返回",children:[]},{level:3,title:"复杂或特殊的if-else",slug:"复杂或特殊的if-else",link:"#复杂或特殊的if-else",children:[]}]}],path:"/python/add-logging-for-llm-app.html",pathLocale:"/",extraFields:[]},{title:"Python 导出 MySQL 库表信息到 Excel",headers:[{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"代码",slug:"代码",link:"#代码",children:[]},{level:2,title:"其他细节",slug:"其他细节",link:"#其他细节",children:[]}],path:"/python/export-mysql-table-into-excel.html",pathLocale:"/",extraFields:[]},{title:"mr.py",headers:[{level:2,title:"源文件:main.py",slug:"源文件-main-py",link:"#源文件-main-py",children:[]},{level:2,title:"测试文件:test/test_mr.py",slug:"测试文件-test-test-mr-py",link:"#测试文件-test-test-mr-py",children:[]}],path:"/python/mr.py.html",pathLocale:"/",extraFields:[]},{title:"单元测试概述",headers:[{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"How",slug:"how",link:"#how",children:[{level:3,title:"测试代码的风格",slug:"测试代码的风格",link:"#测试代码的风格",children:[]},{level:3,title:"测试难点",slug:"测试难点",link:"#测试难点",children:[]},{level:3,title:"常用工具",slug:"常用工具",link:"#常用工具",children:[]}]},{level:2,title:"Bad Examples",slug:"bad-examples",link:"#bad-examples",children:[{level:3,title:"没有测试类",slug:"没有测试类",link:"#没有测试类",children:[]},{level:3,title:"没有断言",slug:"没有断言",link:"#没有断言",children:[]},{level:3,title:"无法重复执行",slug:"无法重复执行",link:"#无法重复执行",children:[]}]}],path:"/software-testing/unit-testing-overview.html",pathLocale:"/",extraFields:[]},{title:"使用 RestAssured 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"为什么不用Postman",slug:"为什么不用postman",link:"#为什么不用postman",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"快速上手",slug:"快速上手",link:"#快速上手",children:[]},{level:2,title:"通用设置",slug:"通用设置",link:"#通用设置",children:[]},{level:2,title:"请求示例",slug:"请求示例",link:"#请求示例",children:[]},{level:2,title:"接口依赖",slug:"接口依赖",link:"#接口依赖",children:[]},{level:2,title:"上传示例",slug:"上传示例",link:"#上传示例",children:[]},{level:2,title:"下载示例",slug:"下载示例",link:"#下载示例",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他问题",slug:"其他问题",link:"#其他问题",children:[{level:3,title:"为什么不用 Pytest",slug:"为什么不用-pytest",link:"#为什么不用-pytest",children:[]},{level:3,title:"这也是单元测试吗",slug:"这也是单元测试吗",link:"#这也是单元测试吗",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/software-testing/use-RestAssured-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Cypress 进行端对端测试",headers:[{level:2,title:"为什么写端对端测试",slug:"为什么写端对端测试",link:"#为什么写端对端测试",children:[]},{level:2,title:"为什么用 Cypress",slug:"为什么用-cypress",link:"#为什么用-cypress",children:[]},{level:2,title:"快速开始",slug:"快速开始",link:"#快速开始",children:[{level:3,title:"安装",slug:"安装",link:"#安装",children:[]},{level:3,title:"加速下载",slug:"加速下载",link:"#加速下载",children:[]},{level:3,title:"目录结构",slug:"目录结构",link:"#目录结构",children:[]},{level:3,title:"与 Jest 协同工作",slug:"与-jest-协同工作",link:"#与-jest-协同工作",children:[]},{level:3,title:"检查依赖及生产安装依赖命令",slug:"检查依赖及生产安装依赖命令",link:"#检查依赖及生产安装依赖命令",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"更复杂的示例",slug:"更复杂的示例",link:"#更复杂的示例",children:[]}]},{level:2,title:"结合TypeScript",slug:"结合typescript",link:"#结合typescript",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"直接运行 Cypress",slug:"直接运行-cypress",link:"#直接运行-cypress",children:[]},{level:3,title:"使用 start-server-and-test",slug:"使用-start-server-and-test",link:"#使用-start-server-and-test",children:[]},{level:3,title:"This job is stuck",slug:"this-job-is-stuck",link:"#this-job-is-stuck",children:[]},{level:3,title:"Cypress Dashbord",slug:"cypress-dashbord",link:"#cypress-dashbord",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"拓展阅读",slug:"拓展阅读",link:"#拓展阅读",children:[]}],path:"/software-testing/use-cypress-for-e2e-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Jest 实践测试驱动开发",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"环境搭建",slug:"环境搭建",link:"#环境搭建",children:[]},{level:2,title:"开发",slug:"开发",link:"#开发",children:[{level:3,title:"文件初始化",slug:"文件初始化",link:"#文件初始化",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"第二个用例",slug:"第二个用例",link:"#第二个用例",children:[]},{level:3,title:"第三个用例",slug:"第三个用例",link:"#第三个用例",children:[]},{level:3,title:"第四个用例",slug:"第四个用例",link:"#第四个用例",children:[]},{level:3,title:"第五个用例",slug:"第五个用例",link:"#第五个用例",children:[]},{level:3,title:"重构",slug:"重构",link:"#重构",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/software-testing/use-jest-for-test-driven-development.html",pathLocale:"/",extraFields:[]},{title:"下一代 UI 自动化测试工具 Playwright",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"代码生成",slug:"代码生成",link:"#代码生成",children:[]},{level:3,title:"修改代码",slug:"修改代码",link:"#修改代码",children:[]},{level:3,title:"执行用例",slug:"执行用例",link:"#执行用例",children:[]},{level:3,title:"调试用例",slug:"调试用例",link:"#调试用例",children:[]},{level:3,title:"查看报告",slug:"查看报告",link:"#查看报告",children:[]}]},{level:2,title:"常见场景与解决方案",slug:"常见场景与解决方案",link:"#常见场景与解决方案",children:[{level:3,title:"应用登录",slug:"应用登录",link:"#应用登录",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"超时时间",slug:"超时时间",link:"#超时时间",children:[]},{level:3,title:"元素选择",slug:"元素选择",link:"#元素选择",children:[]},{level:3,title:"声明断言 && 检查元素是否存在",slug:"声明断言-检查元素是否存在",link:"#声明断言-检查元素是否存在",children:[]},{level:3,title:"获取第n个元素",slug:"获取第n个元素",link:"#获取第n个元素",children:[]},{level:3,title:"遍历元素",slug:"遍历元素",link:"#遍历元素",children:[]},{level:3,title:"获取元素属性",slug:"获取元素属性",link:"#获取元素属性",children:[]},{level:3,title:"判断子元素数量",slug:"判断子元素数量",link:"#判断子元素数量",children:[]},{level:3,title:"鼠标悬浮",slug:"鼠标悬浮",link:"#鼠标悬浮",children:[]},{level:3,title:"操作剪贴板",slug:"操作剪贴板",link:"#操作剪贴板",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"setup.py bdist_wheel did not run successfully",slug:"setup-py-bdist-wheel-did-not-run-successfully",link:"#setup-py-bdist-wheel-did-not-run-successfully",children:[]}]}],path:"/software-testing/use-playwright-for-ui-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Postman 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"本地调试",slug:"本地调试",link:"#本地调试",children:[{level:3,title:"接口集合",slug:"接口集合",link:"#接口集合",children:[]},{level:3,title:"从 cURL 导入",slug:"从-curl-导入",link:"#从-curl-导入",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"请求设置",slug:"请求设置",link:"#请求设置",children:[]}]},{level:2,title:"接口测试",slug:"接口测试",link:"#接口测试",children:[{level:3,title:"编写用例",slug:"编写用例",link:"#编写用例",children:[]},{level:3,title:"上传文件",slug:"上传文件",link:"#上传文件",children:[]},{level:3,title:"运行集合",slug:"运行集合",link:"#运行集合",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"导出接口",slug:"导出接口",link:"#导出接口",children:[]},{level:3,title:"导出环境变量",slug:"导出环境变量",link:"#导出环境变量",children:[]},{level:3,title:"复制要上传的文件",slug:"复制要上传的文件",link:"#复制要上传的文件",children:[]},{level:3,title:"提交到Git",slug:"提交到git",link:"#提交到git",children:[]},{level:3,title:"建立CI任务",slug:"建立ci任务",link:"#建立ci任务",children:[]}]}],path:"/software-testing/use-postman-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 pytest 为LLM应用添加回归测试",headers:[{level:2,title:"回归测试的必要性",slug:"回归测试的必要性",link:"#回归测试的必要性",children:[]},{level:2,title:"pytest",slug:"pytest",link:"#pytest",children:[{level:3,title:"安装",slug:"安装",link:"#安装",children:[]},{level:3,title:"配置",slug:"配置",link:"#配置",children:[]},{level:3,title:"用例",slug:"用例",link:"#用例",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]}],path:"/software-testing/use-pytest-for-regression-testing-in-llm-app.html",pathLocale:"/",extraFields:[]},{title:"科学上网",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"购买指南",slug:"购买指南",link:"#购买指南",children:[]},{level:2,title:"客户端",slug:"客户端",link:"#客户端",children:[]},{level:2,title:"导入配置",slug:"导入配置",link:"#导入配置",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"500 内部代理错误",slug:"_500-内部代理错误",link:"#_500-内部代理错误",children:[]},{level:3,title:"修改PAC文件",slug:"修改pac文件",link:"#修改pac文件",children:[]},{level:3,title:"使用 New Bing",slug:"使用-new-bing",link:"#使用-new-bing",children:[]},{level:3,title:"申请美区apple id",slug:"申请美区apple-id",link:"#申请美区apple-id",children:[]}]}],path:"/tools/how-to-connect-to-internet.html",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]},{title:"Daily",headers:[],path:"/daily/",pathLocale:"/",extraFields:[]},{title:"English",headers:[],path:"/english/",pathLocale:"/",extraFields:[]},{title:"Devops",headers:[],path:"/devops/",pathLocale:"/",extraFields:[]},{title:"Frontend",headers:[],path:"/frontend/",pathLocale:"/",extraFields:[]},{title:"Git",headers:[],path:"/git/",pathLocale:"/",extraFields:[]},{title:"Java",headers:[],path:"/java/",pathLocale:"/",extraFields:[]},{title:"Llm",headers:[],path:"/llm/",pathLocale:"/",extraFields:[]},{title:"Mysql",headers:[],path:"/mysql/",pathLocale:"/",extraFields:[]},{title:"Python",headers:[],path:"/python/",pathLocale:"/",extraFields:[]},{title:"Software Testing",headers:[],path:"/software-testing/",pathLocale:"/",extraFields:[]},{title:"Tools",headers:[],path:"/tools/",pathLocale:"/",extraFields:[]},{title:"分类",headers:[],path:"/category/",pathLocale:"/",extraFields:[]},{title:"标签",headers:[],path:"/tag/",pathLocale:"/",extraFields:[]},{title:"文章",headers:[],path:"/article/",pathLocale:"/",extraFields:[]},{title:"收藏",headers:[],path:"/star/",pathLocale:"/",extraFields:[]},{title:"时间轴",headers:[],path:"/timeline/",pathLocale:"/",extraFields:[]},{title:"标签: Daily",headers:[],path:"/tag/daily/",pathLocale:"/",extraFields:[]},{title:"标签: Frontend",headers:[],path:"/tag/frontend/",pathLocale:"/",extraFields:[]},{title:"标签: Video",headers:[],path:"/tag/video/",pathLocale:"/",extraFields:[]},{title:"标签: MySQL",headers:[],path:"/tag/mysql/",pathLocale:"/",extraFields:[]},{title:"标签: AI",headers:[],path:"/tag/ai/",pathLocale:"/",extraFields:[]},{title:"标签: Emotion",headers:[],path:"/tag/emotion/",pathLocale:"/",extraFields:[]},{title:"标签: Working Experience",headers:[],path:"/tag/working-experience/",pathLocale:"/",extraFields:[]},{title:"标签: Tool",headers:[],path:"/tag/tool/",pathLocale:"/",extraFields:[]},{title:"标签: Design",headers:[],path:"/tag/design/",pathLocale:"/",extraFields:[]},{title:"标签: English",headers:[],path:"/tag/english/",pathLocale:"/",extraFields:[]},{title:"标签: DevOps",headers:[],path:"/tag/devops/",pathLocale:"/",extraFields:[]},{title:"标签: Linux",headers:[],path:"/tag/linux/",pathLocale:"/",extraFields:[]},{title:"标签: S3",headers:[],path:"/tag/s3/",pathLocale:"/",extraFields:[]},{title:"标签: OBS",headers:[],path:"/tag/obs/",pathLocale:"/",extraFields:[]},{title:"标签: OSS",headers:[],path:"/tag/oss/",pathLocale:"/",extraFields:[]},{title:"标签: Python",headers:[],path:"/tag/python/",pathLocale:"/",extraFields:[]},{title:"标签: Git",headers:[],path:"/tag/git/",pathLocale:"/",extraFields:[]},{title:"标签: GitLab",headers:[],path:"/tag/gitlab/",pathLocale:"/",extraFields:[]},{title:"标签: Java",headers:[],path:"/tag/java/",pathLocale:"/",extraFields:[]},{title:"标签: Node.js",headers:[],path:"/tag/node.js/",pathLocale:"/",extraFields:[]},{title:"标签: JavaScript",headers:[],path:"/tag/javascript/",pathLocale:"/",extraFields:[]},{title:"标签: llm",headers:[],path:"/tag/llm/",pathLocale:"/",extraFields:[]},{title:"标签: Testing",headers:[],path:"/tag/testing/",pathLocale:"/",extraFields:[]}],V2=W($2),F2=()=>V2,N2=({searchIndex:e,routeLocale:t,query:l,maxSuggestions:n})=>{const a=E(()=>e.value.filter(i=>i.pathLocale===t.value));return E(()=>{const i=l.value.trim().toLowerCase();if(!i)return[];const r=[],o=(c,u)=>{xs(i,[u.title])&&r.push({link:`${c.path}#${u.slug}`,title:c.title,header:u.title});for(const d of u.children){if(r.length>=n.value)return;o(c,d)}};for(const c of a.value){if(r.length>=n.value)break;if(xs(i,[c.title,...c.extraFields])){r.push({link:c.path,title:c.title});continue}for(const u of c.headers){if(r.length>=n.value)break;o(c,u)}}return r})},j2=e=>{const t=W(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},B2=$({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:l,maxSuggestions:n}=Sd(e),a=Ve(),i=mt(),r=F2(),o=W(null),c=W(!1),u=W(""),d=E(()=>t.value[i.value]??{}),p=N2({searchIndex:r,routeLocale:i,query:u,maxSuggestions:n}),{focusIndex:h,focusNext:v,focusPrev:b}=j2(p);M2({input:o,hotKeys:l});const w=E(()=>c.value&&!!p.value.length),L=()=>{w.value&&b()},m=()=>{w.value&&v()},_=I=>{if(!w.value)return;const R=p.value[I];R&&a.push(R.link).then(()=>{u.value="",h.value=0})};return()=>s("form",{class:"search-box",role:"search"},[s("input",{ref:o,type:"search",placeholder:d.value.placeholder,autocomplete:"off",spellcheck:!1,value:u.value,onFocus:()=>c.value=!0,onBlur:()=>c.value=!1,onInput:I=>u.value=I.target.value,onKeydown:I=>{switch(I.key){case"ArrowUp":{L();break}case"ArrowDown":{m();break}case"Enter":{I.preventDefault(),_(h.value);break}}}}),w.value&&s("ul",{class:"suggestions",onMouseleave:()=>h.value=-1},p.value.map(({link:I,title:R,header:B},C)=>s("li",{class:["suggestion",{focus:h.value===C}],onMouseenter:()=>h.value=C,onMousedown:()=>_(C)},s("a",{href:I,onClick:V=>V.preventDefault()},[s("span",{class:"page-title"},R),B&&s("span",{class:"page-header"},`> ${B}`)]))))])}});const H2={},z2=["s","/"],q2=5,G2=dt({enhance({app:e}){e.component("SearchBox",t=>s(B2,{locales:H2,hotKeys:z2,maxSuggestions:q2,...t}))}}),U2={enhance:({app:e})=>{}},Rn=[N0,Z1,rv,dv,vv,yv,Ev,Iv,Dv,qv,I2,G2,U2],W2=[["v-8daa1a0e","/",{y:"h",t:"levy's blog",i:"home",O:null},["/README.md"]],["v-22a39d25","/about.html",{y:"p",t:"关于",i:"circle-info",O:null},[":md"]],["v-30a50b00","/daily/a-vuepress2-entertaining-video.html",{d:1690848e6,l:"2023年8月1日",g:["Daily","Frontend","Video"],e:`

VuePress2 娱乐视频

+

参考《原神,启动》的梗,做的一个娱乐向视频。

+`,r:{minutes:.11,words:34},y:"a",t:"VuePress2 娱乐视频",O:-1690848e6},[":md"]],["v-0481cc80","/daily/a-warning-from-navicat.html",{d:16907616e5,l:"2023年7月31日",g:["Daily","Video"],e:`

来自Navicat的侵权警告

+

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

+

另外,开发者常用的软件的合法替代品,视频中也有推荐。

+`,r:{minutes:.27,words:81},y:"a",t:"来自Navicat的侵权警告",O:-16907616e5},[":md"]],["v-79e139f8","/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",{d:16920576e5,l:"2023年8月15日",g:["Daily","Video","MySQL"],e:`

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

+

Background

+

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

+
CREATE TABLE \`my_table\` (
+  \`id\` bigint NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY (\`id\`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+

That is the knowledge that today I want to share with you.

+`,r:{minutes:1.78,words:533},y:"a",t:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",O:-16920576e5},[":md"]],["v-7a74360a","/daily/claude-ai-in-action-extract-info-from-html.html",{d:16929216e5,l:"2023年8月25日",g:["Daily","Video","AI"],e:`

Claude AI应用案例,从HTML中抽取文本

+

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

+`,r:{minutes:.19,words:58},y:"a",t:"Claude AI应用案例,从HTML中抽取文本",O:-16929216e5},[":md"]],["v-76f251d2","/daily/copy-code-may-not-be-guilty.html",{d:16955136e5,l:"2023年9月24日",g:["Daily"],e:`

复制代码也许不是罪

+

前言

+

熟悉我的人都知道,我对代码是有追求的。

+

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

+

我早期认为:复制代码就是菜。

+

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

+

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

+

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

+`,r:{minutes:1.51,words:453},y:"a",t:"复制代码也许不是罪",O:-16955136e5},[":md"]],["v-f47f129e","/daily/dont-try-to-argue-with-a-sb.html",{d:16911072e5,l:"2023年8月4日",g:["Daily","Emotion","Working Experience","Video"],e:`

不要与傻逼进行争吵

+

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

+

我在沟通上,有以下可以改正的点:
+0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

+
    +
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. +
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. +
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. +
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. +
+`,r:{minutes:3.92,words:1176},y:"a",t:"不要与傻逼进行争吵",O:-16911072e5},[":md"]],["v-fd7b7f92","/daily/iteration-retrospective-of-sanyuan.html",{d:16941312e5,l:"2023年9月8日",g:["Daily","Working Experience"],e:`

迭代复盘之三员管理

+

前言

+

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

+

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

+

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

+`,r:{minutes:2.52,words:756},y:"a",t:"迭代复盘之三员管理",O:-16941312e5},[":md"]],["v-81a71778","/daily/leverage-ai-to-boost-coding-productivity.html",{d:1693008e6,l:"2023年8月26日",g:["AI","Daily"],e:`

都什么年代了,还在用传统方式写代码?

+

前言

+

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

+

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

+

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

`,r:{minutes:7.37,words:2212},y:"a",t:"都什么年代了,还在用传统方式写代码?",O:-1693008e6},[":md"]],["v-5c48d497","/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",{d:1694304e6,l:"2023年9月10日",g:["Daily","Video"],e:`

微软中国CTO演讲观后感

+

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

+`,r:{minutes:.17,words:51},y:"a",t:"微软中国CTO演讲观后感",O:-1694304e6},[":md"]],["v-4f919602","/daily/testing-environments-should-be-consistent-with-production-environments.html",{d:16997472e5,l:"2023年11月12日",g:["Daily"],e:`

生产教训:测试环境要与生产环境一致

+

事件还原

+

业务流程:

+
    +
  1. app-a 上传文件
  2. +
  3. app-b 下载文件后使用文件
  4. +
+

其他信息:

+
    +
  1. 开发、测试环境使用 MinIO
  2. +
  3. 生产环境使用 Amazon S3
  4. +
+

问题:

+
    +
  1. app-a 上传文件成功
  2. +
  3. app-b 使用文件报错
  4. +
+

逐步分析定位问题:

+
    +
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. +
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. +
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. +
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. +
`,r:{minutes:2.25,words:676},y:"a",t:"生产教训:测试环境要与生产环境一致",O:-16997472e5},[":md"]],["v-1e305501","/daily/things-I-have-to-vent-about-vue.html",{d:16906752e5,l:"2023年7月30日",g:["Frontend","Daily","Video"],e:`

对Vue不得不吐槽的事

+

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

+

总结一下,我对Vue生态不满的地方在于:

+
    +
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. +
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. +
+`,r:{minutes:.42,words:126},y:"a",t:"对Vue不得不吐槽的事",O:-16906752e5},[":md"]],["v-404740fa","/daily/use-claude2-instead-of-chatgpt.html",{d:16914528e5,l:"2023年8月8日",g:["Video","AI","Tool"],e:`

再见ChatGPT,我选择Claude2!

+

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

+

首先要评测的当然是ChatGPT了,因为最早用的就是它。

+`,r:{minutes:3.76,words:1128},y:"a",t:"再见ChatGPT,我选择Claude2!",O:-16914528e5},[":md"]],["v-f1efc11c","/daily/vim-creator-pass-away.html",{d:169128e7,l:"2023年8月6日",g:["Daily","Video"],e:`

Vim 作者离世

+

R.I.P 🙏

+

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

+

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

+`,r:{minutes:.24,words:71},y:"a",t:"Vim 作者离世",O:-169128e7},[":md"]],["v-bd2b5fe8","/daily/you-dont-need-to-add-tenant_id-to-every-table.html",{d:16949952e5,l:"2023年9月18日",g:["Design","Daily"],e:`

技术点评:别每张表都加tenant_id

+

前言

+

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

+`,r:{minutes:3.26,words:977},y:"a",t:"技术点评:别每张表都加tenant_id",O:-16949952e5},[":md"]],["v-971cc7fe","/english/contemporary-college-english-1.html",{d:16537824e5,l:"2022年5月29日",g:"English",e:`

现代大学英语精读(第2版)第一册

+

介绍

+

全书链接:https://www.ximalaya.com/album/43891910

+

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

+

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

`,r:{minutes:1.38,words:414},y:"a",t:"现代大学英语精读(第2版)第一册",O:-16537824e5},[":md"]],["v-93b316c0","/english/contemporary-college-english-2.html",{d:16543008e5,l:"2022年6月4日",g:"English",e:`

现代大学英语精读(第2版)第二册

+

介绍

+

全书链接:https://www.ximalaya.com/album/44290107

+

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

+

值得一读的文章

+

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

`,r:{minutes:1.96,words:588},y:"a",t:"现代大学英语精读(第2版)第二册",O:-16543008e5},[":md"]],["v-90496582","/english/contemporary-college-english-3.html",{d:16561152e5,l:"2022年6月25日",g:"English",e:`

现代大学英语精读(第2版)第三册

+

介绍

+

全书链接:https://www.ximalaya.com/album/44439108

+

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

`,r:{minutes:1.95,words:586},y:"a",t:"现代大学英语精读(第2版)第三册",O:-16561152e5},[":md"]],["v-8cdfb444","/english/contemporary-college-english-4.html",{d:16574976e5,l:"2022年7月11日",g:"English",e:`

现代大学英语精读(第2版)第四册

+

介绍

+

全书链接:https://www.ximalaya.com/album/44641280

+

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

+

值得一读的文章

+

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

`,r:{minutes:2.02,words:607},y:"a",t:"现代大学英语精读(第2版)第四册",O:-16574976e5},[":md"]],["v-89760306","/english/contemporary-college-english-5.html",{d:16616448e5,l:"2022年8月28日",g:"English",e:`

现代大学英语精读(第2版)第五册

+

介绍

+

全书链接:https://www.ximalaya.com/album/49466046

+

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

+

Who Are You and What Are You Doing Here

`,r:{minutes:7.18,words:2155},y:"a",t:"现代大学英语精读(第2版)第五册",O:-16616448e5},[":md"]],["v-860c51c8","/english/contemporary-college-english-6.html",{d:16622496e5,l:"2022年9月4日",g:"English",e:`

现代大学英语精读(第2版)第六册

+

前言

+

全书链接:https://www.ximalaya.com/album/49468954

+

本册是整个系列的最后一册了,完结撒花🎉

+

Paper Tigers

+

原文链接:https://www.ximalaya.com/sound/414998175

`,r:{minutes:5.08,words:1524},y:"a",t:"现代大学英语精读(第2版)第六册",O:-16622496e5},[":md"]],["v-246f4f17","/english/everyone-can-learn-english-1-overview.html",{d:16559424e5,l:"2022年6月23日",g:"English",e:`

人人都能学会的英语1:开篇

+

为什么学

+

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

+

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

+
    +
  • 出国
  • +
  • 去外企
  • +
  • 有国际化需求
  • +
  • 学习专业领域的前沿知识
  • +
+

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

`,r:{minutes:4.46,words:1337},y:"a",t:"人人都能学会的英语1:开篇",O:-16559424e5},[":md"]],["v-3ac0474c","/english/everyone-can-learn-english-2-pronunciation.html",{d:16562016e5,l:"2022年6月26日",g:"English",e:`

人人都能学会的英语2:音标

+

方法

+

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

+

我推荐根据赖世雄的《美语音标》进行学习:

+
    +
  • 在微信公众号 常春藤英语集团 买相关书籍
  • +
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • +
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • +
+

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

`,r:{minutes:3.61,words:1083},y:"a",t:"人人都能学会的英语2:音标",O:-16562016e5},[":md"]],["v-58a409d7","/english/everyone-can-learn-english-3-words.html",{d:1656288e6,l:"2022年6月27日",g:"English",e:`

人人都能学会的英语3:单词

+

方法

+

单词是听说读写的基础,是有必要花时间去学习的。

+

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

+
    +
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. +
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. +
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. +
  7. 学习软件:欧路词典——免费,全平台通用
  8. +
`,r:{minutes:4,words:1200},y:"a",t:"人人都能学会的英语3:单词",O:-1656288e6},[":md"]],["v-788d194a","/english/everyone-can-learn-english-4-listening-and-speaking.html",{d:16563744e5,l:"2022年6月28日",g:"English",e:`

人人都能学会的英语4:听说

+

前言

+

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

+

方法

+

听说属于综合能力,建议遵循以下学习步骤:

+
    +
  1. 激发兴趣,建立信心
  2. +
  3. 明确方向
  4. +
  5. 脚踏实地,坚持输入并输出
  6. +
+

1.建立信心

+

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

+`,r:{minutes:3.82,words:1146},y:"a",t:"人人都能学会的英语4:听说",O:-16563744e5},[":md"]],["v-ae153a4e","/english/everyone-can-learn-english-5-reading-and-writing.html",{d:16564608e5,l:"2022年6月29日",g:"English",e:`

人人都能学会的英语5:读写

+

前言

+

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

+

阅读能力升级之旅

+

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

+
    +
  1. 看到英文网站,第一反应是点击切换中文版
  2. +
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. +
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. +
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. +
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. +
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. +
`,r:{minutes:6.03,words:1808},y:"a",t:"人人都能学会的英语5:读写",O:-16564608e5},[":md"]],["v-d42db13c","/english/how-to-self-evaluate-english-level.html",{d:16573248e5,l:"2022年7月9日",g:"English",e:`

英文能力评测手把手教学

+

前言

+

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

+

单词量

+

进入网站:https://preply.com/en/learn/english/test-your-vocab

+

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
+image.png
+在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
+image.png
+对于该结果,网站有以下值得注意的解释:

`,r:{minutes:1.85,words:554},y:"a",t:"英文能力评测手把手教学",O:-16573248e5},[":md"]],["v-6ed7d996","/english/learning-7000-words-task-completed.html",{d:16633728e5,l:"2022年9月17日",g:"English",e:`

完成刷7k单词任务

+

image.png
+这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

+

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

+

再者,说明下为什么要做这件事。主要原因有三:

`,r:{minutes:4.54,words:1362},y:"a",t:"完成刷7k单词任务",O:-16633728e5},[":md"]],["v-221efd1f","/english/let-chatgpt-be-your-foreign-language-teacher.html",{d:16833312e5,l:"2023年5月6日",g:["English","AI"],e:`

让 ChatGPT 成为你的外语私教

+

前言

+

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

+

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

+

准备工作

+

在开始之前,要准备好几样东西:

+
    +
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. +
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. +
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. +
`,r:{minutes:2.83,words:849},y:"a",t:"让 ChatGPT 成为你的外语私教",O:-16833312e5},[":md"]],["v-3f274907","/devops/about-arm-things-you-need-to-know.html",{d:16909344e5,l:"2023年8月2日",g:["Daily","DevOps","Linux"],e:`

关于 Arm 你需要了解的三件事

+

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

+

跟我们有什么关系呢?

+
    +
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. +
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. +
  5. Docker 镜像的构建有注意事项
  6. +
+

构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
+这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。
+

`,r:{minutes:.72,words:217},y:"a",t:"关于 Arm 你需要了解的三件事",O:-16909344e5},[":md"]],["v-6614b1a1","/devops/common-solutions-of-object-storage-for-static-assets.html",{d:15544224e5,l:"2019年4月5日",g:["Frontend","DevOps","S3","OBS","OSS"],e:`

对象存储静态资源常见操作

+

前言

+

把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

+

本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

+`,r:{minutes:3.03,words:908},y:"a",t:"对象存储静态资源常见操作",O:-15544224e5},[":md"]],["v-2e81d6a4","/devops/docker-build-and-push-script.html",{d:17019936e5,l:"2023年12月8日",g:["DevOps","Linux"],e:`

Docker 构建镜像、推送、启动实用脚本

+

misc

+

存储多份 docker 认证信息:

+
mkdir "~/.project1"
+mkdir "~/.project2"
+
+docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token>
+docker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token> 
+
`,r:{minutes:1.34,words:403},y:"a",t:"Docker 构建镜像、推送、启动实用脚本",O:-17019936e5},[":md"]],["v-3c0c097a","/devops/reduce-python-image-size.html",{d:170208e7,l:"2023年12月9日",g:["DevOps","Python"],e:`

缩减Python应用的镜像体积

+

背景

+

当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!
+
+能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

+`,r:{minutes:2.29,words:687},y:"a",t:"缩减Python应用的镜像体积",O:-170208e7},[":md"]],["v-daf302c6","/devops/what-is-the-difference-between-sh-and-bash.html",{d:16910208e5,l:"2023年8月3日",g:["Linux","DevOps","Video"],e:`

sh与bash的区别

+

结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

+

常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

+`,r:{minutes:1.06,words:318},y:"a",t:"sh与bash的区别",O:-16910208e5},[":md"]],["v-72e84a92","/frontend/old-articles.html",{d:15674688e5,l:"2019年9月3日",g:"Frontend",e:`

旧文章精选

+`,r:{minutes:.3,words:90},y:"a",t:"旧文章精选",O:-15674688e5},[":md"]],["v-7320140c","/frontend/performance-optimization-in-action.html",{d:16117056e5,l:"2021年1月27日",g:"Frontend",e:`

前端项目性能优化实战

+

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

+`,r:{minutes:4.69,words:1407},y:"a",t:"前端项目性能优化实战",O:-16117056e5},[":md"]],["v-1e5872c4","/git/git-best-pratices.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

2020-09-21

+

Git最佳实践

+

精简提交

+

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
+如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

+

频繁提交

+

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
+经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

+

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

`,r:{minutes:3.86,words:1158},y:"a",t:"Git最佳实践",O:-16006464e5},[":md"]],["v-48e494ef","/git/git-definitive-guide-to-merge-code.html",{d:1651104e6,l:"2022年4月28日",g:["Git"],e:`

Git代码合并指南

+

前言

+

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
+在说明问题前,先定义一些概念:

+
    +
  • feat:指代功能分支
  • +
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点: +
      +
    • 受保护,不能直接推送
    • +
    • 不会被删除
    • +
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • +
    +
  • +
  • MR:merge request。代码合并请求
  • +
`,r:{minutes:3.45,words:1035},y:"a",t:"Git代码合并指南",O:-1651104e6},[":md"]],["v-60021cbc","/git/git-history-two-tricks-in-idea.html",{d:1691712e6,l:"2023年8月11日",g:["Git"],e:`

Git查看历史记录小技巧

+

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

+

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

+`,r:{minutes:2.16,words:648},y:"a",t:"Git查看历史记录小技巧",O:-1691712e6},[":md"]],["v-642eaaea","/git/git-useful-commands.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

Git常用命令

+

前言

+

本文将列举Git常见场景,并给出相应解决方案。

+

约定: 下文代码块中\${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

+

推荐: 图形化交互式Git教程

+

配置

+

Mac/Linux 用户 执行以下操作

+
vi ~/.gitconfig
+
`,r:{minutes:5.05,words:1515},y:"a",t:"Git常用命令",O:-16006464e5},[":md"]],["v-4008ff77","/git/gitlab-ci.html",{d:16733088e5,l:"2023年1月10日",g:["Git","GitLab","Java","Node.js"],e:`

GitLab CI

+

前言

+

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

+`,r:{minutes:6.67,words:2e3},y:"a",t:"GitLab CI",O:-16733088e5},[":md"]],["v-0fbf5fdc","/git/rethinking-git-flow.html",{d:16504992e5,l:"2022年4月21日",g:"Git",e:`

再论Git Flow

+

背景

+

团队目前使用的 Git 协作模式是:

+
    +
  1. 对每个功能建立相应的 feat 分支
  2. +
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. +
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. +
+

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

`,r:{minutes:5.53,words:1660},y:"a",t:"再论Git Flow",O:-16504992e5},[":md"]],["v-071be141","/git/use-command-line-tool-to-manage-gitlab-merge-request.html",{d:16795296e5,l:"2023年3月23日",g:["Git","GitLab","Python"],e:`

操作 Gitlab MR 的命令行工具

+

背景

+

为什么开发这个工具?主要解决以下问题:

+
    +
  1. 提测、上 UAT 时,避免漏合代码。
  2. +
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. +
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. +
+

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
+并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
+对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

`,r:{minutes:4.42,words:1325},y:"a",t:"操作 Gitlab MR 的命令行工具",O:-16795296e5},[":md"]],["v-86a15cb6","/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",{d:16693344e5,l:"2022年11月25日",g:["Java","Daily"],e:`

IDEA常见问题与解决方案

+

启动参数过长

+

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
+解决方案:

+
    +
  1. 编辑 .idea/workspace.xml
  2. +
  3. 找到 PropertiesComponent
  4. +
  5. 添加:
  6. +
+

或者这样:
+

`,r:{minutes:2.76,words:827},y:"a",t:"IDEA常见问题与解决方案",O:-16693344e5},[":md"]],["v-6dc18ec6","/java/Resolving-Common-Problems-in-Maven.md.html",{d:1670544e6,l:"2022年12月9日",g:["Java","Daily"],e:`

Maven常见问题与解决方案

+

运行 class 找不到主类

+
maven compile
+

得到 class 文件后

+
cd /my-app/target/com/mycompany/app
+java App
+
`,r:{minutes:3.25,words:976},y:"a",t:"Maven常见问题与解决方案",O:-1670544e6},[":md"]],["v-538a98d7","/java/avoid-sending-password-in-plaintext.html",{d:16981056e5,l:"2023年10月24日",g:["Java","JavaScript","Daily"],e:`

避免密码明文传输

+

说明

+

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

+

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

+

流程说明:前端加密,后端解密。

+

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

+`,r:{minutes:1.63,words:488},y:"a",t:"避免密码明文传输",O:-16981056e5},[":md"]],["v-2f6ae09c","/java/check-if-name-exists.html",{d:16975872e5,l:"2023年10月18日",g:["Java","MySQL","Daily"],e:`

检查名字是否重复

+

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

+`,r:{minutes:1.18,words:353},y:"a",t:"检查名字是否重复",O:-16975872e5},[":md"]],["v-b5a78a7a","/java/common-practices-for-handling-excel.html",{d:1693872e6,l:"2023年9月5日",g:["Java","Daily"],e:`

Excel处理常用实践

+

基础知识

+

导入需要用到对象,MultipartFile。

+
@PostMapping("/import")
+public boolean importLicense(
+      @RequestParam("file") MultipartFile file,
+      @RequestParam("tenantId") @NotBlank String tenantId,
+) {
+  return true;
+}
+
`,r:{minutes:4.88,words:1464},y:"a",t:"Excel处理常用实践",O:-1693872e6},[":md"]],["v-100d1814","/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",{d:16917984e5,l:"2023年8月12日",g:["Java"],e:`

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

+

背景

+

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

+

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

+`,r:{minutes:1.41,words:422},y:"a",t:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",O:-16917984e5},[":md"]],["v-d767e98e","/java/recommend-practices-for-collections-naming-convention.html",{d:16540416e5,l:"2022年6月1日",g:["Java","Daily"],e:`

集合命名推荐

+

概述

+

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

+

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
+

+

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

`,r:{minutes:3.03,words:908},y:"a",t:"集合命名推荐",O:-16540416e5},[":md"]],["v-f5471cb0","/java/recommend-practices-for-query-by-date-range.html",{d:16944768e5,l:"2023年9月12日",g:["Java","Daily"],e:`

根据时间范围查询推荐实践

+

背景

+

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

+

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

+

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

+`,r:{minutes:3.21,words:962},y:"a",t:"根据时间范围查询推荐实践",O:-16944768e5},[":md"]],["v-57d9b582","/java/recommend-practices-for-writing-good-functions.html",{d:16657056e5,l:"2022年10月14日",g:["Java","Daily"],e:`

编写函数的最佳实践

+

前言

+

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

+

本文将推荐一些编写函数的最佳实践,以供参数。

+

减少重复

+

这是在遵守 Don't repeat yourself (DRY) 原则。

+

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

`,r:{minutes:5.88,words:1764},y:"a",t:"编写函数的最佳实践",O:-16657056e5},[":md"]],["v-2ba0b01e","/java/using-enum-in-java.html",{d:16657056e5,l:"2022年10月14日",g:["Java","Daily"],e:`

枚举的推荐实践

+

背景

+

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

+

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

+

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

+

Java

+

极简实现

+

理想状态下,枚举就应该这样简单!

+
public enum SEX {
+  MALE,
+  FEMALE;
+}
+
`,r:{minutes:5.59,words:1676},y:"a",t:"枚举的推荐实践",O:-16657056e5},[":md"]],["v-386e94f1","/java/which-one-is-better-Boolean-or-boolean.html",{d:1654128e6,l:"2022年6月2日",g:["Java","Daily"],e:`

Boolean 还是 boolean?

+

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

+

结论

+

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

+

原文如下:
+

`,r:{minutes:3.48,words:1045},y:"a",t:"Boolean 还是 boolean?",O:-1654128e6},[":md"]],["v-0d6828c2","/java/which-one-is-better-forEach-or-map.html",{d:16491168e5,l:"2022年4月5日",g:["Java","Daily"],e:`

forEach 还是 map?

+

背景

+

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

+
List<Type> result = new ArrayList<>();
+list.forEach(src -> {
+    Type target = BeanUtils.copyProperties(src, target);
+    //省略代码
+    result.add(target);
+});
+
`,r:{minutes:3.67,words:1102},y:"a",t:"forEach 还是 map?",O:-16491168e5},[":md"]],["v-1aafac08","/java/why-i-prefer-fastjson-instead-of-jackson.html",{d:1692576e6,l:"2023年8月21日",g:["Java","Daily","Video"],e:`

Jackson 经典异常 UnrecognizedPropertyException

+

原因是 json 包含的字段,多于 Java 实体类定义的字段。

+

解决方法很简单:

+
new ObjectMapper()
+  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+

或者为相关实体添加注解:

+
@JsonIgnoreProperties(ignoreUnknown = true)
+public class ObjectParseFromJsonString {  }
+
`,r:{minutes:.36,words:109},y:"a",t:"Jackson 经典异常 UnrecognizedPropertyException",O:-1692576e6},[":md"]],["v-ca672354","/java/why-is-it-so-hard-to-upgrade-dependencies.html",{d:1693008e6,l:"2023年8月26日",g:["Java","Daily","Video"],e:`

升个jar版本,怎么这么难?

+

前言

+

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

+

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

+

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

+`,r:{minutes:1.8,words:540},y:"a",t:"升个jar版本,怎么这么难?",O:-1693008e6},[":md"]],["v-8903fc5e","/llm/evaluate-llm-app-with-ragas.html",{d:17121024e5,l:"2024年4月3日",g:["Python","llm"],e:`

使用Ragas评估LLM应用

+

说明

+

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

+

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

+

安装

+
pip install ragas
+
`,r:{minutes:2.71,words:813},y:"a",t:"使用Ragas评估LLM应用",O:-17121024e5},[":md"]],["v-5d5f3b26","/llm/llm-with-recordation-review-of-regulations.html",{d:17169408e5,l:"2024年5月29日",g:["llm"],e:`

大语言模型赋能备案审查

+

业务背景

+

备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
+在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
+因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

+

核心流程

+
`,r:{minutes:1.87,words:561},y:"a",t:"大语言模型赋能备案审查",O:-17169408e5},[":md"]],["v-143a8bce","/mysql/mysql-backup-case-study-mysqldump-in-action.html",{d:16922304e5,l:"2023年8月17日",g:["Daily","MySQL"],e:`

数据备份案例:mysqldump实战

+

背景

+

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

+

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

+

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

+`,r:{minutes:4.1,words:1231},y:"a",t:"数据备份案例:mysqldump实战",O:-16922304e5},[":md"]],["v-7e2c7a0c","/mysql/mysql-data-migration-case-study-add-auto-increment.html",{d:1692144e6,l:"2023年8月16日",g:["Daily","MySQL"],e:`

数据迁移案例:表AUTO_INCREMENT加10w

+

背景

+

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

+

问题分析:

+
    +
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. +
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. +
+

迁移思路:

+
    +
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. +
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. +
  5. 记得动手前先确保数据已备份
  6. +
+

注意:

+
    +
  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • +
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
  • +
+`,r:{minutes:2,words:599},y:"a",t:"数据迁移案例:表AUTO_INCREMENT加10w",O:-1692144e6},[":md"]],["v-f297935a","/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",{d:16924032e5,l:"2023年8月19日",g:["Daily","MySQL"],e:`

MySQL 命令行执行SQL的细节

+

背景

+

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

+

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

+

环境说明

+

先说明下我们的环境信息。
+
+我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

`,r:{minutes:3.02,words:906},y:"a",t:"MySQL 命令行执行SQL的细节",O:-16924032e5},[":md"]],["v-de221860","/python/add-logging-for-llm-app.html",{d:1701648e6,l:"2023年12月4日",g:["Python"],e:`

给LLM应用添加日志

+

logging替代print

+

目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。

+

print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。

+`,r:{minutes:3.3,words:989},y:"a",t:"给LLM应用添加日志",O:-1701648e6},[":md"]],["v-5b37b3c6","/python/export-mysql-table-into-excel.html",{d:16779744e5,l:"2023年3月5日",g:"Python",e:`

Python 导出 MySQL 库表信息到 Excel

+

需求

+

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

+`,r:{minutes:1.47,words:440},y:"a",t:"Python 导出 MySQL 库表信息到 Excel",O:-16779744e5},[":md"]],["v-966d933e","/python/mr.py.html",{d:16859232e5,l:"2023年6月5日",g:["Git","Python"],e:`

mr.py

+

操作 Gitlab MR 的命令行工具的源码与测试代码。

+`,r:{minutes:5,words:1501},y:"a",t:"mr.py",O:-16859232e5},[":md"]],["v-113531b4","/software-testing/unit-testing-overview.html",{d:16905024e5,l:"2023年7月28日",g:["Testing"],e:`

单元测试概述

+

Why

+

为什么要做单元测试?或者说,为什么要写测试代码?

+

个人总结为以下两点:

+
    +
  1. 测试左移,降低修复bug的成本
    +
  2. +
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. +
+`,r:{minutes:2.97,words:892},y:"a",t:"单元测试概述",O:-16905024e5},[":md"]],["v-65b23736","/software-testing/use-RestAssured-for-api-testing.html",{d:16862688e5,l:"2023年6月9日",g:["Java","Testing"],e:`

使用 RestAssured 进行 API 测试

+

前言

+

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

+

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

+

What

+

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

+

Why

+

为什么要做 API 测试呢?

`,r:{minutes:6.99,words:2096},y:"a",t:"使用 RestAssured 进行 API 测试",O:-16862688e5},[":md"]],["v-c488ac58","/software-testing/use-cypress-for-e2e-testing.html",{d:16073856e5,l:"2020年12月8日",g:["Node.js","Testing"],e:`

使用 Cypress 进行端对端测试

+

为什么写端对端测试

+

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

+

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

+

为什么用 Cypress

+

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

`,r:{minutes:8.04,words:2413},y:"a",t:"使用 Cypress 进行端对端测试",O:-16073856e5},[":md"]],["v-efcacba2","/software-testing/use-jest-for-test-driven-development.html",{d:15558048e5,l:"2019年4月21日",g:["Node.js","Testing"],e:`

使用 Jest 实践测试驱动开发

+

前言

+

本文将使用jest进行测试驱动开发的示例,源码在github
+旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

`,r:{minutes:6.26,words:1879},y:"a",t:"使用 Jest 实践测试驱动开发",O:-15558048e5},[":md"]],["v-0be1af08","/software-testing/use-playwright-for-ui-testing.html",{d:16834176e5,l:"2023年5月7日",g:["Node.js","Python","Testing"],e:`

下一代 UI 自动化测试工具 Playwright

+

前言

+

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

+
    +
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. +
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. +
+

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

`,r:{minutes:10,words:3001},y:"a",t:"下一代 UI 自动化测试工具 Playwright",O:-16834176e5},[":md"]],["v-75616b85","/software-testing/use-postman-for-api-testing.html",{d:16945632e5,l:"2023年9月13日",g:["Node.js","Daily"],e:`

使用 Postman 进行 API 测试

+

前言

+

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

+

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

+

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

+`,r:{minutes:5.52,words:1656},y:"a",t:"使用 Postman 进行 API 测试",O:-16945632e5},[":md"]],["v-7ba1021b","/software-testing/use-pytest-for-regression-testing-in-llm-app.html",{d:17019936e5,l:"2023年12月8日",g:["Python","Testing","Gitlab"],e:`

使用 pytest 为LLM应用添加回归测试

+

回归测试的必要性

+

基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

+

因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

+

而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。

+`,r:{minutes:2.9,words:871},y:"a",t:"使用 pytest 为LLM应用添加回归测试",O:-17019936e5},[":md"]],["v-17809471","/tools/how-to-connect-to-internet.html",{d:16556832e5,l:"2022年6月20日",g:"Tool",e:`

科学上网

+

说明

+

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

+

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

+

购买指南

+

点击进入服务页面, 看到如下页面:
+image.png

`,r:{minutes:2.72,words:815},y:"a",t:"科学上网",O:-16556832e5},[":md"]],["v-3706649a","/404.html",{y:"p",t:"",O:null},[]],["v-79c9f96f","/daily/",{y:"p",t:"Daily"},[]],["v-43539db8","/english/",{y:"p",t:"English"},[]],["v-71fde78e","/devops/",{y:"p",t:"Devops"},[]],["v-06198984","/frontend/",{y:"p",t:"Frontend"},[]],["v-74473916","/git/",{y:"p",t:"Git"},[]],["v-14c69af4","/java/",{y:"p",t:"Java"},[]],["v-7449895b","/llm/",{y:"p",t:"Llm"},[]],["v-eb072ff4","/mysql/",{y:"p",t:"Mysql"},[]],["v-63cd5dba","/python/",{y:"p",t:"Python"},[]],["v-0df55bac","/software-testing/",{y:"p",t:"Software Testing"},[]],["v-d440f426","/tools/",{y:"p",t:"Tools"},[]],["v-5bc93818","/category/",{y:"p",t:"分类",I:0},[]],["v-744d024e","/tag/",{y:"p",t:"标签",I:0},[]],["v-e52c881c","/article/",{y:"p",t:"文章",I:0},[]],["v-154dc4c4","/star/",{y:"p",t:"收藏",I:0},[]],["v-01560935","/timeline/",{y:"p",t:"时间轴",I:0},[]],["v-3d5315f8","/tag/daily/",{y:"p",t:"标签: Daily",I:0},[]],["v-1b3ae9cf","/tag/frontend/",{y:"p",t:"标签: Frontend",I:0},[]],["v-007c0ae2","/tag/video/",{y:"p",t:"标签: Video",I:0},[]],["v-1bee38ca","/tag/mysql/",{y:"p",t:"标签: MySQL",I:0},[]],["v-0da0abf9","/tag/ai/",{y:"p",t:"标签: AI",I:0},[]],["v-5a3e80fc","/tag/emotion/",{y:"p",t:"标签: Emotion",I:0},[]],["v-01c1de5b","/tag/working-experience/",{y:"p",t:"标签: Working Experience",I:0},[]],["v-29350809","/tag/tool/",{y:"p",t:"标签: Tool",I:0},[]],["v-50d6e023","/tag/design/",{y:"p",t:"标签: Design",I:0},[]],["v-0ca0efe6","/tag/english/",{y:"p",t:"标签: English",I:0},[]],["v-51040344","/tag/devops/",{y:"p",t:"标签: DevOps",I:0},[]],["v-211f44ee","/tag/linux/",{y:"p",t:"标签: Linux",I:0},[]],["v-0da0e901","/tag/s3/",{y:"p",t:"标签: S3",I:0},[]],["v-b309c306","/tag/obs/",{y:"p",t:"标签: OBS",I:0},[]],["v-b3094364","/tag/oss/",{y:"p",t:"标签: OSS",I:0},[]],["v-245f5676","/tag/python/",{y:"p",t:"标签: Python",I:0},[]],["v-b310d42a","/tag/git/",{y:"p",t:"标签: Git",I:0},[]],["v-13275df4","/tag/gitlab/",{y:"p",t:"标签: GitLab",I:0},[]],["v-28a1d8bf","/tag/java/",{y:"p",t:"标签: Java",I:0},[]],["v-60379330","/tag/node.js/",{y:"p",t:"标签: Node.js",I:0},[]],["v-3b951558","/tag/javascript/",{y:"p",t:"标签: JavaScript",I:0},[]],["v-b30c33a0","/tag/llm/",{y:"p",t:"标签: llm",I:0},[]],["v-48b3e46d","/tag/testing/",{y:"p",t:"标签: Testing",I:0},[]]];var Ts=$({name:"Vuepress",setup(){const e=M0();return()=>s(e.value)}}),K2=()=>W2.reduce((e,[t,l,n,a])=>(e.push({name:t,path:l,component:Ts,meta:n},{path:l.endsWith("/")?l+"index.html":l.substring(0,l.length-5),redirect:l},...a.map(i=>({path:i===":md"?l.substring(0,l.length-5)+".md":i,redirect:l}))),e),[{name:"404",path:"/:catchAll(.*)",component:Ts}]),J2=nh,Q2=()=>{const e=jh({history:J2(xi("/")),routes:K2(),scrollBehavior:(t,l,n)=>n||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,l)=>{var n;(t.path!==l.path||l===Et)&&([Dt.value]=await Promise.all([wt.resolvePageData(t.name),(n=Io[t.name])==null?void 0:n.__asyncLoader()]))}),e},Y2=e=>{e.component("ClientOnly",Zn),e.component("Content",No)},X2=(e,t,l)=>{const n=E(()=>wt.resolveLayouts(l)),a=es(()=>t.currentRoute.value.path),i=es(()=>wt.resolveRouteLocale(ol.value.locales,a.value)),r=E(()=>wt.resolveSiteLocaleData(ol.value,i.value)),o=E(()=>wt.resolvePageFrontmatter(Dt.value)),c=E(()=>wt.resolvePageHeadTitle(Dt.value,r.value)),u=E(()=>wt.resolvePageHead(c.value,o.value,r.value)),d=E(()=>wt.resolvePageLang(Dt.value,r.value)),p=E(()=>wt.resolvePageLayout(Dt.value,n.value));return e.provide(R0,n),e.provide(Co,o),e.provide(D0,c),e.provide(So,u),e.provide(Do,d),e.provide($o,p),e.provide(Ti,i),e.provide(Fo,r),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>o.value},$head:{get:()=>u.value},$headTitle:{get:()=>c.value},$lang:{get:()=>d.value},$page:{get:()=>Dt.value},$routeLocale:{get:()=>i.value},$site:{get:()=>ol.value},$siteLocale:{get:()=>r.value},$withBase:{get:()=>xe}}),{layouts:n,pageData:Dt,pageFrontmatter:o,pageHead:u,pageHeadTitle:c,pageLang:d,pageLayout:p,routeLocale:i,siteData:ol,siteLocaleData:r}},Z2=()=>{const e=S0(),t=Mo(),l=W([]),n=()=>{e.value.forEach(i=>{const r=eg(i);r&&l.value.push(r)})},a=()=>{document.documentElement.lang=t.value,l.value.forEach(i=>{i.parentNode===document.head&&document.head.removeChild(i)}),l.value.splice(0,l.value.length),e.value.forEach(i=>{const r=tg(i);r!==null&&(document.head.appendChild(r),l.value.push(r))})};ot($0,a),ke(()=>{n(),a(),ce(()=>e.value,a)})},eg=([e,t,l=""])=>{const n=Object.entries(t).map(([o,c])=>ae(c)?`[${o}=${JSON.stringify(c)}]`:c===!0?`[${o}]`:"").join(""),a=`head > ${e}${n}`;return Array.from(document.querySelectorAll(a)).find(o=>o.innerText===l)||null},tg=([e,t,l])=>{if(!ae(e))return null;const n=document.createElement(e);return Li(t)&&Object.entries(t).forEach(([a,i])=>{ae(i)?n.setAttribute(a,i):i===!0&&n.setAttribute(a,"")}),ae(l)&&n.appendChild(document.createTextNode(l)),n},lg=_0,ng=async()=>{var l;const e=lg({name:"VuepressApp",setup(){var n;Z2();for(const a of Rn)(n=a.setup)==null||n.call(a);return()=>[s(Qo),...Rn.flatMap(({rootComponents:a=[]})=>a.map(i=>s(i)))]}}),t=Q2();Y2(e),X2(e,t,Rn);for(const n of Rn)await((l=n.enhance)==null?void 0:l.call(n,{app:e,router:t,siteData:ol}));return e.use(t),{app:e,router:t}};ng().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{mo as a,yo as b,ig as c,ng as createVueApp,Ae as d,sg as e,rg as f,Op as o,Je as r,qd as w}; diff --git a/assets/app-ee4d23bf.js b/assets/app-ee4d23bf.js deleted file mode 100644 index d5d7c2c4..00000000 --- a/assets/app-ee4d23bf.js +++ /dev/null @@ -1,478 +0,0 @@ -var Cu=Object.defineProperty;var Su=(e,t,l)=>t in e?Cu(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l;var oa=(e,t,l)=>(Su(e,typeof t!="symbol"?t+"":t,l),l);const Du="modulepreload",Mu=function(e){return"/"+e},Xi={},g=function(t,l,n){if(!l||l.length===0)return t();const a=document.getElementsByTagName("link");return Promise.all(l.map(i=>{if(i=Mu(i),i in Xi)return;Xi[i]=!0;const r=i.endsWith(".css"),o=r?'[rel="stylesheet"]':"";if(!!n)for(let d=a.length-1;d>=0;d--){const p=a[d];if(p.href===i&&(!r||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${i}"]${o}`))return;const u=document.createElement("link");if(u.rel=r?"stylesheet":Du,r||(u.as="script",u.crossOrigin=""),u.href=i,document.head.appendChild(u),r)return new Promise((d,p)=>{u.addEventListener("load",d),u.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t()).catch(i=>{const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=i,window.dispatchEvent(r),!r.defaultPrevented)throw i})};function ei(e,t){const l=Object.create(null),n=e.split(",");for(let a=0;a!!l[a.toLowerCase()]:a=>!!l[a]}const Le={},cl=[],st=()=>{},$u=()=>!1,Vu=/^on[^a-z]/,Zl=e=>Vu.test(e),ti=e=>e.startsWith("onUpdate:"),Oe=Object.assign,li=(e,t)=>{const l=e.indexOf(t);l>-1&&e.splice(l,1)},Fu=Object.prototype.hasOwnProperty,oe=(e,t)=>Fu.call(e,t),X=Array.isArray,Dl=e=>Gn(e)==="[object Map]",Nu=e=>Gn(e)==="[object Set]",te=e=>typeof e=="function",ae=e=>typeof e=="string",ni=e=>typeof e=="symbol",Te=e=>e!==null&&typeof e=="object",As=e=>Te(e)&&te(e.then)&&te(e.catch),ju=Object.prototype.toString,Gn=e=>ju.call(e),Bu=e=>Gn(e).slice(8,-1),Hu=e=>Gn(e)==="[object Object]",ai=e=>ae(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Ml=ei(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Un=e=>{const t=Object.create(null);return l=>t[l]||(t[l]=e(l))},zu=/-(\w)/g,tt=Un(e=>e.replace(zu,(t,l)=>l?l.toUpperCase():"")),qu=/\B([A-Z])/g,wl=Un(e=>e.replace(qu,"-$1").toLowerCase()),en=Un(e=>e.charAt(0).toUpperCase()+e.slice(1)),ca=Un(e=>e?`on${en(e)}`:""),Hl=(e,t)=>!Object.is(e,t),ua=(e,t)=>{for(let l=0;l{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:l})},Gu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Uu=e=>{const t=ae(e)?Number(e):NaN;return isNaN(t)?e:t};let Zi;const Ia=()=>Zi||(Zi=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ii(e){if(X(e)){const t={};for(let l=0;l{if(l){const n=l.split(Ku);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function ri(e){let t="";if(ae(e))t=e;else if(X(e))for(let l=0;l{const t=new Set(e);return t.w=0,t.n=0,t},Is=e=>(e.w&Nt)>0,Rs=e=>(e.n&Nt)>0,ld=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let l=0;for(let n=0;n{(d==="length"||d>=c)&&o.push(u)})}else switch(l!==void 0&&o.push(r.get(l)),t){case"add":X(e)?ai(l)&&o.push(r.get("length")):(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ca)));break;case"delete":X(e)||(o.push(r.get(Jt)),Dl(e)&&o.push(r.get(Ca)));break;case"set":Dl(e)&&o.push(r.get(Jt));break}if(o.length===1)o[0]&&Sa(o[0]);else{const c=[];for(const u of o)u&&c.push(...u);Sa(si(c))}}function Sa(e,t){const l=X(e)?e:[...e];for(const n of l)n.computed&&tr(n);for(const n of l)n.computed||tr(n)}function tr(e,t){(e!==at||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function ad(e,t){var l;return(l=Dn.get(e))==null?void 0:l.get(t)}const id=ei("__proto__,__v_isRef,__isVue"),Ds=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(ni)),rd=ci(),sd=ci(!1,!0),od=ci(!0),lr=cd();function cd(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...l){const n=re(this);for(let i=0,r=this.length;i{e[t]=function(...l){El();const n=re(this)[t].apply(this,l);return Ll(),n}}),e}function ud(e){const t=re(this);return qe(t,"has",e),t.hasOwnProperty(e)}function ci(e=!1,t=!1){return function(n,a,i){if(a==="__v_isReactive")return!e;if(a==="__v_isReadonly")return e;if(a==="__v_isShallow")return t;if(a==="__v_raw"&&i===(e?t?Td:Ns:t?Fs:Vs).get(n))return n;const r=X(n);if(!e){if(r&&oe(lr,a))return Reflect.get(lr,a,i);if(a==="hasOwnProperty")return ud}const o=Reflect.get(n,a,i);return(ni(a)?Ds.has(a):id(a))||(e||qe(n,"get",a),t)?o:Ie(o)?r&&ai(a)?o:o.value:Te(o)?e?Yt(o):tn(o):o}}const dd=Ms(),pd=Ms(!0);function Ms(e=!1){return function(l,n,a,i){let r=l[n];if(vl(r)&&Ie(r)&&!Ie(a))return!1;if(!e&&(!Mn(a)&&!vl(a)&&(r=re(r),a=re(a)),!X(l)&&Ie(r)&&!Ie(a)))return r.value=a,!0;const o=X(l)&&ai(n)?Number(n)e,Wn=e=>Reflect.getPrototypeOf(e);function yn(e,t,l=!1,n=!1){e=e.__v_raw;const a=re(e),i=re(t);l||(t!==i&&qe(a,"get",t),qe(a,"get",i));const{has:r}=Wn(a),o=n?ui:l?hi:zl;if(r.call(a,t))return o(e.get(t));if(r.call(a,i))return o(e.get(i));e!==a&&e.get(t)}function bn(e,t=!1){const l=this.__v_raw,n=re(l),a=re(e);return t||(e!==a&&qe(n,"has",e),qe(n,"has",a)),e===a?l.has(e):l.has(e)||l.has(a)}function _n(e,t=!1){return e=e.__v_raw,!t&&qe(re(e),"iterate",Jt),Reflect.get(e,"size",e)}function nr(e){e=re(e);const t=re(this);return Wn(t).has.call(t,e)||(t.add(e),xt(t,"add",e,e)),this}function ar(e,t){t=re(t);const l=re(this),{has:n,get:a}=Wn(l);let i=n.call(l,e);i||(e=re(e),i=n.call(l,e));const r=a.call(l,e);return l.set(e,t),i?Hl(t,r)&&xt(l,"set",e,t):xt(l,"add",e,t),this}function ir(e){const t=re(this),{has:l,get:n}=Wn(t);let a=l.call(t,e);a||(e=re(e),a=l.call(t,e)),n&&n.call(t,e);const i=t.delete(e);return a&&xt(t,"delete",e,void 0),i}function rr(){const e=re(this),t=e.size!==0,l=e.clear();return t&&xt(e,"clear",void 0,void 0),l}function kn(e,t){return function(n,a){const i=this,r=i.__v_raw,o=re(r),c=t?ui:e?hi:zl;return!e&&qe(o,"iterate",Jt),r.forEach((u,d)=>n.call(a,c(u),c(d),i))}}function wn(e,t,l){return function(...n){const a=this.__v_raw,i=re(a),r=Dl(i),o=e==="entries"||e===Symbol.iterator&&r,c=e==="keys"&&r,u=a[e](...n),d=l?ui:t?hi:zl;return!t&&qe(i,"iterate",c?Ca:Jt),{next(){const{value:p,done:h}=u.next();return h?{value:p,done:h}:{value:o?[d(p[0]),d(p[1])]:d(p),done:h}},[Symbol.iterator](){return this}}}}function Pt(e){return function(...t){return e==="delete"?!1:this}}function yd(){const e={get(i){return yn(this,i)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!1)},t={get(i){return yn(this,i,!1,!0)},get size(){return _n(this)},has:bn,add:nr,set:ar,delete:ir,clear:rr,forEach:kn(!1,!0)},l={get(i){return yn(this,i,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!1)},n={get(i){return yn(this,i,!0,!0)},get size(){return _n(this,!0)},has(i){return bn.call(this,i,!0)},add:Pt("add"),set:Pt("set"),delete:Pt("delete"),clear:Pt("clear"),forEach:kn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(i=>{e[i]=wn(i,!1,!1),l[i]=wn(i,!0,!1),t[i]=wn(i,!1,!0),n[i]=wn(i,!0,!0)}),[e,l,t,n]}const[bd,_d,kd,wd]=yd();function di(e,t){const l=t?e?wd:kd:e?_d:bd;return(n,a,i)=>a==="__v_isReactive"?!e:a==="__v_isReadonly"?e:a==="__v_raw"?n:Reflect.get(oe(l,a)&&a in n?l:n,a,i)}const Ed={get:di(!1,!1)},Ld={get:di(!1,!0)},xd={get:di(!0,!1)},Vs=new WeakMap,Fs=new WeakMap,Ns=new WeakMap,Td=new WeakMap;function Ad(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Od(e){return e.__v_skip||!Object.isExtensible(e)?0:Ad(Bu(e))}function tn(e){return vl(e)?e:pi(e,!1,$s,Ed,Vs)}function js(e){return pi(e,!1,md,Ld,Fs)}function Yt(e){return pi(e,!0,gd,xd,Ns)}function pi(e,t,l,n,a){if(!Te(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const i=a.get(e);if(i)return i;const r=Od(e);if(r===0)return e;const o=new Proxy(e,r===2?n:l);return a.set(e,o),o}function ul(e){return vl(e)?ul(e.__v_raw):!!(e&&e.__v_isReactive)}function vl(e){return!!(e&&e.__v_isReadonly)}function Mn(e){return!!(e&&e.__v_isShallow)}function Bs(e){return ul(e)||vl(e)}function re(e){const t=e&&e.__v_raw;return t?re(t):e}function Hs(e){return Sn(e,"__v_skip",!0),e}const zl=e=>Te(e)?tn(e):e,hi=e=>Te(e)?Yt(e):e;function vi(e){$t&&at&&(e=re(e),Ss(e.dep||(e.dep=si())))}function fi(e,t){e=re(e);const l=e.dep;l&&Sa(l)}function Ie(e){return!!(e&&e.__v_isRef===!0)}function W(e){return zs(e,!1)}function Ge(e){return zs(e,!0)}function zs(e,t){return Ie(e)?e:new Pd(e,t)}class Pd{constructor(t,l){this.__v_isShallow=l,this.dep=void 0,this.__v_isRef=!0,this._rawValue=l?t:re(t),this._value=l?t:zl(t)}get value(){return vi(this),this._value}set value(t){const l=this.__v_isShallow||Mn(t)||vl(t);t=l?t:re(t),Hl(t,this._rawValue)&&(this._rawValue=t,this._value=l?t:zl(t),fi(this))}}function it(e){return Ie(e)?e.value:e}const Id={get:(e,t,l)=>it(Reflect.get(e,t,l)),set:(e,t,l,n)=>{const a=e[t];return Ie(a)&&!Ie(l)?(a.value=l,!0):Reflect.set(e,t,l,n)}};function qs(e){return ul(e)?e:new Proxy(e,Id)}class Rd{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:l,set:n}=t(()=>vi(this),()=>fi(this));this._get=l,this._set=n}get value(){return this._get()}set value(t){this._set(t)}}function Cd(e){return new Rd(e)}function Sd(e){const t=X(e)?new Array(e.length):{};for(const l in e)t[l]=Gs(e,l);return t}class Dd{constructor(t,l,n){this._object=t,this._key=l,this._defaultValue=n,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return ad(re(this._object),this._key)}}class Md{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0}get value(){return this._getter()}}function xl(e,t,l){return Ie(e)?e:te(e)?new Md(e):Te(e)&&arguments.length>1?Gs(e,t,l):W(e)}function Gs(e,t,l){const n=e[t];return Ie(n)?n:new Dd(e,t,l)}class $d{constructor(t,l,n,a){this._setter=l,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new oi(t,()=>{this._dirty||(this._dirty=!0,fi(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!a,this.__v_isReadonly=n}get value(){const t=re(this);return vi(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function Vd(e,t,l=!1){let n,a;const i=te(e);return i?(n=e,a=st):(n=e.get,a=e.set),new $d(n,a,i||!a,l)}function Vt(e,t,l,n){let a;try{a=n?e(...n):e()}catch(i){ln(i,t,l)}return a}function Ze(e,t,l,n){if(te(e)){const i=Vt(e,t,l,n);return i&&As(i)&&i.catch(r=>{ln(r,t,l)}),i}const a=[];for(let i=0;i>>1;Gl(Me[n])ft&&Me.splice(t,1)}function Bd(e){X(e)?dl.push(...e):(!Lt||!Lt.includes(e,e.allowRecurse?Ut+1:Ut))&&dl.push(e),Ws()}function sr(e,t=ql?ft+1:0){for(;tGl(l)-Gl(n)),Ut=0;Ute.id==null?1/0:e.id,Hd=(e,t)=>{const l=Gl(e)-Gl(t);if(l===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return l};function Ks(e){Da=!1,ql=!0,Me.sort(Hd);const t=st;try{for(ft=0;ftae(v)?v.trim():v)),p&&(a=l.map(Gu))}let o,c=n[o=ca(t)]||n[o=ca(tt(t))];!c&&i&&(c=n[o=ca(wl(t))]),c&&Ze(c,e,6,a);const u=n[o+"Once"];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[o])return;e.emitted[o]=!0,Ze(u,e,6,a)}}function Js(e,t,l=!1){const n=t.emitsCache,a=n.get(e);if(a!==void 0)return a;const i=e.emits;let r={},o=!1;if(!te(e)){const c=u=>{const d=Js(u,t,!0);d&&(o=!0,Oe(r,d))};!l&&t.mixins.length&&t.mixins.forEach(c),e.extends&&c(e.extends),e.mixins&&e.mixins.forEach(c)}return!i&&!o?(Te(e)&&n.set(e,null),null):(X(i)?i.forEach(c=>r[c]=null):Oe(r,i),Te(e)&&n.set(e,r),r)}function Jn(e,t){return!e||!Zl(t)?!1:(t=t.slice(2).replace(/Once$/,""),oe(e,t[0].toLowerCase()+t.slice(1))||oe(e,wl(t))||oe(e,t))}let Xe=null,Qs=null;function Vn(e){const t=Xe;return Xe=e,Qs=e&&e.type.__scopeId||null,t}function qd(e,t=Xe,l){if(!t||e._n)return e;const n=(...a)=>{n._d&&br(-1);const i=Vn(t);let r;try{r=e(...a)}finally{Vn(i),n._d&&br(1)}return r};return n._n=!0,n._c=!0,n._d=!0,n}function da(e){const{type:t,vnode:l,proxy:n,withProxy:a,props:i,propsOptions:[r],slots:o,attrs:c,emit:u,render:d,renderCache:p,data:h,setupState:v,ctx:b,inheritAttrs:w}=e;let L,m;const _=Vn(e);try{if(l.shapeFlag&4){const R=a||n;L=nt(d.call(R,R,p,i,v,h,b)),m=c}else{const R=t;L=nt(R.length>1?R(i,{attrs:c,slots:o,emit:u}):R(i,null)),m=t.props?c:Gd(c)}}catch(R){Nl.length=0,ln(R,e,1),L=Ae(et)}let I=L;if(m&&w!==!1){const R=Object.keys(m),{shapeFlag:B}=I;R.length&&B&7&&(r&&R.some(ti)&&(m=Ud(m,r)),I=jt(I,m))}return l.dirs&&(I=jt(I),I.dirs=I.dirs?I.dirs.concat(l.dirs):l.dirs),l.transition&&(I.transition=l.transition),L=I,Vn(_),L}const Gd=e=>{let t;for(const l in e)(l==="class"||l==="style"||Zl(l))&&((t||(t={}))[l]=e[l]);return t},Ud=(e,t)=>{const l={};for(const n in e)(!ti(n)||!(n.slice(9)in t))&&(l[n]=e[n]);return l};function Wd(e,t,l){const{props:n,children:a,component:i}=e,{props:r,children:o,patchFlag:c}=t,u=i.emitsOptions;if(t.dirs||t.transition)return!0;if(l&&c>=0){if(c&1024)return!0;if(c&16)return n?or(n,r,u):!!r;if(c&8){const d=t.dynamicProps;for(let p=0;pe.__isSuspense;function Ys(e,t){t&&t.pendingBranch?X(e)?t.effects.push(...e):t.effects.push(e):Bd(e)}function Xs(e,t){return mi(e,null,t)}const En={};function ce(e,t,l){return mi(e,t,l)}function mi(e,t,{immediate:l,deep:n,flush:a,onTrack:i,onTrigger:r}=Le){var o;const c=Ps()===((o=Re)==null?void 0:o.scope)?Re:null;let u,d=!1,p=!1;if(Ie(e)?(u=()=>e.value,d=Mn(e)):ul(e)?(u=()=>e,n=!0):X(e)?(p=!0,d=e.some(R=>ul(R)||Mn(R)),u=()=>e.map(R=>{if(Ie(R))return R.value;if(ul(R))return sl(R);if(te(R))return Vt(R,c,2)})):te(e)?t?u=()=>Vt(e,c,2):u=()=>{if(!(c&&c.isUnmounted))return h&&h(),Ze(e,c,3,[v])}:u=st,t&&n){const R=u;u=()=>sl(R())}let h,v=R=>{h=_.onStop=()=>{Vt(R,c,4)}},b;if(ml)if(v=st,t?l&&Ze(t,c,3,[u(),p?[]:void 0,v]):u(),a==="sync"){const R=qp();b=R.__watcherHandles||(R.__watcherHandles=[])}else return st;let w=p?new Array(e.length).fill(En):En;const L=()=>{if(_.active)if(t){const R=_.run();(n||d||(p?R.some((B,C)=>Hl(B,w[C])):Hl(R,w)))&&(h&&h(),Ze(t,c,3,[R,w===En?void 0:p&&w[0]===En?[]:w,v]),w=R)}else _.run()};L.allowRecurse=!!t;let m;a==="sync"?m=L:a==="post"?m=()=>Be(L,c&&c.suspense):(L.pre=!0,c&&(L.id=c.uid),m=()=>Kn(L));const _=new oi(u,m);t?l?L():w=_.run():a==="post"?Be(_.run.bind(_),c&&c.suspense):_.run();const I=()=>{_.stop(),c&&c.scope&&li(c.scope.effects,_)};return b&&b.push(I),I}function Qd(e,t,l){const n=this.proxy,a=ae(e)?e.includes(".")?Zs(n,e):()=>n[e]:e.bind(n,n);let i;te(t)?i=t:(i=t.handler,l=t);const r=Re;gl(this);const o=mi(a,i.bind(n),l);return r?gl(r):Qt(),o}function Zs(e,t){const l=t.split(".");return()=>{let n=e;for(let a=0;a{sl(l,t)});else if(Hu(e))for(const l in e)sl(e[l],t);return e}function vt(e,t,l,n){const a=e.dirs,i=t&&t.dirs;for(let r=0;r{e.isMounted=!0}),Yn(()=>{e.isUnmounting=!0}),e}const Qe=[Function,Array],to={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Qe,onEnter:Qe,onAfterEnter:Qe,onEnterCancelled:Qe,onBeforeLeave:Qe,onLeave:Qe,onAfterLeave:Qe,onLeaveCancelled:Qe,onBeforeAppear:Qe,onAppear:Qe,onAfterAppear:Qe,onAppearCancelled:Qe},Yd={name:"BaseTransition",props:to,setup(e,{slots:t}){const l=Xt(),n=eo();let a;return()=>{const i=t.default&&yi(t.default(),!0);if(!i||!i.length)return;let r=i[0];if(i.length>1){for(const w of i)if(w.type!==et){r=w;break}}const o=re(e),{mode:c}=o;if(n.isLeaving)return pa(r);const u=cr(r);if(!u)return pa(r);const d=Ul(u,o,n,l);Wl(u,d);const p=l.subTree,h=p&&cr(p);let v=!1;const{getTransitionKey:b}=u.type;if(b){const w=b();a===void 0?a=w:w!==a&&(a=w,v=!0)}if(h&&h.type!==et&&(!Wt(u,h)||v)){const w=Ul(h,o,n,l);if(Wl(h,w),c==="out-in")return n.isLeaving=!0,w.afterLeave=()=>{n.isLeaving=!1,l.update.active!==!1&&l.update()},pa(r);c==="in-out"&&u.type!==et&&(w.delayLeave=(L,m,_)=>{const I=lo(n,h);I[String(h.key)]=h,L._leaveCb=()=>{m(),L._leaveCb=void 0,delete d.delayedLeave},d.delayedLeave=_})}return r}}},Xd=Yd;function lo(e,t){const{leavingVNodes:l}=e;let n=l.get(t.type);return n||(n=Object.create(null),l.set(t.type,n)),n}function Ul(e,t,l,n){const{appear:a,mode:i,persisted:r=!1,onBeforeEnter:o,onEnter:c,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:p,onLeave:h,onAfterLeave:v,onLeaveCancelled:b,onBeforeAppear:w,onAppear:L,onAfterAppear:m,onAppearCancelled:_}=t,I=String(e.key),R=lo(l,e),B=(T,q)=>{T&&Ze(T,n,9,q)},C=(T,q)=>{const Y=q[1];B(T,q),X(T)?T.every(ne=>ne.length<=1)&&Y():T.length<=1&&Y()},V={mode:i,persisted:r,beforeEnter(T){let q=o;if(!l.isMounted)if(a)q=w||o;else return;T._leaveCb&&T._leaveCb(!0);const Y=R[I];Y&&Wt(e,Y)&&Y.el._leaveCb&&Y.el._leaveCb(),B(q,[T])},enter(T){let q=c,Y=u,ne=d;if(!l.isMounted)if(a)q=L||c,Y=m||u,ne=_||d;else return;let z=!1;const ee=T._enterCb=U=>{z||(z=!0,U?B(ne,[T]):B(Y,[T]),V.delayedLeave&&V.delayedLeave(),T._enterCb=void 0)};q?C(q,[T,ee]):ee()},leave(T,q){const Y=String(e.key);if(T._enterCb&&T._enterCb(!0),l.isUnmounting)return q();B(p,[T]);let ne=!1;const z=T._leaveCb=ee=>{ne||(ne=!0,q(),ee?B(b,[T]):B(v,[T]),T._leaveCb=void 0,R[Y]===e&&delete R[Y])};R[Y]=e,h?C(h,[T,z]):z()},clone(T){return Ul(T,t,l,n)}};return V}function pa(e){if(nn(e))return e=jt(e),e.children=null,e}function cr(e){return nn(e)?e.children?e.children[0]:void 0:e}function Wl(e,t){e.shapeFlag&6&&e.component?Wl(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function yi(e,t=!1,l){let n=[],a=0;for(let i=0;i1)for(let i=0;iOe({name:e.name},t,{setup:e}))():e}const $l=e=>!!e.type.__asyncLoader;function A(e){te(e)&&(e={loader:e});const{loader:t,loadingComponent:l,errorComponent:n,delay:a=200,timeout:i,suspensible:r=!0,onError:o}=e;let c=null,u,d=0;const p=()=>(d++,c=null,h()),h=()=>{let v;return c||(v=c=t().catch(b=>{if(b=b instanceof Error?b:new Error(String(b)),o)return new Promise((w,L)=>{o(b,()=>w(p()),()=>L(b),d+1)});throw b}).then(b=>v!==c&&c?c:(b&&(b.__esModule||b[Symbol.toStringTag]==="Module")&&(b=b.default),u=b,b)))};return $({name:"AsyncComponentWrapper",__asyncLoader:h,get __asyncResolved(){return u},setup(){const v=Re;if(u)return()=>ha(u,v);const b=_=>{c=null,ln(_,v,13,!n)};if(r&&v.suspense||ml)return h().then(_=>()=>ha(_,v)).catch(_=>(b(_),()=>n?Ae(n,{error:_}):null));const w=W(!1),L=W(),m=W(!!a);return a&&setTimeout(()=>{m.value=!1},a),i!=null&&setTimeout(()=>{if(!w.value&&!L.value){const _=new Error(`Async component timed out after ${i}ms.`);b(_),L.value=_}},i),h().then(()=>{w.value=!0,v.parent&&nn(v.parent.vnode)&&Kn(v.parent.update)}).catch(_=>{b(_),L.value=_}),()=>{if(w.value&&u)return ha(u,v);if(L.value&&n)return Ae(n,{error:L.value});if(l&&!m.value)return Ae(l)}}})}function ha(e,t){const{ref:l,props:n,children:a,ce:i}=t.vnode,r=Ae(e,n,a);return r.ref=l,r.ce=i,delete t.vnode.ce,r}const nn=e=>e.type.__isKeepAlive;function Zd(e,t){no(e,"a",t)}function ep(e,t){no(e,"da",t)}function no(e,t,l=Re){const n=e.__wdc||(e.__wdc=()=>{let a=l;for(;a;){if(a.isDeactivated)return;a=a.parent}return e()});if(Qn(t,n,l),l){let a=l.parent;for(;a&&a.parent;)nn(a.parent.vnode)&&tp(n,t,l,a),a=a.parent}}function tp(e,t,l,n){const a=Qn(t,e,n,!0);an(()=>{li(n[t],a)},l)}function Qn(e,t,l=Re,n=!1){if(l){const a=l[e]||(l[e]=[]),i=t.__weh||(t.__weh=(...r)=>{if(l.isUnmounted)return;El(),gl(l);const o=Ze(t,l,e,r);return Qt(),Ll(),o});return n?a.unshift(i):a.push(i),i}}const At=e=>(t,l=Re)=>(!ml||e==="sp")&&Qn(e,(...n)=>t(...n),l),lp=At("bm"),ke=At("m"),np=At("bu"),ao=At("u"),Yn=At("bum"),an=At("um"),ap=At("sp"),ip=At("rtg"),rp=At("rtc");function sp(e,t=Re){Qn("ec",e,t)}const io="components";function Je(e,t){return cp(io,e,!0,t)||e}const op=Symbol.for("v-ndc");function cp(e,t,l=!0,n=!1){const a=Xe||Re;if(a){const i=a.type;if(e===io){const o=Bp(i,!1);if(o&&(o===t||o===tt(t)||o===en(tt(t))))return i}const r=ur(a[e]||i[e],t)||ur(a.appContext[e],t);return!r&&n?i:r}}function ur(e,t){return e&&(e[t]||e[tt(t)]||e[en(tt(t))])}const Ma=e=>e?bo(e)?Ei(e)||e.proxy:Ma(e.parent):null,Vl=Oe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Ma(e.parent),$root:e=>Ma(e.root),$emit:e=>e.emit,$options:e=>bi(e),$forceUpdate:e=>e.f||(e.f=()=>Kn(e.update)),$nextTick:e=>e.n||(e.n=Tl.bind(e.proxy)),$watch:e=>Qd.bind(e)}),va=(e,t)=>e!==Le&&!e.__isScriptSetup&&oe(e,t),up={get({_:e},t){const{ctx:l,setupState:n,data:a,props:i,accessCache:r,type:o,appContext:c}=e;let u;if(t[0]!=="$"){const v=r[t];if(v!==void 0)switch(v){case 1:return n[t];case 2:return a[t];case 4:return l[t];case 3:return i[t]}else{if(va(n,t))return r[t]=1,n[t];if(a!==Le&&oe(a,t))return r[t]=2,a[t];if((u=e.propsOptions[0])&&oe(u,t))return r[t]=3,i[t];if(l!==Le&&oe(l,t))return r[t]=4,l[t];$a&&(r[t]=0)}}const d=Vl[t];let p,h;if(d)return t==="$attrs"&&qe(e,"get",t),d(e);if((p=o.__cssModules)&&(p=p[t]))return p;if(l!==Le&&oe(l,t))return r[t]=4,l[t];if(h=c.config.globalProperties,oe(h,t))return h[t]},set({_:e},t,l){const{data:n,setupState:a,ctx:i}=e;return va(a,t)?(a[t]=l,!0):n!==Le&&oe(n,t)?(n[t]=l,!0):oe(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(i[t]=l,!0)},has({_:{data:e,setupState:t,accessCache:l,ctx:n,appContext:a,propsOptions:i}},r){let o;return!!l[r]||e!==Le&&oe(e,r)||va(t,r)||(o=i[0])&&oe(o,r)||oe(n,r)||oe(Vl,r)||oe(a.config.globalProperties,r)},defineProperty(e,t,l){return l.get!=null?e._.accessCache[t]=0:oe(l,"value")&&this.set(e,t,l.value,null),Reflect.defineProperty(e,t,l)}};function dr(e){return X(e)?e.reduce((t,l)=>(t[l]=null,t),{}):e}let $a=!0;function dp(e){const t=bi(e),l=e.proxy,n=e.ctx;$a=!1,t.beforeCreate&&pr(t.beforeCreate,e,"bc");const{data:a,computed:i,methods:r,watch:o,provide:c,inject:u,created:d,beforeMount:p,mounted:h,beforeUpdate:v,updated:b,activated:w,deactivated:L,beforeDestroy:m,beforeUnmount:_,destroyed:I,unmounted:R,render:B,renderTracked:C,renderTriggered:V,errorCaptured:T,serverPrefetch:q,expose:Y,inheritAttrs:ne,components:z,directives:ee,filters:U}=t;if(u&&pp(u,n,null),r)for(const _e in r){const ve=r[_e];te(ve)&&(n[_e]=ve.bind(l))}if(a){const _e=a.call(l,l);Te(_e)&&(e.data=tn(_e))}if($a=!0,i)for(const _e in i){const ve=i[_e],bt=te(ve)?ve.bind(l,l):te(ve.get)?ve.get.bind(l,l):st,Ot=!te(ve)&&te(ve.set)?ve.set.bind(l):st,pt=E({get:bt,set:Ot});Object.defineProperty(n,_e,{enumerable:!0,configurable:!0,get:()=>pt.value,set:je=>pt.value=je})}if(o)for(const _e in o)ro(o[_e],n,l,_e);if(c){const _e=te(c)?c.call(l):c;Reflect.ownKeys(_e).forEach(ve=>{ot(ve,_e[ve])})}d&&pr(d,e,"c");function he(_e,ve){X(ve)?ve.forEach(bt=>_e(bt.bind(l))):ve&&_e(ve.bind(l))}if(he(lp,p),he(ke,h),he(np,v),he(ao,b),he(Zd,w),he(ep,L),he(sp,T),he(rp,C),he(ip,V),he(Yn,_),he(an,R),he(ap,q),X(Y))if(Y.length){const _e=e.exposed||(e.exposed={});Y.forEach(ve=>{Object.defineProperty(_e,ve,{get:()=>l[ve],set:bt=>l[ve]=bt})})}else e.exposed||(e.exposed={});B&&e.render===st&&(e.render=B),ne!=null&&(e.inheritAttrs=ne),z&&(e.components=z),ee&&(e.directives=ee)}function pp(e,t,l=st){X(e)&&(e=Va(e));for(const n in e){const a=e[n];let i;Te(a)?"default"in a?i=me(a.from||n,a.default,!0):i=me(a.from||n):i=me(a),Ie(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:r=>i.value=r}):t[n]=i}}function pr(e,t,l){Ze(X(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,l)}function ro(e,t,l,n){const a=n.includes(".")?Zs(l,n):()=>l[n];if(ae(e)){const i=t[e];te(i)&&ce(a,i)}else if(te(e))ce(a,e.bind(l));else if(Te(e))if(X(e))e.forEach(i=>ro(i,t,l,n));else{const i=te(e.handler)?e.handler.bind(l):t[e.handler];te(i)&&ce(a,i,e)}}function bi(e){const t=e.type,{mixins:l,extends:n}=t,{mixins:a,optionsCache:i,config:{optionMergeStrategies:r}}=e.appContext,o=i.get(t);let c;return o?c=o:!a.length&&!l&&!n?c=t:(c={},a.length&&a.forEach(u=>Fn(c,u,r,!0)),Fn(c,t,r)),Te(t)&&i.set(t,c),c}function Fn(e,t,l,n=!1){const{mixins:a,extends:i}=t;i&&Fn(e,i,l,!0),a&&a.forEach(r=>Fn(e,r,l,!0));for(const r in t)if(!(n&&r==="expose")){const o=hp[r]||l&&l[r];e[r]=o?o(e[r],t[r]):t[r]}return e}const hp={data:hr,props:vr,emits:vr,methods:Sl,computed:Sl,beforeCreate:Fe,created:Fe,beforeMount:Fe,mounted:Fe,beforeUpdate:Fe,updated:Fe,beforeDestroy:Fe,beforeUnmount:Fe,destroyed:Fe,unmounted:Fe,activated:Fe,deactivated:Fe,errorCaptured:Fe,serverPrefetch:Fe,components:Sl,directives:Sl,watch:fp,provide:hr,inject:vp};function hr(e,t){return t?e?function(){return Oe(te(e)?e.call(this,this):e,te(t)?t.call(this,this):t)}:t:e}function vp(e,t){return Sl(Va(e),Va(t))}function Va(e){if(X(e)){const t={};for(let l=0;l1)return l&&te(t)?t.call(n&&n.proxy):t}}function yp(e,t,l,n=!1){const a={},i={};Sn(i,Xn,1),e.propsDefaults=Object.create(null),oo(e,t,a,i);for(const r in e.propsOptions[0])r in a||(a[r]=void 0);l?e.props=n?a:js(a):e.type.props?e.props=a:e.props=i,e.attrs=i}function bp(e,t,l,n){const{props:a,attrs:i,vnode:{patchFlag:r}}=e,o=re(a),[c]=e.propsOptions;let u=!1;if((n||r>0)&&!(r&16)){if(r&8){const d=e.vnode.dynamicProps;for(let p=0;p{c=!0;const[h,v]=co(p,t,!0);Oe(r,h),v&&o.push(...v)};!l&&t.mixins.length&&t.mixins.forEach(d),e.extends&&d(e.extends),e.mixins&&e.mixins.forEach(d)}if(!i&&!c)return Te(e)&&n.set(e,cl),cl;if(X(i))for(let d=0;d-1,v[1]=w<0||b-1||oe(v,"default"))&&o.push(p)}}}const u=[r,o];return Te(e)&&n.set(e,u),u}function fr(e){return e[0]!=="$"}function gr(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function mr(e,t){return gr(e)===gr(t)}function yr(e,t){return X(t)?t.findIndex(l=>mr(l,e)):te(t)&&mr(t,e)?0:-1}const uo=e=>e[0]==="_"||e==="$stable",_i=e=>X(e)?e.map(nt):[nt(e)],_p=(e,t,l)=>{if(t._n)return t;const n=qd((...a)=>_i(t(...a)),l);return n._c=!1,n},po=(e,t,l)=>{const n=e._ctx;for(const a in e){if(uo(a))continue;const i=e[a];if(te(i))t[a]=_p(a,i,n);else if(i!=null){const r=_i(i);t[a]=()=>r}}},ho=(e,t)=>{const l=_i(t);e.slots.default=()=>l},kp=(e,t)=>{if(e.vnode.shapeFlag&32){const l=t._;l?(e.slots=re(t),Sn(t,"_",l)):po(t,e.slots={})}else e.slots={},t&&ho(e,t);Sn(e.slots,Xn,1)},wp=(e,t,l)=>{const{vnode:n,slots:a}=e;let i=!0,r=Le;if(n.shapeFlag&32){const o=t._;o?l&&o===1?i=!1:(Oe(a,t),!l&&o===1&&delete a._):(i=!t.$stable,po(t,a)),r=t}else t&&(ho(e,t),r={default:1});if(i)for(const o in a)!uo(o)&&!(o in r)&&delete a[o]};function jn(e,t,l,n,a=!1){if(X(e)){e.forEach((h,v)=>jn(h,t&&(X(t)?t[v]:t),l,n,a));return}if($l(n)&&!a)return;const i=n.shapeFlag&4?Ei(n.component)||n.component.proxy:n.el,r=a?null:i,{i:o,r:c}=e,u=t&&t.r,d=o.refs===Le?o.refs={}:o.refs,p=o.setupState;if(u!=null&&u!==c&&(ae(u)?(d[u]=null,oe(p,u)&&(p[u]=null)):Ie(u)&&(u.value=null)),te(c))Vt(c,o,12,[r,d]);else{const h=ae(c),v=Ie(c);if(h||v){const b=()=>{if(e.f){const w=h?oe(p,c)?p[c]:d[c]:c.value;a?X(w)&&li(w,i):X(w)?w.includes(i)||w.push(i):h?(d[c]=[i],oe(p,c)&&(p[c]=d[c])):(c.value=[i],e.k&&(d[e.k]=c.value))}else h?(d[c]=r,oe(p,c)&&(p[c]=r)):v&&(c.value=r,e.k&&(d[e.k]=r))};r?(b.id=-1,Be(b,l)):b()}}}let It=!1;const Ln=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",xn=e=>e.nodeType===8;function Ep(e){const{mt:t,p:l,o:{patchProp:n,createText:a,nextSibling:i,parentNode:r,remove:o,insert:c,createComment:u}}=e,d=(m,_)=>{if(!_.hasChildNodes()){l(null,m,_),$n(),_._vnode=m;return}It=!1,p(_.firstChild,m,null,null,null),$n(),_._vnode=m,It&&console.error("Hydration completed but contains mismatches.")},p=(m,_,I,R,B,C=!1)=>{const V=xn(m)&&m.data==="[",T=()=>w(m,_,I,R,B,V),{type:q,ref:Y,shapeFlag:ne,patchFlag:z}=_;let ee=m.nodeType;_.el=m,z===-2&&(C=!1,_.dynamicChildren=null);let U=null;switch(q){case fl:ee!==3?_.children===""?(c(_.el=a(""),r(m),m),U=m):U=T():(m.data!==_.children&&(It=!0,m.data=_.children),U=i(m));break;case et:ee!==8||V?U=T():U=i(m);break;case Fl:if(V&&(m=i(m),ee=m.nodeType),ee===1||ee===3){U=m;const Se=!_.children.length;for(let he=0;he<_.staticCount;he++)Se&&(_.children+=U.nodeType===1?U.outerHTML:U.data),he===_.staticCount-1&&(_.anchor=U),U=i(U);return V?i(U):U}else T();break;case Ke:V?U=b(m,_,I,R,B,C):U=T();break;default:if(ne&1)ee!==1||_.type.toLowerCase()!==m.tagName.toLowerCase()?U=T():U=h(m,_,I,R,B,C);else if(ne&6){_.slotScopeIds=B;const Se=r(m);if(t(_,Se,null,I,R,Ln(Se),C),U=V?L(m):i(m),U&&xn(U)&&U.data==="teleport end"&&(U=i(U)),$l(_)){let he;V?(he=Ae(Ke),he.anchor=U?U.previousSibling:Se.lastChild):he=m.nodeType===3?yo(""):Ae("div"),he.el=m,_.component.subTree=he}}else ne&64?ee!==8?U=T():U=_.type.hydrate(m,_,I,R,B,C,e,v):ne&128&&(U=_.type.hydrate(m,_,I,R,Ln(r(m)),B,C,e,p))}return Y!=null&&jn(Y,null,R,_),U},h=(m,_,I,R,B,C)=>{C=C||!!_.dynamicChildren;const{type:V,props:T,patchFlag:q,shapeFlag:Y,dirs:ne}=_,z=V==="input"&&ne||V==="option";if(z||q!==-1){if(ne&&vt(_,null,I,"created"),T)if(z||!C||q&48)for(const U in T)(z&&U.endsWith("value")||Zl(U)&&!Ml(U))&&n(m,U,null,T[U],!1,void 0,I);else T.onClick&&n(m,"onClick",null,T.onClick,!1,void 0,I);let ee;if((ee=T&&T.onVnodeBeforeMount)&&Ye(ee,I,_),ne&&vt(_,null,I,"beforeMount"),((ee=T&&T.onVnodeMounted)||ne)&&Ys(()=>{ee&&Ye(ee,I,_),ne&&vt(_,null,I,"mounted")},R),Y&16&&!(T&&(T.innerHTML||T.textContent))){let U=v(m.firstChild,_,m,I,R,B,C);for(;U;){It=!0;const Se=U;U=U.nextSibling,o(Se)}}else Y&8&&m.textContent!==_.children&&(It=!0,m.textContent=_.children)}return m.nextSibling},v=(m,_,I,R,B,C,V)=>{V=V||!!_.dynamicChildren;const T=_.children,q=T.length;for(let Y=0;Y{const{slotScopeIds:V}=_;V&&(B=B?B.concat(V):V);const T=r(m),q=v(i(m),_,T,I,R,B,C);return q&&xn(q)&&q.data==="]"?i(_.anchor=q):(It=!0,c(_.anchor=u("]"),T,q),q)},w=(m,_,I,R,B,C)=>{if(It=!0,_.el=null,C){const q=L(m);for(;;){const Y=i(m);if(Y&&Y!==q)o(Y);else break}}const V=i(m),T=r(m);return o(m),l(null,_,T,V,I,R,Ln(T),B),V},L=m=>{let _=0;for(;m;)if(m=i(m),m&&xn(m)&&(m.data==="["&&_++,m.data==="]")){if(_===0)return i(m);_--}return m};return[d,p]}const Be=Ys;function Lp(e){return xp(e,Ep)}function xp(e,t){const l=Ia();l.__VUE__=!0;const{insert:n,remove:a,patchProp:i,createElement:r,createText:o,createComment:c,setText:u,setElementText:d,parentNode:p,nextSibling:h,setScopeId:v=st,insertStaticContent:b}=e,w=(f,y,k,x=null,P=null,S=null,j=!1,M=null,F=!!y.dynamicChildren)=>{if(f===y)return;f&&!Wt(f,y)&&(x=O(f),je(f,P,S,!0),f=null),y.patchFlag===-2&&(F=!1,y.dynamicChildren=null);const{type:D,ref:J,shapeFlag:G}=y;switch(D){case fl:L(f,y,k,x);break;case et:m(f,y,k,x);break;case Fl:f==null&&_(y,k,x,j);break;case Ke:z(f,y,k,x,P,S,j,M,F);break;default:G&1?B(f,y,k,x,P,S,j,M,F):G&6?ee(f,y,k,x,P,S,j,M,F):(G&64||G&128)&&D.process(f,y,k,x,P,S,j,M,F,N)}J!=null&&P&&jn(J,f&&f.ref,S,y||f,!y)},L=(f,y,k,x)=>{if(f==null)n(y.el=o(y.children),k,x);else{const P=y.el=f.el;y.children!==f.children&&u(P,y.children)}},m=(f,y,k,x)=>{f==null?n(y.el=c(y.children||""),k,x):y.el=f.el},_=(f,y,k,x)=>{[f.el,f.anchor]=b(f.children,y,k,x,f.el,f.anchor)},I=({el:f,anchor:y},k,x)=>{let P;for(;f&&f!==y;)P=h(f),n(f,k,x),f=P;n(y,k,x)},R=({el:f,anchor:y})=>{let k;for(;f&&f!==y;)k=h(f),a(f),f=k;a(y)},B=(f,y,k,x,P,S,j,M,F)=>{j=j||y.type==="svg",f==null?C(y,k,x,P,S,j,M,F):q(f,y,P,S,j,M,F)},C=(f,y,k,x,P,S,j,M)=>{let F,D;const{type:J,props:G,shapeFlag:Q,transition:Z,dirs:le}=f;if(F=f.el=r(f.type,S,G&&G.is,G),Q&8?d(F,f.children):Q&16&&T(f.children,F,null,x,P,S&&J!=="foreignObject",j,M),le&&vt(f,null,x,"created"),V(F,f,f.scopeId,j,x),G){for(const ye in G)ye!=="value"&&!Ml(ye)&&i(F,ye,null,G[ye],S,f.children,x,P,De);"value"in G&&i(F,"value",null,G.value),(D=G.onVnodeBeforeMount)&&Ye(D,x,f)}le&&vt(f,null,x,"beforeMount");const we=(!P||P&&!P.pendingBranch)&&Z&&!Z.persisted;we&&Z.beforeEnter(F),n(F,y,k),((D=G&&G.onVnodeMounted)||we||le)&&Be(()=>{D&&Ye(D,x,f),we&&Z.enter(F),le&&vt(f,null,x,"mounted")},P)},V=(f,y,k,x,P)=>{if(k&&v(f,k),x)for(let S=0;S{for(let D=F;D{const M=y.el=f.el;let{patchFlag:F,dynamicChildren:D,dirs:J}=y;F|=f.patchFlag&16;const G=f.props||Le,Q=y.props||Le;let Z;k&&qt(k,!1),(Z=Q.onVnodeBeforeUpdate)&&Ye(Z,k,y,f),J&&vt(y,f,k,"beforeUpdate"),k&&qt(k,!0);const le=P&&y.type!=="foreignObject";if(D?Y(f.dynamicChildren,D,M,k,x,le,S):j||ve(f,y,M,null,k,x,le,S,!1),F>0){if(F&16)ne(M,y,G,Q,k,x,P);else if(F&2&&G.class!==Q.class&&i(M,"class",null,Q.class,P),F&4&&i(M,"style",G.style,Q.style,P),F&8){const we=y.dynamicProps;for(let ye=0;ye{Z&&Ye(Z,k,y,f),J&&vt(y,f,k,"updated")},x)},Y=(f,y,k,x,P,S,j)=>{for(let M=0;M{if(k!==x){if(k!==Le)for(const M in k)!Ml(M)&&!(M in x)&&i(f,M,k[M],null,j,y.children,P,S,De);for(const M in x){if(Ml(M))continue;const F=x[M],D=k[M];F!==D&&M!=="value"&&i(f,M,D,F,j,y.children,P,S,De)}"value"in x&&i(f,"value",k.value,x.value)}},z=(f,y,k,x,P,S,j,M,F)=>{const D=y.el=f?f.el:o(""),J=y.anchor=f?f.anchor:o("");let{patchFlag:G,dynamicChildren:Q,slotScopeIds:Z}=y;Z&&(M=M?M.concat(Z):Z),f==null?(n(D,k,x),n(J,k,x),T(y.children,k,J,P,S,j,M,F)):G>0&&G&64&&Q&&f.dynamicChildren?(Y(f.dynamicChildren,Q,k,P,S,j,M),(y.key!=null||P&&y===P.subTree)&&vo(f,y,!0)):ve(f,y,k,J,P,S,j,M,F)},ee=(f,y,k,x,P,S,j,M,F)=>{y.slotScopeIds=M,f==null?y.shapeFlag&512?P.ctx.activate(y,k,x,j,F):U(y,k,x,P,S,j,F):Se(f,y,F)},U=(f,y,k,x,P,S,j)=>{const M=f.component=$p(f,x,P);if(nn(f)&&(M.ctx.renderer=N),Vp(M),M.asyncDep){if(P&&P.registerDep(M,he),!f.el){const F=M.subTree=Ae(et);m(null,F,y,k)}return}he(M,f,y,k,P,S,j)},Se=(f,y,k)=>{const x=y.component=f.component;if(Wd(f,y,k))if(x.asyncDep&&!x.asyncResolved){_e(x,y,k);return}else x.next=y,jd(x.update),x.update();else y.el=f.el,x.vnode=y},he=(f,y,k,x,P,S,j)=>{const M=()=>{if(f.isMounted){let{next:J,bu:G,u:Q,parent:Z,vnode:le}=f,we=J,ye;qt(f,!1),J?(J.el=le.el,_e(f,J,j)):J=le,G&&ua(G),(ye=J.props&&J.props.onVnodeBeforeUpdate)&&Ye(ye,Z,J,le),qt(f,!0);const Pe=da(f),lt=f.subTree;f.subTree=Pe,w(lt,Pe,p(lt.el),O(lt),f,P,S),J.el=Pe.el,we===null&&Kd(f,Pe.el),Q&&Be(Q,P),(ye=J.props&&J.props.onVnodeUpdated)&&Be(()=>Ye(ye,Z,J,le),P)}else{let J;const{el:G,props:Q}=y,{bm:Z,m:le,parent:we}=f,ye=$l(y);if(qt(f,!1),Z&&ua(Z),!ye&&(J=Q&&Q.onVnodeBeforeMount)&&Ye(J,we,y),qt(f,!0),G&&fe){const Pe=()=>{f.subTree=da(f),fe(G,f.subTree,f,P,null)};ye?y.type.__asyncLoader().then(()=>!f.isUnmounted&&Pe()):Pe()}else{const Pe=f.subTree=da(f);w(null,Pe,k,x,f,P,S),y.el=Pe.el}if(le&&Be(le,P),!ye&&(J=Q&&Q.onVnodeMounted)){const Pe=y;Be(()=>Ye(J,we,Pe),P)}(y.shapeFlag&256||we&&$l(we.vnode)&&we.vnode.shapeFlag&256)&&f.a&&Be(f.a,P),f.isMounted=!0,y=k=x=null}},F=f.effect=new oi(M,()=>Kn(D),f.scope),D=f.update=()=>F.run();D.id=f.uid,qt(f,!0),D()},_e=(f,y,k)=>{y.component=f;const x=f.vnode.props;f.vnode=y,f.next=null,bp(f,y.props,x,k),wp(f,y.children,k),El(),sr(),Ll()},ve=(f,y,k,x,P,S,j,M,F=!1)=>{const D=f&&f.children,J=f?f.shapeFlag:0,G=y.children,{patchFlag:Q,shapeFlag:Z}=y;if(Q>0){if(Q&128){Ot(D,G,k,x,P,S,j,M,F);return}else if(Q&256){bt(D,G,k,x,P,S,j,M,F);return}}Z&8?(J&16&&De(D,P,S),G!==D&&d(k,G)):J&16?Z&16?Ot(D,G,k,x,P,S,j,M,F):De(D,P,S,!0):(J&8&&d(k,""),Z&16&&T(G,k,x,P,S,j,M,F))},bt=(f,y,k,x,P,S,j,M,F)=>{f=f||cl,y=y||cl;const D=f.length,J=y.length,G=Math.min(D,J);let Q;for(Q=0;QJ?De(f,P,S,!0,!1,G):T(y,k,x,P,S,j,M,F,G)},Ot=(f,y,k,x,P,S,j,M,F)=>{let D=0;const J=y.length;let G=f.length-1,Q=J-1;for(;D<=G&&D<=Q;){const Z=f[D],le=y[D]=F?St(y[D]):nt(y[D]);if(Wt(Z,le))w(Z,le,k,null,P,S,j,M,F);else break;D++}for(;D<=G&&D<=Q;){const Z=f[G],le=y[Q]=F?St(y[Q]):nt(y[Q]);if(Wt(Z,le))w(Z,le,k,null,P,S,j,M,F);else break;G--,Q--}if(D>G){if(D<=Q){const Z=Q+1,le=ZQ)for(;D<=G;)je(f[D],P,S,!0),D++;else{const Z=D,le=D,we=new Map;for(D=le;D<=Q;D++){const Ue=y[D]=F?St(y[D]):nt(y[D]);Ue.key!=null&&we.set(Ue.key,D)}let ye,Pe=0;const lt=Q-le+1;let nl=!1,Ji=0;const Pl=new Array(lt);for(D=0;D=lt){je(Ue,P,S,!0);continue}let ht;if(Ue.key!=null)ht=we.get(Ue.key);else for(ye=le;ye<=Q;ye++)if(Pl[ye-le]===0&&Wt(Ue,y[ye])){ht=ye;break}ht===void 0?je(Ue,P,S,!0):(Pl[ht-le]=D+1,ht>=Ji?Ji=ht:nl=!0,w(Ue,y[ht],k,null,P,S,j,M,F),Pe++)}const Qi=nl?Tp(Pl):cl;for(ye=Qi.length-1,D=lt-1;D>=0;D--){const Ue=le+D,ht=y[Ue],Yi=Ue+1{const{el:S,type:j,transition:M,children:F,shapeFlag:D}=f;if(D&6){pt(f.component.subTree,y,k,x);return}if(D&128){f.suspense.move(y,k,x);return}if(D&64){j.move(f,y,k,N);return}if(j===Ke){n(S,y,k);for(let G=0;GM.enter(S),P);else{const{leave:G,delayLeave:Q,afterLeave:Z}=M,le=()=>n(S,y,k),we=()=>{G(S,()=>{le(),Z&&Z()})};Q?Q(S,le,we):we()}else n(S,y,k)},je=(f,y,k,x=!1,P=!1)=>{const{type:S,props:j,ref:M,children:F,dynamicChildren:D,shapeFlag:J,patchFlag:G,dirs:Q}=f;if(M!=null&&jn(M,null,k,f,!0),J&256){y.ctx.deactivate(f);return}const Z=J&1&&Q,le=!$l(f);let we;if(le&&(we=j&&j.onVnodeBeforeUnmount)&&Ye(we,y,f),J&6)mn(f.component,k,x);else{if(J&128){f.suspense.unmount(k,x);return}Z&&vt(f,null,y,"beforeUnmount"),J&64?f.type.remove(f,y,k,P,N,x):D&&(S!==Ke||G>0&&G&64)?De(D,y,k,!1,!0):(S===Ke&&G&384||!P&&J&16)&&De(F,y,k),x&&tl(f)}(le&&(we=j&&j.onVnodeUnmounted)||Z)&&Be(()=>{we&&Ye(we,y,f),Z&&vt(f,null,y,"unmounted")},k)},tl=f=>{const{type:y,el:k,anchor:x,transition:P}=f;if(y===Ke){ll(k,x);return}if(y===Fl){R(f);return}const S=()=>{a(k),P&&!P.persisted&&P.afterLeave&&P.afterLeave()};if(f.shapeFlag&1&&P&&!P.persisted){const{leave:j,delayLeave:M}=P,F=()=>j(k,S);M?M(f.el,S,F):F()}else S()},ll=(f,y)=>{let k;for(;f!==y;)k=h(f),a(f),f=k;a(y)},mn=(f,y,k)=>{const{bum:x,scope:P,update:S,subTree:j,um:M}=f;x&&ua(x),P.stop(),S&&(S.active=!1,je(j,f,y,k)),M&&Be(M,y),Be(()=>{f.isUnmounted=!0},y),y&&y.pendingBranch&&!y.isUnmounted&&f.asyncDep&&!f.asyncResolved&&f.suspenseId===y.pendingId&&(y.deps--,y.deps===0&&y.resolve())},De=(f,y,k,x=!1,P=!1,S=0)=>{for(let j=S;jf.shapeFlag&6?O(f.component.subTree):f.shapeFlag&128?f.suspense.next():h(f.anchor||f.el),H=(f,y,k)=>{f==null?y._vnode&&je(y._vnode,null,null,!0):w(y._vnode||null,f,y,null,null,null,k),sr(),$n(),y._vnode=f},N={p:w,um:je,m:pt,r:tl,mt:U,mc:T,pc:ve,pbc:Y,n:O,o:e};let K,fe;return t&&([K,fe]=t(N)),{render:H,hydrate:K,createApp:mp(H,K)}}function qt({effect:e,update:t},l){e.allowRecurse=t.allowRecurse=l}function vo(e,t,l=!1){const n=e.children,a=t.children;if(X(n)&&X(a))for(let i=0;i>1,e[l[o]]0&&(t[n]=l[i-1]),l[i]=n)}}for(i=l.length,r=l[i-1];i-- >0;)l[i]=r,r=t[r];return l}const Ap=e=>e.__isTeleport,Ke=Symbol.for("v-fgt"),fl=Symbol.for("v-txt"),et=Symbol.for("v-cmt"),Fl=Symbol.for("v-stc"),Nl=[];let rt=null;function Op(e=!1){Nl.push(rt=e?null:[])}function Pp(){Nl.pop(),rt=Nl[Nl.length-1]||null}let Kl=1;function br(e){Kl+=e}function fo(e){return e.dynamicChildren=Kl>0?rt||cl:null,Pp(),Kl>0&&rt&&rt.push(e),e}function ig(e,t,l,n,a,i){return fo(mo(e,t,l,n,a,i,!0))}function Ip(e,t,l,n,a){return fo(Ae(e,t,l,n,a,!0))}function Na(e){return e?e.__v_isVNode===!0:!1}function Wt(e,t){return e.type===t.type&&e.key===t.key}const Xn="__vInternal",go=({key:e})=>e??null,Cn=({ref:e,ref_key:t,ref_for:l})=>(typeof e=="number"&&(e=""+e),e!=null?ae(e)||Ie(e)||te(e)?{i:Xe,r:e,k:t,f:!!l}:e:null);function mo(e,t=null,l=null,n=0,a=null,i=e===Ke?0:1,r=!1,o=!1){const c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&go(t),ref:t&&Cn(t),scopeId:Qs,slotScopeIds:null,children:l,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:i,patchFlag:n,dynamicProps:a,dynamicChildren:null,appContext:null,ctx:Xe};return o?(ki(c,l),i&128&&e.normalize(c)):l&&(c.shapeFlag|=ae(l)?8:16),Kl>0&&!r&&rt&&(c.patchFlag>0||i&6)&&c.patchFlag!==32&&rt.push(c),c}const Ae=Rp;function Rp(e,t=null,l=null,n=0,a=null,i=!1){if((!e||e===op)&&(e=et),Na(e)){const o=jt(e,t,!0);return l&&ki(o,l),Kl>0&&!i&&rt&&(o.shapeFlag&6?rt[rt.indexOf(e)]=o:rt.push(o)),o.patchFlag|=-2,o}if(Hp(e)&&(e=e.__vccOpts),t){t=Cp(t);let{class:o,style:c}=t;o&&!ae(o)&&(t.class=ri(o)),Te(c)&&(Bs(c)&&!X(c)&&(c=Oe({},c)),t.style=ii(c))}const r=ae(e)?1:Jd(e)?128:Ap(e)?64:Te(e)?4:te(e)?2:0;return mo(e,t,l,n,a,r,i,!0)}function Cp(e){return e?Bs(e)||Xn in e?Oe({},e):e:null}function jt(e,t,l=!1){const{props:n,ref:a,patchFlag:i,children:r}=e,o=t?Sp(n||{},t):n;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:o,key:o&&go(o),ref:t&&t.ref?l&&a?X(a)?a.concat(Cn(t)):[a,Cn(t)]:Cn(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:r,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Ke?i===-1?16:i|16:i,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&jt(e.ssContent),ssFallback:e.ssFallback&&jt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function yo(e=" ",t=0){return Ae(fl,null,e,t)}function rg(e,t){const l=Ae(Fl,null,e);return l.staticCount=t,l}function sg(e="",t=!1){return t?(Op(),Ip(et,null,e)):Ae(et,null,e)}function nt(e){return e==null||typeof e=="boolean"?Ae(et):X(e)?Ae(Ke,null,e.slice()):typeof e=="object"?St(e):Ae(fl,null,String(e))}function St(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:jt(e)}function ki(e,t){let l=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(X(t))l=16;else if(typeof t=="object")if(n&65){const a=t.default;a&&(a._c&&(a._d=!1),ki(e,a()),a._c&&(a._d=!0));return}else{l=32;const a=t._;!a&&!(Xn in t)?t._ctx=Xe:a===3&&Xe&&(Xe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else te(t)?(t={default:t,_ctx:Xe},l=32):(t=String(t),n&64?(l=16,t=[yo(t)]):l=8);e.children=t,e.shapeFlag|=l}function Sp(...e){const t={};for(let l=0;lRe||Xe;let wi,al,_r="__VUE_INSTANCE_SETTERS__";(al=Ia()[_r])||(al=Ia()[_r]=[]),al.push(e=>Re=e),wi=e=>{al.length>1?al.forEach(t=>t(e)):al[0](e)};const gl=e=>{wi(e),e.scope.on()},Qt=()=>{Re&&Re.scope.off(),wi(null)};function bo(e){return e.vnode.shapeFlag&4}let ml=!1;function Vp(e,t=!1){ml=t;const{props:l,children:n}=e.vnode,a=bo(e);yp(e,l,a,t),kp(e,n);const i=a?Fp(e,t):void 0;return ml=!1,i}function Fp(e,t){const l=e.type;e.accessCache=Object.create(null),e.proxy=Hs(new Proxy(e.ctx,up));const{setup:n}=l;if(n){const a=e.setupContext=n.length>1?jp(e):null;gl(e),El();const i=Vt(n,e,0,[e.props,a]);if(Ll(),Qt(),As(i)){if(i.then(Qt,Qt),t)return i.then(r=>{kr(e,r,t)}).catch(r=>{ln(r,e,0)});e.asyncDep=i}else kr(e,i,t)}else _o(e,t)}function kr(e,t,l){te(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Te(t)&&(e.setupState=qs(t)),_o(e,l)}let wr;function _o(e,t,l){const n=e.type;if(!e.render){if(!t&&wr&&!n.render){const a=n.template||bi(e).template;if(a){const{isCustomElement:i,compilerOptions:r}=e.appContext.config,{delimiters:o,compilerOptions:c}=n,u=Oe(Oe({isCustomElement:i,delimiters:o},r),c);n.render=wr(a,u)}}e.render=n.render||st}gl(e),El(),dp(e),Ll(),Qt()}function Np(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,l){return qe(e,"get","$attrs"),t[l]}}))}function jp(e){const t=l=>{e.exposed=l||{}};return{get attrs(){return Np(e)},slots:e.slots,emit:e.emit,expose:t}}function Ei(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(qs(Hs(e.exposed)),{get(t,l){if(l in t)return t[l];if(l in Vl)return Vl[l](e)},has(t,l){return l in t||l in Vl}}))}function Bp(e,t=!0){return te(e)?e.displayName||e.name:e.name||t&&e.__name}function Hp(e){return te(e)&&"__vccOpts"in e}const E=(e,t)=>Vd(e,t,ml);function s(e,t,l){const n=arguments.length;return n===2?Te(t)&&!X(t)?Na(t)?Ae(e,null,[t]):Ae(e,t):Ae(e,null,t):(n>3?l=Array.prototype.slice.call(arguments,2):n===3&&Na(l)&&(l=[l]),Ae(e,t,l))}const zp=Symbol.for("v-scx"),qp=()=>me(zp),Gp="3.3.4",Up="http://www.w3.org/2000/svg",Kt=typeof document<"u"?document:null,Er=Kt&&Kt.createElement("template"),Wp={insert:(e,t,l)=>{t.insertBefore(e,l||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,l,n)=>{const a=t?Kt.createElementNS(Up,e):Kt.createElement(e,l?{is:l}:void 0);return e==="select"&&n&&n.multiple!=null&&a.setAttribute("multiple",n.multiple),a},createText:e=>Kt.createTextNode(e),createComment:e=>Kt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Kt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,l,n,a,i){const r=l?l.previousSibling:t.lastChild;if(a&&(a===i||a.nextSibling))for(;t.insertBefore(a.cloneNode(!0),l),!(a===i||!(a=a.nextSibling)););else{Er.innerHTML=n?`${e}`:e;const o=Er.content;if(n){const c=o.firstChild;for(;c.firstChild;)o.appendChild(c.firstChild);o.removeChild(c)}t.insertBefore(o,l)}return[r?r.nextSibling:t.firstChild,l?l.previousSibling:t.lastChild]}};function Kp(e,t,l){const n=e._vtc;n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):l?e.setAttribute("class",t):e.className=t}function Jp(e,t,l){const n=e.style,a=ae(l);if(l&&!a){if(t&&!ae(t))for(const i in t)l[i]==null&&ja(n,i,"");for(const i in l)ja(n,i,l[i])}else{const i=n.display;a?t!==l&&(n.cssText=l):t&&e.removeAttribute("style"),"_vod"in e&&(n.display=i)}}const Lr=/\s*!important$/;function ja(e,t,l){if(X(l))l.forEach(n=>ja(e,t,n));else if(l==null&&(l=""),t.startsWith("--"))e.setProperty(t,l);else{const n=Qp(e,t);Lr.test(l)?e.setProperty(wl(n),l.replace(Lr,""),"important"):e[n]=l}}const xr=["Webkit","Moz","ms"],fa={};function Qp(e,t){const l=fa[t];if(l)return l;let n=tt(t);if(n!=="filter"&&n in e)return fa[t]=n;n=en(n);for(let a=0;aga||(n0.then(()=>ga=0),ga=Date.now());function i0(e,t){const l=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=l.attached)return;Ze(r0(n,l.value),t,5,[n])};return l.value=e,l.attached=a0(),l}function r0(e,t){if(X(t)){const l=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{l.call(e),e._stopped=!0},t.map(n=>a=>!a._stopped&&n&&n(a))}else return t}const Or=/^on[a-z]/,s0=(e,t,l,n,a=!1,i,r,o,c)=>{t==="class"?Kp(e,n,a):t==="style"?Jp(e,l,n):Zl(t)?ti(t)||t0(e,t,l,n,r):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):o0(e,t,n,a))?Xp(e,t,n,i,r,o,c):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Yp(e,t,n,a))};function o0(e,t,l,n){return n?!!(t==="innerHTML"||t==="textContent"||t in e&&Or.test(t)&&te(l)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Or.test(t)&&ae(l)?!1:t in e}const Rt="transition",Il="animation",Bt=(e,{slots:t})=>s(Xd,wo(e),t);Bt.displayName="Transition";const ko={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},c0=Bt.props=Oe({},to,ko),Gt=(e,t=[])=>{X(e)?e.forEach(l=>l(...t)):e&&e(...t)},Pr=e=>e?X(e)?e.some(t=>t.length>1):e.length>1:!1;function wo(e){const t={};for(const z in e)z in ko||(t[z]=e[z]);if(e.css===!1)return t;const{name:l="v",type:n,duration:a,enterFromClass:i=`${l}-enter-from`,enterActiveClass:r=`${l}-enter-active`,enterToClass:o=`${l}-enter-to`,appearFromClass:c=i,appearActiveClass:u=r,appearToClass:d=o,leaveFromClass:p=`${l}-leave-from`,leaveActiveClass:h=`${l}-leave-active`,leaveToClass:v=`${l}-leave-to`}=e,b=u0(a),w=b&&b[0],L=b&&b[1],{onBeforeEnter:m,onEnter:_,onEnterCancelled:I,onLeave:R,onLeaveCancelled:B,onBeforeAppear:C=m,onAppear:V=_,onAppearCancelled:T=I}=t,q=(z,ee,U)=>{Ct(z,ee?d:o),Ct(z,ee?u:r),U&&U()},Y=(z,ee)=>{z._isLeaving=!1,Ct(z,p),Ct(z,v),Ct(z,h),ee&&ee()},ne=z=>(ee,U)=>{const Se=z?V:_,he=()=>q(ee,z,U);Gt(Se,[ee,he]),Ir(()=>{Ct(ee,z?c:i),kt(ee,z?d:o),Pr(Se)||Rr(ee,n,w,he)})};return Oe(t,{onBeforeEnter(z){Gt(m,[z]),kt(z,i),kt(z,r)},onBeforeAppear(z){Gt(C,[z]),kt(z,c),kt(z,u)},onEnter:ne(!1),onAppear:ne(!0),onLeave(z,ee){z._isLeaving=!0;const U=()=>Y(z,ee);kt(z,p),Lo(),kt(z,h),Ir(()=>{z._isLeaving&&(Ct(z,p),kt(z,v),Pr(R)||Rr(z,n,L,U))}),Gt(R,[z,U])},onEnterCancelled(z){q(z,!1),Gt(I,[z])},onAppearCancelled(z){q(z,!0),Gt(T,[z])},onLeaveCancelled(z){Y(z),Gt(B,[z])}})}function u0(e){if(e==null)return null;if(Te(e))return[ma(e.enter),ma(e.leave)];{const t=ma(e);return[t,t]}}function ma(e){return Uu(e)}function kt(e,t){t.split(/\s+/).forEach(l=>l&&e.classList.add(l)),(e._vtc||(e._vtc=new Set)).add(t)}function Ct(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const{_vtc:l}=e;l&&(l.delete(t),l.size||(e._vtc=void 0))}function Ir(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let d0=0;function Rr(e,t,l,n){const a=e._endId=++d0,i=()=>{a===e._endId&&n()};if(l)return setTimeout(i,l);const{type:r,timeout:o,propCount:c}=Eo(e,t);if(!r)return n();const u=r+"end";let d=0;const p=()=>{e.removeEventListener(u,h),i()},h=v=>{v.target===e&&++d>=c&&p()};setTimeout(()=>{d(l[b]||"").split(", "),a=n(`${Rt}Delay`),i=n(`${Rt}Duration`),r=Cr(a,i),o=n(`${Il}Delay`),c=n(`${Il}Duration`),u=Cr(o,c);let d=null,p=0,h=0;t===Rt?r>0&&(d=Rt,p=r,h=i.length):t===Il?u>0&&(d=Il,p=u,h=c.length):(p=Math.max(r,u),d=p>0?r>u?Rt:Il:null,h=d?d===Rt?i.length:c.length:0);const v=d===Rt&&/\b(transform|all)(,|$)/.test(n(`${Rt}Property`).toString());return{type:d,timeout:p,propCount:h,hasTransform:v}}function Cr(e,t){for(;e.lengthSr(l)+Sr(e[n])))}function Sr(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function Lo(){return document.body.offsetHeight}const xo=new WeakMap,To=new WeakMap,Ao={name:"TransitionGroup",props:Oe({},c0,{tag:String,moveClass:String}),setup(e,{slots:t}){const l=Xt(),n=eo();let a,i;return ao(()=>{if(!a.length)return;const r=e.moveClass||`${e.name||"v"}-move`;if(!m0(a[0].el,l.vnode.el,r))return;a.forEach(v0),a.forEach(f0);const o=a.filter(g0);Lo(),o.forEach(c=>{const u=c.el,d=u.style;kt(u,r),d.transform=d.webkitTransform=d.transitionDuration="";const p=u._moveCb=h=>{h&&h.target!==u||(!h||/transform$/.test(h.propertyName))&&(u.removeEventListener("transitionend",p),u._moveCb=null,Ct(u,r))};u.addEventListener("transitionend",p)})}),()=>{const r=re(e),o=wo(r);let c=r.tag||Ke;a=i,i=t.default?yi(t.default()):[];for(let u=0;udelete e.mode;Ao.props;const h0=Ao;function v0(e){const t=e.el;t._moveCb&&t._moveCb(),t._enterCb&&t._enterCb()}function f0(e){To.set(e,e.el.getBoundingClientRect())}function g0(e){const t=xo.get(e),l=To.get(e),n=t.left-l.left,a=t.top-l.top;if(n||a){const i=e.el.style;return i.transform=i.webkitTransform=`translate(${n}px,${a}px)`,i.transitionDuration="0s",e}}function m0(e,t,l){const n=e.cloneNode();e._vtc&&e._vtc.forEach(r=>{r.split(/\s+/).forEach(o=>o&&n.classList.remove(o))}),l.split(/\s+/).forEach(r=>r&&n.classList.add(r)),n.style.display="none";const a=t.nodeType===1?t:t.parentNode;a.appendChild(n);const{hasTransform:i}=Eo(n);return a.removeChild(n),i}const y0=Oe({patchProp:s0},Wp);let ya,Dr=!1;function b0(){return ya=Dr?ya:Lp(y0),Dr=!0,ya}const _0=(...e)=>{const t=b0().createApp(...e),{mount:l}=t;return t.mount=n=>{const a=k0(n);if(a)return l(a,!0,a instanceof SVGElement)},t};function k0(e){return ae(e)?document.querySelector(e):e}const w0={"v-8daa1a0e":()=>g(()=>import("./index.html-6200e2b6.js"),[]).then(({data:e})=>e),"v-22a39d25":()=>g(()=>import("./about.html-c47dcdc2.js"),[]).then(({data:e})=>e),"v-3f274907":()=>g(()=>import("./about-arm-things-you-need-to-know.html-a6dfc5b5.js"),[]).then(({data:e})=>e),"v-6614b1a1":()=>g(()=>import("./common-solutions-of-object-storage-for-static-assets.html-35623701.js"),[]).then(({data:e})=>e),"v-2e81d6a4":()=>g(()=>import("./docker-build-and-push-script.html-be9258e7.js"),[]).then(({data:e})=>e),"v-3c0c097a":()=>g(()=>import("./reduce-python-image-size.html-b370c494.js"),[]).then(({data:e})=>e),"v-daf302c6":()=>g(()=>import("./what-is-the-difference-between-sh-and-bash.html-d303f17a.js"),[]).then(({data:e})=>e),"v-30a50b00":()=>g(()=>import("./a-vuepress2-entertaining-video.html-13144ff8.js"),[]).then(({data:e})=>e),"v-0481cc80":()=>g(()=>import("./a-warning-from-navicat.html-32552aa3.js"),[]).then(({data:e})=>e),"v-79e139f8":()=>g(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-c08f282b.js"),[]).then(({data:e})=>e),"v-7a74360a":()=>g(()=>import("./claude-ai-in-action-extract-info-from-html.html-a82639eb.js"),[]).then(({data:e})=>e),"v-76f251d2":()=>g(()=>import("./copy-code-may-not-be-guilty.html-a0687c59.js"),[]).then(({data:e})=>e),"v-f47f129e":()=>g(()=>import("./dont-try-to-argue-with-a-sb.html-37ff3a9d.js"),[]).then(({data:e})=>e),"v-fd7b7f92":()=>g(()=>import("./iteration-retrospective-of-sanyuan.html-03cce066.js"),[]).then(({data:e})=>e),"v-81a71778":()=>g(()=>import("./leverage-ai-to-boost-coding-productivity.html-e400b847.js"),[]).then(({data:e})=>e),"v-5c48d497":()=>g(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-69f54150.js"),[]).then(({data:e})=>e),"v-4f919602":()=>g(()=>import("./testing-environments-should-be-consistent-with-production-environments.html-d9f46229.js"),[]).then(({data:e})=>e),"v-1e305501":()=>g(()=>import("./things-I-have-to-vent-about-vue.html-c83b4f39.js"),[]).then(({data:e})=>e),"v-404740fa":()=>g(()=>import("./use-claude2-instead-of-chatgpt.html-69dff4f8.js"),[]).then(({data:e})=>e),"v-f1efc11c":()=>g(()=>import("./vim-creator-pass-away.html-1636038d.js"),[]).then(({data:e})=>e),"v-bd2b5fe8":()=>g(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-381fd837.js"),[]).then(({data:e})=>e),"v-971cc7fe":()=>g(()=>import("./contemporary-college-english-1.html-051e43a8.js"),[]).then(({data:e})=>e),"v-93b316c0":()=>g(()=>import("./contemporary-college-english-2.html-aa3974a2.js"),[]).then(({data:e})=>e),"v-90496582":()=>g(()=>import("./contemporary-college-english-3.html-5dab2a15.js"),[]).then(({data:e})=>e),"v-8cdfb444":()=>g(()=>import("./contemporary-college-english-4.html-519c519e.js"),[]).then(({data:e})=>e),"v-89760306":()=>g(()=>import("./contemporary-college-english-5.html-3c38137f.js"),[]).then(({data:e})=>e),"v-860c51c8":()=>g(()=>import("./contemporary-college-english-6.html-73582c8d.js"),[]).then(({data:e})=>e),"v-246f4f17":()=>g(()=>import("./everyone-can-learn-english-1-overview.html-5218993d.js"),[]).then(({data:e})=>e),"v-3ac0474c":()=>g(()=>import("./everyone-can-learn-english-2-pronunciation.html-338d9049.js"),[]).then(({data:e})=>e),"v-58a409d7":()=>g(()=>import("./everyone-can-learn-english-3-words.html-04c449ad.js"),[]).then(({data:e})=>e),"v-788d194a":()=>g(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-4832e8c7.js"),[]).then(({data:e})=>e),"v-ae153a4e":()=>g(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-accb74c4.js"),[]).then(({data:e})=>e),"v-d42db13c":()=>g(()=>import("./how-to-self-evaluate-english-level.html-d652d2f6.js"),[]).then(({data:e})=>e),"v-6ed7d996":()=>g(()=>import("./learning-7000-words-task-completed.html-30a87853.js"),[]).then(({data:e})=>e),"v-221efd1f":()=>g(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-ce9a8d3a.js"),[]).then(({data:e})=>e),"v-72e84a92":()=>g(()=>import("./old-articles.html-55425101.js"),[]).then(({data:e})=>e),"v-7320140c":()=>g(()=>import("./performance-optimization-in-action.html-a47a49f3.js"),[]).then(({data:e})=>e),"v-1e5872c4":()=>g(()=>import("./git-best-pratices.html-9eed25ec.js"),[]).then(({data:e})=>e),"v-48e494ef":()=>g(()=>import("./git-definitive-guide-to-merge-code.html-99dd472d.js"),[]).then(({data:e})=>e),"v-60021cbc":()=>g(()=>import("./git-history-two-tricks-in-idea.html-0567d0b5.js"),[]).then(({data:e})=>e),"v-642eaaea":()=>g(()=>import("./git-useful-commands.html-4156d3f3.js"),[]).then(({data:e})=>e),"v-4008ff77":()=>g(()=>import("./gitlab-ci.html-bec278aa.js"),[]).then(({data:e})=>e),"v-0fbf5fdc":()=>g(()=>import("./rethinking-git-flow.html-3667168e.js"),[]).then(({data:e})=>e),"v-071be141":()=>g(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-a9aaa8d1.js"),[]).then(({data:e})=>e),"v-86a15cb6":()=>g(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-f4cd0189.js"),[]).then(({data:e})=>e),"v-6dc18ec6":()=>g(()=>import("./Resolving-Common-Problems-in-Maven.md.html-018ae313.js"),[]).then(({data:e})=>e),"v-538a98d7":()=>g(()=>import("./avoid-sending-password-in-plaintext.html-407350df.js"),[]).then(({data:e})=>e),"v-2f6ae09c":()=>g(()=>import("./check-if-name-exists.html-9253bedd.js"),[]).then(({data:e})=>e),"v-b5a78a7a":()=>g(()=>import("./common-practices-for-handling-excel.html-4f5d7b9c.js"),[]).then(({data:e})=>e),"v-100d1814":()=>g(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-256778be.js"),[]).then(({data:e})=>e),"v-d767e98e":()=>g(()=>import("./recommend-practices-for-collections-naming-convention.html-36ba7dcd.js"),[]).then(({data:e})=>e),"v-f5471cb0":()=>g(()=>import("./recommend-practices-for-query-by-date-range.html-45c30670.js"),[]).then(({data:e})=>e),"v-57d9b582":()=>g(()=>import("./recommend-practices-for-writing-good-functions.html-3f88f0bb.js"),[]).then(({data:e})=>e),"v-2ba0b01e":()=>g(()=>import("./using-enum-in-java.html-f7bb1962.js"),[]).then(({data:e})=>e),"v-386e94f1":()=>g(()=>import("./which-one-is-better-Boolean-or-boolean.html-7bae7bc2.js"),[]).then(({data:e})=>e),"v-0d6828c2":()=>g(()=>import("./which-one-is-better-forEach-or-map.html-87f75424.js"),[]).then(({data:e})=>e),"v-1aafac08":()=>g(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-d2a6015e.js"),[]).then(({data:e})=>e),"v-ca672354":()=>g(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-4951d610.js"),[]).then(({data:e})=>e),"v-143a8bce":()=>g(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-60a8f15f.js"),[]).then(({data:e})=>e),"v-7e2c7a0c":()=>g(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-09d1faec.js"),[]).then(({data:e})=>e),"v-f297935a":()=>g(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-34931763.js"),[]).then(({data:e})=>e),"v-de221860":()=>g(()=>import("./add-logging-for-llm-app.html-b2dd1309.js"),[]).then(({data:e})=>e),"v-39c09a30":()=>g(()=>import("./evaluate-llm-app-with-ragas.html-04648c99.js"),[]).then(({data:e})=>e),"v-5b37b3c6":()=>g(()=>import("./export-mysql-table-into-excel.html-09a29b73.js"),[]).then(({data:e})=>e),"v-966d933e":()=>g(()=>import("./mr.py.html-416ec43a.js"),[]).then(({data:e})=>e),"v-113531b4":()=>g(()=>import("./unit-testing-overview.html-5d5018ec.js"),[]).then(({data:e})=>e),"v-65b23736":()=>g(()=>import("./use-RestAssured-for-api-testing.html-0af1208b.js"),[]).then(({data:e})=>e),"v-c488ac58":()=>g(()=>import("./use-cypress-for-e2e-testing.html-34aa8b61.js"),[]).then(({data:e})=>e),"v-efcacba2":()=>g(()=>import("./use-jest-for-test-driven-development.html-134958bf.js"),[]).then(({data:e})=>e),"v-0be1af08":()=>g(()=>import("./use-playwright-for-ui-testing.html-261af66a.js"),[]).then(({data:e})=>e),"v-75616b85":()=>g(()=>import("./use-postman-for-api-testing.html-bc2c854e.js"),[]).then(({data:e})=>e),"v-7ba1021b":()=>g(()=>import("./use-pytest-for-regression-testing-in-llm-app.html-c87ac7e3.js"),[]).then(({data:e})=>e),"v-17809471":()=>g(()=>import("./how-to-connect-to-internet.html-c2ea20a2.js"),[]).then(({data:e})=>e),"v-3706649a":()=>g(()=>import("./404.html-f2d5dd87.js"),[]).then(({data:e})=>e),"v-71fde78e":()=>g(()=>import("./index.html-ff411f50.js"),[]).then(({data:e})=>e),"v-79c9f96f":()=>g(()=>import("./index.html-6a79c9ec.js"),[]).then(({data:e})=>e),"v-43539db8":()=>g(()=>import("./index.html-8779bfb9.js"),[]).then(({data:e})=>e),"v-06198984":()=>g(()=>import("./index.html-6039efb3.js"),[]).then(({data:e})=>e),"v-74473916":()=>g(()=>import("./index.html-b483266e.js"),[]).then(({data:e})=>e),"v-14c69af4":()=>g(()=>import("./index.html-08f7d006.js"),[]).then(({data:e})=>e),"v-eb072ff4":()=>g(()=>import("./index.html-bdb5e891.js"),[]).then(({data:e})=>e),"v-63cd5dba":()=>g(()=>import("./index.html-6866fdfe.js"),[]).then(({data:e})=>e),"v-0df55bac":()=>g(()=>import("./index.html-b3692dcf.js"),[]).then(({data:e})=>e),"v-d440f426":()=>g(()=>import("./index.html-aff33757.js"),[]).then(({data:e})=>e),"v-5bc93818":()=>g(()=>import("./index.html-fe376c9d.js"),[]).then(({data:e})=>e),"v-744d024e":()=>g(()=>import("./index.html-7b393b72.js"),[]).then(({data:e})=>e),"v-e52c881c":()=>g(()=>import("./index.html-1bb998c5.js"),[]).then(({data:e})=>e),"v-154dc4c4":()=>g(()=>import("./index.html-fa3db4ea.js"),[]).then(({data:e})=>e),"v-01560935":()=>g(()=>import("./index.html-c98c047d.js"),[]).then(({data:e})=>e),"v-3d5315f8":()=>g(()=>import("./index.html-e4ea5f08.js"),[]).then(({data:e})=>e),"v-51040344":()=>g(()=>import("./index.html-634a0594.js"),[]).then(({data:e})=>e),"v-211f44ee":()=>g(()=>import("./index.html-5fa1740f.js"),[]).then(({data:e})=>e),"v-1b3ae9cf":()=>g(()=>import("./index.html-db9a5c1a.js"),[]).then(({data:e})=>e),"v-0da0e901":()=>g(()=>import("./index.html-47577403.js"),[]).then(({data:e})=>e),"v-b309c306":()=>g(()=>import("./index.html-84db287f.js"),[]).then(({data:e})=>e),"v-b3094364":()=>g(()=>import("./index.html-4a669a47.js"),[]).then(({data:e})=>e),"v-245f5676":()=>g(()=>import("./index.html-44e80ad7.js"),[]).then(({data:e})=>e),"v-007c0ae2":()=>g(()=>import("./index.html-8811bd65.js"),[]).then(({data:e})=>e),"v-1bee38ca":()=>g(()=>import("./index.html-c50be8cb.js"),[]).then(({data:e})=>e),"v-0da0abf9":()=>g(()=>import("./index.html-90b5caba.js"),[]).then(({data:e})=>e),"v-5a3e80fc":()=>g(()=>import("./index.html-96e262ec.js"),[]).then(({data:e})=>e),"v-01c1de5b":()=>g(()=>import("./index.html-3d1d7f95.js"),[]).then(({data:e})=>e),"v-29350809":()=>g(()=>import("./index.html-fa48630e.js"),[]).then(({data:e})=>e),"v-50d6e023":()=>g(()=>import("./index.html-62782fea.js"),[]).then(({data:e})=>e),"v-0ca0efe6":()=>g(()=>import("./index.html-dc40d5ff.js"),[]).then(({data:e})=>e),"v-b310d42a":()=>g(()=>import("./index.html-5b2edaa1.js"),[]).then(({data:e})=>e),"v-13275df4":()=>g(()=>import("./index.html-15cad1de.js"),[]).then(({data:e})=>e),"v-28a1d8bf":()=>g(()=>import("./index.html-c22f0db4.js"),[]).then(({data:e})=>e),"v-60379330":()=>g(()=>import("./index.html-6d3cb5cc.js"),[]).then(({data:e})=>e),"v-3b951558":()=>g(()=>import("./index.html-db761428.js"),[]).then(({data:e})=>e),"v-b30c33a0":()=>g(()=>import("./index.html-fda8af0e.js"),[]).then(({data:e})=>e),"v-48b3e46d":()=>g(()=>import("./index.html-4736b52e.js"),[]).then(({data:e})=>e)},E0=JSON.parse(`{"base":"/","lang":"zh-CN","title":"levy","description":"levy's blog","head":[["meta",{"name":"google-site-verification","content":"XSoaUnV59ACn-fVEvYre2y_5mka_7o_wEoMPBQpwo2M"}],["script",{"async":true,"src":"https://www.googletagmanager.com/gtag/js?id=G-6HEW6B1S6B"}],["script",{},["window.dataLayer = window.dataLayer || [];\\nfunction gtag(){dataLayer.push(arguments);}\\ngtag('js', new Date());\\ngtag('config','G-6HEW6B1S6B');"]],["link",{"rel":"alternate","type":"application/rss+xml","href":"https://levy.vip/rss.xml","title":"levy RSS Feed"}]],"locales":{}}`);var L0=([e,t,l])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,l]),x0=e=>{const t=new Set,l=[];return e.forEach(n=>{const a=L0(n);t.has(a)||(t.add(a),l.push(n))}),l},T0=e=>e[e.length-1]==="/"||e.endsWith(".html")?e:`${e}/`,A0=e=>e.startsWith("ftp://"),Zt=e=>/^(https?:)?\/\//.test(e),O0=/.md((\?|#).*)?$/,Bn=(e,t="/")=>!!(Zt(e)||A0(e)||e.startsWith("/")&&!e.startsWith(t)&&!O0.test(e)),Oo=e=>/^mailto:/.test(e),P0=e=>/^tel:/.test(e),Li=e=>Object.prototype.toString.call(e)==="[object Object]",xi=e=>e[e.length-1]==="/"?e.slice(0,-1):e,Po=e=>e[0]==="/"?e.slice(1):e,I0=(e,t)=>{const l=Object.keys(e).sort((n,a)=>{const i=a.split("/").length-n.split("/").length;return i!==0?i:a.length-n.length});for(const n of l)if(t.startsWith(n))return n;return"/"};const Io={"v-8daa1a0e":A(()=>g(()=>import("./index.html-eb66a122.js"),["assets/index.html-eb66a122.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-22a39d25":A(()=>g(()=>import("./about.html-d0965c3f.js"),["assets/about.html-d0965c3f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3f274907":A(()=>g(()=>import("./about-arm-things-you-need-to-know.html-cff9d934.js"),["assets/about-arm-things-you-need-to-know.html-cff9d934.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6614b1a1":A(()=>g(()=>import("./common-solutions-of-object-storage-for-static-assets.html-294e093e.js"),["assets/common-solutions-of-object-storage-for-static-assets.html-294e093e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2e81d6a4":A(()=>g(()=>import("./docker-build-and-push-script.html-253684f7.js"),["assets/docker-build-and-push-script.html-253684f7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3c0c097a":A(()=>g(()=>import("./reduce-python-image-size.html-8d55f040.js"),["assets/reduce-python-image-size.html-8d55f040.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-daf302c6":A(()=>g(()=>import("./what-is-the-difference-between-sh-and-bash.html-e23437ca.js"),["assets/what-is-the-difference-between-sh-and-bash.html-e23437ca.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-30a50b00":A(()=>g(()=>import("./a-vuepress2-entertaining-video.html-13910c5b.js"),["assets/a-vuepress2-entertaining-video.html-13910c5b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0481cc80":A(()=>g(()=>import("./a-warning-from-navicat.html-0caac0ee.js"),["assets/a-warning-from-navicat.html-0caac0ee.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79e139f8":A(()=>g(()=>import("./beyond-utf8-do-you-know-utf8mb4-and-collation.html-2ecc34d4.js"),["assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-2ecc34d4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7a74360a":A(()=>g(()=>import("./claude-ai-in-action-extract-info-from-html.html-235260a5.js"),["assets/claude-ai-in-action-extract-info-from-html.html-235260a5.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-76f251d2":A(()=>g(()=>import("./copy-code-may-not-be-guilty.html-e731a413.js"),["assets/copy-code-may-not-be-guilty.html-e731a413.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f47f129e":A(()=>g(()=>import("./dont-try-to-argue-with-a-sb.html-ba82f07c.js"),["assets/dont-try-to-argue-with-a-sb.html-ba82f07c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-fd7b7f92":A(()=>g(()=>import("./iteration-retrospective-of-sanyuan.html-4186c541.js"),["assets/iteration-retrospective-of-sanyuan.html-4186c541.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-81a71778":A(()=>g(()=>import("./leverage-ai-to-boost-coding-productivity.html-851bce24.js"),["assets/leverage-ai-to-boost-coding-productivity.html-851bce24.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5c48d497":A(()=>g(()=>import("./reflections-on-a-speech-by-cto-of-microsoft-china.html-b3036c9b.js"),["assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-b3036c9b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4f919602":A(()=>g(()=>import("./testing-environments-should-be-consistent-with-production-environments.html-65f13b3e.js"),["assets/testing-environments-should-be-consistent-with-production-environments.html-65f13b3e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e305501":A(()=>g(()=>import("./things-I-have-to-vent-about-vue.html-c552fd94.js"),["assets/things-I-have-to-vent-about-vue.html-c552fd94.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-404740fa":A(()=>g(()=>import("./use-claude2-instead-of-chatgpt.html-8a0e6b21.js"),["assets/use-claude2-instead-of-chatgpt.html-8a0e6b21.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f1efc11c":A(()=>g(()=>import("./vim-creator-pass-away.html-d6b70963.js"),["assets/vim-creator-pass-away.html-d6b70963.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-bd2b5fe8":A(()=>g(()=>import("./you-dont-need-to-add-tenant_id-to-every-table.html-e9a846fd.js"),["assets/you-dont-need-to-add-tenant_id-to-every-table.html-e9a846fd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-971cc7fe":A(()=>g(()=>import("./contemporary-college-english-1.html-2dba2810.js"),["assets/contemporary-college-english-1.html-2dba2810.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-93b316c0":A(()=>g(()=>import("./contemporary-college-english-2.html-df977f79.js"),["assets/contemporary-college-english-2.html-df977f79.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-90496582":A(()=>g(()=>import("./contemporary-college-english-3.html-ea59c72d.js"),["assets/contemporary-college-english-3.html-ea59c72d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-8cdfb444":A(()=>g(()=>import("./contemporary-college-english-4.html-ca65df0b.js"),["assets/contemporary-college-english-4.html-ca65df0b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-89760306":A(()=>g(()=>import("./contemporary-college-english-5.html-aca7ab08.js"),["assets/contemporary-college-english-5.html-aca7ab08.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-860c51c8":A(()=>g(()=>import("./contemporary-college-english-6.html-9f53636f.js"),["assets/contemporary-college-english-6.html-9f53636f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-246f4f17":A(()=>g(()=>import("./everyone-can-learn-english-1-overview.html-9e2bcd46.js"),["assets/everyone-can-learn-english-1-overview.html-9e2bcd46.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3ac0474c":A(()=>g(()=>import("./everyone-can-learn-english-2-pronunciation.html-7fdca85a.js"),["assets/everyone-can-learn-english-2-pronunciation.html-7fdca85a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-58a409d7":A(()=>g(()=>import("./everyone-can-learn-english-3-words.html-f6646acd.js"),["assets/everyone-can-learn-english-3-words.html-f6646acd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-788d194a":A(()=>g(()=>import("./everyone-can-learn-english-4-listening-and-speaking.html-543a6f14.js"),["assets/everyone-can-learn-english-4-listening-and-speaking.html-543a6f14.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ae153a4e":A(()=>g(()=>import("./everyone-can-learn-english-5-reading-and-writing.html-1af65f78.js"),["assets/everyone-can-learn-english-5-reading-and-writing.html-1af65f78.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d42db13c":A(()=>g(()=>import("./how-to-self-evaluate-english-level.html-87710031.js"),["assets/how-to-self-evaluate-english-level.html-87710031.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6ed7d996":A(()=>g(()=>import("./learning-7000-words-task-completed.html-72ba0d4d.js"),["assets/learning-7000-words-task-completed.html-72ba0d4d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-221efd1f":A(()=>g(()=>import("./let-chatgpt-be-your-foreign-language-teacher.html-7de04010.js"),["assets/let-chatgpt-be-your-foreign-language-teacher.html-7de04010.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-72e84a92":A(()=>g(()=>import("./old-articles.html-13960583.js"),["assets/old-articles.html-13960583.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7320140c":A(()=>g(()=>import("./performance-optimization-in-action.html-564a74fd.js"),["assets/performance-optimization-in-action.html-564a74fd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1e5872c4":A(()=>g(()=>import("./git-best-pratices.html-c804ad63.js"),["assets/git-best-pratices.html-c804ad63.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48e494ef":A(()=>g(()=>import("./git-definitive-guide-to-merge-code.html-f93ecf11.js"),["assets/git-definitive-guide-to-merge-code.html-f93ecf11.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60021cbc":A(()=>g(()=>import("./git-history-two-tricks-in-idea.html-1143ee1d.js"),["assets/git-history-two-tricks-in-idea.html-1143ee1d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-642eaaea":A(()=>g(()=>import("./git-useful-commands.html-2042b2a2.js"),["assets/git-useful-commands.html-2042b2a2.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-4008ff77":A(()=>g(()=>import("./gitlab-ci.html-6f5b90d6.js"),["assets/gitlab-ci.html-6f5b90d6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0fbf5fdc":A(()=>g(()=>import("./rethinking-git-flow.html-3cd71123.js"),["assets/rethinking-git-flow.html-3cd71123.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-071be141":A(()=>g(()=>import("./use-command-line-tool-to-manage-gitlab-merge-request.html-6b7d6b97.js"),["assets/use-command-line-tool-to-manage-gitlab-merge-request.html-6b7d6b97.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-86a15cb6":A(()=>g(()=>import("./Resolving-Common-Problems-in-IntelliJ-IDEA.html-3148a9c9.js"),["assets/Resolving-Common-Problems-in-IntelliJ-IDEA.html-3148a9c9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-6dc18ec6":A(()=>g(()=>import("./Resolving-Common-Problems-in-Maven.md.html-1c6cb245.js"),["assets/Resolving-Common-Problems-in-Maven.md.html-1c6cb245.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-538a98d7":A(()=>g(()=>import("./avoid-sending-password-in-plaintext.html-21e6cf5c.js"),["assets/avoid-sending-password-in-plaintext.html-21e6cf5c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2f6ae09c":A(()=>g(()=>import("./check-if-name-exists.html-f54588de.js"),["assets/check-if-name-exists.html-f54588de.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b5a78a7a":A(()=>g(()=>import("./common-practices-for-handling-excel.html-8ccc7c4e.js"),["assets/common-practices-for-handling-excel.html-8ccc7c4e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-100d1814":A(()=>g(()=>import("./how-to-convert-snapshot-into-release-jar-without-source-code.html-6c6aee40.js"),["assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-6c6aee40.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d767e98e":A(()=>g(()=>import("./recommend-practices-for-collections-naming-convention.html-8fad9f5f.js"),["assets/recommend-practices-for-collections-naming-convention.html-8fad9f5f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f5471cb0":A(()=>g(()=>import("./recommend-practices-for-query-by-date-range.html-e8d89b5b.js"),["assets/recommend-practices-for-query-by-date-range.html-e8d89b5b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-57d9b582":A(()=>g(()=>import("./recommend-practices-for-writing-good-functions.html-feec5c42.js"),["assets/recommend-practices-for-writing-good-functions.html-feec5c42.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-2ba0b01e":A(()=>g(()=>import("./using-enum-in-java.html-af5dab3a.js"),["assets/using-enum-in-java.html-af5dab3a.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-386e94f1":A(()=>g(()=>import("./which-one-is-better-Boolean-or-boolean.html-0510842d.js"),["assets/which-one-is-better-Boolean-or-boolean.html-0510842d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0d6828c2":A(()=>g(()=>import("./which-one-is-better-forEach-or-map.html-b566c49e.js"),["assets/which-one-is-better-forEach-or-map.html-b566c49e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1aafac08":A(()=>g(()=>import("./why-i-prefer-fastjson-instead-of-jackson.html-9e436949.js"),["assets/why-i-prefer-fastjson-instead-of-jackson.html-9e436949.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-ca672354":A(()=>g(()=>import("./why-is-it-so-hard-to-upgrade-dependencies.html-fa7d71af.js"),["assets/why-is-it-so-hard-to-upgrade-dependencies.html-fa7d71af.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-143a8bce":A(()=>g(()=>import("./mysql-backup-case-study-mysqldump-in-action.html-15a436a3.js"),["assets/mysql-backup-case-study-mysqldump-in-action.html-15a436a3.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7e2c7a0c":A(()=>g(()=>import("./mysql-data-migration-case-study-add-auto-increment.html-9c104292.js"),["assets/mysql-data-migration-case-study-add-auto-increment.html-9c104292.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-f297935a":A(()=>g(()=>import("./mysql-details-you-should-know-when-execute-sql-in-command-line.html-f71636f6.js"),["assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-f71636f6.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-de221860":A(()=>g(()=>import("./add-logging-for-llm-app.html-afc22b3f.js"),["assets/add-logging-for-llm-app.html-afc22b3f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-39c09a30":A(()=>g(()=>import("./evaluate-llm-app-with-ragas.html-e7eee6ad.js"),["assets/evaluate-llm-app-with-ragas.html-e7eee6ad.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5b37b3c6":A(()=>g(()=>import("./export-mysql-table-into-excel.html-829471c4.js"),["assets/export-mysql-table-into-excel.html-829471c4.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-966d933e":A(()=>g(()=>import("./mr.py.html-d220188b.js"),["assets/mr.py.html-d220188b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-113531b4":A(()=>g(()=>import("./unit-testing-overview.html-90b924eb.js"),["assets/unit-testing-overview.html-90b924eb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-65b23736":A(()=>g(()=>import("./use-RestAssured-for-api-testing.html-12bcc171.js"),["assets/use-RestAssured-for-api-testing.html-12bcc171.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-c488ac58":A(()=>g(()=>import("./use-cypress-for-e2e-testing.html-b5ce58b8.js"),["assets/use-cypress-for-e2e-testing.html-b5ce58b8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-efcacba2":A(()=>g(()=>import("./use-jest-for-test-driven-development.html-c1ba0d4d.js"),["assets/use-jest-for-test-driven-development.html-c1ba0d4d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0be1af08":A(()=>g(()=>import("./use-playwright-for-ui-testing.html-f210cda9.js"),["assets/use-playwright-for-ui-testing.html-f210cda9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-75616b85":A(()=>g(()=>import("./use-postman-for-api-testing.html-eec727f0.js"),["assets/use-postman-for-api-testing.html-eec727f0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-7ba1021b":A(()=>g(()=>import("./use-pytest-for-regression-testing-in-llm-app.html-9748b2d8.js"),["assets/use-pytest-for-regression-testing-in-llm-app.html-9748b2d8.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-17809471":A(()=>g(()=>import("./how-to-connect-to-internet.html-2dd64f15.js"),["assets/how-to-connect-to-internet.html-2dd64f15.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3706649a":A(()=>g(()=>import("./404.html-a05e94f9.js"),["assets/404.html-a05e94f9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-71fde78e":A(()=>g(()=>import("./index.html-78b91340.js"),["assets/index.html-78b91340.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-79c9f96f":A(()=>g(()=>import("./index.html-d3101c6f.js"),["assets/index.html-d3101c6f.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-43539db8":A(()=>g(()=>import("./index.html-94ca11d0.js"),["assets/index.html-94ca11d0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-06198984":A(()=>g(()=>import("./index.html-cca92841.js"),["assets/index.html-cca92841.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-74473916":A(()=>g(()=>import("./index.html-1e4f7d0b.js"),["assets/index.html-1e4f7d0b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-14c69af4":A(()=>g(()=>import("./index.html-18399ef9.js"),["assets/index.html-18399ef9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-eb072ff4":A(()=>g(()=>import("./index.html-cd4d0b35.js"),["assets/index.html-cd4d0b35.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-63cd5dba":A(()=>g(()=>import("./index.html-5eaddabe.js"),["assets/index.html-5eaddabe.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0df55bac":A(()=>g(()=>import("./index.html-e538178c.js"),["assets/index.html-e538178c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-d440f426":A(()=>g(()=>import("./index.html-9eba5acd.js"),["assets/index.html-9eba5acd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5bc93818":A(()=>g(()=>import("./index.html-e7690677.js"),["assets/index.html-e7690677.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-744d024e":A(()=>g(()=>import("./index.html-82cd7e71.js"),["assets/index.html-82cd7e71.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-e52c881c":A(()=>g(()=>import("./index.html-30863193.js"),["assets/index.html-30863193.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-154dc4c4":A(()=>g(()=>import("./index.html-4092c5dd.js"),["assets/index.html-4092c5dd.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01560935":A(()=>g(()=>import("./index.html-a4e6ccfb.js"),["assets/index.html-a4e6ccfb.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3d5315f8":A(()=>g(()=>import("./index.html-8ad2339d.js"),["assets/index.html-8ad2339d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-51040344":A(()=>g(()=>import("./index.html-add8b707.js"),["assets/index.html-add8b707.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-211f44ee":A(()=>g(()=>import("./index.html-5d992ea9.js"),["assets/index.html-5d992ea9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1b3ae9cf":A(()=>g(()=>import("./index.html-de3413d0.js"),["assets/index.html-de3413d0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0da0e901":A(()=>g(()=>import("./index.html-5f610c54.js"),["assets/index.html-5f610c54.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b309c306":A(()=>g(()=>import("./index.html-7e34f95b.js"),["assets/index.html-7e34f95b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b3094364":A(()=>g(()=>import("./index.html-cb2d6ac0.js"),["assets/index.html-cb2d6ac0.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-245f5676":A(()=>g(()=>import("./index.html-88de6c6c.js"),["assets/index.html-88de6c6c.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-007c0ae2":A(()=>g(()=>import("./index.html-3ef79533.js"),["assets/index.html-3ef79533.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-1bee38ca":A(()=>g(()=>import("./index.html-3de969f7.js"),["assets/index.html-3de969f7.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0da0abf9":A(()=>g(()=>import("./index.html-d298086d.js"),["assets/index.html-d298086d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-5a3e80fc":A(()=>g(()=>import("./index.html-0420fe01.js"),["assets/index.html-0420fe01.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-01c1de5b":A(()=>g(()=>import("./index.html-9a40117b.js"),["assets/index.html-9a40117b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-29350809":A(()=>g(()=>import("./index.html-7b4364a1.js"),["assets/index.html-7b4364a1.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-50d6e023":A(()=>g(()=>import("./index.html-d7eb7ef3.js"),["assets/index.html-d7eb7ef3.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-0ca0efe6":A(()=>g(()=>import("./index.html-ab0c4e20.js"),["assets/index.html-ab0c4e20.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b310d42a":A(()=>g(()=>import("./index.html-987aca8e.js"),["assets/index.html-987aca8e.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-13275df4":A(()=>g(()=>import("./index.html-acfbcf8b.js"),["assets/index.html-acfbcf8b.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-28a1d8bf":A(()=>g(()=>import("./index.html-c279618d.js"),["assets/index.html-c279618d.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-60379330":A(()=>g(()=>import("./index.html-e4f2c1e9.js"),["assets/index.html-e4f2c1e9.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-3b951558":A(()=>g(()=>import("./index.html-77768903.js"),["assets/index.html-77768903.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-b30c33a0":A(()=>g(()=>import("./index.html-039cb721.js"),["assets/index.html-039cb721.js","assets/plugin-vue_export-helper-c27b6911.js"])),"v-48b3e46d":A(()=>g(()=>import("./index.html-6d8830d6.js"),["assets/index.html-6d8830d6.js","assets/plugin-vue_export-helper-c27b6911.js"]))};var R0=Symbol(""),C0=W(w0),Ro=Yt({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Dt=W(Ro),ue=()=>Dt,Co=Symbol(""),Ee=()=>{const e=me(Co);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},So=Symbol(""),S0=()=>{const e=me(So);if(!e)throw new Error("usePageHead() is called without provider.");return e},D0=Symbol(""),Do=Symbol(""),Mo=()=>{const e=me(Do);if(!e)throw new Error("usePageLang() is called without provider.");return e},$o=Symbol(""),M0=()=>{const e=me($o);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Ti=Symbol(""),mt=()=>{const e=me(Ti);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},ol=W(E0),Vo=()=>ol,Fo=Symbol(""),rn=()=>{const e=me(Fo);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},$0=Symbol(""),V0="Layout",F0="NotFound",wt=tn({resolveLayouts:e=>e.reduce((t,l)=>({...t,...l.layouts}),{}),resolvePageData:async e=>{const t=C0.value[e];return await(t==null?void 0:t())??Ro},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,l)=>{const n=ae(t.description)?t.description:l.description,a=[...X(t.head)?t.head:[],...l.head,["title",{},e],["meta",{name:"description",content:n}]];return x0(a)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(l=>!!l).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let l;if(e.path){const n=e.frontmatter.layout;ae(n)?l=n:l=V0}else l=F0;return t[l]},resolveRouteLocale:(e,t)=>I0(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),Zn=$({name:"ClientOnly",setup(e,t){const l=W(!1);return ke(()=>{l.value=!0}),()=>{var n,a;return l.value?(a=(n=t.slots).default)==null?void 0:a.call(n):null}}}),No=$({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=ue(),l=E(()=>Io[e.pageKey||t.value.key]);return()=>l.value?s(l.value):s("div","404 Not Found")}}),dt=(e={})=>e,xe=e=>Zt(e)?e:`/${Po(e)}`;const N0={};/*! - * vue-router v4.2.4 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */const rl=typeof window<"u";function j0(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ge=Object.assign;function ba(e,t){const l={};for(const n in t){const a=t[n];l[n]=ut(a)?a.map(e):e(a)}return l}const jl=()=>{},ut=Array.isArray,B0=/\/$/,H0=e=>e.replace(B0,"");function _a(e,t,l="/"){let n,a={},i="",r="";const o=t.indexOf("#");let c=t.indexOf("?");return o=0&&(c=-1),c>-1&&(n=t.slice(0,c),i=t.slice(c+1,o>-1?o:t.length),a=e(i)),o>-1&&(n=n||t.slice(0,o),r=t.slice(o,t.length)),n=U0(n??t,l),{fullPath:n+(i&&"?")+i+r,path:n,query:a,hash:r}}function z0(e,t){const l=t.query?e(t.query):"";return t.path+(l&&"?")+l+(t.hash||"")}function Mr(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function q0(e,t,l){const n=t.matched.length-1,a=l.matched.length-1;return n>-1&&n===a&&yl(t.matched[n],l.matched[a])&&jo(t.params,l.params)&&e(t.query)===e(l.query)&&t.hash===l.hash}function yl(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function jo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const l in e)if(!G0(e[l],t[l]))return!1;return!0}function G0(e,t){return ut(e)?$r(e,t):ut(t)?$r(t,e):e===t}function $r(e,t){return ut(t)?e.length===t.length&&e.every((l,n)=>l===t[n]):e.length===1&&e[0]===t}function U0(e,t){if(e.startsWith("/"))return e;if(!e)return t;const l=t.split("/"),n=e.split("/"),a=n[n.length-1];(a===".."||a===".")&&n.push("");let i=l.length-1,r,o;for(r=0;r1&&i--;else break;return l.slice(0,i).join("/")+"/"+n.slice(r-(r===n.length?1:0)).join("/")}var Jl;(function(e){e.pop="pop",e.push="push"})(Jl||(Jl={}));var Bl;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Bl||(Bl={}));function W0(e){if(!e)if(rl){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),H0(e)}const K0=/^[^#]+#/;function J0(e,t){return e.replace(K0,"#")+t}function Q0(e,t){const l=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-l.left-(t.left||0),top:n.top-l.top-(t.top||0)}}const ea=()=>({left:window.pageXOffset,top:window.pageYOffset});function Y0(e){let t;if("el"in e){const l=e.el,n=typeof l=="string"&&l.startsWith("#"),a=typeof l=="string"?n?document.getElementById(l.slice(1)):document.querySelector(l):l;if(!a)return;t=Q0(a,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Vr(e,t){return(history.state?history.state.position-t:-1)+e}const Ba=new Map;function X0(e,t){Ba.set(e,t)}function Z0(e){const t=Ba.get(e);return Ba.delete(e),t}let eh=()=>location.protocol+"//"+location.host;function Bo(e,t){const{pathname:l,search:n,hash:a}=t,i=e.indexOf("#");if(i>-1){let o=a.includes(e.slice(i))?e.slice(i).length:1,c=a.slice(o);return c[0]!=="/"&&(c="/"+c),Mr(c,"")}return Mr(l,e)+n+a}function th(e,t,l,n){let a=[],i=[],r=null;const o=({state:h})=>{const v=Bo(e,location),b=l.value,w=t.value;let L=0;if(h){if(l.value=v,t.value=h,r&&r===b){r=null;return}L=w?h.position-w.position:0}else n(v);a.forEach(m=>{m(l.value,b,{delta:L,type:Jl.pop,direction:L?L>0?Bl.forward:Bl.back:Bl.unknown})})};function c(){r=l.value}function u(h){a.push(h);const v=()=>{const b=a.indexOf(h);b>-1&&a.splice(b,1)};return i.push(v),v}function d(){const{history:h}=window;h.state&&h.replaceState(ge({},h.state,{scroll:ea()}),"")}function p(){for(const h of i)h();i=[],window.removeEventListener("popstate",o),window.removeEventListener("beforeunload",d)}return window.addEventListener("popstate",o),window.addEventListener("beforeunload",d,{passive:!0}),{pauseListeners:c,listen:u,destroy:p}}function Fr(e,t,l,n=!1,a=!1){return{back:e,current:t,forward:l,replaced:n,position:window.history.length,scroll:a?ea():null}}function lh(e){const{history:t,location:l}=window,n={value:Bo(e,l)},a={value:t.state};a.value||i(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function i(c,u,d){const p=e.indexOf("#"),h=p>-1?(l.host&&document.querySelector("base")?e:e.slice(p))+c:eh()+e+c;try{t[d?"replaceState":"pushState"](u,"",h),a.value=u}catch(v){console.error(v),l[d?"replace":"assign"](h)}}function r(c,u){const d=ge({},t.state,Fr(a.value.back,c,a.value.forward,!0),u,{position:a.value.position});i(c,d,!0),n.value=c}function o(c,u){const d=ge({},a.value,t.state,{forward:c,scroll:ea()});i(d.current,d,!0);const p=ge({},Fr(n.value,c,null),{position:d.position+1},u);i(c,p,!1),n.value=c}return{location:n,state:a,push:o,replace:r}}function nh(e){e=W0(e);const t=lh(e),l=th(e,t.state,t.location,t.replace);function n(i,r=!0){r||l.pauseListeners(),history.go(i)}const a=ge({location:"",base:e,go:n,createHref:J0.bind(null,e)},t,l);return Object.defineProperty(a,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(a,"state",{enumerable:!0,get:()=>t.state.value}),a}function ah(e){return typeof e=="string"||e&&typeof e=="object"}function Ho(e){return typeof e=="string"||typeof e=="symbol"}const Et={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},zo=Symbol("");var Nr;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Nr||(Nr={}));function bl(e,t){return ge(new Error,{type:e,[zo]:!0},t)}function _t(e,t){return e instanceof Error&&zo in e&&(t==null||!!(e.type&t))}const jr="[^/]+?",ih={sensitive:!1,strict:!1,start:!0,end:!0},rh=/[.+*?^${}()[\]/\\]/g;function sh(e,t){const l=ge({},ih,t),n=[];let a=l.start?"^":"";const i=[];for(const u of e){const d=u.length?[]:[90];l.strict&&!u.length&&(a+="/");for(let p=0;pt.length?t.length===1&&t[0]===40+40?1:-1:0}function ch(e,t){let l=0;const n=e.score,a=t.score;for(;l0&&t[t.length-1]<0}const uh={type:0,value:""},dh=/[a-zA-Z0-9_]/;function ph(e){if(!e)return[[]];if(e==="/")return[[uh]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(v){throw new Error(`ERR (${l})/"${u}": ${v}`)}let l=0,n=l;const a=[];let i;function r(){i&&a.push(i),i=[]}let o=0,c,u="",d="";function p(){u&&(l===0?i.push({type:0,value:u}):l===1||l===2||l===3?(i.length>1&&(c==="*"||c==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),i.push({type:1,value:u,regexp:d,repeatable:c==="*"||c==="+",optional:c==="*"||c==="?"})):t("Invalid state to consume buffer"),u="")}function h(){u+=c}for(;o{r(_)}:jl}function r(d){if(Ho(d)){const p=n.get(d);p&&(n.delete(d),l.splice(l.indexOf(p),1),p.children.forEach(r),p.alias.forEach(r))}else{const p=l.indexOf(d);p>-1&&(l.splice(p,1),d.record.name&&n.delete(d.record.name),d.children.forEach(r),d.alias.forEach(r))}}function o(){return l}function c(d){let p=0;for(;p=0&&(d.record.path!==l[p].record.path||!qo(d,l[p]));)p++;l.splice(p,0,d),d.record.name&&!zr(d)&&n.set(d.record.name,d)}function u(d,p){let h,v={},b,w;if("name"in d&&d.name){if(h=n.get(d.name),!h)throw bl(1,{location:d});w=h.record.name,v=ge(Hr(p.params,h.keys.filter(_=>!_.optional).map(_=>_.name)),d.params&&Hr(d.params,h.keys.map(_=>_.name))),b=h.stringify(v)}else if("path"in d)b=d.path,h=l.find(_=>_.re.test(b)),h&&(v=h.parse(b),w=h.record.name);else{if(h=p.name?n.get(p.name):l.find(_=>_.re.test(p.path)),!h)throw bl(1,{location:d,currentLocation:p});w=h.record.name,v=ge({},p.params,d.params),b=h.stringify(v)}const L=[];let m=h;for(;m;)L.unshift(m.record),m=m.parent;return{name:w,path:b,params:v,matched:L,meta:mh(L)}}return e.forEach(d=>i(d)),{addRoute:i,resolve:u,removeRoute:r,getRoutes:o,getRecordMatcher:a}}function Hr(e,t){const l={};for(const n of t)n in e&&(l[n]=e[n]);return l}function fh(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:gh(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function gh(e){const t={},l=e.props||!1;if("component"in e)t.default=l;else for(const n in e.components)t[n]=typeof l=="object"?l[n]:l;return t}function zr(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function mh(e){return e.reduce((t,l)=>ge(t,l.meta),{})}function qr(e,t){const l={};for(const n in e)l[n]=n in t?t[n]:e[n];return l}function qo(e,t){return t.children.some(l=>l===e||qo(e,l))}const Go=/#/g,yh=/&/g,bh=/\//g,_h=/=/g,kh=/\?/g,Uo=/\+/g,wh=/%5B/g,Eh=/%5D/g,Wo=/%5E/g,Lh=/%60/g,Ko=/%7B/g,xh=/%7C/g,Jo=/%7D/g,Th=/%20/g;function Ai(e){return encodeURI(""+e).replace(xh,"|").replace(wh,"[").replace(Eh,"]")}function Ah(e){return Ai(e).replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function Ha(e){return Ai(e).replace(Uo,"%2B").replace(Th,"+").replace(Go,"%23").replace(yh,"%26").replace(Lh,"`").replace(Ko,"{").replace(Jo,"}").replace(Wo,"^")}function Oh(e){return Ha(e).replace(_h,"%3D")}function Ph(e){return Ai(e).replace(Go,"%23").replace(kh,"%3F")}function Ih(e){return e==null?"":Ph(e).replace(bh,"%2F")}function Hn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Rh(e){const t={};if(e===""||e==="?")return t;const n=(e[0]==="?"?e.slice(1):e).split("&");for(let a=0;ai&&Ha(i)):[n&&Ha(n)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+l,i!=null&&(t+="="+i))})}return t}function Ch(e){const t={};for(const l in e){const n=e[l];n!==void 0&&(t[l]=ut(n)?n.map(a=>a==null?null:""+a):n==null?n:""+n)}return t}const Sh=Symbol(""),Ur=Symbol(""),ta=Symbol(""),Oi=Symbol(""),za=Symbol("");function Rl(){let e=[];function t(n){return e.push(n),()=>{const a=e.indexOf(n);a>-1&&e.splice(a,1)}}function l(){e=[]}return{add:t,list:()=>e.slice(),reset:l}}function Mt(e,t,l,n,a){const i=n&&(n.enterCallbacks[a]=n.enterCallbacks[a]||[]);return()=>new Promise((r,o)=>{const c=p=>{p===!1?o(bl(4,{from:l,to:t})):p instanceof Error?o(p):ah(p)?o(bl(2,{from:t,to:p})):(i&&n.enterCallbacks[a]===i&&typeof p=="function"&&i.push(p),r())},u=e.call(n&&n.instances[a],t,l,c);let d=Promise.resolve(u);e.length<3&&(d=d.then(c)),d.catch(p=>o(p))})}function ka(e,t,l,n){const a=[];for(const i of e)for(const r in i.components){let o=i.components[r];if(!(t!=="beforeRouteEnter"&&!i.instances[r]))if(Dh(o)){const u=(o.__vccOpts||o)[t];u&&a.push(Mt(u,l,n,i,r))}else{let c=o();a.push(()=>c.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${r}" at "${i.path}"`));const d=j0(u)?u.default:u;i.components[r]=d;const h=(d.__vccOpts||d)[t];return h&&Mt(h,l,n,i,r)()}))}}return a}function Dh(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function qa(e){const t=me(ta),l=me(Oi),n=E(()=>t.resolve(it(e.to))),a=E(()=>{const{matched:c}=n.value,{length:u}=c,d=c[u-1],p=l.matched;if(!d||!p.length)return-1;const h=p.findIndex(yl.bind(null,d));if(h>-1)return h;const v=Wr(c[u-2]);return u>1&&Wr(d)===v&&p[p.length-1].path!==v?p.findIndex(yl.bind(null,c[u-2])):h}),i=E(()=>a.value>-1&&Fh(l.params,n.value.params)),r=E(()=>a.value>-1&&a.value===l.matched.length-1&&jo(l.params,n.value.params));function o(c={}){return Vh(c)?t[it(e.replace)?"replace":"push"](it(e.to)).catch(jl):Promise.resolve()}return{route:n,href:E(()=>n.value.href),isActive:i,isExactActive:r,navigate:o}}const Mh=$({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:qa,setup(e,{slots:t}){const l=tn(qa(e)),{options:n}=me(ta),a=E(()=>({[Kr(e.activeClass,n.linkActiveClass,"router-link-active")]:l.isActive,[Kr(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:l.isExactActive}));return()=>{const i=t.default&&t.default(l);return e.custom?i:s("a",{"aria-current":l.isExactActive?e.ariaCurrentValue:null,href:l.href,onClick:l.navigate,class:a.value},i)}}}),$h=Mh;function Vh(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Fh(e,t){for(const l in t){const n=t[l],a=e[l];if(typeof n=="string"){if(n!==a)return!1}else if(!ut(a)||a.length!==n.length||n.some((i,r)=>i!==a[r]))return!1}return!0}function Wr(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Kr=(e,t,l)=>e??t??l,Nh=$({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:l}){const n=me(za),a=E(()=>e.route||n.value),i=me(Ur,0),r=E(()=>{let u=it(i);const{matched:d}=a.value;let p;for(;(p=d[u])&&!p.components;)u++;return u}),o=E(()=>a.value.matched[r.value]);ot(Ur,E(()=>r.value+1)),ot(Sh,o),ot(za,a);const c=W();return ce(()=>[c.value,o.value,e.name],([u,d,p],[h,v,b])=>{d&&(d.instances[p]=u,v&&v!==d&&u&&u===h&&(d.leaveGuards.size||(d.leaveGuards=v.leaveGuards),d.updateGuards.size||(d.updateGuards=v.updateGuards))),u&&d&&(!v||!yl(d,v)||!h)&&(d.enterCallbacks[p]||[]).forEach(w=>w(u))},{flush:"post"}),()=>{const u=a.value,d=e.name,p=o.value,h=p&&p.components[d];if(!h)return Jr(l.default,{Component:h,route:u});const v=p.props[d],b=v?v===!0?u.params:typeof v=="function"?v(u):v:null,L=s(h,ge({},b,t,{onVnodeUnmounted:m=>{m.component.isUnmounted&&(p.instances[d]=null)},ref:c}));return Jr(l.default,{Component:L,route:u})||L}}});function Jr(e,t){if(!e)return null;const l=e(t);return l.length===1?l[0]:l}const Qo=Nh;function jh(e){const t=vh(e.routes,e),l=e.parseQuery||Rh,n=e.stringifyQuery||Gr,a=e.history,i=Rl(),r=Rl(),o=Rl(),c=Ge(Et);let u=Et;rl&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const d=ba.bind(null,O=>""+O),p=ba.bind(null,Ih),h=ba.bind(null,Hn);function v(O,H){let N,K;return Ho(O)?(N=t.getRecordMatcher(O),K=H):K=O,t.addRoute(K,N)}function b(O){const H=t.getRecordMatcher(O);H&&t.removeRoute(H)}function w(){return t.getRoutes().map(O=>O.record)}function L(O){return!!t.getRecordMatcher(O)}function m(O,H){if(H=ge({},H||c.value),typeof O=="string"){const k=_a(l,O,H.path),x=t.resolve({path:k.path},H),P=a.createHref(k.fullPath);return ge(k,x,{params:h(x.params),hash:Hn(k.hash),redirectedFrom:void 0,href:P})}let N;if("path"in O)N=ge({},O,{path:_a(l,O.path,H.path).path});else{const k=ge({},O.params);for(const x in k)k[x]==null&&delete k[x];N=ge({},O,{params:p(k)}),H.params=p(H.params)}const K=t.resolve(N,H),fe=O.hash||"";K.params=d(h(K.params));const f=z0(n,ge({},O,{hash:Ah(fe),path:K.path})),y=a.createHref(f);return ge({fullPath:f,hash:fe,query:n===Gr?Ch(O.query):O.query||{}},K,{redirectedFrom:void 0,href:y})}function _(O){return typeof O=="string"?_a(l,O,c.value.path):ge({},O)}function I(O,H){if(u!==O)return bl(8,{from:H,to:O})}function R(O){return V(O)}function B(O){return R(ge(_(O),{replace:!0}))}function C(O){const H=O.matched[O.matched.length-1];if(H&&H.redirect){const{redirect:N}=H;let K=typeof N=="function"?N(O):N;return typeof K=="string"&&(K=K.includes("?")||K.includes("#")?K=_(K):{path:K},K.params={}),ge({query:O.query,hash:O.hash,params:"path"in K?{}:O.params},K)}}function V(O,H){const N=u=m(O),K=c.value,fe=O.state,f=O.force,y=O.replace===!0,k=C(N);if(k)return V(ge(_(k),{state:typeof k=="object"?ge({},fe,k.state):fe,force:f,replace:y}),H||N);const x=N;x.redirectedFrom=H;let P;return!f&&q0(n,K,N)&&(P=bl(16,{to:x,from:K}),pt(K,K,!0,!1)),(P?Promise.resolve(P):Y(x,K)).catch(S=>_t(S)?_t(S,2)?S:Ot(S):ve(S,x,K)).then(S=>{if(S){if(_t(S,2))return V(ge({replace:y},_(S.to),{state:typeof S.to=="object"?ge({},fe,S.to.state):fe,force:f}),H||x)}else S=z(x,K,!0,y,fe);return ne(x,K,S),S})}function T(O,H){const N=I(O,H);return N?Promise.reject(N):Promise.resolve()}function q(O){const H=ll.values().next().value;return H&&typeof H.runWithContext=="function"?H.runWithContext(O):O()}function Y(O,H){let N;const[K,fe,f]=Bh(O,H);N=ka(K.reverse(),"beforeRouteLeave",O,H);for(const k of K)k.leaveGuards.forEach(x=>{N.push(Mt(x,O,H))});const y=T.bind(null,O,H);return N.push(y),De(N).then(()=>{N=[];for(const k of i.list())N.push(Mt(k,O,H));return N.push(y),De(N)}).then(()=>{N=ka(fe,"beforeRouteUpdate",O,H);for(const k of fe)k.updateGuards.forEach(x=>{N.push(Mt(x,O,H))});return N.push(y),De(N)}).then(()=>{N=[];for(const k of f)if(k.beforeEnter)if(ut(k.beforeEnter))for(const x of k.beforeEnter)N.push(Mt(x,O,H));else N.push(Mt(k.beforeEnter,O,H));return N.push(y),De(N)}).then(()=>(O.matched.forEach(k=>k.enterCallbacks={}),N=ka(f,"beforeRouteEnter",O,H),N.push(y),De(N))).then(()=>{N=[];for(const k of r.list())N.push(Mt(k,O,H));return N.push(y),De(N)}).catch(k=>_t(k,8)?k:Promise.reject(k))}function ne(O,H,N){o.list().forEach(K=>q(()=>K(O,H,N)))}function z(O,H,N,K,fe){const f=I(O,H);if(f)return f;const y=H===Et,k=rl?history.state:{};N&&(K||y?a.replace(O.fullPath,ge({scroll:y&&k&&k.scroll},fe)):a.push(O.fullPath,fe)),c.value=O,pt(O,H,N,y),Ot()}let ee;function U(){ee||(ee=a.listen((O,H,N)=>{if(!mn.listening)return;const K=m(O),fe=C(K);if(fe){V(ge(fe,{replace:!0}),K).catch(jl);return}u=K;const f=c.value;rl&&X0(Vr(f.fullPath,N.delta),ea()),Y(K,f).catch(y=>_t(y,12)?y:_t(y,2)?(V(y.to,K).then(k=>{_t(k,20)&&!N.delta&&N.type===Jl.pop&&a.go(-1,!1)}).catch(jl),Promise.reject()):(N.delta&&a.go(-N.delta,!1),ve(y,K,f))).then(y=>{y=y||z(K,f,!1),y&&(N.delta&&!_t(y,8)?a.go(-N.delta,!1):N.type===Jl.pop&&_t(y,20)&&a.go(-1,!1)),ne(K,f,y)}).catch(jl)}))}let Se=Rl(),he=Rl(),_e;function ve(O,H,N){Ot(O);const K=he.list();return K.length?K.forEach(fe=>fe(O,H,N)):console.error(O),Promise.reject(O)}function bt(){return _e&&c.value!==Et?Promise.resolve():new Promise((O,H)=>{Se.add([O,H])})}function Ot(O){return _e||(_e=!O,U(),Se.list().forEach(([H,N])=>O?N(O):H()),Se.reset()),O}function pt(O,H,N,K){const{scrollBehavior:fe}=e;if(!rl||!fe)return Promise.resolve();const f=!N&&Z0(Vr(O.fullPath,0))||(K||!N)&&history.state&&history.state.scroll||null;return Tl().then(()=>fe(O,H,f)).then(y=>y&&Y0(y)).catch(y=>ve(y,O,H))}const je=O=>a.go(O);let tl;const ll=new Set,mn={currentRoute:c,listening:!0,addRoute:v,removeRoute:b,hasRoute:L,getRoutes:w,resolve:m,options:e,push:R,replace:B,go:je,back:()=>je(-1),forward:()=>je(1),beforeEach:i.add,beforeResolve:r.add,afterEach:o.add,onError:he.add,isReady:bt,install(O){const H=this;O.component("RouterLink",$h),O.component("RouterView",Qo),O.config.globalProperties.$router=H,Object.defineProperty(O.config.globalProperties,"$route",{enumerable:!0,get:()=>it(c)}),rl&&!tl&&c.value===Et&&(tl=!0,R(a.location).catch(fe=>{}));const N={};for(const fe in Et)Object.defineProperty(N,fe,{get:()=>c.value[fe],enumerable:!0});O.provide(ta,H),O.provide(Oi,js(N)),O.provide(za,c);const K=O.unmount;ll.add(O),O.unmount=function(){ll.delete(O),ll.size<1&&(u=Et,ee&&ee(),ee=null,c.value=Et,tl=!1,_e=!1),K()}}};function De(O){return O.reduce((H,N)=>H.then(()=>q(N)),Promise.resolve())}return mn}function Bh(e,t){const l=[],n=[],a=[],i=Math.max(t.matched.length,e.matched.length);for(let r=0;ryl(u,o))?n.push(o):l.push(o));const c=e.matched[r];c&&(t.matched.find(u=>yl(u,c))||a.push(c))}return[l,n,a]}function Ve(){return me(ta)}function yt(){return me(Oi)}const de=({name:e="",color:t="currentColor"},{slots:l})=>{var n;return s("svg",{xmlns:"http://www.w3.org/2000/svg",class:["icon",`${e}-icon`],viewBox:"0 0 1024 1024",fill:t,"aria-label":`${e} icon`},(n=l.default)==null?void 0:n.call(l))};de.displayName="IconBase";const Yo=({size:e=48,stroke:t=4,wrapper:l=!0,height:n=2*e})=>{const a=s("svg",{xmlns:"http://www.w3.org/2000/svg",width:e,height:e,preserveAspectRatio:"xMidYMid",viewBox:"25 25 50 50"},[s("animateTransform",{attributeName:"transform",type:"rotate",dur:"2s",keyTimes:"0;1",repeatCount:"indefinite",values:"0;360"}),s("circle",{cx:"50",cy:"50",r:"20",fill:"none",stroke:"currentColor","stroke-width":t,"stroke-linecap":"round"},[s("animate",{attributeName:"stroke-dasharray",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"1,200;90,200;1,200"}),s("animate",{attributeName:"stroke-dashoffset",dur:"1.5s",keyTimes:"0;0.5;1",repeatCount:"indefinite",values:"0;-35px;-125px"})])]);return l?s("div",{class:"loading-icon-wrapper",style:`display:flex;align-items:center;justify-content:center;height:${n}px`},a):a};Yo.displayName="LoadingIcon";const Xo=(e,{slots:t})=>{var l;return(l=t.default)==null?void 0:l.call(t)},Pi=(e="")=>{if(e){if(typeof e=="number")return new Date(e);const t=Date.parse(e.toString());if(!Number.isNaN(t))return new Date(t)}return null},la=(e,t)=>{let l=1;for(let n=0;n>6;return l+=l<<3,l^=l>>11,l%t},Zo=Array.isArray,Hh=e=>typeof e=="function",zh=e=>typeof e=="string";var qh=e=>e.startsWith("ftp://"),Ii=e=>/^(https?:)?\/\//.test(e),Gh=/.md((\?|#).*)?$/,Uh=(e,t="/")=>!!(Ii(e)||qh(e)||e.startsWith("/")&&!e.startsWith(t)&&!Gh.test(e)),ec=e=>Object.prototype.toString.call(e)==="[object Object]";function Wh(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function Kh(e){return Wh(),E(()=>!!e())}const Tt=e=>typeof e=="string",Ql=(e,t)=>Tt(e)&&e.startsWith(t),il=(e,t)=>Tt(e)&&e.endsWith(t),sn=Object.entries,Jh=Object.fromEntries,gt=Object.keys,Qh=e=>(e.endsWith(".md")&&(e=`${e.slice(0,-3)}.html`),!e.endsWith("/")&&!e.endsWith(".html")&&(e=`${e}.html`),e=e.replace(/(^|\/)(?:README|index).html$/i,"$1"),e),tc=e=>{const[t,l=""]=e.split("#");return t?`${Qh(t)}${l?`#${l}`:""}`:e},Qr=e=>ec(e)&&Tt(e.name),Yl=(e,t=!1)=>e?Zo(e)?e.map(l=>Tt(l)?{name:l}:Qr(l)?l:null).filter(l=>l!==null):Tt(e)?[{name:e}]:Qr(e)?[e]:(console.error(`Expect "author" to be \`AuthorInfo[] | AuthorInfo | string[] | string ${t?"":"| false"} | undefined\`, but got`,e),[]):[],lc=(e,t)=>{if(e){if(Zo(e)&&e.every(Tt))return e;if(Tt(e))return[e];console.error(`Expect ${t||"value"} to be \`string[] | string | undefined\`, but got`,e)}return[]},nc=e=>lc(e,"category"),ac=e=>lc(e,"tag"),na=e=>Ql(e,"/");let Yh=class{constructor(){oa(this,"containerElement");oa(this,"messageElements",{});const t="message-container",l=document.getElementById(t);l?this.containerElement=l:(this.containerElement=document.createElement("div"),this.containerElement.id=t,document.body.appendChild(this.containerElement))}pop(t,l=2e3){const n=document.createElement("div"),a=Date.now();return n.className="message move-in",n.innerHTML=t,this.containerElement.appendChild(n),this.messageElements[a]=n,l>0&&setTimeout(()=>{this.close(a)},l),a}close(t){if(t){const l=this.messageElements[t];l.classList.remove("move-in"),l.classList.add("move-out"),l.addEventListener("animationend",()=>{l.remove(),delete this.messageElements[t]})}else gt(this.messageElements).forEach(l=>this.close(Number(l)))}destroy(){document.body.removeChild(this.containerElement)}};const ic=/#.*$/u,Xh=e=>{const t=ic.exec(e);return t?t[0]:""},Yr=e=>decodeURI(e).replace(ic,"").replace(/(index)?\.(md|html)$/,""),Ri=(e,t)=>{if(t===void 0)return!1;const l=Yr(e.path),n=Yr(t),a=Xh(t);return a?a===e.hash&&(!n||l===n):l===n},Zh=e=>Ii(e)?e:`https://github.com/${e}`,rc=e=>!Ii(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,_l=(e,...t)=>{const l=e.resolve(...t),n=l.matched[l.matched.length-1];if(!(n!=null&&n.redirect))return l;const{redirect:a}=n,i=Hh(a)?a(l):a,r=zh(i)?{path:i}:i;return _l(e,{hash:l.hash,query:l.query,params:l.params,...r})},e1=e=>{if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget){const t=e.currentTarget.getAttribute("target");if(t!=null&&t.match(/\b_blank\b/i))return}return e.preventDefault(),!0}},Ce=({to:e=""},{slots:t})=>{var l;const n=Ve(),a=(i={})=>e1(i)?n.push(e).catch():Promise.resolve();return s("a",{class:"vp-link",href:xe(tc(e)),onClick:a},(l=t.default)==null?void 0:l.call(t))};Ce.displayName="VPLink";const sc=()=>s(de,{name:"github"},()=>s("path",{d:"M511.957 21.333C241.024 21.333 21.333 240.981 21.333 512c0 216.832 140.544 400.725 335.574 465.664 24.49 4.395 32.256-10.07 32.256-23.083 0-11.69.256-44.245 0-85.205-136.448 29.61-164.736-64.64-164.736-64.64-22.315-56.704-54.4-71.765-54.4-71.765-44.587-30.464 3.285-29.824 3.285-29.824 49.195 3.413 75.179 50.517 75.179 50.517 43.776 75.008 114.816 53.333 142.762 40.79 4.523-31.66 17.152-53.377 31.19-65.537-108.971-12.458-223.488-54.485-223.488-242.602 0-53.547 19.114-97.323 50.517-131.67-5.035-12.33-21.93-62.293 4.779-129.834 0 0 41.258-13.184 134.912 50.346a469.803 469.803 0 0 1 122.88-16.554c41.642.213 83.626 5.632 122.88 16.554 93.653-63.488 134.784-50.346 134.784-50.346 26.752 67.541 9.898 117.504 4.864 129.834 31.402 34.347 50.474 78.123 50.474 131.67 0 188.586-114.73 230.016-224.042 242.09 17.578 15.232 33.578 44.672 33.578 90.454v135.85c0 13.142 7.936 27.606 32.854 22.87C862.25 912.597 1002.667 728.747 1002.667 512c0-271.019-219.648-490.667-490.71-490.667z"}));sc.displayName="GitHubIcon";const oc=()=>s(de,{name:"gitlab"},()=>s("path",{d:"M229.333 78.688C223.52 62 199.895 62 193.895 78.688L87.958 406.438h247.5c-.188 0-106.125-327.75-106.125-327.75zM33.77 571.438c-4.875 15 .563 31.687 13.313 41.25l464.812 345L87.77 406.438zm301.5-165 176.813 551.25 176.812-551.25zm655.125 165-54-165-424.312 551.25 464.812-345c12.938-9.563 18.188-26.25 13.5-41.25zM830.27 78.688c-5.812-16.688-29.437-16.688-35.437 0l-106.125 327.75h247.5z"}));oc.displayName="GitLabIcon";const cc=()=>s(de,{name:"gitee"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm242.97-533.34H482.39a23.7 23.7 0 0 0-23.7 23.7l-.03 59.28c0 13.08 10.59 23.7 23.7 23.7h165.96a23.7 23.7 0 0 1 23.7 23.7v11.85a71.1 71.1 0 0 1-71.1 71.1H375.71a23.7 23.7 0 0 1-23.7-23.7V423.11a71.1 71.1 0 0 1 71.1-71.1h331.8a23.7 23.7 0 0 0 23.7-23.7l.06-59.25a23.73 23.73 0 0 0-23.7-23.73H423.11a177.78 177.78 0 0 0-177.78 177.75v331.83c0 13.08 10.62 23.7 23.7 23.7h349.62a159.99 159.99 0 0 0 159.99-159.99V482.33a23.7 23.7 0 0 0-23.7-23.7z"}));cc.displayName="GiteeIcon";const uc=()=>s(de,{name:"bitbucket"},()=>s("path",{d:"M575.256 490.862c6.29 47.981-52.005 85.723-92.563 61.147-45.714-20.004-45.714-92.562-1.133-113.152 38.29-23.442 93.696 7.424 93.696 52.005zm63.451-11.996c-10.276-81.152-102.29-134.839-177.152-101.156-47.433 21.138-79.433 71.424-77.129 124.562 2.853 69.705 69.157 126.866 138.862 120.576S647.3 548.571 638.708 478.83zm136.558-309.723c-25.161-33.134-67.986-38.839-105.728-45.13-106.862-17.151-216.576-17.7-323.438 1.134-35.438 5.706-75.447 11.996-97.719 43.996 36.572 34.304 88.576 39.424 135.424 45.129 84.553 10.862 171.447 11.447 256 .585 47.433-5.705 99.987-10.276 135.424-45.714zm32.585 591.433c-16.018 55.99-6.839 131.438-66.304 163.986-102.29 56.576-226.304 62.867-338.87 42.862-59.43-10.862-129.135-29.696-161.72-85.723-14.3-54.858-23.442-110.848-32.585-166.84l3.438-9.142 10.276-5.157c170.277 112.567 408.576 112.567 579.438 0 26.844 8.01 6.84 40.558 6.29 60.014zm103.424-549.157c-19.42 125.148-41.728 249.71-63.415 374.272-6.29 36.572-41.728 57.162-71.424 72.558-106.862 53.724-231.424 62.866-348.562 50.286-79.433-8.558-160.585-29.696-225.134-79.433-30.28-23.443-30.28-63.415-35.986-97.134-20.005-117.138-42.862-234.277-57.161-352.585 6.839-51.42 64.585-73.728 107.447-89.71 57.16-21.138 118.272-30.866 178.87-36.571 129.134-12.58 261.157-8.01 386.304 28.562 44.581 13.13 92.563 31.415 122.844 69.705 13.714 17.7 9.143 40.01 6.29 60.014z"}));uc.displayName="BitbucketIcon";const dc=()=>s(de,{name:"source"},()=>s("path",{d:"M601.92 475.2c0 76.428-8.91 83.754-28.512 99.594-14.652 11.88-43.956 14.058-78.012 16.434-18.81 1.386-40.392 2.97-62.172 6.534-18.612 2.97-36.432 9.306-53.064 17.424V299.772c37.818-21.978 63.36-62.766 63.36-109.692 0-69.894-56.826-126.72-126.72-126.72S190.08 120.186 190.08 190.08c0 46.926 25.542 87.714 63.36 109.692v414.216c-37.818 21.978-63.36 62.766-63.36 109.692 0 69.894 56.826 126.72 126.72 126.72s126.72-56.826 126.72-126.72c0-31.086-11.286-59.598-29.7-81.576 13.266-9.504 27.522-17.226 39.996-19.206 16.038-2.574 32.868-3.762 50.688-5.148 48.312-3.366 103.158-7.326 148.896-44.55 61.182-49.698 74.25-103.158 75.24-187.902V475.2h-126.72zM316.8 126.72c34.848 0 63.36 28.512 63.36 63.36s-28.512 63.36-63.36 63.36-63.36-28.512-63.36-63.36 28.512-63.36 63.36-63.36zm0 760.32c-34.848 0-63.36-28.512-63.36-63.36s28.512-63.36 63.36-63.36 63.36 28.512 63.36 63.36-28.512 63.36-63.36 63.36zM823.68 158.4h-95.04V63.36h-126.72v95.04h-95.04v126.72h95.04v95.04h126.72v-95.04h95.04z"}));dc.displayName="SourceIcon";const ct=(e,t)=>{const l=t?t._instance:Xt();return ec(l==null?void 0:l.appContext.components)&&(e in l.appContext.components||tt(e)in l.appContext.components||en(tt(e))in l.appContext.components)},t1=()=>Kh(()=>typeof window<"u"&&window.navigator&&"userAgent"in window.navigator),l1=()=>{const e=t1();return E(()=>e.value&&/\b(?:Android|iPhone)/i.test(navigator.userAgent))},on=e=>{const t=mt();return E(()=>e[t.value])};var n1=Object.defineProperty,a1=Object.defineProperties,i1=Object.getOwnPropertyDescriptors,Xr=Object.getOwnPropertySymbols,r1=Object.prototype.hasOwnProperty,s1=Object.prototype.propertyIsEnumerable,Zr=(e,t,l)=>t in e?n1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,o1=(e,t)=>{for(var l in t||(t={}))r1.call(t,l)&&Zr(e,l,t[l]);if(Xr)for(var l of Xr(t))s1.call(t,l)&&Zr(e,l,t[l]);return e},c1=(e,t)=>a1(e,i1(t));function es(e,t){var l;const n=Ge();return Xs(()=>{n.value=e()},c1(o1({},t),{flush:(l=t==null?void 0:t.flush)!=null?l:"sync"})),Yt(n)}function Al(e){return Ps()?(td(e),!0):!1}function He(e){return typeof e=="function"?e():it(e)}const cn=typeof window<"u",Xl=()=>{},ts=u1();function u1(){var e;return cn&&((e=window==null?void 0:window.navigator)==null?void 0:e.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent)}function pc(e,t){function l(...n){return new Promise((a,i)=>{Promise.resolve(e(()=>t.apply(this,n),{fn:t,thisArg:this,args:n})).then(a).catch(i)})}return l}const hc=e=>e();function d1(e,t=!0,l=!0,n=!1){let a=0,i,r=!0,o=Xl,c;const u=()=>{i&&(clearTimeout(i),i=void 0,o(),o=Xl)};return p=>{const h=He(e),v=Date.now()-a,b=()=>c=p();return u(),h<=0?(a=Date.now(),b()):(v>h&&(l||!r)?(a=Date.now(),b()):t&&(c=new Promise((w,L)=>{o=n?L:w,i=setTimeout(()=>{a=Date.now(),r=!0,w(b()),u()},Math.max(0,h-v))})),!l&&!i&&(i=setTimeout(()=>r=!0,h)),r=!1,c)}}function p1(e=hc){const t=W(!0);function l(){t.value=!1}function n(){t.value=!0}const a=(...i)=>{t.value&&e(...i)};return{isActive:Yt(t),pause:l,resume:n,eventFilter:a}}function h1(...e){if(e.length!==1)return xl(...e);const t=e[0];return typeof t=="function"?Yt(Cd(()=>({get:t,set:Xl}))):W(t)}function v1(e,t=200,l=!1,n=!0,a=!1){return pc(d1(t,l,n,a),e)}function vc(e,t=!0){Xt()?ke(e):t?e():Tl(e)}function f1(e){Xt()&&an(e)}function g1(e,t,l={}){const{immediate:n=!0}=l,a=W(!1);let i=null;function r(){i&&(clearTimeout(i),i=null)}function o(){a.value=!1,r()}function c(...u){r(),a.value=!0,i=setTimeout(()=>{a.value=!1,i=null,e(...u)},He(t))}return n&&(a.value=!0,cn&&c()),Al(o),{isPending:Yt(a),start:c,stop:o}}function ls(e=!1,t={}){const{truthyValue:l=!0,falsyValue:n=!1}=t,a=Ie(e),i=W(e);function r(o){if(arguments.length)return i.value=o,i.value;{const c=He(l);return i.value=i.value===c?He(n):c,i.value}}return a?r:[i,r]}var ns=Object.getOwnPropertySymbols,m1=Object.prototype.hasOwnProperty,y1=Object.prototype.propertyIsEnumerable,b1=(e,t)=>{var l={};for(var n in e)m1.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&ns)for(var n of ns(e))t.indexOf(n)<0&&y1.call(e,n)&&(l[n]=e[n]);return l};function _1(e,t,l={}){const n=l,{eventFilter:a=hc}=n,i=b1(n,["eventFilter"]);return ce(e,pc(a,t),i)}var k1=Object.defineProperty,w1=Object.defineProperties,E1=Object.getOwnPropertyDescriptors,zn=Object.getOwnPropertySymbols,fc=Object.prototype.hasOwnProperty,gc=Object.prototype.propertyIsEnumerable,as=(e,t,l)=>t in e?k1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,L1=(e,t)=>{for(var l in t||(t={}))fc.call(t,l)&&as(e,l,t[l]);if(zn)for(var l of zn(t))gc.call(t,l)&&as(e,l,t[l]);return e},x1=(e,t)=>w1(e,E1(t)),T1=(e,t)=>{var l={};for(var n in e)fc.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&zn)for(var n of zn(e))t.indexOf(n)<0&&gc.call(e,n)&&(l[n]=e[n]);return l};function A1(e,t,l={}){const n=l,{eventFilter:a}=n,i=T1(n,["eventFilter"]),{eventFilter:r,pause:o,resume:c,isActive:u}=p1(a);return{stop:_1(e,t,x1(L1({},i),{eventFilter:r})),pause:o,resume:c,isActive:u}}function Ft(e){var t;const l=He(e);return(t=l==null?void 0:l.$el)!=null?t:l}const Ht=cn?window:void 0,mc=cn?window.document:void 0,O1=cn?window.navigator:void 0;function $e(...e){let t,l,n,a;if(typeof e[0]=="string"||Array.isArray(e[0])?([l,n,a]=e,t=Ht):[t,l,n,a]=e,!t)return Xl;Array.isArray(l)||(l=[l]),Array.isArray(n)||(n=[n]);const i=[],r=()=>{i.forEach(d=>d()),i.length=0},o=(d,p,h,v)=>(d.addEventListener(p,h,v),()=>d.removeEventListener(p,h,v)),c=ce(()=>[Ft(t),He(a)],([d,p])=>{r(),d&&i.push(...l.flatMap(h=>n.map(v=>o(d,h,v,p))))},{immediate:!0,flush:"post"}),u=()=>{c(),r()};return Al(u),u}function P1(){const e=W(!1);return Xt()&&ke(()=>{e.value=!0}),e}function aa(e){const t=P1();return E(()=>(t.value,!!e()))}function yc(e,t={}){const{window:l=Ht}=t,n=aa(()=>l&&"matchMedia"in l&&typeof l.matchMedia=="function");let a;const i=W(!1),r=u=>{i.value=u.matches},o=()=>{a&&("removeEventListener"in a?a.removeEventListener("change",r):a.removeListener(r))},c=Xs(()=>{n.value&&(o(),a=l.matchMedia(He(e)),"addEventListener"in a?a.addEventListener("change",r):a.addListener(r),i.value=a.matches)});return Al(()=>{c(),o(),a=void 0}),i}function I1(e={}){const{navigator:t=O1,read:l=!1,source:n,copiedDuring:a=1500,legacy:i=!1}=e,r=aa(()=>t&&"clipboard"in t),o=E(()=>r.value||i),c=W(""),u=W(!1),d=g1(()=>u.value=!1,a);function p(){r.value?t.clipboard.readText().then(w=>{c.value=w}):c.value=b()}o.value&&l&&$e(["copy","cut"],p);async function h(w=He(n)){o.value&&w!=null&&(r.value?await t.clipboard.writeText(w):v(w),c.value=w,u.value=!0,d.start())}function v(w){const L=document.createElement("textarea");L.value=w??"",L.style.position="absolute",L.style.opacity="0",document.body.appendChild(L),L.select(),document.execCommand("copy"),L.remove()}function b(){var w,L,m;return(m=(L=(w=document==null?void 0:document.getSelection)==null?void 0:w.call(document))==null?void 0:L.toString())!=null?m:""}return{isSupported:o,text:c,copied:u,copy:h}}const Tn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},An="__vueuse_ssr_handlers__",R1=C1();function C1(){return An in Tn||(Tn[An]=Tn[An]||{}),Tn[An]}function S1(e,t){return R1[e]||t}function D1(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}var M1=Object.defineProperty,is=Object.getOwnPropertySymbols,$1=Object.prototype.hasOwnProperty,V1=Object.prototype.propertyIsEnumerable,rs=(e,t,l)=>t in e?M1(e,t,{enumerable:!0,configurable:!0,writable:!0,value:l}):e[t]=l,ss=(e,t)=>{for(var l in t||(t={}))$1.call(t,l)&&rs(e,l,t[l]);if(is)for(var l of is(t))V1.call(t,l)&&rs(e,l,t[l]);return e};const F1={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},os="vueuse-storage";function Ci(e,t,l,n={}){var a;const{flush:i="pre",deep:r=!0,listenToStorageChanges:o=!0,writeDefaults:c=!0,mergeDefaults:u=!1,shallow:d,window:p=Ht,eventFilter:h,onError:v=T=>{console.error(T)}}=n,b=(d?Ge:W)(t);if(!l)try{l=S1("getDefaultStorage",()=>{var T;return(T=Ht)==null?void 0:T.localStorage})()}catch(T){v(T)}if(!l)return b;const w=He(t),L=D1(w),m=(a=n.serializer)!=null?a:F1[L],{pause:_,resume:I}=A1(b,()=>R(b.value),{flush:i,deep:r,eventFilter:h});return p&&o&&($e(p,"storage",V),$e(p,os,C)),V(),b;function R(T){try{if(T==null)l.removeItem(e);else{const q=m.write(T),Y=l.getItem(e);Y!==q&&(l.setItem(e,q),p&&p.dispatchEvent(new CustomEvent(os,{detail:{key:e,oldValue:Y,newValue:q,storageArea:l}})))}}catch(q){v(q)}}function B(T){const q=T?T.newValue:l.getItem(e);if(q==null)return c&&w!==null&&l.setItem(e,m.write(w)),w;if(!T&&u){const Y=m.read(q);return typeof u=="function"?u(Y,w):L==="object"&&!Array.isArray(Y)?ss(ss({},w),Y):Y}else return typeof q!="string"?q:m.read(q)}function C(T){V(T.detail)}function V(T){if(!(T&&T.storageArea!==l)){if(T&&T.key==null){b.value=w;return}if(!(T&&T.key!==e)){_();try{b.value=B(T)}catch(q){v(q)}finally{T?Tl(I):I()}}}}}function N1(e){return yc("(prefers-color-scheme: dark)",e)}var cs=Object.getOwnPropertySymbols,j1=Object.prototype.hasOwnProperty,B1=Object.prototype.propertyIsEnumerable,H1=(e,t)=>{var l={};for(var n in e)j1.call(e,n)&&t.indexOf(n)<0&&(l[n]=e[n]);if(e!=null&&cs)for(var n of cs(e))t.indexOf(n)<0&&B1.call(e,n)&&(l[n]=e[n]);return l};function z1(e,t,l={}){const n=l,{window:a=Ht}=n,i=H1(n,["window"]);let r;const o=aa(()=>a&&"ResizeObserver"in a),c=()=>{r&&(r.disconnect(),r=void 0)},u=E(()=>Array.isArray(e)?e.map(h=>Ft(h)):[Ft(e)]),d=ce(u,h=>{if(c(),o.value&&a){r=new ResizeObserver(t);for(const v of h)v&&r.observe(v,i)}},{immediate:!0,flush:"post",deep:!0}),p=()=>{c(),d()};return Al(p),{isSupported:o,stop:p}}function q1(e,t={width:0,height:0},l={}){const{window:n=Ht,box:a="content-box"}=l,i=E(()=>{var c,u;return(u=(c=Ft(e))==null?void 0:c.namespaceURI)==null?void 0:u.includes("svg")}),r=W(t.width),o=W(t.height);return z1(e,([c])=>{const u=a==="border-box"?c.borderBoxSize:a==="content-box"?c.contentBoxSize:c.devicePixelContentBoxSize;if(n&&i.value){const d=Ft(e);if(d){const p=n.getComputedStyle(d);r.value=Number.parseFloat(p.width),o.value=Number.parseFloat(p.height)}}else if(u){const d=Array.isArray(u)?u:[u];r.value=d.reduce((p,{inlineSize:h})=>p+h,0),o.value=d.reduce((p,{blockSize:h})=>p+h,0)}else r.value=c.contentRect.width,o.value=c.contentRect.height},l),ce(()=>Ft(e),c=>{r.value=c?t.width:0,o.value=c?t.height:0}),{width:r,height:o}}const us=["fullscreenchange","webkitfullscreenchange","webkitendfullscreen","mozfullscreenchange","MSFullscreenChange"];function Si(e,t={}){const{document:l=mc,autoExit:n=!1}=t,a=E(()=>{var m;return(m=Ft(e))!=null?m:l==null?void 0:l.querySelector("html")}),i=W(!1),r=E(()=>["requestFullscreen","webkitRequestFullscreen","webkitEnterFullscreen","webkitEnterFullScreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"].find(m=>l&&m in l||a.value&&m in a.value)),o=E(()=>["exitFullscreen","webkitExitFullscreen","webkitExitFullScreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"].find(m=>l&&m in l||a.value&&m in a.value)),c=E(()=>["fullScreen","webkitIsFullScreen","webkitDisplayingFullscreen","mozFullScreen","msFullscreenElement"].find(m=>l&&m in l||a.value&&m in a.value)),u=["fullscreenElement","webkitFullscreenElement","mozFullScreenElement","msFullscreenElement"].find(m=>l&&m in l),d=aa(()=>a.value&&l&&r.value!==void 0&&o.value!==void 0&&c.value!==void 0),p=()=>u?(l==null?void 0:l[u])===a.value:!1,h=()=>{if(c.value){if(l&&l[c.value]!=null)return l[c.value];{const m=a.value;if((m==null?void 0:m[c.value])!=null)return!!m[c.value]}}return!1};async function v(){if(!(!d.value||!i.value)){if(o.value)if((l==null?void 0:l[o.value])!=null)await l[o.value]();else{const m=a.value;(m==null?void 0:m[o.value])!=null&&await m[o.value]()}i.value=!1}}async function b(){if(!d.value||i.value)return;h()&&await v();const m=a.value;r.value&&(m==null?void 0:m[r.value])!=null&&(await m[r.value](),i.value=!0)}async function w(){await(i.value?v():b())}const L=()=>{const m=h();(!m||m&&p())&&(i.value=m)};return $e(l,us,L,!1),$e(()=>Ft(a),us,L,!1),n&&Al(v),{isSupported:d,isFullscreen:i,enter:b,exit:v,toggle:w}}function wa(e,t=Xl,l={}){const{immediate:n=!0,manual:a=!1,type:i="text/javascript",async:r=!0,crossOrigin:o,referrerPolicy:c,noModule:u,defer:d,document:p=mc,attrs:h={}}=l,v=W(null);let b=null;const w=_=>new Promise((I,R)=>{const B=T=>(v.value=T,I(T),T);if(!p){I(!1);return}let C=!1,V=p.querySelector(`script[src="${He(e)}"]`);V?V.hasAttribute("data-loaded")&&B(V):(V=p.createElement("script"),V.type=i,V.async=r,V.src=He(e),d&&(V.defer=d),o&&(V.crossOrigin=o),u&&(V.noModule=u),c&&(V.referrerPolicy=c),Object.entries(h).forEach(([T,q])=>V==null?void 0:V.setAttribute(T,q)),C=!0),V.addEventListener("error",T=>R(T)),V.addEventListener("abort",T=>R(T)),V.addEventListener("load",()=>{V.setAttribute("data-loaded","true"),t(V),B(V)}),C&&(V=p.head.appendChild(V)),_||B(V)}),L=(_=!0)=>(b||(b=w(_)),b),m=()=>{if(!p)return;b=null,v.value&&(v.value=null);const _=p.querySelector(`script[src="${He(e)}"]`);_&&p.head.removeChild(_)};return n&&!a&&vc(L),a||f1(m),{scriptTag:v,load:L,unload:m}}function bc(e){const t=window.getComputedStyle(e);if(t.overflowX==="scroll"||t.overflowY==="scroll"||t.overflowX==="auto"&&e.clientWidth1?!0:(t.preventDefault&&t.preventDefault(),!1)}function _c(e,t=!1){const l=W(t);let n=null,a;ce(h1(e),o=>{if(o){const c=o;a=c.style.overflow,l.value&&(c.style.overflow="hidden")}},{immediate:!0});const i=()=>{const o=He(e);!o||l.value||(ts&&(n=$e(o,"touchmove",c=>{G1(c)},{passive:!1})),o.style.overflow="hidden",l.value=!0)},r=()=>{const o=He(e);!o||!l.value||(ts&&(n==null||n()),o.style.overflow=a,l.value=!1)};return Al(r),E({get(){return l.value},set(o){o?i():r()}})}function U1({window:e=Ht}={}){if(!e)return{x:W(0),y:W(0)};const t=W(e.scrollX),l=W(e.scrollY);return $e(e,"scroll",()=>{t.value=e.scrollX,l.value=e.scrollY},{capture:!1,passive:!0}),{x:t,y:l}}function W1(e={}){const{window:t=Ht,initialWidth:l=Number.POSITIVE_INFINITY,initialHeight:n=Number.POSITIVE_INFINITY,listenOrientation:a=!0,includeScrollbar:i=!0}=e,r=W(l),o=W(n),c=()=>{t&&(i?(r.value=t.innerWidth,o.value=t.innerHeight):(r.value=t.document.documentElement.clientWidth,o.value=t.document.documentElement.clientHeight))};if(c(),vc(c),$e("resize",c,{passive:!0}),a){const u=yc("(orientation: portrait)");ce(u,()=>c())}return{width:r,height:o}}var K1=$({name:"FontIcon",props:{icon:{type:String,default:""},color:{type:String,default:""},size:{type:[String,Number],default:""}},setup(e){const t=E(()=>{const n=["font-icon icon"],a=`fas fa-${e.icon}`;return n.push("fa-fw fa-sm"),n.push(e.icon.includes(" ")?e.icon:a),n}),l=E(()=>{const n={};return e.color&&(n.color=e.color),e.size&&(n["font-size"]=Number.isNaN(Number(e.size))?e.size:`${e.size}px`),gt(n).length?n:null});return()=>e.icon?s("span",{key:e.icon,class:t.value,style:l.value}):null}});const J1="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture",ds=e=>ae(e)?e:`${e}px`,Q1=(e,t=0)=>{const l=Ge(),n=E(()=>ds(it(e.width)||"100%")),a=W("auto"),i=c=>{if(ae(c)){const[u,d]=c.split(":"),p=Number(u)/Number(d);if(!Number.isNaN(p))return p}return typeof c=="number"?c:16/9},r=c=>{const u=it(e.height),d=i(it(e.ratio));return u?ds(u):`${Number(c)/d+it(t)}px`},o=()=>{l.value&&(a.value=r(l.value.clientWidth))};return ke(()=>{o(),Ie(t)&&ce(t,()=>o()),$e("orientationchange",()=>o()),$e("resize",()=>o())}),{el:l,width:n,height:a}},ps="https://player.bilibili.com/player.html";var Y1=$({name:"BiliBili",props:{bvid:{type:String,default:""},aid:{type:String,default:""},cid:{type:String,default:""},title:{type:String,default:"A BiliBili video"},page:{type:[String,Number],default:1},width:{type:[String,Number],default:"100%"},height:{type:[String,Number],default:void 0},ratio:{type:[String,Number],default:16/9},time:{type:[String,Number],default:0},autoplay:Boolean},setup(e){const{el:t,width:l,height:n}=Q1(e),a=W(!1),i=E(()=>{const{aid:r,bvid:o,cid:c,autoplay:u,time:d,page:p}=e;return r&&c?`${ps}?aid=${r}&cid=${c}&t=${d}&autoplay=${u?1:0}&page=${p}`:o?`${ps}?bvid=${o}&t=${d}&autoplay=${u?1:0}`:null});return()=>i.value?[s("div",{class:"bilibili-desc"},s("a",{class:"sr-only",href:i.value},e.title)),s("iframe",{ref:t,src:i.value,title:e.title,class:"bilibili-iframe",allow:J1,style:{width:l.value,height:a.value?n.value:0},onLoad:()=>{a.value=!0}}),a.value?null:s(Yo)]:[]}});const kc=()=>s(de,{name:"back-to-top"},()=>[s("path",{d:"M512 843.2c-36.2 0-66.4-13.6-85.8-21.8-10.8-4.6-22.6 3.6-21.8 15.2l7 102c.4 6.2 7.6 9.4 12.6 5.6l29-22c3.6-2.8 9-1.8 11.4 2l41 64.2c3 4.8 10.2 4.8 13.2 0l41-64.2c2.4-3.8 7.8-4.8 11.4-2l29 22c5 3.8 12.2.6 12.6-5.6l7-102c.8-11.6-11-20-21.8-15.2-19.6 8.2-49.6 21.8-85.8 21.8z"}),s("path",{d:"m795.4 586.2-96-98.2C699.4 172 513 32 513 32S324.8 172 324.8 488l-96 98.2c-3.6 3.6-5.2 9-4.4 14.2L261.2 824c1.8 11.4 14.2 17 23.6 10.8L419 744s41.4 40 94.2 40c52.8 0 92.2-40 92.2-40l134.2 90.8c9.2 6.2 21.6.6 23.6-10.8l37-223.8c.4-5.2-1.2-10.4-4.8-14zM513 384c-34 0-61.4-28.6-61.4-64s27.6-64 61.4-64c34 0 61.4 28.6 61.4 64S547 384 513 384z"})]);kc.displayName="BackToTopIcon";var X1=$({name:"BackToTop",props:{threshold:{type:Number,default:100},noProgress:Boolean},setup(e){const t=Ee(),l=on({"/":{backToTop:"返回顶部"}}),n=Ge(),{height:a}=q1(n),{height:i}=W1(),{y:r}=U1(),o=E(()=>t.value.backToTop!==!1&&r.value>e.threshold),c=E(()=>r.value/(a.value-i.value));return ke(()=>{n.value=document.body}),()=>s(Bt,{name:"fade"},()=>o.value?s("button",{type:"button",class:"vp-back-to-top-button","aria-label":l.value.backToTop,"data-balloon-pos":"left",onClick:()=>{window.scrollTo({top:0,behavior:"smooth"})}},[e.noProgress?null:s("svg",{class:"vp-scroll-progress"},s("circle",{cx:"50%",cy:"50%",style:{"stroke-dasharray":`calc(${Math.PI*c.value*100}% - ${4*Math.PI}px) calc(${Math.PI*100}% - ${4*Math.PI}px)`}})),s(kc)]):null)}});const Z1=dt({enhance:({app:e})=>{ct("FontIcon")||e.component("FontIcon",K1),ct("BiliBili")||e.component("BiliBili",Y1)},setup:()=>{wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/brands.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/solid.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}}),wa("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/js/fontawesome.min.js",()=>{},{attrs:{"data-auto-replace-svg":"nest"}})},rootComponents:[()=>s(X1,{})]});function ev(e,t,l){var n,a,i;t===void 0&&(t=50),l===void 0&&(l={});var r=(n=l.isImmediate)!=null&&n,o=(a=l.callback)!=null&&a,c=l.maxWait,u=Date.now(),d=[];function p(){if(c!==void 0){var v=Date.now()-u;if(v+t>=c)return c-v}return t}var h=function(){var v=[].slice.call(arguments),b=this;return new Promise(function(w,L){var m=r&&i===void 0;if(i!==void 0&&clearTimeout(i),i=setTimeout(function(){if(i=void 0,u=Date.now(),!r){var I=e.apply(b,v);o&&o(I),d.forEach(function(R){return(0,R.resolve)(I)}),d=[]}},p()),m){var _=e.apply(b,v);return o&&o(_),w(_)}d.push({resolve:w,reject:L})})};return h.cancel=function(v){i!==void 0&&clearTimeout(i),d.forEach(function(b){return(0,b.reject)(v)}),d=[]},h}const tv=({headerLinkSelector:e,headerAnchorSelector:t,delay:l,offset:n=5})=>{const a=Ve(),r=ev(()=>{var w,L;const o=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(o-0)h.some(_=>_.hash===m.hash));for(let m=0;m=(((w=_.parentElement)==null?void 0:w.offsetTop)??0)-n,B=!I||o<(((L=I.parentElement)==null?void 0:L.offsetTop)??0)-n;if(!(R&&B))continue;const V=decodeURIComponent(a.currentRoute.value.hash),T=decodeURIComponent(_.hash);if(V===T)return;if(p){for(let q=m+1;q{window.addEventListener("scroll",r)}),Yn(()=>{window.removeEventListener("scroll",r)})},hs=async(e,t)=>{const{scrollBehavior:l}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=l)},lv=".vp-sidebar-link, .toc-link",nv=".header-anchor",av=200,iv=5,rv=dt({setup(){tv({headerLinkSelector:lv,headerAnchorSelector:nv,delay:av,offset:iv})}});let wc=()=>null;const Ec=Symbol(""),sv=e=>{wc=e},ov=()=>me(Ec),cv=e=>{e.provide(Ec,wc)};var uv=$({name:"AutoCatalog",props:{base:{type:String,default:""},level:{type:Number,default:3},index:Boolean},setup(e){const t=ov(),l=on({"/":{title:"目录",empty:"暂无目录"}}),n=ue(),a=Ve(),i=Vo(),r=u=>{const d=u.I;return typeof d>"u"||d},o=()=>{const u=e.base||n.value.path.replace(/\/[^/]+$/,"/"),d=a.getRoutes(),p=[];return d.filter(({meta:h,path:v})=>{if(!Ql(v,u)||v===u)return!1;if(u==="/"){const b=gt(i.value.locales).filter(w=>w!=="/");if(v==="/404.html"||b.some(w=>Ql(v,w)))return!1}return(il(v,".html")&&!il(v,"/index.html")||il(v,"/"))&&r(h)}).map(({path:h,meta:v})=>{const b=h.substring(u.length).split("/").length;return{title:v.t||"",icon:v.i,base:h.replace(/\/[^/]+\/?$/,"/"),order:v.O||null,level:il(h,"/")?b-1:b,path:h}}).filter(({title:h,level:v})=>h&&v<=e.level).sort(({title:h,level:v,path:b,order:w},{title:L,level:m,path:_,order:I})=>v-m||(il(b,"/index.html")?-1:il(_,"/index.html")?1:w===null?I===null?h.localeCompare(L):I:I===null?w:w>0?I>0?w-I:-1:I<0?w-I:1)).forEach(h=>{var v;const{base:b,level:w}=h;switch(w){case 1:p.push(h);break;case 2:{const L=p.find(m=>m.path===b);L&&(L.children??(L.children=[])).push(h);break}default:{const L=p.find(m=>m.path===b.replace(/\/[^/]+\/$/,"/"));if(L){const m=(v=L.children)==null?void 0:v.find(_=>_.path===b);m&&(m.children??(m.children=[])).push(h)}}}}),p},c=E(()=>o());return()=>s("div",{class:"vp-catalog"},[s("h2",{class:"vp-catalog-main-title"},l.value.title),c.value.length?c.value.map(({children:u=[],icon:d,path:p,title:h},v)=>[s("h3",{id:h,class:["vp-catalog-child-title",{"has-children":u.length}]},[s("a",{href:`#${h}`,class:"header-anchor","aria-hidden":!0},"#"),s(Ce,{class:"vp-catalog-title",to:p},()=>[e.index?`${v+1}.`:null,d&&t?s(t,{icon:d}):null,h||p])]),u.length?s("ul",{class:"vp-catalog-child-catalogs"},u.map(({children:b=[],icon:w,path:L,title:m},_)=>s("li",{class:"vp-child-catalog"},[s("div",{class:["vp-catalog-sub-title",{"has-children":b.length}]},[s("a",{href:`#${m}`,class:"header-anchor"},"#"),s(Ce,{class:"vp-catalog-title",to:L},()=>[e.index?`${v+1}.${_+1}`:null,w&&t?s(t,{icon:w}):null,m||L])]),b.length?s("div",{class:"v-sub-catalogs"},b.map(({icon:I,path:R,title:B},C)=>s(Ce,{class:"vp-sub-catalog",to:R},()=>[e.index?`${v+1}.${_+1}.${C+1}`:null,I&&t?s(t,{icon:I}):null,B||R]))):null]))):null]):s("p",{class:"vp-empty-catalog"},l.value.empty)])}}),dv=dt({enhance:({app:e})=>{cv(e),ct("AutoCatalog",e)||e.component("AutoCatalog",uv)}});const pv=s("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[s("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),s("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),Lc=$({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=mt(),l=E(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>s("span",[pv,s("span",{class:"external-link-icon-sr-only"},l.value.openInNewWindow)])}}),hv={},vv=dt({enhance({app:e}){e.component("ExternalLinkIcon",s(Lc,{locales:hv}))}});/** - * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress - * @license MIT - */const se={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=se.isStarted();e=Ea(e,se.settings.minimum,1),se.status=e===1?null:e;const l=se.render(!t),n=l.querySelector(se.settings.barSelector),a=se.settings.speed,i=se.settings.easing;return l.offsetWidth,fv(r=>{On(n,{transform:"translate3d("+vs(e)+"%,0,0)",transition:"all "+a+"ms "+i}),e===1?(On(l,{transition:"none",opacity:"1"}),l.offsetWidth,setTimeout(function(){On(l,{transition:"all "+a+"ms linear",opacity:"0"}),setTimeout(function(){se.remove(),r()},a)},a)):setTimeout(()=>r(),a)}),se},isStarted:()=>typeof se.status=="number",start:()=>{se.status||se.set(0);const e=()=>{setTimeout(()=>{se.status&&(se.trickle(),e())},se.settings.trickleSpeed)};return se.settings.trickle&&e(),se},done:e=>!e&&!se.status?se:se.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=se.status;return t?(typeof e!="number"&&(e=(1-t)*Ea(Math.random()*t,.1,.95)),t=Ea(t+e,0,.994),se.set(t)):se.start()},trickle:()=>se.inc(Math.random()*se.settings.trickleRate),render:e=>{if(se.isRendered())return document.getElementById("nprogress");fs(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=se.settings.template;const l=t.querySelector(se.settings.barSelector),n=e?"-100":vs(se.status||0),a=document.querySelector(se.settings.parent);return On(l,{transition:"all 0 linear",transform:"translate3d("+n+"%,0,0)"}),a!==document.body&&fs(a,"nprogress-custom-parent"),a==null||a.appendChild(t),t},remove:()=>{gs(document.documentElement,"nprogress-busy"),gs(document.querySelector(se.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&gv(e)},isRendered:()=>!!document.getElementById("nprogress")},Ea=(e,t,l)=>el?l:e,vs=e=>(-1+e)*100,fv=function(){const e=[];function t(){const l=e.shift();l&&l(t)}return function(l){e.push(l),e.length===1&&t()}}(),On=function(){const e=["Webkit","O","Moz","ms"],t={};function l(r){return r.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(o,c){return c.toUpperCase()})}function n(r){const o=document.body.style;if(r in o)return r;let c=e.length;const u=r.charAt(0).toUpperCase()+r.slice(1);let d;for(;c--;)if(d=e[c]+u,d in o)return d;return r}function a(r){return r=l(r),t[r]??(t[r]=n(r))}function i(r,o,c){o=a(o),r.style[o]=c}return function(r,o){for(const c in o){const u=o[c];u!==void 0&&Object.prototype.hasOwnProperty.call(o,c)&&i(r,c,u)}}}(),xc=(e,t)=>(typeof e=="string"?e:Di(e)).indexOf(" "+t+" ")>=0,fs=(e,t)=>{const l=Di(e),n=l+t;xc(l,t)||(e.className=n.substring(1))},gs=(e,t)=>{const l=Di(e);if(!xc(e,t))return;const n=l.replace(" "+t+" "," ");e.className=n.substring(1,n.length-1)},Di=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),gv=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const mv=()=>{ke(()=>{const e=Ve(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(l=>{t.has(l.path)||se.start()}),e.afterEach(l=>{t.add(l.path),se.done()})})},yv=dt({setup(){mv()}}),bv=JSON.parse('{"encrypt":{},"author":{"name":"levy"},"pageInfo":["Date","Tag"],"logo":"/logo.png","repo":"levy9527/blog","docsDir":"src","displayFooter":true,"editLink":false,"darkmode":"disable","blog":{"description":"Javascript/Java/Python\\n都能撸的“全干工程师”","avatar":"https://avatars.githubusercontent.com/u/9384365?v=4","roundAvatar":true,"intro":"/about.html","medias":{"GitHub":"https://github.com/levy9527","BiliBili":"https://search.bilibili.com/video?keyword=levydotvip&from_source=webtop_search","Email":"mailto:info@897895407@qq.com","Rss":"https://levy.vip/rss.xml"}},"locales":{"/":{"lang":"zh-CN","navbarLocales":{"langName":"简体中文","selectLangAriaLabel":"选择语言"},"metaLocales":{"author":"作者","date":"写作日期","origin":"原创","views":"访问量","category":"分类","tag":"标签","readingTime":"阅读时间","words":"字数","toc":"此页内容","prev":"上一页","next":"下一页","lastUpdated":"上次编辑于","contributors":"贡献者","editLink":"编辑此页","print":"打印"},"blogLocales":{"article":"文章","articleList":"文章列表","category":"分类","tag":"标签","timeline":"时间轴","timelineTitle":"昨日不在","all":"全部","intro":"个人介绍","star":"收藏"},"paginationLocales":{"prev":"上一页","next":"下一页","navigate":"跳转到","action":"前往","errorText":"请输入 1 到 $page 之前的页码!"},"outlookLocales":{"themeColor":"主题色","darkmode":"外观","fullscreen":"全屏"},"routeLocales":{"skipToContent":"跳至主要內容","notFoundTitle":"页面不存在","notFoundMsg":["这里什么也没有","我们是怎么来到这儿的?","这 是 四 零 四 !","看起来你访问了一个失效的链接"],"back":"返回上一页","home":"带我回家","openInNewWindow":"Open in new window"},"navbar":[{"text":"首页","icon":"house","link":"/"},{"text":"分类","icon":"list","children":[{"text":"软件研发","icon":"code","link":"/tools/how-to-connect-to-internet"},{"text":"工作日常","icon":"briefcase","link":"/daily"},{"text":"英语学习","icon":"globe","link":"/english"}]},"/about"],"sidebar":{"/daily/":"structure","/english":["let-chatgpt-be-your-foreign-language-teacher","how-to-self-evaluate-english-level","learning-7000-words-task-completed","everyone-can-learn-english-1-overview","everyone-can-learn-english-2-pronunciation","everyone-can-learn-english-3-words","everyone-can-learn-english-4-listening-and-speaking","everyone-can-learn-english-5-reading-and-writing","contemporary-college-english-1","contemporary-college-english-2","contemporary-college-english-3","contemporary-college-english-4","contemporary-college-english-5","contemporary-college-english-6"],"/":[{"text":"实用工具","prefix":"/tools/","children":["how-to-connect-to-internet"]},{"text":"Git相关","prefix":"/git/","children":["git-useful-commands","git-definitive-guide-to-merge-code","git-history-two-tricks-in-idea","gitlab-ci","rethinking-git-flow","git-best-pratices","use-command-line-tool-to-manage-gitlab-merge-request"]},{"text":"软件测试","prefix":"/software-testing/","children":["unit-testing-overview","use-postman-for-api-testing","use-RestAssured-for-api-testing","use-pytest-for-regression-testing-in-llm-app","use-jest-for-test-driven-development","use-playwright-for-ui-testing","use-cypress-for-e2e-testing"]},{"text":"DevOps","prefix":"/devops","children":["docker-build-and-push-script","reduce-python-image-size","what-is-the-difference-between-sh-and-bash","about-arm-things-you-need-to-know","common-solutions-of-object-storage-for-static-assets"]},{"text":"MySQL","prefix":"/mysql","children":["mysql-backup-case-study-mysqldump-in-action","mysql-data-migration-case-study-add-auto-increment","mysql-details-you-should-know-when-execute-sql-in-command-line"]},{"text":"Java","prefix":"/java","children":["Resolving-Common-Problems-in-IntelliJ-IDEA","Resolving-Common-Problems-in-Maven.md","using-enum-in-java","which-one-is-better-Boolean-or-boolean","which-one-is-better-forEach-or-map","recommend-practices-for-collections-naming-convention","recommend-practices-for-writing-good-functions","avoid-sending-password-in-plaintext","recommend-practices-for-query-by-date-range","common-practices-for-handling-excel","check-if-name-exists","how-to-convert-snapshot-into-release-jar-without-source-code","why-i-prefer-fastjson-instead-of-jackson","why-is-it-so-hard-to-upgrade-dependencies"]},{"text":"Python","prefix":"/python","children":["add-logging-for-llm-app.md","mr.py","export-mysql-table-into-excel"]},{"text":"前端技术","prefix":"/frontend","children":["old-articles","performance-optimization-in-action"]}]}}}}'),_v=W(bv),Tc=()=>_v,Ac=Symbol(""),kv=()=>{const e=me(Ac);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},wv=(e,t)=>{const{locales:l,...n}=e;return{...n,...l==null?void 0:l[t]}},Ev=dt({enhance({app:e}){const t=Tc(),l=e._context.provides[Ti],n=E(()=>wv(t.value,l.value));e.provide(Ac,n),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return n.value}}})}});const Lv=800,xv=2e3,Tv={"/":{copy:"复制代码",copied:"已复制",hint:"复制成功"}},Av=!1,Ov=['.theme-hope-content div[class*="language-"] pre'],ms=!1,La=new Map,Pv=()=>{const{copy:e}=I1({legacy:!0}),t=on(Tv),l=ue(),n=l1(),a=o=>{if(!o.hasAttribute("copy-code-registered")){const c=document.createElement("button");c.type="button",c.classList.add("copy-code-button"),c.innerHTML='
',c.setAttribute("aria-label",t.value.copy),c.setAttribute("data-copied",t.value.copied),o.parentElement&&o.parentElement.insertBefore(c,o),o.setAttribute("copy-code-registered","")}},i=()=>Tl().then(()=>new Promise(o=>{setTimeout(()=>{Ov.forEach(c=>{document.querySelectorAll(c).forEach(a)}),o()},Lv)})),r=(o,c,u)=>{let{innerText:d=""}=c;/language-(shellscript|shell|bash|sh|zsh)/.test(o.classList.toString())&&(d=d.replace(/^ *(\$|>) /gm,"")),e(d).then(()=>{u.classList.add("copied"),clearTimeout(La.get(u));const p=setTimeout(()=>{u.classList.remove("copied"),u.blur(),La.delete(u)},xv);La.set(u,p)})};ke(()=>{(!n.value||ms)&&i(),$e("click",o=>{const c=o.target;if(c.matches('div[class*="language-"] > button.copy')){const u=c.parentElement,d=c.nextElementSibling;d&&r(u,d,c)}else if(c.matches('div[class*="language-"] div.copy-icon')){const u=c.parentElement,d=u.parentElement,p=u.nextElementSibling;p&&r(d,p,u)}}),ce(()=>l.value.path,()=>{(!n.value||ms)&&i()})})};var Iv=dt({setup:()=>{Pv()}});const Pn=Ci("VUEPRESS_CODE_TAB_STORE",{});var Rv=$({name:"CodeTabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ge([]),a=()=>{e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),e.tabId&&(Pn.value[e.tabId]=e.data[l.value].id)},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>Pn.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>Pn.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-code-tabs"},[s("div",{class:"vp-code-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-code-tab-nav",{active:p}],role:"tab","aria-controls":`codetab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-code-tab",{active:p}],id:`codetab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Oc=({active:e=!1},{slots:t})=>{var l;return s("div",{class:["code-group-item",{active:e}],"aria-selected":e},(l=t.default)==null?void 0:l.call(t))};Oc.displayName="CodeGroupItem";const Cv=$({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const l=W(-1),n=Ge([]),a=(o=l.value)=>{l.value=o{l.value=o>0?o-1:n.value.length-1,n.value[l.value].focus()},r=(o,c)=>{o.key===" "||o.key==="Enter"?(o.preventDefault(),l.value=c):o.key==="ArrowRight"?(o.preventDefault(),a(c)):o.key==="ArrowLeft"&&(o.preventDefault(),i(c))};return()=>{var o;const c=(((o=t.default)==null?void 0:o.call(t))||[]).filter(u=>u.type.name==="CodeGroupItem").map(u=>(u.props===null&&(u.props={}),u));return c.length===0?null:(l.value<0||l.value>c.length-1?(l.value=c.findIndex(u=>"active"in u.props),l.value===-1&&(l.value=0)):c.forEach((u,d)=>{u.props.active=d===l.value}),s("div",{class:"code-group"},[s("div",{class:"code-group-nav"},c.map((u,d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["code-group-nav-tab",{active:p}],"aria-pressed":p,"aria-expanded":p,onClick:()=>{l.value=d},onKeydown:h=>r(h,d)},u.props.title)})),c]))}}});const xa=Ci("VUEPRESS_TAB_STORE",{});var Sv=$({name:"Tabs",props:{active:{type:Number,default:0},data:{type:Array,required:!0},id:{type:String,required:!0},tabId:{type:String,default:""}},slots:Object,setup(e,{slots:t}){const l=W(e.active),n=Ge([]),a=()=>{e.tabId&&(xa.value[e.tabId]=e.data[l.value].id)},i=(u=l.value)=>{l.value=u{l.value=u>0?u-1:n.value.length-1,n.value[l.value].focus()},o=(u,d)=>{u.key===" "||u.key==="Enter"?(u.preventDefault(),l.value=d):u.key==="ArrowRight"?(u.preventDefault(),i()):u.key==="ArrowLeft"&&(u.preventDefault(),r()),a()},c=()=>{if(e.tabId){const u=e.data.findIndex(({id:d})=>xa.value[e.tabId]===d);if(u!==-1)return u}return e.active};return ke(()=>{l.value=c(),ce(()=>xa.value[e.tabId],(u,d)=>{if(e.tabId&&u!==d){const p=e.data.findIndex(({id:h})=>h===u);p!==-1&&(l.value=p)}})}),()=>e.data.length?s("div",{class:"vp-tabs"},[s("div",{class:"vp-tabs-nav",role:"tablist"},e.data.map(({id:u},d)=>{const p=d===l.value;return s("button",{type:"button",ref:h=>{h&&(n.value[d]=h)},class:["vp-tab-nav",{active:p}],role:"tab","aria-controls":`tab-${e.id}-${d}`,"aria-selected":p,onClick:()=>{l.value=d,a()},onKeydown:h=>o(h,d)},t[`title${d}`]({value:u,isActive:p}))})),e.data.map(({id:u},d)=>{const p=d===l.value;return s("div",{class:["vp-tab",{active:p}],id:`tab-${e.id}-${d}`,role:"tabpanel","aria-expanded":p},t[`tab${d}`]({value:u,isActive:p}))})]):null}});const Dv=dt({enhance:({app:e})=>{e.component("CodeTabs",Rv),ct("CodeGroup",e)||e.component("CodeGroup",Cv),ct("CodeGroupItem",e)||e.component("CodeGroupItem",Oc),e.component("Tabs",Sv)},setup:()=>{}});let Mv={};const Pc=Symbol(""),$v=()=>me(Pc),Vv=e=>{e.provide(Pc,Mv)};const Fv=".theme-hope-content :not(a) > img:not([no-view])",Nv={"/":{closeTitle:"关闭",downloadTitle:"下载图片",fullscreenTitle:"切换全屏",zoomTitle:"缩放",arrowPrevTitle:"上一个 (左箭头)",arrowNextTitle:"下一个 (右箭头)"}},jv=800,Bv='
',Hv=e=>ae(e)?Array.from(document.querySelectorAll(e)):e.map(t=>Array.from(document.querySelectorAll(t))).flat(),Ic=e=>new Promise((t,l)=>{e.complete?t({type:"image",element:e,src:e.src,width:e.naturalWidth,height:e.naturalHeight,alt:e.alt,msrc:e.src}):(e.onload=()=>t(Ic(e)),e.onerror=n=>l(n))}),zv=()=>{const{isSupported:e,toggle:t}=Si(),l=$v(),n=on(Nv),a=ue();let i;const r=c=>{c.on("uiRegister",()=>{e&&c.ui.registerElement({name:"fullscreen",order:7,isButton:!0,html:'',onClick:()=>{t()}}),c.ui.registerElement({name:"download",order:8,isButton:!0,tagName:"a",html:{isCustomSVG:!0,inner:'',outlineID:"pswp__icn-download"},onInit:(u,d)=>{u.setAttribute("download",""),u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"),d.on("change",()=>{u.setAttribute("href",d.currSlide.data.src)})}}),c.ui.registerElement({name:"bulletsIndicator",className:"photo-swipe-bullets-indicator",appendTo:"wrapper",onInit:(u,d)=>{const p=[];let h=-1;for(let v=0;v{d.goTo(p.indexOf(w.target))},p.push(b),u.appendChild(b)}d.on("change",()=>{h>=0&&p[h].classList.remove("active"),p[d.currIndex].classList.add("active"),h=d.currIndex})}})})},o=()=>Promise.all([g(()=>import("./photoswipe.esm-5794cde2.js"),[]),Tl().then(()=>new Promise(c=>setTimeout(c,jv)).then(()=>Hv(Fv)))]).then(([{default:c},u])=>{const d=u.map(p=>({html:Bv,element:p,msrc:p.src}));u.forEach((p,h)=>{const v=()=>{i=new c({preloaderDelay:0,showHideAnimationType:"zoom",...n.value,...l,dataSource:d,index:h,closeOnVerticalDrag:!0,wheelToZoom:!1}),r(i),i.addFilter("thumbEl",()=>p),i.addFilter("placeholderSrc",()=>p.src),i.init()};p.style.cursor="zoom-in",p.addEventListener("click",()=>{v()}),p.addEventListener("keypress",({key:b})=>{b==="Enter"&&v()})}),u.forEach((p,h)=>{Ic(p).then(v=>{d.splice(h,1,v),i==null||i.refreshSlideContent(h)})})});ke(()=>{$e("wheel",()=>{i==null||i.close()}),o(),ce(()=>a.value.path,()=>o())})};var qv=dt({enhance:({app:e})=>{Vv(e)},setup:()=>{zv()}});const Rc=()=>{const e=ue();return E(()=>e.value.readingTime??null)},Ga=typeof{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}}>"u"?null:{"/":{word:"约 $word 字",less1Minute:"小于 1 分钟",time:"大约 $time 分钟"}},Cc=(e,t)=>{const{minutes:l,words:n}=e,{less1Minute:a,word:i,time:r}=t;return{time:l<1?a:r.replace("$time",Math.round(l).toString()),words:i.replace("$word",n.toString())}},ys={words:"",time:""},Sc=()=>Ga?on(Ga):E(()=>null),Gv=()=>{if(typeof Ga>"u")return E(()=>ys);const e=Rc(),t=Sc();return E(()=>e.value&&t.value?Cc(e.value,t.value):ys)},el=()=>Tc(),ie=()=>kv(),Ol=()=>E(()=>!!el().value.pure);var Ta=$({name:"EmptyComponent",setup:()=>()=>null});const Uv="719px",Wv="1440px",Kv="false",Mi={mobileBreakPoint:Uv,pcBreakPoint:Wv,enableThemeColor:Kv},$i={"/daily/":["testing-environments-should-be-consistent-with-production-environments","copy-code-may-not-be-guilty","you-dont-need-to-add-tenant_id-to-every-table","reflections-on-a-speech-by-cto-of-microsoft-china","iteration-retrospective-of-sanyuan","leverage-ai-to-boost-coding-productivity","claude-ai-in-action-extract-info-from-html","beyond-utf8-do-you-know-utf8mb4-and-collation","use-claude2-instead-of-chatgpt","vim-creator-pass-away","dont-try-to-argue-with-a-sb","a-vuepress2-entertaining-video","a-warning-from-navicat","things-I-have-to-vent-about-vue"]},Dc=e=>{const{icon:t="",color:l,size:n}=e,a={};return l&&(a.color=l),n&&(a.height=Number.isNaN(Number(n))?n:`${n}px`),Zt(t)?s("img",{class:"icon",src:t,"no-view":"",style:a}):na(t)?s("img",{class:"icon",src:xe(t),"no-view":"",style:a}):s(Je("FontIcon"),e)};Dc.displayName="HopeIcon";var Ne=Dc,be=(e=>(e.type="y",e.title="t",e.shortTitle="s",e.icon="i",e.author="a",e.date="d",e.localizedDate="l",e.category="c",e.tag="g",e.isEncrypted="n",e.isOriginal="o",e.readingTime="r",e.excerpt="e",e.sticky="u",e.cover="v",e.index="I",e.order="O",e))(be||{}),Mc=(e=>(e.article="a",e.home="h",e.slide="s",e.page="p",e))(Mc||{});const pl=(e,t,l=!1)=>{const n=encodeURI(t);let a=_l(e,tc(n));a.name==="404"&&(a=_l(e,n));const{fullPath:i,meta:r,name:o}=a;return{text:!l&&r[be.shortTitle]?r[be.shortTitle]:r[be.title]||t,link:o==="404"?t:i,...r[be.icon]?{icon:r[be.icon]}:{}}},un=()=>{const e=Ve(),t=yt();return l=>{if(l)if(na(l))t.path!==l&&e.push(l);else if(Zt(l)||Oo(l))window&&window.open(l);else{const n=t.path.slice(0,t.path.lastIndexOf("/"));e.push(`${n}/${encodeURI(l)}`)}}},$c=()=>{const e=ie(),t=Ee();return E(()=>{const{author:l}=t.value;return l?Yl(l):l===!1?[]:Yl(e.value.author,!1)})},Jv=()=>{const e=Ee();return E(()=>nc(e.value.category).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("categoryMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Qv=()=>{const e=Ee();return E(()=>ac(e.value.tag).map(t=>{var l,n;return{name:t,path:((n=(l=me(Symbol.for("tagMap")))==null?void 0:l.value.map[t])==null?void 0:n.path)||""}}))},Yv=()=>{const e=Ee(),t=ue();return E(()=>{const l=Pi(e.value.date);if(l)return l;const{createdTime:n}=t.value.git||{};return n?new Date(n):null})},Xv=()=>{const e=ie(),t=ue(),l=Ee(),n=$c(),a=Jv(),i=Qv(),r=Yv(),o=Rc(),c=Gv(),u=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value.localizedDate,tag:i.value,isOriginal:l.value.isOriginal||!1,readingTime:o.value,readingTimeLocale:c.value,pageview:"pageview"in l.value?l.value.pageview:!0})),d=E(()=>"pageInfo"in l.value?l.value.pageInfo:"pageInfo"in e.value?e.value.pageInfo:null);return{info:u,items:d}},{mobileBreakPoint:Zv,pcBreakPoint:ef}=Mi,bs=e=>e.endsWith("px")?Number(e.slice(0,-2)):null,dn=()=>{const e=W(!1),t=W(!1),l=()=>{e.value=window.innerWidth<=(bs(Zv)??719),t.value=window.innerWidth>=(bs(ef)??1440)};return ke(()=>{l(),$e("resize",l,!1),$e("orientationchange",l,!1)}),{isMobile:e,isPC:t}},Vc=Symbol(""),pn=()=>{const e=me(Vc);if(!e)throw new Error("useDarkmode() is called without provider.");return e},tf=e=>{const t=el(),l=N1(),n=Ci("vuepress-theme-hope-scheme","auto"),a=E(()=>t.value.darkmode||"switch"),i=E(()=>{const o=a.value;return o==="disable"?!1:o==="enable"?!0:o==="auto"?l.value:o==="toggle"?n.value==="dark":n.value==="dark"||n.value==="auto"&&l.value}),r=E(()=>{const o=a.value;return o==="switch"||o==="toggle"});e.provide(Vc,{canToggle:r,config:a,isDarkmode:i,status:n}),Object.defineProperties(e.config.globalProperties,{$isDarkmode:{get:()=>i.value}})},lf=()=>{const{isDarkmode:e}=pn(),t=(l=e.value)=>document.documentElement.setAttribute("data-theme",l?"dark":"light");ke(()=>{ce(e,t,{immediate:!0})})};var ze=$({name:"AutoLink",inheritAttrs:!1,props:{config:{type:Object,required:!0},exact:Boolean,noExternalLinkIcon:Boolean},emits:["focusout"],slots:Object,setup(e,{attrs:t,emit:l,slots:n}){const a=yt(),i=Vo(),r=xl(e,"config"),o=E(()=>Zt(r.value.link)),c=E(()=>Oo(r.value.link)||P0(r.value.link)),u=E(()=>c.value?void 0:r.value.target||(o.value?"_blank":void 0)),d=E(()=>u.value==="_blank"),p=E(()=>!o.value&&!c.value&&!d.value),h=E(()=>c.value?void 0:r.value.rel||(d.value?"noopener noreferrer":void 0)),v=E(()=>r.value.ariaLabel||r.value.text),b=E(()=>{if(e.exact)return!1;const L=gt(i.value.locales);return L.length?L.every(m=>m!==r.value.link):r.value.link!=="/"}),w=E(()=>p.value?r.value.activeMatch?new RegExp(r.value.activeMatch).test(a.path):b.value?Ql(a.path,r.value.link):a.path===r.value.link:!1);return()=>{const{before:L,after:m,default:_}=n,{text:I,icon:R,link:B}=r.value;return p.value?s(Ce,{to:B,"aria-label":v.value,...t,class:["nav-link",{active:w.value},t.class],onFocusout:()=>l("focusout")},()=>_?_():[L?L():s(Ne,{icon:R}),I,m==null?void 0:m()]):s("a",{href:B,rel:h.value,target:u.value,"aria-label":v.value,...t,class:["nav-link",t.class],onFocusout:()=>l("focusout")},_?_():[L?L():s(Ne,{icon:R}),I,e.noExternalLinkIcon?null:s(Lc),m==null?void 0:m()])}}});const kl=(e,t,l=!1)=>"activeMatch"in t?new RegExp(t.activeMatch).test(e.path):Ri(e,t.link)?!0:t.children&&!l?t.children.some(n=>kl(e,n)):!1,Fc=(e,t)=>t.type==="group"?t.children.some(l=>l.type==="group"?Fc(e,l):l.type==="page"&&kl(e,l,!0))||"prefix"in t&&Ri(e,t.prefix):!1,Nc=(e,t)=>ae(e.link)?s(ze,{...t,config:e}):s("p",t,[s(Ne,{icon:e.icon}),e.text]),jc=e=>{const t=yt();return e?s("ul",{class:"vp-sidebar-sub-headers"},e.map(l=>{const n=kl(t,l,!0);return s("li",{class:"vp-sidebar-sub-header"},[Nc(l,{class:["vp-sidebar-link","vp-heading",{active:n}]}),jc(l.children)])})):null},Aa=(e="",t="")=>na(t)?t:`${T0(e)}${t}`,nf=(e,t)=>{const l=ue();return{type:"heading",text:e.title,link:`${l.value.path}#${e.slug}`,children:Vi(e.children,t)}},Vi=(e,t)=>t>0?e.map(l=>nf(l,t-1)):[],Bc=e=>{const t=ue();return Vi(t.value.headers,e)},Ua=(e,t,l="")=>{const n=Ve(),a=ue(),i=(r,o=l)=>{var c;const u=ae(r)?pl(n,Aa(o,r)):r.link?{...r,...Bn(r.link)?{}:{link:pl(n,Aa(o,r.link)).link}}:r;if("children"in u){const d=Aa(o,u.prefix),p=u.children==="structure"?$i[d]:u.children;return{type:"group",...u,prefix:d,children:p.map(h=>i(h,d))}}return{type:"page",...u,children:u.link===a.value.path?Vi(((c=a.value.headers[0])==null?void 0:c.level)===1?a.value.headers[0].children:a.value.headers,t):[]}};return e.map(r=>i(r))},af=(e,t)=>{const l=ue(),n=gt(e).sort((a,i)=>i.length-a.length);for(const a of n)if(Ql(decodeURI(l.value.path),a)){const i=e[a];return i?Ua(i==="structure"?$i[a]:i==="heading"?Bc(t):i,t,a):[]}return console.warn(`${l.value.path} is missing sidebar config.`),[]},rf=(e,t)=>{const l=mt();return e===!1?[]:e==="heading"?Bc(t):e==="structure"?Ua($i[l.value],t,l.value):X(e)?Ua(e,t):Li(e)?af(e,t):[]},Hc=Symbol(""),sf=()=>{const e=Ee(),t=ie(),l=E(()=>e.value.home?!1:e.value.sidebar??t.value.sidebar??"structure"),n=E(()=>e.value.headerDepth??t.value.headerDepth??2),a=E(()=>rf(l.value,n.value));ot(Hc,a)},Fi=()=>{const e=me(Hc);if(!e)throw new Error("useSidebarItems() is called without provider.");return e};var of=$({name:"PageFooter",setup(){const e=Ee(),t=ie(),l=$c(),n=E(()=>{const{copyright:r,footer:o}=e.value;return o!==!1&&!!(r||o||t.value.displayFooter)}),a=E(()=>{const{footer:r}=e.value;return r===!1?!1:ae(r)?r:t.value.footer||""}),i=E(()=>"copyright"in e.value?e.value.copyright:"copyright"in t.value?t.value.copyright:l.value.length?`Copyright © ${new Date().getFullYear()} ${l.value[0].name}`:!1);return()=>n.value?s("footer",{class:"vp-footer-wrapper"},[a.value?s("div",{class:"vp-footer",innerHTML:a.value}):null,i.value?s("div",{class:"vp-copyright",innerHTML:i.value}):null]):null}}),cf=$({name:"NavbarDropdownLink",props:{config:{type:Object,required:!0}},slots:Object,setup(e,{slots:t}){const l=ue(),n=xl(e,"config"),a=E(()=>n.value.ariaLabel||n.value.text),i=W(!1);ce(()=>l.value.path,()=>{i.value=!1});const r=o=>{o.detail===0&&(i.value=!i.value)};return()=>{var o;return s("div",{class:["dropdown-wrapper",{open:i.value}]},[s("button",{type:"button",class:"dropdown-title","aria-label":a.value,onClick:r},[((o=t.title)==null?void 0:o.call(t))||s("span",{class:"title"},[s(Ne,{icon:n.value.icon}),e.config.text]),s("span",{class:"arrow"}),s("ul",{class:"nav-dropdown"},n.value.children.map((c,u)=>{const d=u===n.value.children.length-1;return s("li",{class:"dropdown-item"},"children"in c?[s("h4",{class:"dropdown-subtitle"},c.link?s(ze,{config:c,onFocusout:()=>{c.children.length===0&&d&&(i.value=!1)}}):s("span",c.text)),s("ul",{class:"dropdown-subitem-wrapper"},c.children.map((p,h)=>s("li",{class:"dropdown-subitem"},s(ze,{config:p,onFocusout:()=>{h===c.children.length-1&&d&&(i.value=!1)}}))))]:s(ze,{config:c,onFocusout:()=>{d&&(i.value=!1)}}))}))])])}}});const zc=(e,t,l="")=>ae(t)?pl(e,`${l}${t}`):"children"in t?{...t,...t.link&&!Bn(t.link)?pl(e,`${l}${t.link}`):{},children:t.children.map(n=>zc(e,n,`${l}${t.prefix||""}`))}:{...t,link:Bn(t.link)?t.link:pl(e,`${l}${t.link}`).link},qc=()=>{const e=ie(),t=Ve(),l=()=>(e.value.navbar||[]).map(a=>zc(t,a)),n=W(l());return ce(e,()=>{n.value=l()}),n},uf=()=>{const e=ie(),t=E(()=>e.value.repo||null),l=E(()=>t.value?Zh(t.value):null),n=E(()=>t.value?rc(t.value):null),a=E(()=>l.value?e.value.repoLabel??(n.value===null?"Source":n.value):null);return E(()=>!l.value||!a.value||e.value.repoDisplay===!1?null:{type:n.value||"Source",label:a.value,link:l.value})};var df=$({name:"NavScreenDropdown",props:{config:{type:Object,required:!0}},setup(e){const t=ue(),l=xl(e,"config"),n=E(()=>l.value.ariaLabel||l.value.text),a=W(!1);ce(()=>t.value.path,()=>{a.value=!1});const i=(r,o)=>o[o.length-1]===r;return()=>[s("button",{type:"button",class:["nav-screen-dropdown-title",{active:a.value}],"aria-label":n.value,onClick:()=>{a.value=!a.value}},[s("span",{class:"title"},[s(Ne,{icon:l.value.icon}),e.config.text]),s("span",{class:["arrow",a.value?"down":"end"]})]),s("ul",{class:["nav-screen-dropdown",{hide:!a.value}]},l.value.children.map(r=>s("li",{class:"dropdown-item"},"children"in r?[s("h4",{class:"dropdown-subtitle"},r.link?s(ze,{config:r,onFocusout:()=>{i(r,l.value.children)&&r.children.length===0&&(a.value=!1)}}):s("span",r.text)),s("ul",{class:"dropdown-subitem-wrapper"},r.children.map(o=>s("li",{class:"dropdown-subitem"},s(ze,{config:o,onFocusout:()=>{i(o,r.children)&&i(r,l.value.children)&&(a.value=!1)}}))))]:s(ze,{config:r,onFocusout:()=>{i(r,l.value.children)&&(a.value=!1)}}))))]}}),pf=$({name:"NavScreenLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"nav-screen-links"},e.value.map(t=>s("div",{class:"navbar-links-item"},"children"in t?s(df,{config:t}):s(ze,{config:t})))):null}});const Gc=()=>s(de,{name:"dark"},()=>s("path",{d:"M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"}));Gc.displayName="DarkIcon";const Uc=()=>s(de,{name:"light"},()=>s("path",{d:"M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"}));Uc.displayName="LightIcon";const Wc=()=>s(de,{name:"auto"},()=>s("path",{d:"M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"}));Wc.displayName="AutoIcon";const Kc=()=>s(de,{name:"enter-fullscreen"},()=>s("path",{d:"M762.773 90.24h-497.28c-96.106 0-174.4 78.293-174.4 174.4v497.28c0 96.107 78.294 174.4 174.4 174.4h497.28c96.107 0 175.04-78.293 174.4-174.4V264.64c0-96.213-78.186-174.4-174.4-174.4zm-387.2 761.173H215.04c-21.867 0-40.427-17.92-41.067-41.066V649.92c0-22.507 17.92-40.427 40.427-40.427 11.307 0 21.227 4.694 28.48 11.947 7.253 7.253 11.947 17.92 11.947 28.48v62.293l145.28-145.28c15.893-15.893 41.813-15.893 57.706 0 15.894 15.894 15.894 41.814 0 57.707l-145.28 145.28h62.294c22.506 0 40.426 17.92 40.426 40.427s-17.173 41.066-39.68 41.066zM650.24 165.76h160.427c21.866 0 40.426 17.92 41.066 41.067v160.426c0 22.507-17.92 40.427-40.426 40.427-11.307 0-21.227-4.693-28.48-11.947-7.254-7.253-11.947-17.92-11.947-28.48v-62.186L625.6 450.347c-15.893 15.893-41.813 15.893-57.707 0-15.893-15.894-15.893-41.814 0-57.707l145.28-145.28H650.88c-22.507 0-40.427-17.92-40.427-40.427s17.174-41.173 39.787-41.173z"}));Kc.displayName="EnterFullScreenIcon";const Jc=()=>s(de,{name:"cancel-fullscreen"},()=>s("path",{d:"M778.468 78.62H247.922c-102.514 0-186.027 83.513-186.027 186.027V795.08c0 102.514 83.513 186.027 186.027 186.027h530.432c102.514 0 186.71-83.513 186.026-186.027V264.647C964.494 162.02 880.981 78.62 778.468 78.62zM250.88 574.35h171.122c23.324 0 43.122 19.115 43.804 43.805v171.121c0 24.008-19.114 43.122-43.122 43.122-12.06 0-22.641-5.006-30.378-12.743s-12.743-19.115-12.743-30.379V722.83L224.597 877.91c-16.953 16.952-44.6 16.952-61.553 0-16.953-16.954-16.953-44.602 0-61.554L318.009 661.39h-66.446c-24.007 0-43.122-19.114-43.122-43.122 0-24.12 18.432-43.918 42.439-43.918zm521.899-98.873H601.657c-23.325 0-43.122-19.114-43.805-43.804V260.55c0-24.007 19.115-43.122 43.122-43.122 12.06 0 22.642 5.007 30.379 12.743s12.743 19.115 12.743 30.38v66.445l154.965-154.965c16.953-16.953 44.601-16.953 61.554 0 16.953 16.953 16.953 44.6 0 61.554L705.536 388.55h66.446c24.007 0 43.122 19.115 43.122 43.122.114 24.007-18.318 43.804-42.325 43.804z"}));Jc.displayName="CancelFullScreenIcon";const Qc=()=>s(de,{name:"outlook"},()=>[s("path",{d:"M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4 38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32 51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0 102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2 6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4 0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2 9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224 419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4 470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0 22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6 12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128 505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2 16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8 86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4 80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6 6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"})]);Qc.displayName="OutlookIcon";var Yc=$({name:"AppearanceSwitch",setup(){const{config:e,status:t}=pn(),l=()=>{e.value==="switch"?t.value={light:"dark",dark:"auto",auto:"light"}[t.value]:t.value=t.value==="light"?"dark":"light"};return()=>s("button",{type:"button",id:"appearance-switch",onClick:()=>l()},[s(Wc,{style:{display:t.value==="auto"?"block":"none"}}),s(Gc,{style:{display:t.value==="dark"?"block":"none"}}),s(Uc,{style:{display:t.value==="light"?"block":"none"}})])}}),hf=$({name:"AppearanceMode",setup(){const e=ie(),{canToggle:t}=pn(),l=E(()=>e.value.outlookLocales.darkmode);return()=>t.value?s("div",{class:"appearance-wrapper"},[s("label",{class:"appearance-title",for:"appearance-switch"},l.value),s(Yc)]):null}});const Oa="VUEPRESS_THEME_COLOR";var vf=$({name:"ThemeColorPicker",props:{themeColor:{type:Object,required:!0}},setup(e){const t=(l="")=>{const n=document.documentElement.classList,a=gt(e.themeColor);if(!l){localStorage.removeItem(Oa),n.remove(...a);return}n.remove(...a.filter(i=>i!==l)),n.add(l),localStorage.setItem(Oa,l)};return ke(()=>{const l=localStorage.getItem(Oa);l&&t(l)}),()=>s("ul",{id:"theme-color-picker"},[s("li",s("span",{class:"theme-color",onClick:()=>t()})),sn(e.themeColor).map(([l,n])=>s("li",s("span",{style:{background:n},onClick:()=>t(l)})))])}});const hl=Mi.enableThemeColor==="true",ff=hl?Jh(sn(Mi).filter(([e])=>e.startsWith("theme-"))):{};var gf=$({name:"ThemeColor",setup(){const e=ie(),t=E(()=>e.value.outlookLocales.themeColor);return()=>hl?s("div",{class:"theme-color-wrapper"},[s("label",{class:"theme-color-title",for:"theme-color-picker"},t.value),s(vf,{themeColor:ff})]):null}}),Xc=$({name:"ToggleFullScreenButton",setup(){const e=ie(),{isSupported:t,isFullscreen:l,toggle:n}=Si(),a=E(()=>e.value.outlookLocales.fullscreen);return()=>t?s("div",{class:"full-screen-wrapper"},[s("label",{class:"full-screen-title",for:"full-screen-switch"},a.value),s("button",{type:"button",id:"full-screen-switch",class:"full-screen",ariaPressed:l.value,onClick:()=>n()},l.value?s(Jc):s(Kc))]):null}}),Zc=$({name:"OutlookSettings",setup(){const e=el(),t=Ol(),l=E(()=>!t.value&&e.value.fullscreen);return()=>s(Zn,()=>[hl?s(gf):null,s(hf),l.value?s(Xc):null])}}),mf=$({name:"NavScreen",props:{show:Boolean},emits:["close"],slots:Object,setup(e,{emit:t,slots:l}){const n=ue(),{isMobile:a}=dn(),i=Ge(),r=_c(i);return ke(()=>{i.value=document.body,ce(a,o=>{!o&&e.show&&(r.value=!1,t("close"))}),ce(()=>n.value.path,()=>{r.value=!1,t("close")})}),an(()=>{r.value=!1}),()=>s(Bt,{name:"fade",onEnter:()=>{r.value=!0},onAfterLeave:()=>{r.value=!1}},()=>{var o,c;return e.show?s("div",{id:"nav-screen"},s("div",{class:"vp-nav-screen-container"},[(o=l.before)==null?void 0:o.call(l),s(pf),s("div",{class:"vp-outlook-wrapper"},s(Zc)),(c=l.after)==null?void 0:c.call(l)])):null})}}),yf=$({name:"NavbarBrand",setup(){const e=mt(),t=rn(),l=ie(),n=E(()=>l.value.home||e.value),a=E(()=>t.value.title),i=E(()=>l.value.navTitle??a.value),r=E(()=>l.value.logo?xe(l.value.logo):null),o=E(()=>l.value.logoDark?xe(l.value.logoDark):null);return()=>s(Ce,{to:n.value,class:"vp-brand"},()=>[r.value?s("img",{class:["vp-nav-logo",{light:!!o.value}],src:r.value,alt:a.value}):null,o.value?s("img",{class:["vp-nav-logo dark"],src:o.value,alt:a.value}):null,i.value?s("span",{class:["vp-site-name",{"hide-in-pad":r.value&&l.value.hideSiteNameOnMobile!==!1}]},i.value):null])}}),bf=$({name:"NavbarLinks",setup(){const e=qc();return()=>e.value.length?s("nav",{class:"vp-nav-links"},e.value.map(t=>s("div",{class:"nav-item hide-in-mobile"},"children"in t?s(cf,{config:t}):s(ze,{config:t})))):null}}),_f=$({name:"RepoLink",components:{BitbucketIcon:uc,GiteeIcon:cc,GitHubIcon:sc,GitLabIcon:oc,SourceIcon:dc},setup(){const e=uf();return()=>e.value?s("div",{class:"nav-item vp-repo"},s("a",{class:"vp-repo-link",href:e.value.link,target:"_blank",rel:"noopener noreferrer","aria-label":e.value.label},s(Je(`${e.value.type}Icon`),{style:{width:"1.25rem",height:"1.25rem",verticalAlign:"middle"}}))):null}});const eu=({active:e=!1},{emit:t})=>s("button",{type:"button",class:["vp-toggle-navbar-button",{"is-active":e}],"aria-label":"Toggle Navbar","aria-expanded":e,"aria-controls":"nav-screen",onClick:()=>t("toggle")},s("span",[s("span",{class:"vp-top"}),s("span",{class:"vp-middle"}),s("span",{class:"vp-bottom"})]));eu.displayName="ToggleNavbarButton";var kf=eu;const Wa=(e,{emit:t})=>s("button",{type:"button",class:"vp-toggle-sidebar-button",title:"Toggle Sidebar",onClick:()=>t("toggle")},s("span",{class:"icon"}));Wa.displayName="ToggleSidebarButton",Wa.emits=["toggle"];var wf=Wa,Ef=$({name:"OutlookButton",setup(){const{isSupported:e}=Si(),t=el(),l=Ol(),n=ue(),{canToggle:a}=pn(),i=W(!1),r=E(()=>!l.value&&t.value.fullscreen&&e);return ce(()=>n.value.path,()=>{i.value=!1}),()=>a.value||r.value||hl?s("div",{class:"nav-item hide-in-mobile"},a.value&&!r.value&&!hl?s(Yc):r.value&&!a.value&&!hl?s(Xc):s("button",{type:"button",class:["outlook-button",{open:i.value}],tabindex:"-1","aria-hidden":!0},[s(Qc),s("div",{class:"outlook-dropdown"},s(Zc))])):null}}),Lf=$({name:"NavBar",emits:["toggleSidebar"],slots:Object,setup(e,{emit:t,slots:l}){const n=ie(),{isMobile:a}=dn(),i=W(!1),r=E(()=>{const{navbarAutoHide:d="mobile"}=n.value;return d!=="none"&&(d==="always"||a.value)}),o=E(()=>n.value.navbarLayout||{start:["Brand"],center:["Links"],end:["Language","Repo","Outlook","Search"]}),c={Brand:yf,Language:Ta,Links:bf,Repo:_f,Outlook:Ef,Search:ct("Docsearch")?Je("Docsearch"):ct("SearchBox")?Je("SearchBox"):Ta},u=d=>c[d]??(ct(d)?Je(d):Ta);return()=>{var d,p,h,v,b,w;return[s("header",{id:"navbar",class:["vp-navbar",{"auto-hide":r.value,"hide-icon":n.value.navbarIcon===!1}]},[s("div",{class:"vp-navbar-start"},[s(wf,{onToggle:()=>{i.value&&(i.value=!1),t("toggleSidebar")}}),(d=l.startBefore)==null?void 0:d.call(l),(o.value.start||[]).map(L=>s(u(L))),(p=l.startAfter)==null?void 0:p.call(l)]),s("div",{class:"vp-navbar-center"},[(h=l.centerBefore)==null?void 0:h.call(l),(o.value.center||[]).map(L=>s(u(L))),(v=l.centerAfter)==null?void 0:v.call(l)]),s("div",{class:"vp-navbar-end"},[(b=l.endBefore)==null?void 0:b.call(l),(o.value.end||[]).map(L=>s(u(L))),(w=l.endAfter)==null?void 0:w.call(l),s(kf,{active:i.value,onToggle:()=>{i.value=!i.value}})])]),s(mf,{show:i.value,onClose:()=>{i.value=!1}},{before:()=>{var L;return(L=l.screenTop)==null?void 0:L.call(l)},after:()=>{var L;return(L=l.screenBottom)==null?void 0:L.call(l)}})]}}}),xf=$({name:"SidebarChild",props:{config:{type:Object,required:!0}},setup(e){const t=yt();return()=>[Nc(e.config,{class:["vp-sidebar-link",`vp-sidebar-${e.config.type}`,{active:kl(t,e.config,!0)}],exact:!0}),jc(e.config.children)]}}),Tf=$({name:"SidebarGroup",props:{config:{type:Object,required:!0},open:{type:Boolean,required:!0}},emits:["toggle"],setup(e,{emit:t}){const l=yt(),n=E(()=>kl(l,e.config)),a=E(()=>kl(l,e.config,!0));return()=>{const{collapsible:i,children:r=[],icon:o,prefix:c,link:u,text:d}=e.config;return s("section",{class:"vp-sidebar-group"},[s(i?"button":"p",{class:["vp-sidebar-heading",{clickable:i||u,exact:a.value,active:n.value}],...i?{type:"button",onClick:()=>t("toggle"),onKeydown:p=>{p.key==="Enter"&&t("toggle")}}:{}},[s(Ne,{icon:o}),u?s(ze,{class:"vp-sidebar-title",config:{text:d,link:u},noExternalLinkIcon:!0}):s("span",{class:"vp-sidebar-title"},d),i?s("span",{class:["vp-arrow",e.open?"down":"end"]}):null]),e.open||!i?s(tu,{key:c,config:r}):null])}}}),tu=$({name:"SidebarLinks",props:{config:{type:Array,required:!0}},setup(e){const t=yt(),l=W(-1),n=a=>{l.value=a===l.value?-1:a};return ce(()=>t.path,()=>{const a=e.config.findIndex(i=>Fc(t,i));l.value=a},{immediate:!0,flush:"post"}),()=>s("ul",{class:"vp-sidebar-links"},e.config.map((a,i)=>s("li",a.type==="group"?s(Tf,{config:a,open:i===l.value,onToggle:()=>n(i)}):s(xf,{config:a}))))}}),Af=$({name:"SideBar",slots:Object,setup(e,{slots:t}){const l=yt(),n=ie(),a=Fi(),i=Ge();return ke(()=>{ce(()=>l.hash,r=>{const o=document.querySelector(`.vp-sidebar a.vp-sidebar-link[href="${l.path}${r}"]`);if(!o)return;const{top:c,height:u}=i.value.getBoundingClientRect(),{top:d,height:p}=o.getBoundingClientRect();dc+u&&o.scrollIntoView(!1)})}),()=>{var r,o,c;return s("aside",{ref:i,id:"sidebar",class:["vp-sidebar",{"hide-icon":n.value.sidebarIcon===!1}]},[(r=t.top)==null?void 0:r.call(t),((o=t.default)==null?void 0:o.call(t))||s(tu,{config:a.value}),(c=t.bottom)==null?void 0:c.call(t)])}}}),Ni=$({name:"CommonWrapper",props:{containerClass:{type:String,default:""},noNavbar:Boolean,noSidebar:Boolean,noToc:Boolean},slots:Object,setup(e,{slots:t}){const l=Ve(),n=ue(),a=Ee(),i=ie(),{isMobile:r,isPC:o}=dn(),[c,u]=ls(!1),[d,p]=ls(!1),h=Fi(),v=W(!1),b=E(()=>e.noNavbar||a.value.navbar===!1||i.value.navbar===!1?!1:!!(n.value.title||i.value.logo||i.value.repo||i.value.navbar)),w=E(()=>e.noSidebar?!1:a.value.sidebar!==!1&&h.value.length!==0&&!a.value.home),L=E(()=>e.noToc||a.value.home?!1:a.value.toc||i.value.toc!==!1&&a.value.toc!==!1),m={x:0,y:0},_=C=>{m.x=C.changedTouches[0].clientX,m.y=C.changedTouches[0].clientY},I=C=>{const V=C.changedTouches[0].clientX-m.x,T=C.changedTouches[0].clientY-m.y;Math.abs(V)>Math.abs(T)*1.5&&Math.abs(V)>40&&(V>0&&m.x<=80?u(!0):u(!1))},R=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;let B=0;return $e("scroll",v1(()=>{const C=R();C<=58||C{C||u(!1)}),ke(()=>{const C=_c(document.body);ce(c,T=>{C.value=T});const V=l.afterEach(()=>{u(!1)});an(()=>{C.value=!1,V()})}),()=>s(ct("GlobalEncrypt")?Je("GlobalEncrypt"):Xo,()=>s("div",{class:["theme-container",{"no-navbar":!b.value,"no-sidebar":!w.value&&!(t.sidebar||t.sidebarTop||t.sidebarBottom),"has-toc":L.value,"hide-navbar":v.value,"sidebar-collapsed":!r.value&&!o.value&&d.value,"sidebar-open":r.value&&c.value},e.containerClass,a.value.containerClass||""],onTouchStart:_,onTouchEnd:I},[b.value?s(Lf,{onToggleSidebar:()=>u()},{startBefore:()=>{var C;return(C=t.navbarStartBefore)==null?void 0:C.call(t)},startAfter:()=>{var C;return(C=t.navbarStartAfter)==null?void 0:C.call(t)},centerBefore:()=>{var C;return(C=t.navbarCenterBefore)==null?void 0:C.call(t)},centerAfter:()=>{var C;return(C=t.navbarCenterAfter)==null?void 0:C.call(t)},endBefore:()=>{var C;return(C=t.navbarEndBefore)==null?void 0:C.call(t)},endAfter:()=>{var C;return(C=t.navbarEndAfter)==null?void 0:C.call(t)},screenTop:()=>{var C;return(C=t.navScreenTop)==null?void 0:C.call(t)},screenBottom:()=>{var C;return(C=t.navScreenBottom)==null?void 0:C.call(t)}}):null,s(Bt,{name:"fade"},()=>c.value?s("div",{class:"vp-sidebar-mask",onClick:()=>u(!1)}):null),s(Bt,{name:"fade"},()=>r.value?null:s("div",{class:"toggle-sidebar-wrapper",onClick:()=>p()},s("span",{class:["arrow",d.value?"end":"start"]}))),s(Af,{},{...t.sidebar?{default:()=>t.sidebar()}:{},top:()=>{var C;return(C=t.sidebarTop)==null?void 0:C.call(t)},bottom:()=>{var C;return(C=t.sidebarBottom)==null?void 0:C.call(t)}}),t.default(),s(of)]))}}),pe=$({name:"DropTransition",props:{type:{type:String,default:"single"},delay:{type:Number,default:0},duration:{type:Number,default:.25},appear:Boolean},slots:Object,setup(e,{slots:t}){const l=a=>{a.style.transition=`transform ${e.duration}s ease-in-out ${e.delay}s, opacity ${e.duration}s ease-in-out ${e.delay}s`,a.style.transform="translateY(-20px)",a.style.opacity="0"},n=a=>{a.style.transform="translateY(0)",a.style.opacity="1"};return()=>s(e.type==="single"?Bt:h0,{name:"drop",appear:e.appear,onAppear:l,onAfterAppear:n,onEnter:l,onAfterEnter:n,onBeforeLeave:l},()=>t.default())}});const Ka=({custom:e})=>s(No,{class:["theme-hope-content",{custom:e}]});Ka.displayName="MarkdownContent",Ka.props={custom:Boolean};var ji=Ka;const lu=()=>s(de,{name:"author"},()=>s("path",{d:"M649.6 633.6c86.4-48 147.2-144 147.2-249.6 0-160-128-288-288-288s-288 128-288 288c0 108.8 57.6 201.6 147.2 249.6-121.6 48-214.4 153.6-240 288-3.2 9.6 0 19.2 6.4 25.6 3.2 9.6 12.8 12.8 22.4 12.8h704c9.6 0 19.2-3.2 25.6-12.8 6.4-6.4 9.6-16 6.4-25.6-25.6-134.4-121.6-240-243.2-288z"}));lu.displayName="AuthorIcon";const nu=()=>s(de,{name:"calendar"},()=>s("path",{d:"M716.4 110.137c0-18.753-14.72-33.473-33.472-33.473-18.753 0-33.473 14.72-33.473 33.473v33.473h66.993v-33.473zm-334.87 0c0-18.753-14.72-33.473-33.473-33.473s-33.52 14.72-33.52 33.473v33.473h66.993v-33.473zm468.81 33.52H716.4v100.465c0 18.753-14.72 33.473-33.472 33.473a33.145 33.145 0 01-33.473-33.473V143.657H381.53v100.465c0 18.753-14.72 33.473-33.473 33.473a33.145 33.145 0 01-33.473-33.473V143.657H180.6A134.314 134.314 0 0046.66 277.595v535.756A134.314 134.314 0 00180.6 947.289h669.74a134.36 134.36 0 00133.94-133.938V277.595a134.314 134.314 0 00-133.94-133.938zm33.473 267.877H147.126a33.145 33.145 0 01-33.473-33.473c0-18.752 14.72-33.473 33.473-33.473h736.687c18.752 0 33.472 14.72 33.472 33.473a33.145 33.145 0 01-33.472 33.473z"}));nu.displayName="CalendarIcon";const au=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));au.displayName="CategoryIcon";const iu=()=>s(de,{name:"print"},()=>s("path",{d:"M819.2 364.8h-44.8V128c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v236.8h-44.8C145.067 364.8 96 413.867 96 473.6v192c0 59.733 49.067 108.8 108.8 108.8h44.8V896c0 17.067 14.933 32 32 32h460.8c17.067 0 32-14.933 32-32V774.4h44.8c59.733 0 108.8-49.067 108.8-108.8v-192c0-59.733-49.067-108.8-108.8-108.8zM313.6 160h396.8v204.8H313.6V160zm396.8 704H313.6V620.8h396.8V864zM864 665.6c0 25.6-19.2 44.8-44.8 44.8h-44.8V588.8c0-17.067-14.933-32-32-32H281.6c-17.067 0-32 14.933-32 32v121.6h-44.8c-25.6 0-44.8-19.2-44.8-44.8v-192c0-25.6 19.2-44.8 44.8-44.8h614.4c25.6 0 44.8 19.2 44.8 44.8v192z"}));iu.displayName="PrintIcon";const ru=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));ru.displayName="TagIcon";const su=()=>s(de,{name:"timer"},()=>s("path",{d:"M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"}));su.displayName="TimerIcon";const ou=()=>s(de,{name:"word"},()=>[s("path",{d:"M518.217 432.64V73.143A73.143 73.143 0 01603.43 1.097a512 512 0 01419.474 419.474 73.143 73.143 0 01-72.046 85.212H591.36a73.143 73.143 0 01-73.143-73.143z"}),s("path",{d:"M493.714 566.857h340.297a73.143 73.143 0 0173.143 85.577A457.143 457.143 0 11371.566 117.76a73.143 73.143 0 0185.577 73.143v339.383a36.571 36.571 0 0036.571 36.571z"})]);ou.displayName="WordIcon";const zt=()=>{const e=ie();return E(()=>e.value.metaLocales)};var Of=$({name:"AuthorInfo",inheritAttrs:!1,props:{author:{type:Array,required:!0},pure:Boolean},setup(e){const t=zt();return()=>e.author.length?s("span",{class:"page-author-info","aria-label":`${t.value.author}${e.pure?"":"🖊"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(lu),s("span",e.author.map(l=>l.url?s("a",{class:"page-author-item",href:l.url,target:"_blank",rel:"noopener noreferrer"},l.name):s("span",{class:"page-author-item"},l.name))),s("span",{property:"author",content:e.author.map(l=>l.name).join(", ")})]):null}}),Pf=$({name:"CategoryInfo",inheritAttrs:!1,props:{category:{type:Array,required:!0},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=zt(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.category.length?s("span",{class:"page-category-info","aria-label":`${n.value.category}${e.pure?"":"🌈"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(au),e.category.map(({name:i,path:r})=>s("span",{class:["page-category-item",{[`category${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"articleSection",content:e.category.map(({name:i})=>i).join(",")})]):null}}),If=$({name:"DateInfo",inheritAttrs:!1,props:{date:{type:Object,default:null},localizedDate:{type:String,default:""},pure:Boolean},setup(e){const t=Mo(),l=zt();return()=>e.date?s("span",{class:"page-date-info","aria-label":`${l.value.date}${e.pure?"":"📅"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(nu),s("span",s(Zn,()=>e.localizedDate||e.date.toLocaleDateString(t.value))),s("meta",{property:"datePublished",content:e.date.toISOString()||""})]):null}}),Rf=$({name:"OriginalInfo",inheritAttrs:!1,props:{isOriginal:Boolean},setup(e){const t=zt();return()=>e.isOriginal?s("span",{class:"page-original-info"},t.value.origin):null}}),Cf=$({name:"ReadingTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=zt(),l=E(()=>{if(!e.readingTime)return null;const{minutes:n}=e.readingTime;return n<1?"PT1M":`PT${Math.round(n)}M`});return()=>{var n,a;return(n=e.readingTimeLocale)!=null&&n.time?s("span",{class:"page-reading-time-info","aria-label":`${t.value.readingTime}${e.pure?"":"⌛"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(su),s("span",(a=e.readingTimeLocale)==null?void 0:a.time),s("meta",{property:"timeRequired",content:l.value})]):null}}}),Sf=$({name:"TagInfo",inheritAttrs:!1,props:{tag:{type:Array,default:()=>[]},pure:Boolean},setup(e){const t=Ve(),l=ue(),n=zt(),a=(i,r="")=>{r&&l.value.path!==r&&(i.preventDefault(),t.push(r))};return()=>e.tag.length?s("span",{class:"page-tag-info","aria-label":`${n.value.tag}${e.pure?"":"🏷"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ru),e.tag.map(({name:i,path:r})=>s("span",{class:["page-tag-item",{[`tag${la(i,9)}`]:!e.pure,clickable:r}],role:r?"navigation":"",onClick:o=>a(o,r)},i)),s("meta",{property:"keywords",content:e.tag.map(({name:i})=>i).join(",")})]):null}}),Df=$({name:"ReadTimeInfo",inheritAttrs:!1,props:{readingTime:{type:Object,default:()=>null},readingTimeLocale:{type:Object,default:()=>null},pure:Boolean},setup(e){const t=zt();return()=>{var l,n,a;return(l=e.readingTimeLocale)!=null&&l.words?s("span",{class:"page-word-info","aria-label":`${t.value.words}${e.pure?"":"🔠"}`,...e.pure?{}:{"data-balloon-pos":"down"}},[s(ou),s("span",(n=e.readingTimeLocale)==null?void 0:n.words),s("meta",{property:"wordCount",content:(a=e.readingTime)==null?void 0:a.words})]):null}}}),cu=$({name:"PageInfo",components:{AuthorInfo:Of,CategoryInfo:Pf,DateInfo:If,OriginalInfo:Rf,PageViewInfo:()=>null,ReadingTimeInfo:Cf,TagInfo:Sf,WordInfo:Df},props:{items:{type:[Array,Boolean],default:()=>["Author","Original","Date","PageView","ReadingTime","Category","Tag"]},info:{type:Object,required:!0}},setup(e){const t=Ol();return()=>e.items?s("div",{class:"page-info"},e.items.map(l=>s(Je(`${l}Info`),{...e.info,pure:t.value}))):null}}),Mf=$({name:"PrintButton",setup(){const e=el(),t=ie();return()=>e.value.print===!1?null:s("button",{type:"button",class:"print-button",title:t.value.metaLocales.print,onClick:()=>{window.print()}},s(iu))}});const $f=({title:e,level:t,slug:l})=>s(Ce,{to:`#${l}`,class:["toc-link",`level${t}`]},()=>e),Ja=(e,t)=>{const l=yt();return e.length&&t>0?s("ul",{class:"toc-list"},e.map(n=>{const a=Ja(n.children,t-1);return[s("li",{class:["toc-item",{active:Ri(l,`#${n.slug}`)}]},$f(n)),a?s("li",a):null]})):null};var uu=$({name:"TOC",props:{items:{type:Array,default:()=>[]},headerDepth:{type:Number,default:2}},slots:Object,setup(e,{slots:t}){const l=yt(),n=ue(),a=zt(),i=Ge(),r=W("-1.7rem"),o=u=>{var d;(d=i.value)==null||d.scrollTo({top:u,behavior:"smooth"})},c=()=>{if(i.value){const u=document.querySelector(".toc-item.active");u?r.value=`${u.getBoundingClientRect().top-i.value.getBoundingClientRect().top+i.value.scrollTop}px`:r.value="-1.7rem"}else r.value="-1.7rem"};return ke(()=>{ce(()=>l.hash,u=>{if(i.value){const d=document.querySelector(`#toc a.toc-link[href$="${u}"]`);if(!d)return;const{top:p,height:h}=i.value.getBoundingClientRect(),{top:v,height:b}=d.getBoundingClientRect();vp+h&&o(i.value.scrollTop+v+b-p-h)}}),ce(()=>l.fullPath,()=>c(),{flush:"post",immediate:!0})}),()=>{var u,d;const p=e.items.length?Ja(e.items,e.headerDepth):n.value.headers?Ja(n.value.headers,e.headerDepth):null;return p?s("div",{class:"toc-place-holder"},[s("aside",{id:"toc"},[(u=t.before)==null?void 0:u.call(t),s("div",{class:"toc-header"},[a.value.toc,s(Mf)]),s("div",{class:"toc-wrapper",ref:i},[p,s("div",{class:"toc-marker",style:{top:r.value}})]),(d=t.after)==null?void 0:d.call(t)])]):null}}}),Bi=$({name:"SkipLink",props:{content:{type:String,default:"main-content"}},setup(e){const t=ue(),l=ie(),n=Ge(),a=({target:i})=>{const r=document.querySelector(i.hash);if(r){const o=()=>{r.removeAttribute("tabindex"),r.removeEventListener("blur",o)};r.setAttribute("tabindex","-1"),r.addEventListener("blur",o),r.focus(),window.scrollTo(0,0)}};return ke(()=>{ce(()=>t.value.path,()=>n.value.focus())}),()=>[s("span",{ref:n,tabindex:"-1"}),s("a",{href:`#${e.content}`,class:"vp-skip-link sr-only",onClick:a},l.value.routeLocales.skipToContent)]}});let Pa=null,In=null;const Vf={wait:()=>Pa,pending:()=>{Pa=new Promise(e=>In=e)},resolve:()=>{In==null||In(),Pa=null,In=null}},du=()=>Vf;var Ff=$({name:"FadeSlideY",slots:Object,setup(e,{slots:t}){const{resolve:l,pending:n}=du();return()=>s(Bt,{name:"fade-slide-y",mode:"out-in",onBeforeEnter:l,onBeforeLeave:n},()=>{var a;return(a=t.default)==null?void 0:a.call(t)})}});const Nf=(e,t)=>{const l=e.replace(t,"/").split("/"),n=[];let a=xi(t);return l.forEach((i,r)=>{r!==l.length-1?(a+=`${i}/`,n.push({link:a,name:i||"Home"})):i!==""&&(a+=i,n.push({link:a,name:i}))}),n},pu=(e,{slots:t})=>{var l,n;const{bgImage:a,bgImageDark:i,bgImageStyle:r,color:o,description:c,image:u,imageDark:d,header:p,features:h=[]}=e;return s("div",{class:"vp-feature-wrapper"},[a?s("div",{class:["vp-feature-bg",{light:i}],style:[{"background-image":`url(${a})`},r]}):null,i?s("div",{class:"vp-feature-bg dark",style:[{"background-image":`url(${i})`},r]}):null,s("div",{class:"vp-feature",style:o?{color:o}:{}},[((l=t.image)==null?void 0:l.call(t,e))||[u?s("img",{class:["vp-feature-image",{light:d}],src:xe(u),alt:p}):null,d?s("img",{class:"vp-feature-image dark",src:xe(d),alt:p}):null],((n=t.info)==null?void 0:n.call(t,e))||[p?s("h2",{class:"vp-feature-header"},p):null,c?s("p",{class:"vp-feature-description",innerHTML:c}):null],h.length?s("div",{class:"vp-features"},h.map(({icon:v,title:b,details:w,link:L})=>{const m=[s("h3",{class:"vp-feature-title"},[s(Ne,{icon:v}),s("span",{innerHTML:b})]),s("p",{class:"vp-feature-details",innerHTML:w})];return L?Bn(L)?s("a",{class:"vp-feature-item link",href:L,role:"navigation","aria-label":b,target:"_blank"},m):s(Ce,{class:"vp-feature-item link",to:L,role:"navigation","aria-label":b},()=>m):s("div",{class:"vp-feature-item"},m)})):null])])};pu.displayName="FeaturePanel";var _s=pu,jf=$({name:"HeroInfo",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=E(()=>l.value.heroFullScreen??!1),i=E(()=>{const{heroText:u,tagline:d}=l.value;return{text:u??n.value.title??"Hello",tagline:d??n.value.description??"",isFullScreen:a.value}}),r=E(()=>{const{heroText:u,heroImage:d,heroImageDark:p,heroAlt:h,heroImageStyle:v}=l.value;return{image:d?xe(d):null,imageDark:p?xe(p):null,heroStyle:v,alt:h||u||"hero image",isFullScreen:a.value}}),o=E(()=>{const{bgImage:u,bgImageDark:d,bgImageStyle:p}=l.value;return{image:Tt(u)?xe(u):null,imageDark:Tt(d)?xe(d):null,bgStyle:p,isFullScreen:a.value}}),c=E(()=>l.value.actions??[]);return()=>{var u,d,p;return s("header",{class:["vp-hero-info-wrapper",{fullscreen:a.value}]},[((u=t.heroBg)==null?void 0:u.call(t,o.value))||[o.value.image?s("div",{class:["vp-hero-mask",{light:o.value.imageDark}],style:[{"background-image":`url(${o.value.image})`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-hero-mask dark",style:[{"background-image":`url(${o.value.imageDark})`},o.value.bgStyle]}):null],s("div",{class:"vp-hero-info"},[((d=t.heroImage)==null?void 0:d.call(t,r.value))||s(pe,{appear:!0,type:"group"},()=>[r.value.image?s("img",{key:"light",class:["vp-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),((p=t.heroInfo)==null?void 0:p.call(t,i.value))??s("div",{class:"vp-hero-infos"},[i.value.text?s(pe,{appear:!0,delay:.04},()=>s("h1",{id:"main-title"},i.value.text)):null,i.value.tagline?s(pe,{appear:!0,delay:.08},()=>s("p",{class:"vp-description",innerHTML:i.value.tagline})):null,c.value.length?s(pe,{appear:!0,delay:.12},()=>s("p",{class:"vp-actions"},c.value.map(h=>s(ze,{class:["vp-action",h.type||"default"],config:h,noExternalLinkIcon:!0})))):null])])])}}});const hu=(e,{slots:t})=>{var l,n,a;const{bgImage:i,bgImageDark:r,bgImageStyle:o,color:c,description:u,image:d,imageDark:p,header:h,highlights:v=[],type:b="un-order"}=e;return s("div",{class:"vp-highlight-wrapper",style:c?{color:c}:{}},[i?s("div",{class:["vp-highlight-bg",{light:r}],style:[{"background-image":`url(${i})`},o]}):null,r?s("div",{class:"vp-highlight-bg dark",style:[{"background-image":`url(${r})`},o]}):null,s("div",{class:"vp-highlight"},[((l=t.image)==null?void 0:l.call(t,e))||[d?s("img",{class:["vp-highlight-image",{light:p}],src:xe(d),alt:h}):null,p?s("img",{class:"vp-highlight-image dark",src:xe(p),alt:h}):null],((n=t.info)==null?void 0:n.call(t,e))||[s("div",{class:"vp-highlight-info-wrapper"},s("div",{class:"vp-highlight-info"},[h?s("h2",{class:"vp-highlight-header",innerHTML:h}):null,u?s("p",{class:"vp-highlight-description",innerHTML:u}):null,((a=t.highlights)==null?void 0:a.call(t,v))||s(b==="order"?"ol":b==="no-order"?"dl":"ul",{class:"vp-highlights"},v.map(({icon:w,title:L,details:m,link:_})=>{const I=[s(b==="no-order"?"dt":"h3",{class:"vp-highlight-title"},[w?s(Ne,{class:"vp-highlight-icon",icon:w}):null,s("span",{innerHTML:L})]),m?s(b==="no-order"?"dd":"p",{class:"vp-highlight-details",innerHTML:m}):null];return s(b==="no-order"?"div":"li",{class:["vp-highlight-item-wrapper",{link:_}]},_?Uh(_)?s("a",{class:"vp-highlight-item link",href:_,role:"navigation","aria-label":L,target:"_blank"},I):s(Ce,{class:"vp-highlight-item link",to:_,role:"navigation","aria-label":L},()=>I):s("div",{class:"vp-highlight-item"},I))}))]))]])])};hu.displayName="HighlightPanel";var Bf=hu,Hf=$({name:"HomePage",slots:Object,setup(e,{slots:t}){const l=Ol(),n=Ee(),a=E(()=>{const{features:r}=n.value;return X(r)?r:null}),i=E(()=>{const{highlights:r}=n.value;return X(r)?r:null});return()=>{var r,o,c,u;return s("main",{id:"main-content",class:["vp-project-home ",{pure:l.value}],"aria-labelledby":n.value.heroText===null?"":"main-title"},[(r=t.top)==null?void 0:r.call(t),s(jf),((o=i.value)==null?void 0:o.map(d=>"features"in d?s(_s,d):s(Bf,d)))||(a.value?s(pe,{appear:!0,delay:.24},()=>s(_s,{features:a.value})):null),(c=t.center)==null?void 0:c.call(t),s(pe,{appear:!0,delay:.32},()=>s(ji)),(u=t.bottom)==null?void 0:u.call(t)])}}}),zf=$({name:"BreadCrumb",setup(){const e=Ve(),t=ue(),l=mt(),n=Ee(),a=ie(),i=Ge([]),r=E(()=>(n.value.breadcrumb||n.value.breadcrumb!==!1&&a.value.breadcrumb!==!1)&&i.value.length>1),o=E(()=>n.value.breadcrumbIcon||n.value.breadcrumbIcon!==!1&&a.value.breadcrumbIcon!==!1),c=()=>{const u=e.getRoutes(),d=Nf(t.value.path,l.value).map(({link:p,name:h})=>{const v=u.find(b=>b.path===p);if(v){const{meta:b,path:w}=_l(e,v.path);return{title:b[be.shortTitle]||b[be.title]||h,icon:b[be.icon],path:w}}return null}).filter(p=>p!==null);d.length>1&&(i.value=d)};return ke(()=>{c(),ce(()=>t.value.path,c)}),()=>s("nav",{class:["vp-breadcrumb",{disable:!r.value}]},r.value?s("ol",{vocab:"https://schema.org/",typeof:"BreadcrumbList"},i.value.map((u,d)=>s("li",{class:{"is-active":i.value.length-1===d},property:"itemListElement",typeof:"ListItem"},[s(Ce,{to:u.path,property:"item",typeof:"WebPage"},()=>[o.value?s(Ne,{icon:u.icon}):null,s("span",{property:"name"},u.title||"Unknown")]),s("meta",{property:"position",content:d+1})]))):[])}});const ks=e=>{const t=Ve();return e===!1?!1:ae(e)?pl(t,e,!0):Li(e)?e:null},Qa=(e,t,l)=>{const n=e.findIndex(a=>a.link===t);if(n!==-1){const a=e[n+l];return a!=null&&a.link?a:null}for(const a of e)if(a.children){const i=Qa(a.children,t,l);if(i)return i}return null};var qf=$({name:"PageNav",setup(){const e=ie(),t=Ee(),l=Fi(),n=ue(),a=un(),i=E(()=>{const o=ks(t.value.prev);return o===!1?null:o||(e.value.prevLink===!1?null:Qa(l.value,n.value.path,-1))}),r=E(()=>{const o=ks(t.value.next);return o===!1?null:o||(e.value.nextLink===!1?null:Qa(l.value,n.value.path,1))});return $e("keydown",o=>{o.altKey&&(o.key==="ArrowRight"?r.value&&(a(r.value.link),o.preventDefault()):o.key==="ArrowLeft"&&i.value&&(a(i.value.link),o.preventDefault()))}),()=>i.value||r.value?s("nav",{class:"vp-page-nav"},[i.value?s(ze,{class:"prev",config:i.value},()=>{var o,c;return[s("div",{class:"hint"},[s("span",{class:"arrow start"}),e.value.metaLocales.prev]),s("div",{class:"link"},[s(Ne,{icon:(o=i.value)==null?void 0:o.icon}),(c=i.value)==null?void 0:c.text])]}):null,r.value?s(ze,{class:"next",config:r.value},()=>{var o,c;return[s("div",{class:"hint"},[e.value.metaLocales.next,s("span",{class:"arrow end"})]),s("div",{class:"link"},[(o=r.value)==null?void 0:o.text,s(Ne,{icon:(c=r.value)==null?void 0:c.icon})])]}):null]):null}});const Gf={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},Uf=({docsRepo:e,docsBranch:t,docsDir:l,filePathRelative:n,editLinkPattern:a})=>{if(!n)return null;const i=rc(e);let r;return a?r=a:i!==null&&(r=Gf[i]),r?r.replace(/:repo/,Zt(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,Po(`${xi(l)}/${n}`)):null},Wf=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{const{repo:n,docsRepo:a=n,docsBranch:i="main",docsDir:r="",editLink:o,editLinkPattern:c=""}=e.value;if(!(l.value.editLink??o??!0)||!a)return null;const u=Uf({docsRepo:a,docsBranch:i,docsDir:r,editLinkPattern:c,filePathRelative:t.value.filePathRelative});return u?{text:e.value.metaLocales.editLink,link:u}:null})},Kf=()=>{const e=rn(),t=ie(),l=ue(),n=Ee();return E(()=>{var a,i;return!(n.value.lastUpdated??t.value.lastUpdated??!0)||!((a=l.value.git)!=null&&a.updatedTime)?null:new Date((i=l.value.git)==null?void 0:i.updatedTime).toLocaleString(e.value.lang)})},Jf=()=>{const e=ie(),t=ue(),l=Ee();return E(()=>{var n;return l.value.contributors??e.value.contributors??!0?((n=t.value.git)==null?void 0:n.contributors)??null:null})};var Qf=$({name:"PageTitle",setup(){const e=ue(),t=Ee(),l=ie(),{info:n,items:a}=Xv();return()=>s("div",{class:"vp-page-title"},[s("h1",[l.value.titleIcon===!1?null:s(Ne,{icon:t.value.icon}),e.value.title]),s(cu,{info:n.value,...a.value===null?{}:{items:a.value}}),s("hr")])}});const vu=()=>s(de,{name:"edit"},()=>[s("path",{d:"M430.818 653.65a60.46 60.46 0 0 1-50.96-93.281l71.69-114.012 7.773-10.365L816.038 80.138A60.46 60.46 0 0 1 859.225 62a60.46 60.46 0 0 1 43.186 18.138l43.186 43.186a60.46 60.46 0 0 1 0 86.373L588.879 565.55l-8.637 8.637-117.466 68.234a60.46 60.46 0 0 1-31.958 11.229z"}),s("path",{d:"M728.802 962H252.891A190.883 190.883 0 0 1 62.008 771.98V296.934a190.883 190.883 0 0 1 190.883-192.61h267.754a60.46 60.46 0 0 1 0 120.92H252.891a69.962 69.962 0 0 0-69.098 69.099V771.98a69.962 69.962 0 0 0 69.098 69.098h475.911A69.962 69.962 0 0 0 797.9 771.98V503.363a60.46 60.46 0 1 1 120.922 0V771.98A190.883 190.883 0 0 1 728.802 962z"})]);vu.displayName="EditIcon";var Yf=$({name:"PageMeta",setup(){const e=ie(),t=Wf(),l=Kf(),n=Jf();return()=>{const{metaLocales:a}=e.value;return s("footer",{class:"page-meta"},[t.value?s("div",{class:"meta-item edit-link"},s(ze,{class:"label",config:t.value},{before:()=>s(vu)})):null,s("div",{class:"meta-item git-info"},[l.value?s("div",{class:"update-time"},[s("span",{class:"label"},`${a.lastUpdated}: `),s(Zn,()=>s("span",{class:"info"},l.value))]):null,n.value&&n.value.length?s("div",{class:"contributors"},[s("span",{class:"label"},`${a.contributors}: `),n.value.map(({email:i,name:r},o)=>[s("span",{class:"contributor",title:`email: ${i}`},r),o!==n.value.length-1?",":""])]):null])])}}}),Xf=$({name:"NormalPage",slots:Object,setup(e,{slots:t}){const l=Ee(),n=ue(),{isDarkmode:a}=pn(),i=ie(),r=E(()=>l.value.toc||l.value.toc!==!1&&i.value.toc!==!1);return()=>s("main",{id:"main-content",class:"vp-page"},s(ct("LocalEncrypt")?Je("LocalEncrypt"):Xo,()=>{var o,c,u,d;return[(o=t.top)==null?void 0:o.call(t),l.value.cover?s("img",{class:"page-cover",src:xe(l.value.cover),alt:n.value.title,"no-view":""}):null,s(zf),s(Qf),r.value?s(uu,{headerDepth:l.value.headerDepth??i.value.headerDepth??2},{before:()=>{var p;return(p=t.tocBefore)==null?void 0:p.call(t)},after:()=>{var p;return(p=t.tocAfter)==null?void 0:p.call(t)}}):null,(c=t.contentBefore)==null?void 0:c.call(t),s(ji),(u=t.contentAfter)==null?void 0:u.call(t),s(Yf),s(qf),ct("CommentService")?s(Je("CommentService"),{darkmode:a.value}):null,(d=t.bottom)==null?void 0:d.call(t)]}))}}),Zf=$({name:"Layout",setup(){const e=el(),t=ie(),l=ue(),n=Ee(),{isMobile:a}=dn(),i=E(()=>{var r,o;return((r=t.value.blog)==null?void 0:r.sidebarDisplay)||((o=e.value.blog)==null?void 0:o.sidebarDisplay)||"mobile"});return()=>[s(Bi),s(Ni,{},{default:()=>n.value.home?s(Hf):s(Ff,()=>s(Xf,{key:l.value.path})),...i.value!=="none"?{navScreenBottom:()=>s(Je("BloggerInfo"))}:{},...!a.value&&i.value==="always"?{sidebar:()=>s(Je("BloggerInfo"))}:{}})]}}),e2=$({name:"NotFoundHint",setup(){const e=ie(),t=()=>{const l=e.value.routeLocales.notFoundMsg;return l[Math.floor(Math.random()*l.length)]};return()=>s("div",{class:"not-found-hint"},[s("p",{class:"error-code"},"404"),s("h1",{class:"error-title"},e.value.routeLocales.notFoundTitle),s("p",{class:"error-hint"},t())])}}),t2=$({name:"NotFound",slots:Object,setup(e,{slots:t}){const l=mt(),n=ie(),{navigate:a}=qa({to:n.value.home??l.value});return()=>[s(Bi),s(Ni,{noSidebar:!0},()=>{var i;return s("main",{id:"main-content",class:"vp-page not-found"},((i=t.default)==null?void 0:i.call(t))||[s(e2),s("div",{class:"actions"},[s("button",{type:"button",class:"action-button",onClick:()=>{window.history.go(-1)}},n.value.routeLocales.back),s("button",{type:"button",class:"action-button",onClick:()=>a()},n.value.routeLocales.home)])])})]}});const l2={GitHub:'',BiliBili:'',Email:'',Rss:''},n2={category:{"/":{path:"/category/",map:{}}},tag:{"/":{path:"/tag/",map:{Daily:{path:"/tag/daily/",keys:["v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-f1efc11c","v-f47f129e","v-3f274907","v-30a50b00","v-0481cc80","v-1e305501","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-386e94f1","v-d767e98e","v-0d6828c2"]},DevOps:{path:"/tag/devops/",keys:["v-3c0c097a","v-2e81d6a4","v-daf302c6","v-3f274907","v-6614b1a1"]},Linux:{path:"/tag/linux/",keys:["v-2e81d6a4","v-daf302c6","v-3f274907"]},Frontend:{path:"/tag/frontend/",keys:["v-30a50b00","v-1e305501","v-7320140c","v-72e84a92","v-6614b1a1"]},S3:{path:"/tag/s3/",keys:["v-6614b1a1"]},OBS:{path:"/tag/obs/",keys:["v-6614b1a1"]},OSS:{path:"/tag/oss/",keys:["v-6614b1a1"]},Python:{path:"/tag/python/",keys:["v-39c09a30","v-3c0c097a","v-7ba1021b","v-de221860","v-966d933e","v-0be1af08","v-071be141","v-5b37b3c6"]},Video:{path:"/tag/video/",keys:["v-5c48d497","v-ca672354","v-7a74360a","v-1aafac08","v-79e139f8","v-404740fa","v-f1efc11c","v-f47f129e","v-daf302c6","v-30a50b00","v-0481cc80","v-1e305501"]},MySQL:{path:"/tag/mysql/",keys:["v-2f6ae09c","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8"]},AI:{path:"/tag/ai/",keys:["v-81a71778","v-7a74360a","v-404740fa","v-221efd1f"]},Emotion:{path:"/tag/emotion/",keys:["v-f47f129e"]},"Working Experience":{path:"/tag/working-experience/",keys:["v-fd7b7f92","v-f47f129e"]},Tool:{path:"/tag/tool/",keys:["v-404740fa","v-17809471"]},Design:{path:"/tag/design/",keys:["v-bd2b5fe8"]},English:{path:"/tag/english/",keys:["v-221efd1f","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-93b316c0","v-971cc7fe"]},Git:{path:"/tag/git/",keys:["v-60021cbc","v-966d933e","v-071be141","v-4008ff77","v-48e494ef","v-0fbf5fdc","v-1e5872c4","v-642eaaea"]},GitLab:{path:"/tag/gitlab/",keys:["v-071be141","v-4008ff77"]},Java:{path:"/tag/java/",keys:["v-538a98d7","v-2f6ae09c","v-f5471cb0","v-b5a78a7a","v-ca672354","v-1aafac08","v-100d1814","v-65b23736","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-386e94f1","v-d767e98e","v-0d6828c2"]},"Node.js":{path:"/tag/node.js/",keys:["v-75616b85","v-0be1af08","v-4008ff77","v-c488ac58","v-efcacba2"]},JavaScript:{path:"/tag/javascript/",keys:["v-538a98d7"]},llm:{path:"/tag/llm/",keys:["v-39c09a30"]},Testing:{path:"/tag/testing/",keys:["v-7ba1021b","v-113531b4","v-65b23736","v-0be1af08","v-c488ac58","v-efcacba2"]},Gitlab:{path:"/tag/gitlab/",keys:["v-7ba1021b"]}}}}},a2={article:{"/":{path:"/article/",keys:["v-39c09a30","v-3c0c097a","v-2e81d6a4","v-7ba1021b","v-de221860","v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-daf302c6","v-3f274907","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-966d933e","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-386e94f1","v-d767e98e","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-0d6828c2","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2","v-6614b1a1"]}},star:{"/":{path:"/star/",keys:[]}},timeline:{"/":{path:"/timeline/",keys:["v-39c09a30","v-3c0c097a","v-2e81d6a4","v-7ba1021b","v-de221860","v-4f919602","v-538a98d7","v-2f6ae09c","v-76f251d2","v-bd2b5fe8","v-75616b85","v-f5471cb0","v-5c48d497","v-fd7b7f92","v-b5a78a7a","v-81a71778","v-ca672354","v-7a74360a","v-1aafac08","v-f297935a","v-143a8bce","v-7e2c7a0c","v-79e139f8","v-100d1814","v-60021cbc","v-404740fa","v-f1efc11c","v-f47f129e","v-daf302c6","v-3f274907","v-30a50b00","v-0481cc80","v-1e305501","v-113531b4","v-65b23736","v-966d933e","v-0be1af08","v-221efd1f","v-071be141","v-5b37b3c6","v-4008ff77","v-6dc18ec6","v-86a15cb6","v-57d9b582","v-2ba0b01e","v-6ed7d996","v-860c51c8","v-89760306","v-8cdfb444","v-d42db13c","v-ae153a4e","v-788d194a","v-58a409d7","v-3ac0474c","v-90496582","v-246f4f17","v-17809471","v-93b316c0","v-386e94f1","v-d767e98e","v-971cc7fe","v-48e494ef","v-0fbf5fdc","v-0d6828c2","v-7320140c","v-c488ac58","v-1e5872c4","v-642eaaea","v-72e84a92","v-efcacba2","v-6614b1a1"]}}},ws=W(n2),fu=(e="")=>{const t=ue(),l=Ve(),n=mt();return E(()=>{var a;const i=e||((a=Ee().value.blog)==null?void 0:a.key)||"";if(!i)return console.warn("useBlogCategory: key not found"),{path:"/",map:{}};const r=l.getRoutes();if(!ws.value[i])throw new Error(`useBlogCategory: key ${i} is invalid`);const o=ws.value[i][n.value],c={path:o.path,map:{}};for(const u in o.map){const d=o.map[u];c.map[u]={path:d.path,items:[]};for(const p of d.keys){const h=r.find(({name:v})=>v===p);if(h){const v=_l(l,h.path);c.map[u].items.push({path:v.path,info:v.meta})}}t.value.path===d.path&&(c.currentItems=c.map[u].items)}return c})},Es=W(a2),ia=(e="")=>{const t=Ve(),l=mt();return E(()=>{var n;const a=e||((n=Ee().value.blog)==null?void 0:n.key)||"";if(!a)return console.warn("useBlogType: key not found"),{path:"/",items:[]};if(!Es.value[a])throw new Error(`useBlogType: key ${e} is invalid`);const i=t.getRoutes(),r=Es.value[a][l.value],o={path:r.path,items:[]};for(const c of r.keys){const u=i.find(({name:d})=>d===c);if(u){const d=_l(t,u.path);o.items.push({path:d.path,info:d.meta})}}return o})};const i2="/assets/hero-197a9d2d.jpg",gu=Symbol.for("categoryMap"),hn=()=>{const e=me(gu);if(!e)throw new Error("useCategoryMap() is called without provider.");return e},r2=()=>{const e=fu("category");ot(gu,e)},vn=()=>{const e=el(),t=ie();return E(()=>({...e.value.blog,...t.value.blog}))},mu=Symbol.for("tagMap"),fn=()=>{const e=me(mu);if(!e)throw new Error("useTagMap() is called without provider.");return e},s2=()=>{const e=fu("tag");ot(mu,e)},o2=e=>{const t=ie();return E(()=>{const{[be.author]:l}=e.value;return l?Yl(l):l===!1?[]:Yl(t.value.author,!1)})},c2=e=>{const t=hn();return E(()=>nc(e.value[be.category]).map(l=>({name:l,path:t.value.map[l].path})))},u2=e=>{const t=fn();return E(()=>ac(e.value[be.tag]).map(l=>({name:l,path:t.value.map[l].path})))},d2=e=>E(()=>{const{[be.date]:t}=e.value;return Pi(t)}),p2=e=>{const t=xl(e,"info"),l=vn(),n=o2(t),a=c2(t),i=u2(t),r=d2(t),o=Sc(),c=E(()=>({author:n.value,category:a.value,date:r.value,localizedDate:t.value[be.localizedDate]||"",tag:i.value,isOriginal:t.value[be.isOriginal]||!1,readingTime:t.value[be.readingTime]||null,readingTimeLocale:t.value[be.readingTime]&&o.value?Cc(t.value[be.readingTime],o.value):null,pageview:e.path})),u=E(()=>l.value.articleInfo);return{info:c,items:u}},yu=Symbol(""),gn=()=>{const e=me(yu);if(!e)throw new Error("useArticles() is called without provider.");return e},h2=()=>{const e=ia("article");ot(yu,e)},bu=Symbol(""),Hi=()=>{const e=me(bu);if(!e)throw new Error("useStars() is called without provider.");return e},v2=()=>{const e=ia("star");ot(bu,e)},_u=Symbol(""),zi=()=>{const e=me(_u);if(!e)throw new Error("useTimelines() is called without provider.");return e},f2=()=>{const e=ia("timeline"),t=E(()=>{const l=[];return e.value.items.forEach(({info:n,path:a})=>{const i=Pi(n[be.date]),r=i==null?void 0:i.getFullYear(),o=i?i.getMonth()+1:null,c=i==null?void 0:i.getDate();r&&o&&c&&((!l[0]||l[0].year!==r)&&l.unshift({year:r,items:[]}),l[0].items.push({date:`${o}/${c}`,info:n,path:a}))}),{...e.value,config:l.reverse()}});ot(_u,t)},g2=()=>{h2(),r2(),v2(),s2(),f2()};var m2=$({name:"SocialMedia",setup(){const e=vn(),t=Ol(),l=E(()=>{const n=e.value.medias;return n?sn(n).map(([a,i])=>({name:a,icon:l2[a],url:i})):[]});return()=>l.value.length?s("div",{class:"vp-social-medias"},l.value.map(({name:n,icon:a,url:i})=>s("a",{class:"vp-social-media",href:i,rel:"noopener noreferrer",target:"_blank","aria-label":n,...t.value?{}:{"data-balloon-pos":"up"},innerHTML:a}))):null}}),qi=$({name:"BloggerInfo",setup(){const e=vn(),t=rn(),l=ie(),n=gn(),a=hn(),i=fn(),r=zi(),o=un(),c=E(()=>{var h;return e.value.name||((h=Yl(l.value.author)[0])==null?void 0:h.name)||t.value.title}),u=E(()=>e.value.avatar||l.value.logo),d=E(()=>l.value.blogLocales),p=E(()=>e.value.intro);return()=>{const{article:h,category:v,tag:b,timeline:w}=d.value,L=[[n.value.path,n.value.items.length,h],[a.value.path,gt(a.value.map).length,v],[i.value.path,gt(i.value.map).length,b],[r.value.path,r.value.items.length,w]];return s("div",{class:"vp-blogger-info",vocab:"https://schema.org/",typeof:"Person"},[s("div",{class:"vp-blogger",...p.value?{style:{cursor:"pointer"},"aria-label":d.value.intro,"data-balloon-pos":"down",role:"navigation",onClick:()=>o(p.value)}:{}},[u.value?s("img",{class:["vp-blogger-avatar",{round:e.value.roundAvatar}],src:xe(u.value),property:"image",alt:"Blogger Avatar"}):null,c.value?s("div",{class:"vp-blogger-name",property:"name"},c.value):null,e.value.description?s("div",{class:"vp-blogger-description",innerHTML:e.value.description}):null,p.value?s("meta",{property:"url",content:xe(p.value)}):null]),s("div",{class:"vp-blog-counts"},L.map(([m,_,I])=>s(Ce,{class:"vp-blog-count",to:m},()=>[s("div",{class:"count"},_),s("div",I)]))),s(m2)])}}});const Ya=()=>s(de,{name:"category"},()=>s("path",{d:"M148.41 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H148.41c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.311-40.31zM147.556 553.478H429.73c22.263 0 40.311 18.048 40.311 40.31v282.176c0 22.263-18.048 40.312-40.31 40.312H147.555c-22.263 0-40.311-18.049-40.311-40.312V593.79c0-22.263 18.048-40.311 40.31-40.311zM593.927 106.992h282.176c22.263 0 40.31 18.048 40.31 40.31V429.48c0 22.263-18.047 40.31-40.31 40.31H593.927c-22.263 0-40.311-18.047-40.311-40.31V147.302c0-22.263 18.048-40.31 40.31-40.31zM730.22 920.502H623.926c-40.925 0-74.22-33.388-74.22-74.425V623.992c0-41.038 33.387-74.424 74.425-74.424h222.085c41.038 0 74.424 33.226 74.424 74.067v114.233c0 10.244-8.304 18.548-18.547 18.548s-18.548-8.304-18.548-18.548V623.635c0-20.388-16.746-36.974-37.33-36.974H624.13c-20.585 0-37.331 16.747-37.331 37.33v222.086c0 20.585 16.654 37.331 37.126 37.331H730.22c10.243 0 18.547 8.304 18.547 18.547 0 10.244-8.304 18.547-18.547 18.547z"}));Ya.displayName="CategoryIcon";const Xa=()=>s(de,{name:"tag"},()=>s("path",{d:"M939.902 458.563L910.17 144.567c-1.507-16.272-14.465-29.13-30.737-30.737L565.438 84.098h-.402c-3.215 0-5.726 1.005-7.634 2.913l-470.39 470.39a10.004 10.004 0 000 14.164l365.423 365.424c1.909 1.908 4.42 2.913 7.132 2.913s5.223-1.005 7.132-2.913l470.39-470.39c2.01-2.11 3.014-5.023 2.813-8.036zm-240.067-72.121c-35.458 0-64.286-28.828-64.286-64.286s28.828-64.285 64.286-64.285 64.286 28.828 64.286 64.285-28.829 64.286-64.286 64.286z"}));Xa.displayName="TagIcon";const Gi=()=>s(de,{name:"timeline"},()=>s("path",{d:"M511.997 70.568c-243.797 0-441.429 197.633-441.429 441.435 0 243.797 197.632 441.429 441.43 441.429S953.431 755.8 953.431 512.002c0-243.796-197.637-441.434-441.435-441.434zm150.158 609.093-15.605 15.61c-8.621 8.615-22.596 8.615-31.215 0L472.197 552.126c-4.95-4.944-4.34-14.888-4.34-24.677V247.14c0-12.19 9.882-22.07 22.07-22.07h22.07c12.19 0 22.07 9.882 22.07 22.07v273.218l128.088 128.088c8.62 8.62 8.62 22.595 0 31.215zm0 0"}));Gi.displayName="TimelineIcon";const ku=()=>s(de,{name:"slides"},()=>s("path",{d:"M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"}));ku.displayName="SlideIcon";const wu=()=>s(de,{name:"sticky"},()=>[s("path",{d:"m381.3 733.8l-161.9 118c-5.9 4.5-13.2 6.6-20.1 6.6-8.7 0-17.7-3.4-24.3-10-12.2-12.2-13.9-31.3-3.5-45.2l144.5-195.5-113.6-112.9c-11.1-11.1-13.2-28.4-5.5-42 5.5-8.7 52.1-76.4 155.5-51 1.8 0.3 3.5 0.3 5.6 0.7 4.2 0.3 9 0.7 14.2 1.7 21.9 3.5 60.8-13.9 94.5-42.7 32.3-27.5 53.1-59.4 53.1-81.6 0-5.2 0-10.8-0.3-16-0.7-20.8-2.1-52.8 21.5-76.4 28.1-28.1 72.9-30.6 103.9-5.2 0.6 0.3 1 1 1.7 1.7 16.7 16.3 187.5 187.2 189.3 188.9 14.5 14.6 22.9 34.4 22.9 55.3 0 20.8-8 40.2-22.9 54.8-23.7 23.6-56 22.6-77.1 21.6-4.9 0-10.5-0.4-15.7-0.4-20.8 0-45.8 14.6-70.5 41.3-34.3 37.5-55.5 85.8-53.8 107.7 0.7 6.9 2.1 19.1 2.4 20.8 25 101.4-42.7 147.6-50.7 152.8-13.9 8.4-31.6 6.3-42.7-4.8l-112.1-112.2z"})]);wu.displayName="StickyIcon";const qn=()=>s(de,{name:"article"},()=>s("path",{d:"M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"}));qn.displayName="ArticleIcon";const Eu=()=>s(de,{name:"book"},()=>s("path",{d:"M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"}));Eu.displayName="BookIcon";const Lu=()=>s(de,{name:"link"},()=>s("path",{d:"M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"}));Lu.displayName="LinkIcon";const xu=()=>s(de,{name:"project"},()=>s("path",{d:"M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"}));xu.displayName="ProjectIcon";const Tu=()=>s(de,{name:"friend"},()=>s("path",{d:"M860.16 213.333A268.373 268.373 0 0 0 512 186.027a267.52 267.52 0 0 0-348.16 404.48L428.8 855.893a118.613 118.613 0 0 0 166.4 0l264.96-265.386a267.52 267.52 0 0 0 0-377.174zM800 531.627l-264.96 264.96a32.427 32.427 0 0 1-46.08 0L224 530.347a183.04 183.04 0 0 1 0-256 182.187 182.187 0 0 1 256 0 42.667 42.667 0 0 0 60.587 0 182.187 182.187 0 0 1 256 0 183.04 183.04 0 0 1 3.413 256z"}));Tu.displayName="FriendIcon";const Za=()=>s(de,{name:"slide-down"},()=>s("path",{d:"M108.775 312.23c13.553 0 27.106 3.734 39.153 11.806l375.205 250.338 363.641-252.808c32.587-21.624 76.499-12.83 98.123 19.757 21.685 32.467 12.95 76.56-19.576 98.184l-402.854 278.89c-23.733 15.901-54.694 15.962-78.547.12L69.501 442.097c-32.647-21.685-41.441-65.777-19.817-98.304 13.734-20.54 36.201-31.563 59.09-31.563Z"}));Za.displayName="SlideDownIcon";const Au=()=>s("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",class:"empty-icon",viewBox:"0 0 1024 1024",innerHTML:''});Au.displayName="EmptyIcon";const Ou=()=>s(de,{name:"lock"},()=>s("path",{d:"M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"}));Ou.displayName="LockIcon";var y2=$({name:"ArticleItem",props:{info:{type:Object,required:!0},path:{type:String,required:!0}},slots:Object,setup(e,{slots:t}){const l=xl(e,"info"),{info:n,items:a}=p2(e);return()=>{var i,r,o;const{[be.title]:c,[be.type]:u,[be.isEncrypted]:d=!1,[be.cover]:p,[be.excerpt]:h,[be.sticky]:v}=l.value,b=n.value;return s("div",{class:"vp-article-wrapper"},s("article",{class:"vp-article-item",vocab:"https://schema.org/",typeof:"Article"},[((i=t.cover)==null?void 0:i.call(t,{cover:p}))||(p?[s("img",{class:"vp-article-cover",src:xe(p)}),s("meta",{property:"image",content:xe(p)})]:[]),v?s(wu):null,s(Ce,{to:e.path},()=>{var w;return((w=t.title)==null?void 0:w.call(t,{title:c,isEncrypted:d,type:u}))||s("header",{class:"vp-article-title"},[d?s(Ou):null,u===Mc.slide?s(ku):null,s("span",{property:"headline"},c)])}),((r=t.excerpt)==null?void 0:r.call(t,{excerpt:h}))||(h?s("div",{class:"vp-article-excerpt",innerHTML:h}):null),s("hr",{class:"vp-article-hr"}),((o=t.info)==null?void 0:o.call(t,{info:b}))||s(cu,{info:b,...a.value?{items:a.value}:{}})]))}}}),b2=$({name:"Pagination",props:{total:{type:Number,default:10},perPage:{type:Number,default:10},current:{type:Number,default:1}},emits:["updateCurrentPage"],setup(e,{emit:t}){let l;const n=ie(),a=W(""),i=E(()=>n.value.paginationLocales),r=E(()=>Math.ceil(e.total/e.perPage)),o=E(()=>!!r.value&&r.value!==1),c=E(()=>r.value<7?!1:e.current>4),u=E(()=>r.value<7?!1:e.current{const{current:v}=e;let b=1,w=r.value;const L=[];r.value>=7&&(v<=4&&v4&&v>=r.value-3?(w=r.value,b=r.value-4):r.value>7&&(b=v-2,w=v+2));for(let m=b;m<=w;m++)L.push(m);return L}),p=v=>t("updateCurrentPage",v),h=v=>{const b=parseInt(v);b<=r.value&&b>0?p(b):l.pop(`${i.value.errorText.replace(/\$page/g,r.value.toString())}`)};return ke(()=>{l=new Yh}),()=>s("div",{class:"vp-pagination"},o.value?s("div",{class:"vp-pagination-list"},[s("div",{class:"vp-pagination-number "},[e.current>1?s("div",{class:"prev",role:"navigation",unselectable:"on",onClick:()=>p(e.current-1)},i.value.prev):null,c.value?[s("div",{role:"navigation",onClick:()=>p(1)},1),s("div",{class:"ellipsis"},"...")]:null,d.value.map(v=>s("div",{key:v,class:{active:e.current===v},role:"navigation",onClick:()=>p(v)},v)),u.value?[s("div",{class:"ellipsis"},"..."),s("div",{role:"navigation",onClick:()=>p(r.value)},r.value)]:null,e.currentp(e.current+1)},i.value.next):null]),s("div",{class:"vp-pagination-nav"},[s("label",{for:"navigation-text"},`${i.value.navigate}: `),s("input",{id:"navigation-text",value:a.value,onInput:({target:v})=>{a.value=v.value},onKeydown:v=>{v.key==="Enter"&&(v.preventDefault(),h(a.value))}}),s("button",{class:"vp-pagination-button",role:"navigation",title:i.value.action,onClick:()=>h(a.value)},i.value.action)])]):[])}}),Ui=$({name:"ArticleList",props:{items:{type:Array,default:()=>[]}},setup(e){const t=yt(),l=Ve(),n=vn(),a=W(1),i=E(()=>n.value.articlePerPage||10),r=E(()=>e.items.slice((a.value-1)*i.value,a.value*i.value)),o=c=>{a.value=c;const u={...t.query};u.page===c.toString()||c===1&&!u.page||(c===1?delete u.page:u.page=c.toString(),l.push({path:t.path,query:u}))};return ke(()=>{const{page:c}=t.query;o(c?Number(c):1),ce(a,()=>{const u=document.querySelector("#article-list").getBoundingClientRect().top+window.scrollY;setTimeout(()=>{window.scrollTo(0,u)},100)}),ce(()=>t.query,({page:u})=>{o(u?Number(u):1)})}),()=>s("div",{id:"article-list",class:"vp-article-list"},r.value.length?[...r.value.map(({info:c,path:u},d)=>s(pe,{appear:!0,delay:d*.04},()=>s(y2,{key:u,info:c,path:u}))),s(b2,{current:a.value,perPage:i.value,total:e.items.length,onUpdateCurrentPage:o})]:s(Au))}}),Wi=$({name:"CategoryList",setup(){const e=ue(),t=hn();return()=>s("ul",{class:"vp-category-list"},sn(t.value.map).map(([l,{path:n,items:a}])=>s("li",{class:["vp-category",`vp-category${la(l,9)}`,{active:n===e.value.path}]},s(Ce,{to:n},()=>[l,s("span",{class:"count"},a.length)]))))}}),Ki=$({name:"TagList",setup(){const e=Ee(),t=fn(),l=n=>{var a;return n===((a=e.value.blog)==null?void 0:a.name)};return()=>s("ul",{class:"tag-list-wrapper"},sn(t.value.map).map(([n,{path:a,items:i}])=>s("li",{class:["tag",`tag${la(n,9)}`,{active:l(n)}]},s(Ce,{to:a},()=>[n,s("span",{class:"tag-num"},i.length)]))))}}),_2=$({name:"TimelineList",setup(){const e=ie(),t=zi(),l=un(),n=E(()=>e.value.blogLocales.timeline);return()=>s("div",{class:"timeline-list-wrapper"},[s("div",{class:"timeline-list-title",onClick:()=>l(t.value.path)},[s(Gi),s("span",{class:"num"},t.value.items.length),n.value]),s("hr"),s("div",{class:"timeline-content"},s("ul",{class:"timeline-list"},t.value.config.map(({year:a,items:i},r)=>s(pe,{appear:!0,delay:.08*(r+1)},()=>s("li",[s("h3",{class:"timeline-year"},a),s("ul",{class:"timeline-year-wrapper"},i.map(({date:o,info:c,path:u})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},o),s(Ce,{class:"timeline-title",to:u},()=>c[be.title])])))])))))])}}),Pu=$({name:"InfoList",setup(){const e=ie(),t=gn(),l=hn(),n=E(()=>gt(l.value.map).length),a=Hi(),i=fn(),r=E(()=>gt(i.value.map).length),o=un(),c=W("article"),u=E(()=>e.value.blogLocales),d=[["article",qn],["category",Ya],["tag",Xa],["timeline",Gi]];return()=>s("div",{class:"vp-blog-infos"},[s("div",{class:"vp-blog-type-switcher"},d.map(([p,h])=>s("button",{type:"button",class:"vp-blog-type-button",onClick:()=>{c.value=p}},s("div",{class:["icon-wrapper",{active:c.value===p}],"aria-label":u.value[p],"data-balloon-pos":"up"},s(h))))),s(pe,()=>c.value==="article"?s("div",{class:"vp-sticky-article-wrapper"},[s("div",{class:"title",onClick:()=>o(t.value.path)},[s(qn),s("span",{class:"num"},t.value.items.length),u.value.article]),s("hr"),s("ul",{class:"vp-sticky-articles"},a.value.items.map(({info:p,path:h},v)=>s(pe,{appear:!0,delay:.08*(v+1)},()=>s("li",{class:"vp-sticky-article"},s(Ce,{to:h},()=>p[be.title])))))]):c.value==="category"?s("div",{class:"vp-category-wrapper"},[n.value?s("div",{class:"title",onClick:()=>o(l.value.path)},[s(Ya),s("span",{class:"num"},n.value),u.value.category]):null,s("hr"),s(pe,{delay:.04},()=>s(Wi))]):c.value==="tag"?s("div",{class:"vp-tag-wrapper"},[r.value?s("div",{class:"title",onClick:()=>o(i.value.path)},[s(Xa),s("span",{class:"num"},r.value),u.value.tag]):null,s("hr"),s(pe,{delay:.04},()=>s(Ki))]):s(pe,()=>s(_2)))])}}),ra=$({name:"BlogWrapper",slots:Object,setup(e,{slots:t}){const{isMobile:l}=dn();return()=>[s(Bi),s(Ni,{noSidebar:!0,noToc:!0},{default:()=>t.default(),navScreenBottom:()=>s(qi),...l.value?{sidebar:()=>s(Pu)}:{}})]}});const Iu=()=>s("aside",{class:"vp-blog-info-wrapper"},[s(pe,()=>s(qi)),s(pe,{delay:.04},()=>s(Pu))]);Iu.displayName="InfoPanel";var sa=Iu,k2=$({name:"BlogPage",components:{CategoryList:Wi,TagList:Ki},setup(){const e=ue(),t=Ee(),l=hn(),n=fn(),a=E(()=>t.value.blog||{}),i=E(()=>{const{key:o=""}=a.value;return o==="category"?"CategoryList":o==="tag"?"TagList":null}),r=E(()=>{const{name:o="",key:c=""}=a.value;return c==="category"?o?l.value.map[o].items:[]:c==="tag"?o?n.value.map[o].items:[]:[]});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>i.value?s(Je(i.value)):null),a.value.name?s(pe,{appear:!0,delay:.24},()=>s(Ui,{key:e.value.path,items:r.value})):null]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),w2=$({name:"BlogHero",slots:Object,setup(e,{slots:t}){const l=Ee(),n=rn(),a=Ge(),i=E(()=>l.value.heroFullScreen??!1),r=E(()=>{const{heroText:c,heroImage:u,heroImageDark:d,heroAlt:p,heroImageStyle:h,tagline:v}=l.value;return{text:c??n.value.title??"Hello",image:u?xe(u):null,imageDark:d?xe(d):null,heroStyle:h,alt:p||c||"hero image",tagline:v??"",isFullScreen:i.value}}),o=E(()=>{const{bgImage:c,bgImageDark:u,bgImageStyle:d}=l.value;return{image:ae(c)?xe(c):c===!1?null:i2,imageDark:ae(u)?xe(u):null,bgStyle:d,isFullScreen:i.value}});return()=>{var c,u;return l.value.hero===!1?null:s("div",{ref:a,class:["vp-blog-hero",{fullscreen:i.value,"no-bg":!o.value.image}]},[((c=t.heroBg)==null?void 0:c.call(t,o.value))||[o.value.image?s("div",{class:["vp-blog-mask",{light:o.value.imageDark}],style:[{background:`url(${o.value.image}) center/cover no-repeat`},o.value.bgStyle]}):null,o.value.imageDark?s("div",{class:"vp-blog-mask dark",style:[{background:`url(${o.value.imageDark}) center/cover no-repeat`},o.value.bgStyle]}):null],((u=t.heroInfo)==null?void 0:u.call(t,r.value))||[s(pe,{appear:!0,type:"group",delay:.04},()=>[r.value.image?s("img",{key:"light",class:["vp-blog-hero-image",{light:r.value.imageDark}],style:r.value.heroStyle,src:r.value.image,alt:r.value.alt}):null,r.value.imageDark?s("img",{key:"dark",class:"vp-blog-hero-image dark",style:r.value.heroStyle,src:r.value.imageDark,alt:r.value.alt}):null]),s(pe,{appear:!0,delay:.08},()=>r.value.text?s("h1",{class:"vp-blog-hero-title"},r.value.text):null),s(pe,{appear:!0,delay:.12},()=>r.value.tagline?s("p",{class:"vp-blog-hero-description",innerHTML:r.value.tagline}):null)],r.value.isFullScreen?s("button",{type:"button",class:"slide-down-button",onClick:()=>{window.scrollTo({top:a.value.clientHeight,behavior:"smooth"})}},[s(Za),s(Za)]):null])}}});const E2=["link","article","book","project","friend"];var L2=$({name:"ProjectPanel",components:{ArticleIcon:qn,BookIcon:Eu,FriendIcon:Tu,LinkIcon:Lu,ProjectIcon:xu},setup(){const e=Ee(),t=Ol(),l=un(),n=(a="",i="icon")=>E2.includes(a)?s(Je(`${a}-icon`)):Zt(a)?s("img",{class:"vp-project-image",src:a,alt:i}):na(a)?s("img",{class:"vp-project-image",src:xe(a),alt:i}):s(Ne,{icon:a});return()=>{var a;return(a=e.value.projects)!=null&&a.length?s("div",{class:"vp-project-panel"},e.value.projects.map(({icon:i,link:r,name:o,desc:c},u)=>s("div",{class:["vp-project-card",{[`project${u%9}`]:!t.value}],onClick:()=>l(r)},[n(i,o),s("div",{class:"vp-project-name"},o),s("div",{class:"vp-project-desc"},c)]))):null}}}),x2=$({name:"BlogHome",setup(){const e=gn();return()=>s("div",{class:"vp-page vp-blog"},[s(w2),s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.16},()=>s(L2)),s(pe,{appear:!0,delay:.24},()=>s(Ui,{items:e.value.items}))]),s(pe,{appear:!0,delay:.16},()=>s(sa,{key:"blog"}))]),s(pe,{appear:!0,delay:.28},()=>s(ji))])}}),T2=$({name:"BlogHome",setup(){return()=>s(ra,()=>s(x2))}}),Ru=$({name:"ArticleType",setup(){const e=ue(),t=mt(),l=ie(),n=gn(),a=Hi(),i=E(()=>{const r=l.value.blogLocales;return[{text:r.all,path:n.value.path},{text:r.star,path:a.value.path},...[].map(({key:o,path:c})=>({text:r[o],path:c.replace(/^\//,t.value)}))]});return()=>s("ul",{class:"vp-article-type-wrapper"},i.value.map(r=>s("li",{class:["vp-article-type",{active:r.path===e.value.path}]},s(Ce,{to:r.path},()=>r.text))))}}),A2=$({name:"BlogPage",setup(){const e=ia(),t=Ee(),l=ue(),n=gn(),a=Hi(),i=E(()=>{const{key:r="",type:o}=t.value.blog||{};return r==="star"?a.value.items:o==="type"&&r?e.value.items:n.value.items});return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,()=>s(Ru)),s(pe,{appear:!0,delay:.24},()=>s(Ui,{key:l.value.path,items:i.value}))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}}),O2=$({name:"TimelineItems",setup(){const e=vn(),t=ie(),l=zi(),n=E(()=>e.value.timeline||t.value.blogLocales.timelineTitle),a=E(()=>l.value.config.map(({year:i})=>({title:i.toString(),level:2,slug:i.toString(),children:[]})));return()=>s("div",{class:"timeline-wrapper"},s("ul",{class:"timeline-content"},[s(pe,()=>s("li",{class:"motto"},n.value)),s(uu,{items:a.value}),l.value.config.map(({year:i,items:r},o)=>s(pe,{appear:!0,delay:.08*(o+1),type:"group"},()=>[s("h3",{key:"title",id:i,class:"timeline-year-title"},s("span",i)),s("li",{key:"content",class:"timeline-year-list"},[s("ul",{class:"timeline-year-wrapper"},r.map(({date:c,info:u,path:d})=>s("li",{class:"timeline-item"},[s("span",{class:"timeline-date"},c),s(Ce,{class:"timeline-title",to:d},()=>u[be.title])])))])]))]))}}),P2=$({name:"Timeline",components:{ArticleType:Ru,CategoryList:Wi,TagList:Ki},setup(){return()=>s(ra,()=>s("div",{class:"vp-page vp-blog"},s("div",{class:"blog-page-wrapper"},[s("main",{id:"main-content",class:"vp-blog-main"},[s(pe,{appear:!0,delay:.24},()=>s(O2))]),s(pe,{delay:.16},()=>s(sa,{key:"blog"}))])))}});sv(Ne);const I2=dt({enhance:({app:e,router:t})=>{const{scrollBehavior:l}=t.options;t.options.scrollBehavior=async(...n)=>(await du().wait(),l(...n)),tf(e),e.component("HopeIcon",Ne),e.component("VPLink",Ce),e.component("BloggerInfo",qi)},setup:()=>{lf(),sf(),g2()},layouts:{Layout:Zf,NotFound:t2,BlogCategory:k2,BlogHome:T2,BlogType:A2,Timeline:P2}}),R2=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,C2=(e,t)=>t.some(l=>{if(ae(l))return l===e.key;const{key:n,ctrl:a=!1,shift:i=!1,alt:r=!1}=l;return n===e.key&&a===e.ctrlKey&&i===e.shiftKey&&r===e.altKey}),S2=/[^\x00-\x7F]/,D2=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),Ls=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),xs=(e,t)=>{const l=t.join(" "),n=D2(e);if(S2.test(e))return n.some(r=>l.toLowerCase().indexOf(r)>-1);const a=e.endsWith(" ");return new RegExp(n.map((r,o)=>n.length===o+1&&!a?`(?=.*\\b${Ls(r)})`:`(?=.*\\b${Ls(r)}\\b)`).join("")+".+","gi").test(l)},M2=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const l=n=>{e.value&&C2(n,t.value)&&!R2(n.target)&&(n.preventDefault(),e.value.focus())};ke(()=>{document.addEventListener("keydown",l)}),Yn(()=>{document.removeEventListener("keydown",l)})},$2=[{title:"levy's blog",headers:[],path:"/",pathLocale:"/",extraFields:[]},{title:"关于",headers:[],path:"/about.html",pathLocale:"/",extraFields:[]},{title:"关于 Arm 你需要了解的三件事",headers:[],path:"/devops/about-arm-things-you-need-to-know.html",pathLocale:"/",extraFields:[]},{title:"对象存储静态资源常见操作",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"阿里云OSS",slug:"阿里云oss",link:"#阿里云oss",children:[{level:3,title:"绑定域名",slug:"绑定域名",link:"#绑定域名",children:[]},{level:3,title:"CNAME设置",slug:"cname设置",link:"#cname设置",children:[]},{level:3,title:"HTTPS证书托管",slug:"https证书托管",link:"#https证书托管",children:[]},{level:3,title:"公共读",slug:"公共读",link:"#公共读",children:[]},{level:3,title:"CORS跨域设置",slug:"cors跨域设置",link:"#cors跨域设置",children:[]}]},{level:2,title:"华为云OBS",slug:"华为云obs",link:"#华为云obs",children:[{level:3,title:"跨域设置",slug:"跨域设置",link:"#跨域设置",children:[]}]}],path:"/devops/common-solutions-of-object-storage-for-static-assets.html",pathLocale:"/",extraFields:[]},{title:"Docker 构建镜像、推送、启动实用脚本",headers:[{level:2,title:"misc",slug:"misc",link:"#misc",children:[]},{level:2,title:"get-version.sh",slug:"get-version-sh",link:"#get-version-sh",children:[]},{level:2,title:"build-image.sh",slug:"build-image-sh",link:"#build-image-sh",children:[]},{level:2,title:"startup.sh",slug:"startup-sh",link:"#startup-sh",children:[]}],path:"/devops/docker-build-and-push-script.html",pathLocale:"/",extraFields:[]},{title:"缩减Python应用的镜像体积",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"原始Dockerfile",slug:"原始dockerfile",link:"#原始dockerfile",children:[]},{level:2,title:"使用slim镜像",slug:"使用slim镜像",link:"#使用slim镜像",children:[]},{level:2,title:"减少层数、取消本地缓存",slug:"减少层数、取消本地缓存",link:"#减少层数、取消本地缓存",children:[]},{level:2,title:"优化效果",slug:"优化效果",link:"#优化效果",children:[]},{level:2,title:"参考",slug:"参考",link:"#参考",children:[]}],path:"/devops/reduce-python-image-size.html",pathLocale:"/",extraFields:[]},{title:"sh与bash的区别",headers:[],path:"/devops/what-is-the-difference-between-sh-and-bash.html",pathLocale:"/",extraFields:[]},{title:"VuePress2 娱乐视频",headers:[],path:"/daily/a-vuepress2-entertaining-video.html",pathLocale:"/",extraFields:[]},{title:"来自Navicat的侵权警告",headers:[],path:"/daily/a-warning-from-navicat.html",pathLocale:"/",extraFields:[]},{title:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",headers:[{level:2,title:"Background",slug:"background",link:"#background",children:[]},{level:2,title:"utf8mb4(UTF-8 MultiByte 4-Byte)",slug:"utf8mb4-utf-8-multibyte-4-byte",link:"#utf8mb4-utf-8-multibyte-4-byte",children:[]},{level:2,title:"utf8mb4_unicode_ci",slug:"utf8mb4-unicode-ci",link:"#utf8mb4-unicode-ci",children:[]},{level:2,title:"Some tips",slug:"some-tips",link:"#some-tips",children:[]}],path:"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",pathLocale:"/",extraFields:[]},{title:"Claude AI应用案例,从HTML中抽取文本",headers:[],path:"/daily/claude-ai-in-action-extract-info-from-html.html",pathLocale:"/",extraFields:[]},{title:"复制代码也许不是罪",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/copy-code-may-not-be-guilty.html",pathLocale:"/",extraFields:[]},{title:"不要与傻逼进行争吵",headers:[],path:"/daily/dont-try-to-argue-with-a-sb.html",pathLocale:"/",extraFields:[]},{title:"迭代复盘之三员管理",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"动手前,至少梳理出接口清单",slug:"动手前-至少梳理出接口清单",link:"#动手前-至少梳理出接口清单",children:[]},{level:2,title:"迁移时,采取结队编程",slug:"迁移时-采取结队编程",link:"#迁移时-采取结队编程",children:[]},{level:2,title:"迁移后,需要对自己负责的功能设计测试用例",slug:"迁移后-需要对自己负责的功能设计测试用例",link:"#迁移后-需要对自己负责的功能设计测试用例",children:[]}],path:"/daily/iteration-retrospective-of-sanyuan.html",pathLocale:"/",extraFields:[]},{title:"都什么年代了,还在用传统方式写代码?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"开发流程",slug:"开发流程",link:"#开发流程",children:[]},{level:2,title:"程序设计",slug:"程序设计",link:"#程序设计",children:[]},{level:2,title:"代码编写",slug:"代码编写",link:"#代码编写",children:[{level:3,title:"生成真实代码",slug:"生成真实代码",link:"#生成真实代码",children:[]},{level:3,title:"辅助编程工具",slug:"辅助编程工具",link:"#辅助编程工具",children:[]}]},{level:2,title:"软件测试",slug:"软件测试",link:"#软件测试",children:[]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"附:CodeWhisperer 安装",slug:"附-codewhisperer-安装",link:"#附-codewhisperer-安装",children:[]}],path:"/daily/leverage-ai-to-boost-coding-productivity.html",pathLocale:"/",extraFields:[]},{title:"微软中国CTO演讲观后感",headers:[],path:"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",pathLocale:"/",extraFields:[]},{title:"生产教训:测试环境要与生产环境一致",headers:[{level:2,title:"事件还原",slug:"事件还原",link:"#事件还原",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/daily/testing-environments-should-be-consistent-with-production-environments.html",pathLocale:"/",extraFields:[]},{title:"对Vue不得不吐槽的事",headers:[],path:"/daily/things-I-have-to-vent-about-vue.html",pathLocale:"/",extraFields:[]},{title:"再见ChatGPT,我选择Claude2!",headers:[],path:"/daily/use-claude2-instead-of-chatgpt.html",pathLocale:"/",extraFields:[]},{title:"Vim 作者离世",headers:[],path:"/daily/vim-creator-pass-away.html",pathLocale:"/",extraFields:[]},{title:"技术点评:别每张表都加tenant_id",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"正文",slug:"正文",link:"#正文",children:[]}],path:"/daily/you-dont-need-to-add-tenant_id-to-every-table.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第一册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-1.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第二册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-2.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第三册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-3.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第四册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"值得一读的文章",slug:"值得一读的文章",link:"#值得一读的文章",children:[]}],path:"/english/contemporary-college-english-4.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第五册",headers:[{level:2,title:"介绍",slug:"介绍",link:"#介绍",children:[]},{level:2,title:"Who Are You and What Are You Doing Here",slug:"who-are-you-and-what-are-you-doing-here",link:"#who-are-you-and-what-are-you-doing-here",children:[]},{level:2,title:"Two Kinds",slug:"two-kinds",link:"#two-kinds",children:[]},{level:2,title:"Love is a Fallacy",slug:"love-is-a-fallacy",link:"#love-is-a-fallacy",children:[]},{level:2,title:"Rewriting American History",slug:"rewriting-american-history",link:"#rewriting-american-history",children:[]},{level:2,title:"Nobel Peace Price About Global Warming",slug:"nobel-peace-price-about-global-warming",link:"#nobel-peace-price-about-global-warming",children:[]},{level:2,title:"The Bluest Eyes",slug:"the-bluest-eyes",link:"#the-bluest-eyes",children:[]},{level:2,title:"How News Becomes Options and Opinions Off-Limits",slug:"how-news-becomes-options-and-opinions-off-limits",link:"#how-news-becomes-options-and-opinions-off-limits",children:[]},{level:2,title:"The Indispensable Opposition",slug:"the-indispensable-opposition",link:"#the-indispensable-opposition",children:[]},{level:2,title:"The Danger of a Single Story",slug:"the-danger-of-a-single-story",link:"#the-danger-of-a-single-story",children:[]},{level:2,title:"Come Rain or Come Shine",slug:"come-rain-or-come-shine",link:"#come-rain-or-come-shine",children:[]},{level:2,title:"Invisible Man",slug:"invisible-man",link:"#invisible-man",children:[]},{level:2,title:"You've Got to Find What You Love",slug:"you-ve-got-to-find-what-you-love",link:"#you-ve-got-to-find-what-you-love",children:[]},{level:2,title:"Where Do We Go from Here",slug:"where-do-we-go-from-here",link:"#where-do-we-go-from-here",children:[]}],path:"/english/contemporary-college-english-5.html",pathLocale:"/",extraFields:[]},{title:"现代大学英语精读(第2版)第六册",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"Paper Tigers",slug:"paper-tigers",link:"#paper-tigers",children:[]},{level:2,title:"What Is News",slug:"what-is-news",link:"#what-is-news",children:[]},{level:2,title:"At War with the Planet",slug:"at-war-with-the-planet",link:"#at-war-with-the-planet",children:[]},{level:2,title:"How to Get the Poor off Our Conscience",slug:"how-to-get-the-poor-off-our-conscience",link:"#how-to-get-the-poor-off-our-conscience",children:[]},{level:2,title:"Housewifely Arts",slug:"housewifely-arts",link:"#housewifely-arts",children:[]},{level:2,title:"The One Against The Many",slug:"the-one-against-the-many",link:"#the-one-against-the-many",children:[]},{level:2,title:"Notes on the English Character",slug:"notes-on-the-english-character",link:"#notes-on-the-english-character",children:[]},{level:2,title:"The Death of a Pig",slug:"the-death-of-a-pig",link:"#the-death-of-a-pig",children:[]},{level:2,title:"Don't Eat Fortune's Cookie",slug:"don-t-eat-fortune-s-cookie",link:"#don-t-eat-fortune-s-cookie",children:[]},{level:2,title:"The Accidental Universe",slug:"the-accidental-universe",link:"#the-accidental-universe",children:[]},{level:2,title:"Rowling's Speech at Harvard",slug:"rowling-s-speech-at-harvard",link:"#rowling-s-speech-at-harvard",children:[]}],path:"/english/contemporary-college-english-6.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语1:开篇",headers:[{level:2,title:"为什么学",slug:"为什么学",link:"#为什么学",children:[]},{level:2,title:"怎么学",slug:"怎么学",link:"#怎么学",children:[{level:3,title:"建设心态",slug:"建设心态",link:"#建设心态",children:[]},{level:3,title:"提升认知",slug:"提升认知",link:"#提升认知",children:[]},{level:3,title:"明确意义",slug:"明确意义",link:"#明确意义",children:[]},{level:3,title:"制定计划",slug:"制定计划",link:"#制定计划",children:[]},{level:3,title:"培养习惯",slug:"培养习惯",link:"#培养习惯",children:[]},{level:3,title:"注重方法",slug:"注重方法",link:"#注重方法",children:[]}]}],path:"/english/everyone-can-learn-english-1-overview.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语2:音标",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"讨论",slug:"讨论",link:"#讨论",children:[{level:3,title:"建议买实体书",slug:"建议买实体书",link:"#建议买实体书",children:[]},{level:3,title:"美语是主流",slug:"美语是主流",link:"#美语是主流",children:[]},{level:3,title:"不要纠结口音",slug:"不要纠结口音",link:"#不要纠结口音",children:[]}]}],path:"/english/everyone-can-learn-english-2-pronunciation.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语3:单词",headers:[{level:2,title:"方法",slug:"方法",link:"#方法",children:[]},{level:2,title:"提醒",slug:"提醒",link:"#提醒",children:[]}],path:"/english/everyone-can-learn-english-3-words.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语4:听说",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"方法",slug:"方法",link:"#方法",children:[{level:3,title:"1.建立信心",slug:"_1-建立信心",link:"#_1-建立信心",children:[]},{level:3,title:"2.明确方向",slug:"_2-明确方向",link:"#_2-明确方向",children:[]},{level:3,title:"3.坚持输入并输出",slug:"_3-坚持输入并输出",link:"#_3-坚持输入并输出",children:[]}]}],path:"/english/everyone-can-learn-english-4-listening-and-speaking.html",pathLocale:"/",extraFields:[]},{title:"人人都能学会的英语5:读写",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"阅读能力升级之旅",slug:"阅读能力升级之旅",link:"#阅读能力升级之旅",children:[]},{level:2,title:"阅读方法",slug:"阅读方法",link:"#阅读方法",children:[{level:3,title:"建立信心",slug:"建立信心",link:"#建立信心",children:[]},{level:3,title:"根据蓝思值选书",slug:"根据蓝思值选书",link:"#根据蓝思值选书",children:[]},{level:3,title:"巧查生词",slug:"巧查生词",link:"#巧查生词",children:[]},{level:3,title:"阅读材料推荐",slug:"阅读材料推荐",link:"#阅读材料推荐",children:[]}]},{level:2,title:"写作",slug:"写作",link:"#写作",children:[]}],path:"/english/everyone-can-learn-english-5-reading-and-writing.html",pathLocale:"/",extraFields:[]},{title:"英文能力评测手把手教学",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"单词量",slug:"单词量",link:"#单词量",children:[]},{level:2,title:"阅读能力",slug:"阅读能力",link:"#阅读能力",children:[]}],path:"/english/how-to-self-evaluate-english-level.html",pathLocale:"/",extraFields:[]},{title:"完成刷7k单词任务",headers:[],path:"/english/learning-7000-words-task-completed.html",pathLocale:"/",extraFields:[]},{title:"让 ChatGPT 成为你的外语私教",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"准备工作",slug:"准备工作",link:"#准备工作",children:[]},{level:2,title:"常用Prompt",slug:"常用prompt",link:"#常用prompt",children:[]},{level:2,title:"进行对话",slug:"进行对话",link:"#进行对话",children:[]},{level:2,title:"记录回答",slug:"记录回答",link:"#记录回答",children:[]}],path:"/english/let-chatgpt-be-your-foreign-language-teacher.html",pathLocale:"/",extraFields:[]},{title:"旧文章精选",headers:[],path:"/frontend/old-articles.html",pathLocale:"/",extraFields:[]},{title:"前端项目性能优化实战",headers:[{level:2,title:"检测",slug:"检测",link:"#检测",children:[]},{level:2,title:"图片优化",slug:"图片优化",link:"#图片优化",children:[]},{level:2,title:"提高TTFB时间",slug:"提高ttfb时间",link:"#提高ttfb时间",children:[]},{level:2,title:"移除未使用的 Javascript",slug:"移除未使用的-javascript",link:"#移除未使用的-javascript",children:[]},{level:2,title:"延迟静态资源的加载",slug:"延迟静态资源的加载",link:"#延迟静态资源的加载",children:[]},{level:2,title:"启用文本压缩",slug:"启用文本压缩",link:"#启用文本压缩",children:[]},{level:2,title:"优化缓存策略",slug:"优化缓存策略",link:"#优化缓存策略",children:[]}],path:"/frontend/performance-optimization-in-action.html",pathLocale:"/",extraFields:[]},{title:"Git最佳实践",headers:[{level:2,title:"精简提交",slug:"精简提交",link:"#精简提交",children:[]},{level:2,title:"频繁提交",slug:"频繁提交",link:"#频繁提交",children:[]},{level:2,title:"不要提交不完整的改动",slug:"不要提交不完整的改动",link:"#不要提交不完整的改动",children:[]},{level:2,title:"提交前测试那些改动",slug:"提交前测试那些改动",link:"#提交前测试那些改动",children:[]},{level:2,title:"版本控制不是备份系统",slug:"版本控制不是备份系统",link:"#版本控制不是备份系统",children:[]},{level:2,title:"Github实例",slug:"github实例",link:"#github实例",children:[{level:3,title:"一个功能对应一个分支",slug:"一个功能对应一个分支",link:"#一个功能对应一个分支",children:[]},{level:3,title:"提交“瘦”的PR",slug:"提交-瘦-的pr",link:"#提交-瘦-的pr",children:[]},{level:3,title:"使用正确的标题",slug:"使用正确的标题",link:"#使用正确的标题",children:[]},{level:3,title:"根据模板填写PR描述",slug:"根据模板填写pr描述",link:"#根据模板填写pr描述",children:[]},{level:3,title:"自动关闭issue",slug:"自动关闭issue",link:"#自动关闭issue",children:[]},{level:3,title:"1+2 review 规则",slug:"_1-2-review-规则",link:"#_1-2-review-规则",children:[]},{level:3,title:"礼貌提问",slug:"礼貌提问",link:"#礼貌提问",children:[]}]},{level:2,title:"学习资源",slug:"学习资源",link:"#学习资源",children:[]}],path:"/git/git-best-pratices.html",pathLocale:"/",extraFields:[]},{title:"Git代码合并指南",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"功能分支合并长驻分支冲突",slug:"功能分支合并长驻分支冲突",link:"#功能分支合并长驻分支冲突",children:[{level:3,title:"解决思路",slug:"解决思路",link:"#解决思路",children:[]},{level:3,title:"操作步骤",slug:"操作步骤",link:"#操作步骤",children:[]}]},{level:2,title:"功能分支被污染",slug:"功能分支被污染",link:"#功能分支被污染",children:[{level:3,title:"解决思路",slug:"解决思路-1",link:"#解决思路-1",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-1",link:"#操作步骤-1",children:[]}]},{level:2,title:"挑选别的分支部分代码合并",slug:"挑选别的分支部分代码合并",link:"#挑选别的分支部分代码合并",children:[{level:3,title:"解决思路",slug:"解决思路-2",link:"#解决思路-2",children:[]},{level:3,title:"操作步骤",slug:"操作步骤-2",link:"#操作步骤-2",children:[]}]}],path:"/git/git-definitive-guide-to-merge-code.html",pathLocale:"/",extraFields:[]},{title:"Git查看历史记录小技巧",headers:[],path:"/git/git-history-two-tricks-in-idea.html",pathLocale:"/",extraFields:[]},{title:"Git常用命令",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[]},{level:2,title:"增强",slug:"增强",link:"#增强",children:[]},{level:2,title:"记住账号密码",slug:"记住账号密码",link:"#记住账号密码",children:[]},{level:2,title:"初始化",slug:"初始化",link:"#初始化",children:[]},{level:2,title:"本地提交",slug:"本地提交",link:"#本地提交",children:[{level:3,title:"取消未暂存的修改",slug:"取消未暂存的修改",link:"#取消未暂存的修改",children:[]},{level:3,title:"取消add",slug:"取消add",link:"#取消add",children:[]},{level:3,title:"取消提交",slug:"取消提交",link:"#取消提交",children:[]},{level:3,title:"修正提交",slug:"修正提交",link:"#修正提交",children:[]},{level:3,title:"stash修改",slug:"stash修改",link:"#stash修改",children:[]},{level:3,title:"恢复stash",slug:"恢复stash",link:"#恢复stash",children:[]}]},{level:2,title:"分支管理",slug:"分支管理",link:"#分支管理",children:[{level:3,title:"创建分支",slug:"创建分支",link:"#创建分支",children:[]},{level:3,title:"查看远程分支",slug:"查看远程分支",link:"#查看远程分支",children:[]},{level:3,title:"创建干净历史分支",slug:"创建干净历史分支",link:"#创建干净历史分支",children:[]},{level:3,title:"删除分支",slug:"删除分支",link:"#删除分支",children:[]}]},{level:2,title:"远程仓库",slug:"远程仓库",link:"#远程仓库",children:[{level:3,title:"远程仓库管理",slug:"远程仓库管理",link:"#远程仓库管理",children:[]},{level:3,title:"浅克隆",slug:"浅克隆",link:"#浅克隆",children:[]},{level:3,title:"克隆指定分支",slug:"克隆指定分支",link:"#克隆指定分支",children:[]},{level:3,title:"克隆失败因为文件名太长",slug:"克隆失败因为文件名太长",link:"#克隆失败因为文件名太长",children:[]},{level:3,title:"强行推送",slug:"强行推送",link:"#强行推送",children:[]},{level:3,title:"取消错误的推送",slug:"取消错误的推送",link:"#取消错误的推送",children:[]}]},{level:2,title:"标签管理",slug:"标签管理",link:"#标签管理",children:[{level:3,title:"新建本地标签",slug:"新建本地标签",link:"#新建本地标签",children:[]},{level:3,title:"删除本地标签",slug:"删除本地标签",link:"#删除本地标签",children:[]},{level:3,title:"查看本地所有标签",slug:"查看本地所有标签",link:"#查看本地所有标签",children:[]},{level:3,title:"推送本地标签",slug:"推送本地标签",link:"#推送本地标签",children:[]},{level:3,title:"获取远程标签",slug:"获取远程标签",link:"#获取远程标签",children:[]},{level:3,title:"删除远程标签",slug:"删除远程标签",link:"#删除远程标签",children:[]}]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"cherry-pick",slug:"cherry-pick",link:"#cherry-pick",children:[]},{level:3,title:"merge unrelated histories",slug:"merge-unrelated-histories",link:"#merge-unrelated-histories",children:[]},{level:3,title:"git log 丢失最新提交",slug:"git-log-丢失最新提交",link:"#git-log-丢失最新提交",children:[]},{level:3,title:"查看分支创建时间",slug:"查看分支创建时间",link:"#查看分支创建时间",children:[]},{level:3,title:"根据文件搜索历史",slug:"根据文件搜索历史",link:"#根据文件搜索历史",children:[]},{level:3,title:"从所有提交中删除一个文件",slug:"从所有提交中删除一个文件",link:"#从所有提交中删除一个文件",children:[]}]}],path:"/git/git-useful-commands.html",pathLocale:"/",extraFields:[]},{title:"GitLab CI",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装与配置",slug:"安装与配置",link:"#安装与配置",children:[{level:3,title:"GitLab Runner 安装",slug:"gitlab-runner-安装",link:"#gitlab-runner-安装",children:[]},{level:3,title:"GitLab Runner 注册",slug:"gitlab-runner-注册",link:"#gitlab-runner-注册",children:[]},{level:3,title:"提交.gitlab-ci.yml",slug:"提交-gitlab-ci-yml",link:"#提交-gitlab-ci-yml",children:[]}]},{level:2,title:"合并代码前进行检查",slug:"合并代码前进行检查",link:"#合并代码前进行检查",children:[{level:3,title:"背景",slug:"背景",link:"#背景",children:[]},{level:3,title:"设置MR检查",slug:"设置mr检查",link:"#设置mr检查",children:[]},{level:3,title:".gitlab-ci.yml 示例",slug:"gitlab-ci-yml-示例",link:"#gitlab-ci-yml-示例",children:[]},{level:3,title:"效果",slug:"效果",link:"#效果",children:[]}]},{level:2,title:"集成单元测试",slug:"集成单元测试",link:"#集成单元测试",children:[]},{level:2,title:"线上发布 jar",slug:"线上发布-jar",link:"#线上发布-jar",children:[{level:3,title:"Maven配置",slug:"maven配置",link:"#maven配置",children:[]},{level:3,title:".gitlab-ci.yml 配置",slug:"gitlab-ci-yml-配置",link:"#gitlab-ci-yml-配置",children:[]},{level:3,title:"拉取最新的jar",slug:"拉取最新的jar",link:"#拉取最新的jar",children:[]}]},{level:2,title:"保存中间产物",slug:"保存中间产物",link:"#保存中间产物",children:[]},{level:2,title:"其他问题与解决方案",slug:"其他问题与解决方案",link:"#其他问题与解决方案",children:[{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"Node.js",slug:"node-js",link:"#node-js",children:[]},{level:3,title:"创建不了容器",slug:"创建不了容器",link:"#创建不了容器",children:[]},{level:3,title:"本地成功,流水线失败",slug:"本地成功-流水线失败",link:"#本地成功-流水线失败",children:[]}]},{level:2,title:"参考文档",slug:"参考文档",link:"#参考文档",children:[]}],path:"/git/gitlab-ci.html",pathLocale:"/",extraFields:[]},{title:"再论Git Flow",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"动机",slug:"动机",link:"#动机",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[{level:3,title:"剔除代码",slug:"剔除代码",link:"#剔除代码",children:[]},{level:3,title:"再次提交",slug:"再次提交",link:"#再次提交",children:[]},{level:3,title:"比较优劣",slug:"比较优劣",link:"#比较优劣",children:[]}]},{level:2,title:"实例",slug:"实例",link:"#实例",children:[{level:3,title:"分支模型",slug:"分支模型",link:"#分支模型",children:[]},{level:3,title:"功能提交",slug:"功能提交",link:"#功能提交",children:[]},{level:3,title:"功能回撤",slug:"功能回撤",link:"#功能回撤",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/git/rethinking-git-flow.html",pathLocale:"/",extraFields:[]},{title:"操作 Gitlab MR 的命令行工具",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[{level:3,title:"解压zip",slug:"解压zip",link:"#解压zip",children:[]},{level:3,title:"安装git bash",slug:"安装git-bash",link:"#安装git-bash",children:[]}]},{level:2,title:"配置",slug:"配置",link:"#配置",children:[{level:3,title:"gitlab_token",slug:"gitlab-token",link:"#gitlab-token",children:[]},{level:3,title:"codebases",slug:"codebases",link:"#codebases",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"IDEA",slug:"idea",link:"#idea",children:[]}]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"创建MR",slug:"创建mr",link:"#创建mr",children:[]},{level:3,title:"查看MR",slug:"查看mr",link:"#查看mr",children:[]},{level:3,title:"合并MR",slug:"合并mr",link:"#合并mr",children:[]},{level:3,title:"冲突处理",slug:"冲突处理",link:"#冲突处理",children:[]}]}],path:"/git/use-command-line-tool-to-manage-gitlab-merge-request.html",pathLocale:"/",extraFields:[]},{title:"IDEA常见问题与解决方案",headers:[{level:2,title:"启动参数过长",slug:"启动参数过长",link:"#启动参数过长",children:[]},{level:2,title:"设置JDK版本",slug:"设置jdk版本",link:"#设置jdk版本",children:[]},{level:2,title:"lombok 编译报错",slug:"lombok-编译报错",link:"#lombok-编译报错",children:[]},{level:2,title:"设置启动参数",slug:"设置启动参数",link:"#设置启动参数",children:[]},{level:2,title:"栈溢出",slug:"栈溢出",link:"#栈溢出",children:[]},{level:2,title:"内存不足",slug:"内存不足",link:"#内存不足",children:[]},{level:2,title:"热加载",slug:"热加载",link:"#热加载",children:[]},{level:2,title:"终端加载环境变量",slug:"终端加载环境变量",link:"#终端加载环境变量",children:[]},{level:2,title:"添加外部jar作为依赖",slug:"添加外部jar作为依赖",link:"#添加外部jar作为依赖",children:[]},{level:2,title:"文件找不到——依赖冲突",slug:"文件找不到——依赖冲突",link:"#文件找不到——依赖冲突",children:[]},{level:2,title:"自动import",slug:"自动import",link:"#自动import",children:[]},{level:2,title:"文件乱码",slug:"文件乱码",link:"#文件乱码",children:[]},{level:2,title:"autowired 提示变量未赋值",slug:"autowired-提示变量未赋值",link:"#autowired-提示变量未赋值",children:[]}],path:"/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",pathLocale:"/",extraFields:[]},{title:"Maven常见问题与解决方案",headers:[{level:2,title:"运行 class 找不到主类",slug:"运行-class-找不到主类",link:"#运行-class-找不到主类",children:[]},{level:2,title:"运行 jar 找不到主类",slug:"运行-jar-找不到主类",link:"#运行-jar-找不到主类",children:[]},{level:2,title:"编译时找不到主类",slug:"编译时找不到主类",link:"#编译时找不到主类",children:[]},{level:2,title:"设置Maven目录",slug:"设置maven目录",link:"#设置maven目录",children:[]},{level:2,title:"无法识别 Maven 项目",slug:"无法识别-maven-项目",link:"#无法识别-maven-项目",children:[]},{level:2,title:"使用了不想要的镜像源",slug:"使用了不想要的镜像源",link:"#使用了不想要的镜像源",children:[]},{level:2,title:"下载 jar 失败",slug:"下载-jar-失败",link:"#下载-jar-失败",children:[]},{level:2,title:"私服认证401",slug:"私服认证401",link:"#私服认证401",children:[]},{level:2,title:"避免缓存",slug:"避免缓存",link:"#避免缓存",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/Resolving-Common-Problems-in-Maven.md.html",pathLocale:"/",extraFields:[]},{title:"避免密码明文传输",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"前端代码",slug:"前端代码",link:"#前端代码",children:[]},{level:2,title:"后端代码",slug:"后端代码",link:"#后端代码",children:[]}],path:"/java/avoid-sending-password-in-plaintext.html",pathLocale:"/",extraFields:[]},{title:"检查名字是否重复",headers:[{level:2,title:"推荐做法",slug:"推荐做法",link:"#推荐做法",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]}],path:"/java/check-if-name-exists.html",pathLocale:"/",extraFields:[]},{title:"Excel处理常用实践",headers:[{level:2,title:"基础知识",slug:"基础知识",link:"#基础知识",children:[]},{level:2,title:"应用框架",slug:"应用框架",link:"#应用框架",children:[{level:3,title:"导出",slug:"导出",link:"#导出",children:[]},{level:3,title:"导入",slug:"导入",link:"#导入",children:[]}]},{level:2,title:"常见问题与解决方案",slug:"常见问题与解决方案",link:"#常见问题与解决方案",children:[{level:3,title:"浏览器下载",slug:"浏览器下载",link:"#浏览器下载",children:[]},{level:3,title:"上传文件大小限制",slug:"上传文件大小限制",link:"#上传文件大小限制",children:[]},{level:3,title:"缺少字体",slug:"缺少字体",link:"#缺少字体",children:[]},{level:3,title:"序列化失败",slug:"序列化失败",link:"#序列化失败",children:[]}]}],path:"/java/common-practices-for-handling-excel.html",pathLocale:"/",extraFields:[]},{title:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"下载",slug:"下载",link:"#下载",children:[]},{level:2,title:"修改",slug:"修改",link:"#修改",children:[]},{level:2,title:"上传",slug:"上传",link:"#上传",children:[]}],path:"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",pathLocale:"/",extraFields:[]},{title:"集合命名推荐",headers:[{level:2,title:"概述",slug:"概述",link:"#概述",children:[]},{level:2,title:"List",slug:"list",link:"#list",children:[]},{level:2,title:"Set",slug:"set",link:"#set",children:[]},{level:2,title:"Map",slug:"map",link:"#map",children:[]}],path:"/java/recommend-practices-for-collections-naming-convention.html",pathLocale:"/",extraFields:[]},{title:"根据时间范围查询推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"分析",slug:"分析",link:"#分析",children:[]},{level:2,title:"实现",slug:"实现",link:"#实现",children:[{level:3,title:"MySQL",slug:"mysql",link:"#mysql",children:[]},{level:3,title:"MyBatis",slug:"mybatis",link:"#mybatis",children:[]},{level:3,title:"LoxalDate",slug:"loxaldate",link:"#loxaldate",children:[]},{level:3,title:"Jackson",slug:"jackson",link:"#jackson",children:[]}]},{level:2,title:"结语",slug:"结语",link:"#结语",children:[]}],path:"/java/recommend-practices-for-query-by-date-range.html",pathLocale:"/",extraFields:[]},{title:"编写函数的最佳实践",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[{level:3,title:"减少重复",slug:"减少重复",link:"#减少重复",children:[]},{level:3,title:"隐藏细节",slug:"隐藏细节",link:"#隐藏细节",children:[]}]},{level:2,title:"建议",slug:"建议",link:"#建议",children:[{level:3,title:"优先根据业务命名",slug:"优先根据业务命名",link:"#优先根据业务命名",children:[]},{level:3,title:"一个函数只做一件事",slug:"一个函数只做一件事",link:"#一个函数只做一件事",children:[]},{level:3,title:"优先使用纯函数",slug:"优先使用纯函数",link:"#优先使用纯函数",children:[]},{level:3,title:"编写不需要返回值的函数",slug:"编写不需要返回值的函数",link:"#编写不需要返回值的函数",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/recommend-practices-for-writing-good-functions.html",pathLocale:"/",extraFields:[]},{title:"枚举的推荐实践",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"Java",slug:"java",link:"#java",children:[{level:3,title:"极简实现",slug:"极简实现",link:"#极简实现",children:[]},{level:3,title:"常见实现",slug:"常见实现",link:"#常见实现",children:[]},{level:3,title:"更好的方式",slug:"更好的方式",link:"#更好的方式",children:[]},{level:3,title:"为什么还要定义数字?",slug:"为什么还要定义数字",link:"#为什么还要定义数字",children:[]}]},{level:2,title:"数据库",slug:"数据库",link:"#数据库",children:[{level:3,title:"排序特点",slug:"排序特点",link:"#排序特点",children:[]},{level:3,title:"添加新值",slug:"添加新值",link:"#添加新值",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/java/using-enum-in-java.html",pathLocale:"/",extraFields:[]},{title:"Boolean 还是 boolean?",headers:[{level:2,title:"结论",slug:"结论",link:"#结论",children:[]},{level:2,title:"争议",slug:"争议",link:"#争议",children:[]},{level:2,title:"实战",slug:"实战",link:"#实战",children:[{level:3,title:"简单例子",slug:"简单例子",link:"#简单例子",children:[]},{level:3,title:"复杂例子",slug:"复杂例子",link:"#复杂例子",children:[]}]},{level:2,title:"附",slug:"附",link:"#附",children:[]}],path:"/java/which-one-is-better-Boolean-or-boolean.html",pathLocale:"/",extraFields:[]},{title:"forEach 还是 map?",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]},{level:2,title:"解析",slug:"解析",link:"#解析",children:[]},{level:2,title:"实战",slug:"实战",link:"#实战",children:[]}],path:"/java/which-one-is-better-forEach-or-map.html",pathLocale:"/",extraFields:[]},{title:"Jackson 经典异常 UnrecognizedPropertyException",headers:[],path:"/java/why-i-prefer-fastjson-instead-of-jackson.html",pathLocale:"/",extraFields:[]},{title:"升个jar版本,怎么这么难?",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"接口调用示意图",slug:"接口调用示意图",link:"#接口调用示意图",children:[]},{level:2,title:"已知信息",slug:"已知信息",link:"#已知信息",children:[]},{level:2,title:"问题",slug:"问题",link:"#问题",children:[]},{level:2,title:"复盘",slug:"复盘",link:"#复盘",children:[]}],path:"/java/why-is-it-so-hard-to-upgrade-dependencies.html",pathLocale:"/",extraFields:[]},{title:"数据备份案例:mysqldump实战",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"架构",slug:"架构",link:"#架构",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"导出",slug:"导出",link:"#导出",children:[{level:3,title:"--set-gtid-purged=OFF",slug:"set-gtid-purged-off",link:"#set-gtid-purged-off",children:[]},{level:3,title:"--ignore-table",slug:"ignore-table",link:"#ignore-table",children:[]}]},{level:2,title:"导入",slug:"导入",link:"#导入",children:[]},{level:2,title:"为什么不?",slug:"为什么不",link:"#为什么不",children:[]}],path:"/mysql/mysql-backup-case-study-mysqldump-in-action.html",pathLocale:"/",extraFields:[]},{title:"数据迁移案例:表AUTO_INCREMENT加10w",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"备份",slug:"备份",link:"#备份",children:[]},{level:2,title:"SQL编写",slug:"sql编写",link:"#sql编写",children:[]},{level:2,title:"存储过程(可复用",slug:"存储过程-可复用",link:"#存储过程-可复用",children:[]}],path:"/mysql/mysql-data-migration-case-study-add-auto-increment.html",pathLocale:"/",extraFields:[]},{title:"MySQL 命令行执行SQL的细节",headers:[{level:2,title:"背景",slug:"背景",link:"#背景",children:[]},{level:2,title:"环境说明",slug:"环境说明",link:"#环境说明",children:[]},{level:2,title:"执行SQL文件",slug:"执行sql文件",link:"#执行sql文件",children:[]},{level:2,title:"复制粘贴执行",slug:"复制粘贴执行",link:"#复制粘贴执行",children:[]},{level:2,title:"如果要删除错误的数据怎么办?",slug:"如果要删除错误的数据怎么办",link:"#如果要删除错误的数据怎么办",children:[]}],path:"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",pathLocale:"/",extraFields:[]},{title:"给LLM应用添加日志",headers:[{level:2,title:"logging替代print",slug:"logging替代print",link:"#logging替代print",children:[]},{level:2,title:"何时打印日志",slug:"何时打印日志",link:"#何时打印日志",children:[{level:3,title:"外部调用",slug:"外部调用",link:"#外部调用",children:[]},{level:3,title:"异常捕获",slug:"异常捕获",link:"#异常捕获",children:[]},{level:3,title:"提前返回",slug:"提前返回",link:"#提前返回",children:[]},{level:3,title:"复杂或特殊的if-else",slug:"复杂或特殊的if-else",link:"#复杂或特殊的if-else",children:[]}]}],path:"/python/add-logging-for-llm-app.html",pathLocale:"/",extraFields:[]},{title:"使用Ragas评估LLM应用",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"数据说明",slug:"数据说明",link:"#数据说明",children:[]},{level:2,title:"正确性❌",slug:"正确性❌",link:"#正确性❌",children:[]},{level:2,title:"忠实度✔",slug:"忠实度✔",link:"#忠实度✔",children:[]},{level:2,title:"实战演示",slug:"实战演示",link:"#实战演示",children:[{level:3,title:"准备好样例问题",slug:"准备好样例问题",link:"#准备好样例问题",children:[]},{level:3,title:"准备好正确答案",slug:"准备好正确答案",link:"#准备好正确答案",children:[]},{level:3,title:"编写回答函数",slug:"编写回答函数",link:"#编写回答函数",children:[]},{level:3,title:"进行答案评估",slug:"进行答案评估",link:"#进行答案评估",children:[]}]}],path:"/python/evaluate-llm-app-with-ragas.html",pathLocale:"/",extraFields:[]},{title:"Python 导出 MySQL 库表信息到 Excel",headers:[{level:2,title:"需求",slug:"需求",link:"#需求",children:[]},{level:2,title:"代码",slug:"代码",link:"#代码",children:[]},{level:2,title:"其他细节",slug:"其他细节",link:"#其他细节",children:[]}],path:"/python/export-mysql-table-into-excel.html",pathLocale:"/",extraFields:[]},{title:"mr.py",headers:[{level:2,title:"源文件:main.py",slug:"源文件-main-py",link:"#源文件-main-py",children:[]},{level:2,title:"测试文件:test/test_mr.py",slug:"测试文件-test-test-mr-py",link:"#测试文件-test-test-mr-py",children:[]}],path:"/python/mr.py.html",pathLocale:"/",extraFields:[]},{title:"单元测试概述",headers:[{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"How",slug:"how",link:"#how",children:[{level:3,title:"测试代码的风格",slug:"测试代码的风格",link:"#测试代码的风格",children:[]},{level:3,title:"测试难点",slug:"测试难点",link:"#测试难点",children:[]},{level:3,title:"常用工具",slug:"常用工具",link:"#常用工具",children:[]}]},{level:2,title:"Bad Examples",slug:"bad-examples",link:"#bad-examples",children:[{level:3,title:"没有测试类",slug:"没有测试类",link:"#没有测试类",children:[]},{level:3,title:"没有断言",slug:"没有断言",link:"#没有断言",children:[]},{level:3,title:"无法重复执行",slug:"无法重复执行",link:"#无法重复执行",children:[]}]}],path:"/software-testing/unit-testing-overview.html",pathLocale:"/",extraFields:[]},{title:"使用 RestAssured 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"What",slug:"what",link:"#what",children:[]},{level:2,title:"Why",slug:"why",link:"#why",children:[]},{level:2,title:"为什么不用Postman",slug:"为什么不用postman",link:"#为什么不用postman",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"快速上手",slug:"快速上手",link:"#快速上手",children:[]},{level:2,title:"通用设置",slug:"通用设置",link:"#通用设置",children:[]},{level:2,title:"请求示例",slug:"请求示例",link:"#请求示例",children:[]},{level:2,title:"接口依赖",slug:"接口依赖",link:"#接口依赖",children:[]},{level:2,title:"上传示例",slug:"上传示例",link:"#上传示例",children:[]},{level:2,title:"下载示例",slug:"下载示例",link:"#下载示例",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他问题",slug:"其他问题",link:"#其他问题",children:[{level:3,title:"为什么不用 Pytest",slug:"为什么不用-pytest",link:"#为什么不用-pytest",children:[]},{level:3,title:"这也是单元测试吗",slug:"这也是单元测试吗",link:"#这也是单元测试吗",children:[]}]},{level:2,title:"参考资料",slug:"参考资料",link:"#参考资料",children:[]}],path:"/software-testing/use-RestAssured-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Cypress 进行端对端测试",headers:[{level:2,title:"为什么写端对端测试",slug:"为什么写端对端测试",link:"#为什么写端对端测试",children:[]},{level:2,title:"为什么用 Cypress",slug:"为什么用-cypress",link:"#为什么用-cypress",children:[]},{level:2,title:"快速开始",slug:"快速开始",link:"#快速开始",children:[{level:3,title:"安装",slug:"安装",link:"#安装",children:[]},{level:3,title:"加速下载",slug:"加速下载",link:"#加速下载",children:[]},{level:3,title:"目录结构",slug:"目录结构",link:"#目录结构",children:[]},{level:3,title:"与 Jest 协同工作",slug:"与-jest-协同工作",link:"#与-jest-协同工作",children:[]},{level:3,title:"检查依赖及生产安装依赖命令",slug:"检查依赖及生产安装依赖命令",link:"#检查依赖及生产安装依赖命令",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"更复杂的示例",slug:"更复杂的示例",link:"#更复杂的示例",children:[]}]},{level:2,title:"结合TypeScript",slug:"结合typescript",link:"#结合typescript",children:[]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"直接运行 Cypress",slug:"直接运行-cypress",link:"#直接运行-cypress",children:[]},{level:3,title:"使用 start-server-and-test",slug:"使用-start-server-and-test",link:"#使用-start-server-and-test",children:[]},{level:3,title:"This job is stuck",slug:"this-job-is-stuck",link:"#this-job-is-stuck",children:[]},{level:3,title:"Cypress Dashbord",slug:"cypress-dashbord",link:"#cypress-dashbord",children:[]}]},{level:2,title:"总结",slug:"总结",link:"#总结",children:[]},{level:2,title:"拓展阅读",slug:"拓展阅读",link:"#拓展阅读",children:[]}],path:"/software-testing/use-cypress-for-e2e-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Jest 实践测试驱动开发",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"环境搭建",slug:"环境搭建",link:"#环境搭建",children:[]},{level:2,title:"开发",slug:"开发",link:"#开发",children:[{level:3,title:"文件初始化",slug:"文件初始化",link:"#文件初始化",children:[]},{level:3,title:"第一个用例",slug:"第一个用例",link:"#第一个用例",children:[]},{level:3,title:"第二个用例",slug:"第二个用例",link:"#第二个用例",children:[]},{level:3,title:"第三个用例",slug:"第三个用例",link:"#第三个用例",children:[]},{level:3,title:"第四个用例",slug:"第四个用例",link:"#第四个用例",children:[]},{level:3,title:"第五个用例",slug:"第五个用例",link:"#第五个用例",children:[]},{level:3,title:"重构",slug:"重构",link:"#重构",children:[]}]},{level:2,title:"结论",slug:"结论",link:"#结论",children:[]}],path:"/software-testing/use-jest-for-test-driven-development.html",pathLocale:"/",extraFields:[]},{title:"下一代 UI 自动化测试工具 Playwright",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"安装",slug:"安装",link:"#安装",children:[]},{level:2,title:"使用",slug:"使用",link:"#使用",children:[{level:3,title:"代码生成",slug:"代码生成",link:"#代码生成",children:[]},{level:3,title:"修改代码",slug:"修改代码",link:"#修改代码",children:[]},{level:3,title:"执行用例",slug:"执行用例",link:"#执行用例",children:[]},{level:3,title:"调试用例",slug:"调试用例",link:"#调试用例",children:[]},{level:3,title:"查看报告",slug:"查看报告",link:"#查看报告",children:[]}]},{level:2,title:"常见场景与解决方案",slug:"常见场景与解决方案",link:"#常见场景与解决方案",children:[{level:3,title:"应用登录",slug:"应用登录",link:"#应用登录",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"超时时间",slug:"超时时间",link:"#超时时间",children:[]},{level:3,title:"元素选择",slug:"元素选择",link:"#元素选择",children:[]},{level:3,title:"声明断言 && 检查元素是否存在",slug:"声明断言-检查元素是否存在",link:"#声明断言-检查元素是否存在",children:[]},{level:3,title:"获取第n个元素",slug:"获取第n个元素",link:"#获取第n个元素",children:[]},{level:3,title:"遍历元素",slug:"遍历元素",link:"#遍历元素",children:[]},{level:3,title:"获取元素属性",slug:"获取元素属性",link:"#获取元素属性",children:[]},{level:3,title:"判断子元素数量",slug:"判断子元素数量",link:"#判断子元素数量",children:[]},{level:3,title:"鼠标悬浮",slug:"鼠标悬浮",link:"#鼠标悬浮",children:[]},{level:3,title:"操作剪贴板",slug:"操作剪贴板",link:"#操作剪贴板",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"setup.py bdist_wheel did not run successfully",slug:"setup-py-bdist-wheel-did-not-run-successfully",link:"#setup-py-bdist-wheel-did-not-run-successfully",children:[]}]}],path:"/software-testing/use-playwright-for-ui-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 Postman 进行 API 测试",headers:[{level:2,title:"前言",slug:"前言",link:"#前言",children:[]},{level:2,title:"本地调试",slug:"本地调试",link:"#本地调试",children:[{level:3,title:"接口集合",slug:"接口集合",link:"#接口集合",children:[]},{level:3,title:"从 cURL 导入",slug:"从-curl-导入",link:"#从-curl-导入",children:[]},{level:3,title:"环境变量",slug:"环境变量",link:"#环境变量",children:[]},{level:3,title:"请求设置",slug:"请求设置",link:"#请求设置",children:[]}]},{level:2,title:"接口测试",slug:"接口测试",link:"#接口测试",children:[{level:3,title:"编写用例",slug:"编写用例",link:"#编写用例",children:[]},{level:3,title:"上传文件",slug:"上传文件",link:"#上传文件",children:[]},{level:3,title:"运行集合",slug:"运行集合",link:"#运行集合",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[{level:3,title:"导出接口",slug:"导出接口",link:"#导出接口",children:[]},{level:3,title:"导出环境变量",slug:"导出环境变量",link:"#导出环境变量",children:[]},{level:3,title:"复制要上传的文件",slug:"复制要上传的文件",link:"#复制要上传的文件",children:[]},{level:3,title:"提交到Git",slug:"提交到git",link:"#提交到git",children:[]},{level:3,title:"建立CI任务",slug:"建立ci任务",link:"#建立ci任务",children:[]}]}],path:"/software-testing/use-postman-for-api-testing.html",pathLocale:"/",extraFields:[]},{title:"使用 pytest 为LLM应用添加回归测试",headers:[{level:2,title:"回归测试的必要性",slug:"回归测试的必要性",link:"#回归测试的必要性",children:[]},{level:2,title:"pytest",slug:"pytest",link:"#pytest",children:[{level:3,title:"安装",slug:"安装",link:"#安装",children:[]},{level:3,title:"配置",slug:"配置",link:"#配置",children:[]},{level:3,title:"用例",slug:"用例",link:"#用例",children:[]}]},{level:2,title:"持续集成",slug:"持续集成",link:"#持续集成",children:[]}],path:"/software-testing/use-pytest-for-regression-testing-in-llm-app.html",pathLocale:"/",extraFields:[]},{title:"科学上网",headers:[{level:2,title:"说明",slug:"说明",link:"#说明",children:[]},{level:2,title:"购买指南",slug:"购买指南",link:"#购买指南",children:[]},{level:2,title:"客户端",slug:"客户端",link:"#客户端",children:[]},{level:2,title:"导入配置",slug:"导入配置",link:"#导入配置",children:[]},{level:2,title:"其他",slug:"其他",link:"#其他",children:[{level:3,title:"500 内部代理错误",slug:"_500-内部代理错误",link:"#_500-内部代理错误",children:[]},{level:3,title:"修改PAC文件",slug:"修改pac文件",link:"#修改pac文件",children:[]},{level:3,title:"使用 New Bing",slug:"使用-new-bing",link:"#使用-new-bing",children:[]},{level:3,title:"申请美区apple id",slug:"申请美区apple-id",link:"#申请美区apple-id",children:[]}]}],path:"/tools/how-to-connect-to-internet.html",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]},{title:"Devops",headers:[],path:"/devops/",pathLocale:"/",extraFields:[]},{title:"Daily",headers:[],path:"/daily/",pathLocale:"/",extraFields:[]},{title:"English",headers:[],path:"/english/",pathLocale:"/",extraFields:[]},{title:"Frontend",headers:[],path:"/frontend/",pathLocale:"/",extraFields:[]},{title:"Git",headers:[],path:"/git/",pathLocale:"/",extraFields:[]},{title:"Java",headers:[],path:"/java/",pathLocale:"/",extraFields:[]},{title:"Mysql",headers:[],path:"/mysql/",pathLocale:"/",extraFields:[]},{title:"Python",headers:[],path:"/python/",pathLocale:"/",extraFields:[]},{title:"Software Testing",headers:[],path:"/software-testing/",pathLocale:"/",extraFields:[]},{title:"Tools",headers:[],path:"/tools/",pathLocale:"/",extraFields:[]},{title:"分类",headers:[],path:"/category/",pathLocale:"/",extraFields:[]},{title:"标签",headers:[],path:"/tag/",pathLocale:"/",extraFields:[]},{title:"文章",headers:[],path:"/article/",pathLocale:"/",extraFields:[]},{title:"收藏",headers:[],path:"/star/",pathLocale:"/",extraFields:[]},{title:"时间轴",headers:[],path:"/timeline/",pathLocale:"/",extraFields:[]},{title:"标签: Daily",headers:[],path:"/tag/daily/",pathLocale:"/",extraFields:[]},{title:"标签: DevOps",headers:[],path:"/tag/devops/",pathLocale:"/",extraFields:[]},{title:"标签: Linux",headers:[],path:"/tag/linux/",pathLocale:"/",extraFields:[]},{title:"标签: Frontend",headers:[],path:"/tag/frontend/",pathLocale:"/",extraFields:[]},{title:"标签: S3",headers:[],path:"/tag/s3/",pathLocale:"/",extraFields:[]},{title:"标签: OBS",headers:[],path:"/tag/obs/",pathLocale:"/",extraFields:[]},{title:"标签: OSS",headers:[],path:"/tag/oss/",pathLocale:"/",extraFields:[]},{title:"标签: Python",headers:[],path:"/tag/python/",pathLocale:"/",extraFields:[]},{title:"标签: Video",headers:[],path:"/tag/video/",pathLocale:"/",extraFields:[]},{title:"标签: MySQL",headers:[],path:"/tag/mysql/",pathLocale:"/",extraFields:[]},{title:"标签: AI",headers:[],path:"/tag/ai/",pathLocale:"/",extraFields:[]},{title:"标签: Emotion",headers:[],path:"/tag/emotion/",pathLocale:"/",extraFields:[]},{title:"标签: Working Experience",headers:[],path:"/tag/working-experience/",pathLocale:"/",extraFields:[]},{title:"标签: Tool",headers:[],path:"/tag/tool/",pathLocale:"/",extraFields:[]},{title:"标签: Design",headers:[],path:"/tag/design/",pathLocale:"/",extraFields:[]},{title:"标签: English",headers:[],path:"/tag/english/",pathLocale:"/",extraFields:[]},{title:"标签: Git",headers:[],path:"/tag/git/",pathLocale:"/",extraFields:[]},{title:"标签: GitLab",headers:[],path:"/tag/gitlab/",pathLocale:"/",extraFields:[]},{title:"标签: Java",headers:[],path:"/tag/java/",pathLocale:"/",extraFields:[]},{title:"标签: Node.js",headers:[],path:"/tag/node.js/",pathLocale:"/",extraFields:[]},{title:"标签: JavaScript",headers:[],path:"/tag/javascript/",pathLocale:"/",extraFields:[]},{title:"标签: llm",headers:[],path:"/tag/llm/",pathLocale:"/",extraFields:[]},{title:"标签: Testing",headers:[],path:"/tag/testing/",pathLocale:"/",extraFields:[]}],V2=W($2),F2=()=>V2,N2=({searchIndex:e,routeLocale:t,query:l,maxSuggestions:n})=>{const a=E(()=>e.value.filter(i=>i.pathLocale===t.value));return E(()=>{const i=l.value.trim().toLowerCase();if(!i)return[];const r=[],o=(c,u)=>{xs(i,[u.title])&&r.push({link:`${c.path}#${u.slug}`,title:c.title,header:u.title});for(const d of u.children){if(r.length>=n.value)return;o(c,d)}};for(const c of a.value){if(r.length>=n.value)break;if(xs(i,[c.title,...c.extraFields])){r.push({link:c.path,title:c.title});continue}for(const u of c.headers){if(r.length>=n.value)break;o(c,u)}}return r})},j2=e=>{const t=W(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},B2=$({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:l,maxSuggestions:n}=Sd(e),a=Ve(),i=mt(),r=F2(),o=W(null),c=W(!1),u=W(""),d=E(()=>t.value[i.value]??{}),p=N2({searchIndex:r,routeLocale:i,query:u,maxSuggestions:n}),{focusIndex:h,focusNext:v,focusPrev:b}=j2(p);M2({input:o,hotKeys:l});const w=E(()=>c.value&&!!p.value.length),L=()=>{w.value&&b()},m=()=>{w.value&&v()},_=I=>{if(!w.value)return;const R=p.value[I];R&&a.push(R.link).then(()=>{u.value="",h.value=0})};return()=>s("form",{class:"search-box",role:"search"},[s("input",{ref:o,type:"search",placeholder:d.value.placeholder,autocomplete:"off",spellcheck:!1,value:u.value,onFocus:()=>c.value=!0,onBlur:()=>c.value=!1,onInput:I=>u.value=I.target.value,onKeydown:I=>{switch(I.key){case"ArrowUp":{L();break}case"ArrowDown":{m();break}case"Enter":{I.preventDefault(),_(h.value);break}}}}),w.value&&s("ul",{class:"suggestions",onMouseleave:()=>h.value=-1},p.value.map(({link:I,title:R,header:B},C)=>s("li",{class:["suggestion",{focus:h.value===C}],onMouseenter:()=>h.value=C,onMousedown:()=>_(C)},s("a",{href:I,onClick:V=>V.preventDefault()},[s("span",{class:"page-title"},R),B&&s("span",{class:"page-header"},`> ${B}`)]))))])}});const H2={},z2=["s","/"],q2=5,G2=dt({enhance({app:e}){e.component("SearchBox",t=>s(B2,{locales:H2,hotKeys:z2,maxSuggestions:q2,...t}))}}),U2={enhance:({app:e})=>{}},Rn=[N0,Z1,rv,dv,vv,yv,Ev,Iv,Dv,qv,I2,G2,U2],W2=[["v-8daa1a0e","/",{y:"h",t:"levy's blog",i:"home",O:null},["/README.md"]],["v-22a39d25","/about.html",{y:"p",t:"关于",i:"circle-info",O:null},[":md"]],["v-3f274907","/devops/about-arm-things-you-need-to-know.html",{d:16909344e5,l:"2023年8月2日",g:["Daily","DevOps","Linux"],e:`

关于 Arm 你需要了解的三件事

-

Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

-

跟我们有什么关系呢?

-
    -
  1. MacOS 的 M1 芯片是基于 Arm 的
  2. -
  3. 云厂商及生态都在积极与 Arm 进行合作
  4. -
  5. Docker 镜像的构建有注意事项
  6. -
-

构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
-这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。
-

`,r:{minutes:.72,words:217},y:"a",t:"关于 Arm 你需要了解的三件事",O:-16909344e5},[":md"]],["v-6614b1a1","/devops/common-solutions-of-object-storage-for-static-assets.html",{d:15544224e5,l:"2019年4月5日",g:["Frontend","DevOps","S3","OBS","OSS"],e:`

对象存储静态资源常见操作

-

前言

-

把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

-

本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

-`,r:{minutes:3.03,words:908},y:"a",t:"对象存储静态资源常见操作",O:-15544224e5},[":md"]],["v-2e81d6a4","/devops/docker-build-and-push-script.html",{d:17019936e5,l:"2023年12月8日",g:["DevOps","Linux"],e:`

Docker 构建镜像、推送、启动实用脚本

-

misc

-

存储多份 docker 认证信息:

-
mkdir "~/.project1"
-mkdir "~/.project2"
-
-docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token>
-docker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token> 
-
`,r:{minutes:1.34,words:403},y:"a",t:"Docker 构建镜像、推送、启动实用脚本",O:-17019936e5},[":md"]],["v-3c0c097a","/devops/reduce-python-image-size.html",{d:170208e7,l:"2023年12月9日",g:["DevOps","Python"],e:`

缩减Python应用的镜像体积

-

背景

-

当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!
-
-能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

-`,r:{minutes:2.29,words:687},y:"a",t:"缩减Python应用的镜像体积",O:-170208e7},[":md"]],["v-daf302c6","/devops/what-is-the-difference-between-sh-and-bash.html",{d:16910208e5,l:"2023年8月3日",g:["Linux","DevOps","Video"],e:`

sh与bash的区别

-

结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

-

常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

-`,r:{minutes:1.06,words:318},y:"a",t:"sh与bash的区别",O:-16910208e5},[":md"]],["v-30a50b00","/daily/a-vuepress2-entertaining-video.html",{d:1690848e6,l:"2023年8月1日",g:["Daily","Frontend","Video"],e:`

VuePress2 娱乐视频

-

参考《原神,启动》的梗,做的一个娱乐向视频。

-`,r:{minutes:.11,words:34},y:"a",t:"VuePress2 娱乐视频",O:-1690848e6},[":md"]],["v-0481cc80","/daily/a-warning-from-navicat.html",{d:16907616e5,l:"2023年7月31日",g:["Daily","Video"],e:`

来自Navicat的侵权警告

-

公司收到了Navicat的侵权警告, 很有可能要吃官司。在此还是呼吁大家使用正版,拒绝使用盗版软件。

-

另外,开发者常用的软件的合法替代品,视频中也有推荐。

-`,r:{minutes:.27,words:81},y:"a",t:"来自Navicat的侵权警告",O:-16907616e5},[":md"]],["v-79e139f8","/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html",{d:16920576e5,l:"2023年8月15日",g:["Daily","Video","MySQL"],e:`

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

-

Background

-

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

-
CREATE TABLE \`my_table\` (
-  \`id\` bigint NOT NULL AUTO_INCREMENT,
-  PRIMARY KEY (\`id\`) USING BTREE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-

That is the knowledge that today I want to share with you.

-`,r:{minutes:1.78,words:533},y:"a",t:"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?",O:-16920576e5},[":md"]],["v-7a74360a","/daily/claude-ai-in-action-extract-info-from-html.html",{d:16929216e5,l:"2023年8月25日",g:["Daily","Video","AI"],e:`

Claude AI应用案例,从HTML中抽取文本

-

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

-`,r:{minutes:.19,words:58},y:"a",t:"Claude AI应用案例,从HTML中抽取文本",O:-16929216e5},[":md"]],["v-76f251d2","/daily/copy-code-may-not-be-guilty.html",{d:16955136e5,l:"2023年9月24日",g:["Daily"],e:`

复制代码也许不是罪

-

前言

-

熟悉我的人都知道,我对代码是有追求的。

-

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

-

我早期认为:复制代码就是菜。

-

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

-

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

-

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

-`,r:{minutes:1.51,words:453},y:"a",t:"复制代码也许不是罪",O:-16955136e5},[":md"]],["v-f47f129e","/daily/dont-try-to-argue-with-a-sb.html",{d:16911072e5,l:"2023年8月4日",g:["Daily","Emotion","Working Experience","Video"],e:`

不要与傻逼进行争吵

-

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

-

我在沟通上,有以下可以改正的点:
-0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

-
    -
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. -
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. -
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. -
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. -
-`,r:{minutes:3.92,words:1176},y:"a",t:"不要与傻逼进行争吵",O:-16911072e5},[":md"]],["v-fd7b7f92","/daily/iteration-retrospective-of-sanyuan.html",{d:16941312e5,l:"2023年9月8日",g:["Daily","Working Experience"],e:`

迭代复盘之三员管理

-

前言

-

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

-

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

-

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

-`,r:{minutes:2.52,words:756},y:"a",t:"迭代复盘之三员管理",O:-16941312e5},[":md"]],["v-81a71778","/daily/leverage-ai-to-boost-coding-productivity.html",{d:1693008e6,l:"2023年8月26日",g:["AI","Daily"],e:`

都什么年代了,还在用传统方式写代码?

-

前言

-

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

-

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

-

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

`,r:{minutes:7.37,words:2212},y:"a",t:"都什么年代了,还在用传统方式写代码?",O:-1693008e6},[":md"]],["v-5c48d497","/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html",{d:1694304e6,l:"2023年9月10日",g:["Daily","Video"],e:`

微软中国CTO演讲观后感

-

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

-`,r:{minutes:.17,words:51},y:"a",t:"微软中国CTO演讲观后感",O:-1694304e6},[":md"]],["v-4f919602","/daily/testing-environments-should-be-consistent-with-production-environments.html",{d:16997472e5,l:"2023年11月12日",g:["Daily"],e:`

生产教训:测试环境要与生产环境一致

-

事件还原

-

业务流程:

-
    -
  1. app-a 上传文件
  2. -
  3. app-b 下载文件后使用文件
  4. -
-

其他信息:

-
    -
  1. 开发、测试环境使用 MinIO
  2. -
  3. 生产环境使用 Amazon S3
  4. -
-

问题:

-
    -
  1. app-a 上传文件成功
  2. -
  3. app-b 使用文件报错
  4. -
-

逐步分析定位问题:

-
    -
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. -
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. -
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. -
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. -
`,r:{minutes:2.25,words:676},y:"a",t:"生产教训:测试环境要与生产环境一致",O:-16997472e5},[":md"]],["v-1e305501","/daily/things-I-have-to-vent-about-vue.html",{d:16906752e5,l:"2023年7月30日",g:["Frontend","Daily","Video"],e:`

对Vue不得不吐槽的事

-

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

-

总结一下,我对Vue生态不满的地方在于:

-
    -
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. -
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. -
-`,r:{minutes:.42,words:126},y:"a",t:"对Vue不得不吐槽的事",O:-16906752e5},[":md"]],["v-404740fa","/daily/use-claude2-instead-of-chatgpt.html",{d:16914528e5,l:"2023年8月8日",g:["Video","AI","Tool"],e:`

再见ChatGPT,我选择Claude2!

-

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

-

首先要评测的当然是ChatGPT了,因为最早用的就是它。

-`,r:{minutes:3.76,words:1128},y:"a",t:"再见ChatGPT,我选择Claude2!",O:-16914528e5},[":md"]],["v-f1efc11c","/daily/vim-creator-pass-away.html",{d:169128e7,l:"2023年8月6日",g:["Daily","Video"],e:`

Vim 作者离世

-

R.I.P 🙏

-

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

-

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

-`,r:{minutes:.24,words:71},y:"a",t:"Vim 作者离世",O:-169128e7},[":md"]],["v-bd2b5fe8","/daily/you-dont-need-to-add-tenant_id-to-every-table.html",{d:16949952e5,l:"2023年9月18日",g:["Design","Daily"],e:`

技术点评:别每张表都加tenant_id

-

前言

-

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

-`,r:{minutes:3.26,words:977},y:"a",t:"技术点评:别每张表都加tenant_id",O:-16949952e5},[":md"]],["v-971cc7fe","/english/contemporary-college-english-1.html",{d:16537824e5,l:"2022年5月29日",g:"English",e:`

现代大学英语精读(第2版)第一册

-

介绍

-

全书链接:https://www.ximalaya.com/album/43891910

-

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

-

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

`,r:{minutes:1.38,words:414},y:"a",t:"现代大学英语精读(第2版)第一册",O:-16537824e5},[":md"]],["v-93b316c0","/english/contemporary-college-english-2.html",{d:16543008e5,l:"2022年6月4日",g:"English",e:`

现代大学英语精读(第2版)第二册

-

介绍

-

全书链接:https://www.ximalaya.com/album/44290107

-

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

-

值得一读的文章

-

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

`,r:{minutes:1.96,words:588},y:"a",t:"现代大学英语精读(第2版)第二册",O:-16543008e5},[":md"]],["v-90496582","/english/contemporary-college-english-3.html",{d:16561152e5,l:"2022年6月25日",g:"English",e:`

现代大学英语精读(第2版)第三册

-

介绍

-

全书链接:https://www.ximalaya.com/album/44439108

-

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

`,r:{minutes:1.95,words:586},y:"a",t:"现代大学英语精读(第2版)第三册",O:-16561152e5},[":md"]],["v-8cdfb444","/english/contemporary-college-english-4.html",{d:16574976e5,l:"2022年7月11日",g:"English",e:`

现代大学英语精读(第2版)第四册

-

介绍

-

全书链接:https://www.ximalaya.com/album/44641280

-

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

-

值得一读的文章

-

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

`,r:{minutes:2.02,words:607},y:"a",t:"现代大学英语精读(第2版)第四册",O:-16574976e5},[":md"]],["v-89760306","/english/contemporary-college-english-5.html",{d:16616448e5,l:"2022年8月28日",g:"English",e:`

现代大学英语精读(第2版)第五册

-

介绍

-

全书链接:https://www.ximalaya.com/album/49466046

-

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

-

Who Are You and What Are You Doing Here

`,r:{minutes:7.18,words:2155},y:"a",t:"现代大学英语精读(第2版)第五册",O:-16616448e5},[":md"]],["v-860c51c8","/english/contemporary-college-english-6.html",{d:16622496e5,l:"2022年9月4日",g:"English",e:`

现代大学英语精读(第2版)第六册

-

前言

-

全书链接:https://www.ximalaya.com/album/49468954

-

本册是整个系列的最后一册了,完结撒花🎉

-

Paper Tigers

-

原文链接:https://www.ximalaya.com/sound/414998175

`,r:{minutes:5.08,words:1524},y:"a",t:"现代大学英语精读(第2版)第六册",O:-16622496e5},[":md"]],["v-246f4f17","/english/everyone-can-learn-english-1-overview.html",{d:16559424e5,l:"2022年6月23日",g:"English",e:`

人人都能学会的英语1:开篇

-

为什么学

-

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

-

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

-
    -
  • 出国
  • -
  • 去外企
  • -
  • 有国际化需求
  • -
  • 学习专业领域的前沿知识
  • -
-

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

`,r:{minutes:4.46,words:1337},y:"a",t:"人人都能学会的英语1:开篇",O:-16559424e5},[":md"]],["v-3ac0474c","/english/everyone-can-learn-english-2-pronunciation.html",{d:16562016e5,l:"2022年6月26日",g:"English",e:`

人人都能学会的英语2:音标

-

方法

-

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

-

我推荐根据赖世雄的《美语音标》进行学习:

-
    -
  • 在微信公众号 常春藤英语集团 买相关书籍
  • -
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • -
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • -
-

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

`,r:{minutes:3.61,words:1083},y:"a",t:"人人都能学会的英语2:音标",O:-16562016e5},[":md"]],["v-58a409d7","/english/everyone-can-learn-english-3-words.html",{d:1656288e6,l:"2022年6月27日",g:"English",e:`

人人都能学会的英语3:单词

-

方法

-

单词是听说读写的基础,是有必要花时间去学习的。

-

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

-
    -
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. -
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. -
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. -
  7. 学习软件:欧路词典——免费,全平台通用
  8. -
`,r:{minutes:4,words:1200},y:"a",t:"人人都能学会的英语3:单词",O:-1656288e6},[":md"]],["v-788d194a","/english/everyone-can-learn-english-4-listening-and-speaking.html",{d:16563744e5,l:"2022年6月28日",g:"English",e:`

人人都能学会的英语4:听说

-

前言

-

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

-

方法

-

听说属于综合能力,建议遵循以下学习步骤:

-
    -
  1. 激发兴趣,建立信心
  2. -
  3. 明确方向
  4. -
  5. 脚踏实地,坚持输入并输出
  6. -
-

1.建立信心

-

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

-`,r:{minutes:3.82,words:1146},y:"a",t:"人人都能学会的英语4:听说",O:-16563744e5},[":md"]],["v-ae153a4e","/english/everyone-can-learn-english-5-reading-and-writing.html",{d:16564608e5,l:"2022年6月29日",g:"English",e:`

人人都能学会的英语5:读写

-

前言

-

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

-

阅读能力升级之旅

-

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

-
    -
  1. 看到英文网站,第一反应是点击切换中文版
  2. -
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. -
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. -
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. -
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. -
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. -
`,r:{minutes:6.03,words:1808},y:"a",t:"人人都能学会的英语5:读写",O:-16564608e5},[":md"]],["v-d42db13c","/english/how-to-self-evaluate-english-level.html",{d:16573248e5,l:"2022年7月9日",g:"English",e:`

英文能力评测手把手教学

-

前言

-

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

-

单词量

-

进入网站:https://preply.com/en/learn/english/test-your-vocab

-

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
-image.png
-在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
-image.png
-对于该结果,网站有以下值得注意的解释:

`,r:{minutes:1.85,words:554},y:"a",t:"英文能力评测手把手教学",O:-16573248e5},[":md"]],["v-6ed7d996","/english/learning-7000-words-task-completed.html",{d:16633728e5,l:"2022年9月17日",g:"English",e:`

完成刷7k单词任务

-

image.png
-这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

-

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

-

再者,说明下为什么要做这件事。主要原因有三:

`,r:{minutes:4.54,words:1362},y:"a",t:"完成刷7k单词任务",O:-16633728e5},[":md"]],["v-221efd1f","/english/let-chatgpt-be-your-foreign-language-teacher.html",{d:16833312e5,l:"2023年5月6日",g:["English","AI"],e:`

让 ChatGPT 成为你的外语私教

-

前言

-

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

-

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

-

准备工作

-

在开始之前,要准备好几样东西:

-
    -
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. -
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. -
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. -
`,r:{minutes:2.83,words:849},y:"a",t:"让 ChatGPT 成为你的外语私教",O:-16833312e5},[":md"]],["v-72e84a92","/frontend/old-articles.html",{d:15674688e5,l:"2019年9月3日",g:"Frontend",e:`

旧文章精选

-`,r:{minutes:.3,words:90},y:"a",t:"旧文章精选",O:-15674688e5},[":md"]],["v-7320140c","/frontend/performance-optimization-in-action.html",{d:16117056e5,l:"2021年1月27日",g:"Frontend",e:`

前端项目性能优化实战

-

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

-`,r:{minutes:4.69,words:1407},y:"a",t:"前端项目性能优化实战",O:-16117056e5},[":md"]],["v-1e5872c4","/git/git-best-pratices.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

2020-09-21

-

Git最佳实践

-

精简提交

-

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
-如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

-

频繁提交

-

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
-经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

-

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

`,r:{minutes:3.86,words:1158},y:"a",t:"Git最佳实践",O:-16006464e5},[":md"]],["v-48e494ef","/git/git-definitive-guide-to-merge-code.html",{d:1651104e6,l:"2022年4月28日",g:["Git"],e:`

Git代码合并指南

-

前言

-

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
-在说明问题前,先定义一些概念:

-
    -
  • feat:指代功能分支
  • -
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点: -
      -
    • 受保护,不能直接推送
    • -
    • 不会被删除
    • -
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • -
    -
  • -
  • MR:merge request。代码合并请求
  • -
`,r:{minutes:3.45,words:1035},y:"a",t:"Git代码合并指南",O:-1651104e6},[":md"]],["v-60021cbc","/git/git-history-two-tricks-in-idea.html",{d:1691712e6,l:"2023年8月11日",g:["Git"],e:`

Git查看历史记录小技巧

-

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

-

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

-`,r:{minutes:2.16,words:648},y:"a",t:"Git查看历史记录小技巧",O:-1691712e6},[":md"]],["v-642eaaea","/git/git-useful-commands.html",{d:16006464e5,l:"2020年9月21日",g:"Git",e:`

Git常用命令

-

前言

-

本文将列举Git常见场景,并给出相应解决方案。

-

约定: 下文代码块中\${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

-

推荐: 图形化交互式Git教程

-

配置

-

Mac/Linux 用户 执行以下操作

-
vi ~/.gitconfig
-
`,r:{minutes:5.05,words:1515},y:"a",t:"Git常用命令",O:-16006464e5},[":md"]],["v-4008ff77","/git/gitlab-ci.html",{d:16733088e5,l:"2023年1月10日",g:["Git","GitLab","Java","Node.js"],e:`

GitLab CI

-

前言

-

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

-`,r:{minutes:6.67,words:2e3},y:"a",t:"GitLab CI",O:-16733088e5},[":md"]],["v-0fbf5fdc","/git/rethinking-git-flow.html",{d:16504992e5,l:"2022年4月21日",g:"Git",e:`

再论Git Flow

-

背景

-

团队目前使用的 Git 协作模式是:

-
    -
  1. 对每个功能建立相应的 feat 分支
  2. -
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. -
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. -
-

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

`,r:{minutes:5.53,words:1660},y:"a",t:"再论Git Flow",O:-16504992e5},[":md"]],["v-071be141","/git/use-command-line-tool-to-manage-gitlab-merge-request.html",{d:16795296e5,l:"2023年3月23日",g:["Git","GitLab","Python"],e:`

操作 Gitlab MR 的命令行工具

-

背景

-

为什么开发这个工具?主要解决以下问题:

-
    -
  1. 提测、上 UAT 时,避免漏合代码。
  2. -
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. -
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. -
-

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
-并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
-对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

`,r:{minutes:4.42,words:1325},y:"a",t:"操作 Gitlab MR 的命令行工具",O:-16795296e5},[":md"]],["v-86a15cb6","/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html",{d:16693344e5,l:"2022年11月25日",g:["Java","Daily"],e:`

IDEA常见问题与解决方案

-

启动参数过长

-

Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
-解决方案:

-
    -
  1. 编辑 .idea/workspace.xml
  2. -
  3. 找到 PropertiesComponent
  4. -
  5. 添加:
  6. -
-

或者这样:
-

`,r:{minutes:2.76,words:827},y:"a",t:"IDEA常见问题与解决方案",O:-16693344e5},[":md"]],["v-6dc18ec6","/java/Resolving-Common-Problems-in-Maven.md.html",{d:1670544e6,l:"2022年12月9日",g:["Java","Daily"],e:`

Maven常见问题与解决方案

-

运行 class 找不到主类

-
maven compile
-

得到 class 文件后

-
cd /my-app/target/com/mycompany/app
-java App
-
`,r:{minutes:3.25,words:976},y:"a",t:"Maven常见问题与解决方案",O:-1670544e6},[":md"]],["v-538a98d7","/java/avoid-sending-password-in-plaintext.html",{d:16981056e5,l:"2023年10月24日",g:["Java","JavaScript","Daily"],e:`

避免密码明文传输

-

说明

-

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

-

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

-

流程说明:前端加密,后端解密。

-

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

-`,r:{minutes:1.63,words:488},y:"a",t:"避免密码明文传输",O:-16981056e5},[":md"]],["v-2f6ae09c","/java/check-if-name-exists.html",{d:16975872e5,l:"2023年10月18日",g:["Java","MySQL","Daily"],e:`

检查名字是否重复

-

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

-`,r:{minutes:1.18,words:353},y:"a",t:"检查名字是否重复",O:-16975872e5},[":md"]],["v-b5a78a7a","/java/common-practices-for-handling-excel.html",{d:1693872e6,l:"2023年9月5日",g:["Java","Daily"],e:`

Excel处理常用实践

-

基础知识

-

导入需要用到对象,MultipartFile。

-
@PostMapping("/import")
-public boolean importLicense(
-      @RequestParam("file") MultipartFile file,
-      @RequestParam("tenantId") @NotBlank String tenantId,
-) {
-  return true;
-}
-
`,r:{minutes:4.88,words:1464},y:"a",t:"Excel处理常用实践",O:-1693872e6},[":md"]],["v-100d1814","/java/how-to-convert-snapshot-into-release-jar-without-source-code.html",{d:16917984e5,l:"2023年8月12日",g:["Java"],e:`

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

-

背景

-

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

-

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

-`,r:{minutes:1.41,words:422},y:"a",t:"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包",O:-16917984e5},[":md"]],["v-d767e98e","/java/recommend-practices-for-collections-naming-convention.html",{d:16540416e5,l:"2022年6月1日",g:["Java","Daily"],e:`

集合命名推荐

-

概述

-

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

-

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
-

-

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

`,r:{minutes:3.03,words:908},y:"a",t:"集合命名推荐",O:-16540416e5},[":md"]],["v-f5471cb0","/java/recommend-practices-for-query-by-date-range.html",{d:16944768e5,l:"2023年9月12日",g:["Java","Daily"],e:`

根据时间范围查询推荐实践

-

背景

-

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

-

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

-

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

-`,r:{minutes:3.21,words:962},y:"a",t:"根据时间范围查询推荐实践",O:-16944768e5},[":md"]],["v-57d9b582","/java/recommend-practices-for-writing-good-functions.html",{d:16657056e5,l:"2022年10月14日",g:["Java","Daily"],e:`

编写函数的最佳实践

-

前言

-

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

-

本文将推荐一些编写函数的最佳实践,以供参数。

-

减少重复

-

这是在遵守 Don't repeat yourself (DRY) 原则。

-

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

`,r:{minutes:5.88,words:1764},y:"a",t:"编写函数的最佳实践",O:-16657056e5},[":md"]],["v-2ba0b01e","/java/using-enum-in-java.html",{d:16657056e5,l:"2022年10月14日",g:["Java","Daily"],e:`

枚举的推荐实践

-

背景

-

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

-

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

-

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

-

Java

-

极简实现

-

理想状态下,枚举就应该这样简单!

-
public enum SEX {
-  MALE,
-  FEMALE;
-}
-
`,r:{minutes:5.59,words:1676},y:"a",t:"枚举的推荐实践",O:-16657056e5},[":md"]],["v-386e94f1","/java/which-one-is-better-Boolean-or-boolean.html",{d:1654128e6,l:"2022年6月2日",g:["Java","Daily"],e:`

Boolean 还是 boolean?

-

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

-

结论

-

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

-

原文如下:
-

`,r:{minutes:3.48,words:1045},y:"a",t:"Boolean 还是 boolean?",O:-1654128e6},[":md"]],["v-0d6828c2","/java/which-one-is-better-forEach-or-map.html",{d:16491168e5,l:"2022年4月5日",g:["Java","Daily"],e:`

forEach 还是 map?

-

背景

-

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

-
List<Type> result = new ArrayList<>();
-list.forEach(src -> {
-    Type target = BeanUtils.copyProperties(src, target);
-    //省略代码
-    result.add(target);
-});
-
`,r:{minutes:3.67,words:1102},y:"a",t:"forEach 还是 map?",O:-16491168e5},[":md"]],["v-1aafac08","/java/why-i-prefer-fastjson-instead-of-jackson.html",{d:1692576e6,l:"2023年8月21日",g:["Java","Daily","Video"],e:`

Jackson 经典异常 UnrecognizedPropertyException

-

原因是 json 包含的字段,多于 Java 实体类定义的字段。

-

解决方法很简单:

-
new ObjectMapper()
-  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
-

或者为相关实体添加注解:

-
@JsonIgnoreProperties(ignoreUnknown = true)
-public class ObjectParseFromJsonString {  }
-
`,r:{minutes:.36,words:109},y:"a",t:"Jackson 经典异常 UnrecognizedPropertyException",O:-1692576e6},[":md"]],["v-ca672354","/java/why-is-it-so-hard-to-upgrade-dependencies.html",{d:1693008e6,l:"2023年8月26日",g:["Java","Daily","Video"],e:`

升个jar版本,怎么这么难?

-

前言

-

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

-

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

-

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

-`,r:{minutes:1.8,words:540},y:"a",t:"升个jar版本,怎么这么难?",O:-1693008e6},[":md"]],["v-143a8bce","/mysql/mysql-backup-case-study-mysqldump-in-action.html",{d:16922304e5,l:"2023年8月17日",g:["Daily","MySQL"],e:`

数据备份案例:mysqldump实战

-

背景

-

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

-

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

-

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

-`,r:{minutes:4.1,words:1231},y:"a",t:"数据备份案例:mysqldump实战",O:-16922304e5},[":md"]],["v-7e2c7a0c","/mysql/mysql-data-migration-case-study-add-auto-increment.html",{d:1692144e6,l:"2023年8月16日",g:["Daily","MySQL"],e:`

数据迁移案例:表AUTO_INCREMENT加10w

-

背景

-

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

-

问题分析:

-
    -
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. -
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. -
-

迁移思路:

-
    -
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. -
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. -
  5. 记得动手前先确保数据已备份
  6. -
-

注意:

-
    -
  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • -
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
  • -
-`,r:{minutes:2,words:599},y:"a",t:"数据迁移案例:表AUTO_INCREMENT加10w",O:-1692144e6},[":md"]],["v-f297935a","/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html",{d:16924032e5,l:"2023年8月19日",g:["Daily","MySQL"],e:`

MySQL 命令行执行SQL的细节

-

背景

-

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

-

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

-

环境说明

-

先说明下我们的环境信息。
-
-我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

`,r:{minutes:3.02,words:906},y:"a",t:"MySQL 命令行执行SQL的细节",O:-16924032e5},[":md"]],["v-de221860","/python/add-logging-for-llm-app.html",{d:1701648e6,l:"2023年12月4日",g:["Python"],e:`

给LLM应用添加日志

-

logging替代print

-

目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。

-

print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。

-`,r:{minutes:3.3,words:989},y:"a",t:"给LLM应用添加日志",O:-1701648e6},[":md"]],["v-39c09a30","/python/evaluate-llm-app-with-ragas.html",{d:17121024e5,l:"2024年4月3日",g:["Python","llm"],e:`

使用Ragas评估LLM应用

-

说明

-

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

-

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

-

安装

-
pip install ragas
-
`,r:{minutes:2.71,words:813},y:"a",t:"使用Ragas评估LLM应用",O:-17121024e5},[":md"]],["v-5b37b3c6","/python/export-mysql-table-into-excel.html",{d:16779744e5,l:"2023年3月5日",g:"Python",e:`

Python 导出 MySQL 库表信息到 Excel

-

需求

-

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

-`,r:{minutes:1.47,words:440},y:"a",t:"Python 导出 MySQL 库表信息到 Excel",O:-16779744e5},[":md"]],["v-966d933e","/python/mr.py.html",{d:16859232e5,l:"2023年6月5日",g:["Git","Python"],e:`

mr.py

-

操作 Gitlab MR 的命令行工具的源码与测试代码。

-`,r:{minutes:5,words:1501},y:"a",t:"mr.py",O:-16859232e5},[":md"]],["v-113531b4","/software-testing/unit-testing-overview.html",{d:16905024e5,l:"2023年7月28日",g:["Testing"],e:`

单元测试概述

-

Why

-

为什么要做单元测试?或者说,为什么要写测试代码?

-

个人总结为以下两点:

-
    -
  1. 测试左移,降低修复bug的成本
    -
  2. -
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. -
-`,r:{minutes:2.97,words:892},y:"a",t:"单元测试概述",O:-16905024e5},[":md"]],["v-65b23736","/software-testing/use-RestAssured-for-api-testing.html",{d:16862688e5,l:"2023年6月9日",g:["Java","Testing"],e:`

使用 RestAssured 进行 API 测试

-

前言

-

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

-

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

-

What

-

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

-

Why

-

为什么要做 API 测试呢?

`,r:{minutes:6.99,words:2096},y:"a",t:"使用 RestAssured 进行 API 测试",O:-16862688e5},[":md"]],["v-c488ac58","/software-testing/use-cypress-for-e2e-testing.html",{d:16073856e5,l:"2020年12月8日",g:["Node.js","Testing"],e:`

使用 Cypress 进行端对端测试

-

为什么写端对端测试

-

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

-

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

-

为什么用 Cypress

-

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

`,r:{minutes:8.04,words:2413},y:"a",t:"使用 Cypress 进行端对端测试",O:-16073856e5},[":md"]],["v-efcacba2","/software-testing/use-jest-for-test-driven-development.html",{d:15558048e5,l:"2019年4月21日",g:["Node.js","Testing"],e:`

使用 Jest 实践测试驱动开发

-

前言

-

本文将使用jest进行测试驱动开发的示例,源码在github
-旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

`,r:{minutes:6.26,words:1879},y:"a",t:"使用 Jest 实践测试驱动开发",O:-15558048e5},[":md"]],["v-0be1af08","/software-testing/use-playwright-for-ui-testing.html",{d:16834176e5,l:"2023年5月7日",g:["Node.js","Python","Testing"],e:`

下一代 UI 自动化测试工具 Playwright

-

前言

-

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

-
    -
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. -
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. -
-

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

`,r:{minutes:10,words:3001},y:"a",t:"下一代 UI 自动化测试工具 Playwright",O:-16834176e5},[":md"]],["v-75616b85","/software-testing/use-postman-for-api-testing.html",{d:16945632e5,l:"2023年9月13日",g:["Node.js","Daily"],e:`

使用 Postman 进行 API 测试

-

前言

-

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

-

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

-

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

-`,r:{minutes:5.52,words:1656},y:"a",t:"使用 Postman 进行 API 测试",O:-16945632e5},[":md"]],["v-7ba1021b","/software-testing/use-pytest-for-regression-testing-in-llm-app.html",{d:17019936e5,l:"2023年12月8日",g:["Python","Testing","Gitlab"],e:`

使用 pytest 为LLM应用添加回归测试

-

回归测试的必要性

-

基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

-

因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

-

而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。

-`,r:{minutes:2.9,words:871},y:"a",t:"使用 pytest 为LLM应用添加回归测试",O:-17019936e5},[":md"]],["v-17809471","/tools/how-to-connect-to-internet.html",{d:16556832e5,l:"2022年6月20日",g:"Tool",e:`

科学上网

-

说明

-

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

-

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

-

购买指南

-

点击进入服务页面, 看到如下页面:
-image.png

`,r:{minutes:2.72,words:815},y:"a",t:"科学上网",O:-16556832e5},[":md"]],["v-3706649a","/404.html",{y:"p",t:"",O:null},[]],["v-71fde78e","/devops/",{y:"p",t:"Devops"},[]],["v-79c9f96f","/daily/",{y:"p",t:"Daily"},[]],["v-43539db8","/english/",{y:"p",t:"English"},[]],["v-06198984","/frontend/",{y:"p",t:"Frontend"},[]],["v-74473916","/git/",{y:"p",t:"Git"},[]],["v-14c69af4","/java/",{y:"p",t:"Java"},[]],["v-eb072ff4","/mysql/",{y:"p",t:"Mysql"},[]],["v-63cd5dba","/python/",{y:"p",t:"Python"},[]],["v-0df55bac","/software-testing/",{y:"p",t:"Software Testing"},[]],["v-d440f426","/tools/",{y:"p",t:"Tools"},[]],["v-5bc93818","/category/",{y:"p",t:"分类",I:0},[]],["v-744d024e","/tag/",{y:"p",t:"标签",I:0},[]],["v-e52c881c","/article/",{y:"p",t:"文章",I:0},[]],["v-154dc4c4","/star/",{y:"p",t:"收藏",I:0},[]],["v-01560935","/timeline/",{y:"p",t:"时间轴",I:0},[]],["v-3d5315f8","/tag/daily/",{y:"p",t:"标签: Daily",I:0},[]],["v-51040344","/tag/devops/",{y:"p",t:"标签: DevOps",I:0},[]],["v-211f44ee","/tag/linux/",{y:"p",t:"标签: Linux",I:0},[]],["v-1b3ae9cf","/tag/frontend/",{y:"p",t:"标签: Frontend",I:0},[]],["v-0da0e901","/tag/s3/",{y:"p",t:"标签: S3",I:0},[]],["v-b309c306","/tag/obs/",{y:"p",t:"标签: OBS",I:0},[]],["v-b3094364","/tag/oss/",{y:"p",t:"标签: OSS",I:0},[]],["v-245f5676","/tag/python/",{y:"p",t:"标签: Python",I:0},[]],["v-007c0ae2","/tag/video/",{y:"p",t:"标签: Video",I:0},[]],["v-1bee38ca","/tag/mysql/",{y:"p",t:"标签: MySQL",I:0},[]],["v-0da0abf9","/tag/ai/",{y:"p",t:"标签: AI",I:0},[]],["v-5a3e80fc","/tag/emotion/",{y:"p",t:"标签: Emotion",I:0},[]],["v-01c1de5b","/tag/working-experience/",{y:"p",t:"标签: Working Experience",I:0},[]],["v-29350809","/tag/tool/",{y:"p",t:"标签: Tool",I:0},[]],["v-50d6e023","/tag/design/",{y:"p",t:"标签: Design",I:0},[]],["v-0ca0efe6","/tag/english/",{y:"p",t:"标签: English",I:0},[]],["v-b310d42a","/tag/git/",{y:"p",t:"标签: Git",I:0},[]],["v-13275df4","/tag/gitlab/",{y:"p",t:"标签: GitLab",I:0},[]],["v-28a1d8bf","/tag/java/",{y:"p",t:"标签: Java",I:0},[]],["v-60379330","/tag/node.js/",{y:"p",t:"标签: Node.js",I:0},[]],["v-3b951558","/tag/javascript/",{y:"p",t:"标签: JavaScript",I:0},[]],["v-b30c33a0","/tag/llm/",{y:"p",t:"标签: llm",I:0},[]],["v-48b3e46d","/tag/testing/",{y:"p",t:"标签: Testing",I:0},[]]];var Ts=$({name:"Vuepress",setup(){const e=M0();return()=>s(e.value)}}),K2=()=>W2.reduce((e,[t,l,n,a])=>(e.push({name:t,path:l,component:Ts,meta:n},{path:l.endsWith("/")?l+"index.html":l.substring(0,l.length-5),redirect:l},...a.map(i=>({path:i===":md"?l.substring(0,l.length-5)+".md":i,redirect:l}))),e),[{name:"404",path:"/:catchAll(.*)",component:Ts}]),J2=nh,Q2=()=>{const e=jh({history:J2(xi("/")),routes:K2(),scrollBehavior:(t,l,n)=>n||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,l)=>{var n;(t.path!==l.path||l===Et)&&([Dt.value]=await Promise.all([wt.resolvePageData(t.name),(n=Io[t.name])==null?void 0:n.__asyncLoader()]))}),e},Y2=e=>{e.component("ClientOnly",Zn),e.component("Content",No)},X2=(e,t,l)=>{const n=E(()=>wt.resolveLayouts(l)),a=es(()=>t.currentRoute.value.path),i=es(()=>wt.resolveRouteLocale(ol.value.locales,a.value)),r=E(()=>wt.resolveSiteLocaleData(ol.value,i.value)),o=E(()=>wt.resolvePageFrontmatter(Dt.value)),c=E(()=>wt.resolvePageHeadTitle(Dt.value,r.value)),u=E(()=>wt.resolvePageHead(c.value,o.value,r.value)),d=E(()=>wt.resolvePageLang(Dt.value,r.value)),p=E(()=>wt.resolvePageLayout(Dt.value,n.value));return e.provide(R0,n),e.provide(Co,o),e.provide(D0,c),e.provide(So,u),e.provide(Do,d),e.provide($o,p),e.provide(Ti,i),e.provide(Fo,r),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>o.value},$head:{get:()=>u.value},$headTitle:{get:()=>c.value},$lang:{get:()=>d.value},$page:{get:()=>Dt.value},$routeLocale:{get:()=>i.value},$site:{get:()=>ol.value},$siteLocale:{get:()=>r.value},$withBase:{get:()=>xe}}),{layouts:n,pageData:Dt,pageFrontmatter:o,pageHead:u,pageHeadTitle:c,pageLang:d,pageLayout:p,routeLocale:i,siteData:ol,siteLocaleData:r}},Z2=()=>{const e=S0(),t=Mo(),l=W([]),n=()=>{e.value.forEach(i=>{const r=eg(i);r&&l.value.push(r)})},a=()=>{document.documentElement.lang=t.value,l.value.forEach(i=>{i.parentNode===document.head&&document.head.removeChild(i)}),l.value.splice(0,l.value.length),e.value.forEach(i=>{const r=tg(i);r!==null&&(document.head.appendChild(r),l.value.push(r))})};ot($0,a),ke(()=>{n(),a(),ce(()=>e.value,a)})},eg=([e,t,l=""])=>{const n=Object.entries(t).map(([o,c])=>ae(c)?`[${o}=${JSON.stringify(c)}]`:c===!0?`[${o}]`:"").join(""),a=`head > ${e}${n}`;return Array.from(document.querySelectorAll(a)).find(o=>o.innerText===l)||null},tg=([e,t,l])=>{if(!ae(e))return null;const n=document.createElement(e);return Li(t)&&Object.entries(t).forEach(([a,i])=>{ae(i)?n.setAttribute(a,i):i===!0&&n.setAttribute(a,"")}),ae(l)&&n.appendChild(document.createTextNode(l)),n},lg=_0,ng=async()=>{var l;const e=lg({name:"VuepressApp",setup(){var n;Z2();for(const a of Rn)(n=a.setup)==null||n.call(a);return()=>[s(Qo),...Rn.flatMap(({rootComponents:a=[]})=>a.map(i=>s(i)))]}}),t=Q2();Y2(e),X2(e,t,Rn);for(const n of Rn)await((l=n.enhance)==null?void 0:l.call(n,{app:e,router:t,siteData:ol}));return e.use(t),{app:e,router:t}};ng().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{mo as a,yo as b,ig as c,ng as createVueApp,Ae as d,rg as e,sg as f,Op as o,Je as r,qd as w}; diff --git a/assets/avoid-sending-password-in-plaintext.html-21e6cf5c.js b/assets/avoid-sending-password-in-plaintext.html-0a23e59b.js similarity index 93% rename from assets/avoid-sending-password-in-plaintext.html-21e6cf5c.js rename to assets/avoid-sending-password-in-plaintext.html-0a23e59b.js index d06a3aaa..95afa140 100644 --- a/assets/avoid-sending-password-in-plaintext.html-21e6cf5c.js +++ b/assets/avoid-sending-password-in-plaintext.html-0a23e59b.js @@ -1,4 +1,4 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,f as e,a as n,b as s,e as c}from"./app-ee4d23bf.js";const o={},l=n("h1",{id:"避免密码明文传输",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#避免密码明文传输","aria-hidden":"true"},"#"),s(" 避免密码明文传输")],-1),i=n("h2",{id:"说明",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#说明","aria-hidden":"true"},"#"),s(" 说明")],-1),u=n("p",null,"密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。",-1),k=n("p",null,"本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。",-1),r=n("p",null,"流程说明:前端加密,后端解密。",-1),d=n("p",null,"当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。",-1),v=c(`

前端代码

记得安装相应的 npm 模块。

/*******************************
+import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,e,a as n,b as s,f as c}from"./app-dcf5468f.js";const o={},l=n("h1",{id:"避免密码明文传输",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#避免密码明文传输","aria-hidden":"true"},"#"),s(" 避免密码明文传输")],-1),i=n("h2",{id:"说明",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#说明","aria-hidden":"true"},"#"),s(" 说明")],-1),u=n("p",null,"密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。",-1),k=n("p",null,"本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。",-1),r=n("p",null,"流程说明:前端加密,后端解密。",-1),d=n("p",null,"当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。",-1),v=c(`

前端代码

记得安装相应的 npm 模块。

/*******************************
 Description: aes加解密工具方法
 ********************************/
 import AES from 'crypto-js/aes'
diff --git a/assets/avoid-sending-password-in-plaintext.html-407350df.js b/assets/avoid-sending-password-in-plaintext.html-eee63490.js
similarity index 66%
rename from assets/avoid-sending-password-in-plaintext.html-407350df.js
rename to assets/avoid-sending-password-in-plaintext.html-eee63490.js
index f512bba5..5142ecdc 100644
--- a/assets/avoid-sending-password-in-plaintext.html-407350df.js
+++ b/assets/avoid-sending-password-in-plaintext.html-eee63490.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-538a98d7","path":"/java/avoid-sending-password-in-plaintext.html","title":"避免密码明文传输","lang":"zh-CN","frontmatter":{"date":"2023-10-24T00:00:00.000Z","tag":["Java","JavaScript","Daily"],"description":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/avoid-sending-password-in-plaintext.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"避免密码明文传输"}],["meta",{"property":"og:description","content":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"JavaScript"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"避免密码明文传输\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-24T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"前端代码","slug":"前端代码","link":"#前端代码","children":[]},{"level":2,"title":"后端代码","slug":"后端代码","link":"#后端代码","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.63,"words":488},"filePathRelative":"java/avoid-sending-password-in-plaintext.md","localizedDate":"2023年10月24日","excerpt":"

避免密码明文传输

\\n

说明

\\n

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

\\n

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

\\n

流程说明:前端加密,后端解密。

\\n

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

\\n","autoDesc":true}');export{t as data}; +const e=JSON.parse('{"key":"v-538a98d7","path":"/java/avoid-sending-password-in-plaintext.html","title":"避免密码明文传输","lang":"zh-CN","frontmatter":{"date":"2023-10-24T00:00:00.000Z","tag":["Java","JavaScript","Daily"],"description":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/avoid-sending-password-in-plaintext.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"避免密码明文传输"}],["meta",{"property":"og:description","content":"避免密码明文传输 说明 密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。 本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。 流程说明:前端加密,后端解密。 当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"JavaScript"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"避免密码明文传输\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-24T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"前端代码","slug":"前端代码","link":"#前端代码","children":[]},{"level":2,"title":"后端代码","slug":"后端代码","link":"#后端代码","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.63,"words":488},"filePathRelative":"java/avoid-sending-password-in-plaintext.md","localizedDate":"2023年10月24日","excerpt":"

避免密码明文传输

\\n

说明

\\n

密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

\\n

本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

\\n

流程说明:前端加密,后端解密。

\\n

当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-2ecc34d4.js b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-2ecc34d4.js deleted file mode 100644 index 5fb6f555..00000000 --- a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-2ecc34d4.js +++ /dev/null @@ -1,7 +0,0 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as t,c as o,f as i,d as c,e}from"./app-ee4d23bf.js";const r={},l=e('

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

Background

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

CREATE TABLE `my_table` (\n  `id` bigint NOT NULL AUTO_INCREMENT,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n

That is the knowledge that today I want to share with you.

',5),d=e(`

utf8mb4(UTF-8 MultiByte 4-Byte)

UTF-8 was initially designed to support characters from the Unicode standard, which includes characters from various writing systems used across different languages.

And the original UTF-8 encoding was Basic Multilingual Plane (BMP), which is a specific range of Unicode code points from U+0000 to U+FFFF, including a total of 65,536 code points, using 1 to 3 bytes.

However, as the Unicode standard expanded to include more characters beyond the BMP, there arose a need for a new encoding to accommodate these additional characters. This is where utf8mb4 comes into play.

The key differences between utf8 and utf8mb4 are:

  1. Character Range: utf8mb4 supports the entire Unicode character range, while utf8 is limited to the BMP.
  2. Number of Bytes: utf8 characters can be stored using 1 to 3 bytes, while utf8mb4 characters can use up to 4 bytes.

In practical terms, if you want to store or display characters beyond the BMP (e.g., emojis) in your MySQL database, you need to use the utf8mb4 character set.

utf8mb4_unicode_ci

This is about Collation.

Collation determines the mechanism of string comparisons, specifically regarding sorting and searching.

Let's take **utf8mb4_unicode_ci and utf8mb4_general_ci **for examples. Since ci stands for case-insensitive, both of them ignore differences in lettercase.

And their main differences are:

  1. utf8mb4_unicode_ci:
    • This collation provides a more comprehensive and accurate comparison algorithm based on the Unicode standard.
    • It is generally more suitable when dealing with multilingual applications or when precise sorting and comparisons are required.
  2. utf8mb4_general_ci:
    • This collation is generally faster for sorting and comparisons.
    • Its comparison algorithm would also ignore differences in certain character variations (such as accents or diacritics).
    • However, it may not produce accurate results when dealing with some complex language-specific sorting and comparison rules, because it might treat accented characters as identical to their unaccented counterparts.

And here're some examples of Accented Characters in Latin-based Languages:

  • á (acute accent) - Unaccented: a
  • ä (umlaut/diaeresis) - Unaccented: a

As a general recommendation, utf8mb4_unicode_ci is often considered a better default choice, especially in applications with internationalization (i18n) requirements. It provides more accurate sorting and comparison results for a wide range of languages and characters.
However, there may be some specific use cases where utf8mb4_general_ci is preferred, such as when performance is a critical concern and language-specific sorting rules are not essential.

Some tips

You can use the following command to check default collation for your MySQL database:

SHOW VARIABLES LIKE 'collation_database';
-

You may encounter this error Illegal mix of collations:

select id from my_table where tenant_id=@target_tenant_id;
-
-Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) 
-and (utf8mb4_general_ci,IMPLICIT) for operation '='
-

and here's the solution, using the keyword COLLATE:

SET @target_tenant_id := 'your_value' COLLATE utf8mb4_unicode_ci;
-
`,23);function p(u,m){const a=s("BiliBili");return t(),o("div",null,[l,i(" more "),c(a,{bvid:"BV1Rp4y1g7Uw"}),d])}const g=n(r,[["render",p],["__file","beyond-utf8-do-you-know-utf8mb4-and-collation.html.vue"]]);export{g as default}; diff --git a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-89862497.js b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-89862497.js new file mode 100644 index 00000000..bfce1137 --- /dev/null +++ b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-89862497.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-79e139f8","path":"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html","title":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?","lang":"zh-CN","frontmatter":{"date":"2023-08-15T00:00:00.000Z","tag":["Daily","Video","MySQL"],"description":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you.","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?"}],["meta",{"property":"og:description","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-15T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-15T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Background","slug":"background","link":"#background","children":[]},{"level":2,"title":"utf8mb4(UTF-8 MultiByte 4-Byte)","slug":"utf8mb4-utf-8-multibyte-4-byte","link":"#utf8mb4-utf-8-multibyte-4-byte","children":[]},{"level":2,"title":"utf8mb4_unicode_ci","slug":"utf8mb4-unicode-ci","link":"#utf8mb4-unicode-ci","children":[]},{"level":2,"title":"Some tips","slug":"some-tips","link":"#some-tips","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.78,"words":533},"filePathRelative":"daily/beyond-utf8-do-you-know-utf8mb4-and-collation.md","localizedDate":"2023年8月15日","excerpt":"

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

\\n

Background

\\n

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

\\n
CREATE TABLE `my_table` (\\n  `id` bigint NOT NULL AUTO_INCREMENT,\\n  PRIMARY KEY (`id`) USING BTREE\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\\n

That is the knowledge that today I want to share with you.

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-948c7db2.js b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-948c7db2.js new file mode 100644 index 00000000..c8a75005 --- /dev/null +++ b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-948c7db2.js @@ -0,0 +1,7 @@ +import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as t,c as o,e as i,d as c,f as e}from"./app-dcf5468f.js";const r={},l=e('

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

Background

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

CREATE TABLE `my_table` (\n  `id` bigint NOT NULL AUTO_INCREMENT,\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n

That is the knowledge that today I want to share with you.

',5),d=e(`

utf8mb4(UTF-8 MultiByte 4-Byte)

UTF-8 was initially designed to support characters from the Unicode standard, which includes characters from various writing systems used across different languages.

And the original UTF-8 encoding was Basic Multilingual Plane (BMP), which is a specific range of Unicode code points from U+0000 to U+FFFF, including a total of 65,536 code points, using 1 to 3 bytes.

However, as the Unicode standard expanded to include more characters beyond the BMP, there arose a need for a new encoding to accommodate these additional characters. This is where utf8mb4 comes into play.

The key differences between utf8 and utf8mb4 are:

  1. Character Range: utf8mb4 supports the entire Unicode character range, while utf8 is limited to the BMP.
  2. Number of Bytes: utf8 characters can be stored using 1 to 3 bytes, while utf8mb4 characters can use up to 4 bytes.

In practical terms, if you want to store or display characters beyond the BMP (e.g., emojis) in your MySQL database, you need to use the utf8mb4 character set.

utf8mb4_unicode_ci

This is about Collation.

Collation determines the mechanism of string comparisons, specifically regarding sorting and searching.

Let's take **utf8mb4_unicode_ci and utf8mb4_general_ci **for examples. Since ci stands for case-insensitive, both of them ignore differences in lettercase.

And their main differences are:

  1. utf8mb4_unicode_ci:
    • This collation provides a more comprehensive and accurate comparison algorithm based on the Unicode standard.
    • It is generally more suitable when dealing with multilingual applications or when precise sorting and comparisons are required.
  2. utf8mb4_general_ci:
    • This collation is generally faster for sorting and comparisons.
    • Its comparison algorithm would also ignore differences in certain character variations (such as accents or diacritics).
    • However, it may not produce accurate results when dealing with some complex language-specific sorting and comparison rules, because it might treat accented characters as identical to their unaccented counterparts.

And here're some examples of Accented Characters in Latin-based Languages:

  • á (acute accent) - Unaccented: a
  • ä (umlaut/diaeresis) - Unaccented: a

As a general recommendation, utf8mb4_unicode_ci is often considered a better default choice, especially in applications with internationalization (i18n) requirements. It provides more accurate sorting and comparison results for a wide range of languages and characters.
However, there may be some specific use cases where utf8mb4_general_ci is preferred, such as when performance is a critical concern and language-specific sorting rules are not essential.

Some tips

You can use the following command to check default collation for your MySQL database:

SHOW VARIABLES LIKE 'collation_database';
+

You may encounter this error Illegal mix of collations:

select id from my_table where tenant_id=@target_tenant_id;
+
+Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) 
+and (utf8mb4_general_ci,IMPLICIT) for operation '='
+

and here's the solution, using the keyword COLLATE:

SET @target_tenant_id := 'your_value' COLLATE utf8mb4_unicode_ci;
+
`,23);function p(u,m){const a=s("BiliBili");return t(),o("div",null,[l,i(" more "),c(a,{bvid:"BV1Rp4y1g7Uw"}),d])}const g=n(r,[["render",p],["__file","beyond-utf8-do-you-know-utf8mb4-and-collation.html.vue"]]);export{g as default}; diff --git a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-c08f282b.js b/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-c08f282b.js deleted file mode 100644 index e14ace21..00000000 --- a/assets/beyond-utf8-do-you-know-utf8mb4-and-collation.html-c08f282b.js +++ /dev/null @@ -1 +0,0 @@ -const t=JSON.parse('{"key":"v-79e139f8","path":"/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html","title":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?","lang":"zh-CN","frontmatter":{"date":"2023-08-15T00:00:00.000Z","tag":["Daily","Video","MySQL"],"description":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you.","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?"}],["meta",{"property":"og:description","content":"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? Background Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci? CREATE TABLE `my_table` ( `id` bigint NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; That is the knowledge that today I want to share with you."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-15T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-15T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Background","slug":"background","link":"#background","children":[]},{"level":2,"title":"utf8mb4(UTF-8 MultiByte 4-Byte)","slug":"utf8mb4-utf-8-multibyte-4-byte","link":"#utf8mb4-utf-8-multibyte-4-byte","children":[]},{"level":2,"title":"utf8mb4_unicode_ci","slug":"utf8mb4-unicode-ci","link":"#utf8mb4-unicode-ci","children":[]},{"level":2,"title":"Some tips","slug":"some-tips","link":"#some-tips","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.78,"words":533},"filePathRelative":"daily/beyond-utf8-do-you-know-utf8mb4-and-collation.md","localizedDate":"2023年8月15日","excerpt":"

Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

\\n

Background

\\n

Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

\\n
CREATE TABLE `my_table` (\\n  `id` bigint NOT NULL AUTO_INCREMENT,\\n  PRIMARY KEY (`id`) USING BTREE\\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\\n

That is the knowledge that today I want to share with you.

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/check-if-name-exists.html-f54588de.js b/assets/check-if-name-exists.html-0118cdf7.js similarity index 91% rename from assets/check-if-name-exists.html-f54588de.js rename to assets/check-if-name-exists.html-0118cdf7.js index 5d2d0e84..e3647946 100644 --- a/assets/check-if-name-exists.html-f54588de.js +++ b/assets/check-if-name-exists.html-0118cdf7.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as t,f as e,a as n,b as p,e as o}from"./app-ee4d23bf.js";const c={},l=n("h1",{id:"检查名字是否重复",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检查名字是否重复","aria-hidden":"true"},"#"),p(" 检查名字是否重复")],-1),i=n("p",null,"检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。",-1),u=o(`

推荐做法

借助数据库的来实现,执行以下语句:

ALTER TABLE my_table ADD UNIQUE(name);
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as t,e,a as n,b as p,f as o}from"./app-dcf5468f.js";const c={},l=n("h1",{id:"检查名字是否重复",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检查名字是否重复","aria-hidden":"true"},"#"),p(" 检查名字是否重复")],-1),i=n("p",null,"检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。",-1),u=o(`

推荐做法

借助数据库的来实现,执行以下语句:

ALTER TABLE my_table ADD UNIQUE(name);
 

然后,在程序里添加全局异常处理类:

@Slf4j
 @RestControllerAdvice
 public class GlobalExceptionHandler {
diff --git a/assets/check-if-name-exists.html-9253bedd.js b/assets/check-if-name-exists.html-4984a2ab.js
similarity index 71%
rename from assets/check-if-name-exists.html-9253bedd.js
rename to assets/check-if-name-exists.html-4984a2ab.js
index d777d1d0..71043fe6 100644
--- a/assets/check-if-name-exists.html-9253bedd.js
+++ b/assets/check-if-name-exists.html-4984a2ab.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-2f6ae09c","path":"/java/check-if-name-exists.html","title":"检查名字是否重复","lang":"zh-CN","frontmatter":{"date":"2023-10-18T00:00:00.000Z","tag":["Java","MySQL","Daily"],"description":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/check-if-name-exists.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"检查名字是否重复"}],["meta",{"property":"og:description","content":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"检查名字是否重复\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-18T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"推荐做法","slug":"推荐做法","link":"#推荐做法","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.18,"words":353},"filePathRelative":"java/check-if-name-exists.md","localizedDate":"2023年10月18日","excerpt":"

检查名字是否重复

\\n

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-2f6ae09c","path":"/java/check-if-name-exists.html","title":"检查名字是否重复","lang":"zh-CN","frontmatter":{"date":"2023-10-18T00:00:00.000Z","tag":["Java","MySQL","Daily"],"description":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/check-if-name-exists.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"检查名字是否重复"}],["meta",{"property":"og:description","content":"检查名字是否重复 检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-10-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"检查名字是否重复\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-10-18T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"推荐做法","slug":"推荐做法","link":"#推荐做法","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.18,"words":353},"filePathRelative":"java/check-if-name-exists.md","localizedDate":"2023年10月18日","excerpt":"

检查名字是否重复

\\n

检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/claude-ai-in-action-extract-info-from-html.html-a82639eb.js b/assets/claude-ai-in-action-extract-info-from-html.html-454eb9b7.js similarity index 68% rename from assets/claude-ai-in-action-extract-info-from-html.html-a82639eb.js rename to assets/claude-ai-in-action-extract-info-from-html.html-454eb9b7.js index 0b9b3766..e6a086fe 100644 --- a/assets/claude-ai-in-action-extract-info-from-html.html-a82639eb.js +++ b/assets/claude-ai-in-action-extract-info-from-html.html-454eb9b7.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-7a74360a","path":"/daily/claude-ai-in-action-extract-info-from-html.html","title":"Claude AI应用案例,从HTML中抽取文本","lang":"zh-CN","frontmatter":{"date":"2023-08-25T00:00:00.000Z","tag":["Daily","Video","AI"],"description":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/claude-ai-in-action-extract-info-from-html.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Claude AI应用案例,从HTML中抽取文本"}],["meta",{"property":"og:description","content":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-08-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Claude AI应用案例,从HTML中抽取文本\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-25T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.19,"words":58},"filePathRelative":"daily/claude-ai-in-action-extract-info-from-html.md","localizedDate":"2023年8月25日","excerpt":"

Claude AI应用案例,从HTML中抽取文本

\\n

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

\\n","autoDesc":true}');export{t as data}; +const e=JSON.parse('{"key":"v-7a74360a","path":"/daily/claude-ai-in-action-extract-info-from-html.html","title":"Claude AI应用案例,从HTML中抽取文本","lang":"zh-CN","frontmatter":{"date":"2023-08-25T00:00:00.000Z","tag":["Daily","Video","AI"],"description":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/claude-ai-in-action-extract-info-from-html.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Claude AI应用案例,从HTML中抽取文本"}],["meta",{"property":"og:description","content":"Claude AI应用案例,从HTML中抽取文本 今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-08-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Claude AI应用案例,从HTML中抽取文本\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-25T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.19,"words":58},"filePathRelative":"daily/claude-ai-in-action-extract-info-from-html.md","localizedDate":"2023年8月25日","excerpt":"

Claude AI应用案例,从HTML中抽取文本

\\n

今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/claude-ai-in-action-extract-info-from-html.html-235260a5.js b/assets/claude-ai-in-action-extract-info-from-html.html-b23660e4.js similarity index 74% rename from assets/claude-ai-in-action-extract-info-from-html.html-235260a5.js rename to assets/claude-ai-in-action-extract-info-from-html.html-b23660e4.js index 1babcfd7..9bcd390f 100644 --- a/assets/claude-ai-in-action-extract-info-from-html.html-235260a5.js +++ b/assets/claude-ai-in-action-extract-info-from-html.html-b23660e4.js @@ -1 +1 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,f as n,d as r,a as e,b as l}from"./app-ee4d23bf.js";const s={},d=e("h1",{id:"claude-ai应用案例-从html中抽取文本",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#claude-ai应用案例-从html中抽取文本","aria-hidden":"true"},"#"),l(" Claude AI应用案例,从HTML中抽取文本")],-1),m=e("p",null,"今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!",-1);function _(f,h){const t=o("BiliBili");return c(),i("div",null,[d,m,n(" more "),r(t,{bvid:"BV13u4y1D7Kj"})])}const B=a(s,[["render",_],["__file","claude-ai-in-action-extract-info-from-html.html.vue"]]);export{B as default}; +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,e as n,d as r,a as e,b as l}from"./app-dcf5468f.js";const s={},d=e("h1",{id:"claude-ai应用案例-从html中抽取文本",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#claude-ai应用案例-从html中抽取文本","aria-hidden":"true"},"#"),l(" Claude AI应用案例,从HTML中抽取文本")],-1),m=e("p",null,"今天跟大家分享AI使用心得:不再是让AI帮我们写代码,而是直接帮我们干活!",-1);function _(h,u){const t=o("BiliBili");return c(),i("div",null,[d,m,n(" more "),r(t,{bvid:"BV13u4y1D7Kj"})])}const B=a(s,[["render",_],["__file","claude-ai-in-action-extract-info-from-html.html.vue"]]);export{B as default}; diff --git a/assets/common-practices-for-handling-excel.html-8ccc7c4e.js b/assets/common-practices-for-handling-excel.html-148e90f5.js similarity index 99% rename from assets/common-practices-for-handling-excel.html-8ccc7c4e.js rename to assets/common-practices-for-handling-excel.html-148e90f5.js index 2fb334ce..8939c89e 100644 --- a/assets/common-practices-for-handling-excel.html-8ccc7c4e.js +++ b/assets/common-practices-for-handling-excel.html-148e90f5.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,e as t}from"./app-ee4d23bf.js";const p={},e=t(`

Excel处理常用实践

基础知识

导入需要用到对象,MultipartFile。

@PostMapping("/import")
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-dcf5468f.js";const p={},e=t(`

Excel处理常用实践

基础知识

导入需要用到对象,MultipartFile。

@PostMapping("/import")
 public boolean importLicense(
       @RequestParam("file") MultipartFile file,
       @RequestParam("tenantId") @NotBlank String tenantId,
diff --git a/assets/common-practices-for-handling-excel.html-18fd5108.js b/assets/common-practices-for-handling-excel.html-18fd5108.js
new file mode 100644
index 00000000..55b884b5
--- /dev/null
+++ b/assets/common-practices-for-handling-excel.html-18fd5108.js
@@ -0,0 +1 @@
+const e=JSON.parse('{"key":"v-b5a78a7a","path":"/java/common-practices-for-handling-excel.html","title":"Excel处理常用实践","lang":"zh-CN","frontmatter":{"date":"2023-09-05T00:00:00.000Z","tag":["Java","Daily"],"description":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/common-practices-for-handling-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Excel处理常用实践"}],["meta",{"property":"og:description","content":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Excel处理常用实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"基础知识","slug":"基础知识","link":"#基础知识","children":[]},{"level":2,"title":"应用框架","slug":"应用框架","link":"#应用框架","children":[{"level":3,"title":"导出","slug":"导出","link":"#导出","children":[]},{"level":3,"title":"导入","slug":"导入","link":"#导入","children":[]}]},{"level":2,"title":"常见问题与解决方案","slug":"常见问题与解决方案","link":"#常见问题与解决方案","children":[{"level":3,"title":"浏览器下载","slug":"浏览器下载","link":"#浏览器下载","children":[]},{"level":3,"title":"上传文件大小限制","slug":"上传文件大小限制","link":"#上传文件大小限制","children":[]},{"level":3,"title":"缺少字体","slug":"缺少字体","link":"#缺少字体","children":[]},{"level":3,"title":"序列化失败","slug":"序列化失败","link":"#序列化失败","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.88,"words":1464},"filePathRelative":"java/common-practices-for-handling-excel.md","localizedDate":"2023年9月5日","excerpt":"

Excel处理常用实践

\\n

基础知识

\\n

导入需要用到对象,MultipartFile。

\\n
@PostMapping(\\"/import\\")\\npublic boolean importLicense(\\n      @RequestParam(\\"file\\") MultipartFile file,\\n      @RequestParam(\\"tenantId\\") @NotBlank String tenantId,\\n) {\\n  return true;\\n}\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/common-practices-for-handling-excel.html-4f5d7b9c.js b/assets/common-practices-for-handling-excel.html-4f5d7b9c.js deleted file mode 100644 index 523e7c65..00000000 --- a/assets/common-practices-for-handling-excel.html-4f5d7b9c.js +++ /dev/null @@ -1 +0,0 @@ -const n=JSON.parse('{"key":"v-b5a78a7a","path":"/java/common-practices-for-handling-excel.html","title":"Excel处理常用实践","lang":"zh-CN","frontmatter":{"date":"2023-09-05T00:00:00.000Z","tag":["Java","Daily"],"description":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/common-practices-for-handling-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Excel处理常用实践"}],["meta",{"property":"og:description","content":"Excel处理常用实践 基础知识 导入需要用到对象,MultipartFile。 @PostMapping(\\"/import\\") public boolean importLicense( @RequestParam(\\"file\\") MultipartFile file, @RequestParam(\\"tenantId\\") @NotBlank String tenantId, ) { return true; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Excel处理常用实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"基础知识","slug":"基础知识","link":"#基础知识","children":[]},{"level":2,"title":"应用框架","slug":"应用框架","link":"#应用框架","children":[{"level":3,"title":"导出","slug":"导出","link":"#导出","children":[]},{"level":3,"title":"导入","slug":"导入","link":"#导入","children":[]}]},{"level":2,"title":"常见问题与解决方案","slug":"常见问题与解决方案","link":"#常见问题与解决方案","children":[{"level":3,"title":"浏览器下载","slug":"浏览器下载","link":"#浏览器下载","children":[]},{"level":3,"title":"上传文件大小限制","slug":"上传文件大小限制","link":"#上传文件大小限制","children":[]},{"level":3,"title":"缺少字体","slug":"缺少字体","link":"#缺少字体","children":[]},{"level":3,"title":"序列化失败","slug":"序列化失败","link":"#序列化失败","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4.88,"words":1464},"filePathRelative":"java/common-practices-for-handling-excel.md","localizedDate":"2023年9月5日","excerpt":"

Excel处理常用实践

\\n

基础知识

\\n

导入需要用到对象,MultipartFile。

\\n
@PostMapping(\\"/import\\")\\npublic boolean importLicense(\\n      @RequestParam(\\"file\\") MultipartFile file,\\n      @RequestParam(\\"tenantId\\") @NotBlank String tenantId,\\n) {\\n  return true;\\n}\\n
","autoDesc":true}');export{n as data}; diff --git a/assets/common-solutions-of-object-storage-for-static-assets.html-35623701.js b/assets/common-solutions-of-object-storage-for-static-assets.html-288ae794.js similarity index 76% rename from assets/common-solutions-of-object-storage-for-static-assets.html-35623701.js rename to assets/common-solutions-of-object-storage-for-static-assets.html-288ae794.js index a4e3458e..e2b67940 100644 --- a/assets/common-solutions-of-object-storage-for-static-assets.html-35623701.js +++ b/assets/common-solutions-of-object-storage-for-static-assets.html-288ae794.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-6614b1a1","path":"/devops/common-solutions-of-object-storage-for-static-assets.html","title":"对象存储静态资源常见操作","lang":"zh-CN","frontmatter":{"date":"2019-04-05T00:00:00.000Z","tag":["Frontend","DevOps","S3","OBS","OSS"],"description":"对象存储静态资源常见操作 前言 把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。 本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"对象存储静态资源常见操作"}],["meta",{"property":"og:description","content":"对象存储静态资源常见操作 前言 把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。 本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"S3"}],["meta",{"property":"article:tag","content":"OBS"}],["meta",{"property":"article:tag","content":"OSS"}],["meta",{"property":"article:published_time","content":"2019-04-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"对象存储静态资源常见操作\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-04-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"阿里云OSS","slug":"阿里云oss","link":"#阿里云oss","children":[{"level":3,"title":"绑定域名","slug":"绑定域名","link":"#绑定域名","children":[]},{"level":3,"title":"CNAME设置","slug":"cname设置","link":"#cname设置","children":[]},{"level":3,"title":"HTTPS证书托管","slug":"https证书托管","link":"#https证书托管","children":[]},{"level":3,"title":"公共读","slug":"公共读","link":"#公共读","children":[]},{"level":3,"title":"CORS跨域设置","slug":"cors跨域设置","link":"#cors跨域设置","children":[]}]},{"level":2,"title":"华为云OBS","slug":"华为云obs","link":"#华为云obs","children":[{"level":3,"title":"跨域设置","slug":"跨域设置","link":"#跨域设置","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.03,"words":908},"filePathRelative":"devops/common-solutions-of-object-storage-for-static-assets.md","localizedDate":"2019年4月5日","excerpt":"

对象存储静态资源常见操作

\\n

前言

\\n

把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

\\n

本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-6614b1a1","path":"/devops/common-solutions-of-object-storage-for-static-assets.html","title":"对象存储静态资源常见操作","lang":"zh-CN","frontmatter":{"date":"2019-04-05T00:00:00.000Z","tag":["Frontend","DevOps","S3","OBS","OSS"],"description":"对象存储静态资源常见操作 前言 把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。 本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"对象存储静态资源常见操作"}],["meta",{"property":"og:description","content":"对象存储静态资源常见操作 前言 把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。 本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"S3"}],["meta",{"property":"article:tag","content":"OBS"}],["meta",{"property":"article:tag","content":"OSS"}],["meta",{"property":"article:published_time","content":"2019-04-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"对象存储静态资源常见操作\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-04-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"阿里云OSS","slug":"阿里云oss","link":"#阿里云oss","children":[{"level":3,"title":"绑定域名","slug":"绑定域名","link":"#绑定域名","children":[]},{"level":3,"title":"CNAME设置","slug":"cname设置","link":"#cname设置","children":[]},{"level":3,"title":"HTTPS证书托管","slug":"https证书托管","link":"#https证书托管","children":[]},{"level":3,"title":"公共读","slug":"公共读","link":"#公共读","children":[]},{"level":3,"title":"CORS跨域设置","slug":"cors跨域设置","link":"#cors跨域设置","children":[]}]},{"level":2,"title":"华为云OBS","slug":"华为云obs","link":"#华为云obs","children":[{"level":3,"title":"跨域设置","slug":"跨域设置","link":"#跨域设置","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.03,"words":908},"filePathRelative":"devops/common-solutions-of-object-storage-for-static-assets.md","localizedDate":"2019年4月5日","excerpt":"

对象存储静态资源常见操作

\\n

前言

\\n

把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

\\n

本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/common-solutions-of-object-storage-for-static-assets.html-294e093e.js b/assets/common-solutions-of-object-storage-for-static-assets.html-5016aa62.js similarity index 98% rename from assets/common-solutions-of-object-storage-for-static-assets.html-294e093e.js rename to assets/common-solutions-of-object-storage-for-static-assets.html-5016aa62.js index d5100a18..f24a03db 100644 --- a/assets/common-solutions-of-object-storage-for-static-assets.html-294e093e.js +++ b/assets/common-solutions-of-object-storage-for-static-assets.html-5016aa62.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as d,c as m,f as s,a,b as e,d as t,e as r}from"./app-ee4d23bf.js";const p={},c=a("h1",{id:"对象存储静态资源常见操作",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#对象存储静态资源常见操作","aria-hidden":"true"},"#"),e(" 对象存储静态资源常见操作")],-1),h=a("h2",{id:"前言",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),e(" 前言")],-1),l=a("p",null,"把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。",-1),g=a("p",null,"本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。",-1),f=r('

阿里云OSS

对于新建的bucket,需要做一些设置,才能正常使用静态资源。

绑定域名

则使用自定义域名访问,可以解决访问 html 变成下载的问题。

CNAME设置

如果绑定的是同一个阿里云账号下的域名,则可以自动添加 CNAME 记录。否则需要手动添加。

',8),u={href:"http://my-bucket.oss-cn-shenzhen.aliyuncs.com",target:"_blank",rel:"noopener noreferrer"},b=a("br",null,null,-1),y=a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1702348349732-a5a3015a-262a-4b32-b89d-593c32e56cfc.png#averageHue=%23faf9f9&clientId=u60f42bc1-4481-4&from=paste&height=662&id=u278acbcc&originHeight=662&originWidth=1590&originalType=binary&ratio=1&rotation=0&showTitle=false&size=226370&status=done&style=none&taskId=u455741d8-010a-4202-9b96-170a9d9ffb7&title=&width=1590",alt:""},null,-1),_=a("br",null,null,-1),w={href:"http://static.domain.com",target:"_blank",rel:"noopener noreferrer"},k={href:"http://my-bucket.oss-cn-shenzhen.aliyuncs.com",target:"_blank",rel:"noopener noreferrer"},T=a("h3",{id:"https证书托管",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#https证书托管","aria-hidden":"true"},"#"),e(" HTTPS证书托管")],-1),v=a("p",null,[e("上传证书,开启 HTTPS"),a("br"),a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1702348442889-df82e3da-e07d-451e-af99-45f40f7e789a.png#averageHue=%23e6e4e0&clientId=u60f42bc1-4481-4&from=paste&height=340&id=u94fa9d74&originHeight=340&originWidth=1586&originalType=binary&ratio=1&rotation=0&showTitle=false&size=228171&status=done&style=none&taskId=u51004980-ecd6-4be3-914a-75f0b84cec0&title=&width=1586",alt:""}),a("br"),a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1702348569375-f32bac2c-25ae-4b38-a2e8-3d3d5a42164c.png#averageHue=%23f4f3f3&clientId=u60f42bc1-4481-4&from=paste&height=904&id=u6ae4a65b&originHeight=904&originWidth=1607&originalType=binary&ratio=1&rotation=0&showTitle=false&size=308821&status=done&style=none&taskId=uaa66eaf6-767e-47db-92fa-fdeaeb76403&title=&width=1607",alt:""})],-1),H={href:"https://github.com/levy9527/blog/issues/5",target:"_blank",rel:"noopener noreferrer"},x=r('

公共读

这样可以解决访问链接超时的问题。

CORS跨域设置

在基础设置下,找到跨域设置

在来源中设置域名,或ip地址。下面给出最简单的示例为 *,实际可以根据需要填写允许的域名,一行一个。

  • 将allowed origins设置成 *
  • 将allowed methods设置成GET, POST, PUT, DELETE, HEAD
  • 将allowed headers设置成 *
  • 将expose headers设置成
    • etag
    • x-oss-request-id

这样可以解决字体无法显示、JavaScript跨域的问题。

华为云OBS

跨域设置

华为云的入口如下:

具体规则的填写是类似阿里云OSS的。

',11);function S(I,z){const i=n("ExternalLinkIcon");return d(),m("div",null,[c,h,l,g,s(" more "),f,a("p",null,[e("查看 bucket 外网地址:"),a("a",u,[e("my-bucket.oss-cn-shenzhen.aliyuncs.com"),t(i)]),b,y]),a("p",null,[e("则去域名解析供应商设置:"),_,a("a",w,[e("static.domain.com"),t(i)]),e("(自定义域名) -> CNAME -> "),a("a",k,[e("my-bucket.oss-cn-shenzhen.aliyuncs.com"),t(i)])]),T,v,a("p",null,[e("如果没有证书,查看教程获取:"),a("a",H,[e("🔒免费开启HTTPS"),t(i)])]),x])}const O=o(p,[["render",S],["__file","common-solutions-of-object-storage-for-static-assets.html.vue"]]);export{O as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as d,c as m,e as s,a,b as e,d as t,f as r}from"./app-dcf5468f.js";const p={},c=a("h1",{id:"对象存储静态资源常见操作",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#对象存储静态资源常见操作","aria-hidden":"true"},"#"),e(" 对象存储静态资源常见操作")],-1),h=a("h2",{id:"前言",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),e(" 前言")],-1),l=a("p",null,"把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。",-1),g=a("p",null,"本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。",-1),f=r('

阿里云OSS

对于新建的bucket,需要做一些设置,才能正常使用静态资源。

绑定域名

则使用自定义域名访问,可以解决访问 html 变成下载的问题。

CNAME设置

如果绑定的是同一个阿里云账号下的域名,则可以自动添加 CNAME 记录。否则需要手动添加。

',8),u={href:"http://my-bucket.oss-cn-shenzhen.aliyuncs.com",target:"_blank",rel:"noopener noreferrer"},b=a("br",null,null,-1),y=a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1702348349732-a5a3015a-262a-4b32-b89d-593c32e56cfc.png#averageHue=%23faf9f9&clientId=u60f42bc1-4481-4&from=paste&height=662&id=u278acbcc&originHeight=662&originWidth=1590&originalType=binary&ratio=1&rotation=0&showTitle=false&size=226370&status=done&style=none&taskId=u455741d8-010a-4202-9b96-170a9d9ffb7&title=&width=1590",alt:""},null,-1),_=a("br",null,null,-1),w={href:"http://static.domain.com",target:"_blank",rel:"noopener noreferrer"},k={href:"http://my-bucket.oss-cn-shenzhen.aliyuncs.com",target:"_blank",rel:"noopener noreferrer"},T=a("h3",{id:"https证书托管",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#https证书托管","aria-hidden":"true"},"#"),e(" HTTPS证书托管")],-1),v=a("p",null,[e("上传证书,开启 HTTPS"),a("br"),a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1702348442889-df82e3da-e07d-451e-af99-45f40f7e789a.png#averageHue=%23e6e4e0&clientId=u60f42bc1-4481-4&from=paste&height=340&id=u94fa9d74&originHeight=340&originWidth=1586&originalType=binary&ratio=1&rotation=0&showTitle=false&size=228171&status=done&style=none&taskId=u51004980-ecd6-4be3-914a-75f0b84cec0&title=&width=1586",alt:""}),a("br"),a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1702348569375-f32bac2c-25ae-4b38-a2e8-3d3d5a42164c.png#averageHue=%23f4f3f3&clientId=u60f42bc1-4481-4&from=paste&height=904&id=u6ae4a65b&originHeight=904&originWidth=1607&originalType=binary&ratio=1&rotation=0&showTitle=false&size=308821&status=done&style=none&taskId=uaa66eaf6-767e-47db-92fa-fdeaeb76403&title=&width=1607",alt:""})],-1),H={href:"https://github.com/levy9527/blog/issues/5",target:"_blank",rel:"noopener noreferrer"},x=r('

公共读

这样可以解决访问链接超时的问题。

CORS跨域设置

在基础设置下,找到跨域设置

在来源中设置域名,或ip地址。下面给出最简单的示例为 *,实际可以根据需要填写允许的域名,一行一个。

  • 将allowed origins设置成 *
  • 将allowed methods设置成GET, POST, PUT, DELETE, HEAD
  • 将allowed headers设置成 *
  • 将expose headers设置成
    • etag
    • x-oss-request-id

这样可以解决字体无法显示、JavaScript跨域的问题。

华为云OBS

跨域设置

华为云的入口如下:

具体规则的填写是类似阿里云OSS的。

',11);function S(I,z){const i=n("ExternalLinkIcon");return d(),m("div",null,[c,h,l,g,s(" more "),f,a("p",null,[e("查看 bucket 外网地址:"),a("a",u,[e("my-bucket.oss-cn-shenzhen.aliyuncs.com"),t(i)]),b,y]),a("p",null,[e("则去域名解析供应商设置:"),_,a("a",w,[e("static.domain.com"),t(i)]),e("(自定义域名) -> CNAME -> "),a("a",k,[e("my-bucket.oss-cn-shenzhen.aliyuncs.com"),t(i)])]),T,v,a("p",null,[e("如果没有证书,查看教程获取:"),a("a",H,[e("🔒免费开启HTTPS"),t(i)])]),x])}const O=o(p,[["render",S],["__file","common-solutions-of-object-storage-for-static-assets.html.vue"]]);export{O as default}; diff --git a/assets/contemporary-college-english-1.html-2dba2810.js b/assets/contemporary-college-english-1.html-5b89cc6a.js similarity index 98% rename from assets/contemporary-college-english-1.html-2dba2810.js rename to assets/contemporary-college-english-1.html-5b89cc6a.js index 0e548990..22758d4d 100644 --- a/assets/contemporary-college-english-1.html-2dba2810.js +++ b/assets/contemporary-college-english-1.html-5b89cc6a.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as s,c as l,a as e,b as t,d as n}from"./app-ee4d23bf.js";const h={},c=e("h1",{id:"现代大学英语精读-第2版-第一册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第一册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第一册")],-1),i=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/43891910",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。",-1),u=e("p",null,"虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。",-1),m=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"本册里面大部分是记叙文,阅读趣味性比较强。",-1),f=e("p",null,"欧亨利不愧是大师,第一册里收录了两篇他的小说。",-1),w={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},x=e("br",null,null,-1),y={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},b={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),k={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"另外,戏剧 The Monsters Are Due in Maple Street 也值得一读",-1),v={href:"https://www.ximalaya.com/sound/357087229",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。",-1);function B(N,T){const r=a("ExternalLinkIcon");return s(),l("div",null,[c,i,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/43891910"),n(r)])]),d,u,m,p,f,e("ul",null,[e("li",null,[e("a",w,[t("After Twenty Years"),n(r)]),x,t(" 经典短篇小说,应该大家都在学校看过中文版。"),e("a",y,[t("https://www.ximalaya.com/sound/356764931"),n(r)])]),e("li",null,[e("a",b,[t("Hearts And Hands"),n(r)]),g,t(" 我认为这篇小说是第一册中最有阅读难度的,可以作为阅读能力的检测,看能不能读懂。反正我第一次没读懂。本文使用了经典的结尾手法——最后一句话反转全文。"),e("a",k,[t("https://americanliterature.com/author/o-henry/short-story/hearts-and-hands"),n(r)])])]),E,e("ul",null,[e("li",null,[e("a",v,[t("https://www.ximalaya.com/sound/357087229"),n(r)])])]),A])}const H=o(h,[["render",B],["__file","contemporary-college-english-1.html.vue"]]);export{H as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as s,c as l,a as e,b as t,d as n}from"./app-dcf5468f.js";const h={},c=e("h1",{id:"现代大学英语精读-第2版-第一册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第一册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第一册")],-1),i=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/43891910",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。",-1),u=e("p",null,"虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。",-1),m=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"本册里面大部分是记叙文,阅读趣味性比较强。",-1),f=e("p",null,"欧亨利不愧是大师,第一册里收录了两篇他的小说。",-1),w={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},x=e("br",null,null,-1),y={href:"https://www.ximalaya.com/sound/356764931",target:"_blank",rel:"noopener noreferrer"},b={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),k={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"另外,戏剧 The Monsters Are Due in Maple Street 也值得一读",-1),v={href:"https://www.ximalaya.com/sound/357087229",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。",-1);function B(N,T){const r=a("ExternalLinkIcon");return s(),l("div",null,[c,i,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/43891910"),n(r)])]),d,u,m,p,f,e("ul",null,[e("li",null,[e("a",w,[t("After Twenty Years"),n(r)]),x,t(" 经典短篇小说,应该大家都在学校看过中文版。"),e("a",y,[t("https://www.ximalaya.com/sound/356764931"),n(r)])]),e("li",null,[e("a",b,[t("Hearts And Hands"),n(r)]),g,t(" 我认为这篇小说是第一册中最有阅读难度的,可以作为阅读能力的检测,看能不能读懂。反正我第一次没读懂。本文使用了经典的结尾手法——最后一句话反转全文。"),e("a",k,[t("https://americanliterature.com/author/o-henry/short-story/hearts-and-hands"),n(r)])])]),E,e("ul",null,[e("li",null,[e("a",v,[t("https://www.ximalaya.com/sound/357087229"),n(r)])])]),A])}const H=o(h,[["render",B],["__file","contemporary-college-english-1.html.vue"]]);export{H as default}; diff --git a/assets/contemporary-college-english-1.html-051e43a8.js b/assets/contemporary-college-english-1.html-d0fabfd1.js similarity index 65% rename from assets/contemporary-college-english-1.html-051e43a8.js rename to assets/contemporary-college-english-1.html-d0fabfd1.js index 94c8d960..f2244e59 100644 --- a/assets/contemporary-college-english-1.html-051e43a8.js +++ b/assets/contemporary-college-english-1.html-d0fabfd1.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-971cc7fe","path":"/english/contemporary-college-english-1.html","title":"现代大学英语精读(第2版)第一册","lang":"zh-CN","frontmatter":{"date":"2022-05-29T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-1.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第一册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-05-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第一册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-05-29T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.38,"words":414},"filePathRelative":"english/contemporary-college-english-1.md","localizedDate":"2022年5月29日","excerpt":"

现代大学英语精读(第2版)第一册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/43891910

\\n

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

\\n

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-971cc7fe","path":"/english/contemporary-college-english-1.html","title":"现代大学英语精读(第2版)第一册","lang":"zh-CN","frontmatter":{"date":"2022-05-29T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-1.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第一册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第一册 介绍 全书链接:https://www.ximalaya.com/album/43891910 虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。 虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-05-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第一册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-05-29T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.38,"words":414},"filePathRelative":"english/contemporary-college-english-1.md","localizedDate":"2022年5月29日","excerpt":"

现代大学英语精读(第2版)第一册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/43891910

\\n

虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

\\n

虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-2.html-df977f79.js b/assets/contemporary-college-english-2.html-8021f470.js similarity index 98% rename from assets/contemporary-college-english-2.html-df977f79.js rename to assets/contemporary-college-english-2.html-8021f470.js index e20edfc1..a225e1ed 100644 --- a/assets/contemporary-college-english-2.html-df977f79.js +++ b/assets/contemporary-college-english-2.html-8021f470.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as o}from"./app-ee4d23bf.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第二册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第二册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第二册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/44290107",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。",-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂",-1),w=e("ul",null,[e("li")],-1),m={href:"https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf",target:"_blank",rel:"noopener noreferrer"},f={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},g=e("p",null,"The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~",-1),y=e("ul",null,[e("li")],-1),x={href:"https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?",-1),T=e("ul",null,[e("li")],-1),v={href:"https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"A Doctor's Dilemma 医生的困境",-1),C={href:"http://www.kekenet.com/daxue/201706/508290.shtml",target:"_blank",rel:"noopener noreferrer"},B={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},S=e("br",null,null,-1),W={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"the-oyster-and-the-pearl",-1),I={href:"http://www.kekenet.com/daxue/201708/521462.shtml",target:"_blank",rel:"noopener noreferrer"},L=e("li",null,null,-1),N={href:"https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:",-1),V=e("ol",null,[e("li",null,"赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人"),e("li",null,"用好词,美化自己,把功劳往自己身上揽"),e("li",null,"强调双方关系: 我们是很友好的哦、我们是合作伙伴哦"),e("li",null,"回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!")],-1),Y=e("p",null,"相关链接:",-1),M={href:"http://www.kekenet.com/daxue/201707/518237.shtml",target:"_blank",rel:"noopener noreferrer"},O={href:"http://www.kekenet.com/daxue/201708/518540.shtml",target:"_blank",rel:"noopener noreferrer"},X=e("p",null,"另外,我摘录了一些觉得不错的句子:",-1),j=e("ul",null,[e("li",null,"When dealing with people, let us remember we are not dealing with creatures of logic."),e("li",null,"We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity."),e("li",null,[t("Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be"),e("br"),t(" understanding and forgiving.")])],-1);function z(H,R){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,c,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/44290107"),o(n)])]),d,u,p,w,e("p",null,[t("原文:"),e("a",m,[t("https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf"),o(n)])]),e("ul",null,[e("li",null,[e("a",f,[t("wiki"),o(n)]),k,t(" 说明:"),e("a",b,[t("https://en.wikipedia.org/wiki/Say_Yes_(short_story)"),o(n)])])]),g,y,e("p",null,[t("原文:"),e("a",x,[t("https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf"),o(n)])]),D,T,e("p",null,[t("原文:"),e("a",v,[t("https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf"),o(n)])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201706/508290.shtml"),o(n)])]),e("li",null,[t("拓展阅读,"),e("a",B,[t("肖伯纳的 THE DOCTOR’S DILEMMA"),o(n)]),S,t(" :"),e("a",W,[t("https://www.gutenberg.org/files/5070/5070-h/5070-h.htm"),o(n)])])]),A,e("ul",null,[e("li",null,[t("原文:"),e("a",I,[t("http://www.kekenet.com/daxue/201708/521462.shtml"),o(n)])]),L]),e("p",null,[t("解析:"),e("a",N,[t("https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/"),o(n)])]),P,V,Y,e("ul",null,[e("li",null,[t("竞选胜利演讲:"),e("a",M,[t("http://www.kekenet.com/daxue/201707/518237.shtml"),o(n)])]),e("li",null,[t("在上海的演讲:"),e("a",O,[t("http://www.kekenet.com/daxue/201708/518540.shtml"),o(n)])])]),X,j])}const G=l(i,[["render",z],["__file","contemporary-college-english-2.html.vue"]]);export{G as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as o}from"./app-dcf5468f.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第二册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第二册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第二册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),_={href:"https://www.ximalaya.com/album/44290107",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。",-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),p=e("p",null,"say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂",-1),w=e("ul",null,[e("li")],-1),m={href:"https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf",target:"_blank",rel:"noopener noreferrer"},f={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/Say_Yes_(short_story)",target:"_blank",rel:"noopener noreferrer"},g=e("p",null,"The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~",-1),y=e("ul",null,[e("li")],-1),x={href:"https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?",-1),T=e("ul",null,[e("li")],-1),v={href:"https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"A Doctor's Dilemma 医生的困境",-1),C={href:"http://www.kekenet.com/daxue/201706/508290.shtml",target:"_blank",rel:"noopener noreferrer"},B={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},S=e("br",null,null,-1),W={href:"https://www.gutenberg.org/files/5070/5070-h/5070-h.htm",target:"_blank",rel:"noopener noreferrer"},A=e("p",null,"the-oyster-and-the-pearl",-1),I={href:"http://www.kekenet.com/daxue/201708/521462.shtml",target:"_blank",rel:"noopener noreferrer"},L=e("li",null,null,-1),N={href:"https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:",-1),V=e("ol",null,[e("li",null,"赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人"),e("li",null,"用好词,美化自己,把功劳往自己身上揽"),e("li",null,"强调双方关系: 我们是很友好的哦、我们是合作伙伴哦"),e("li",null,"回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!")],-1),Y=e("p",null,"相关链接:",-1),M={href:"http://www.kekenet.com/daxue/201707/518237.shtml",target:"_blank",rel:"noopener noreferrer"},O={href:"http://www.kekenet.com/daxue/201708/518540.shtml",target:"_blank",rel:"noopener noreferrer"},X=e("p",null,"另外,我摘录了一些觉得不错的句子:",-1),j=e("ul",null,[e("li",null,"When dealing with people, let us remember we are not dealing with creatures of logic."),e("li",null,"We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity."),e("li",null,[t("Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be"),e("br"),t(" understanding and forgiving.")])],-1);function z(H,R){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,c,e("p",null,[t("全书链接:"),e("a",_,[t("https://www.ximalaya.com/album/44290107"),o(n)])]),d,u,p,w,e("p",null,[t("原文:"),e("a",m,[t("https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdf"),o(n)])]),e("ul",null,[e("li",null,[e("a",f,[t("wiki"),o(n)]),k,t(" 说明:"),e("a",b,[t("https://en.wikipedia.org/wiki/Say_Yes_(short_story)"),o(n)])])]),g,y,e("p",null,[t("原文:"),e("a",x,[t("https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdf"),o(n)])]),D,T,e("p",null,[t("原文:"),e("a",v,[t("https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdf"),o(n)])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201706/508290.shtml"),o(n)])]),e("li",null,[t("拓展阅读,"),e("a",B,[t("肖伯纳的 THE DOCTOR’S DILEMMA"),o(n)]),S,t(" :"),e("a",W,[t("https://www.gutenberg.org/files/5070/5070-h/5070-h.htm"),o(n)])])]),A,e("ul",null,[e("li",null,[t("原文:"),e("a",I,[t("http://www.kekenet.com/daxue/201708/521462.shtml"),o(n)])]),L]),e("p",null,[t("解析:"),e("a",N,[t("https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/"),o(n)])]),P,V,Y,e("ul",null,[e("li",null,[t("竞选胜利演讲:"),e("a",M,[t("http://www.kekenet.com/daxue/201707/518237.shtml"),o(n)])]),e("li",null,[t("在上海的演讲:"),e("a",O,[t("http://www.kekenet.com/daxue/201708/518540.shtml"),o(n)])])]),X,j])}const G=l(i,[["render",z],["__file","contemporary-college-english-2.html.vue"]]);export{G as default}; diff --git a/assets/contemporary-college-english-2.html-aa3974a2.js b/assets/contemporary-college-english-2.html-b3e5d0ca.js similarity index 66% rename from assets/contemporary-college-english-2.html-aa3974a2.js rename to assets/contemporary-college-english-2.html-b3e5d0ca.js index eec42385..f1252da3 100644 --- a/assets/contemporary-college-english-2.html-aa3974a2.js +++ b/assets/contemporary-college-english-2.html-b3e5d0ca.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-93b316c0","path":"/english/contemporary-college-english-2.html","title":"现代大学英语精读(第2版)第二册","lang":"zh-CN","frontmatter":{"date":"2022-06-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-2.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第二册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第二册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.96,"words":588},"filePathRelative":"english/contemporary-college-english-2.md","localizedDate":"2022年6月4日","excerpt":"

现代大学英语精读(第2版)第二册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44290107

\\n

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

\\n

值得一读的文章

\\n

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-93b316c0","path":"/english/contemporary-college-english-2.html","title":"现代大学英语精读(第2版)第二册","lang":"zh-CN","frontmatter":{"date":"2022-06-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-2.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第二册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第二册 介绍 全书链接:https://www.ximalaya.com/album/44290107 本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。 值得一读的文章 say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第二册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.96,"words":588},"filePathRelative":"english/contemporary-college-english-2.md","localizedDate":"2022年6月4日","excerpt":"

现代大学英语精读(第2版)第二册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44290107

\\n

本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

\\n

值得一读的文章

\\n

say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-3.html-5dab2a15.js b/assets/contemporary-college-english-3.html-cbd18de0.js similarity index 67% rename from assets/contemporary-college-english-3.html-5dab2a15.js rename to assets/contemporary-college-english-3.html-cbd18de0.js index fc6a7dd9..7d18d24b 100644 --- a/assets/contemporary-college-english-3.html-5dab2a15.js +++ b/assets/contemporary-college-english-3.html-cbd18de0.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-90496582","path":"/english/contemporary-college-english-3.html","title":"现代大学英语精读(第2版)第三册","lang":"zh-CN","frontmatter":{"date":"2022-06-25T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-3.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第三册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第三册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-25T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.95,"words":586},"filePathRelative":"english/contemporary-college-english-3.md","localizedDate":"2022年6月25日","excerpt":"

现代大学英语精读(第2版)第三册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44439108

\\n

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-90496582","path":"/english/contemporary-college-english-3.html","title":"现代大学英语精读(第2版)第三册","lang":"zh-CN","frontmatter":{"date":"2022-06-25T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-3.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第三册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第三册 介绍 全书链接:https://www.ximalaya.com/album/44439108 阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-25T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第三册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-25T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.95,"words":586},"filePathRelative":"english/contemporary-college-english-3.md","localizedDate":"2022年6月25日","excerpt":"

现代大学英语精读(第2版)第三册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44439108

\\n

阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-3.html-ea59c72d.js b/assets/contemporary-college-english-3.html-e7cb754f.js similarity index 98% rename from assets/contemporary-college-english-3.html-ea59c72d.js rename to assets/contemporary-college-english-3.html-e7cb754f.js index 51907be5..098b67a6 100644 --- a/assets/contemporary-college-english-3.html-ea59c72d.js +++ b/assets/contemporary-college-english-3.html-e7cb754f.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as l}from"./app-ee4d23bf.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第三册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第三册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第三册")],-1),_=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/44439108",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。",-1),p=e("p",null,"这种转变,说明了两点:",-1),m=e("ol",null,[e("li",null,"我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了"),e("li",null,"我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读")],-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:",-1),f=e("ul",null,[e("li")],-1),k={href:"https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The Invisible Japanese Gentlemen",-1),x=e("ul",null,[e("li")],-1),v={href:"https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292",target:"_blank",rel:"noopener noreferrer"},L={href:"http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"My Grandmother, the Bag Lady 被这篇文章打动,泪目了",-1),D={href:"http://www.kekenet.com/daxue/201704/504622.shtml",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇",-1),C={href:"http://www.kekenet.com/daxue/201705/509083.shtml",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史",-1),A={href:"https://www.ximalaya.com/sound/361971944",target:"_blank",rel:"noopener noreferrer"},B=e("p",null,"the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过",-1),K=e("ul",null,[e("li")],-1),T={href:"https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis",target:"_blank",rel:"noopener noreferrer"},M=e("p",null,"The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状",-1),N={href:"http://www.kekenet.com/daxue/201708/521739.shtml",target:"_blank",rel:"noopener noreferrer"},V=e("p",null,"Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。",-1),W={href:"http://www.kekenet.com/daxue/201708/522771.shtml",target:"_blank",rel:"noopener noreferrer"},j=e("li",null,null,-1),F={href:"https://www.bilibili.com/bangumi/play/ep332629?theme=movie&spm_id_from=333.337.0.0",target:"_blank",rel:"noopener noreferrer"};function G(S,H){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,_,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/44439108"),l(n)])]),d,p,m,u,w,f,e("p",null,[t("原文:"),e("a",k,[t("https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf"),l(n)])]),e("ul",null,[e("li",null,[t("wiki"),g,t(" 解析:"),e("a",b,[t("https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary"),l(n)])])]),y,x,e("p",null,[t("原文:"),e("a",v,[t("https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292"),l(n)])]),e("ul",null,[e("li",null,[e("a",L,[t("分析:http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/"),l(n)])])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",D,[t("http://www.kekenet.com/daxue/201704/504622.shtml"),l(n)])])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201705/509083.shtml"),l(n)])])]),P,e("ul",null,[e("li",null,[t("原文:"),e("a",A,[t("https://www.ximalaya.com/sound/361971944"),l(n)])])]),B,K,e("p",null,[t("原文+分析(左边是内容,右边是分析):"),e("a",T,[t("https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis"),l(n)])]),M,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("http://www.kekenet.com/daxue/201708/521739.shtml"),l(n)])])]),V,e("ul",null,[e("li",null,[t("原文:"),e("a",W,[t("http://www.kekenet.com/daxue/201708/522771.shtml"),l(n)])]),j]),e("p",null,[t("电影:"),e("a",F,[t("https://www.bilibili.com/bangumi/play/ep332629?theme=movie"),l(n)])])])}const z=o(i,[["render",G],["__file","contemporary-college-english-3.html.vue"]]);export{z as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as a,a as e,b as t,d as l}from"./app-dcf5468f.js";const i={},h=e("h1",{id:"现代大学英语精读-第2版-第三册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第三册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第三册")],-1),_=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/44439108",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。",-1),p=e("p",null,"这种转变,说明了两点:",-1),m=e("ol",null,[e("li",null,"我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了"),e("li",null,"我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读")],-1),u=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:",-1),f=e("ul",null,[e("li")],-1),k={href:"https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),b={href:"https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The Invisible Japanese Gentlemen",-1),x=e("ul",null,[e("li")],-1),v={href:"https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292",target:"_blank",rel:"noopener noreferrer"},L={href:"http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"My Grandmother, the Bag Lady 被这篇文章打动,泪目了",-1),D={href:"http://www.kekenet.com/daxue/201704/504622.shtml",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇",-1),C={href:"http://www.kekenet.com/daxue/201705/509083.shtml",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史",-1),A={href:"https://www.ximalaya.com/sound/361971944",target:"_blank",rel:"noopener noreferrer"},B=e("p",null,"the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过",-1),K=e("ul",null,[e("li")],-1),T={href:"https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis",target:"_blank",rel:"noopener noreferrer"},M=e("p",null,"The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状",-1),N={href:"http://www.kekenet.com/daxue/201708/521739.shtml",target:"_blank",rel:"noopener noreferrer"},V=e("p",null,"Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。",-1),W={href:"http://www.kekenet.com/daxue/201708/522771.shtml",target:"_blank",rel:"noopener noreferrer"},j=e("li",null,null,-1),F={href:"https://www.bilibili.com/bangumi/play/ep332629?theme=movie&spm_id_from=333.337.0.0",target:"_blank",rel:"noopener noreferrer"};function G(S,H){const n=r("ExternalLinkIcon");return s(),a("div",null,[h,_,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/44439108"),l(n)])]),d,p,m,u,w,f,e("p",null,[t("原文:"),e("a",k,[t("https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdf"),l(n)])]),e("ul",null,[e("li",null,[t("wiki"),g,t(" 解析:"),e("a",b,[t("https://en.wikipedia.org/wiki/A_Dill_Pickle#Plot_summary"),l(n)])])]),y,x,e("p",null,[t("原文:"),e("a",v,[t("https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292"),l(n)])]),e("ul",null,[e("li",null,[e("a",L,[t("分析:http://sittingbee.com/the-invisible-japanese-gentlemen-graham-greene/"),l(n)])])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",D,[t("http://www.kekenet.com/daxue/201704/504622.shtml"),l(n)])])]),E,e("ul",null,[e("li",null,[t("原文:"),e("a",C,[t("http://www.kekenet.com/daxue/201705/509083.shtml"),l(n)])])]),P,e("ul",null,[e("li",null,[t("原文:"),e("a",A,[t("https://www.ximalaya.com/sound/361971944"),l(n)])])]),B,K,e("p",null,[t("原文+分析(左边是内容,右边是分析):"),e("a",T,[t("https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysis"),l(n)])]),M,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("http://www.kekenet.com/daxue/201708/521739.shtml"),l(n)])])]),V,e("ul",null,[e("li",null,[t("原文:"),e("a",W,[t("http://www.kekenet.com/daxue/201708/522771.shtml"),l(n)])]),j]),e("p",null,[t("电影:"),e("a",F,[t("https://www.bilibili.com/bangumi/play/ep332629?theme=movie"),l(n)])])])}const z=o(i,[["render",G],["__file","contemporary-college-english-3.html.vue"]]);export{z as default}; diff --git a/assets/contemporary-college-english-4.html-ca65df0b.js b/assets/contemporary-college-english-4.html-0f930a4d.js similarity index 98% rename from assets/contemporary-college-english-4.html-ca65df0b.js rename to assets/contemporary-college-english-4.html-0f930a4d.js index 273868aa..bd231fca 100644 --- a/assets/contemporary-college-english-4.html-ca65df0b.js +++ b/assets/contemporary-college-english-4.html-0f930a4d.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as a,c as s,a as e,b as t,d as o}from"./app-ee4d23bf.js";const h={},u=e("h1",{id:"现代大学英语精读-第2版-第四册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第四册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第四册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),d={href:"https://www.ximalaya.com/album/44641280",target:"_blank",rel:"noopener noreferrer"},i=e("p",null,"本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。",-1),_=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从",-1),p={href:"http://www.kekenet.com/daxue/201808/561902.shtml",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”",-1),f={href:"http://www.kekenet.com/daxue/201811/571277.shtml",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”",-1),x={href:"http://www.kekenet.com/daxue/201812/573461.shtml",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定",-1),g={href:"http://www.kekenet.com/daxue/201812/574998.shtml",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The World House 马丁·路德·金 的文章",-1),F={href:"http://www.kekenet.com/daxue/201901/576160.shtml",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"Soldier's Heart 也叫PTSD,由战后士兵自述",-1),E={href:"http://www.kekenet.com/daxue/201902/578424.shtml",target:"_blank",rel:"noopener noreferrer"},C=e("p",null,"Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。",-1),T={href:"http://www.kekenet.com/daxue/201903/580020.shtml",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,[t("The Rivals"),e("br"),t(" 两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1688258250066.png",alt:"1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png"}),e("br"),t(" 强烈推荐,你一定也会“surprised”!")],-1),B={href:"http://www.kekenet.com/daxue/201904/584021.shtml",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"Cord:一对母女的故事",-1),N={href:"https://www.ximalaya.com/sound/364021411",target:"_blank",rel:"noopener noreferrer"},A=e("li",null,null,-1),H={href:"https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf?from=https%3A%2F%2Fwww.yuque.com%2Flevy%2Fblog%2Fxweufx%2Fedit",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章",-1),P={href:"https://www.ximalaya.com/sound/364032034",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"Man of the Moment 又到了令人享受的戏剧欣赏时间",-1),V={href:"https://www.ximalaya.com/sound/364032690",target:"_blank",rel:"noopener noreferrer"},G=e("p",null,"Is Everybody Happy 讨论了幸福的定义",-1),L={href:"https://www.ximalaya.com/sound/364033142",target:"_blank",rel:"noopener noreferrer"},M={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},R=e("br",null,null,-1);function W(j,z){const n=r("ExternalLinkIcon");return a(),s("div",null,[u,c,e("p",null,[t("全书链接:"),e("a",d,[t("https://www.ximalaya.com/album/44641280"),o(n)])]),i,_,w,e("ul",null,[e("li",null,[t("原文:"),e("a",p,[t("http://www.kekenet.com/daxue/201808/561902.shtml"),o(n)])])]),m,e("ul",null,[e("li",null,[t("原文:"),e("a",f,[t("http://www.kekenet.com/daxue/201811/571277.shtml"),o(n)])])]),k,e("ul",null,[e("li",null,[t("原文:"),e("a",x,[t("http://www.kekenet.com/daxue/201812/573461.shtml"),o(n)])])]),b,e("ul",null,[e("li",null,[t("原文:"),e("a",g,[t("http://www.kekenet.com/daxue/201812/574998.shtml"),o(n)])])]),y,e("ul",null,[e("li",null,[t("原文:"),e("a",F,[t("http://www.kekenet.com/daxue/201901/576160.shtml"),o(n)])])]),v,e("ul",null,[e("li",null,[t("原文:"),e("a",E,[t("http://www.kekenet.com/daxue/201902/578424.shtml"),o(n)])])]),C,e("ul",null,[e("li",null,[t("原文:"),e("a",T,[t("http://www.kekenet.com/daxue/201903/580020.shtml"),o(n)])])]),q,e("ul",null,[e("li",null,[t("原文:"),e("a",B,[t("http://www.kekenet.com/daxue/201904/584021.shtml"),o(n)])])]),D,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("https://www.ximalaya.com/sound/364021411"),o(n)])]),A]),e("p",null,[t("论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:"),e("a",H,[t("https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf"),o(n)])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",P,[t("https://www.ximalaya.com/sound/364032034"),o(n)])])]),S,e("ul",null,[e("li",null,[t("原文:"),e("a",V,[t("https://www.ximalaya.com/sound/364032690"),o(n)])])]),G,e("ul",null,[e("li",null,[e("p",null,[t("原文:"),e("a",L,[t("https://www.ximalaya.com/sound/364033142"),o(n)])]),e("p",null,[e("a",M,[R,o(n)])])])])])}const O=l(h,[["render",W],["__file","contemporary-college-english-4.html.vue"]]);export{O as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as a,c as s,a as e,b as t,d as o}from"./app-dcf5468f.js";const h={},u=e("h1",{id:"现代大学英语精读-第2版-第四册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第四册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第四册")],-1),c=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),d={href:"https://www.ximalaya.com/album/44641280",target:"_blank",rel:"noopener noreferrer"},i=e("p",null,"本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。",-1),_=e("h2",{id:"值得一读的文章",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#值得一读的文章","aria-hidden":"true"},"#"),t(" 值得一读的文章")],-1),w=e("p",null,"Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从",-1),p={href:"http://www.kekenet.com/daxue/201808/561902.shtml",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”",-1),f={href:"http://www.kekenet.com/daxue/201811/571277.shtml",target:"_blank",rel:"noopener noreferrer"},k=e("p",null,"The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”",-1),x={href:"http://www.kekenet.com/daxue/201812/573461.shtml",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定",-1),g={href:"http://www.kekenet.com/daxue/201812/574998.shtml",target:"_blank",rel:"noopener noreferrer"},y=e("p",null,"The World House 马丁·路德·金 的文章",-1),F={href:"http://www.kekenet.com/daxue/201901/576160.shtml",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"Soldier's Heart 也叫PTSD,由战后士兵自述",-1),E={href:"http://www.kekenet.com/daxue/201902/578424.shtml",target:"_blank",rel:"noopener noreferrer"},C=e("p",null,"Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。",-1),T={href:"http://www.kekenet.com/daxue/201903/580020.shtml",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,[t("The Rivals"),e("br"),t(" 两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1688258250066.png",alt:"1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png"}),e("br"),t(" 强烈推荐,你一定也会“surprised”!")],-1),B={href:"http://www.kekenet.com/daxue/201904/584021.shtml",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"Cord:一对母女的故事",-1),N={href:"https://www.ximalaya.com/sound/364021411",target:"_blank",rel:"noopener noreferrer"},A=e("li",null,null,-1),H={href:"https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf?from=https%3A%2F%2Fwww.yuque.com%2Flevy%2Fblog%2Fxweufx%2Fedit",target:"_blank",rel:"noopener noreferrer"},I=e("p",null,"The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章",-1),P={href:"https://www.ximalaya.com/sound/364032034",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"Man of the Moment 又到了令人享受的戏剧欣赏时间",-1),V={href:"https://www.ximalaya.com/sound/364032690",target:"_blank",rel:"noopener noreferrer"},G=e("p",null,"Is Everybody Happy 讨论了幸福的定义",-1),L={href:"https://www.ximalaya.com/sound/364033142",target:"_blank",rel:"noopener noreferrer"},M={href:"https://americanliterature.com/author/o-henry/short-story/hearts-and-hands",target:"_blank",rel:"noopener noreferrer"},R=e("br",null,null,-1);function W(j,z){const n=r("ExternalLinkIcon");return a(),s("div",null,[u,c,e("p",null,[t("全书链接:"),e("a",d,[t("https://www.ximalaya.com/album/44641280"),o(n)])]),i,_,w,e("ul",null,[e("li",null,[t("原文:"),e("a",p,[t("http://www.kekenet.com/daxue/201808/561902.shtml"),o(n)])])]),m,e("ul",null,[e("li",null,[t("原文:"),e("a",f,[t("http://www.kekenet.com/daxue/201811/571277.shtml"),o(n)])])]),k,e("ul",null,[e("li",null,[t("原文:"),e("a",x,[t("http://www.kekenet.com/daxue/201812/573461.shtml"),o(n)])])]),b,e("ul",null,[e("li",null,[t("原文:"),e("a",g,[t("http://www.kekenet.com/daxue/201812/574998.shtml"),o(n)])])]),y,e("ul",null,[e("li",null,[t("原文:"),e("a",F,[t("http://www.kekenet.com/daxue/201901/576160.shtml"),o(n)])])]),v,e("ul",null,[e("li",null,[t("原文:"),e("a",E,[t("http://www.kekenet.com/daxue/201902/578424.shtml"),o(n)])])]),C,e("ul",null,[e("li",null,[t("原文:"),e("a",T,[t("http://www.kekenet.com/daxue/201903/580020.shtml"),o(n)])])]),q,e("ul",null,[e("li",null,[t("原文:"),e("a",B,[t("http://www.kekenet.com/daxue/201904/584021.shtml"),o(n)])])]),D,e("ul",null,[e("li",null,[t("原文:"),e("a",N,[t("https://www.ximalaya.com/sound/364021411"),o(n)])]),A]),e("p",null,[t("论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:"),e("a",H,[t("https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdf"),o(n)])]),I,e("ul",null,[e("li",null,[t("原文:"),e("a",P,[t("https://www.ximalaya.com/sound/364032034"),o(n)])])]),S,e("ul",null,[e("li",null,[t("原文:"),e("a",V,[t("https://www.ximalaya.com/sound/364032690"),o(n)])])]),G,e("ul",null,[e("li",null,[e("p",null,[t("原文:"),e("a",L,[t("https://www.ximalaya.com/sound/364033142"),o(n)])]),e("p",null,[e("a",M,[R,o(n)])])])])])}const O=l(h,[["render",W],["__file","contemporary-college-english-4.html.vue"]]);export{O as default}; diff --git a/assets/contemporary-college-english-4.html-519c519e.js b/assets/contemporary-college-english-4.html-fe4a2c8d.js similarity index 65% rename from assets/contemporary-college-english-4.html-519c519e.js rename to assets/contemporary-college-english-4.html-fe4a2c8d.js index 66fbcdcd..969fc392 100644 --- a/assets/contemporary-college-english-4.html-519c519e.js +++ b/assets/contemporary-college-english-4.html-fe4a2c8d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-8cdfb444","path":"/english/contemporary-college-english-4.html","title":"现代大学英语精读(第2版)第四册","lang":"zh-CN","frontmatter":{"date":"2022-07-11T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-4.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第四册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第四册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-11T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.02,"words":607},"filePathRelative":"english/contemporary-college-english-4.md","localizedDate":"2022年7月11日","excerpt":"

现代大学英语精读(第2版)第四册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44641280

\\n

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

\\n

值得一读的文章

\\n

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-8cdfb444","path":"/english/contemporary-college-english-4.html","title":"现代大学英语精读(第2版)第四册","lang":"zh-CN","frontmatter":{"date":"2022-07-11T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-4.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第四册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第四册 介绍 全书链接:https://www.ximalaya.com/album/44641280 本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。 值得一读的文章 Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第四册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-11T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"值得一读的文章","slug":"值得一读的文章","link":"#值得一读的文章","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.02,"words":607},"filePathRelative":"english/contemporary-college-english-4.md","localizedDate":"2022年7月11日","excerpt":"

现代大学英语精读(第2版)第四册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/44641280

\\n

本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

\\n

值得一读的文章

\\n

Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

","autoDesc":true}');export{e as data}; diff --git a/assets/contemporary-college-english-5.html-aca7ab08.js b/assets/contemporary-college-english-5.html-3b490327.js similarity index 98% rename from assets/contemporary-college-english-5.html-aca7ab08.js rename to assets/contemporary-college-english-5.html-3b490327.js index c23519a7..73219c10 100644 --- a/assets/contemporary-college-english-5.html-aca7ab08.js +++ b/assets/contemporary-college-english-5.html-3b490327.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as s,c as h,a as e,b as t,d as a,e as n}from"./app-ee4d23bf.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第五册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第五册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第五册")],-1),u=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/49466046",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。",-1),p=e("h2",{id:"who-are-you-and-what-are-you-doing-here",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#who-are-you-and-what-are-you-doing-here","aria-hidden":"true"},"#"),t(" Who Are You and What Are You Doing Here")],-1),y={href:"https://m.kekenet.com/daxue/201909/593904.shtml",target:"_blank",rel:"noopener noreferrer"},w=n('

本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
particular. To almost every university education is a means to an end. For students, that end is a good job.

The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
have the experience that Longinus associated with the sublime: You feel that you have actually created the text
yourself. For somehow your predecessors are more yourself than you are.

Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

Two Kinds

',5),f={href:"https://m.kekenet.com/daxue/201909/595380.shtml",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。",-1),g=e("p",null,[t("摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This"),e("br"),t(" wasn't China. I had listened to her before and look what happened. She was the stupid one.")],-1),_=e("p",null,`You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"`,-1),k=e("p",null,[t('"Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only'),e("br"),t(' one kind of daughter can live in this house. Obedient daughter!"')],-1),v=e("p",null,`"Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.`,-1),x=e("h2",{id:"love-is-a-fallacy",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#love-is-a-fallacy","aria-hidden":"true"},"#"),t(" Love is a Fallacy")],-1),I={href:"https://www.ximalaya.com/sound/414753193",target:"_blank",rel:"noopener noreferrer"},T=e("p",null,"这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。",-1),A=e("h2",{id:"rewriting-american-history",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rewriting-american-history","aria-hidden":"true"},"#"),t(" Rewriting American History")],-1),Y={href:"https://www.ximalaya.com/sound/414754775",target:"_blank",rel:"noopener noreferrer"},j=e("p",null,"本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。",-1),S=e("h2",{id:"nobel-peace-price-about-global-warming",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#nobel-peace-price-about-global-warming","aria-hidden":"true"},"#"),t(" Nobel Peace Price About Global Warming")],-1),q={href:"https://www.ximalaya.com/sound/414756339",target:"_blank",rel:"noopener noreferrer"},N=e("h2",{id:"the-bluest-eyes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-bluest-eyes","aria-hidden":"true"},"#"),t(" The Bluest Eyes")],-1),B={href:"https://www.ximalaya.com/sound/414757228",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。",-1),O=e("h2",{id:"how-news-becomes-options-and-opinions-off-limits",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-news-becomes-options-and-opinions-off-limits","aria-hidden":"true"},"#"),t(" How News Becomes Options and Opinions Off-Limits")],-1),F={href:"https://www.ximalaya.com/sound/414759448",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。",-1),W=e("p",null,[t("摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without"),e("br"),t(" which all other freedoms would fall.")],-1),C=e("p",null,[t("In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and"),e("br"),t(" untrammeled.")],-1),E=e("p",null,[t("A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free"),e("br"),t(" societies are dynamic, noisy, turbulent and full of radical disagreements.")],-1),z=e("h2",{id:"the-indispensable-opposition",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-indispensable-opposition","aria-hidden":"true"},"#"),t(" The Indispensable Opposition")],-1),H={href:"https://m.kekenet.com/daxue/202001/603745.shtml",target:"_blank",rel:"noopener noreferrer"},R=n('

本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
is to find the truth.

The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
is tolerated as constitutional but must be maintained because it is in fact indispensable.

The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
just as necessary that the party in power should never outrage the minority. That means that it must listen to the
minority and be moved by the criticisms of the minority. That means that its measures must take account of the
minority's objections, and that in administering measures it must remember that the minority may become the majority.

The Danger of a Single Story

',7),P={href:"https://www.ximalaya.com/sound/414765357",target:"_blank",rel:"noopener noreferrer"},V=n('

认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
the story of another person, but to make it the definitive story of that person.

All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
overlook the many other stories that formed me.

The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
incomplete. They make one story become the only story.

The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
difficult. It emphasizes how we are different rather than how we are similar.

Come Rain or Come Shine

',6),G={href:"https://www.ximalaya.com/sound/414767340",target:"_blank",rel:"noopener noreferrer"},J=e("p",null,"看得不太懂,但故事倒挺有意思的。",-1),K={href:"https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db",target:"_blank",rel:"noopener noreferrer"},M=e("h2",{id:"invisible-man",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#invisible-man","aria-hidden":"true"},"#"),t(" Invisible Man")],-1),Q={href:"https://www.ximalaya.com/sound/414776047",target:"_blank",rel:"noopener noreferrer"},U=e("p",null,"节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.",-1),X=e("h2",{id:"you-ve-got-to-find-what-you-love",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#you-ve-got-to-find-what-you-love","aria-hidden":"true"},"#"),t(" You've Got to Find What You Love")],-1),Z={href:"https://www.ximalaya.com/sound/414769322",target:"_blank",rel:"noopener noreferrer"},$=n('

Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
you off the well-worn path. And that would make all the difference.

Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
There is no reason not to follow your heart.

Where Do We Go from Here

',5),ee={href:"https://www.ximalaya.com/sound/414771197",target:"_blank",rel:"noopener noreferrer"},te=e("p",null,"本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。",-1),oe=e("p",null,[t("摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is"),e("br"),t(" the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or"),e("br"),t(" Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to"),e("br"),t(" the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.")],-1),ae=e("p",null,[t("And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you"),e("br"),t(" can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may"),e("br"),t(" murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.")],-1);function ne(re,ie){const o=i("ExternalLinkIcon");return s(),h("div",null,[d,u,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/49466046"),a(o)])]),m,p,e("p",null,[t("原文链接:"),e("a",y,[t("https://m.kekenet.com/daxue/201909/593904.shtml"),a(o)])]),w,e("p",null,[t("原文链接:"),e("a",f,[t("https://m.kekenet.com/daxue/201909/595380.shtml"),a(o)])]),b,g,_,k,v,x,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/414753193"),a(o)])]),T,A,e("p",null,[t("原文链接:"),e("a",Y,[t("https://www.ximalaya.com/sound/414754775"),a(o)])]),j,S,e("p",null,[t("原文链接:"),e("a",q,[t("https://www.ximalaya.com/sound/414756339"),a(o)])]),N,e("p",null,[t("原文链接:"),e("a",B,[t("https://www.ximalaya.com/sound/414757228"),a(o)])]),D,O,e("p",null,[t("原文链接:"),e("a",F,[t("https://www.ximalaya.com/sound/414759448"),a(o)])]),L,W,C,E,z,e("p",null,[t("原文链接:"),e("a",H,[t("https://m.kekenet.com/daxue/202001/603745.shtml"),a(o)])]),R,e("p",null,[t("原文链接:"),e("a",P,[t("https://www.ximalaya.com/sound/414765357"),a(o)])]),V,e("p",null,[t("原文链接:"),e("a",G,[t("https://www.ximalaya.com/sound/414767340"),a(o)])]),J,e("p",null,[t("解析:"),e("a",K,[t("https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db"),a(o)])]),M,e("p",null,[t("原文链接:"),e("a",Q,[t("https://www.ximalaya.com/sound/414776047"),a(o)])]),U,X,e("p",null,[t("原文链接:"),e("a",Z,[t("https://www.ximalaya.com/sound/414769322"),a(o)])]),$,e("p",null,[t("原文链接:"),e("a",ee,[t("https://www.ximalaya.com/sound/414771197"),a(o)])]),te,oe,ae])}const le=r(l,[["render",ne],["__file","contemporary-college-english-5.html.vue"]]);export{le as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as s,c as h,a as e,b as t,d as a,f as n}from"./app-dcf5468f.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第五册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第五册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第五册")],-1),u=e("h2",{id:"介绍",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),t(" 介绍")],-1),c={href:"https://www.ximalaya.com/album/49466046",target:"_blank",rel:"noopener noreferrer"},m=e("p",null,"本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。",-1),p=e("h2",{id:"who-are-you-and-what-are-you-doing-here",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#who-are-you-and-what-are-you-doing-here","aria-hidden":"true"},"#"),t(" Who Are You and What Are You Doing Here")],-1),y={href:"https://m.kekenet.com/daxue/201909/593904.shtml",target:"_blank",rel:"noopener noreferrer"},f=n('

本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
particular. To almost every university education is a means to an end. For students, that end is a good job.

The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
have the experience that Longinus associated with the sublime: You feel that you have actually created the text
yourself. For somehow your predecessors are more yourself than you are.

Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

Two Kinds

',5),w={href:"https://m.kekenet.com/daxue/201909/595380.shtml",target:"_blank",rel:"noopener noreferrer"},b=e("p",null,"本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。",-1),g=e("p",null,[t("摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This"),e("br"),t(" wasn't China. I had listened to her before and look what happened. She was the stupid one.")],-1),_=e("p",null,`You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"`,-1),k=e("p",null,[t('"Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only'),e("br"),t(' one kind of daughter can live in this house. Obedient daughter!"')],-1),v=e("p",null,`"Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.`,-1),x=e("h2",{id:"love-is-a-fallacy",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#love-is-a-fallacy","aria-hidden":"true"},"#"),t(" Love is a Fallacy")],-1),I={href:"https://www.ximalaya.com/sound/414753193",target:"_blank",rel:"noopener noreferrer"},T=e("p",null,"这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。",-1),A=e("h2",{id:"rewriting-american-history",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rewriting-american-history","aria-hidden":"true"},"#"),t(" Rewriting American History")],-1),Y={href:"https://www.ximalaya.com/sound/414754775",target:"_blank",rel:"noopener noreferrer"},j=e("p",null,"本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。",-1),S=e("h2",{id:"nobel-peace-price-about-global-warming",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#nobel-peace-price-about-global-warming","aria-hidden":"true"},"#"),t(" Nobel Peace Price About Global Warming")],-1),q={href:"https://www.ximalaya.com/sound/414756339",target:"_blank",rel:"noopener noreferrer"},N=e("h2",{id:"the-bluest-eyes",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-bluest-eyes","aria-hidden":"true"},"#"),t(" The Bluest Eyes")],-1),B={href:"https://www.ximalaya.com/sound/414757228",target:"_blank",rel:"noopener noreferrer"},D=e("p",null,"这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。",-1),O=e("h2",{id:"how-news-becomes-options-and-opinions-off-limits",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-news-becomes-options-and-opinions-off-limits","aria-hidden":"true"},"#"),t(" How News Becomes Options and Opinions Off-Limits")],-1),F={href:"https://www.ximalaya.com/sound/414759448",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。",-1),W=e("p",null,[t("摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without"),e("br"),t(" which all other freedoms would fall.")],-1),C=e("p",null,[t("In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and"),e("br"),t(" untrammeled.")],-1),E=e("p",null,[t("A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free"),e("br"),t(" societies are dynamic, noisy, turbulent and full of radical disagreements.")],-1),z=e("h2",{id:"the-indispensable-opposition",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-indispensable-opposition","aria-hidden":"true"},"#"),t(" The Indispensable Opposition")],-1),H={href:"https://m.kekenet.com/daxue/202001/603745.shtml",target:"_blank",rel:"noopener noreferrer"},R=n('

本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
is to find the truth.

The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
is tolerated as constitutional but must be maintained because it is in fact indispensable.

The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
just as necessary that the party in power should never outrage the minority. That means that it must listen to the
minority and be moved by the criticisms of the minority. That means that its measures must take account of the
minority's objections, and that in administering measures it must remember that the minority may become the majority.

The Danger of a Single Story

',7),P={href:"https://www.ximalaya.com/sound/414765357",target:"_blank",rel:"noopener noreferrer"},V=n('

认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
the story of another person, but to make it the definitive story of that person.

All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
overlook the many other stories that formed me.

The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
incomplete. They make one story become the only story.

The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
difficult. It emphasizes how we are different rather than how we are similar.

Come Rain or Come Shine

',6),G={href:"https://www.ximalaya.com/sound/414767340",target:"_blank",rel:"noopener noreferrer"},J=e("p",null,"看得不太懂,但故事倒挺有意思的。",-1),K={href:"https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db",target:"_blank",rel:"noopener noreferrer"},M=e("h2",{id:"invisible-man",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#invisible-man","aria-hidden":"true"},"#"),t(" Invisible Man")],-1),Q={href:"https://www.ximalaya.com/sound/414776047",target:"_blank",rel:"noopener noreferrer"},U=e("p",null,"节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.",-1),X=e("h2",{id:"you-ve-got-to-find-what-you-love",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#you-ve-got-to-find-what-you-love","aria-hidden":"true"},"#"),t(" You've Got to Find What You Love")],-1),Z={href:"https://www.ximalaya.com/sound/414769322",target:"_blank",rel:"noopener noreferrer"},$=n('

Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
you off the well-worn path. And that would make all the difference.

Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
There is no reason not to follow your heart.

Where Do We Go from Here

',5),ee={href:"https://www.ximalaya.com/sound/414771197",target:"_blank",rel:"noopener noreferrer"},te=e("p",null,"本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。",-1),oe=e("p",null,[t("摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is"),e("br"),t(" the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or"),e("br"),t(" Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to"),e("br"),t(" the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.")],-1),ae=e("p",null,[t("And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you"),e("br"),t(" can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may"),e("br"),t(" murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.")],-1);function ne(re,ie){const o=i("ExternalLinkIcon");return s(),h("div",null,[d,u,e("p",null,[t("全书链接:"),e("a",c,[t("https://www.ximalaya.com/album/49466046"),a(o)])]),m,p,e("p",null,[t("原文链接:"),e("a",y,[t("https://m.kekenet.com/daxue/201909/593904.shtml"),a(o)])]),f,e("p",null,[t("原文链接:"),e("a",w,[t("https://m.kekenet.com/daxue/201909/595380.shtml"),a(o)])]),b,g,_,k,v,x,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/414753193"),a(o)])]),T,A,e("p",null,[t("原文链接:"),e("a",Y,[t("https://www.ximalaya.com/sound/414754775"),a(o)])]),j,S,e("p",null,[t("原文链接:"),e("a",q,[t("https://www.ximalaya.com/sound/414756339"),a(o)])]),N,e("p",null,[t("原文链接:"),e("a",B,[t("https://www.ximalaya.com/sound/414757228"),a(o)])]),D,O,e("p",null,[t("原文链接:"),e("a",F,[t("https://www.ximalaya.com/sound/414759448"),a(o)])]),L,W,C,E,z,e("p",null,[t("原文链接:"),e("a",H,[t("https://m.kekenet.com/daxue/202001/603745.shtml"),a(o)])]),R,e("p",null,[t("原文链接:"),e("a",P,[t("https://www.ximalaya.com/sound/414765357"),a(o)])]),V,e("p",null,[t("原文链接:"),e("a",G,[t("https://www.ximalaya.com/sound/414767340"),a(o)])]),J,e("p",null,[t("解析:"),e("a",K,[t("https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652db"),a(o)])]),M,e("p",null,[t("原文链接:"),e("a",Q,[t("https://www.ximalaya.com/sound/414776047"),a(o)])]),U,X,e("p",null,[t("原文链接:"),e("a",Z,[t("https://www.ximalaya.com/sound/414769322"),a(o)])]),$,e("p",null,[t("原文链接:"),e("a",ee,[t("https://www.ximalaya.com/sound/414771197"),a(o)])]),te,oe,ae])}const le=r(l,[["render",ne],["__file","contemporary-college-english-5.html.vue"]]);export{le as default}; diff --git a/assets/contemporary-college-english-5.html-3c38137f.js b/assets/contemporary-college-english-5.html-a86ee847.js similarity index 78% rename from assets/contemporary-college-english-5.html-3c38137f.js rename to assets/contemporary-college-english-5.html-a86ee847.js index 0ff8b1c8..860170c1 100644 --- a/assets/contemporary-college-english-5.html-3c38137f.js +++ b/assets/contemporary-college-english-5.html-a86ee847.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-89760306","path":"/english/contemporary-college-english-5.html","title":"现代大学英语精读(第2版)第五册","lang":"zh-CN","frontmatter":{"date":"2022-08-28T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-5.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第五册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-08-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第五册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-08-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"Who Are You and What Are You Doing Here","slug":"who-are-you-and-what-are-you-doing-here","link":"#who-are-you-and-what-are-you-doing-here","children":[]},{"level":2,"title":"Two Kinds","slug":"two-kinds","link":"#two-kinds","children":[]},{"level":2,"title":"Love is a Fallacy","slug":"love-is-a-fallacy","link":"#love-is-a-fallacy","children":[]},{"level":2,"title":"Rewriting American History","slug":"rewriting-american-history","link":"#rewriting-american-history","children":[]},{"level":2,"title":"Nobel Peace Price About Global Warming","slug":"nobel-peace-price-about-global-warming","link":"#nobel-peace-price-about-global-warming","children":[]},{"level":2,"title":"The Bluest Eyes","slug":"the-bluest-eyes","link":"#the-bluest-eyes","children":[]},{"level":2,"title":"How News Becomes Options and Opinions Off-Limits","slug":"how-news-becomes-options-and-opinions-off-limits","link":"#how-news-becomes-options-and-opinions-off-limits","children":[]},{"level":2,"title":"The Indispensable Opposition","slug":"the-indispensable-opposition","link":"#the-indispensable-opposition","children":[]},{"level":2,"title":"The Danger of a Single Story","slug":"the-danger-of-a-single-story","link":"#the-danger-of-a-single-story","children":[]},{"level":2,"title":"Come Rain or Come Shine","slug":"come-rain-or-come-shine","link":"#come-rain-or-come-shine","children":[]},{"level":2,"title":"Invisible Man","slug":"invisible-man","link":"#invisible-man","children":[]},{"level":2,"title":"You've Got to Find What You Love","slug":"you-ve-got-to-find-what-you-love","link":"#you-ve-got-to-find-what-you-love","children":[]},{"level":2,"title":"Where Do We Go from Here","slug":"where-do-we-go-from-here","link":"#where-do-we-go-from-here","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":7.18,"words":2155},"filePathRelative":"english/contemporary-college-english-5.md","localizedDate":"2022年8月28日","excerpt":"

现代大学英语精读(第2版)第五册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/49466046

\\n

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

\\n

Who Are You and What Are You Doing Here

","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-89760306","path":"/english/contemporary-college-english-5.html","title":"现代大学英语精读(第2版)第五册","lang":"zh-CN","frontmatter":{"date":"2022-08-28T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-5.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第五册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第五册 介绍 全书链接:https://www.ximalaya.com/album/49466046 本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。 Who Are You and What Are You Doing Here"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-08-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第五册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-08-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"介绍","slug":"介绍","link":"#介绍","children":[]},{"level":2,"title":"Who Are You and What Are You Doing Here","slug":"who-are-you-and-what-are-you-doing-here","link":"#who-are-you-and-what-are-you-doing-here","children":[]},{"level":2,"title":"Two Kinds","slug":"two-kinds","link":"#two-kinds","children":[]},{"level":2,"title":"Love is a Fallacy","slug":"love-is-a-fallacy","link":"#love-is-a-fallacy","children":[]},{"level":2,"title":"Rewriting American History","slug":"rewriting-american-history","link":"#rewriting-american-history","children":[]},{"level":2,"title":"Nobel Peace Price About Global Warming","slug":"nobel-peace-price-about-global-warming","link":"#nobel-peace-price-about-global-warming","children":[]},{"level":2,"title":"The Bluest Eyes","slug":"the-bluest-eyes","link":"#the-bluest-eyes","children":[]},{"level":2,"title":"How News Becomes Options and Opinions Off-Limits","slug":"how-news-becomes-options-and-opinions-off-limits","link":"#how-news-becomes-options-and-opinions-off-limits","children":[]},{"level":2,"title":"The Indispensable Opposition","slug":"the-indispensable-opposition","link":"#the-indispensable-opposition","children":[]},{"level":2,"title":"The Danger of a Single Story","slug":"the-danger-of-a-single-story","link":"#the-danger-of-a-single-story","children":[]},{"level":2,"title":"Come Rain or Come Shine","slug":"come-rain-or-come-shine","link":"#come-rain-or-come-shine","children":[]},{"level":2,"title":"Invisible Man","slug":"invisible-man","link":"#invisible-man","children":[]},{"level":2,"title":"You've Got to Find What You Love","slug":"you-ve-got-to-find-what-you-love","link":"#you-ve-got-to-find-what-you-love","children":[]},{"level":2,"title":"Where Do We Go from Here","slug":"where-do-we-go-from-here","link":"#where-do-we-go-from-here","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":7.18,"words":2155},"filePathRelative":"english/contemporary-college-english-5.md","localizedDate":"2022年8月28日","excerpt":"

现代大学英语精读(第2版)第五册

\\n

介绍

\\n

全书链接:https://www.ximalaya.com/album/49466046

\\n

本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

\\n

Who Are You and What Are You Doing Here

","autoDesc":true}`);export{e as data}; diff --git a/assets/contemporary-college-english-6.html-9f53636f.js b/assets/contemporary-college-english-6.html-1c25b40f.js similarity index 99% rename from assets/contemporary-college-english-6.html-9f53636f.js rename to assets/contemporary-college-english-6.html-1c25b40f.js index 84eeee6b..a559436e 100644 --- a/assets/contemporary-college-english-6.html-9f53636f.js +++ b/assets/contemporary-college-english-6.html-1c25b40f.js @@ -1 +1 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as h,a as e,b as t,d as a,e as n}from"./app-ee4d23bf.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第六册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第六册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第六册")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),u={href:"https://www.ximalaya.com/album/49468954",target:"_blank",rel:"noopener noreferrer"},p=e("p",null,"本册是整个系列的最后一册了,完结撒花🎉",-1),f=e("h2",{id:"paper-tigers",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#paper-tigers","aria-hidden":"true"},"#"),t(" Paper Tigers")],-1),m={href:"https://www.ximalaya.com/sound/414998175",target:"_blank",rel:"noopener noreferrer"},w=n("

探讨了 Asian American 的教育经历与社会成就不符的现象。

摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
Damn earnest, striving middle-class servility.

Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
Hyun, who wrote a book called Breaking the Bamboo Ceiling.

At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

",5),b=e("br",null,null,-1),y={href:"https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7",target:"_blank",rel:"noopener noreferrer"},g=e("h2",{id:"what-is-news",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#what-is-news","aria-hidden":"true"},"#"),t(" What Is News")],-1),_={href:"https://www.ximalaya.com/sound/414999846",target:"_blank",rel:"noopener noreferrer"},x=n('

提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

摘录如下: The news is made rather than gather.

We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
accept their notions.

“What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
“come-on" to keep the viewer's attention until the commercials come.

All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

At War with the Planet

',6),k={href:"https://www.ximalaya.com/sound/415001340",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。",-1),T=e("h2",{id:"how-to-get-the-poor-off-our-conscience",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-to-get-the-poor-off-our-conscience","aria-hidden":"true"},"#"),t(" How to Get the Poor off Our Conscience")],-1),A={href:"https://www.ximalaya.com/sound/415004615",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。",-1),C=e("h2",{id:"housewifely-arts",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#housewifely-arts","aria-hidden":"true"},"#"),t(" Housewifely Arts")],-1),I={href:"https://www.ximalaya.com/sound/415006607",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,"本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:",-1),B=e("ol",null,[e("li",null,"冷幽默,毕竟有几处地方让我笑了"),e("li",null,"引以为戒,千万别学女主")],-1),D=e("h2",{id:"the-one-against-the-many",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-one-against-the-many","aria-hidden":"true"},"#"),t(" The One Against The Many")],-1),N={href:"https://www.ximalaya.com/sound/415007539",target:"_blank",rel:"noopener noreferrer"},W=n('

本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
detailed, more comprehensive, more dogmatic.

An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
infallible priesthood.

The American tradition has found this view of human history repugnant and false, against the belief in the
all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
that all answers to political and social problems can be found in the back of some sacred book, against the
deterministic interpretation of history.

Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
certainty of absolute abuse.

The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
limitations of the human intellect and the infirmity of the human spirit.

Notes on the English Character

',8),j={href:"https://www.ximalaya.com/sound/415017440",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"本文对英格兰人的性格特征进行了简单的探讨。",-1),F=e("h2",{id:"the-death-of-a-pig",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-death-of-a-pig","aria-hidden":"true"},"#"),t(" The Death of a Pig")],-1),H={href:"https://www.ximalaya.com/sound/415019607",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。",-1),V=e("h2",{id:"don-t-eat-fortune-s-cookie",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#don-t-eat-fortune-s-cookie","aria-hidden":"true"},"#"),t(" Don't Eat Fortune's Cookie")],-1),M={href:"https://www.ximalaya.com/sound/415020743",target:"_blank",rel:"noopener noreferrer"},z=e("p",null,"Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。",-1),G=e("h2",{id:"the-accidental-universe",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-accidental-universe","aria-hidden":"true"},"#"),t(" The Accidental Universe")],-1),O={href:"https://www.ximalaya.com/sound/415022329",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。",-1),Y=e("h2",{id:"rowling-s-speech-at-harvard",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rowling-s-speech-at-harvard","aria-hidden":"true"},"#"),t(" Rowling's Speech at Harvard")],-1),J={href:"https://www.ximalaya.com/sound/415018537",target:"_blank",rel:"noopener noreferrer"},R=e("p",null,"罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。",-1);function U(K,Q){const o=r("ExternalLinkIcon");return s(),h("div",null,[d,c,e("p",null,[t("全书链接:"),e("a",u,[t("https://www.ximalaya.com/album/49468954"),a(o)])]),p,f,e("p",null,[t("原文链接:"),e("a",m,[t("https://www.ximalaya.com/sound/414998175"),a(o)])]),w,e("p",null,[t("注:Chua"),b,t(" 也就是《虎妈战歌》的作者:"),e("a",y,[t("https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7"),a(o)])]),g,e("p",null,[t("原文链接:"),e("a",_,[t("https://www.ximalaya.com/sound/414999846"),a(o)])]),x,e("p",null,[t("原文链接:"),e("a",k,[t("https://www.ximalaya.com/sound/415001340"),a(o)])]),v,T,e("p",null,[t("原文链接:"),e("a",A,[t("https://www.ximalaya.com/sound/415004615"),a(o)])]),E,C,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/415006607"),a(o)])]),q,B,D,e("p",null,[t("原文链接:"),e("a",N,[t("https://www.ximalaya.com/sound/415007539"),a(o)])]),W,e("p",null,[t("原文链接:"),e("a",j,[t("https://www.ximalaya.com/sound/415017440"),a(o)])]),P,F,e("p",null,[t("原文链接:"),e("a",H,[t("https://www.ximalaya.com/sound/415019607"),a(o)])]),L,V,e("p",null,[t("原文链接:"),e("a",M,[t("https://www.ximalaya.com/sound/415020743"),a(o)])]),z,G,e("p",null,[t("原文链接:"),e("a",O,[t("https://www.ximalaya.com/sound/415022329"),a(o)])]),S,Y,e("p",null,[t("原文链接:"),e("a",J,[t("https://www.ximalaya.com/sound/415018537"),a(o)])]),R])}const $=i(l,[["render",U],["__file","contemporary-college-english-6.html.vue"]]);export{$ as default}; +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as s,c as h,a as e,b as t,d as a,f as n}from"./app-dcf5468f.js";const l={},d=e("h1",{id:"现代大学英语精读-第2版-第六册",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#现代大学英语精读-第2版-第六册","aria-hidden":"true"},"#"),t(" 现代大学英语精读(第2版)第六册")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),u={href:"https://www.ximalaya.com/album/49468954",target:"_blank",rel:"noopener noreferrer"},p=e("p",null,"本册是整个系列的最后一册了,完结撒花🎉",-1),f=e("h2",{id:"paper-tigers",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#paper-tigers","aria-hidden":"true"},"#"),t(" Paper Tigers")],-1),m={href:"https://www.ximalaya.com/sound/414998175",target:"_blank",rel:"noopener noreferrer"},w=n("

探讨了 Asian American 的教育经历与社会成就不符的现象。

摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
Damn earnest, striving middle-class servility.

Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
Hyun, who wrote a book called Breaking the Bamboo Ceiling.

At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

",5),b=e("br",null,null,-1),y={href:"https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7",target:"_blank",rel:"noopener noreferrer"},g=e("h2",{id:"what-is-news",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#what-is-news","aria-hidden":"true"},"#"),t(" What Is News")],-1),_={href:"https://www.ximalaya.com/sound/414999846",target:"_blank",rel:"noopener noreferrer"},x=n('

提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

摘录如下: The news is made rather than gather.

We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
accept their notions.

“What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
“come-on" to keep the viewer's attention until the commercials come.

All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

At War with the Planet

',6),k={href:"https://www.ximalaya.com/sound/415001340",target:"_blank",rel:"noopener noreferrer"},v=e("p",null,"本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。",-1),T=e("h2",{id:"how-to-get-the-poor-off-our-conscience",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#how-to-get-the-poor-off-our-conscience","aria-hidden":"true"},"#"),t(" How to Get the Poor off Our Conscience")],-1),A={href:"https://www.ximalaya.com/sound/415004615",target:"_blank",rel:"noopener noreferrer"},E=e("p",null,"本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。",-1),C=e("h2",{id:"housewifely-arts",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#housewifely-arts","aria-hidden":"true"},"#"),t(" Housewifely Arts")],-1),I={href:"https://www.ximalaya.com/sound/415006607",target:"_blank",rel:"noopener noreferrer"},q=e("p",null,"本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:",-1),B=e("ol",null,[e("li",null,"冷幽默,毕竟有几处地方让我笑了"),e("li",null,"引以为戒,千万别学女主")],-1),D=e("h2",{id:"the-one-against-the-many",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-one-against-the-many","aria-hidden":"true"},"#"),t(" The One Against The Many")],-1),N={href:"https://www.ximalaya.com/sound/415007539",target:"_blank",rel:"noopener noreferrer"},W=n('

本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
detailed, more comprehensive, more dogmatic.

An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
infallible priesthood.

The American tradition has found this view of human history repugnant and false, against the belief in the
all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
that all answers to political and social problems can be found in the back of some sacred book, against the
deterministic interpretation of history.

Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
certainty of absolute abuse.

The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
limitations of the human intellect and the infirmity of the human spirit.

Notes on the English Character

',8),j={href:"https://www.ximalaya.com/sound/415017440",target:"_blank",rel:"noopener noreferrer"},P=e("p",null,"本文对英格兰人的性格特征进行了简单的探讨。",-1),F=e("h2",{id:"the-death-of-a-pig",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-death-of-a-pig","aria-hidden":"true"},"#"),t(" The Death of a Pig")],-1),H={href:"https://www.ximalaya.com/sound/415019607",target:"_blank",rel:"noopener noreferrer"},L=e("p",null,"本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。",-1),V=e("h2",{id:"don-t-eat-fortune-s-cookie",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#don-t-eat-fortune-s-cookie","aria-hidden":"true"},"#"),t(" Don't Eat Fortune's Cookie")],-1),M={href:"https://www.ximalaya.com/sound/415020743",target:"_blank",rel:"noopener noreferrer"},z=e("p",null,"Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。",-1),G=e("h2",{id:"the-accidental-universe",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#the-accidental-universe","aria-hidden":"true"},"#"),t(" The Accidental Universe")],-1),O={href:"https://www.ximalaya.com/sound/415022329",target:"_blank",rel:"noopener noreferrer"},S=e("p",null,"本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。",-1),Y=e("h2",{id:"rowling-s-speech-at-harvard",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#rowling-s-speech-at-harvard","aria-hidden":"true"},"#"),t(" Rowling's Speech at Harvard")],-1),J={href:"https://www.ximalaya.com/sound/415018537",target:"_blank",rel:"noopener noreferrer"},R=e("p",null,"罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。",-1);function U(K,Q){const o=r("ExternalLinkIcon");return s(),h("div",null,[d,c,e("p",null,[t("全书链接:"),e("a",u,[t("https://www.ximalaya.com/album/49468954"),a(o)])]),p,f,e("p",null,[t("原文链接:"),e("a",m,[t("https://www.ximalaya.com/sound/414998175"),a(o)])]),w,e("p",null,[t("注:Chua"),b,t(" 也就是《虎妈战歌》的作者:"),e("a",y,[t("https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7"),a(o)])]),g,e("p",null,[t("原文链接:"),e("a",_,[t("https://www.ximalaya.com/sound/414999846"),a(o)])]),x,e("p",null,[t("原文链接:"),e("a",k,[t("https://www.ximalaya.com/sound/415001340"),a(o)])]),v,T,e("p",null,[t("原文链接:"),e("a",A,[t("https://www.ximalaya.com/sound/415004615"),a(o)])]),E,C,e("p",null,[t("原文链接:"),e("a",I,[t("https://www.ximalaya.com/sound/415006607"),a(o)])]),q,B,D,e("p",null,[t("原文链接:"),e("a",N,[t("https://www.ximalaya.com/sound/415007539"),a(o)])]),W,e("p",null,[t("原文链接:"),e("a",j,[t("https://www.ximalaya.com/sound/415017440"),a(o)])]),P,F,e("p",null,[t("原文链接:"),e("a",H,[t("https://www.ximalaya.com/sound/415019607"),a(o)])]),L,V,e("p",null,[t("原文链接:"),e("a",M,[t("https://www.ximalaya.com/sound/415020743"),a(o)])]),z,G,e("p",null,[t("原文链接:"),e("a",O,[t("https://www.ximalaya.com/sound/415022329"),a(o)])]),S,Y,e("p",null,[t("原文链接:"),e("a",J,[t("https://www.ximalaya.com/sound/415018537"),a(o)])]),R])}const $=i(l,[["render",U],["__file","contemporary-college-english-6.html.vue"]]);export{$ as default}; diff --git a/assets/contemporary-college-english-6.html-73582c8d.js b/assets/contemporary-college-english-6.html-97ed8266.js similarity index 75% rename from assets/contemporary-college-english-6.html-73582c8d.js rename to assets/contemporary-college-english-6.html-97ed8266.js index 652836bb..4dabf13d 100644 --- a/assets/contemporary-college-english-6.html-73582c8d.js +++ b/assets/contemporary-college-english-6.html-97ed8266.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-860c51c8","path":"/english/contemporary-college-english-6.html","title":"现代大学英语精读(第2版)第六册","lang":"zh-CN","frontmatter":{"date":"2022-09-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-6.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第六册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第六册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"Paper Tigers","slug":"paper-tigers","link":"#paper-tigers","children":[]},{"level":2,"title":"What Is News","slug":"what-is-news","link":"#what-is-news","children":[]},{"level":2,"title":"At War with the Planet","slug":"at-war-with-the-planet","link":"#at-war-with-the-planet","children":[]},{"level":2,"title":"How to Get the Poor off Our Conscience","slug":"how-to-get-the-poor-off-our-conscience","link":"#how-to-get-the-poor-off-our-conscience","children":[]},{"level":2,"title":"Housewifely Arts","slug":"housewifely-arts","link":"#housewifely-arts","children":[]},{"level":2,"title":"The One Against The Many","slug":"the-one-against-the-many","link":"#the-one-against-the-many","children":[]},{"level":2,"title":"Notes on the English Character","slug":"notes-on-the-english-character","link":"#notes-on-the-english-character","children":[]},{"level":2,"title":"The Death of a Pig","slug":"the-death-of-a-pig","link":"#the-death-of-a-pig","children":[]},{"level":2,"title":"Don't Eat Fortune's Cookie","slug":"don-t-eat-fortune-s-cookie","link":"#don-t-eat-fortune-s-cookie","children":[]},{"level":2,"title":"The Accidental Universe","slug":"the-accidental-universe","link":"#the-accidental-universe","children":[]},{"level":2,"title":"Rowling's Speech at Harvard","slug":"rowling-s-speech-at-harvard","link":"#rowling-s-speech-at-harvard","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5.08,"words":1524},"filePathRelative":"english/contemporary-college-english-6.md","localizedDate":"2022年9月4日","excerpt":"

现代大学英语精读(第2版)第六册

\\n

前言

\\n

全书链接:https://www.ximalaya.com/album/49468954

\\n

本册是整个系列的最后一册了,完结撒花🎉

\\n

Paper Tigers

\\n

原文链接:https://www.ximalaya.com/sound/414998175

","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-860c51c8","path":"/english/contemporary-college-english-6.html","title":"现代大学英语精读(第2版)第六册","lang":"zh-CN","frontmatter":{"date":"2022-09-04T00:00:00.000Z","tag":"English","description":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/contemporary-college-english-6.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"现代大学英语精读(第2版)第六册"}],["meta",{"property":"og:description","content":"现代大学英语精读(第2版)第六册 前言 全书链接:https://www.ximalaya.com/album/49468954 本册是整个系列的最后一册了,完结撒花🎉 Paper Tigers 原文链接:https://www.ximalaya.com/sound/414998175"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"现代大学英语精读(第2版)第六册\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"Paper Tigers","slug":"paper-tigers","link":"#paper-tigers","children":[]},{"level":2,"title":"What Is News","slug":"what-is-news","link":"#what-is-news","children":[]},{"level":2,"title":"At War with the Planet","slug":"at-war-with-the-planet","link":"#at-war-with-the-planet","children":[]},{"level":2,"title":"How to Get the Poor off Our Conscience","slug":"how-to-get-the-poor-off-our-conscience","link":"#how-to-get-the-poor-off-our-conscience","children":[]},{"level":2,"title":"Housewifely Arts","slug":"housewifely-arts","link":"#housewifely-arts","children":[]},{"level":2,"title":"The One Against The Many","slug":"the-one-against-the-many","link":"#the-one-against-the-many","children":[]},{"level":2,"title":"Notes on the English Character","slug":"notes-on-the-english-character","link":"#notes-on-the-english-character","children":[]},{"level":2,"title":"The Death of a Pig","slug":"the-death-of-a-pig","link":"#the-death-of-a-pig","children":[]},{"level":2,"title":"Don't Eat Fortune's Cookie","slug":"don-t-eat-fortune-s-cookie","link":"#don-t-eat-fortune-s-cookie","children":[]},{"level":2,"title":"The Accidental Universe","slug":"the-accidental-universe","link":"#the-accidental-universe","children":[]},{"level":2,"title":"Rowling's Speech at Harvard","slug":"rowling-s-speech-at-harvard","link":"#rowling-s-speech-at-harvard","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.08,"words":1524},"filePathRelative":"english/contemporary-college-english-6.md","localizedDate":"2022年9月4日","excerpt":"

现代大学英语精读(第2版)第六册

\\n

前言

\\n

全书链接:https://www.ximalaya.com/album/49468954

\\n

本册是整个系列的最后一册了,完结撒花🎉

\\n

Paper Tigers

\\n

原文链接:https://www.ximalaya.com/sound/414998175

","autoDesc":true}`);export{e as data}; diff --git a/assets/copy-code-may-not-be-guilty.html-8aca485c.js b/assets/copy-code-may-not-be-guilty.html-8aca485c.js new file mode 100644 index 00000000..6f14d90a --- /dev/null +++ b/assets/copy-code-may-not-be-guilty.html-8aca485c.js @@ -0,0 +1 @@ +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as r,e as i,f as e}from"./app-dcf5468f.js";const o={},m=e('

复制代码也许不是罪

前言

熟悉我的人都知道,我对代码是有追求的。

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

我早期认为:复制代码就是菜。

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

',8),c=e('

正文

我总结了一下,主要有以下原因:
1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

',5);function d(n,h){return a(),r("div",null,[m,i(" more "),c])}const l=t(o,[["render",d],["__file","copy-code-may-not-be-guilty.html.vue"]]);export{l as default}; diff --git a/assets/copy-code-may-not-be-guilty.html-e731a413.js b/assets/copy-code-may-not-be-guilty.html-e731a413.js deleted file mode 100644 index 539744df..00000000 --- a/assets/copy-code-may-not-be-guilty.html-e731a413.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as r,f as i,e}from"./app-ee4d23bf.js";const o={},m=e('

复制代码也许不是罪

前言

熟悉我的人都知道,我对代码是有追求的。

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

我早期认为:复制代码就是菜。

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

',8),c=e('

正文

我总结了一下,主要有以下原因:
1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

',5);function d(n,h){return a(),r("div",null,[m,i(" more "),c])}const l=t(o,[["render",d],["__file","copy-code-may-not-be-guilty.html.vue"]]);export{l as default}; diff --git a/assets/copy-code-may-not-be-guilty.html-a0687c59.js b/assets/copy-code-may-not-be-guilty.html-f5f78127.js similarity index 65% rename from assets/copy-code-may-not-be-guilty.html-a0687c59.js rename to assets/copy-code-may-not-be-guilty.html-f5f78127.js index bf4c8d23..7800aa6d 100644 --- a/assets/copy-code-may-not-be-guilty.html-a0687c59.js +++ b/assets/copy-code-may-not-be-guilty.html-f5f78127.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-76f251d2","path":"/daily/copy-code-may-not-be-guilty.html","title":"复制代码也许不是罪","lang":"zh-CN","frontmatter":{"date":"2023-09-24T00:00:00.000Z","tag":["Daily"],"description":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/copy-code-may-not-be-guilty.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"复制代码也许不是罪"}],["meta",{"property":"og:description","content":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"复制代码也许不是罪\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-24T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.51,"words":453},"filePathRelative":"daily/copy-code-may-not-be-guilty.md","localizedDate":"2023年9月24日","excerpt":"

复制代码也许不是罪

\\n

前言

\\n

熟悉我的人都知道,我对代码是有追求的。

\\n

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

\\n

我早期认为:复制代码就是菜。

\\n

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

\\n

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

\\n

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-76f251d2","path":"/daily/copy-code-may-not-be-guilty.html","title":"复制代码也许不是罪","lang":"zh-CN","frontmatter":{"date":"2023-09-24T00:00:00.000Z","tag":["Daily"],"description":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/copy-code-may-not-be-guilty.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"复制代码也许不是罪"}],["meta",{"property":"og:description","content":"复制代码也许不是罪 前言 熟悉我的人都知道,我对代码是有追求的。 正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。 我早期认为:复制代码就是菜。 后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。 而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。 编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-24T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"复制代码也许不是罪\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-24T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.51,"words":453},"filePathRelative":"daily/copy-code-may-not-be-guilty.md","localizedDate":"2023年9月24日","excerpt":"

复制代码也许不是罪

\\n

前言

\\n

熟悉我的人都知道,我对代码是有追求的。

\\n

正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

\\n

我早期认为:复制代码就是菜。

\\n

后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

\\n

而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

\\n

编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/docker-build-and-push-script.html-be9258e7.js b/assets/docker-build-and-push-script.html-3e6607f0.js similarity index 50% rename from assets/docker-build-and-push-script.html-be9258e7.js rename to assets/docker-build-and-push-script.html-3e6607f0.js index 52077aed..61b7190c 100644 --- a/assets/docker-build-and-push-script.html-be9258e7.js +++ b/assets/docker-build-and-push-script.html-3e6607f0.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-2e81d6a4","path":"/devops/docker-build-and-push-script.html","title":"Docker 构建镜像、推送、启动实用脚本","lang":"zh-CN","frontmatter":{"date":"2023-12-08T00:00:00.000Z","tag":["DevOps","Linux"],"description":"Docker 构建镜像、推送、启动实用脚本 misc 存储多份 docker 认证信息: mkdir \\"~/.project1\\" mkdir \\"~/.project2\\" docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token> docker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token>","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/docker-build-and-push-script.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Docker 构建镜像、推送、启动实用脚本"}],["meta",{"property":"og:description","content":"Docker 构建镜像、推送、启动实用脚本 misc 存储多份 docker 认证信息: mkdir \\"~/.project1\\" mkdir \\"~/.project2\\" docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token> docker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token>"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:published_time","content":"2023-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Docker 构建镜像、推送、启动实用脚本\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"misc","slug":"misc","link":"#misc","children":[]},{"level":2,"title":"get-version.sh","slug":"get-version-sh","link":"#get-version-sh","children":[]},{"level":2,"title":"build-image.sh","slug":"build-image-sh","link":"#build-image-sh","children":[]},{"level":2,"title":"startup.sh","slug":"startup-sh","link":"#startup-sh","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.34,"words":403},"filePathRelative":"devops/docker-build-and-push-script.md","localizedDate":"2023年12月8日","excerpt":"

Docker 构建镜像、推送、启动实用脚本

\\n

misc

\\n

存储多份 docker 认证信息:

\\n
mkdir \\"~/.project1\\"\\nmkdir \\"~/.project2\\"\\n\\ndocker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token>\\ndocker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token> \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-2e81d6a4","path":"/devops/docker-build-and-push-script.html","title":"Docker 构建镜像、推送、启动实用脚本","lang":"zh-CN","frontmatter":{"date":"2023-12-08T00:00:00.000Z","tag":["DevOps","Linux"],"description":"Docker 构建镜像、推送、启动实用脚本 misc 存储多份 docker 认证信息: mkdir \\"~/.project1\\" mkdir \\"~/.project2\\" docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token> docker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token>","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/docker-build-and-push-script.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Docker 构建镜像、推送、启动实用脚本"}],["meta",{"property":"og:description","content":"Docker 构建镜像、推送、启动实用脚本 misc 存储多份 docker 认证信息: mkdir \\"~/.project1\\" mkdir \\"~/.project2\\" docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token> docker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token>"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:published_time","content":"2023-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Docker 构建镜像、推送、启动实用脚本\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"misc","slug":"misc","link":"#misc","children":[]},{"level":2,"title":"get-version.sh","slug":"get-version-sh","link":"#get-version-sh","children":[]},{"level":2,"title":"build-image.sh","slug":"build-image-sh","link":"#build-image-sh","children":[]},{"level":2,"title":"startup.sh","slug":"startup-sh","link":"#startup-sh","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.34,"words":403},"filePathRelative":"devops/docker-build-and-push-script.md","localizedDate":"2023年12月8日","excerpt":"

Docker 构建镜像、推送、启动实用脚本

\\n

misc

\\n

存储多份 docker 认证信息:

\\n
mkdir \\"~/.project1\\"\\nmkdir \\"~/.project2\\"\\n\\ndocker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token>\\ndocker --config ~/.project2 login registry.example.com -u <username> -p <deploy_token> \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/docker-build-and-push-script.html-253684f7.js b/assets/docker-build-and-push-script.html-634e2d74.js similarity index 99% rename from assets/docker-build-and-push-script.html-253684f7.js rename to assets/docker-build-and-push-script.html-634e2d74.js index 242e5180..b9535d45 100644 --- a/assets/docker-build-and-push-script.html-253684f7.js +++ b/assets/docker-build-and-push-script.html-634e2d74.js @@ -1,4 +1,4 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as p,c as o,a as s,b as a,d as l,e as n}from"./app-ee4d23bf.js";const c={},r=n(`

Docker 构建镜像、推送、启动实用脚本

misc

存储多份 docker 认证信息:

mkdir "~/.project1"
+import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as p,c as o,a as s,b as a,d as l,f as n}from"./app-dcf5468f.js";const c={},r=n(`

Docker 构建镜像、推送、启动实用脚本

misc

存储多份 docker 认证信息:

mkdir "~/.project1"
 mkdir "~/.project2"
 
 docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token>
diff --git a/assets/dont-try-to-argue-with-a-sb.html-37ff3a9d.js b/assets/dont-try-to-argue-with-a-sb.html-7b0b7267.js
similarity index 65%
rename from assets/dont-try-to-argue-with-a-sb.html-37ff3a9d.js
rename to assets/dont-try-to-argue-with-a-sb.html-7b0b7267.js
index 8b977a70..a8b6c839 100644
--- a/assets/dont-try-to-argue-with-a-sb.html-37ff3a9d.js
+++ b/assets/dont-try-to-argue-with-a-sb.html-7b0b7267.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-f47f129e","path":"/daily/dont-try-to-argue-with-a-sb.html","title":"不要与傻逼进行争吵","lang":"zh-CN","frontmatter":{"date":"2023-08-04T00:00:00.000Z","tag":["Daily","Emotion","Working Experience","Video"],"description":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/dont-try-to-argue-with-a-sb.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"不要与傻逼进行争吵"}],["meta",{"property":"og:description","content":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Emotion"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"不要与傻逼进行争吵\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.92,"words":1176},"filePathRelative":"daily/dont-try-to-argue-with-a-sb.md","localizedDate":"2023年8月4日","excerpt":"

不要与傻逼进行争吵

\\n

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

\\n

我在沟通上,有以下可以改正的点:
\\n0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

\\n
    \\n
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. \\n
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. \\n
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. \\n
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. \\n
\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-f47f129e","path":"/daily/dont-try-to-argue-with-a-sb.html","title":"不要与傻逼进行争吵","lang":"zh-CN","frontmatter":{"date":"2023-08-04T00:00:00.000Z","tag":["Daily","Emotion","Working Experience","Video"],"description":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/dont-try-to-argue-with-a-sb.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"不要与傻逼进行争吵"}],["meta",{"property":"og:description","content":"不要与傻逼进行争吵 这是个职场吐槽帖,也是个自我反思、情绪管理帖。 我在沟通上,有以下可以改正的点: 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Emotion"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-04T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"不要与傻逼进行争吵\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-04T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.92,"words":1176},"filePathRelative":"daily/dont-try-to-argue-with-a-sb.md","localizedDate":"2023年8月4日","excerpt":"

不要与傻逼进行争吵

\\n

这是个职场吐槽帖,也是个自我反思、情绪管理帖。

\\n

我在沟通上,有以下可以改正的点:
\\n0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

\\n
    \\n
  1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
  2. \\n
  3. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
  4. \\n
  5. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
  6. \\n
  7. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!
  8. \\n
\\n","autoDesc":true}');export{t as data}; diff --git a/assets/dont-try-to-argue-with-a-sb.html-ba82f07c.js b/assets/dont-try-to-argue-with-a-sb.html-c362fa80.js similarity index 97% rename from assets/dont-try-to-argue-with-a-sb.html-ba82f07c.js rename to assets/dont-try-to-argue-with-a-sb.html-c362fa80.js index f8e04537..c78450eb 100644 --- a/assets/dont-try-to-argue-with-a-sb.html-ba82f07c.js +++ b/assets/dont-try-to-argue-with-a-sb.html-c362fa80.js @@ -1 +1 @@ -import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as n,f as a,d as s,a as e,b as t,e as i}from"./app-ee4d23bf.js";const c={},d=e("h1",{id:"不要与傻逼进行争吵",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#不要与傻逼进行争吵","aria-hidden":"true"},"#"),t(" 不要与傻逼进行争吵")],-1),_=e("p",null,"这是个职场吐槽帖,也是个自我反思、情绪管理帖。",-1),h=e("p",null,[t("我在沟通上,有以下可以改正的点:"),e("br"),t(" 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你")],-1),m=e("ol",null,[e("li",null,"我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你"),e("li",null,"你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通"),e("li",null,"你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答"),e("li",null,"最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!")],-1),u=i("

事情是这样的。

我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

",18);function f(b,B){const o=r("BiliBili");return l(),n("div",null,[d,_,h,m,a(" more "),u,s(o,{bvid:"BV1Yh4y1r7k8"})])}const x=p(c,[["render",f],["__file","dont-try-to-argue-with-a-sb.html.vue"]]);export{x as default}; +import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as n,e as a,d as s,a as e,b as t,f as i}from"./app-dcf5468f.js";const c={},d=e("h1",{id:"不要与傻逼进行争吵",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#不要与傻逼进行争吵","aria-hidden":"true"},"#"),t(" 不要与傻逼进行争吵")],-1),_=e("p",null,"这是个职场吐槽帖,也是个自我反思、情绪管理帖。",-1),h=e("p",null,[t("我在沟通上,有以下可以改正的点:"),e("br"),t(" 0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你")],-1),m=e("ol",null,[e("li",null,"我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你"),e("li",null,"你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通"),e("li",null,"你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答"),e("li",null,"最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!")],-1),u=i("

事情是这样的。

我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

",18);function f(b,B){const o=r("BiliBili");return l(),n("div",null,[d,_,h,m,a(" more "),u,s(o,{bvid:"BV1Yh4y1r7k8"})])}const x=p(c,[["render",f],["__file","dont-try-to-argue-with-a-sb.html.vue"]]);export{x as default}; diff --git a/assets/evaluate-llm-app-with-ragas.html-04648c99.js b/assets/evaluate-llm-app-with-ragas.html-04648c99.js deleted file mode 100644 index 4abfc4df..00000000 --- a/assets/evaluate-llm-app-with-ragas.html-04648c99.js +++ /dev/null @@ -1 +0,0 @@ -const e=JSON.parse('{"key":"v-39c09a30","path":"/python/evaluate-llm-app-with-ragas.html","title":"使用Ragas评估LLM应用","lang":"zh-CN","frontmatter":{"date":"2024-04-03T00:00:00.000Z","tag":["Python","llm"],"description":"使用Ragas评估LLM应用 说明 对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。 注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。 安装 pip install ragas","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/evaluate-llm-app-with-ragas.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用Ragas评估LLM应用"}],["meta",{"property":"og:description","content":"使用Ragas评估LLM应用 说明 对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。 注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。 安装 pip install ragas"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"llm"}],["meta",{"property":"article:published_time","content":"2024-04-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用Ragas评估LLM应用\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2024-04-03T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"数据说明","slug":"数据说明","link":"#数据说明","children":[]},{"level":2,"title":"正确性❌","slug":"正确性❌","link":"#正确性❌","children":[]},{"level":2,"title":"忠实度✔","slug":"忠实度✔","link":"#忠实度✔","children":[]},{"level":2,"title":"实战演示","slug":"实战演示","link":"#实战演示","children":[{"level":3,"title":"准备好样例问题","slug":"准备好样例问题","link":"#准备好样例问题","children":[]},{"level":3,"title":"准备好正确答案","slug":"准备好正确答案","link":"#准备好正确答案","children":[]},{"level":3,"title":"编写回答函数","slug":"编写回答函数","link":"#编写回答函数","children":[]},{"level":3,"title":"进行答案评估","slug":"进行答案评估","link":"#进行答案评估","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.71,"words":813},"filePathRelative":"python/evaluate-llm-app-with-ragas.md","localizedDate":"2024年4月3日","excerpt":"

使用Ragas评估LLM应用

\\n

说明

\\n

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

\\n

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

\\n

安装

\\n
pip install ragas\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/evaluate-llm-app-with-ragas.html-e7eee6ad.js b/assets/evaluate-llm-app-with-ragas.html-45c81a74.js similarity index 99% rename from assets/evaluate-llm-app-with-ragas.html-e7eee6ad.js rename to assets/evaluate-llm-app-with-ragas.html-45c81a74.js index 2b25e7c0..7eafe2f6 100644 --- a/assets/evaluate-llm-app-with-ragas.html-e7eee6ad.js +++ b/assets/evaluate-llm-app-with-ragas.html-45c81a74.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,e as t}from"./app-ee4d23bf.js";const e={},p=t(`

使用Ragas评估LLM应用

说明

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

安装

pip install ragas
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-dcf5468f.js";const e={},p=t(`

使用Ragas评估LLM应用

说明

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

安装

pip install ragas
 

数据说明

对以下数据进行评估。

事实:Einstein was born in 1879 in Germany.
提问:

  1. When did Einstein born?
  2. Where did Einstein born?

正确答案:

  1. Einstein was born in 1879.
  2. Einstein was born in Germany.

正确性❌

from dotenv import load_dotenv
 load_dotenv()
 
diff --git a/assets/evaluate-llm-app-with-ragas.html-9ec1f483.js b/assets/evaluate-llm-app-with-ragas.html-9ec1f483.js
new file mode 100644
index 00000000..10269379
--- /dev/null
+++ b/assets/evaluate-llm-app-with-ragas.html-9ec1f483.js
@@ -0,0 +1 @@
+const e=JSON.parse('{"key":"v-8903fc5e","path":"/llm/evaluate-llm-app-with-ragas.html","title":"使用Ragas评估LLM应用","lang":"zh-CN","frontmatter":{"date":"2024-04-03T00:00:00.000Z","tag":["Python","llm"],"description":"使用Ragas评估LLM应用 说明 对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。 注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。 安装 pip install ragas","head":[["meta",{"property":"og:url","content":"https://levy.vip/llm/evaluate-llm-app-with-ragas.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用Ragas评估LLM应用"}],["meta",{"property":"og:description","content":"使用Ragas评估LLM应用 说明 对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。 注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。 安装 pip install ragas"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"llm"}],["meta",{"property":"article:published_time","content":"2024-04-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用Ragas评估LLM应用\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2024-04-03T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"数据说明","slug":"数据说明","link":"#数据说明","children":[]},{"level":2,"title":"正确性❌","slug":"正确性❌","link":"#正确性❌","children":[]},{"level":2,"title":"忠实度✔","slug":"忠实度✔","link":"#忠实度✔","children":[]},{"level":2,"title":"实战演示","slug":"实战演示","link":"#实战演示","children":[{"level":3,"title":"准备好样例问题","slug":"准备好样例问题","link":"#准备好样例问题","children":[]},{"level":3,"title":"准备好正确答案","slug":"准备好正确答案","link":"#准备好正确答案","children":[]},{"level":3,"title":"编写回答函数","slug":"编写回答函数","link":"#编写回答函数","children":[]},{"level":3,"title":"进行答案评估","slug":"进行答案评估","link":"#进行答案评估","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.71,"words":813},"filePathRelative":"llm/evaluate-llm-app-with-ragas.md","localizedDate":"2024年4月3日","excerpt":"

使用Ragas评估LLM应用

\\n

说明

\\n

对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

\\n

注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

\\n

安装

\\n
pip install ragas\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-1-overview.html-5218993d.js b/assets/everyone-can-learn-english-1-overview.html-38f7a62a.js similarity index 68% rename from assets/everyone-can-learn-english-1-overview.html-5218993d.js rename to assets/everyone-can-learn-english-1-overview.html-38f7a62a.js index 890fd8bd..943a9442 100644 --- a/assets/everyone-can-learn-english-1-overview.html-5218993d.js +++ b/assets/everyone-can-learn-english-1-overview.html-38f7a62a.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-246f4f17","path":"/english/everyone-can-learn-english-1-overview.html","title":"人人都能学会的英语1:开篇","lang":"zh-CN","frontmatter":{"date":"2022-06-23T00:00:00.000Z","tag":"English","description":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-1-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语1:开篇"}],["meta",{"property":"og:description","content":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语1:开篇\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-23T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么学","slug":"为什么学","link":"#为什么学","children":[]},{"level":2,"title":"怎么学","slug":"怎么学","link":"#怎么学","children":[{"level":3,"title":"建设心态","slug":"建设心态","link":"#建设心态","children":[]},{"level":3,"title":"提升认知","slug":"提升认知","link":"#提升认知","children":[]},{"level":3,"title":"明确意义","slug":"明确意义","link":"#明确意义","children":[]},{"level":3,"title":"制定计划","slug":"制定计划","link":"#制定计划","children":[]},{"level":3,"title":"培养习惯","slug":"培养习惯","link":"#培养习惯","children":[]},{"level":3,"title":"注重方法","slug":"注重方法","link":"#注重方法","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4.46,"words":1337},"filePathRelative":"english/everyone-can-learn-english-1-overview.md","localizedDate":"2022年6月23日","excerpt":"

人人都能学会的英语1:开篇

\\n

为什么学

\\n

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

\\n

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

\\n
    \\n
  • 出国
  • \\n
  • 去外企
  • \\n
  • 有国际化需求
  • \\n
  • 学习专业领域的前沿知识
  • \\n
\\n

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-246f4f17","path":"/english/everyone-can-learn-english-1-overview.html","title":"人人都能学会的英语1:开篇","lang":"zh-CN","frontmatter":{"date":"2022-06-23T00:00:00.000Z","tag":"English","description":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-1-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语1:开篇"}],["meta",{"property":"og:description","content":"人人都能学会的英语1:开篇 为什么学 不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。 也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语: 出国 去外企 有国际化需求 学习专业领域的前沿知识 我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语1:开篇\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-23T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么学","slug":"为什么学","link":"#为什么学","children":[]},{"level":2,"title":"怎么学","slug":"怎么学","link":"#怎么学","children":[{"level":3,"title":"建设心态","slug":"建设心态","link":"#建设心态","children":[]},{"level":3,"title":"提升认知","slug":"提升认知","link":"#提升认知","children":[]},{"level":3,"title":"明确意义","slug":"明确意义","link":"#明确意义","children":[]},{"level":3,"title":"制定计划","slug":"制定计划","link":"#制定计划","children":[]},{"level":3,"title":"培养习惯","slug":"培养习惯","link":"#培养习惯","children":[]},{"level":3,"title":"注重方法","slug":"注重方法","link":"#注重方法","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.46,"words":1337},"filePathRelative":"english/everyone-can-learn-english-1-overview.md","localizedDate":"2022年6月23日","excerpt":"

人人都能学会的英语1:开篇

\\n

为什么学

\\n

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

\\n

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

\\n
    \\n
  • 出国
  • \\n
  • 去外企
  • \\n
  • 有国际化需求
  • \\n
  • 学习专业领域的前沿知识
  • \\n
\\n

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-1-overview.html-9e2bcd46.js b/assets/everyone-can-learn-english-1-overview.html-44c5fd6a.js similarity index 98% rename from assets/everyone-can-learn-english-1-overview.html-9e2bcd46.js rename to assets/everyone-can-learn-english-1-overview.html-44c5fd6a.js index 3023d122..d0d40f6c 100644 --- a/assets/everyone-can-learn-english-1-overview.html-9e2bcd46.js +++ b/assets/everyone-can-learn-english-1-overview.html-44c5fd6a.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as i,e as r}from"./app-ee4d23bf.js";const h={},l=r('

人人都能学会的英语1:开篇

为什么学

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

  • 出国
  • 去外企
  • 有国际化需求
  • 学习专业领域的前沿知识

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

怎么学

关于怎么学的问题,我认为需要内外兼修。

建设心态

在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

提升认知

一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

明确意义

需要问一下自己,英语对自己而言到底是什么?

是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

上面只是举例,并且例子之间并不冲突,是可以兼容的。

总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

制定计划

整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

培养习惯

注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

注重方法

前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

至于学习方法,因学习内容而异。按照知识体系划可大致分为:

  • 基础知识:
    • 音标
    • 单词
    • 语法
  • 综合能力
    • 听说
    • 读写

基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

  • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
  • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

',38),p=[l];function d(n,t){return a(),i("div",null,p)}const o=e(h,[["render",d],["__file","everyone-can-learn-english-1-overview.html.vue"]]);export{o as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as i,f as r}from"./app-dcf5468f.js";const h={},l=r('

人人都能学会的英语1:开篇

为什么学

不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

  • 出国
  • 去外企
  • 有国际化需求
  • 学习专业领域的前沿知识

我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

怎么学

关于怎么学的问题,我认为需要内外兼修。

建设心态

在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

提升认知

一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

明确意义

需要问一下自己,英语对自己而言到底是什么?

是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

上面只是举例,并且例子之间并不冲突,是可以兼容的。

总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

制定计划

整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

培养习惯

注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

注重方法

前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

至于学习方法,因学习内容而异。按照知识体系划可大致分为:

  • 基础知识:
    • 音标
    • 单词
    • 语法
  • 综合能力
    • 听说
    • 读写

基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

  • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
  • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

',38),p=[l];function d(n,t){return a(),i("div",null,p)}const o=e(h,[["render",d],["__file","everyone-can-learn-english-1-overview.html.vue"]]);export{o as default}; diff --git a/assets/everyone-can-learn-english-2-pronunciation.html-7fdca85a.js b/assets/everyone-can-learn-english-2-pronunciation.html-27d767db.js similarity index 97% rename from assets/everyone-can-learn-english-2-pronunciation.html-7fdca85a.js rename to assets/everyone-can-learn-english-2-pronunciation.html-27d767db.js index 5605bde4..d33dbf6d 100644 --- a/assets/everyone-can-learn-english-2-pronunciation.html-7fdca85a.js +++ b/assets/everyone-can-learn-english-2-pronunciation.html-27d767db.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as l,c as h,a as e,b as a,d as r,e as i}from"./app-ee4d23bf.js";const c={},s=i('

人人都能学会的英语2:音标

方法

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

我推荐根据赖世雄的《美语音标》进行学习:

  • 在微信公众号 常春藤英语集团 买相关书籍
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

我给出了我之前的学习进度记录,仅供参考
image.png

',7),p={href:"https://www.bilibili.com/video/BV1qE411v7oE?vd_source=046b87a1b6f0cc325266cccbb4f307fa",target:"_blank",rel:"noopener noreferrer"},d=e("br",null,null,-1),_=e("p",null,"再给出关于音标的一些资料:",-1),u={href:"http://yinbiao.tingclass.net/show-16-9-1.html",target:"_blank",rel:"noopener noreferrer"},f={href:"https://new.qq.com/omn/20190106/20190106G062W8.html",target:"_blank",rel:"noopener noreferrer"},m=i('

讨论

这里我补充几点,虽然不是核心,却与主题息息相关:

  1. 是否要买实体书?
  2. 为什么是美语,而不是“英语”
  3. 关于口音(accent)

建议买实体书

现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

  • 捐书
  • 送人

美语是主流

也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

不要纠结口音

在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

',17);function b(g,x){const n=t("ExternalLinkIcon");return l(),h("div",null,[s,e("p",null,[a("学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:"),e("a",p,[a("https://www.bilibili.com/video/BV1qE411v7oE"),r(n)]),d,a(" (我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)")]),_,e("ul",null,[e("li",null,[e("a",u,[a("音标发音大全 48个英语音标表(IPA,DJ音标,KK音标对照表)"),r(n)])]),e("li",null,[e("a",f,[a("一文理解IPA音标、DJ音标、KK音标的区别"),r(n)])])]),m])}const k=o(c,[["render",b],["__file","everyone-can-learn-english-2-pronunciation.html.vue"]]);export{k as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as l,c as h,a as e,b as a,d as r,f as i}from"./app-dcf5468f.js";const c={},s=i('

人人都能学会的英语2:音标

方法

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

我推荐根据赖世雄的《美语音标》进行学习:

  • 在微信公众号 常春藤英语集团 买相关书籍
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

我给出了我之前的学习进度记录,仅供参考
image.png

',7),p={href:"https://www.bilibili.com/video/BV1qE411v7oE?vd_source=046b87a1b6f0cc325266cccbb4f307fa",target:"_blank",rel:"noopener noreferrer"},d=e("br",null,null,-1),_=e("p",null,"再给出关于音标的一些资料:",-1),u={href:"http://yinbiao.tingclass.net/show-16-9-1.html",target:"_blank",rel:"noopener noreferrer"},f={href:"https://new.qq.com/omn/20190106/20190106G062W8.html",target:"_blank",rel:"noopener noreferrer"},m=i('

讨论

这里我补充几点,虽然不是核心,却与主题息息相关:

  1. 是否要买实体书?
  2. 为什么是美语,而不是“英语”
  3. 关于口音(accent)

建议买实体书

现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

  • 捐书
  • 送人

美语是主流

也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

不要纠结口音

在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

',17);function b(g,x){const n=t("ExternalLinkIcon");return l(),h("div",null,[s,e("p",null,[a("学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:"),e("a",p,[a("https://www.bilibili.com/video/BV1qE411v7oE"),r(n)]),d,a(" (我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)")]),_,e("ul",null,[e("li",null,[e("a",u,[a("音标发音大全 48个英语音标表(IPA,DJ音标,KK音标对照表)"),r(n)])]),e("li",null,[e("a",f,[a("一文理解IPA音标、DJ音标、KK音标的区别"),r(n)])])]),m])}const k=o(c,[["render",b],["__file","everyone-can-learn-english-2-pronunciation.html.vue"]]);export{k as default}; diff --git a/assets/everyone-can-learn-english-2-pronunciation.html-338d9049.js b/assets/everyone-can-learn-english-2-pronunciation.html-7684ead6.js similarity index 66% rename from assets/everyone-can-learn-english-2-pronunciation.html-338d9049.js rename to assets/everyone-can-learn-english-2-pronunciation.html-7684ead6.js index ca49e527..da7b2398 100644 --- a/assets/everyone-can-learn-english-2-pronunciation.html-338d9049.js +++ b/assets/everyone-can-learn-english-2-pronunciation.html-7684ead6.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-3ac0474c","path":"/english/everyone-can-learn-english-2-pronunciation.html","title":"人人都能学会的英语2:音标","lang":"zh-CN","frontmatter":{"date":"2022-06-26T00:00:00.000Z","tag":"English","description":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-2-pronunciation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语2:音标"}],["meta",{"property":"og:description","content":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语2:音标\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-26T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"讨论","slug":"讨论","link":"#讨论","children":[{"level":3,"title":"建议买实体书","slug":"建议买实体书","link":"#建议买实体书","children":[]},{"level":3,"title":"美语是主流","slug":"美语是主流","link":"#美语是主流","children":[]},{"level":3,"title":"不要纠结口音","slug":"不要纠结口音","link":"#不要纠结口音","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.61,"words":1083},"filePathRelative":"english/everyone-can-learn-english-2-pronunciation.md","localizedDate":"2022年6月26日","excerpt":"

人人都能学会的英语2:音标

\\n

方法

\\n

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

\\n

我推荐根据赖世雄的《美语音标》进行学习:

\\n
    \\n
  • 在微信公众号 常春藤英语集团 买相关书籍
  • \\n
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • \\n
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • \\n
\\n

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-3ac0474c","path":"/english/everyone-can-learn-english-2-pronunciation.html","title":"人人都能学会的英语2:音标","lang":"zh-CN","frontmatter":{"date":"2022-06-26T00:00:00.000Z","tag":"English","description":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-2-pronunciation.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语2:音标"}],["meta",{"property":"og:description","content":"人人都能学会的英语2:音标 方法 音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。 我推荐根据赖世雄的《美语音标》进行学习: 在微信公众号 常春藤英语集团 买相关书籍 在 喜马拉雅 或 B站 听相关音频,进行跟读练习 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语2:音标\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-26T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"讨论","slug":"讨论","link":"#讨论","children":[{"level":3,"title":"建议买实体书","slug":"建议买实体书","link":"#建议买实体书","children":[]},{"level":3,"title":"美语是主流","slug":"美语是主流","link":"#美语是主流","children":[]},{"level":3,"title":"不要纠结口音","slug":"不要纠结口音","link":"#不要纠结口音","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.61,"words":1083},"filePathRelative":"english/everyone-can-learn-english-2-pronunciation.md","localizedDate":"2022年6月26日","excerpt":"

人人都能学会的英语2:音标

\\n

方法

\\n

音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

\\n

我推荐根据赖世雄的《美语音标》进行学习:

\\n
    \\n
  • 在微信公众号 常春藤英语集团 买相关书籍
  • \\n
  • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
  • \\n
  • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
  • \\n
\\n

有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-3-words.html-f6646acd.js b/assets/everyone-can-learn-english-3-words.html-25949b28.js similarity index 95% rename from assets/everyone-can-learn-english-3-words.html-f6646acd.js rename to assets/everyone-can-learn-english-3-words.html-25949b28.js index f9fadcb1..c6792377 100644 --- a/assets/everyone-can-learn-english-3-words.html-f6646acd.js +++ b/assets/everyone-can-learn-english-3-words.html-25949b28.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as s,c as p,a as r,b as e,d as t,w as c,e as a}from"./app-ee4d23bf.js";const h={},d=a('

人人都能学会的英语3:单词

方法

单词是听说读写的基础,是有必要花时间去学习的。

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  3. 只推荐利用词根记忆(当然并非百分百有效)
  4. 学习软件:欧路词典——免费,全平台通用

以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
image.png
注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
image.png
就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

提醒

必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

',12),m={href:"http://testyourvocab.com/result?user=13229458",target:"_blank",rel:"noopener noreferrer"},g=r("br",null,null,-1),_=r("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426218162.png",alt:"image.png"},null,-1),u=a('

4360 是网站为我估算的单词数量。

为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
image.png
上图的含义为:

  • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
  • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
  • 学习中 4122,具体又可细分为:
    • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
    • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
    • 不认识:给了例句也看不懂

从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

',7);function b(f,v){const l=i("ExternalLinkIcon"),n=i("RouterLink");return s(),p("div",null,[d,r("p",null,[e("我之前在某个词汇量评测网站做过评估:"),r("a",m,[e("http://testyourvocab.com/result?user=13229458"),t(l)]),g,_]),u,r("p",null,[e("延伸阅读:"),t(n,{to:"/english/learning-7000-words-task-completed.html"},{default:c(()=>[e("刷7000个单词的经验总结")]),_:1}),e("。")])])}const w=o(h,[["render",b],["__file","everyone-can-learn-english-3-words.html.vue"]]);export{w as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as s,c as p,a as r,b as e,d as t,w as c,f as a}from"./app-dcf5468f.js";const h={},d=a('

人人都能学会的英语3:单词

方法

单词是听说读写的基础,是有必要花时间去学习的。

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  3. 只推荐利用词根记忆(当然并非百分百有效)
  4. 学习软件:欧路词典——免费,全平台通用

以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
image.png
注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
image.png
就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

提醒

必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

',12),m={href:"http://testyourvocab.com/result?user=13229458",target:"_blank",rel:"noopener noreferrer"},g=r("br",null,null,-1),_=r("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426218162.png",alt:"image.png"},null,-1),u=a('

4360 是网站为我估算的单词数量。

为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
image.png
上图的含义为:

  • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
  • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
  • 学习中 4122,具体又可细分为:
    • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
    • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
    • 不认识:给了例句也看不懂

从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

',7);function f(b,v){const l=i("ExternalLinkIcon"),n=i("RouterLink");return s(),p("div",null,[d,r("p",null,[e("我之前在某个词汇量评测网站做过评估:"),r("a",m,[e("http://testyourvocab.com/result?user=13229458"),t(l)]),g,_]),u,r("p",null,[e("延伸阅读:"),t(n,{to:"/english/learning-7000-words-task-completed.html"},{default:c(()=>[e("刷7000个单词的经验总结")]),_:1}),e("。")])])}const w=o(h,[["render",f],["__file","everyone-can-learn-english-3-words.html.vue"]]);export{w as default}; diff --git a/assets/everyone-can-learn-english-3-words.html-04c449ad.js b/assets/everyone-can-learn-english-3-words.html-69cddd91.js similarity index 65% rename from assets/everyone-can-learn-english-3-words.html-04c449ad.js rename to assets/everyone-can-learn-english-3-words.html-69cddd91.js index 9997b65b..2b6e9916 100644 --- a/assets/everyone-can-learn-english-3-words.html-04c449ad.js +++ b/assets/everyone-can-learn-english-3-words.html-69cddd91.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-58a409d7","path":"/english/everyone-can-learn-english-3-words.html","title":"人人都能学会的英语3:单词","lang":"zh-CN","frontmatter":{"date":"2022-06-27T00:00:00.000Z","tag":"English","description":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-3-words.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语3:单词"}],["meta",{"property":"og:description","content":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语3:单词\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-27T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"提醒","slug":"提醒","link":"#提醒","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4,"words":1200},"filePathRelative":"english/everyone-can-learn-english-3-words.md","localizedDate":"2022年6月27日","excerpt":"

人人都能学会的英语3:单词

\\n

方法

\\n

单词是听说读写的基础,是有必要花时间去学习的。

\\n

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

\\n
    \\n
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. \\n
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. \\n
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. \\n
  7. 学习软件:欧路词典——免费,全平台通用
  8. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-58a409d7","path":"/english/everyone-can-learn-english-3-words.html","title":"人人都能学会的英语3:单词","lang":"zh-CN","frontmatter":{"date":"2022-06-27T00:00:00.000Z","tag":"English","description":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-3-words.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语3:单词"}],["meta",{"property":"og:description","content":"人人都能学会的英语3:单词 方法 单词是听说读写的基础,是有必要花时间去学习的。 然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议: 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。 只推荐利用词根记忆(当然并非百分百有效) 学习软件:欧路词典——免费,全平台通用"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语3:单词\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-27T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[]},{"level":2,"title":"提醒","slug":"提醒","link":"#提醒","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4,"words":1200},"filePathRelative":"english/everyone-can-learn-english-3-words.md","localizedDate":"2022年6月27日","excerpt":"

人人都能学会的英语3:单词

\\n

方法

\\n

单词是听说读写的基础,是有必要花时间去学习的。

\\n

然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

\\n
    \\n
  1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
  2. \\n
  3. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
  4. \\n
  5. 只推荐利用词根记忆(当然并非百分百有效)
  6. \\n
  7. 学习软件:欧路词典——免费,全平台通用
  8. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-4-listening-and-speaking.html-4832e8c7.js b/assets/everyone-can-learn-english-4-listening-and-speaking.html-33930ae5.js similarity index 65% rename from assets/everyone-can-learn-english-4-listening-and-speaking.html-4832e8c7.js rename to assets/everyone-can-learn-english-4-listening-and-speaking.html-33930ae5.js index 49dcc5ef..91b10586 100644 --- a/assets/everyone-can-learn-english-4-listening-and-speaking.html-4832e8c7.js +++ b/assets/everyone-can-learn-english-4-listening-and-speaking.html-33930ae5.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-788d194a","path":"/english/everyone-can-learn-english-4-listening-and-speaking.html","title":"人人都能学会的英语4:听说","lang":"zh-CN","frontmatter":{"date":"2022-06-28T00:00:00.000Z","tag":"English","description":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-4-listening-and-speaking.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语4:听说"}],["meta",{"property":"og:description","content":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语4:听说\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[{"level":3,"title":"1.建立信心","slug":"_1-建立信心","link":"#_1-建立信心","children":[]},{"level":3,"title":"2.明确方向","slug":"_2-明确方向","link":"#_2-明确方向","children":[]},{"level":3,"title":"3.坚持输入并输出","slug":"_3-坚持输入并输出","link":"#_3-坚持输入并输出","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.82,"words":1146},"filePathRelative":"english/everyone-can-learn-english-4-listening-and-speaking.md","localizedDate":"2022年6月28日","excerpt":"

人人都能学会的英语4:听说

\\n

前言

\\n

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

\\n

方法

\\n

听说属于综合能力,建议遵循以下学习步骤:

\\n
    \\n
  1. 激发兴趣,建立信心
  2. \\n
  3. 明确方向
  4. \\n
  5. 脚踏实地,坚持输入并输出
  6. \\n
\\n

1.建立信心

\\n

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-788d194a","path":"/english/everyone-can-learn-english-4-listening-and-speaking.html","title":"人人都能学会的英语4:听说","lang":"zh-CN","frontmatter":{"date":"2022-06-28T00:00:00.000Z","tag":"English","description":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-4-listening-and-speaking.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语4:听说"}],["meta",{"property":"og:description","content":"人人都能学会的英语4:听说 前言 听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。 方法 听说属于综合能力,建议遵循以下学习步骤: 激发兴趣,建立信心 明确方向 脚踏实地,坚持输入并输出 1.建立信心 首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料: Journey to the West: https://b23.tv/61xkAgc English at Work:https://www.bbc.co.uk/learningenglish/english/features/english-at-work"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语4:听说\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"方法","slug":"方法","link":"#方法","children":[{"level":3,"title":"1.建立信心","slug":"_1-建立信心","link":"#_1-建立信心","children":[]},{"level":3,"title":"2.明确方向","slug":"_2-明确方向","link":"#_2-明确方向","children":[]},{"level":3,"title":"3.坚持输入并输出","slug":"_3-坚持输入并输出","link":"#_3-坚持输入并输出","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.82,"words":1146},"filePathRelative":"english/everyone-can-learn-english-4-listening-and-speaking.md","localizedDate":"2022年6月28日","excerpt":"

人人都能学会的英语4:听说

\\n

前言

\\n

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

\\n

方法

\\n

听说属于综合能力,建议遵循以下学习步骤:

\\n
    \\n
  1. 激发兴趣,建立信心
  2. \\n
  3. 明确方向
  4. \\n
  5. 脚踏实地,坚持输入并输出
  6. \\n
\\n

1.建立信心

\\n

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-4-listening-and-speaking.html-543a6f14.js b/assets/everyone-can-learn-english-4-listening-and-speaking.html-4c61079e.js similarity index 98% rename from assets/everyone-can-learn-english-4-listening-and-speaking.html-543a6f14.js rename to assets/everyone-can-learn-english-4-listening-and-speaking.html-4c61079e.js index cd54c03c..6ba81b48 100644 --- a/assets/everyone-can-learn-english-4-listening-and-speaking.html-543a6f14.js +++ b/assets/everyone-can-learn-english-4-listening-and-speaking.html-4c61079e.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c as a,a as e,b as n,d as s,e as o}from"./app-ee4d23bf.js";const h={},c=o('

人人都能学会的英语4:听说

前言

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

方法

听说属于综合能力,建议遵循以下学习步骤:

  1. 激发兴趣,建立信心
  2. 明确方向
  3. 脚踏实地,坚持输入并输出

1.建立信心

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

',8),d={href:"https://b23.tv/61xkAgc",target:"_blank",rel:"noopener noreferrer"},p={href:"https://www.bbc.co.uk/learningenglish/english/features/english-at-work",target:"_blank",rel:"noopener noreferrer"},u=e("p",null,[n("前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。"),e("br"),n(" 两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。")],-1),_=e("p",null,"记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。",-1),g=e("h3",{id:"_2-明确方向",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#_2-明确方向","aria-hidden":"true"},"#"),n(" 2.明确方向")],-1),f=e("p",null,"在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?",-1),b=e("p",null,"当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。",-1),w={href:"https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),m={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},x=e("p",null,"Business English,我推荐 eslpod 的教材:",-1),v={href:"https://secure3.eslpod.com/product/using-english-at-work/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://secure3.eslpod.com/product/english-for-business-meetings/",target:"_blank",rel:"noopener noreferrer"},E={href:"https://secure3.eslpod.com/product/interview-questions-answered/",target:"_blank",rel:"noopener noreferrer"},B=o('

这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

以上三本教材我自己买了,有需要的可以私信我发你。

另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

3.坚持输入并输出

有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

  1. 不看课本(字幕),全部语音听完
  2. 看课本(字幕),做笔记,学习单词与短语
  3. 慢速跟读
    1. 看课本(字幕),播一句,暂停,跟读一句
    2. 不看课本(字幕),播一句,暂停,跟读一句
    3. 看课本(字幕),不暂停,课程跟读
  4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
  5. 脱稿表达(此步骤可选)

在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

',9);function q(C,A){const r=l("ExternalLinkIcon");return i(),a("div",null,[c,e("ul",null,[e("li",null,[n("Journey to the West: "),e("a",d,[n("https://b23.tv/61xkAgc"),s(r)])]),e("li",null,[n("English at Work:"),e("a",p,[n("https://www.bbc.co.uk/learningenglish/english/features/english-at-work"),s(r)])])]),u,_,g,f,b,e("p",null,[n("Daily English 我推荐:"),e("a",w,[n("https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation"),s(r)]),k,n(" 这个视频如果看不了,可以看我写的教程:"),e("a",m,[n("https://www.yuque.com/levy/blog/how-to-surf"),s(r)])]),x,e("ol",null,[e("li",null,[e("a",v,[n("Using English at Work"),s(r)])]),e("li",null,[e("a",y,[n("English for Business Meetings"),s(r)])]),e("li",null,[e("a",E,[n("Interview Questions Answered"),s(r)])])]),B])}const S=t(h,[["render",q],["__file","everyone-can-learn-english-4-listening-and-speaking.html.vue"]]);export{S as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c as a,a as e,b as n,d as s,f as o}from"./app-dcf5468f.js";const h={},c=o('

人人都能学会的英语4:听说

前言

听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

方法

听说属于综合能力,建议遵循以下学习步骤:

  1. 激发兴趣,建立信心
  2. 明确方向
  3. 脚踏实地,坚持输入并输出

1.建立信心

首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

',8),d={href:"https://b23.tv/61xkAgc",target:"_blank",rel:"noopener noreferrer"},p={href:"https://www.bbc.co.uk/learningenglish/english/features/english-at-work",target:"_blank",rel:"noopener noreferrer"},u=e("p",null,[n("前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。"),e("br"),n(" 两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。")],-1),_=e("p",null,"记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。",-1),g=e("h3",{id:"_2-明确方向",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#_2-明确方向","aria-hidden":"true"},"#"),n(" 2.明确方向")],-1),f=e("p",null,"在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?",-1),b=e("p",null,"当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。",-1),w={href:"https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation",target:"_blank",rel:"noopener noreferrer"},k=e("br",null,null,-1),m={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},x=e("p",null,"Business English,我推荐 eslpod 的教材:",-1),v={href:"https://secure3.eslpod.com/product/using-english-at-work/",target:"_blank",rel:"noopener noreferrer"},y={href:"https://secure3.eslpod.com/product/english-for-business-meetings/",target:"_blank",rel:"noopener noreferrer"},E={href:"https://secure3.eslpod.com/product/interview-questions-answered/",target:"_blank",rel:"noopener noreferrer"},B=o('

这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

以上三本教材我自己买了,有需要的可以私信我发你。

另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

3.坚持输入并输出

有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

  1. 不看课本(字幕),全部语音听完
  2. 看课本(字幕),做笔记,学习单词与短语
  3. 慢速跟读
    1. 看课本(字幕),播一句,暂停,跟读一句
    2. 不看课本(字幕),播一句,暂停,跟读一句
    3. 看课本(字幕),不暂停,课程跟读
  4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
  5. 脱稿表达(此步骤可选)

在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

',9);function q(C,A){const r=l("ExternalLinkIcon");return i(),a("div",null,[c,e("ul",null,[e("li",null,[n("Journey to the West: "),e("a",d,[n("https://b23.tv/61xkAgc"),s(r)])]),e("li",null,[n("English at Work:"),e("a",p,[n("https://www.bbc.co.uk/learningenglish/english/features/english-at-work"),s(r)])])]),u,_,g,f,b,e("p",null,[n("Daily English 我推荐:"),e("a",w,[n("https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversation"),s(r)]),k,n(" 这个视频如果看不了,可以看我写的教程:"),e("a",m,[n("https://www.yuque.com/levy/blog/how-to-surf"),s(r)])]),x,e("ol",null,[e("li",null,[e("a",v,[n("Using English at Work"),s(r)])]),e("li",null,[e("a",y,[n("English for Business Meetings"),s(r)])]),e("li",null,[e("a",E,[n("Interview Questions Answered"),s(r)])])]),B])}const S=t(h,[["render",q],["__file","everyone-can-learn-english-4-listening-and-speaking.html.vue"]]);export{S as default}; diff --git a/assets/everyone-can-learn-english-5-reading-and-writing.html-accb74c4.js b/assets/everyone-can-learn-english-5-reading-and-writing.html-c9f3798a.js similarity index 70% rename from assets/everyone-can-learn-english-5-reading-and-writing.html-accb74c4.js rename to assets/everyone-can-learn-english-5-reading-and-writing.html-c9f3798a.js index 3a768b91..e4d6c171 100644 --- a/assets/everyone-can-learn-english-5-reading-and-writing.html-accb74c4.js +++ b/assets/everyone-can-learn-english-5-reading-and-writing.html-c9f3798a.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-ae153a4e","path":"/english/everyone-can-learn-english-5-reading-and-writing.html","title":"人人都能学会的英语5:读写","lang":"zh-CN","frontmatter":{"date":"2022-06-29T00:00:00.000Z","tag":"English","description":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-5-reading-and-writing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语5:读写"}],["meta",{"property":"og:description","content":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语5:读写\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-29T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"阅读能力升级之旅","slug":"阅读能力升级之旅","link":"#阅读能力升级之旅","children":[]},{"level":2,"title":"阅读方法","slug":"阅读方法","link":"#阅读方法","children":[{"level":3,"title":"建立信心","slug":"建立信心","link":"#建立信心","children":[]},{"level":3,"title":"根据蓝思值选书","slug":"根据蓝思值选书","link":"#根据蓝思值选书","children":[]},{"level":3,"title":"巧查生词","slug":"巧查生词","link":"#巧查生词","children":[]},{"level":3,"title":"阅读材料推荐","slug":"阅读材料推荐","link":"#阅读材料推荐","children":[]}]},{"level":2,"title":"写作","slug":"写作","link":"#写作","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":6.03,"words":1808},"filePathRelative":"english/everyone-can-learn-english-5-reading-and-writing.md","localizedDate":"2022年6月29日","excerpt":"

人人都能学会的英语5:读写

\\n

前言

\\n

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

\\n

阅读能力升级之旅

\\n

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

\\n
    \\n
  1. 看到英文网站,第一反应是点击切换中文版
  2. \\n
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. \\n
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. \\n
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. \\n
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. \\n
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-ae153a4e","path":"/english/everyone-can-learn-english-5-reading-and-writing.html","title":"人人都能学会的英语5:读写","lang":"zh-CN","frontmatter":{"date":"2022-06-29T00:00:00.000Z","tag":"English","description":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/everyone-can-learn-english-5-reading-and-writing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"人人都能学会的英语5:读写"}],["meta",{"property":"og:description","content":"人人都能学会的英语5:读写 前言 读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。 阅读能力升级之旅 我先给出自己经历过的全英阅读能力的变化过程,仅供参考: 看到英文网站,第一反应是点击切换中文版 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域) 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-06-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"人人都能学会的英语5:读写\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-29T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"阅读能力升级之旅","slug":"阅读能力升级之旅","link":"#阅读能力升级之旅","children":[]},{"level":2,"title":"阅读方法","slug":"阅读方法","link":"#阅读方法","children":[{"level":3,"title":"建立信心","slug":"建立信心","link":"#建立信心","children":[]},{"level":3,"title":"根据蓝思值选书","slug":"根据蓝思值选书","link":"#根据蓝思值选书","children":[]},{"level":3,"title":"巧查生词","slug":"巧查生词","link":"#巧查生词","children":[]},{"level":3,"title":"阅读材料推荐","slug":"阅读材料推荐","link":"#阅读材料推荐","children":[]}]},{"level":2,"title":"写作","slug":"写作","link":"#写作","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.03,"words":1808},"filePathRelative":"english/everyone-can-learn-english-5-reading-and-writing.md","localizedDate":"2022年6月29日","excerpt":"

人人都能学会的英语5:读写

\\n

前言

\\n

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

\\n

阅读能力升级之旅

\\n

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

\\n
    \\n
  1. 看到英文网站,第一反应是点击切换中文版
  2. \\n
  3. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  4. \\n
  5. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  6. \\n
  7. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  8. \\n
  9. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  10. \\n
  11. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了
  12. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/everyone-can-learn-english-5-reading-and-writing.html-1af65f78.js b/assets/everyone-can-learn-english-5-reading-and-writing.html-fa801eaa.js similarity index 98% rename from assets/everyone-can-learn-english-5-reading-and-writing.html-1af65f78.js rename to assets/everyone-can-learn-english-5-reading-and-writing.html-fa801eaa.js index 6e6f98bd..214a1631 100644 --- a/assets/everyone-can-learn-english-5-reading-and-writing.html-1af65f78.js +++ b/assets/everyone-can-learn-english-5-reading-and-writing.html-fa801eaa.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as s,c as h,a as e,b as i,d as n,w as d,e as a}from"./app-ee4d23bf.js";const p={},c=a('

人人都能学会的英语5:读写

前言

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

阅读能力升级之旅

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

  1. 看到英文网站,第一反应是点击切换中文版
  2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

这是怎么做到的呢?我主要采用了以下方法。

阅读方法

建立信心

如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

  • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
  • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

  • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
  • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
image.png

根据蓝思值选书

有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

',17),g={href:"https://lexile.com/parents-students/understanding-your-lexile-measure/lexile-measures-reading/",target:"_blank",rel:"noopener noreferrer"},m=e("br",null,null,-1),_=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427484594.png",alt:"image.png"},null,-1),u=e("br",null,null,-1),b=e("br",null,null,-1),f=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427489979.png",alt:"image.png"},null,-1),x=e("p",null,"综上所述,推荐按以下步骤提升阅读能力:",-1),k={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},w=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427496404.png",alt:"image.png"},null,-1),y={href:"https://hub.lexile.com/find-a-book/book-results",target:"_blank",rel:"noopener noreferrer"},v=a('

相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。
lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg

巧查生词

阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

因此,对生词,我建议:一页只查一个生词。它的意义是:

  1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
  2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
  3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

  • 有些形容词或副词不认识,并不影响理解句子的大意
  • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
  • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

阅读材料推荐

',10),L=e("h2",{id:"写作",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#写作","aria-hidden":"true"},"#"),i(" 写作")],-1),B=e("p",null,"阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。",-1),N=e("p",null,"读书笔记示例:",-1),R=e("figure",null,[e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/indispensable-opposition.png",alt:"indispensable-opposition.png",tabindex:"0"}),e("figcaption",null,"indispensable-opposition.png")],-1),V={href:"http://eslpod.com",target:"_blank",rel:"noopener noreferrer"},A=e("br",null,null,-1),E=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427508571.png",alt:"image.png"},null,-1),W=e("p",null,[i("这是我申请 JetBrains 正版授权时发的邮件:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427519794.png",alt:"image.png"})],-1),C=e("p",null,[i("还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。"),e("br"),i(" 这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。")],-1),S=e("p",null,"总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!",-1);function j(D,I){const r=l("ExternalLinkIcon"),t=l("RouterLink");return s(),h("div",null,[c,e("p",null,[i("核心思路是评估自己的阅读"),e("a",g,[i("蓝思值"),n(r)]),i(",再找到适合自己的阅读材料。什么是蓝思值呢?"),m,_,u,i(" 假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。"),b,f]),x,e("ol",null,[e("li",null,[i("评估自己的阅读能力 "),e("a",k,[i("https://readtheory.org/"),n(r)]),i(),w]),e("li",null,[i("找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 "),e("a",y,[i("https://hub.lexile.com/find-a-book/book-results"),n(r)])])]),v,e("p",null,[i("如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接"),n(t,{to:"/english/contemporary-college-english-1.html"},{default:d(()=>[i("点击查看")]),_:1}),i("。")]),L,B,N,R,e("p",null,[i("这是我购买 "),e("a",V,[i("eslpod.com"),n(r)]),i(" 教材时发的邮件:"),A,E]),W,C,S])}const M=o(p,[["render",j],["__file","everyone-can-learn-english-5-reading-and-writing.html.vue"]]);export{M as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as s,c as h,a as e,b as i,d as n,w as d,f as a}from"./app-dcf5468f.js";const p={},c=a('

人人都能学会的英语5:读写

前言

读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

阅读能力升级之旅

我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

  1. 看到英文网站,第一反应是点击切换中文版
  2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
  3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
  4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
  5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
  6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

这是怎么做到的呢?我主要采用了以下方法。

阅读方法

建立信心

如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

  • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
  • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

  • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
  • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
image.png

根据蓝思值选书

有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

',17),g={href:"https://lexile.com/parents-students/understanding-your-lexile-measure/lexile-measures-reading/",target:"_blank",rel:"noopener noreferrer"},m=e("br",null,null,-1),_=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427484594.png",alt:"image.png"},null,-1),u=e("br",null,null,-1),b=e("br",null,null,-1),f=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427489979.png",alt:"image.png"},null,-1),x=e("p",null,"综上所述,推荐按以下步骤提升阅读能力:",-1),k={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},w=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427496404.png",alt:"image.png"},null,-1),y={href:"https://hub.lexile.com/find-a-book/book-results",target:"_blank",rel:"noopener noreferrer"},v=a('

相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。
lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg

巧查生词

阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

因此,对生词,我建议:一页只查一个生词。它的意义是:

  1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
  2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
  3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

  • 有些形容词或副词不认识,并不影响理解句子的大意
  • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
  • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

阅读材料推荐

',10),L=e("h2",{id:"写作",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#写作","aria-hidden":"true"},"#"),i(" 写作")],-1),B=e("p",null,"阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。",-1),N=e("p",null,"读书笔记示例:",-1),R=e("figure",null,[e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/indispensable-opposition.png",alt:"indispensable-opposition.png",tabindex:"0"}),e("figcaption",null,"indispensable-opposition.png")],-1),V={href:"http://eslpod.com",target:"_blank",rel:"noopener noreferrer"},A=e("br",null,null,-1),E=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427508571.png",alt:"image.png"},null,-1),W=e("p",null,[i("这是我申请 JetBrains 正版授权时发的邮件:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682427519794.png",alt:"image.png"})],-1),C=e("p",null,[i("还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。"),e("br"),i(" 这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。")],-1),S=e("p",null,"总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!",-1);function j(D,I){const r=l("ExternalLinkIcon"),t=l("RouterLink");return s(),h("div",null,[c,e("p",null,[i("核心思路是评估自己的阅读"),e("a",g,[i("蓝思值"),n(r)]),i(",再找到适合自己的阅读材料。什么是蓝思值呢?"),m,_,u,i(" 假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。"),b,f]),x,e("ol",null,[e("li",null,[i("评估自己的阅读能力 "),e("a",k,[i("https://readtheory.org/"),n(r)]),i(),w]),e("li",null,[i("找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 "),e("a",y,[i("https://hub.lexile.com/find-a-book/book-results"),n(r)])])]),v,e("p",null,[i("如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接"),n(t,{to:"/english/contemporary-college-english-1.html"},{default:d(()=>[i("点击查看")]),_:1}),i("。")]),L,B,N,R,e("p",null,[i("这是我购买 "),e("a",V,[i("eslpod.com"),n(r)]),i(" 教材时发的邮件:"),A,E]),W,C,S])}const M=o(p,[["render",j],["__file","everyone-can-learn-english-5-reading-and-writing.html.vue"]]);export{M as default}; diff --git a/assets/export-mysql-table-into-excel.html-829471c4.js b/assets/export-mysql-table-into-excel.html-14d9fea6.js similarity index 99% rename from assets/export-mysql-table-into-excel.html-829471c4.js rename to assets/export-mysql-table-into-excel.html-14d9fea6.js index 6e1ecec2..ad634c5d 100644 --- a/assets/export-mysql-table-into-excel.html-829471c4.js +++ b/assets/export-mysql-table-into-excel.html-14d9fea6.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as p,c as o,f as c,a as n,b as s,d as l,e as i}from"./app-ee4d23bf.js";const u={},r=n("h1",{id:"python-导出-mysql-库表信息到-excel",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#python-导出-mysql-库表信息到-excel","aria-hidden":"true"},"#"),s(" Python 导出 MySQL 库表信息到 Excel")],-1),k=n("h2",{id:"需求",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#需求","aria-hidden":"true"},"#"),s(" 需求")],-1),d=n("p",null,"查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。",-1),m=i(`

代码

import mysql.connector
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as p,c as o,e as c,a as n,b as s,d as l,f as i}from"./app-dcf5468f.js";const u={},r=n("h1",{id:"python-导出-mysql-库表信息到-excel",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#python-导出-mysql-库表信息到-excel","aria-hidden":"true"},"#"),s(" Python 导出 MySQL 库表信息到 Excel")],-1),k=n("h2",{id:"需求",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#需求","aria-hidden":"true"},"#"),s(" 需求")],-1),d=n("p",null,"查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。",-1),m=i(`

代码

import mysql.connector
 import xlsxwriter
 
 # 根据实际修改下面的变量
diff --git a/assets/export-mysql-table-into-excel.html-09a29b73.js b/assets/export-mysql-table-into-excel.html-90242810.js
similarity index 71%
rename from assets/export-mysql-table-into-excel.html-09a29b73.js
rename to assets/export-mysql-table-into-excel.html-90242810.js
index db5b103e..eb93ed21 100644
--- a/assets/export-mysql-table-into-excel.html-09a29b73.js
+++ b/assets/export-mysql-table-into-excel.html-90242810.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-5b37b3c6","path":"/python/export-mysql-table-into-excel.html","title":"Python 导出 MySQL 库表信息到 Excel","lang":"zh-CN","frontmatter":{"date":"2023-03-05T00:00:00.000Z","tag":"Python","description":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/export-mysql-table-into-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Python 导出 MySQL 库表信息到 Excel"}],["meta",{"property":"og:description","content":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Python 导出 MySQL 库表信息到 Excel\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"代码","slug":"代码","link":"#代码","children":[]},{"level":2,"title":"其他细节","slug":"其他细节","link":"#其他细节","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.47,"words":440},"filePathRelative":"python/export-mysql-table-into-excel.md","localizedDate":"2023年3月5日","excerpt":"

Python 导出 MySQL 库表信息到 Excel

\\n

需求

\\n

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-5b37b3c6","path":"/python/export-mysql-table-into-excel.html","title":"Python 导出 MySQL 库表信息到 Excel","lang":"zh-CN","frontmatter":{"date":"2023-03-05T00:00:00.000Z","tag":"Python","description":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/export-mysql-table-into-excel.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Python 导出 MySQL 库表信息到 Excel"}],["meta",{"property":"og:description","content":"Python 导出 MySQL 库表信息到 Excel 需求 查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Python 导出 MySQL 库表信息到 Excel\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"代码","slug":"代码","link":"#代码","children":[]},{"level":2,"title":"其他细节","slug":"其他细节","link":"#其他细节","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.47,"words":440},"filePathRelative":"python/export-mysql-table-into-excel.md","localizedDate":"2023年3月5日","excerpt":"

Python 导出 MySQL 库表信息到 Excel

\\n

需求

\\n

查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/git-best-pratices.html-c804ad63.js b/assets/git-best-pratices.html-4df1a641.js similarity index 99% rename from assets/git-best-pratices.html-c804ad63.js rename to assets/git-best-pratices.html-4df1a641.js index 679cab21..d88332cc 100644 --- a/assets/git-best-pratices.html-c804ad63.js +++ b/assets/git-best-pratices.html-4df1a641.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o,c as l,a as e,b as r,d as i,e as t}from"./app-ee4d23bf.js";const h={},d=t('

2020-09-21

Git最佳实践

精简提交

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

频繁提交

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

不要提交不完整的改动

虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

提交前测试那些改动

不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

版本控制不是备份系统

版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

Github实例

一个功能对应一个分支

下面是好的示例: 格式化代码,也应该单独一个PR
下面是不好的示例:因为一个PR修改了不同的主题内容

提交“瘦”的PR

',18),c={href:"https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendly",target:"_blank",rel:"noopener noreferrer"},p=e("br",null,null,-1),g=e("br",null,null,-1),m=e("br",null,null,-1),u=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682344998899.png",alt:""},null,-1),b=e("p",null,[r("下图是拆分后:"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345004360.png",alt:""})],-1),_=e("p",null,[r("单个PR的改动文件只有11个"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345009991.png",alt:""}),r("每个 PR 改动的文件少了,这样 review 起来就更容易了。")],-1),f=e("h3",{id:"使用正确的标题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#使用正确的标题","aria-hidden":"true"},"#"),r(" 使用正确的标题")],-1),v={href:"https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits",target:"_blank",rel:"noopener noreferrer"},x=t(`

另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

  • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
  • 优先使用正面肯定语句,而不是否定句。

好的示例:docs: extraQuery 的正确使用方法
不好的示例:docs: 更新不直观的例子

根据模板填写PR描述

这是我们 Github 的 PR 模板,融合了我们的最佳实践
下面是实际的好的例子

自动关闭issue

image.png
image.png
git commit -m 'fix #6'
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o,c as l,a as e,b as r,d as i,f as t}from"./app-dcf5468f.js";const h={},d=t('

2020-09-21

Git最佳实践

精简提交

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

频繁提交

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

不要提交不完整的改动

虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

提交前测试那些改动

不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

版本控制不是备份系统

版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

Github实例

一个功能对应一个分支

下面是好的示例: 格式化代码,也应该单独一个PR
下面是不好的示例:因为一个PR修改了不同的主题内容

提交“瘦”的PR

',18),c={href:"https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendly",target:"_blank",rel:"noopener noreferrer"},p=e("br",null,null,-1),g=e("br",null,null,-1),m=e("br",null,null,-1),u=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682344998899.png",alt:""},null,-1),b=e("p",null,[r("下图是拆分后:"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345004360.png",alt:""})],-1),_=e("p",null,[r("单个PR的改动文件只有11个"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/git/1682345009991.png",alt:""}),r("每个 PR 改动的文件少了,这样 review 起来就更容易了。")],-1),f=e("h3",{id:"使用正确的标题",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#使用正确的标题","aria-hidden":"true"},"#"),r(" 使用正确的标题")],-1),v={href:"https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits",target:"_blank",rel:"noopener noreferrer"},x=t(`

另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

  • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
  • 优先使用正面肯定语句,而不是否定句。

好的示例:docs: extraQuery 的正确使用方法
不好的示例:docs: 更新不直观的例子

根据模板填写PR描述

这是我们 Github 的 PR 模板,融合了我们的最佳实践
下面是实际的好的例子

自动关闭issue

image.png
image.png
git commit -m 'fix #6'
 # 或
 git commit -m 'close #6'
 

当pr合并时,将自动close issue

1+2 review 规则

1 是指发起 PR 的人,2 是指进行 code review 的人。也即,每一个 PR,至少要经过两个团队成员 approve 才能合并。

上面是针对 github 的协作,项目组中可酌情变为 1+1 规则


礼貌提问

在 github 向人提问时,需要有礼貌。当提出 feature request时,还要说明自己的情况,尽可能提供更多的信息给对方。

上面的示例有三个重点:

  1. 开头表达感谢
  2. 中间说明己方的使用情况,并给出相应链接
  3. 最后参考业界已有实现,给出一个方案设想,并给出相应链接

学习资源

`,17),w={href:"https://git.oschina.net/progit/",target:"_blank",rel:"noopener noreferrer"},k={href:"https://learngitbranching.js.org/?NODEMO",target:"_blank",rel:"noopener noreferrer"};function y(P,R){const a=s("ExternalLinkIcon");return o(),l("div",null,[d,e("p",null,[r("参考文章:"),e("a",c,[r("https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendly"),i(a)]),p,r(" 其中最重要的一点:不要一次提交一个很大改动的PR,否则别人很难 review,要学会拆分步骤。"),g,r(" 下面是一个 PR 示例:"),m,r(" 拆分前,包含了35个改动,很难 review"),u]),b,_,f,e("p",null,[e("a",v,[r("相关规范看这里"),i(a)])]),x,e("ul",null,[e("li",null,[e("a",w,[r("Pro Git"),i(a)])]),e("li",null,[e("a",k,[r("https://learngitbranching.js.org"),i(a)])])])])}const q=n(h,[["render",y],["__file","git-best-pratices.html.vue"]]);export{q as default}; diff --git a/assets/git-best-pratices.html-9eed25ec.js b/assets/git-best-pratices.html-a5f43181.js similarity index 73% rename from assets/git-best-pratices.html-9eed25ec.js rename to assets/git-best-pratices.html-a5f43181.js index 38890b88..f916a88a 100644 --- a/assets/git-best-pratices.html-9eed25ec.js +++ b/assets/git-best-pratices.html-a5f43181.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1e5872c4","path":"/git/git-best-pratices.html","title":"Git最佳实践","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-best-pratices.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git最佳实践"}],["meta",{"property":"og:description","content":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"精简提交","slug":"精简提交","link":"#精简提交","children":[]},{"level":2,"title":"频繁提交","slug":"频繁提交","link":"#频繁提交","children":[]},{"level":2,"title":"不要提交不完整的改动","slug":"不要提交不完整的改动","link":"#不要提交不完整的改动","children":[]},{"level":2,"title":"提交前测试那些改动","slug":"提交前测试那些改动","link":"#提交前测试那些改动","children":[]},{"level":2,"title":"版本控制不是备份系统","slug":"版本控制不是备份系统","link":"#版本控制不是备份系统","children":[]},{"level":2,"title":"Github实例","slug":"github实例","link":"#github实例","children":[{"level":3,"title":"一个功能对应一个分支","slug":"一个功能对应一个分支","link":"#一个功能对应一个分支","children":[]},{"level":3,"title":"提交“瘦”的PR","slug":"提交-瘦-的pr","link":"#提交-瘦-的pr","children":[]},{"level":3,"title":"使用正确的标题","slug":"使用正确的标题","link":"#使用正确的标题","children":[]},{"level":3,"title":"根据模板填写PR描述","slug":"根据模板填写pr描述","link":"#根据模板填写pr描述","children":[]},{"level":3,"title":"自动关闭issue","slug":"自动关闭issue","link":"#自动关闭issue","children":[]},{"level":3,"title":"1+2 review 规则","slug":"_1-2-review-规则","link":"#_1-2-review-规则","children":[]},{"level":3,"title":"礼貌提问","slug":"礼貌提问","link":"#礼貌提问","children":[]}]},{"level":2,"title":"学习资源","slug":"学习资源","link":"#学习资源","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.86,"words":1158},"filePathRelative":"git/git-best-pratices.md","localizedDate":"2020年9月21日","excerpt":"

2020-09-21

\\n

Git最佳实践

\\n

精简提交

\\n

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
\\n如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

\\n

频繁提交

\\n

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
\\n经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

\\n

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-1e5872c4","path":"/git/git-best-pratices.html","title":"Git最佳实践","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-best-pratices.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git最佳实践"}],["meta",{"property":"og:description","content":"2020-09-21 Git最佳实践 精简提交 一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。 如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。 频繁提交 一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。 经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。 此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"精简提交","slug":"精简提交","link":"#精简提交","children":[]},{"level":2,"title":"频繁提交","slug":"频繁提交","link":"#频繁提交","children":[]},{"level":2,"title":"不要提交不完整的改动","slug":"不要提交不完整的改动","link":"#不要提交不完整的改动","children":[]},{"level":2,"title":"提交前测试那些改动","slug":"提交前测试那些改动","link":"#提交前测试那些改动","children":[]},{"level":2,"title":"版本控制不是备份系统","slug":"版本控制不是备份系统","link":"#版本控制不是备份系统","children":[]},{"level":2,"title":"Github实例","slug":"github实例","link":"#github实例","children":[{"level":3,"title":"一个功能对应一个分支","slug":"一个功能对应一个分支","link":"#一个功能对应一个分支","children":[]},{"level":3,"title":"提交“瘦”的PR","slug":"提交-瘦-的pr","link":"#提交-瘦-的pr","children":[]},{"level":3,"title":"使用正确的标题","slug":"使用正确的标题","link":"#使用正确的标题","children":[]},{"level":3,"title":"根据模板填写PR描述","slug":"根据模板填写pr描述","link":"#根据模板填写pr描述","children":[]},{"level":3,"title":"自动关闭issue","slug":"自动关闭issue","link":"#自动关闭issue","children":[]},{"level":3,"title":"1+2 review 规则","slug":"_1-2-review-规则","link":"#_1-2-review-规则","children":[]},{"level":3,"title":"礼貌提问","slug":"礼貌提问","link":"#礼貌提问","children":[]}]},{"level":2,"title":"学习资源","slug":"学习资源","link":"#学习资源","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.86,"words":1158},"filePathRelative":"git/git-best-pratices.md","localizedDate":"2020年9月21日","excerpt":"

2020-09-21

\\n

Git最佳实践

\\n

精简提交

\\n

一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
\\n如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

\\n

频繁提交

\\n

一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
\\n经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

\\n

此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

","autoDesc":true}');export{e as data}; diff --git a/assets/git-definitive-guide-to-merge-code.html-99dd472d.js b/assets/git-definitive-guide-to-merge-code.html-710385ae.js similarity index 71% rename from assets/git-definitive-guide-to-merge-code.html-99dd472d.js rename to assets/git-definitive-guide-to-merge-code.html-710385ae.js index daaa6961..40eca723 100644 --- a/assets/git-definitive-guide-to-merge-code.html-99dd472d.js +++ b/assets/git-definitive-guide-to-merge-code.html-710385ae.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-48e494ef","path":"/git/git-definitive-guide-to-merge-code.html","title":"Git代码合并指南","lang":"zh-CN","frontmatter":{"date":"2022-04-28T00:00:00.000Z","tag":["Git"],"description":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-definitive-guide-to-merge-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git代码合并指南"}],["meta",{"property":"og:description","content":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git代码合并指南\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"功能分支合并长驻分支冲突","slug":"功能分支合并长驻分支冲突","link":"#功能分支合并长驻分支冲突","children":[{"level":3,"title":"解决思路","slug":"解决思路","link":"#解决思路","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤","link":"#操作步骤","children":[]}]},{"level":2,"title":"功能分支被污染","slug":"功能分支被污染","link":"#功能分支被污染","children":[{"level":3,"title":"解决思路","slug":"解决思路-1","link":"#解决思路-1","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-1","link":"#操作步骤-1","children":[]}]},{"level":2,"title":"挑选别的分支部分代码合并","slug":"挑选别的分支部分代码合并","link":"#挑选别的分支部分代码合并","children":[{"level":3,"title":"解决思路","slug":"解决思路-2","link":"#解决思路-2","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-2","link":"#操作步骤-2","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.45,"words":1035},"filePathRelative":"git/git-definitive-guide-to-merge-code.md","localizedDate":"2022年4月28日","excerpt":"

Git代码合并指南

\\n

前言

\\n

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
\\n在说明问题前,先定义一些概念:

\\n
    \\n
  • feat:指代功能分支
  • \\n
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:\\n
      \\n
    • 受保护,不能直接推送
    • \\n
    • 不会被删除
    • \\n
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • \\n
    \\n
  • \\n
  • MR:merge request。代码合并请求
  • \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-48e494ef","path":"/git/git-definitive-guide-to-merge-code.html","title":"Git代码合并指南","lang":"zh-CN","frontmatter":{"date":"2022-04-28T00:00:00.000Z","tag":["Git"],"description":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-definitive-guide-to-merge-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git代码合并指南"}],["meta",{"property":"og:description","content":"Git代码合并指南 前言 合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。 在说明问题前,先定义一些概念: feat:指代功能分支 dev 与 test:指代两条不同的长驻分支,它们具有以下特点: 受保护,不能直接推送 不会被删除 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test MR:merge request。代码合并请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git代码合并指南\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"功能分支合并长驻分支冲突","slug":"功能分支合并长驻分支冲突","link":"#功能分支合并长驻分支冲突","children":[{"level":3,"title":"解决思路","slug":"解决思路","link":"#解决思路","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤","link":"#操作步骤","children":[]}]},{"level":2,"title":"功能分支被污染","slug":"功能分支被污染","link":"#功能分支被污染","children":[{"level":3,"title":"解决思路","slug":"解决思路-1","link":"#解决思路-1","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-1","link":"#操作步骤-1","children":[]}]},{"level":2,"title":"挑选别的分支部分代码合并","slug":"挑选别的分支部分代码合并","link":"#挑选别的分支部分代码合并","children":[{"level":3,"title":"解决思路","slug":"解决思路-2","link":"#解决思路-2","children":[]},{"level":3,"title":"操作步骤","slug":"操作步骤-2","link":"#操作步骤-2","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.45,"words":1035},"filePathRelative":"git/git-definitive-guide-to-merge-code.md","localizedDate":"2022年4月28日","excerpt":"

Git代码合并指南

\\n

前言

\\n

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
\\n在说明问题前,先定义一些概念:

\\n
    \\n
  • feat:指代功能分支
  • \\n
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:\\n
      \\n
    • 受保护,不能直接推送
    • \\n
    • 不会被删除
    • \\n
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • \\n
    \\n
  • \\n
  • MR:merge request。代码合并请求
  • \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/git-definitive-guide-to-merge-code.html-f93ecf11.js b/assets/git-definitive-guide-to-merge-code.html-aa033054.js similarity index 99% rename from assets/git-definitive-guide-to-merge-code.html-f93ecf11.js rename to assets/git-definitive-guide-to-merge-code.html-aa033054.js index 5091abb7..3a6778ab 100644 --- a/assets/git-definitive-guide-to-merge-code.html-f93ecf11.js +++ b/assets/git-definitive-guide-to-merge-code.html-aa033054.js @@ -1 +1 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as a,e as t}from"./app-ee4d23bf.js";const l={},r=t('

Git代码合并指南

前言

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
在说明问题前,先定义一些概念:

  • feat:指代功能分支
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
    • 受保护,不能直接推送
    • 不会被删除
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
  • MR:merge request。代码合并请求

以及说明本文解决冲突涉及到的工具及平台:

  • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
  • 使用 GitLab 托管代码

功能分支合并长驻分支冲突

这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
下面先给出解决思路,再给出图文操作步骤。

解决思路

  1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
  2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
  3. 推送 conflict/resolved 分支
  4. 提交 MR:conflict/resolved -> dev

操作步骤

  1. 本地切换到 dev 分支,更新代码
  2. 合并相应的 feat 分支
  1. 弹出冲突提示,点击合并
  1. 首先处理无冲突的代码,点击下图红框处
  1. 再根据情况,选择合并代码或丢弃代码。
  1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
  1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

功能分支被污染

分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
这种场景的出现可能有多种原因:

  1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
  2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

解决思路

  1. 基于目标分支如 (test 分支)切一个干净的分支 clean
  2. 使用 cherry-pick,挑选自己想要的提交
  3. 再提交MR(clean -> test)

注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

操作步骤

  1. 更新目标分支(在这里是 test)
  1. 基于 test 切新分支,这里示例命名为:clean
  1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
  2. 右键,点击 Cherry-Pick
  3. 则相应的提交记录就会合并到 clean 分支
  4. 推送 clean,提交MR(clean -> test)

挑选别的分支部分代码合并

有可能会出现这样一种场景:

  • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
  • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
  • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

此时该如何是好?

解决思路

其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
feat 分支就算被删了,只要提交记录还在,那也没关系:

  • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
  • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

操作步骤

此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

',45),o=[r];function c(d,n){return e(),a("div",null,o)}const f=i(l,[["render",c],["__file","git-definitive-guide-to-merge-code.html.vue"]]);export{f as default}; +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as a,f as t}from"./app-dcf5468f.js";const l={},r=t('

Git代码合并指南

前言

合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
在说明问题前,先定义一些概念:

  • feat:指代功能分支
  • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
    • 受保护,不能直接推送
    • 不会被删除
    • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
  • MR:merge request。代码合并请求

以及说明本文解决冲突涉及到的工具及平台:

  • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
  • 使用 GitLab 托管代码

功能分支合并长驻分支冲突

这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
下面先给出解决思路,再给出图文操作步骤。

解决思路

  1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
  2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
  3. 推送 conflict/resolved 分支
  4. 提交 MR:conflict/resolved -> dev

操作步骤

  1. 本地切换到 dev 分支,更新代码
  2. 合并相应的 feat 分支
  1. 弹出冲突提示,点击合并
  1. 首先处理无冲突的代码,点击下图红框处
  1. 再根据情况,选择合并代码或丢弃代码。
  1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
  1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

功能分支被污染

分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
这种场景的出现可能有多种原因:

  1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
  2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

解决思路

  1. 基于目标分支如 (test 分支)切一个干净的分支 clean
  2. 使用 cherry-pick,挑选自己想要的提交
  3. 再提交MR(clean -> test)

注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

操作步骤

  1. 更新目标分支(在这里是 test)
  1. 基于 test 切新分支,这里示例命名为:clean
  1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
  2. 右键,点击 Cherry-Pick
  3. 则相应的提交记录就会合并到 clean 分支
  4. 推送 clean,提交MR(clean -> test)

挑选别的分支部分代码合并

有可能会出现这样一种场景:

  • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
  • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
  • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

此时该如何是好?

解决思路

其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
feat 分支就算被删了,只要提交记录还在,那也没关系:

  • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
  • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

操作步骤

此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

',45),o=[r];function c(d,n){return e(),a("div",null,o)}const f=i(l,[["render",c],["__file","git-definitive-guide-to-merge-code.html.vue"]]);export{f as default}; diff --git a/assets/git-history-two-tricks-in-idea.html-1143ee1d.js b/assets/git-history-two-tricks-in-idea.html-12d7b14c.js similarity index 98% rename from assets/git-history-two-tricks-in-idea.html-1143ee1d.js rename to assets/git-history-two-tricks-in-idea.html-12d7b14c.js index 6bac622d..a0f3f073 100644 --- a/assets/git-history-two-tricks-in-idea.html-1143ee1d.js +++ b/assets/git-history-two-tricks-in-idea.html-12d7b14c.js @@ -1 +1 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as o,f as a,a as e,b as r,e as m}from"./app-ee4d23bf.js";const c={},s=e("h1",{id:"git查看历史记录小技巧",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git查看历史记录小技巧","aria-hidden":"true"},"#"),r(" Git查看历史记录小技巧")],-1),l=e("p",null,"分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。",-1),p=e("p",null,"这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。",-1),n=m('

第一个是叫 annotate with git blame
在IDEA的行号这个位置,右键,再点击即可。如图所示:

效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

在合并冲突的时候也可以用这个技巧。

对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

这样就能提供更多的信息帮助解决冲突。

就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

之所以这样,可能会有以下的原因:

  1. 代码格式化
  2. 移动代码,比如说拷贝代码、迁移代码
  3. 合并代码,解决冲突

上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

这就引出了第二个小技巧了: git show history

点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

就会出现如图所示的这样的一个 git log的 界面。

那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

',16);function d(g,h){return i(),o("div",null,[s,l,p,a(" more "),n])}const b=t(c,[["render",d],["__file","git-history-two-tricks-in-idea.html.vue"]]);export{b as default}; +import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as o,e as a,a as e,b as r,f as m}from"./app-dcf5468f.js";const c={},s=e("h1",{id:"git查看历史记录小技巧",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#git查看历史记录小技巧","aria-hidden":"true"},"#"),r(" Git查看历史记录小技巧")],-1),l=e("p",null,"分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。",-1),p=e("p",null,"这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。",-1),n=m('

第一个是叫 annotate with git blame
在IDEA的行号这个位置,右键,再点击即可。如图所示:

效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

在合并冲突的时候也可以用这个技巧。

对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

这样就能提供更多的信息帮助解决冲突。

就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

之所以这样,可能会有以下的原因:

  1. 代码格式化
  2. 移动代码,比如说拷贝代码、迁移代码
  3. 合并代码,解决冲突

上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

这就引出了第二个小技巧了: git show history

点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

就会出现如图所示的这样的一个 git log的 界面。

那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

',16);function d(g,h){return i(),o("div",null,[s,l,p,a(" more "),n])}const b=t(c,[["render",d],["__file","git-history-two-tricks-in-idea.html.vue"]]);export{b as default}; diff --git a/assets/git-history-two-tricks-in-idea.html-0567d0b5.js b/assets/git-history-two-tricks-in-idea.html-5d7d3044.js similarity index 65% rename from assets/git-history-two-tricks-in-idea.html-0567d0b5.js rename to assets/git-history-two-tricks-in-idea.html-5d7d3044.js index a60990da..81d24aa5 100644 --- a/assets/git-history-two-tricks-in-idea.html-0567d0b5.js +++ b/assets/git-history-two-tricks-in-idea.html-5d7d3044.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-60021cbc","path":"/git/git-history-two-tricks-in-idea.html","title":"Git查看历史记录小技巧","lang":"zh-CN","frontmatter":{"date":"2023-08-11T00:00:00.000Z","tag":["Git"],"description":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-history-two-tricks-in-idea.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git查看历史记录小技巧"}],["meta",{"property":"og:description","content":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2023-08-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git查看历史记录小技巧\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-11T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.16,"words":648},"filePathRelative":"git/git-history-two-tricks-in-idea.md","localizedDate":"2023年8月11日","excerpt":"

Git查看历史记录小技巧

\\n

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

\\n

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-60021cbc","path":"/git/git-history-two-tricks-in-idea.html","title":"Git查看历史记录小技巧","lang":"zh-CN","frontmatter":{"date":"2023-08-11T00:00:00.000Z","tag":["Git"],"description":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-history-two-tricks-in-idea.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git查看历史记录小技巧"}],["meta",{"property":"og:description","content":"Git查看历史记录小技巧 分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。 这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2023-08-11T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git查看历史记录小技巧\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-11T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.16,"words":648},"filePathRelative":"git/git-history-two-tricks-in-idea.md","localizedDate":"2023年8月11日","excerpt":"

Git查看历史记录小技巧

\\n

分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

\\n

这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/git-useful-commands.html-4156d3f3.js b/assets/git-useful-commands.html-1d5e62e9.js similarity index 81% rename from assets/git-useful-commands.html-4156d3f3.js rename to assets/git-useful-commands.html-1d5e62e9.js index a809be88..07e7cfa4 100644 --- a/assets/git-useful-commands.html-4156d3f3.js +++ b/assets/git-useful-commands.html-1d5e62e9.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-642eaaea","path":"/git/git-useful-commands.html","title":"Git常用命令","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-useful-commands.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git常用命令"}],["meta",{"property":"og:description","content":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git常用命令\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[]},{"level":2,"title":"增强","slug":"增强","link":"#增强","children":[]},{"level":2,"title":"记住账号密码","slug":"记住账号密码","link":"#记住账号密码","children":[]},{"level":2,"title":"初始化","slug":"初始化","link":"#初始化","children":[]},{"level":2,"title":"本地提交","slug":"本地提交","link":"#本地提交","children":[{"level":3,"title":"取消未暂存的修改","slug":"取消未暂存的修改","link":"#取消未暂存的修改","children":[]},{"level":3,"title":"取消add","slug":"取消add","link":"#取消add","children":[]},{"level":3,"title":"取消提交","slug":"取消提交","link":"#取消提交","children":[]},{"level":3,"title":"修正提交","slug":"修正提交","link":"#修正提交","children":[]},{"level":3,"title":"stash修改","slug":"stash修改","link":"#stash修改","children":[]},{"level":3,"title":"恢复stash","slug":"恢复stash","link":"#恢复stash","children":[]}]},{"level":2,"title":"分支管理","slug":"分支管理","link":"#分支管理","children":[{"level":3,"title":"创建分支","slug":"创建分支","link":"#创建分支","children":[]},{"level":3,"title":"查看远程分支","slug":"查看远程分支","link":"#查看远程分支","children":[]},{"level":3,"title":"创建干净历史分支","slug":"创建干净历史分支","link":"#创建干净历史分支","children":[]},{"level":3,"title":"删除分支","slug":"删除分支","link":"#删除分支","children":[]}]},{"level":2,"title":"远程仓库","slug":"远程仓库","link":"#远程仓库","children":[{"level":3,"title":"远程仓库管理","slug":"远程仓库管理","link":"#远程仓库管理","children":[]},{"level":3,"title":"浅克隆","slug":"浅克隆","link":"#浅克隆","children":[]},{"level":3,"title":"克隆指定分支","slug":"克隆指定分支","link":"#克隆指定分支","children":[]},{"level":3,"title":"克隆失败因为文件名太长","slug":"克隆失败因为文件名太长","link":"#克隆失败因为文件名太长","children":[]},{"level":3,"title":"强行推送","slug":"强行推送","link":"#强行推送","children":[]},{"level":3,"title":"取消错误的推送","slug":"取消错误的推送","link":"#取消错误的推送","children":[]}]},{"level":2,"title":"标签管理","slug":"标签管理","link":"#标签管理","children":[{"level":3,"title":"新建本地标签","slug":"新建本地标签","link":"#新建本地标签","children":[]},{"level":3,"title":"删除本地标签","slug":"删除本地标签","link":"#删除本地标签","children":[]},{"level":3,"title":"查看本地所有标签","slug":"查看本地所有标签","link":"#查看本地所有标签","children":[]},{"level":3,"title":"推送本地标签","slug":"推送本地标签","link":"#推送本地标签","children":[]},{"level":3,"title":"获取远程标签","slug":"获取远程标签","link":"#获取远程标签","children":[]},{"level":3,"title":"删除远程标签","slug":"删除远程标签","link":"#删除远程标签","children":[]}]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"cherry-pick","slug":"cherry-pick","link":"#cherry-pick","children":[]},{"level":3,"title":"merge unrelated histories","slug":"merge-unrelated-histories","link":"#merge-unrelated-histories","children":[]},{"level":3,"title":"git log 丢失最新提交","slug":"git-log-丢失最新提交","link":"#git-log-丢失最新提交","children":[]},{"level":3,"title":"查看分支创建时间","slug":"查看分支创建时间","link":"#查看分支创建时间","children":[]},{"level":3,"title":"根据文件搜索历史","slug":"根据文件搜索历史","link":"#根据文件搜索历史","children":[]},{"level":3,"title":"从所有提交中删除一个文件","slug":"从所有提交中删除一个文件","link":"#从所有提交中删除一个文件","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5.05,"words":1515},"filePathRelative":"git/git-useful-commands.md","localizedDate":"2020年9月21日","excerpt":"

Git常用命令

\\n

前言

\\n

本文将列举Git常见场景,并给出相应解决方案。

\\n

约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

\\n

推荐: 图形化交互式Git教程

\\n

配置

\\n

Mac/Linux 用户 执行以下操作

\\n
vi ~/.gitconfig\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-642eaaea","path":"/git/git-useful-commands.html","title":"Git常用命令","lang":"zh-CN","frontmatter":{"date":"2020-09-21T00:00:00.000Z","tag":"Git","description":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/git-useful-commands.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Git常用命令"}],["meta",{"property":"og:description","content":"Git常用命令 前言 本文将列举Git常见场景,并给出相应解决方案。 约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。 推荐: 图形化交互式Git教程 配置 Mac/Linux 用户 执行以下操作 vi ~/.gitconfig"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2020-09-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Git常用命令\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-09-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[]},{"level":2,"title":"增强","slug":"增强","link":"#增强","children":[]},{"level":2,"title":"记住账号密码","slug":"记住账号密码","link":"#记住账号密码","children":[]},{"level":2,"title":"初始化","slug":"初始化","link":"#初始化","children":[]},{"level":2,"title":"本地提交","slug":"本地提交","link":"#本地提交","children":[{"level":3,"title":"取消未暂存的修改","slug":"取消未暂存的修改","link":"#取消未暂存的修改","children":[]},{"level":3,"title":"取消add","slug":"取消add","link":"#取消add","children":[]},{"level":3,"title":"取消提交","slug":"取消提交","link":"#取消提交","children":[]},{"level":3,"title":"修正提交","slug":"修正提交","link":"#修正提交","children":[]},{"level":3,"title":"stash修改","slug":"stash修改","link":"#stash修改","children":[]},{"level":3,"title":"恢复stash","slug":"恢复stash","link":"#恢复stash","children":[]}]},{"level":2,"title":"分支管理","slug":"分支管理","link":"#分支管理","children":[{"level":3,"title":"创建分支","slug":"创建分支","link":"#创建分支","children":[]},{"level":3,"title":"查看远程分支","slug":"查看远程分支","link":"#查看远程分支","children":[]},{"level":3,"title":"创建干净历史分支","slug":"创建干净历史分支","link":"#创建干净历史分支","children":[]},{"level":3,"title":"删除分支","slug":"删除分支","link":"#删除分支","children":[]}]},{"level":2,"title":"远程仓库","slug":"远程仓库","link":"#远程仓库","children":[{"level":3,"title":"远程仓库管理","slug":"远程仓库管理","link":"#远程仓库管理","children":[]},{"level":3,"title":"浅克隆","slug":"浅克隆","link":"#浅克隆","children":[]},{"level":3,"title":"克隆指定分支","slug":"克隆指定分支","link":"#克隆指定分支","children":[]},{"level":3,"title":"克隆失败因为文件名太长","slug":"克隆失败因为文件名太长","link":"#克隆失败因为文件名太长","children":[]},{"level":3,"title":"强行推送","slug":"强行推送","link":"#强行推送","children":[]},{"level":3,"title":"取消错误的推送","slug":"取消错误的推送","link":"#取消错误的推送","children":[]}]},{"level":2,"title":"标签管理","slug":"标签管理","link":"#标签管理","children":[{"level":3,"title":"新建本地标签","slug":"新建本地标签","link":"#新建本地标签","children":[]},{"level":3,"title":"删除本地标签","slug":"删除本地标签","link":"#删除本地标签","children":[]},{"level":3,"title":"查看本地所有标签","slug":"查看本地所有标签","link":"#查看本地所有标签","children":[]},{"level":3,"title":"推送本地标签","slug":"推送本地标签","link":"#推送本地标签","children":[]},{"level":3,"title":"获取远程标签","slug":"获取远程标签","link":"#获取远程标签","children":[]},{"level":3,"title":"删除远程标签","slug":"删除远程标签","link":"#删除远程标签","children":[]}]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"cherry-pick","slug":"cherry-pick","link":"#cherry-pick","children":[]},{"level":3,"title":"merge unrelated histories","slug":"merge-unrelated-histories","link":"#merge-unrelated-histories","children":[]},{"level":3,"title":"git log 丢失最新提交","slug":"git-log-丢失最新提交","link":"#git-log-丢失最新提交","children":[]},{"level":3,"title":"查看分支创建时间","slug":"查看分支创建时间","link":"#查看分支创建时间","children":[]},{"level":3,"title":"根据文件搜索历史","slug":"根据文件搜索历史","link":"#根据文件搜索历史","children":[]},{"level":3,"title":"从所有提交中删除一个文件","slug":"从所有提交中删除一个文件","link":"#从所有提交中删除一个文件","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.05,"words":1515},"filePathRelative":"git/git-useful-commands.md","localizedDate":"2020年9月21日","excerpt":"

Git常用命令

\\n

前言

\\n

本文将列举Git常见场景,并给出相应解决方案。

\\n

约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

\\n

推荐: 图形化交互式Git教程

\\n

配置

\\n

Mac/Linux 用户 执行以下操作

\\n
vi ~/.gitconfig\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/git-useful-commands.html-2042b2a2.js b/assets/git-useful-commands.html-e051d6dd.js similarity index 99% rename from assets/git-useful-commands.html-2042b2a2.js rename to assets/git-useful-commands.html-e051d6dd.js index 71fadc04..18f42266 100644 --- a/assets/git-useful-commands.html-2042b2a2.js +++ b/assets/git-useful-commands.html-e051d6dd.js @@ -1,4 +1,4 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as d,o as r,c,a,b as s,d as e,e as i}from"./app-ee4d23bf.js";const t={},o=a("h1",{id:"git常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#git常用命令","aria-hidden":"true"},"#"),s(" Git常用命令")],-1),p=a("h2",{id:"前言",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),s(" 前言")],-1),u=a("p",null,"本文将列举Git常见场景,并给出相应解决方案。",-1),h=a("p",null,[s("约定: 下文代码块中"),a("code",null,"${}"),s("里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。")],-1),b={href:"https://learngitbranching.js.org/?locale=zh_CN",target:"_blank",rel:"noopener noreferrer"},v=i(`

配置

Mac/Linux 用户 执行以下操作

vi ~/.gitconfig
+import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as d,o as r,c,a,b as s,d as e,f as i}from"./app-dcf5468f.js";const t={},o=a("h1",{id:"git常用命令",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#git常用命令","aria-hidden":"true"},"#"),s(" Git常用命令")],-1),p=a("h2",{id:"前言",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),s(" 前言")],-1),u=a("p",null,"本文将列举Git常见场景,并给出相应解决方案。",-1),h=a("p",null,[s("约定: 下文代码块中"),a("code",null,"${}"),s("里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。")],-1),b={href:"https://learngitbranching.js.org/?locale=zh_CN",target:"_blank",rel:"noopener noreferrer"},v=i(`

配置

Mac/Linux 用户 执行以下操作

vi ~/.gitconfig
 

Windows用户在桌面用户文件夹下有个.gitconfig隐藏文件,直接修改即可

补充以下内容

[alias]
   st = status
   cm = commit
diff --git a/assets/gitlab-ci.html-bec278aa.js b/assets/gitlab-ci.html-783ced8a.js
similarity index 83%
rename from assets/gitlab-ci.html-bec278aa.js
rename to assets/gitlab-ci.html-783ced8a.js
index d8becdb7..cd9561c2 100644
--- a/assets/gitlab-ci.html-bec278aa.js
+++ b/assets/gitlab-ci.html-783ced8a.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-4008ff77","path":"/git/gitlab-ci.html","title":"GitLab CI","lang":"zh-CN","frontmatter":{"date":"2023-01-10T00:00:00.000Z","tag":["Git","GitLab","Java","Node.js"],"description":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/gitlab-ci.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"GitLab CI"}],["meta",{"property":"og:description","content":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:published_time","content":"2023-01-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"GitLab CI\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-01-10T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装与配置","slug":"安装与配置","link":"#安装与配置","children":[{"level":3,"title":"GitLab Runner 安装","slug":"gitlab-runner-安装","link":"#gitlab-runner-安装","children":[]},{"level":3,"title":"GitLab Runner 注册","slug":"gitlab-runner-注册","link":"#gitlab-runner-注册","children":[]},{"level":3,"title":"提交.gitlab-ci.yml","slug":"提交-gitlab-ci-yml","link":"#提交-gitlab-ci-yml","children":[]}]},{"level":2,"title":"合并代码前进行检查","slug":"合并代码前进行检查","link":"#合并代码前进行检查","children":[{"level":3,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":3,"title":"设置MR检查","slug":"设置mr检查","link":"#设置mr检查","children":[]},{"level":3,"title":".gitlab-ci.yml 示例","slug":"gitlab-ci-yml-示例","link":"#gitlab-ci-yml-示例","children":[]},{"level":3,"title":"效果","slug":"效果","link":"#效果","children":[]}]},{"level":2,"title":"集成单元测试","slug":"集成单元测试","link":"#集成单元测试","children":[]},{"level":2,"title":"线上发布 jar","slug":"线上发布-jar","link":"#线上发布-jar","children":[{"level":3,"title":"Maven配置","slug":"maven配置","link":"#maven配置","children":[]},{"level":3,"title":".gitlab-ci.yml 配置","slug":"gitlab-ci-yml-配置","link":"#gitlab-ci-yml-配置","children":[]},{"level":3,"title":"拉取最新的jar","slug":"拉取最新的jar","link":"#拉取最新的jar","children":[]}]},{"level":2,"title":"保存中间产物","slug":"保存中间产物","link":"#保存中间产物","children":[]},{"level":2,"title":"其他问题与解决方案","slug":"其他问题与解决方案","link":"#其他问题与解决方案","children":[{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"Node.js","slug":"node-js","link":"#node-js","children":[]},{"level":3,"title":"创建不了容器","slug":"创建不了容器","link":"#创建不了容器","children":[]},{"level":3,"title":"本地成功,流水线失败","slug":"本地成功-流水线失败","link":"#本地成功-流水线失败","children":[]}]},{"level":2,"title":"参考文档","slug":"参考文档","link":"#参考文档","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":6.67,"words":2000},"filePathRelative":"git/gitlab-ci.md","localizedDate":"2023年1月10日","excerpt":"

GitLab CI

\\n

前言

\\n

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-4008ff77","path":"/git/gitlab-ci.html","title":"GitLab CI","lang":"zh-CN","frontmatter":{"date":"2023-01-10T00:00:00.000Z","tag":["Git","GitLab","Java","Node.js"],"description":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/gitlab-ci.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"GitLab CI"}],["meta",{"property":"og:description","content":"GitLab CI 前言 GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:published_time","content":"2023-01-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"GitLab CI\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-01-10T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装与配置","slug":"安装与配置","link":"#安装与配置","children":[{"level":3,"title":"GitLab Runner 安装","slug":"gitlab-runner-安装","link":"#gitlab-runner-安装","children":[]},{"level":3,"title":"GitLab Runner 注册","slug":"gitlab-runner-注册","link":"#gitlab-runner-注册","children":[]},{"level":3,"title":"提交.gitlab-ci.yml","slug":"提交-gitlab-ci-yml","link":"#提交-gitlab-ci-yml","children":[]}]},{"level":2,"title":"合并代码前进行检查","slug":"合并代码前进行检查","link":"#合并代码前进行检查","children":[{"level":3,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":3,"title":"设置MR检查","slug":"设置mr检查","link":"#设置mr检查","children":[]},{"level":3,"title":".gitlab-ci.yml 示例","slug":"gitlab-ci-yml-示例","link":"#gitlab-ci-yml-示例","children":[]},{"level":3,"title":"效果","slug":"效果","link":"#效果","children":[]}]},{"level":2,"title":"集成单元测试","slug":"集成单元测试","link":"#集成单元测试","children":[]},{"level":2,"title":"线上发布 jar","slug":"线上发布-jar","link":"#线上发布-jar","children":[{"level":3,"title":"Maven配置","slug":"maven配置","link":"#maven配置","children":[]},{"level":3,"title":".gitlab-ci.yml 配置","slug":"gitlab-ci-yml-配置","link":"#gitlab-ci-yml-配置","children":[]},{"level":3,"title":"拉取最新的jar","slug":"拉取最新的jar","link":"#拉取最新的jar","children":[]}]},{"level":2,"title":"保存中间产物","slug":"保存中间产物","link":"#保存中间产物","children":[]},{"level":2,"title":"其他问题与解决方案","slug":"其他问题与解决方案","link":"#其他问题与解决方案","children":[{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"Node.js","slug":"node-js","link":"#node-js","children":[]},{"level":3,"title":"创建不了容器","slug":"创建不了容器","link":"#创建不了容器","children":[]},{"level":3,"title":"本地成功,流水线失败","slug":"本地成功-流水线失败","link":"#本地成功-流水线失败","children":[]}]},{"level":2,"title":"参考文档","slug":"参考文档","link":"#参考文档","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.67,"words":2000},"filePathRelative":"git/gitlab-ci.md","localizedDate":"2023年1月10日","excerpt":"

GitLab CI

\\n

前言

\\n

GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/gitlab-ci.html-6f5b90d6.js b/assets/gitlab-ci.html-e3af106a.js similarity index 99% rename from assets/gitlab-ci.html-6f5b90d6.js rename to assets/gitlab-ci.html-e3af106a.js index eabcc835..e9bd8097 100644 --- a/assets/gitlab-ci.html-6f5b90d6.js +++ b/assets/gitlab-ci.html-e3af106a.js @@ -1,4 +1,4 @@ -import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c as r,f as u,a as n,b as a,d as s,w as p,e as t}from"./app-ee4d23bf.js";const d={},m=n("h1",{id:"gitlab-ci",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#gitlab-ci","aria-hidden":"true"},"#"),a(" GitLab CI")],-1),k=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),g=n("p",null,"GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。",-1),v=t(`

安装与配置

GitLab Runner 安装

进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

这里推荐使用 docker 的方式安装,复制以下命令执行即可:

docker run -d --name gitlab-runner --restart always \\
+import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o,c as r,e as u,a as n,b as a,d as s,w as p,f as t}from"./app-dcf5468f.js";const d={},m=n("h1",{id:"gitlab-ci",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#gitlab-ci","aria-hidden":"true"},"#"),a(" GitLab CI")],-1),k=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),g=n("p",null,"GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。",-1),v=t(`

安装与配置

GitLab Runner 安装

进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

这里推荐使用 docker 的方式安装,复制以下命令执行即可:

docker run -d --name gitlab-runner --restart always \\
   -v /var/run/docker.sock:/var/run/docker.sock \\
   -v /srv/gitlab-runner/config:/etc/gitlab-runner \\
   gitlab/gitlab-runner:latest
diff --git a/assets/how-to-connect-to-internet.html-c2ea20a2.js b/assets/how-to-connect-to-internet.html-ae1c63f6.js
similarity index 69%
rename from assets/how-to-connect-to-internet.html-c2ea20a2.js
rename to assets/how-to-connect-to-internet.html-ae1c63f6.js
index 1e4bb390..dd2141f4 100644
--- a/assets/how-to-connect-to-internet.html-c2ea20a2.js
+++ b/assets/how-to-connect-to-internet.html-ae1c63f6.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-17809471","path":"/tools/how-to-connect-to-internet.html","title":"科学上网","lang":"zh-CN","frontmatter":{"date":"2022-06-20T00:00:00.000Z","tag":"Tool","description":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:","head":[["meta",{"property":"og:url","content":"https://levy.vip/tools/how-to-connect-to-internet.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"科学上网"}],["meta",{"property":"og:description","content":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2022-06-20T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"科学上网\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-20T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"购买指南","slug":"购买指南","link":"#购买指南","children":[]},{"level":2,"title":"客户端","slug":"客户端","link":"#客户端","children":[]},{"level":2,"title":"导入配置","slug":"导入配置","link":"#导入配置","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"500 内部代理错误","slug":"_500-内部代理错误","link":"#_500-内部代理错误","children":[]},{"level":3,"title":"修改PAC文件","slug":"修改pac文件","link":"#修改pac文件","children":[]},{"level":3,"title":"使用 New Bing","slug":"使用-new-bing","link":"#使用-new-bing","children":[]},{"level":3,"title":"申请美区apple id","slug":"申请美区apple-id","link":"#申请美区apple-id","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.72,"words":815},"filePathRelative":"tools/how-to-connect-to-internet.md","localizedDate":"2022年6月20日","excerpt":"

科学上网

\\n

说明

\\n

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

\\n

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

\\n

购买指南

\\n

点击进入服务页面, 看到如下页面:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-17809471","path":"/tools/how-to-connect-to-internet.html","title":"科学上网","lang":"zh-CN","frontmatter":{"date":"2022-06-20T00:00:00.000Z","tag":"Tool","description":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:","head":[["meta",{"property":"og:url","content":"https://levy.vip/tools/how-to-connect-to-internet.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"科学上网"}],["meta",{"property":"og:description","content":"科学上网 说明 AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍? 本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。 购买指南 点击进入服务页面, 看到如下页面:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2022-06-20T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"科学上网\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-20T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"说明","slug":"说明","link":"#说明","children":[]},{"level":2,"title":"购买指南","slug":"购买指南","link":"#购买指南","children":[]},{"level":2,"title":"客户端","slug":"客户端","link":"#客户端","children":[]},{"level":2,"title":"导入配置","slug":"导入配置","link":"#导入配置","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"500 内部代理错误","slug":"_500-内部代理错误","link":"#_500-内部代理错误","children":[]},{"level":3,"title":"修改PAC文件","slug":"修改pac文件","link":"#修改pac文件","children":[]},{"level":3,"title":"使用 New Bing","slug":"使用-new-bing","link":"#使用-new-bing","children":[]},{"level":3,"title":"申请美区apple id","slug":"申请美区apple-id","link":"#申请美区apple-id","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.72,"words":815},"filePathRelative":"tools/how-to-connect-to-internet.md","localizedDate":"2022年6月20日","excerpt":"

科学上网

\\n

说明

\\n

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

\\n

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

\\n

购买指南

\\n

点击进入服务页面, 看到如下页面:
\\n\\"image.png\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/how-to-connect-to-internet.html-2dd64f15.js b/assets/how-to-connect-to-internet.html-c6a7d2c0.js similarity index 98% rename from assets/how-to-connect-to-internet.html-2dd64f15.js rename to assets/how-to-connect-to-internet.html-c6a7d2c0.js index 2c653b36..d4d3ce7a 100644 --- a/assets/how-to-connect-to-internet.html-2dd64f15.js +++ b/assets/how-to-connect-to-internet.html-c6a7d2c0.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as s,c as l,a as e,b as a,d as o,e as i}from"./app-ee4d23bf.js";const p={},c=i('

科学上网

说明

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

购买指南

',5),d={href:"https://cp.cloudnx.cc/aff.php?aff=22930",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),h=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/tools/1682172119852.png",alt:"image.png"},null,-1),m=i('

点击注册账户, 会出现如图提示:
image.png

点击购买产品,就会出现查看产品列表面(为什么要这么绕,因为这是保护措施,避免产品主页被黑)。

推荐购买 Basic 套餐,一个人用的话,学习工作、娱乐看视频,每月 50GB 足够了。多个客户端可以同时在线,反正就是随便玩!算下来,一天不到7毛钱,很划算了。
image.png

点击购买后,选择包年,即可享受优惠价格,如下图所示:
image.png

支持支付宝,购买非常方便。

客户端

支持全平台客户端:

',7),u={href:"https://github.com/yichengchen/clashX/releases",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/shadowsocks/shadowsocks-windows/releases",target:"_blank",rel:"noopener noreferrer"},_=e("li",null,"Android 推荐 V2ray",-1),v=e("li",null,"iOS 推荐 Shadowrocket(花点小钱,使用美区 apple id——文末有分享如何申请)",-1),w=i(`

导入配置

以 Shadowsocks 为例。

根据客户端复制相应的订阅地址:
image.png

先禁用系统代理:
image.png

点击在线配置:
image.png

输入URL,点击更新:
image.png

选择一个服务器:
image.png
image.png

再恢复系统代理,选择PAC模式:
image.png

就可以愉快地上网啦!

其他

500 内部代理错误

image.png
image.png

出现此问题时,一般是内网自定义域名不允许走代理,需要禁用掉系统代理:
image.png

修改PAC文件

PAC模式是指:根据规则识别某网站是否需要使用代理访问。

什么时候需要修改PAC文件呢?

  • 当某个网站不想走代理
  • 设置某网站一定走代理

操作如下(以Shadowsocks为例):

按下图所示,模仿添加,即可实现遇到下列网站时选择直连:

不走代理的示例修改:

 "@@||company.yuque.com",
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as s,c as l,a as e,b as a,d as o,f as i}from"./app-dcf5468f.js";const p={},c=i('

科学上网

说明

AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

购买指南

',5),d={href:"https://cp.cloudnx.cc/aff.php?aff=22930",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),h=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/tools/1682172119852.png",alt:"image.png"},null,-1),m=i('

点击注册账户, 会出现如图提示:
image.png

点击购买产品,就会出现查看产品列表面(为什么要这么绕,因为这是保护措施,避免产品主页被黑)。

推荐购买 Basic 套餐,一个人用的话,学习工作、娱乐看视频,每月 50GB 足够了。多个客户端可以同时在线,反正就是随便玩!算下来,一天不到7毛钱,很划算了。
image.png

点击购买后,选择包年,即可享受优惠价格,如下图所示:
image.png

支持支付宝,购买非常方便。

客户端

支持全平台客户端:

',7),u={href:"https://github.com/yichengchen/clashX/releases",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/shadowsocks/shadowsocks-windows/releases",target:"_blank",rel:"noopener noreferrer"},_=e("li",null,"Android 推荐 V2ray",-1),v=e("li",null,"iOS 推荐 Shadowrocket(花点小钱,使用美区 apple id——文末有分享如何申请)",-1),w=i(`

导入配置

以 Shadowsocks 为例。

根据客户端复制相应的订阅地址:
image.png

先禁用系统代理:
image.png

点击在线配置:
image.png

输入URL,点击更新:
image.png

选择一个服务器:
image.png
image.png

再恢复系统代理,选择PAC模式:
image.png

就可以愉快地上网啦!

其他

500 内部代理错误

image.png
image.png

出现此问题时,一般是内网自定义域名不允许走代理,需要禁用掉系统代理:
image.png

修改PAC文件

PAC模式是指:根据规则识别某网站是否需要使用代理访问。

什么时候需要修改PAC文件呢?

  • 当某个网站不想走代理
  • 设置某网站一定走代理

操作如下(以Shadowsocks为例):

按下图所示,模仿添加,即可实现遇到下列网站时选择直连:

不走代理的示例修改:

 "@@||company.yuque.com",
 

必走代理的示例修改:

"*.openai.com",
 "*.bing.com",
 

保存后记得重启软件。

使用 New Bing

再给一个配置 Clash 使用 New Bing 的示例:
image.png
编辑文件:
image.png
添加如下设置:

 - DOMAIN-KEYWORD,bing,🚀 节点选择
diff --git a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-256778be.js b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-298785a8.js
similarity index 71%
rename from assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-256778be.js
rename to assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-298785a8.js
index 5fe59326..77c4cbfc 100644
--- a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-256778be.js
+++ b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-298785a8.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-100d1814","path":"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html","title":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包","lang":"zh-CN","frontmatter":{"date":"2023-08-12T00:00:00.000Z","tag":["Java"],"description":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/how-to-convert-snapshot-into-release-jar-without-source-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包"}],["meta",{"property":"og:description","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:published_time","content":"2023-08-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-12T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"下载","slug":"下载","link":"#下载","children":[]},{"level":2,"title":"修改","slug":"修改","link":"#修改","children":[]},{"level":2,"title":"上传","slug":"上传","link":"#上传","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.41,"words":422},"filePathRelative":"java/how-to-convert-snapshot-into-release-jar-without-source-code.md","localizedDate":"2023年8月12日","excerpt":"

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

\\n

背景

\\n

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

\\n

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-100d1814","path":"/java/how-to-convert-snapshot-into-release-jar-without-source-code.html","title":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包","lang":"zh-CN","frontmatter":{"date":"2023-08-12T00:00:00.000Z","tag":["Java"],"description":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/how-to-convert-snapshot-into-release-jar-without-source-code.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包"}],["meta",{"property":"og:description","content":"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 背景 项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。 问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:published_time","content":"2023-08-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-12T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"下载","slug":"下载","link":"#下载","children":[]},{"level":2,"title":"修改","slug":"修改","link":"#修改","children":[]},{"level":2,"title":"上传","slug":"上传","link":"#上传","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.41,"words":422},"filePathRelative":"java/how-to-convert-snapshot-into-release-jar-without-source-code.md","localizedDate":"2023年8月12日","excerpt":"

奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

\\n

背景

\\n

项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

\\n

问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-6c6aee40.js b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9bf0899.js similarity index 98% rename from assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-6c6aee40.js rename to assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9bf0899.js index 19676cad..acfebed2 100644 --- a/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-6c6aee40.js +++ b/assets/how-to-convert-snapshot-into-release-jar-without-source-code.html-f9bf0899.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as r,c as i,f as s,a as e,b as a,e as n}from"./app-ee4d23bf.js";const o={},c=e("h1",{id:"奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包","aria-hidden":"true"},"#"),a(" 奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包")],-1),d=e("h2",{id:"背景",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),a(" 背景")],-1),m=e("p",null,"项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。",-1),l=e("p",null,"问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。",-1),h=n(`

下载


首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


找到关键的三个 jar:

  • x.jar
  • x-sources.jar
  • x.pom

分别点击进入详情

依次点击 Path,下载到本地。

修改


首先修改名字,如图所示。

再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


再点击 maven 文件夹


修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

上传

把前面解压出来的文件重新打包成 jar

jar cvf my-1.4.1.jar com META-INF
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{o as r,c as i,e as s,a as e,b as a,f as n}from"./app-dcf5468f.js";const o={},c=e("h1",{id:"奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#奇技淫巧-在没有源码的情况下-把-snapshot-转成-release-包","aria-hidden":"true"},"#"),a(" 奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包")],-1),d=e("h2",{id:"背景",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),a(" 背景")],-1),m=e("p",null,"项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。",-1),l=e("p",null,"问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。",-1),h=n(`

下载


首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


找到关键的三个 jar:

  • x.jar
  • x-sources.jar
  • x.pom

分别点击进入详情

依次点击 Path,下载到本地。

修改


首先修改名字,如图所示。

再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


再点击 maven 文件夹


修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

上传

把前面解压出来的文件重新打包成 jar

jar cvf my-1.4.1.jar com META-INF
 
 # you can also use zip 
 # zip -r my-1.4.1.jar com META-INF
diff --git a/assets/how-to-self-evaluate-english-level.html-d652d2f6.js b/assets/how-to-self-evaluate-english-level.html-3db293dc.js
similarity index 61%
rename from assets/how-to-self-evaluate-english-level.html-d652d2f6.js
rename to assets/how-to-self-evaluate-english-level.html-3db293dc.js
index 3f791c52..582e1aa5 100644
--- a/assets/how-to-self-evaluate-english-level.html-d652d2f6.js
+++ b/assets/how-to-self-evaluate-english-level.html-3db293dc.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-d42db13c","path":"/english/how-to-self-evaluate-english-level.html","title":"英文能力评测手把手教学","lang":"zh-CN","frontmatter":{"date":"2022-07-09T00:00:00.000Z","tag":"English","description":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/how-to-self-evaluate-english-level.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"英文能力评测手把手教学"}],["meta",{"property":"og:description","content":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"英文能力评测手把手教学\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"单词量","slug":"单词量","link":"#单词量","children":[]},{"level":2,"title":"阅读能力","slug":"阅读能力","link":"#阅读能力","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.85,"words":554},"filePathRelative":"english/how-to-self-evaluate-english-level.md","localizedDate":"2022年7月9日","excerpt":"

英文能力评测手把手教学

\\n

前言

\\n

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

\\n

单词量

\\n

进入网站:https://preply.com/en/learn/english/test-your-vocab

\\n

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
\\n\\"image.png\\"
\\n在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
\\n\\"image.png\\"
\\n对于该结果,网站有以下值得注意的解释:

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-d42db13c","path":"/english/how-to-self-evaluate-english-level.html","title":"英文能力评测手把手教学","lang":"zh-CN","frontmatter":{"date":"2022-07-09T00:00:00.000Z","tag":"English","description":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/how-to-self-evaluate-english-level.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"英文能力评测手把手教学"}],["meta",{"property":"og:description","content":"英文能力评测手把手教学 前言 之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。 单词量 进入网站:https://preply.com/en/learn/english/test-your-vocab 看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。 在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。 对于该结果,网站有以下值得注意的解释:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-07-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"英文能力评测手把手教学\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-07-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"单词量","slug":"单词量","link":"#单词量","children":[]},{"level":2,"title":"阅读能力","slug":"阅读能力","link":"#阅读能力","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.85,"words":554},"filePathRelative":"english/how-to-self-evaluate-english-level.md","localizedDate":"2022年7月9日","excerpt":"

英文能力评测手把手教学

\\n

前言

\\n

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

\\n

单词量

\\n

进入网站:https://preply.com/en/learn/english/test-your-vocab

\\n

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
\\n\\"image.png\\"
\\n在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
\\n\\"image.png\\"
\\n对于该结果,网站有以下值得注意的解释:

","autoDesc":true}');export{e as data}; diff --git a/assets/how-to-self-evaluate-english-level.html-87710031.js b/assets/how-to-self-evaluate-english-level.html-839828a6.js similarity index 97% rename from assets/how-to-self-evaluate-english-level.html-87710031.js rename to assets/how-to-self-evaluate-english-level.html-839828a6.js index 70a88c42..18c3c728 100644 --- a/assets/how-to-self-evaluate-english-level.html-87710031.js +++ b/assets/how-to-self-evaluate-english-level.html-839828a6.js @@ -1 +1 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as l,c as s,a as e,b as r,d as t,e as a}from"./app-ee4d23bf.js";const g={},h=a('

英文能力评测手把手教学

前言

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

单词量

',4),c={href:"https://preply.com/en/learn/english/test-your-vocab",target:"_blank",rel:"noopener noreferrer"},m=a('

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
image.png
在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
image.png
对于该结果,网站有以下值得注意的解释:

  1. 结果偏差在正负10%左右:

Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

  1. 英语非母语者,用单词量作为能力标准通常划分如下:
image.png
image.png
  1. 结果一年测一次最有效
image.png
image.png

阅读能力

',8),p={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,[r("右上角点击注册:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426663590.png",alt:"image.png"})],-1),u=e("p",null,[r("选择作为学生:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426667189.png",alt:"image.png"})],-1),_=e("br",null,null,-1),b=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426672418.png",alt:"image.png"},null,-1),f=e("br",null,null,-1),v={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},y=e("br",null,null,-1),w=a('

确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
image.png

开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
image.png

做完后,点击确认:
image.png

点击下图红框处:
image.png

往下翻阅,即可看到自己的蓝思值初步评分:
image.png

再给出通用的蓝思值能力对照表:
image.png
以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

',6);function x(k,E){const o=n("ExternalLinkIcon");return l(),s("div",null,[h,e("p",null,[r("进入网站:"),e("a",c,[r("https://preply.com/en/learn/english/test-your-vocab"),t(o)])]),m,e("p",null,[r("评估阅读能力也就是评估蓝思值。进入网站:"),e("a",p,[r("https://readtheory.org/"),t(o)])]),d,u,e("p",null,[r("可以直接使用谷歌账号注册:"),_,b,f,r(" 如果不能谷歌,可以看我写的谷歌教程:"),e("a",v,[r("https://www.yuque.com/levy/blog/how-to-surf"),t(o)]),y,r(" 或者自己填表单,进入下一步。")]),w])}const V=i(g,[["render",x],["__file","how-to-self-evaluate-english-level.html.vue"]]);export{V as default}; +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as l,c as s,a as e,b as r,d as t,f as a}from"./app-dcf5468f.js";const g={},h=a('

英文能力评测手把手教学

前言

之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

单词量

',4),c={href:"https://preply.com/en/learn/english/test-your-vocab",target:"_blank",rel:"noopener noreferrer"},m=a('

看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
image.png
在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
image.png
对于该结果,网站有以下值得注意的解释:

  1. 结果偏差在正负10%左右:

Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

  1. 英语非母语者,用单词量作为能力标准通常划分如下:
image.png
image.png
  1. 结果一年测一次最有效
image.png
image.png

阅读能力

',8),p={href:"https://readtheory.org/",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,[r("右上角点击注册:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426663590.png",alt:"image.png"})],-1),u=e("p",null,[r("选择作为学生:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426667189.png",alt:"image.png"})],-1),_=e("br",null,null,-1),b=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1682426672418.png",alt:"image.png"},null,-1),f=e("br",null,null,-1),v={href:"https://www.yuque.com/levy/blog/how-to-surf",target:"_blank",rel:"noopener noreferrer"},y=e("br",null,null,-1),w=a('

确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
image.png

开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
image.png

做完后,点击确认:
image.png

点击下图红框处:
image.png

往下翻阅,即可看到自己的蓝思值初步评分:
image.png

再给出通用的蓝思值能力对照表:
image.png
以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

',6);function x(k,E){const o=n("ExternalLinkIcon");return l(),s("div",null,[h,e("p",null,[r("进入网站:"),e("a",c,[r("https://preply.com/en/learn/english/test-your-vocab"),t(o)])]),m,e("p",null,[r("评估阅读能力也就是评估蓝思值。进入网站:"),e("a",p,[r("https://readtheory.org/"),t(o)])]),d,u,e("p",null,[r("可以直接使用谷歌账号注册:"),_,b,f,r(" 如果不能谷歌,可以看我写的谷歌教程:"),e("a",v,[r("https://www.yuque.com/levy/blog/how-to-surf"),t(o)]),y,r(" 或者自己填表单,进入下一步。")]),w])}const V=i(g,[["render",x],["__file","how-to-self-evaluate-english-level.html.vue"]]);export{V as default}; diff --git a/assets/index.html-3de969f7.js b/assets/index.html-103d33d4.js similarity index 71% rename from assets/index.html-3de969f7.js rename to assets/index.html-103d33d4.js index 78e593a1..aa9ae73f 100644 --- a/assets/index.html-3de969f7.js +++ b/assets/index.html-103d33d4.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-039cb721.js b/assets/index.html-120f4470.js similarity index 71% rename from assets/index.html-039cb721.js rename to assets/index.html-120f4470.js index 78e593a1..aa9ae73f 100644 --- a/assets/index.html-039cb721.js +++ b/assets/index.html-120f4470.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-0420fe01.js b/assets/index.html-1461acaf.js similarity index 71% rename from assets/index.html-0420fe01.js rename to assets/index.html-1461acaf.js index 78e593a1..aa9ae73f 100644 --- a/assets/index.html-0420fe01.js +++ b/assets/index.html-1461acaf.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-30863193.js b/assets/index.html-17f35239.js similarity index 71% rename from assets/index.html-30863193.js rename to assets/index.html-17f35239.js index 78e593a1..aa9ae73f 100644 --- a/assets/index.html-30863193.js +++ b/assets/index.html-17f35239.js @@ -1 +1 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-18766f96.js b/assets/index.html-18766f96.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-18766f96.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-1e5dd181.js b/assets/index.html-1e5dd181.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-1e5dd181.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-18399ef9.js b/assets/index.html-1eefb1bb.js similarity index 76% rename from assets/index.html-18399ef9.js rename to assets/index.html-1eefb1bb.js index 7f7097cf..4c6ca55d 100644 --- a/assets/index.html-18399ef9.js +++ b/assets/index.html-1eefb1bb.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-204d82a3.js b/assets/index.html-204d82a3.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-204d82a3.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-27179df5.js b/assets/index.html-27179df5.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-27179df5.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-27e28243.js b/assets/index.html-27e28243.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-27e28243.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-2a23ac5a.js b/assets/index.html-2a23ac5a.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-2a23ac5a.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-3c69023e.js b/assets/index.html-3c69023e.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-3c69023e.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-3ef79533.js b/assets/index.html-3ef79533.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-3ef79533.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-4092c5dd.js b/assets/index.html-4092c5dd.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-4092c5dd.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-53073c6a.js b/assets/index.html-53073c6a.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-53073c6a.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-1e4f7d0b.js b/assets/index.html-55f7638e.js similarity index 76% rename from assets/index.html-1e4f7d0b.js rename to assets/index.html-55f7638e.js index 7f7097cf..4c6ca55d 100644 --- a/assets/index.html-1e4f7d0b.js +++ b/assets/index.html-55f7638e.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-6200e2b6.js b/assets/index.html-593aec50.js similarity index 62% rename from assets/index.html-6200e2b6.js rename to assets/index.html-593aec50.js index f5033fcd..efe21baf 100644 --- a/assets/index.html-6200e2b6.js +++ b/assets/index.html-593aec50.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-8daa1a0e","path":"/","title":"levy's blog","lang":"zh-CN","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"levy's blog","heroText":"levy's blog","heroFullScreen":true,"tagline":"思考,表达,练习,创造","description":"","head":[["meta",{"property":"og:url","content":"https://levy.vip/"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"levy's blog"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"levy's blog\\"}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.21,"words":64},"filePathRelative":"README.md","localizedDate":"2024年4月29日","excerpt":"","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-8daa1a0e","path":"/","title":"levy's blog","lang":"zh-CN","frontmatter":{"home":true,"layout":"BlogHome","icon":"home","title":"levy's blog","heroText":"levy's blog","heroFullScreen":true,"tagline":"思考,表达,练习,创造","description":"","head":[["meta",{"property":"og:url","content":"https://levy.vip/"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"levy's blog"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"levy's blog\\"}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.21,"words":64},"filePathRelative":"README.md","localizedDate":"2024年5月28日","excerpt":"","autoDesc":true}`);export{e as data}; diff --git a/assets/index.html-5eaddabe.js b/assets/index.html-5b00eb2a.js similarity index 76% rename from assets/index.html-5eaddabe.js rename to assets/index.html-5b00eb2a.js index 7f7097cf..4c6ca55d 100644 --- a/assets/index.html-5eaddabe.js +++ b/assets/index.html-5b00eb2a.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-5d992ea9.js b/assets/index.html-5d992ea9.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-5d992ea9.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-5f610c54.js b/assets/index.html-5f610c54.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-5f610c54.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-673c18e7.js b/assets/index.html-673c18e7.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-673c18e7.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-6d8830d6.js b/assets/index.html-6d8830d6.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-6d8830d6.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-77768903.js b/assets/index.html-77768903.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-77768903.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-7ae7db32.js b/assets/index.html-7ae7db32.js new file mode 100644 index 00000000..e871e42d --- /dev/null +++ b/assets/index.html-7ae7db32.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7449895b","path":"/llm/","title":"Llm","lang":"zh-CN","frontmatter":{"title":"Llm","article":false,"feed":false,"sitemap":false,"description":"","head":[["meta",{"property":"og:url","content":"https://levy.vip/llm/"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Llm"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"article:author","content":"levy"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"Llm\\"}"]]},"headers":[],"git":{},"readingTime":{"minutes":0,"words":1},"filePathRelative":null,"excerpt":"","autoDesc":true}');export{e as data}; diff --git a/assets/index.html-7b4364a1.js b/assets/index.html-7b4364a1.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-7b4364a1.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-7e34f95b.js b/assets/index.html-7e34f95b.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-7e34f95b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-82cd7e71.js b/assets/index.html-82cd7e71.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-82cd7e71.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-83502a15.js b/assets/index.html-83502a15.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-83502a15.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-8758c156.js b/assets/index.html-8758c156.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-8758c156.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-88de6c6c.js b/assets/index.html-88de6c6c.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-88de6c6c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-890f2f8a.js b/assets/index.html-890f2f8a.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-890f2f8a.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-8ad2339d.js b/assets/index.html-8ad2339d.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-8ad2339d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-93fd52bc.js b/assets/index.html-93fd52bc.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-93fd52bc.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-94ca11d0.js b/assets/index.html-94ca11d0.js deleted file mode 100644 index 7f7097cf..00000000 --- a/assets/index.html-94ca11d0.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-987aca8e.js b/assets/index.html-987aca8e.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-987aca8e.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-98cb1677.js b/assets/index.html-98cb1677.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-98cb1677.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-99c4dd42.js b/assets/index.html-99c4dd42.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-99c4dd42.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9a40117b.js b/assets/index.html-9a40117b.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-9a40117b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9ac6dabf.js b/assets/index.html-9ac6dabf.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-9ac6dabf.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-78b91340.js b/assets/index.html-9c9d5c68.js similarity index 76% rename from assets/index.html-78b91340.js rename to assets/index.html-9c9d5c68.js index 7f7097cf..4c6ca55d 100644 --- a/assets/index.html-78b91340.js +++ b/assets/index.html-9c9d5c68.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9e4080be.js b/assets/index.html-9e4080be.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-9e4080be.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-9eba5acd.js b/assets/index.html-9eba5acd.js deleted file mode 100644 index 7f7097cf..00000000 --- a/assets/index.html-9eba5acd.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-a0bca378.js b/assets/index.html-a0bca378.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-a0bca378.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-a4e6ccfb.js b/assets/index.html-a4e6ccfb.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-a4e6ccfb.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-ab0c4e20.js b/assets/index.html-ab0c4e20.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-ab0c4e20.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-acfbcf8b.js b/assets/index.html-acfbcf8b.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-acfbcf8b.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-add8b707.js b/assets/index.html-add8b707.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-add8b707.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c023e196.js b/assets/index.html-c023e196.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-c023e196.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c279618d.js b/assets/index.html-c279618d.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-c279618d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c2feef9e.js b/assets/index.html-c2feef9e.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-c2feef9e.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c3cc9dcf.js b/assets/index.html-c3cc9dcf.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-c3cc9dcf.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-c44e132b.js b/assets/index.html-c44e132b.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-c44e132b.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-cafd4adb.js b/assets/index.html-cafd4adb.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-cafd4adb.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-cb2d6ac0.js b/assets/index.html-cb2d6ac0.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-cb2d6ac0.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-cca92841.js b/assets/index.html-cca92841.js deleted file mode 100644 index 7f7097cf..00000000 --- a/assets/index.html-cca92841.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-ccf4e4b1.js b/assets/index.html-ccf4e4b1.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-ccf4e4b1.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-cd4d0b35.js b/assets/index.html-cd4d0b35.js deleted file mode 100644 index 7f7097cf..00000000 --- a/assets/index.html-cd4d0b35.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-ce33adeb.js b/assets/index.html-ce33adeb.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-ce33adeb.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-d298086d.js b/assets/index.html-d298086d.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-d298086d.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-d3101c6f.js b/assets/index.html-d3101c6f.js deleted file mode 100644 index 7f7097cf..00000000 --- a/assets/index.html-d3101c6f.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-d7eb7ef3.js b/assets/index.html-d7eb7ef3.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-d7eb7ef3.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-de3413d0.js b/assets/index.html-de3413d0.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-de3413d0.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e3868ed7.js b/assets/index.html-e3868ed7.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-e3868ed7.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e4f2c1e9.js b/assets/index.html-e4f2c1e9.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-e4f2c1e9.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e538178c.js b/assets/index.html-e538178c.js deleted file mode 100644 index 7f7097cf..00000000 --- a/assets/index.html-e538178c.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-ee4d23bf.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e7690677.js b/assets/index.html-e7690677.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-e7690677.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-e82db359.js b/assets/index.html-e82db359.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-e82db359.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-eb66a122.js b/assets/index.html-eb66a122.js deleted file mode 100644 index 78e593a1..00000000 --- a/assets/index.html-eb66a122.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-ee4d23bf.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-eda7fbed.js b/assets/index.html-eda7fbed.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-eda7fbed.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-f4dbc26f.js b/assets/index.html-f4dbc26f.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-f4dbc26f.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-f661c803.js b/assets/index.html-f661c803.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-f661c803.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-fa943177.js b/assets/index.html-fa943177.js new file mode 100644 index 00000000..4c6ca55d --- /dev/null +++ b/assets/index.html-fa943177.js @@ -0,0 +1 @@ +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as n,c,d as r}from"./app-dcf5468f.js";const a={};function _(s,l){const e=t("AutoCatalog");return n(),c("div",null,[r(e)])}const f=o(a,[["render",_],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/index.html-fd0e4a7f.js b/assets/index.html-fd0e4a7f.js new file mode 100644 index 00000000..aa9ae73f --- /dev/null +++ b/assets/index.html-fd0e4a7f.js @@ -0,0 +1 @@ +import{_ as e}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c}from"./app-dcf5468f.js";const o={};function r(n,_){return t(),c("div")}const f=e(o,[["render",r],["__file","index.html.vue"]]);export{f as default}; diff --git a/assets/iteration-retrospective-of-sanyuan.html-03cce066.js b/assets/iteration-retrospective-of-sanyuan.html-774f41bd.js similarity index 70% rename from assets/iteration-retrospective-of-sanyuan.html-03cce066.js rename to assets/iteration-retrospective-of-sanyuan.html-774f41bd.js index 5b2b47b1..2ca03711 100644 --- a/assets/iteration-retrospective-of-sanyuan.html-03cce066.js +++ b/assets/iteration-retrospective-of-sanyuan.html-774f41bd.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-fd7b7f92","path":"/daily/iteration-retrospective-of-sanyuan.html","title":"迭代复盘之三员管理","lang":"zh-CN","frontmatter":{"date":"2023-09-08T00:00:00.000Z","tag":["Daily","Working Experience"],"description":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/iteration-retrospective-of-sanyuan.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"迭代复盘之三员管理"}],["meta",{"property":"og:description","content":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:published_time","content":"2023-09-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"迭代复盘之三员管理\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"动手前,至少梳理出接口清单","slug":"动手前-至少梳理出接口清单","link":"#动手前-至少梳理出接口清单","children":[]},{"level":2,"title":"迁移时,采取结队编程","slug":"迁移时-采取结队编程","link":"#迁移时-采取结队编程","children":[]},{"level":2,"title":"迁移后,需要对自己负责的功能设计测试用例","slug":"迁移后-需要对自己负责的功能设计测试用例","link":"#迁移后-需要对自己负责的功能设计测试用例","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.52,"words":756},"filePathRelative":"daily/iteration-retrospective-of-sanyuan.md","localizedDate":"2023年9月8日","excerpt":"

迭代复盘之三员管理

\\n

前言

\\n

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

\\n

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

\\n

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-fd7b7f92","path":"/daily/iteration-retrospective-of-sanyuan.html","title":"迭代复盘之三员管理","lang":"zh-CN","frontmatter":{"date":"2023-09-08T00:00:00.000Z","tag":["Daily","Working Experience"],"description":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/iteration-retrospective-of-sanyuan.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"迭代复盘之三员管理"}],["meta",{"property":"og:description","content":"迭代复盘之三员管理 前言 本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。 这次迭代因为各种原因,延期了快一个星期(周六还加了班)。 那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Working Experience"}],["meta",{"property":"article:published_time","content":"2023-09-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"迭代复盘之三员管理\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"动手前,至少梳理出接口清单","slug":"动手前-至少梳理出接口清单","link":"#动手前-至少梳理出接口清单","children":[]},{"level":2,"title":"迁移时,采取结队编程","slug":"迁移时-采取结队编程","link":"#迁移时-采取结队编程","children":[]},{"level":2,"title":"迁移后,需要对自己负责的功能设计测试用例","slug":"迁移后-需要对自己负责的功能设计测试用例","link":"#迁移后-需要对自己负责的功能设计测试用例","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.52,"words":756},"filePathRelative":"daily/iteration-retrospective-of-sanyuan.md","localizedDate":"2023年9月8日","excerpt":"

迭代复盘之三员管理

\\n

前言

\\n

本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

\\n

这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

\\n

那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/iteration-retrospective-of-sanyuan.html-4186c541.js b/assets/iteration-retrospective-of-sanyuan.html-e6a9971f.js similarity index 98% rename from assets/iteration-retrospective-of-sanyuan.html-4186c541.js rename to assets/iteration-retrospective-of-sanyuan.html-e6a9971f.js index 843e4404..1f200927 100644 --- a/assets/iteration-retrospective-of-sanyuan.html-4186c541.js +++ b/assets/iteration-retrospective-of-sanyuan.html-e6a9971f.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as i,f as o,a as e,b as a,e as n}from"./app-ee4d23bf.js";const d={},s=e("h1",{id:"迭代复盘之三员管理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#迭代复盘之三员管理","aria-hidden":"true"},"#"),a(" 迭代复盘之三员管理")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),l=e("p",null,"本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。",-1),h=e("p",null,"这次迭代因为各种原因,延期了快一个星期(周六还加了班)。",-1),p=e("p",null,"那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。",-1),_=n('

动手前,至少梳理出接口清单

太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

更优的工作流应该是:

  1. 先进行业务梳理
  2. 整理出接口清单
  3. 再进行代码迁移
  4. 根据接口清单,逐个验证迁移的代码是否符合需求

迁移时,采取结队编程

看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

迁移后,需要对自己负责的功能设计测试用例

这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

  1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
  2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

当然,由于时间的关系,做完善的测试一般都不太可能了。
对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

',13);function f(u,m){return t(),i("div",null,[s,c,l,h,p,o(" more "),_])}const v=r(d,[["render",f],["__file","iteration-retrospective-of-sanyuan.html.vue"]]);export{v as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as i,e as o,a as e,b as a,f as n}from"./app-dcf5468f.js";const d={},s=e("h1",{id:"迭代复盘之三员管理",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#迭代复盘之三员管理","aria-hidden":"true"},"#"),a(" 迭代复盘之三员管理")],-1),c=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),l=e("p",null,"本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。",-1),h=e("p",null,"这次迭代因为各种原因,延期了快一个星期(周六还加了班)。",-1),p=e("p",null,"那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。",-1),_=n('

动手前,至少梳理出接口清单

太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

更优的工作流应该是:

  1. 先进行业务梳理
  2. 整理出接口清单
  3. 再进行代码迁移
  4. 根据接口清单,逐个验证迁移的代码是否符合需求

迁移时,采取结队编程

看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

迁移后,需要对自己负责的功能设计测试用例

这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

  1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
  2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

当然,由于时间的关系,做完善的测试一般都不太可能了。
对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

',13);function f(u,m){return t(),i("div",null,[s,c,l,h,p,o(" more "),_])}const v=r(d,[["render",f],["__file","iteration-retrospective-of-sanyuan.html.vue"]]);export{v as default}; diff --git a/assets/learning-7000-words-task-completed.html-30a87853.js b/assets/learning-7000-words-task-completed.html-5cd182e1.js similarity index 62% rename from assets/learning-7000-words-task-completed.html-30a87853.js rename to assets/learning-7000-words-task-completed.html-5cd182e1.js index b53d5dd8..b2e9dc4a 100644 --- a/assets/learning-7000-words-task-completed.html-30a87853.js +++ b/assets/learning-7000-words-task-completed.html-5cd182e1.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-6ed7d996","path":"/english/learning-7000-words-task-completed.html","title":"完成刷7k单词任务","lang":"zh-CN","frontmatter":{"date":"2022-09-17T00:00:00.000Z","tag":"English","description":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。 再者,说明下为什么要做这件事。主要原因有三:","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/learning-7000-words-task-completed.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"完成刷7k单词任务"}],["meta",{"property":"og:description","content":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。 再者,说明下为什么要做这件事。主要原因有三:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"完成刷7k单词任务\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-17T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4.54,"words":1362},"filePathRelative":"english/learning-7000-words-task-completed.md","localizedDate":"2022年9月17日","excerpt":"

完成刷7k单词任务

\\n

\\"image.png\\"
\\n这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

\\n

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

\\n

再者,说明下为什么要做这件事。主要原因有三:

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-6ed7d996","path":"/english/learning-7000-words-task-completed.html","title":"完成刷7k单词任务","lang":"zh-CN","frontmatter":{"date":"2022-09-17T00:00:00.000Z","tag":"English","description":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。 再者,说明下为什么要做这件事。主要原因有三:","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/learning-7000-words-task-completed.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"完成刷7k单词任务"}],["meta",{"property":"og:description","content":"完成刷7k单词任务 这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。 首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。 再者,说明下为什么要做这件事。主要原因有三:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:published_time","content":"2022-09-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"完成刷7k单词任务\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-09-17T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.54,"words":1362},"filePathRelative":"english/learning-7000-words-task-completed.md","localizedDate":"2022年9月17日","excerpt":"

完成刷7k单词任务

\\n

\\"image.png\\"
\\n这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

\\n

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

\\n

再者,说明下为什么要做这件事。主要原因有三:

","autoDesc":true}');export{e as data}; diff --git a/assets/learning-7000-words-task-completed.html-72ba0d4d.js b/assets/learning-7000-words-task-completed.html-bce0206f.js similarity index 98% rename from assets/learning-7000-words-task-completed.html-72ba0d4d.js rename to assets/learning-7000-words-task-completed.html-bce0206f.js index 061dd455..567fb4ee 100644 --- a/assets/learning-7000-words-task-completed.html-72ba0d4d.js +++ b/assets/learning-7000-words-task-completed.html-bce0206f.js @@ -1 +1 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{o as l,c as e,e as r}from"./app-ee4d23bf.js";const p={},t=r('

完成刷7k单词任务

image.png
这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

再者,说明下为什么要做这件事。主要原因有三:

  1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
  2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
  3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

对单词的掌握程度区分优先级:

  • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
  • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
  • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
  • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

  • 结合相应的例句,一次只记一个意思
  • 常见的词意,优先记住
  • 冷门的词意,可以跳过
  • 另外,有些中文词意解释很牵强,可以找英英释义

对词汇进行分类掌握:识别出单词属于哪个领域的

  • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
  • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

',17),o=[t];function a(s,c){return l(),e("div",null,o)}const m=i(p,[["render",a],["__file","learning-7000-words-task-completed.html.vue"]]);export{m as default}; +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{o as l,c as e,f as r}from"./app-dcf5468f.js";const p={},t=r('

完成刷7k单词任务

image.png
这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

再者,说明下为什么要做这件事。主要原因有三:

  1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
  2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
  3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

对单词的掌握程度区分优先级:

  • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
  • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
  • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
  • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

  • 结合相应的例句,一次只记一个意思
  • 常见的词意,优先记住
  • 冷门的词意,可以跳过
  • 另外,有些中文词意解释很牵强,可以找英英释义

对词汇进行分类掌握:识别出单词属于哪个领域的

  • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
  • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

',17),o=[t];function a(s,c){return l(),e("div",null,o)}const m=i(p,[["render",a],["__file","learning-7000-words-task-completed.html.vue"]]);export{m as default}; diff --git a/assets/let-chatgpt-be-your-foreign-language-teacher.html-7de04010.js b/assets/let-chatgpt-be-your-foreign-language-teacher.html-058401da.js similarity index 98% rename from assets/let-chatgpt-be-your-foreign-language-teacher.html-7de04010.js rename to assets/let-chatgpt-be-your-foreign-language-teacher.html-058401da.js index 25c04e66..3ff1511e 100644 --- a/assets/let-chatgpt-be-your-foreign-language-teacher.html-7de04010.js +++ b/assets/let-chatgpt-be-your-foreign-language-teacher.html-058401da.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as l,c as g,a as e,b as a,d as r,w as m,e as n}from"./app-ee4d23bf.js";const p={},c=n('

让 ChatGPT 成为你的外语私教

前言

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

准备工作

在开始之前,要准备好几样东西:

',6),h={start:"0"},d={href:"https://chat.openai.com/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://chrome.google.com/webstore/detail/voice-control-for-chatgpt/eollffkcakegifhacjnlnegohfdlidhn",target:"_blank",rel:"noopener noreferrer"},_={href:"https://liuxue.koolearn.com/ielts/speak-1-44-0/",target:"_blank",rel:"noopener noreferrer"},b=n(`

安装好插件后,打开 chatGPT 界面,下方就会出现语音输入按钮。
image.png

常用Prompt

下面总结了常用的 Prompt,可以有根据需要进行使用或调整。

设置角色:

  1. Please act as an English teacher.
  2. Please act as an English-speaking test examiner.
  3. Please act as IELTS speaking test examiner.

进入一问一答模式:

  1. You're supposed to asked me questions and wait for my answer. The next question is: xxx

对回答进行完善:

  1. Please revise my answer
  2. Please modify my answer to make it more fluent

对回答进行评分:

  1. Please rate my answer

进行对话

第一句话,是设置好 AI 的角色,让它扮演口语考官。

可以使用以下 prompt:

act as an English-speaking test examiner
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{r as t,o as l,c as g,a as e,b as a,d as r,w as m,f as n}from"./app-dcf5468f.js";const p={},c=n('

让 ChatGPT 成为你的外语私教

前言

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

准备工作

在开始之前,要准备好几样东西:

',6),h={start:"0"},d={href:"https://chat.openai.com/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://chrome.google.com/webstore/detail/voice-control-for-chatgpt/eollffkcakegifhacjnlnegohfdlidhn",target:"_blank",rel:"noopener noreferrer"},_={href:"https://liuxue.koolearn.com/ielts/speak-1-44-0/",target:"_blank",rel:"noopener noreferrer"},b=n(`

安装好插件后,打开 chatGPT 界面,下方就会出现语音输入按钮。
image.png

常用Prompt

下面总结了常用的 Prompt,可以有根据需要进行使用或调整。

设置角色:

  1. Please act as an English teacher.
  2. Please act as an English-speaking test examiner.
  3. Please act as IELTS speaking test examiner.

进入一问一答模式:

  1. You're supposed to asked me questions and wait for my answer. The next question is: xxx

对回答进行完善:

  1. Please revise my answer
  2. Please modify my answer to make it more fluent

对回答进行评分:

  1. Please rate my answer

进行对话

第一句话,是设置好 AI 的角色,让它扮演口语考官。

可以使用以下 prompt:

act as an English-speaking test examiner
 

image.png
可以看出,语音转文字出现错误,单词 IELTS 始终未能正确识别,但 ChatGPT 却能明白其中的意思。

开启语音插件的意义在于,如果语音识别不了自己说的话,很有可能是自己的发音有问题,起到提醒自己纠正发音的作用。另外,ChatGPT 回复的文字,也会转换成语音输出,顺便练习了听力。

根据练习材料,让 AI 问自己问题。
image.png
记得让 AI 对自己的回答评分,可以使用以下 prompt:

please rate my answer after I answer the question each time
 
image.png
image.png

进行下一个问题:
image.png
上述回答不太好,AI 给出了理由:
image.png

修改后再回答,有所进步
image.png

再问下一个问题:
image.png
这个回答同样不理想,但看了提示也不知道要怎么改:
image.png

此时可以新建一个聊天窗口,让 AI 提供示例回答:
image.png

根据示例答案,结合关键词,重新组织语言,切换回原聊天窗口,再回答一次:
image.png
有所进步!

有时对话长了,AI 会“糊涂”如下所示:
image.png

此时要重新强调它扮演的角色,让其回忆起上下文,可以使用以下 prompt:

focus on the speaking test and assume that you ask me this question
 
image.png
image.png

除此之外,就没啥值得注意的了。重复上述过程,不断练习即可。

记录回答

做完了练习,还要作笔记。但在记录回答之前,还要润色一下,毕竟口语表达的时候,可能会存在语法错误。

`,32),f={href:"https://quillbot.com/",target:"_blank",rel:"noopener noreferrer"},v=e("br",null,null,-1),w=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1683211964246.png",alt:"image.png"},null,-1),k=e("br",null,null,-1),x=e("br",null,null,-1),y=e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/docs/english/1683211997332.png",alt:"image.png"},null,-1),P=e("br",null,null,-1);function I(T,C){const i=t("ExternalLinkIcon"),o=t("RouterLink");return l(),g("div",null,[c,e("ol",h,[e("li",null,[e("a",d,[a("ChatGPT"),r(i)]),a(", 如果没有账号或不能上网,请查看"),r(o,{to:"/tools/how-to-connect-to-internet.html"},{default:m(()=>[a("上网教程")]),_:1})]),e("li",null,[e("a",u,[a("Chrome 浏览器插件 voice-control-for-chatgpt"),r(i)])]),e("li",null,[a("口语练习题,根据个人需求查找即可,下文将以"),e("a",_,[a("雅思"),r(i)]),a("为例进行说明")])]),b,e("p",null,[a("进入 "),e("a",f,[a("https://quillbot.com/"),r(i)]),a(",把答案复制上去,先进行语法检查:"),v,w,k,a(" 再进行流畅度润色:"),x,y,P,a(" 最后保存到笔记本上即可。")])])}const L=s(p,[["render",I],["__file","let-chatgpt-be-your-foreign-language-teacher.html.vue"]]);export{L as default}; diff --git a/assets/let-chatgpt-be-your-foreign-language-teacher.html-ce9a8d3a.js b/assets/let-chatgpt-be-your-foreign-language-teacher.html-7a6652d7.js similarity index 63% rename from assets/let-chatgpt-be-your-foreign-language-teacher.html-ce9a8d3a.js rename to assets/let-chatgpt-be-your-foreign-language-teacher.html-7a6652d7.js index dd14f3dd..e704691b 100644 --- a/assets/let-chatgpt-be-your-foreign-language-teacher.html-ce9a8d3a.js +++ b/assets/let-chatgpt-be-your-foreign-language-teacher.html-7a6652d7.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-221efd1f","path":"/english/let-chatgpt-be-your-foreign-language-teacher.html","title":"让 ChatGPT 成为你的外语私教","lang":"zh-CN","frontmatter":{"date":"2023-05-06T00:00:00.000Z","tag":["English","AI"],"description":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/let-chatgpt-be-your-foreign-language-teacher.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"让 ChatGPT 成为你的外语私教"}],["meta",{"property":"og:description","content":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-05-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"让 ChatGPT 成为你的外语私教\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-06T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"准备工作","slug":"准备工作","link":"#准备工作","children":[]},{"level":2,"title":"常用Prompt","slug":"常用prompt","link":"#常用prompt","children":[]},{"level":2,"title":"进行对话","slug":"进行对话","link":"#进行对话","children":[]},{"level":2,"title":"记录回答","slug":"记录回答","link":"#记录回答","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.83,"words":849},"filePathRelative":"english/let-chatgpt-be-your-foreign-language-teacher.md","localizedDate":"2023年5月6日","excerpt":"

让 ChatGPT 成为你的外语私教

\\n

前言

\\n

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

\\n

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

\\n

准备工作

\\n

在开始之前,要准备好几样东西:

\\n
    \\n
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. \\n
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. \\n
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-221efd1f","path":"/english/let-chatgpt-be-your-foreign-language-teacher.html","title":"让 ChatGPT 成为你的外语私教","lang":"zh-CN","frontmatter":{"date":"2023-05-06T00:00:00.000Z","tag":["English","AI"],"description":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明","head":[["meta",{"property":"og:url","content":"https://levy.vip/english/let-chatgpt-be-your-foreign-language-teacher.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"让 ChatGPT 成为你的外语私教"}],["meta",{"property":"og:description","content":"让 ChatGPT 成为你的外语私教 前言 有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。 本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。 准备工作 在开始之前,要准备好几样东西: ChatGPT, 如果没有账号或不能上网,请查看上网教程 Chrome 浏览器插件 voice-control-for-chatgpt 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"English"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:published_time","content":"2023-05-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"让 ChatGPT 成为你的外语私教\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-06T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"准备工作","slug":"准备工作","link":"#准备工作","children":[]},{"level":2,"title":"常用Prompt","slug":"常用prompt","link":"#常用prompt","children":[]},{"level":2,"title":"进行对话","slug":"进行对话","link":"#进行对话","children":[]},{"level":2,"title":"记录回答","slug":"记录回答","link":"#记录回答","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.83,"words":849},"filePathRelative":"english/let-chatgpt-be-your-foreign-language-teacher.md","localizedDate":"2023年5月6日","excerpt":"

让 ChatGPT 成为你的外语私教

\\n

前言

\\n

有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

\\n

本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

\\n

准备工作

\\n

在开始之前,要准备好几样东西:

\\n
    \\n
  1. ChatGPT, 如果没有账号或不能上网,请查看上网教程
  2. \\n
  3. Chrome 浏览器插件 voice-control-for-chatgpt
  4. \\n
  5. 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明
  6. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/leverage-ai-to-boost-coding-productivity.html-851bce24.js b/assets/leverage-ai-to-boost-coding-productivity.html-306f73df.js similarity index 99% rename from assets/leverage-ai-to-boost-coding-productivity.html-851bce24.js rename to assets/leverage-ai-to-boost-coding-productivity.html-306f73df.js index 41fe9785..51534a78 100644 --- a/assets/leverage-ai-to-boost-coding-productivity.html-851bce24.js +++ b/assets/leverage-ai-to-boost-coding-productivity.html-306f73df.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,e as t}from"./app-ee4d23bf.js";const p={},e=t(`

都什么年代了,还在用传统方式写代码?

前言

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

开发流程

软件的一般研发流程为:

  1. 需求分析
  2. 程序设计
  3. 代码编写
  4. 软件测试
  5. 部署上线

我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

程序设计

经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

我们来看一下伪代码示例:

plainText = JSON.stringify(data)
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as t}from"./app-dcf5468f.js";const p={},e=t(`

都什么年代了,还在用传统方式写代码?

前言

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

开发流程

软件的一般研发流程为:

  1. 需求分析
  2. 程序设计
  3. 代码编写
  4. 软件测试
  5. 部署上线

我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

程序设计

经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

我们来看一下伪代码示例:

plainText = JSON.stringify(data)
 digest = hash(plainText) // 防篡改
 secret = Symmetric.getKey()  
 cipherText = encryptText(data, secret) // 防内容泄密
diff --git a/assets/leverage-ai-to-boost-coding-productivity.html-e400b847.js b/assets/leverage-ai-to-boost-coding-productivity.html-f7cbca20.js
similarity index 70%
rename from assets/leverage-ai-to-boost-coding-productivity.html-e400b847.js
rename to assets/leverage-ai-to-boost-coding-productivity.html-f7cbca20.js
index fb3c35e1..5f30ec2e 100644
--- a/assets/leverage-ai-to-boost-coding-productivity.html-e400b847.js
+++ b/assets/leverage-ai-to-boost-coding-productivity.html-f7cbca20.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-81a71778","path":"/daily/leverage-ai-to-boost-coding-productivity.html","title":"都什么年代了,还在用传统方式写代码?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["AI","Daily"],"description":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/leverage-ai-to-boost-coding-productivity.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"都什么年代了,还在用传统方式写代码?"}],["meta",{"property":"og:description","content":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"都什么年代了,还在用传统方式写代码?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"开发流程","slug":"开发流程","link":"#开发流程","children":[]},{"level":2,"title":"程序设计","slug":"程序设计","link":"#程序设计","children":[]},{"level":2,"title":"代码编写","slug":"代码编写","link":"#代码编写","children":[{"level":3,"title":"生成真实代码","slug":"生成真实代码","link":"#生成真实代码","children":[]},{"level":3,"title":"辅助编程工具","slug":"辅助编程工具","link":"#辅助编程工具","children":[]}]},{"level":2,"title":"软件测试","slug":"软件测试","link":"#软件测试","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"附:CodeWhisperer 安装","slug":"附-codewhisperer-安装","link":"#附-codewhisperer-安装","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":7.37,"words":2212},"filePathRelative":"daily/leverage-ai-to-boost-coding-productivity.md","localizedDate":"2023年8月26日","excerpt":"

都什么年代了,还在用传统方式写代码?

\\n

前言

\\n

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

\\n

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

\\n

本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-81a71778","path":"/daily/leverage-ai-to-boost-coding-productivity.html","title":"都什么年代了,还在用传统方式写代码?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["AI","Daily"],"description":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/leverage-ai-to-boost-coding-productivity.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"都什么年代了,还在用传统方式写代码?"}],["meta",{"property":"og:description","content":"都什么年代了,还在用传统方式写代码? 前言 还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。 本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。 本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"都什么年代了,还在用传统方式写代码?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"开发流程","slug":"开发流程","link":"#开发流程","children":[]},{"level":2,"title":"程序设计","slug":"程序设计","link":"#程序设计","children":[]},{"level":2,"title":"代码编写","slug":"代码编写","link":"#代码编写","children":[{"level":3,"title":"生成真实代码","slug":"生成真实代码","link":"#生成真实代码","children":[]},{"level":3,"title":"辅助编程工具","slug":"辅助编程工具","link":"#辅助编程工具","children":[]}]},{"level":2,"title":"软件测试","slug":"软件测试","link":"#软件测试","children":[]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"附:CodeWhisperer 安装","slug":"附-codewhisperer-安装","link":"#附-codewhisperer-安装","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":7.37,"words":2212},"filePathRelative":"daily/leverage-ai-to-boost-coding-productivity.md","localizedDate":"2023年8月26日","excerpt":"

都什么年代了,还在用传统方式写代码?

\\n

前言

\\n

还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

\\n

本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

\\n

本文将分享 AI 时代的编程新实践,教你如何从一个 \\"Ctrl + C\\"、 \\"Ctrl + V\\" 工程师,变成一个 \\"Tab + Enter\\" 工程师🤣。

","autoDesc":true}');export{e as data}; diff --git a/assets/llm-with-recordation-review-of-regulations.html-46d4f78c.js b/assets/llm-with-recordation-review-of-regulations.html-46d4f78c.js new file mode 100644 index 00000000..f69621f9 --- /dev/null +++ b/assets/llm-with-recordation-review-of-regulations.html-46d4f78c.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5d5f3b26","path":"/llm/llm-with-recordation-review-of-regulations.html","title":"大语言模型赋能备案审查","lang":"zh-CN","frontmatter":{"date":"2024-05-29T00:00:00.000Z","tag":["llm"],"description":"大语言模型赋能备案审查 业务背景 备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。 在这个过程中,最重要的就是确保下位法不会与上位法相抵触。 因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。 核心流程","head":[["meta",{"property":"og:url","content":"https://levy.vip/llm/llm-with-recordation-review-of-regulations.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"大语言模型赋能备案审查"}],["meta",{"property":"og:description","content":"大语言模型赋能备案审查 业务背景 备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。 在这个过程中,最重要的就是确保下位法不会与上位法相抵触。 因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。 核心流程"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"llm"}],["meta",{"property":"article:published_time","content":"2024-05-29T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"大语言模型赋能备案审查\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2024-05-29T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"业务背景","slug":"业务背景","link":"#业务背景","children":[]},{"level":2,"title":"核心流程","slug":"核心流程","link":"#核心流程","children":[]},{"level":2,"title":"项目难点","slug":"项目难点","link":"#项目难点","children":[]},{"level":2,"title":"经验总结","slug":"经验总结","link":"#经验总结","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.87,"words":561},"filePathRelative":"llm/llm-with-recordation-review-of-regulations.md","localizedDate":"2024年5月29日","excerpt":"

大语言模型赋能备案审查

\\n

业务背景

\\n

备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
\\n在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
\\n因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

\\n

核心流程

\\n
\\"\\"
","autoDesc":true}');export{e as data}; diff --git a/assets/llm-with-recordation-review-of-regulations.html-4ba9dc58.js b/assets/llm-with-recordation-review-of-regulations.html-4ba9dc58.js new file mode 100644 index 00000000..f431860d --- /dev/null +++ b/assets/llm-with-recordation-review-of-regulations.html-4ba9dc58.js @@ -0,0 +1 @@ +import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as l,a as e,b as r,d as t,f as d}from"./app-dcf5468f.js";const h={},s=d('

大语言模型赋能备案审查

业务背景

备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

核心流程

项目难点

  1. 海量文件,必须自动化分片,不可能人工处理,也即手工插入分隔符是不可行的

解决方案:写代码处理

  1. 通过下位法法条,能找到对应的上位法法条

解决方案:在分片质量有所保证的提前下,在常规语义检索的基础上,通过结构化数据筛选,提高匹配率

  1. 判断下位法是否与上位法相抵触

解决方案:参考经典案例,学习其判断逻辑;指示AI,无法判断时不要乱下结论。

经验总结

数据很重要,一定要了解业务数据。

数据要分类:

  1. 参考的数据要与验证的数据区分开来。一定要在前期就找客户要参考案例,并问清楚如何验证。问清楚后,要么让客户给验证数据,要么自己造验证数据,千万不能连怎么验证都不知道就动手。
  2. 基于场景分类,如下位法对上位法范围扩大、范围缩小,它们就要区分开来。

参考资料

',17),c={href:"https://baike.baidu.com/item/%E4%B8%8A%E4%BD%8D%E6%B3%95",target:"_blank",rel:"noopener noreferrer"},f=e("br",null,null,-1),p={href:"https://wap.zuel.edu.cn/2023/0615/c1236a337820/pagem.htm",target:"_blank",rel:"noopener noreferrer"},u=e("br",null,null,-1),_={href:"https://www.gdpc.gov.cn/gdrdw/rdzt/bascalxb/",target:"_blank",rel:"noopener noreferrer"},g=e("br",null,null,-1),b=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/1716934571936-866c2e58-59e4-4c4a-aae6-4c2b54624d7d.png#averageHue=%23f8f3f1&clientId=ue4d82166-db4f-4&from=paste&height=385&id=u86ef6742&originHeight=578&originWidth=1397&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=168640&status=done&style=none&taskId=ud877cee2-9be7-4ce6-a968-98c30e15f45&title=&width=931.3333333333334",alt:""},null,-1);function m(x,w){const a=n("ExternalLinkIcon");return o(),l("div",null,[s,e("p",null,[e("a",c,[r("什么是上位法"),t(a)]),f,e("a",p,[r("论大语言模型在规范性文件备案审查中的应用"),t(a)]),u,e("a",_,[r("备案审查案例选编"),t(a)]),g,b])])}const B=i(h,[["render",m],["__file","llm-with-recordation-review-of-regulations.html.vue"]]);export{B as default}; diff --git a/assets/mr.py.html-d220188b.js b/assets/mr.py.html-1198e72d.js similarity index 99% rename from assets/mr.py.html-d220188b.js rename to assets/mr.py.html-1198e72d.js index d7394bbf..40348d8d 100644 --- a/assets/mr.py.html-d220188b.js +++ b/assets/mr.py.html-1198e72d.js @@ -1,4 +1,4 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as c,c as i,a as n,b as s,d as a,w as l,f as u,e as r}from"./app-ee4d23bf.js";const k={},d={id:"mr-py",tabindex:"-1"},v=n("a",{class:"header-anchor",href:"#mr-py","aria-hidden":"true"},"#",-1),m={href:"http://mr.py",target:"_blank",rel:"noopener noreferrer"},b={id:"源文件-main-py",tabindex:"-1"},g=n("a",{class:"header-anchor",href:"#源文件-main-py","aria-hidden":"true"},"#",-1),_={href:"http://main.py",target:"_blank",rel:"noopener noreferrer"},f=r(`
import re
+import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as c,c as i,a as n,b as s,d as a,w as l,e as u,f as r}from"./app-dcf5468f.js";const k={},d={id:"mr-py",tabindex:"-1"},v=n("a",{class:"header-anchor",href:"#mr-py","aria-hidden":"true"},"#",-1),m={href:"http://mr.py",target:"_blank",rel:"noopener noreferrer"},b={id:"源文件-main-py",tabindex:"-1"},g=n("a",{class:"header-anchor",href:"#源文件-main-py","aria-hidden":"true"},"#",-1),_={href:"http://main.py",target:"_blank",rel:"noopener noreferrer"},f=r(`
import re
 import sys
 import os
 import requests
diff --git a/assets/mr.py.html-416ec43a.js b/assets/mr.py.html-c2cab192.js
similarity index 65%
rename from assets/mr.py.html-416ec43a.js
rename to assets/mr.py.html-c2cab192.js
index fecff3d3..2e5e8406 100644
--- a/assets/mr.py.html-416ec43a.js
+++ b/assets/mr.py.html-c2cab192.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-966d933e","path":"/python/mr.py.html","title":"mr.py","lang":"zh-CN","frontmatter":{"date":"2023-06-05T00:00:00.000Z","tag":["Git","Python"],"description":"mr.py 操作 Gitlab MR 的命令行工具的源码与测试代码。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/mr.py.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"mr.py"}],["meta",{"property":"og:description","content":"mr.py 操作 Gitlab MR 的命令行工具的源码与测试代码。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-06-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"mr.py\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-06-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"源文件:main.py","slug":"源文件-main-py","link":"#源文件-main-py","children":[]},{"level":2,"title":"测试文件:test/test_mr.py","slug":"测试文件-test-test-mr-py","link":"#测试文件-test-test-mr-py","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5,"words":1501},"filePathRelative":"python/mr.py.md","localizedDate":"2023年6月5日","excerpt":"

mr.py

\\n

操作 Gitlab MR 的命令行工具的源码与测试代码。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-966d933e","path":"/python/mr.py.html","title":"mr.py","lang":"zh-CN","frontmatter":{"date":"2023-06-05T00:00:00.000Z","tag":["Git","Python"],"description":"mr.py 操作 Gitlab MR 的命令行工具的源码与测试代码。","head":[["meta",{"property":"og:url","content":"https://levy.vip/python/mr.py.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"mr.py"}],["meta",{"property":"og:description","content":"mr.py 操作 Gitlab MR 的命令行工具的源码与测试代码。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-06-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"mr.py\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-06-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"源文件:main.py","slug":"源文件-main-py","link":"#源文件-main-py","children":[]},{"level":2,"title":"测试文件:test/test_mr.py","slug":"测试文件-test-test-mr-py","link":"#测试文件-test-test-mr-py","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5,"words":1501},"filePathRelative":"python/mr.py.md","localizedDate":"2023年6月5日","excerpt":"

mr.py

\\n

操作 Gitlab MR 的命令行工具的源码与测试代码。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/mysql-backup-case-study-mysqldump-in-action.html-60a8f15f.js b/assets/mysql-backup-case-study-mysqldump-in-action.html-d41bb3be.js similarity index 69% rename from assets/mysql-backup-case-study-mysqldump-in-action.html-60a8f15f.js rename to assets/mysql-backup-case-study-mysqldump-in-action.html-d41bb3be.js index 1286e150..71640c93 100644 --- a/assets/mysql-backup-case-study-mysqldump-in-action.html-60a8f15f.js +++ b/assets/mysql-backup-case-study-mysqldump-in-action.html-d41bb3be.js @@ -1 +1 @@ -const e=JSON.parse(`{"key":"v-143a8bce","path":"/mysql/mysql-backup-case-study-mysqldump-in-action.html","title":"数据备份案例:mysqldump实战","lang":"zh-CN","frontmatter":{"date":"2023-08-17T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据备份案例:mysqldump实战"}],["meta",{"property":"og:description","content":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据备份案例:mysqldump实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-17T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"架构","slug":"架构","link":"#架构","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"导出","slug":"导出","link":"#导出","children":[{"level":3,"title":"--set-gtid-purged=OFF","slug":"set-gtid-purged-off","link":"#set-gtid-purged-off","children":[]},{"level":3,"title":"--ignore-table","slug":"ignore-table","link":"#ignore-table","children":[]}]},{"level":2,"title":"导入","slug":"导入","link":"#导入","children":[]},{"level":2,"title":"为什么不?","slug":"为什么不","link":"#为什么不","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4.1,"words":1231},"filePathRelative":"mysql/mysql-backup-case-study-mysqldump-in-action.md","localizedDate":"2023年8月17日","excerpt":"

数据备份案例:mysqldump实战

\\n

背景

\\n

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

\\n

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

\\n

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

\\n","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-143a8bce","path":"/mysql/mysql-backup-case-study-mysqldump-in-action.html","title":"数据备份案例:mysqldump实战","lang":"zh-CN","frontmatter":{"date":"2023-08-17T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据备份案例:mysqldump实战"}],["meta",{"property":"og:description","content":"数据备份案例:mysqldump实战 背景 前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。 并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。 在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-17T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据备份案例:mysqldump实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-17T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"架构","slug":"架构","link":"#架构","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"导出","slug":"导出","link":"#导出","children":[{"level":3,"title":"--set-gtid-purged=OFF","slug":"set-gtid-purged-off","link":"#set-gtid-purged-off","children":[]},{"level":3,"title":"--ignore-table","slug":"ignore-table","link":"#ignore-table","children":[]}]},{"level":2,"title":"导入","slug":"导入","link":"#导入","children":[]},{"level":2,"title":"为什么不?","slug":"为什么不","link":"#为什么不","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.1,"words":1231},"filePathRelative":"mysql/mysql-backup-case-study-mysqldump-in-action.md","localizedDate":"2023年8月17日","excerpt":"

数据备份案例:mysqldump实战

\\n

背景

\\n

前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

\\n

并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

\\n

在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

\\n","autoDesc":true}`);export{e as data}; diff --git a/assets/mysql-backup-case-study-mysqldump-in-action.html-15a436a3.js b/assets/mysql-backup-case-study-mysqldump-in-action.html-fb973b11.js similarity index 98% rename from assets/mysql-backup-case-study-mysqldump-in-action.html-15a436a3.js rename to assets/mysql-backup-case-study-mysqldump-in-action.html-fb973b11.js index 376a50b0..7827ef16 100644 --- a/assets/mysql-backup-case-study-mysqldump-in-action.html-15a436a3.js +++ b/assets/mysql-backup-case-study-mysqldump-in-action.html-fb973b11.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c as t,a,b as s,d as r,w as o,f as p,e as d}from"./app-ee4d23bf.js";const c={},u=a("h1",{id:"数据备份案例-mysqldump实战",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#数据备份案例-mysqldump实战","aria-hidden":"true"},"#"),s(" 数据备份案例:mysqldump实战")],-1),m=a("h2",{id:"背景",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),s(" 背景")],-1),b=a("p",null,"并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。",-1),h=a("p",null,"在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。",-1),v=d(`

架构


注意到:

  1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
  2. 关于 sql 的编写在另一文中已有提及,就不重复了
  3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

安装

首先要在跳板机安装 mysqldump。

如果够幸运,可以用包管理工具安装:

sudo apt update
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as l,c as t,a,b as s,d as r,w as o,e as p,f as d}from"./app-dcf5468f.js";const c={},u=a("h1",{id:"数据备份案例-mysqldump实战",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#数据备份案例-mysqldump实战","aria-hidden":"true"},"#"),s(" 数据备份案例:mysqldump实战")],-1),m=a("h2",{id:"背景",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),s(" 背景")],-1),b=a("p",null,"并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。",-1),h=a("p",null,"在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。",-1),v=d(`

架构


注意到:

  1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
  2. 关于 sql 的编写在另一文中已有提及,就不重复了
  3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

安装

首先要在跳板机安装 mysqldump。

如果够幸运,可以用包管理工具安装:

sudo apt update
 
 sudo apt install mysql-client
 
sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
diff --git a/assets/mysql-data-migration-case-study-add-auto-increment.html-9c104292.js b/assets/mysql-data-migration-case-study-add-auto-increment.html-17bba961.js
similarity index 98%
rename from assets/mysql-data-migration-case-study-add-auto-increment.html-9c104292.js
rename to assets/mysql-data-migration-case-study-add-auto-increment.html-17bba961.js
index 2436888f..015024dd 100644
--- a/assets/mysql-data-migration-case-study-add-auto-increment.html-9c104292.js
+++ b/assets/mysql-data-migration-case-study-add-auto-increment.html-17bba961.js
@@ -1,4 +1,4 @@
-import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,f as c,a as n,b as s,d as i,w as r,e as a}from"./app-ee4d23bf.js";const d={},u=a('

数据迁移案例:表AUTO_INCREMENT加10w

背景

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

问题分析:

  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。

迁移思路:

  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  3. 记得动手前先确保数据已备份

注意:

  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
',9),k=n("h2",{id:"备份",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#备份","aria-hidden":"true"},"#"),s(" 备份")],-1),m=a(`

SQL编写

mysql-a 可以先 insert:

insert into table_a(id, other_fields...)
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,e as c,a as n,b as s,d as i,w as r,f as a}from"./app-dcf5468f.js";const d={},u=a('

数据迁移案例:表AUTO_INCREMENT加10w

背景

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

问题分析:

  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。

迁移思路:

  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  3. 记得动手前先确保数据已备份

注意:

  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
',9),k=n("h2",{id:"备份",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#备份","aria-hidden":"true"},"#"),s(" 备份")],-1),m=a(`

SQL编写

mysql-a 可以先 insert:

insert into table_a(id, other_fields...)
 select id+100000, other_fields... from table_a
 where is_deleted=0;
 

再导出新增的数据:

select * 
diff --git a/assets/mysql-data-migration-case-study-add-auto-increment.html-09d1faec.js b/assets/mysql-data-migration-case-study-add-auto-increment.html-c5a554fd.js
similarity index 67%
rename from assets/mysql-data-migration-case-study-add-auto-increment.html-09d1faec.js
rename to assets/mysql-data-migration-case-study-add-auto-increment.html-c5a554fd.js
index 39aab696..5ee3092e 100644
--- a/assets/mysql-data-migration-case-study-add-auto-increment.html-09d1faec.js
+++ b/assets/mysql-data-migration-case-study-add-auto-increment.html-c5a554fd.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-7e2c7a0c","path":"/mysql/mysql-data-migration-case-study-add-auto-increment.html","title":"数据迁移案例:表AUTO_INCREMENT加10w","lang":"zh-CN","frontmatter":{"date":"2023-08-16T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意: mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-data-migration-case-study-add-auto-increment.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据迁移案例:表AUTO_INCREMENT加10w"}],["meta",{"property":"og:description","content":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意: mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-16T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据迁移案例:表AUTO_INCREMENT加10w\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-16T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"备份","slug":"备份","link":"#备份","children":[]},{"level":2,"title":"SQL编写","slug":"sql编写","link":"#sql编写","children":[]},{"level":2,"title":"存储过程(可复用","slug":"存储过程-可复用","link":"#存储过程-可复用","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2,"words":599},"filePathRelative":"mysql/mysql-data-migration-case-study-add-auto-increment.md","localizedDate":"2023年8月16日","excerpt":"

数据迁移案例:表AUTO_INCREMENT加10w

\\n

背景

\\n

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

\\n

问题分析:

\\n
    \\n
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. \\n
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. \\n
\\n

迁移思路:

\\n
    \\n
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. \\n
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. \\n
  5. 记得动手前先确保数据已备份
  6. \\n
\\n

注意:

\\n
    \\n
  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • \\n
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
  • \\n
\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-7e2c7a0c","path":"/mysql/mysql-data-migration-case-study-add-auto-increment.html","title":"数据迁移案例:表AUTO_INCREMENT加10w","lang":"zh-CN","frontmatter":{"date":"2023-08-16T00:00:00.000Z","tag":["Daily","MySQL"],"description":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意: mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-data-migration-case-study-add-auto-increment.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"数据迁移案例:表AUTO_INCREMENT加10w"}],["meta",{"property":"og:description","content":"数据迁移案例:表AUTO_INCREMENT加10w 背景 项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。 问题分析: 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。 迁移思路: 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条) mysql-b 的相应的表 AUTO_INCREMENT 加 10w 记得动手前先确保数据已备份 注意: mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-16T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"数据迁移案例:表AUTO_INCREMENT加10w\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-16T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"备份","slug":"备份","link":"#备份","children":[]},{"level":2,"title":"SQL编写","slug":"sql编写","link":"#sql编写","children":[]},{"level":2,"title":"存储过程(可复用","slug":"存储过程-可复用","link":"#存储过程-可复用","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2,"words":599},"filePathRelative":"mysql/mysql-data-migration-case-study-add-auto-increment.md","localizedDate":"2023年8月16日","excerpt":"

数据迁移案例:表AUTO_INCREMENT加10w

\\n

背景

\\n

项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

\\n

问题分析:

\\n
    \\n
  1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
  2. \\n
  3. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。
  4. \\n
\\n

迁移思路:

\\n
    \\n
  1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
  2. \\n
  3. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
  4. \\n
  5. 记得动手前先确保数据已备份
  6. \\n
\\n

注意:

\\n
    \\n
  • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
  • \\n
  • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。
  • \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-34931763.js b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-29a4e446.js similarity index 67% rename from assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-34931763.js rename to assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-29a4e446.js index d2f6fc7c..b7236fe3 100644 --- a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-34931763.js +++ b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-29a4e446.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-f297935a","path":"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html","title":"MySQL 命令行执行SQL的细节","lang":"zh-CN","frontmatter":{"date":"2023-08-19T00:00:00.000Z","tag":["Daily","MySQL"],"description":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"MySQL 命令行执行SQL的细节"}],["meta",{"property":"og:description","content":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-19T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"MySQL 命令行执行SQL的细节\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-19T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"环境说明","slug":"环境说明","link":"#环境说明","children":[]},{"level":2,"title":"执行SQL文件","slug":"执行sql文件","link":"#执行sql文件","children":[]},{"level":2,"title":"复制粘贴执行","slug":"复制粘贴执行","link":"#复制粘贴执行","children":[]},{"level":2,"title":"如果要删除错误的数据怎么办?","slug":"如果要删除错误的数据怎么办","link":"#如果要删除错误的数据怎么办","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.02,"words":906},"filePathRelative":"mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.md","localizedDate":"2023年8月19日","excerpt":"

MySQL 命令行执行SQL的细节

\\n

背景

\\n

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

\\n

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

\\n

环境说明

\\n

先说明下我们的环境信息。
\\n\\"\\"
\\n我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-f297935a","path":"/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html","title":"MySQL 命令行执行SQL的细节","lang":"zh-CN","frontmatter":{"date":"2023-08-19T00:00:00.000Z","tag":["Daily","MySQL"],"description":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。","head":[["meta",{"property":"og:url","content":"https://levy.vip/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"MySQL 命令行执行SQL的细节"}],["meta",{"property":"og:description","content":"MySQL 命令行执行SQL的细节 背景 经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了! 但要小心,在正式环境中执行 SQL,也许会有意想不到的坑! 环境说明 先说明下我们的环境信息。 我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"MySQL"}],["meta",{"property":"article:published_time","content":"2023-08-19T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"MySQL 命令行执行SQL的细节\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-19T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"环境说明","slug":"环境说明","link":"#环境说明","children":[]},{"level":2,"title":"执行SQL文件","slug":"执行sql文件","link":"#执行sql文件","children":[]},{"level":2,"title":"复制粘贴执行","slug":"复制粘贴执行","link":"#复制粘贴执行","children":[]},{"level":2,"title":"如果要删除错误的数据怎么办?","slug":"如果要删除错误的数据怎么办","link":"#如果要删除错误的数据怎么办","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.02,"words":906},"filePathRelative":"mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.md","localizedDate":"2023年8月19日","excerpt":"

MySQL 命令行执行SQL的细节

\\n

背景

\\n

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

\\n

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

\\n

环境说明

\\n

先说明下我们的环境信息。
\\n\\"\\"
\\n我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

","autoDesc":true}');export{e as data}; diff --git a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-f71636f6.js b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-47e85e35.js similarity index 99% rename from assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-f71636f6.js rename to assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-47e85e35.js index e32c2178..ed21009d 100644 --- a/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-f71636f6.js +++ b/assets/mysql-details-you-should-know-when-execute-sql-in-command-line.html-47e85e35.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as o,a as e,b as a,d as i,e as s}from"./app-ee4d23bf.js";const p={},c=s(`

MySQL 命令行执行SQL的细节

背景

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

环境说明

先说明下我们的环境信息。

我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

则我们执行SQL语句的方式有两种:

  1. 执行导出的SQL语句文件
  2. 复制粘贴SQL语句执行

当然,在执行前,我们要确保已进行了数据备份。

执行SQL文件

执行SQL文件是最简单的,一般实践也是在命令行批量执行SQL文件。

相关的命令与恢复备份的命令一致:

mysql -h your-ip -u your-username -p\${password} your-database <  script.sql
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as o,a as e,b as a,d as i,f as s}from"./app-dcf5468f.js";const p={},c=s(`

MySQL 命令行执行SQL的细节

背景

经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

环境说明

先说明下我们的环境信息。

我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

则我们执行SQL语句的方式有两种:

  1. 执行导出的SQL语句文件
  2. 复制粘贴SQL语句执行

当然,在执行前,我们要确保已进行了数据备份。

执行SQL文件

执行SQL文件是最简单的,一般实践也是在命令行批量执行SQL文件。

相关的命令与恢复备份的命令一致:

mysql -h your-ip -u your-username -p\${password} your-database <  script.sql
 

这是推荐的方式,因为执行语句一旦出错,就会停下,并告知是第几行的语句出错。

但出于某些原因,你可能不想把所有SQL语句都合并到一个 script.sql 文件中。
另外,上传文件到跳板机,可能也比较麻烦,于是,你想采用第二种方式。

复制粘贴执行

通过 mysql 客户端直接上 MySQL 后,在命令上执行 SQL 语句会有一个问题:错误的语句不会中断后续的执行。

更可怕的是在命令行里,很可能你SQL语句包括的中文字符串会被过滤掉,变成空字符串。如:

  • '创建人' -> ''
  • '中英en混杂' -> 'en'

这真是血的教训😭。

我们先来看下错误是否中断的实验。

新建一个只有两行的SQL文件,其中第一行语句是错误的。

ss;
 select 1;
 

使用导入命令:

mysql -h your-ip -u your-username -p\${password} your-database < test.sql
diff --git a/assets/old-articles.html-55425101.js b/assets/old-articles.html-55425101.js
deleted file mode 100644
index de9bcd02..00000000
--- a/assets/old-articles.html-55425101.js
+++ /dev/null
@@ -1 +0,0 @@
-const e=JSON.parse('{"key":"v-72e84a92","path":"/frontend/old-articles.html","title":"旧文章精选","lang":"zh-CN","frontmatter":{"date":"2019-09-03T00:00:00.000Z","tag":"Frontend","description":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/old-articles.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"旧文章精选"}],["meta",{"property":"og:description","content":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2019-09-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"旧文章精选\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-09-03T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.3,"words":90},"filePathRelative":"frontend/old-articles.md","localizedDate":"2019年9月3日","excerpt":"

旧文章精选

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/old-articles.html-6516a558.js b/assets/old-articles.html-6516a558.js new file mode 100644 index 00000000..078ffffd --- /dev/null +++ b/assets/old-articles.html-6516a558.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-72e84a92","path":"/frontend/old-articles.html","title":"旧文章精选","lang":"zh-CN","frontmatter":{"date":"2019-09-03T00:00:00.000Z","tag":"Frontend","description":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/old-articles.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"旧文章精选"}],["meta",{"property":"og:description","content":"旧文章精选 📦vue组件发布npm最佳实践 🔨揭秘vue-sfc-cli:组件研发利器 🚀Github集成TravisCI:自动发布 ⚡Github集成Netlify:快速预览PR 🌪自动化的Github Workflow 🤖如何写一个GithubApp 🔒免费开启HTTPS 🕸捕获与改写HTTPS请求"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2019-09-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"旧文章精选\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-09-03T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.3,"words":90},"filePathRelative":"frontend/old-articles.md","localizedDate":"2019年9月3日","excerpt":"

旧文章精选

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/old-articles.html-13960583.js b/assets/old-articles.html-68158fb3.js similarity index 96% rename from assets/old-articles.html-13960583.js rename to assets/old-articles.html-68158fb3.js index a690e9c9..a5191bb3 100644 --- a/assets/old-articles.html-13960583.js +++ b/assets/old-articles.html-68158fb3.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as i,a as e,b as r,d as o}from"./app-ee4d23bf.js";const a={},h=e("h1",{id:"旧文章精选",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#旧文章精选","aria-hidden":"true"},"#"),r(" 旧文章精选")],-1),c={href:"https://github.com/levy9527/blog/issues/2",target:"_blank",rel:"noopener noreferrer"},_={href:"https://github.com/levy9527/blog/issues/7",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/levy9527/blog/issues/1",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/levy9527/blog/issues/4",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/levy9527/blog/issues/12",target:"_blank",rel:"noopener noreferrer"},p={href:"https://github.com/levy9527/blog/issues/10",target:"_blank",rel:"noopener noreferrer"},d={href:"https://github.com/levy9527/blog/issues/5",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/levy9527/blog/issues/11",target:"_blank",rel:"noopener noreferrer"};function m(v,k){const t=n("ExternalLinkIcon");return s(),i("div",null,[h,e("ul",null,[e("li",null,[e("a",c,[r("📦vue组件发布npm最佳实践"),o(t)])]),e("li",null,[e("a",_,[r("🔨揭秘vue-sfc-cli:组件研发利器"),o(t)])]),e("li",null,[e("a",u,[r("🚀Github集成TravisCI:自动发布"),o(t)])]),e("li",null,[e("a",b,[r("⚡Github集成Netlify:快速预览PR"),o(t)])]),e("li",null,[e("a",f,[r("🌪自动化的Github Workflow"),o(t)])]),e("li",null,[e("a",p,[r("🤖如何写一个GithubApp"),o(t)])]),e("li",null,[e("a",d,[r("🔒免费开启HTTPS"),o(t)])]),e("li",null,[e("a",g,[r("🕸捕获与改写HTTPS请求"),o(t)])])])])}const T=l(a,[["render",m],["__file","old-articles.html.vue"]]);export{T as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as i,a as e,b as r,d as o}from"./app-dcf5468f.js";const a={},h=e("h1",{id:"旧文章精选",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#旧文章精选","aria-hidden":"true"},"#"),r(" 旧文章精选")],-1),c={href:"https://github.com/levy9527/blog/issues/2",target:"_blank",rel:"noopener noreferrer"},_={href:"https://github.com/levy9527/blog/issues/7",target:"_blank",rel:"noopener noreferrer"},u={href:"https://github.com/levy9527/blog/issues/1",target:"_blank",rel:"noopener noreferrer"},b={href:"https://github.com/levy9527/blog/issues/4",target:"_blank",rel:"noopener noreferrer"},f={href:"https://github.com/levy9527/blog/issues/12",target:"_blank",rel:"noopener noreferrer"},p={href:"https://github.com/levy9527/blog/issues/10",target:"_blank",rel:"noopener noreferrer"},d={href:"https://github.com/levy9527/blog/issues/5",target:"_blank",rel:"noopener noreferrer"},g={href:"https://github.com/levy9527/blog/issues/11",target:"_blank",rel:"noopener noreferrer"};function m(v,k){const t=n("ExternalLinkIcon");return s(),i("div",null,[h,e("ul",null,[e("li",null,[e("a",c,[r("📦vue组件发布npm最佳实践"),o(t)])]),e("li",null,[e("a",_,[r("🔨揭秘vue-sfc-cli:组件研发利器"),o(t)])]),e("li",null,[e("a",u,[r("🚀Github集成TravisCI:自动发布"),o(t)])]),e("li",null,[e("a",b,[r("⚡Github集成Netlify:快速预览PR"),o(t)])]),e("li",null,[e("a",f,[r("🌪自动化的Github Workflow"),o(t)])]),e("li",null,[e("a",p,[r("🤖如何写一个GithubApp"),o(t)])]),e("li",null,[e("a",d,[r("🔒免费开启HTTPS"),o(t)])]),e("li",null,[e("a",g,[r("🕸捕获与改写HTTPS请求"),o(t)])])])])}const T=l(a,[["render",m],["__file","old-articles.html.vue"]]);export{T as default}; diff --git a/assets/performance-optimization-in-action.html-564a74fd.js b/assets/performance-optimization-in-action.html-c864ffc7.js similarity index 99% rename from assets/performance-optimization-in-action.html-564a74fd.js rename to assets/performance-optimization-in-action.html-c864ffc7.js index 73e5ea7e..ac3755ce 100644 --- a/assets/performance-optimization-in-action.html-564a74fd.js +++ b/assets/performance-optimization-in-action.html-c864ffc7.js @@ -1,4 +1,4 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as p,c as r,f as c,a as n,b as a,d as e,e as t}from"./app-ee4d23bf.js";const l={},d=n("h1",{id:"前端项目性能优化实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前端项目性能优化实战","aria-hidden":"true"},"#"),a(" 前端项目性能优化实战")],-1),u=n("p",null,"本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。",-1),m=n("h2",{id:"检测",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检测","aria-hidden":"true"},"#"),a(" 检测")],-1),h=n("p",null,"使用两个工具分析项目首页性能情况:",-1),g={href:"https://developers.google.com/speed/pagespeed/insights/",target:"_blank",rel:"noopener noreferrer"},b={href:"https://tools.pingdom.com/",target:"_blank",rel:"noopener noreferrer"},k=n("p",null,[a("得到结果如下:"),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1611714344531-6501362c-526d-40e5-b1a4-a3399a544ea1.png",alt:""}),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607738629800-10ff9829-ea0c-402b-b826-1d05903ee302.png",alt:""}),n("br"),a(" 可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。")],-1),v=n("h2",{id:"图片优化",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#图片优化","aria-hidden":"true"},"#"),a(" 图片优化")],-1),f=n("br",null,null,-1),_=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607739432571-c4ebd1bb-bf91-4ced-afca-ad70b84c4de6.png",alt:""},null,-1),y=n("br",null,null,-1),w={href:"https://zhuanlan.zhihu.com/p/99769484",target:"_blank",rel:"noopener noreferrer"},x=t('
  1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
  2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

注意:img-url 应该是 oss 的链接,并且是 https 协议。
如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

则在此环节,一次性做到了上图中的三个优化点。

提高TTFB时间

来看下一条优化建议:

因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

经过分析,上述代码存在两个问题:

  1. 可在客户端执行却放在了服务端
  2. 可并行执行却写成了串行

修改如下:


',10),C=n("br",null,null,-1),q=n("br",null,null,-1),j=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607929114683-23cb4794-bc67-4bed-8e95-0a8f493cb1e6.png",alt:""},null,-1),E=n("br",null,null,-1),T={href:"https://ssr.vuejs.org/guide/universal.html#component-lifecycle-hooks",target:"_blank",rel:"noopener noreferrer"},N=t(`

修改如下:

移除未使用的 Javascript

来看下一条优化建议:

因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

此时注意用到以下基本操作:

  1. google/github 查询库的用途
  2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

延迟静态资源的加载

来看另一个相关的建议:

如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

const srcs = [
+import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as i,o as p,c as r,e as c,a as n,b as a,d as e,f as t}from"./app-dcf5468f.js";const l={},d=n("h1",{id:"前端项目性能优化实战",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前端项目性能优化实战","aria-hidden":"true"},"#"),a(" 前端项目性能优化实战")],-1),u=n("p",null,"本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。",-1),m=n("h2",{id:"检测",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#检测","aria-hidden":"true"},"#"),a(" 检测")],-1),h=n("p",null,"使用两个工具分析项目首页性能情况:",-1),g={href:"https://developers.google.com/speed/pagespeed/insights/",target:"_blank",rel:"noopener noreferrer"},b={href:"https://tools.pingdom.com/",target:"_blank",rel:"noopener noreferrer"},k=n("p",null,[a("得到结果如下:"),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1611714344531-6501362c-526d-40e5-b1a4-a3399a544ea1.png",alt:""}),n("br"),n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607738629800-10ff9829-ea0c-402b-b826-1d05903ee302.png",alt:""}),n("br"),a(" 可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。")],-1),v=n("h2",{id:"图片优化",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#图片优化","aria-hidden":"true"},"#"),a(" 图片优化")],-1),f=n("br",null,null,-1),_=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607739432571-c4ebd1bb-bf91-4ced-afca-ad70b84c4de6.png",alt:""},null,-1),y=n("br",null,null,-1),w={href:"https://zhuanlan.zhihu.com/p/99769484",target:"_blank",rel:"noopener noreferrer"},x=t('
  1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
  2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

注意:img-url 应该是 oss 的链接,并且是 https 协议。
如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

则在此环节,一次性做到了上图中的三个优化点。

提高TTFB时间

来看下一条优化建议:

因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

经过分析,上述代码存在两个问题:

  1. 可在客户端执行却放在了服务端
  2. 可并行执行却写成了串行

修改如下:


',10),C=n("br",null,null,-1),q=n("br",null,null,-1),j=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1607929114683-23cb4794-bc67-4bed-8e95-0a8f493cb1e6.png",alt:""},null,-1),E=n("br",null,null,-1),T={href:"https://ssr.vuejs.org/guide/universal.html#component-lifecycle-hooks",target:"_blank",rel:"noopener noreferrer"},N=t(`

修改如下:

移除未使用的 Javascript

来看下一条优化建议:

因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

此时注意用到以下基本操作:

  1. google/github 查询库的用途
  2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

延迟静态资源的加载

来看另一个相关的建议:

如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

const srcs = [
   // 百度统计
   {
     src: 'https://hm.baidu.com/hm.js',
diff --git a/assets/performance-optimization-in-action.html-a47a49f3.js b/assets/performance-optimization-in-action.html-e09ad751.js
similarity index 76%
rename from assets/performance-optimization-in-action.html-a47a49f3.js
rename to assets/performance-optimization-in-action.html-e09ad751.js
index 65c92273..591056ed 100644
--- a/assets/performance-optimization-in-action.html-a47a49f3.js
+++ b/assets/performance-optimization-in-action.html-e09ad751.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-7320140c","path":"/frontend/performance-optimization-in-action.html","title":"前端项目性能优化实战","lang":"zh-CN","frontmatter":{"date":"2021-01-27T00:00:00.000Z","tag":"Frontend","description":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/performance-optimization-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"前端项目性能优化实战"}],["meta",{"property":"og:description","content":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2021-01-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"前端项目性能优化实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2021-01-27T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"检测","slug":"检测","link":"#检测","children":[]},{"level":2,"title":"图片优化","slug":"图片优化","link":"#图片优化","children":[]},{"level":2,"title":"提高TTFB时间","slug":"提高ttfb时间","link":"#提高ttfb时间","children":[]},{"level":2,"title":"移除未使用的 Javascript","slug":"移除未使用的-javascript","link":"#移除未使用的-javascript","children":[]},{"level":2,"title":"延迟静态资源的加载","slug":"延迟静态资源的加载","link":"#延迟静态资源的加载","children":[]},{"level":2,"title":"启用文本压缩","slug":"启用文本压缩","link":"#启用文本压缩","children":[]},{"level":2,"title":"优化缓存策略","slug":"优化缓存策略","link":"#优化缓存策略","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4.69,"words":1407},"filePathRelative":"frontend/performance-optimization-in-action.md","localizedDate":"2021年1月27日","excerpt":"

前端项目性能优化实战

\\n

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-7320140c","path":"/frontend/performance-optimization-in-action.html","title":"前端项目性能优化实战","lang":"zh-CN","frontmatter":{"date":"2021-01-27T00:00:00.000Z","tag":"Frontend","description":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。","head":[["meta",{"property":"og:url","content":"https://levy.vip/frontend/performance-optimization-in-action.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"前端项目性能优化实战"}],["meta",{"property":"og:description","content":"前端项目性能优化实战 本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:published_time","content":"2021-01-27T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"前端项目性能优化实战\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2021-01-27T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"检测","slug":"检测","link":"#检测","children":[]},{"level":2,"title":"图片优化","slug":"图片优化","link":"#图片优化","children":[]},{"level":2,"title":"提高TTFB时间","slug":"提高ttfb时间","link":"#提高ttfb时间","children":[]},{"level":2,"title":"移除未使用的 Javascript","slug":"移除未使用的-javascript","link":"#移除未使用的-javascript","children":[]},{"level":2,"title":"延迟静态资源的加载","slug":"延迟静态资源的加载","link":"#延迟静态资源的加载","children":[]},{"level":2,"title":"启用文本压缩","slug":"启用文本压缩","link":"#启用文本压缩","children":[]},{"level":2,"title":"优化缓存策略","slug":"优化缓存策略","link":"#优化缓存策略","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.69,"words":1407},"filePathRelative":"frontend/performance-optimization-in-action.md","localizedDate":"2021年1月27日","excerpt":"

前端项目性能优化实战

\\n

本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/recommend-practices-for-collections-naming-convention.html-36ba7dcd.js b/assets/recommend-practices-for-collections-naming-convention.html-41ae727d.js similarity index 67% rename from assets/recommend-practices-for-collections-naming-convention.html-36ba7dcd.js rename to assets/recommend-practices-for-collections-naming-convention.html-41ae727d.js index d0b2bc33..6f9e9ca2 100644 --- a/assets/recommend-practices-for-collections-naming-convention.html-36ba7dcd.js +++ b/assets/recommend-practices-for-collections-naming-convention.html-41ae727d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-d767e98e","path":"/java/recommend-practices-for-collections-naming-convention.html","title":"集合命名推荐","lang":"zh-CN","frontmatter":{"date":"2022-06-01T00:00:00.000Z","tag":["Java","Daily"],"description":"集合命名推荐 概述 建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。 当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章? 这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-collections-naming-convention.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"集合命名推荐"}],["meta",{"property":"og:description","content":"集合命名推荐 概述 建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。 当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章? 这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-06-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"集合命名推荐\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-01T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"概述","slug":"概述","link":"#概述","children":[]},{"level":2,"title":"List","slug":"list","link":"#list","children":[]},{"level":2,"title":"Set","slug":"set","link":"#set","children":[]},{"level":2,"title":"Map","slug":"map","link":"#map","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.03,"words":908},"filePathRelative":"java/recommend-practices-for-collections-naming-convention.md","localizedDate":"2022年6月1日","excerpt":"

集合命名推荐

\\n

概述

\\n

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

\\n

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
\\n\\"\\"

\\n

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-d767e98e","path":"/java/recommend-practices-for-collections-naming-convention.html","title":"集合命名推荐","lang":"zh-CN","frontmatter":{"date":"2022-06-01T00:00:00.000Z","tag":["Java","Daily"],"description":"集合命名推荐 概述 建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。 当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章? 这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-collections-naming-convention.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"集合命名推荐"}],["meta",{"property":"og:description","content":"集合命名推荐 概述 建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。 当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章? 这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-06-01T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"集合命名推荐\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-01T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"概述","slug":"概述","link":"#概述","children":[]},{"level":2,"title":"List","slug":"list","link":"#list","children":[]},{"level":2,"title":"Set","slug":"set","link":"#set","children":[]},{"level":2,"title":"Map","slug":"map","link":"#map","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.03,"words":908},"filePathRelative":"java/recommend-practices-for-collections-naming-convention.md","localizedDate":"2022年6月1日","excerpt":"

集合命名推荐

\\n

概述

\\n

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

\\n

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?
\\n\\"\\"

\\n

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

","autoDesc":true}');export{e as data}; diff --git a/assets/recommend-practices-for-collections-naming-convention.html-8fad9f5f.js b/assets/recommend-practices-for-collections-naming-convention.html-b81f48ef.js similarity index 91% rename from assets/recommend-practices-for-collections-naming-convention.html-8fad9f5f.js rename to assets/recommend-practices-for-collections-naming-convention.html-b81f48ef.js index 9f3ae59b..7a708a53 100644 --- a/assets/recommend-practices-for-collections-naming-convention.html-8fad9f5f.js +++ b/assets/recommend-practices-for-collections-naming-convention.html-b81f48ef.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as s,e}from"./app-ee4d23bf.js";const t={},p=e(`

集合命名推荐

概述

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

List

List 的变量,一般以 List 或 s 结尾, 如 idList 或 ids。这点易于理解,大家也容易遵守。

坏的示例:

nodeType.forEach(t -> {
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as a,c as s,f as e}from"./app-dcf5468f.js";const t={},p=e(`

集合命名推荐

概述

建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?

这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

List

List 的变量,一般以 List 或 s 结尾, 如 idList 或 ids。这点易于理解,大家也容易遵守。

坏的示例:

nodeType.forEach(t -> {
     // 省略代码
 });
 

第一眼看到这代码的时候,不知道读者是什么反应?

按照习惯,nodeType 通常要么是字符串、数字、或枚举,但上述居然能调用 forEach 方法?我不禁愣了一下,赶紧去看了下定义,才发现原来是List。

好的示例:

nodeTypes.forEach(t -> {
diff --git a/assets/recommend-practices-for-query-by-date-range.html-45c30670.js b/assets/recommend-practices-for-query-by-date-range.html-9ac25fe6.js
similarity index 72%
rename from assets/recommend-practices-for-query-by-date-range.html-45c30670.js
rename to assets/recommend-practices-for-query-by-date-range.html-9ac25fe6.js
index 3ccb756d..1d89d184 100644
--- a/assets/recommend-practices-for-query-by-date-range.html-45c30670.js
+++ b/assets/recommend-practices-for-query-by-date-range.html-9ac25fe6.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-f5471cb0","path":"/java/recommend-practices-for-query-by-date-range.html","title":"根据时间范围查询推荐实践","lang":"zh-CN","frontmatter":{"date":"2023-09-12T00:00:00.000Z","tag":["Java","Daily"],"description":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-query-by-date-range.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"根据时间范围查询推荐实践"}],["meta",{"property":"og:description","content":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"根据时间范围查询推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-12T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"实现","slug":"实现","link":"#实现","children":[{"level":3,"title":"MySQL","slug":"mysql","link":"#mysql","children":[]},{"level":3,"title":"MyBatis","slug":"mybatis","link":"#mybatis","children":[]},{"level":3,"title":"LoxalDate","slug":"loxaldate","link":"#loxaldate","children":[]},{"level":3,"title":"Jackson","slug":"jackson","link":"#jackson","children":[]}]},{"level":2,"title":"结语","slug":"结语","link":"#结语","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.21,"words":962},"filePathRelative":"java/recommend-practices-for-query-by-date-range.md","localizedDate":"2023年9月12日","excerpt":"

根据时间范围查询推荐实践

\\n

背景

\\n

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

\\n

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

\\n

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-f5471cb0","path":"/java/recommend-practices-for-query-by-date-range.html","title":"根据时间范围查询推荐实践","lang":"zh-CN","frontmatter":{"date":"2023-09-12T00:00:00.000Z","tag":["Java","Daily"],"description":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-query-by-date-range.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"根据时间范围查询推荐实践"}],["meta",{"property":"og:description","content":"根据时间范围查询推荐实践 背景 不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。 虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。 本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"根据时间范围查询推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-12T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"需求","slug":"需求","link":"#需求","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"实现","slug":"实现","link":"#实现","children":[{"level":3,"title":"MySQL","slug":"mysql","link":"#mysql","children":[]},{"level":3,"title":"MyBatis","slug":"mybatis","link":"#mybatis","children":[]},{"level":3,"title":"LoxalDate","slug":"loxaldate","link":"#loxaldate","children":[]},{"level":3,"title":"Jackson","slug":"jackson","link":"#jackson","children":[]}]},{"level":2,"title":"结语","slug":"结语","link":"#结语","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.21,"words":962},"filePathRelative":"java/recommend-practices-for-query-by-date-range.md","localizedDate":"2023年9月12日","excerpt":"

根据时间范围查询推荐实践

\\n

背景

\\n

不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

\\n

虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

\\n

本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/recommend-practices-for-query-by-date-range.html-e8d89b5b.js b/assets/recommend-practices-for-query-by-date-range.html-e11d2f0c.js similarity index 99% rename from assets/recommend-practices-for-query-by-date-range.html-e8d89b5b.js rename to assets/recommend-practices-for-query-by-date-range.html-e11d2f0c.js index 479c9f2d..7b40ed59 100644 --- a/assets/recommend-practices-for-query-by-date-range.html-e8d89b5b.js +++ b/assets/recommend-practices-for-query-by-date-range.html-e11d2f0c.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as t,f as p,a as n,b as a,e as o}from"./app-ee4d23bf.js";const c={},l=n("h1",{id:"根据时间范围查询推荐实践",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#根据时间范围查询推荐实践","aria-hidden":"true"},"#"),a(" 根据时间范围查询推荐实践")],-1),i=n("h2",{id:"背景",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),a(" 背景")],-1),u=n("p",null,"不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。",-1),r=n("p",null,"虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。",-1),d=n("p",null,"本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson",-1),k=o(`

需求

删除某个时间段以前的日志。类似于消除浏览记录:

分析

上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

  • 开始时间
  • 结束时间

如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

实现

MySQL

对于 MySQL,推荐使用,因为简单直观,且方便:

SELECT * FROM operation_logs 
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as e,c as t,e as p,a as n,b as a,f as o}from"./app-dcf5468f.js";const c={},l=n("h1",{id:"根据时间范围查询推荐实践",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#根据时间范围查询推荐实践","aria-hidden":"true"},"#"),a(" 根据时间范围查询推荐实践")],-1),i=n("h2",{id:"背景",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#背景","aria-hidden":"true"},"#"),a(" 背景")],-1),u=n("p",null,"不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。",-1),r=n("p",null,"虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。",-1),d=n("p",null,"本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson",-1),k=o(`

需求

删除某个时间段以前的日志。类似于消除浏览记录:

分析

上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

  • 开始时间
  • 结束时间

如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

实现

MySQL

对于 MySQL,推荐使用,因为简单直观,且方便:

SELECT * FROM operation_logs 
 WHERE created_time BETWEEN '2023-09-12 11:44:26' AND '2023-09-12 13:54:52';
 

另一种方式:

SELECT * FROM operation_logs 
 WHERE created_time >= '2023-09-12 11:44:26' AND created_time <= '2023-09-12 13:54:52';
diff --git a/assets/recommend-practices-for-writing-good-functions.html-feec5c42.js b/assets/recommend-practices-for-writing-good-functions.html-413d3f52.js
similarity index 99%
rename from assets/recommend-practices-for-writing-good-functions.html-feec5c42.js
rename to assets/recommend-practices-for-writing-good-functions.html-413d3f52.js
index 18710ef9..e1fe899f 100644
--- a/assets/recommend-practices-for-writing-good-functions.html-feec5c42.js
+++ b/assets/recommend-practices-for-writing-good-functions.html-413d3f52.js
@@ -1,4 +1,4 @@
-import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as l,a as n,b as a,d as e,e as p}from"./app-ee4d23bf.js";const i={},u=p('

编写函数的最佳实践

前言

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

本文将推荐一些编写函数的最佳实践,以供参数。

减少重复

',5),d={href:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself",target:"_blank",rel:"noopener noreferrer"},r=p(`

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

然而,有时代码只是相似,不完全相同,不能简单地使用 IDEA 右键 + Refactor + Extract Method 来抽取函数。
此时,为减少重复,需要进行一些思考。

可以把程序的划分成三个部分:

Program = Control + Logic + Data Structure
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as l,a as n,b as a,d as e,f as p}from"./app-dcf5468f.js";const i={},u=p('

编写函数的最佳实践

前言

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

本文将推荐一些编写函数的最佳实践,以供参数。

减少重复

',5),d={href:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself",target:"_blank",rel:"noopener noreferrer"},r=p(`

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

然而,有时代码只是相似,不完全相同,不能简单地使用 IDEA 右键 + Refactor + Extract Method 来抽取函数。
此时,为减少重复,需要进行一些思考。

可以把程序的划分成三个部分:

Program = Control + Logic + Data Structure
 

一般而言,函数的入参都是数据变量,也即 Data Structure。
而 Java 8 以后,lambda 表达式(也即函数)可以作为入参,其代表的是 Logic。
因此,最抽象的函数,是只定义了 Control、把 Logic 及 Data Structure 都作为入参的函数。当遇到类似却不完全相同的代码、想封装函数有遇难时,可以借助上述思路来梳理逻辑。

隐藏细节

隐藏细节,是为了减少使用者的心智负担,方便其调用。

有一个简单的判断标准:如果调用者需要频繁查看函数内部情况,以确定函数的目的或实现细节,那么隐藏细节的意图是失败的。

建议

为了达到前文所述的目的,如以下实践建议。需要指出的是,以下提倡的是建议,并非金科玉律;只适用于一般情况,并非所有情况,特殊情况是可以特殊处理的。

优先根据业务命名

一般而言,函数名最好是根据业务逻辑、结合业务领域来命名,而不是根据程序逻辑来命名。

示例:

// bad
 String getString(UserDTO user);
 
diff --git a/assets/recommend-practices-for-writing-good-functions.html-3f88f0bb.js b/assets/recommend-practices-for-writing-good-functions.html-fb82c5c2.js
similarity index 70%
rename from assets/recommend-practices-for-writing-good-functions.html-3f88f0bb.js
rename to assets/recommend-practices-for-writing-good-functions.html-fb82c5c2.js
index 8021c98e..72cf135b 100644
--- a/assets/recommend-practices-for-writing-good-functions.html-3f88f0bb.js
+++ b/assets/recommend-practices-for-writing-good-functions.html-fb82c5c2.js
@@ -1 +1 @@
-const e=JSON.parse(`{"key":"v-57d9b582","path":"/java/recommend-practices-for-writing-good-functions.html","title":"编写函数的最佳实践","lang":"zh-CN","frontmatter":{"date":"2022-10-14T00:00:00.000Z","tag":["Java","Daily"],"description":"编写函数的最佳实践 前言 编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。 本文将推荐一些编写函数的最佳实践,以供参数。 减少重复 这是在遵守 Don't repeat yourself (DRY) 原则。 实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-writing-good-functions.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"编写函数的最佳实践"}],["meta",{"property":"og:description","content":"编写函数的最佳实践 前言 编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。 本文将推荐一些编写函数的最佳实践,以供参数。 减少重复 这是在遵守 Don't repeat yourself (DRY) 原则。 实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-10-14T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"编写函数的最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-10-14T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[{"level":3,"title":"减少重复","slug":"减少重复","link":"#减少重复","children":[]},{"level":3,"title":"隐藏细节","slug":"隐藏细节","link":"#隐藏细节","children":[]}]},{"level":2,"title":"建议","slug":"建议","link":"#建议","children":[{"level":3,"title":"优先根据业务命名","slug":"优先根据业务命名","link":"#优先根据业务命名","children":[]},{"level":3,"title":"一个函数只做一件事","slug":"一个函数只做一件事","link":"#一个函数只做一件事","children":[]},{"level":3,"title":"优先使用纯函数","slug":"优先使用纯函数","link":"#优先使用纯函数","children":[]},{"level":3,"title":"编写不需要返回值的函数","slug":"编写不需要返回值的函数","link":"#编写不需要返回值的函数","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5.88,"words":1764},"filePathRelative":"java/recommend-practices-for-writing-good-functions.md","localizedDate":"2022年10月14日","excerpt":"

编写函数的最佳实践

\\n

前言

\\n

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

\\n

本文将推荐一些编写函数的最佳实践,以供参数。

\\n

减少重复

\\n

这是在遵守 Don't repeat yourself (DRY) 原则。

\\n

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

","autoDesc":true}`);export{e as data}; +const e=JSON.parse(`{"key":"v-57d9b582","path":"/java/recommend-practices-for-writing-good-functions.html","title":"编写函数的最佳实践","lang":"zh-CN","frontmatter":{"date":"2022-10-14T00:00:00.000Z","tag":["Java","Daily"],"description":"编写函数的最佳实践 前言 编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。 本文将推荐一些编写函数的最佳实践,以供参数。 减少重复 这是在遵守 Don't repeat yourself (DRY) 原则。 实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/recommend-practices-for-writing-good-functions.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"编写函数的最佳实践"}],["meta",{"property":"og:description","content":"编写函数的最佳实践 前言 编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。 本文将推荐一些编写函数的最佳实践,以供参数。 减少重复 这是在遵守 Don't repeat yourself (DRY) 原则。 实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-10-14T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"编写函数的最佳实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-10-14T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[{"level":3,"title":"减少重复","slug":"减少重复","link":"#减少重复","children":[]},{"level":3,"title":"隐藏细节","slug":"隐藏细节","link":"#隐藏细节","children":[]}]},{"level":2,"title":"建议","slug":"建议","link":"#建议","children":[{"level":3,"title":"优先根据业务命名","slug":"优先根据业务命名","link":"#优先根据业务命名","children":[]},{"level":3,"title":"一个函数只做一件事","slug":"一个函数只做一件事","link":"#一个函数只做一件事","children":[]},{"level":3,"title":"优先使用纯函数","slug":"优先使用纯函数","link":"#优先使用纯函数","children":[]},{"level":3,"title":"编写不需要返回值的函数","slug":"编写不需要返回值的函数","link":"#编写不需要返回值的函数","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.88,"words":1764},"filePathRelative":"java/recommend-practices-for-writing-good-functions.md","localizedDate":"2022年10月14日","excerpt":"

编写函数的最佳实践

\\n

前言

\\n

编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

\\n

本文将推荐一些编写函数的最佳实践,以供参数。

\\n

减少重复

\\n

这是在遵守 Don't repeat yourself (DRY) 原则。

\\n

实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

","autoDesc":true}`);export{e as data}; diff --git a/assets/reduce-python-image-size.html-b370c494.js b/assets/reduce-python-image-size.html-53326baa.js similarity index 67% rename from assets/reduce-python-image-size.html-b370c494.js rename to assets/reduce-python-image-size.html-53326baa.js index 86a27a96..239da4ad 100644 --- a/assets/reduce-python-image-size.html-b370c494.js +++ b/assets/reduce-python-image-size.html-53326baa.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-3c0c097a","path":"/devops/reduce-python-image-size.html","title":"缩减Python应用的镜像体积","lang":"zh-CN","frontmatter":{"date":"2023-12-09T00:00:00.000Z","tag":["DevOps","Python"],"description":"缩减Python应用的镜像体积 背景 当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G! 能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/reduce-python-image-size.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"缩减Python应用的镜像体积"}],["meta",{"property":"og:description","content":"缩减Python应用的镜像体积 背景 当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G! 能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-12-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"缩减Python应用的镜像体积\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"原始Dockerfile","slug":"原始dockerfile","link":"#原始dockerfile","children":[]},{"level":2,"title":"使用slim镜像","slug":"使用slim镜像","link":"#使用slim镜像","children":[]},{"level":2,"title":"减少层数、取消本地缓存","slug":"减少层数、取消本地缓存","link":"#减少层数、取消本地缓存","children":[]},{"level":2,"title":"优化效果","slug":"优化效果","link":"#优化效果","children":[]},{"level":2,"title":"参考","slug":"参考","link":"#参考","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.29,"words":687},"filePathRelative":"devops/reduce-python-image-size.md","localizedDate":"2023年12月9日","excerpt":"

缩减Python应用的镜像体积

\\n

背景

\\n

当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!
\\n\\"\\"
\\n能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-3c0c097a","path":"/devops/reduce-python-image-size.html","title":"缩减Python应用的镜像体积","lang":"zh-CN","frontmatter":{"date":"2023-12-09T00:00:00.000Z","tag":["DevOps","Python"],"description":"缩减Python应用的镜像体积 背景 当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G! 能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/reduce-python-image-size.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"缩减Python应用的镜像体积"}],["meta",{"property":"og:description","content":"缩减Python应用的镜像体积 背景 当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G! 能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-12-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"缩减Python应用的镜像体积\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"原始Dockerfile","slug":"原始dockerfile","link":"#原始dockerfile","children":[]},{"level":2,"title":"使用slim镜像","slug":"使用slim镜像","link":"#使用slim镜像","children":[]},{"level":2,"title":"减少层数、取消本地缓存","slug":"减少层数、取消本地缓存","link":"#减少层数、取消本地缓存","children":[]},{"level":2,"title":"优化效果","slug":"优化效果","link":"#优化效果","children":[]},{"level":2,"title":"参考","slug":"参考","link":"#参考","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.29,"words":687},"filePathRelative":"devops/reduce-python-image-size.md","localizedDate":"2023年12月9日","excerpt":"

缩减Python应用的镜像体积

\\n

背景

\\n

当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!
\\n\\"\\"
\\n能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/reduce-python-image-size.html-8d55f040.js b/assets/reduce-python-image-size.html-5940c01d.js similarity index 98% rename from assets/reduce-python-image-size.html-8d55f040.js rename to assets/reduce-python-image-size.html-5940c01d.js index ff750c85..e36f53fa 100644 --- a/assets/reduce-python-image-size.html-8d55f040.js +++ b/assets/reduce-python-image-size.html-5940c01d.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as p,f as c,a as e,b as n,d as s,e as i}from"./app-ee4d23bf.js";const d={},o=i('

缩减Python应用的镜像体积

背景

当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!

能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

',3),u=i(`

原始Dockerfile

先来看看 Dockerfile 的原始模样:

FROM python:3.10.13
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r,o as l,c as p,e as c,a as e,b as n,d as s,f as i}from"./app-dcf5468f.js";const d={},o=i('

缩减Python应用的镜像体积

背景

当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!

能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

',3),u=i(`

原始Dockerfile

先来看看 Dockerfile 的原始模样:

FROM python:3.10.13
 
 WORKDIR /app
 
diff --git a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-b3036c9b.js b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-59308fd7.js
similarity index 81%
rename from assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-b3036c9b.js
rename to assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-59308fd7.js
index efd341a4..5d2faabc 100644
--- a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-b3036c9b.js
+++ b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-59308fd7.js
@@ -1 +1 @@
-import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as c,o as a,c as n,f as r,d as s,a as e,b as i}from"./app-ee4d23bf.js";const l={},_=e("h1",{id:"微软中国cto演讲观后感",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#微软中国cto演讲观后感","aria-hidden":"true"},"#"),i(" 微软中国CTO演讲观后感")],-1),d=e("p",null,"看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。",-1);function f(h,m){const o=c("BiliBili");return a(),n("div",null,[_,d,r(" more "),s(o,{bvid:"BV1E14y1C72L"})])}const u=t(l,[["render",f],["__file","reflections-on-a-speech-by-cto-of-microsoft-china.html.vue"]]);export{u as default};
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as c,o as a,c as n,e as r,d as s,a as e,b as i}from"./app-dcf5468f.js";const l={},_=e("h1",{id:"微软中国cto演讲观后感",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#微软中国cto演讲观后感","aria-hidden":"true"},"#"),i(" 微软中国CTO演讲观后感")],-1),d=e("p",null,"看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。",-1);function f(h,m){const o=c("BiliBili");return a(),n("div",null,[_,d,r(" more "),s(o,{bvid:"BV1E14y1C72L"})])}const u=t(l,[["render",f],["__file","reflections-on-a-speech-by-cto-of-microsoft-china.html.vue"]]);export{u as default};
diff --git a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-69f54150.js b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-9956b49d.js
similarity index 69%
rename from assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-69f54150.js
rename to assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-9956b49d.js
index aa27d9ab..d592fc78 100644
--- a/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-69f54150.js
+++ b/assets/reflections-on-a-speech-by-cto-of-microsoft-china.html-9956b49d.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-5c48d497","path":"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html","title":"微软中国CTO演讲观后感","lang":"zh-CN","frontmatter":{"date":"2023-09-10T00:00:00.000Z","tag":["Daily","Video"],"description":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"微软中国CTO演讲观后感"}],["meta",{"property":"og:description","content":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-09-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"微软中国CTO演讲观后感\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-10T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"daily/reflections-on-a-speech-by-cto-of-microsoft-china.md","localizedDate":"2023年9月10日","excerpt":"

微软中国CTO演讲观后感

\\n

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-5c48d497","path":"/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html","title":"微软中国CTO演讲观后感","lang":"zh-CN","frontmatter":{"date":"2023-09-10T00:00:00.000Z","tag":["Daily","Video"],"description":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"微软中国CTO演讲观后感"}],["meta",{"property":"og:description","content":"微软中国CTO演讲观后感 看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-09-10T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"微软中国CTO演讲观后感\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-10T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.17,"words":51},"filePathRelative":"daily/reflections-on-a-speech-by-cto-of-microsoft-china.md","localizedDate":"2023年9月10日","excerpt":"

微软中国CTO演讲观后感

\\n

看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/rethinking-git-flow.html-3667168e.js b/assets/rethinking-git-flow.html-59994a2c.js similarity index 71% rename from assets/rethinking-git-flow.html-3667168e.js rename to assets/rethinking-git-flow.html-59994a2c.js index fd00dac2..198b4f60 100644 --- a/assets/rethinking-git-flow.html-3667168e.js +++ b/assets/rethinking-git-flow.html-59994a2c.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-0fbf5fdc","path":"/git/rethinking-git-flow.html","title":"再论Git Flow","lang":"zh-CN","frontmatter":{"date":"2022-04-21T00:00:00.000Z","tag":"Git","description":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/rethinking-git-flow.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再论Git Flow"}],["meta",{"property":"og:description","content":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再论Git Flow\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"动机","slug":"动机","link":"#动机","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[{"level":3,"title":"剔除代码","slug":"剔除代码","link":"#剔除代码","children":[]},{"level":3,"title":"再次提交","slug":"再次提交","link":"#再次提交","children":[]},{"level":3,"title":"比较优劣","slug":"比较优劣","link":"#比较优劣","children":[]}]},{"level":2,"title":"实例","slug":"实例","link":"#实例","children":[{"level":3,"title":"分支模型","slug":"分支模型","link":"#分支模型","children":[]},{"level":3,"title":"功能提交","slug":"功能提交","link":"#功能提交","children":[]},{"level":3,"title":"功能回撤","slug":"功能回撤","link":"#功能回撤","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5.53,"words":1660},"filePathRelative":"git/rethinking-git-flow.md","localizedDate":"2022年4月21日","excerpt":"

再论Git Flow

\\n

背景

\\n

团队目前使用的 Git 协作模式是:

\\n
    \\n
  1. 对每个功能建立相应的 feat 分支
  2. \\n
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. \\n
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. \\n
\\n

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-0fbf5fdc","path":"/git/rethinking-git-flow.html","title":"再论Git Flow","lang":"zh-CN","frontmatter":{"date":"2022-04-21T00:00:00.000Z","tag":"Git","description":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/rethinking-git-flow.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再论Git Flow"}],["meta",{"property":"og:description","content":"再论Git Flow 背景 团队目前使用的 Git 协作模式是: 对每个功能建立相应的 feat 分支 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支 这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:published_time","content":"2022-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再论Git Flow\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"动机","slug":"动机","link":"#动机","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[{"level":3,"title":"剔除代码","slug":"剔除代码","link":"#剔除代码","children":[]},{"level":3,"title":"再次提交","slug":"再次提交","link":"#再次提交","children":[]},{"level":3,"title":"比较优劣","slug":"比较优劣","link":"#比较优劣","children":[]}]},{"level":2,"title":"实例","slug":"实例","link":"#实例","children":[{"level":3,"title":"分支模型","slug":"分支模型","link":"#分支模型","children":[]},{"level":3,"title":"功能提交","slug":"功能提交","link":"#功能提交","children":[]},{"level":3,"title":"功能回撤","slug":"功能回撤","link":"#功能回撤","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.53,"words":1660},"filePathRelative":"git/rethinking-git-flow.md","localizedDate":"2022年4月21日","excerpt":"

再论Git Flow

\\n

背景

\\n

团队目前使用的 Git 协作模式是:

\\n
    \\n
  1. 对每个功能建立相应的 feat 分支
  2. \\n
  3. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  4. \\n
  5. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支
  6. \\n
\\n

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

","autoDesc":true}');export{e as data}; diff --git a/assets/rethinking-git-flow.html-3cd71123.js b/assets/rethinking-git-flow.html-f432c019.js similarity index 99% rename from assets/rethinking-git-flow.html-3cd71123.js rename to assets/rethinking-git-flow.html-f432c019.js index 273fd920..7e0ee2bb 100644 --- a/assets/rethinking-git-flow.html-3cd71123.js +++ b/assets/rethinking-git-flow.html-f432c019.js @@ -1 +1 @@ -import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as h,c,a,b as e,d as i,e as t}from"./app-ee4d23bf.js";const d={},n=t('

再论Git Flow

背景

团队目前使用的 Git 协作模式是:

  1. 对每个功能建立相应的 feat 分支
  2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

  1. 某 feat 合并至 dev 后,并不想合并至 test
  2. 某 feat 合并至 test 后,并不想合并至 uat

本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

动机

为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

  1. feat -> dev,解决冲突
  2. feat -> test,又要解决冲突
  3. feat -> uat,还要解决冲突
',10),p={href:"https://www.cloudbees.com/blog/pitfalls-feature-branching",target:"_blank",rel:"noopener noreferrer"},s=t('

再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

为此,本文思考是否存在另一种分支协作的方式。

分析

首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

  1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
  2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
  3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

剔除代码

先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

',8),f={href:"https://www.clock.co.uk/insight/deleting-a-git-commit",target:"_blank",rel:"noopener noreferrer"},u=t('
  1. git rebase
  2. git cherry-pick

git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

再次提交

真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

比较优劣

要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

实例

下面举例说明,如何应用上述分析结果。

分支模型

长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

  • 按版本,如: release/v2.15
  • 按上线日期,如:release/04-26

功能提交

每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

每次需要集成发布时,正常的分支合并操作如下:

  1. feat -> dev
  2. feat -> release
  3. release -> test
  4. release -> uat

则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

为什么一个 feat 要合并两次?

因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

并且这样也能适应不同的功能分批提测的研发节奏。

功能回撤

当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

  1. checkout rollback 分支
  2. 进行回滚提交,或 revert,或屏蔽功能入口
  3. 请求合并至 UAT(不合并至 release/分支):

后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

结论

通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

',36);function _(b,g){const r=o("ExternalLinkIcon");return h(),c("div",null,[n,a("p",null,[e("正如"),a("a",p,[e("此文章"),i(r)]),e("所说,“把时间浪费在解决不必要的冲突上”。")]),s,a("p",null,[e("想“剔除某 feat 分支的代码”,可操作方式如下,更多请"),a("a",f,[e("参考此文章"),i(r)]),e(":")]),u])}const v=l(d,[["render",_],["__file","rethinking-git-flow.html.vue"]]);export{v as default}; +import{_ as l}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as h,c,a,b as e,d as i,f as t}from"./app-dcf5468f.js";const d={},n=t('

再论Git Flow

背景

团队目前使用的 Git 协作模式是:

  1. 对每个功能建立相应的 feat 分支
  2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
  3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

  1. 某 feat 合并至 dev 后,并不想合并至 test
  2. 某 feat 合并至 test 后,并不想合并至 uat

本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

动机

为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

  1. feat -> dev,解决冲突
  2. feat -> test,又要解决冲突
  3. feat -> uat,还要解决冲突
',10),p={href:"https://www.cloudbees.com/blog/pitfalls-feature-branching",target:"_blank",rel:"noopener noreferrer"},s=t('

再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

为此,本文思考是否存在另一种分支协作的方式。

分析

首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

  1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
  2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
  3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

剔除代码

先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

',8),f={href:"https://www.clock.co.uk/insight/deleting-a-git-commit",target:"_blank",rel:"noopener noreferrer"},u=t('
  1. git rebase
  2. git cherry-pick

git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

再次提交

真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

比较优劣

要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

实例

下面举例说明,如何应用上述分析结果。

分支模型

长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

  • 按版本,如: release/v2.15
  • 按上线日期,如:release/04-26

功能提交

每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

每次需要集成发布时,正常的分支合并操作如下:

  1. feat -> dev
  2. feat -> release
  3. release -> test
  4. release -> uat

则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

为什么一个 feat 要合并两次?

因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

并且这样也能适应不同的功能分批提测的研发节奏。

功能回撤

当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

  1. checkout rollback 分支
  2. 进行回滚提交,或 revert,或屏蔽功能入口
  3. 请求合并至 UAT(不合并至 release/分支):

后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

结论

通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

',36);function _(b,g){const r=o("ExternalLinkIcon");return h(),c("div",null,[n,a("p",null,[e("正如"),a("a",p,[e("此文章"),i(r)]),e("所说,“把时间浪费在解决不必要的冲突上”。")]),s,a("p",null,[e("想“剔除某 feat 分支的代码”,可操作方式如下,更多请"),a("a",f,[e("参考此文章"),i(r)]),e(":")]),u])}const v=l(d,[["render",_],["__file","rethinking-git-flow.html.vue"]]);export{v as default}; diff --git a/assets/testing-environments-should-be-consistent-with-production-environments.html-65f13b3e.js b/assets/testing-environments-should-be-consistent-with-production-environments.html-1e136f28.js similarity index 98% rename from assets/testing-environments-should-be-consistent-with-production-environments.html-65f13b3e.js rename to assets/testing-environments-should-be-consistent-with-production-environments.html-1e136f28.js index f5c38560..3cd511ed 100644 --- a/assets/testing-environments-should-be-consistent-with-production-environments.html-65f13b3e.js +++ b/assets/testing-environments-should-be-consistent-with-production-environments.html-1e136f28.js @@ -1,2 +1,2 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as e,e as n}from"./app-ee4d23bf.js";const l={},t=n(`

生产教训:测试环境要与生产环境一致

事件还原

业务流程:

  1. app-a 上传文件
  2. app-b 下载文件后使用文件

其他信息:

  1. 开发、测试环境使用 MinIO
  2. 生产环境使用 Amazon S3

问题:

  1. app-a 上传文件成功
  2. app-b 使用文件报错

逐步分析定位问题:

  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  3. app-a 是否真的上传成功?——确认文件已在 S3
  4. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。

所以问题就在于,为什么 app-b 的代码会判断文件不存在?

  • 是传过去的路径参数不对?
  • 还是 app-b 的逻辑有问题?

经过确认,传参是正确的,那么只有一个推论: app-b 判断文件是否存在的逻辑有问题。

具体是哪里有问题呢?

原来文件下载前,有一个判断目录是否存在的逻辑,其实现是判断 S3 的对象是否存在,示例代码如下:

return s3Client.doesObjectExist("/path/dir")
+import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as i,c as e,f as n}from"./app-dcf5468f.js";const l={},t=n(`

生产教训:测试环境要与生产环境一致

事件还原

业务流程:

  1. app-a 上传文件
  2. app-b 下载文件后使用文件

其他信息:

  1. 开发、测试环境使用 MinIO
  2. 生产环境使用 Amazon S3

问题:

  1. app-a 上传文件成功
  2. app-b 使用文件报错

逐步分析定位问题:

  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  3. app-a 是否真的上传成功?——确认文件已在 S3
  4. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。

所以问题就在于,为什么 app-b 的代码会判断文件不存在?

  • 是传过去的路径参数不对?
  • 还是 app-b 的逻辑有问题?

经过确认,传参是正确的,那么只有一个推论: app-b 判断文件是否存在的逻辑有问题。

具体是哪里有问题呢?

原来文件下载前,有一个判断目录是否存在的逻辑,其实现是判断 S3 的对象是否存在,示例代码如下:

return s3Client.doesObjectExist("/path/dir")
 

则该代码永远为 false。

如何修复呢?改为调用 listObjects就可以了。如下图所示(左边是修改前,右边是修改后):

分析

很难想像,为什么在涉及对象存储的代码中,会有判断目录是否存在的逻辑。

好在我是个善于为别人找理由的人。我观察了下代码库,发现接口与类结构如下:

如果定义 IStorage 与实现 S3Storage 的代码的人并不相同(我相信很可能是这样),那么我更倾向于认为责任在定义 IStorage 的人身上,因为TA并没有合理地设计接口,导致后来者被迫实现没有意义的接口,从而出错。

结论

为什么开发、测试环境没问题?或者说,为什么 MinIO 没这个问题?那是因为实现 MinIOStorage 的人,没有踩这个坑。

所以,本此事件给我来的经验教训是什么呢?既不是 S3 如何判断目录是否存在,也不是接口定义的重要性。而是:要让测试环境与生产环境尽可能保持一致,提前暴露问题。而不是测试环境是这样的配置,生产环境又是那样的配置——这就会加大生产环境出问题的可能性!

`,25),s=[t];function p(c,d){return i(),e("div",null,s)}const h=a(l,[["render",p],["__file","testing-environments-should-be-consistent-with-production-environments.html.vue"]]);export{h as default}; diff --git a/assets/testing-environments-should-be-consistent-with-production-environments.html-d9f46229.js b/assets/testing-environments-should-be-consistent-with-production-environments.html-429ac068.js similarity index 66% rename from assets/testing-environments-should-be-consistent-with-production-environments.html-d9f46229.js rename to assets/testing-environments-should-be-consistent-with-production-environments.html-429ac068.js index 50cbc7b3..58d1ef22 100644 --- a/assets/testing-environments-should-be-consistent-with-production-environments.html-d9f46229.js +++ b/assets/testing-environments-should-be-consistent-with-production-environments.html-429ac068.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-4f919602","path":"/daily/testing-environments-should-be-consistent-with-production-environments.html","title":"生产教训:测试环境要与生产环境一致","lang":"zh-CN","frontmatter":{"date":"2023-11-12T00:00:00.000Z","tag":["Daily"],"description":"生产教训:测试环境要与生产环境一致 事件还原 业务流程: app-a 上传文件 app-b 下载文件后使用文件 其他信息: 开发、测试环境使用 MinIO 生产环境使用 Amazon S3 问题: app-a 上传文件成功 app-b 使用文件报错 逐步分析定位问题: app-a 与 app-b 配置是否一致?——确认都是使用 S3 S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题 app-a 是否真的上传成功?——确认文件已在 S3 app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/testing-environments-should-be-consistent-with-production-environments.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"生产教训:测试环境要与生产环境一致"}],["meta",{"property":"og:description","content":"生产教训:测试环境要与生产环境一致 事件还原 业务流程: app-a 上传文件 app-b 下载文件后使用文件 其他信息: 开发、测试环境使用 MinIO 生产环境使用 Amazon S3 问题: app-a 上传文件成功 app-b 使用文件报错 逐步分析定位问题: app-a 与 app-b 配置是否一致?——确认都是使用 S3 S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题 app-a 是否真的上传成功?——确认文件已在 S3 app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-11-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"生产教训:测试环境要与生产环境一致\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-11-12T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"事件还原","slug":"事件还原","link":"#事件还原","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.25,"words":676},"filePathRelative":"daily/testing-environments-should-be-consistent-with-production-environments.md","localizedDate":"2023年11月12日","excerpt":"

生产教训:测试环境要与生产环境一致

\\n

事件还原

\\n

业务流程:

\\n
    \\n
  1. app-a 上传文件
  2. \\n
  3. app-b 下载文件后使用文件
  4. \\n
\\n

其他信息:

\\n
    \\n
  1. 开发、测试环境使用 MinIO
  2. \\n
  3. 生产环境使用 Amazon S3
  4. \\n
\\n

问题:

\\n
    \\n
  1. app-a 上传文件成功
  2. \\n
  3. app-b 使用文件报错
  4. \\n
\\n

逐步分析定位问题:

\\n
    \\n
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. \\n
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. \\n
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. \\n
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. \\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-4f919602","path":"/daily/testing-environments-should-be-consistent-with-production-environments.html","title":"生产教训:测试环境要与生产环境一致","lang":"zh-CN","frontmatter":{"date":"2023-11-12T00:00:00.000Z","tag":["Daily"],"description":"生产教训:测试环境要与生产环境一致 事件还原 业务流程: app-a 上传文件 app-b 下载文件后使用文件 其他信息: 开发、测试环境使用 MinIO 生产环境使用 Amazon S3 问题: app-a 上传文件成功 app-b 使用文件报错 逐步分析定位问题: app-a 与 app-b 配置是否一致?——确认都是使用 S3 S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题 app-a 是否真的上传成功?——确认文件已在 S3 app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/testing-environments-should-be-consistent-with-production-environments.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"生产教训:测试环境要与生产环境一致"}],["meta",{"property":"og:description","content":"生产教训:测试环境要与生产环境一致 事件还原 业务流程: app-a 上传文件 app-b 下载文件后使用文件 其他信息: 开发、测试环境使用 MinIO 生产环境使用 Amazon S3 问题: app-a 上传文件成功 app-b 使用文件报错 逐步分析定位问题: app-a 与 app-b 配置是否一致?——确认都是使用 S3 S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题 app-a 是否真的上传成功?——确认文件已在 S3 app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-11-12T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"生产教训:测试环境要与生产环境一致\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-11-12T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"事件还原","slug":"事件还原","link":"#事件还原","children":[]},{"level":2,"title":"分析","slug":"分析","link":"#分析","children":[]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.25,"words":676},"filePathRelative":"daily/testing-environments-should-be-consistent-with-production-environments.md","localizedDate":"2023年11月12日","excerpt":"

生产教训:测试环境要与生产环境一致

\\n

事件还原

\\n

业务流程:

\\n
    \\n
  1. app-a 上传文件
  2. \\n
  3. app-b 下载文件后使用文件
  4. \\n
\\n

其他信息:

\\n
    \\n
  1. 开发、测试环境使用 MinIO
  2. \\n
  3. 生产环境使用 Amazon S3
  4. \\n
\\n

问题:

\\n
    \\n
  1. app-a 上传文件成功
  2. \\n
  3. app-b 使用文件报错
  4. \\n
\\n

逐步分析定位问题:

\\n
    \\n
  1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
  2. \\n
  3. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
  4. \\n
  5. app-a 是否真的上传成功?——确认文件已在 S3
  6. \\n
  7. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。
  8. \\n
","autoDesc":true}');export{e as data}; diff --git a/assets/things-I-have-to-vent-about-vue.html-c552fd94.js b/assets/things-I-have-to-vent-about-vue.html-569980a1.js similarity index 93% rename from assets/things-I-have-to-vent-about-vue.html-c552fd94.js rename to assets/things-I-have-to-vent-about-vue.html-569980a1.js index e6ad3ddd..f9453f3c 100644 --- a/assets/things-I-have-to-vent-about-vue.html-c552fd94.js +++ b/assets/things-I-have-to-vent-about-vue.html-569980a1.js @@ -1 +1 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as a,d as l,a as e,b as i}from"./app-ee4d23bf.js";const c={},r=e("h1",{id:"对vue不得不吐槽的事",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#对vue不得不吐槽的事","aria-hidden":"true"},"#"),i(" 对Vue不得不吐槽的事")],-1),_=e("p",null,"为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!",-1),u=e("p",null,"总结一下,我对Vue生态不满的地方在于:",-1),d=e("ol",null,[e("li",null,"Vue总是破坏性升级,新技术完全不管旧用户的体验"),e("li",null,"你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性")],-1);function h(p,m){const t=n("BiliBili");return s(),a("div",null,[r,_,u,d,l(t,{bvid:"BV1oN411a7jp"})])}const f=o(c,[["render",h],["__file","things-I-have-to-vent-about-vue.html.vue"]]);export{f as default}; +import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o as s,c as a,d as l,a as e,b as i}from"./app-dcf5468f.js";const c={},r=e("h1",{id:"对vue不得不吐槽的事",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#对vue不得不吐槽的事","aria-hidden":"true"},"#"),i(" 对Vue不得不吐槽的事")],-1),_=e("p",null,"为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!",-1),u=e("p",null,"总结一下,我对Vue生态不满的地方在于:",-1),d=e("ol",null,[e("li",null,"Vue总是破坏性升级,新技术完全不管旧用户的体验"),e("li",null,"你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性")],-1);function h(p,m){const t=n("BiliBili");return s(),a("div",null,[r,_,u,d,l(t,{bvid:"BV1oN411a7jp"})])}const f=o(c,[["render",h],["__file","things-I-have-to-vent-about-vue.html.vue"]]);export{f as default}; diff --git a/assets/things-I-have-to-vent-about-vue.html-c83b4f39.js b/assets/things-I-have-to-vent-about-vue.html-9d87b484.js similarity index 68% rename from assets/things-I-have-to-vent-about-vue.html-c83b4f39.js rename to assets/things-I-have-to-vent-about-vue.html-9d87b484.js index 2abd940b..36e4d06e 100644 --- a/assets/things-I-have-to-vent-about-vue.html-c83b4f39.js +++ b/assets/things-I-have-to-vent-about-vue.html-9d87b484.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-1e305501","path":"/daily/things-I-have-to-vent-about-vue.html","title":"对Vue不得不吐槽的事","lang":"zh-CN","frontmatter":{"date":"2023-07-30T00:00:00.000Z","tag":["Frontend","Daily","Video"],"description":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/things-I-have-to-vent-about-vue.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"对Vue不得不吐槽的事"}],["meta",{"property":"og:description","content":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-30T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"对Vue不得不吐槽的事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-30T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.42,"words":126},"filePathRelative":"daily/things-I-have-to-vent-about-vue.md","localizedDate":"2023年7月30日","excerpt":"

对Vue不得不吐槽的事

\\n

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

\\n

总结一下,我对Vue生态不满的地方在于:

\\n
    \\n
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. \\n
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. \\n
\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-1e305501","path":"/daily/things-I-have-to-vent-about-vue.html","title":"对Vue不得不吐槽的事","lang":"zh-CN","frontmatter":{"date":"2023-07-30T00:00:00.000Z","tag":["Frontend","Daily","Video"],"description":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/things-I-have-to-vent-about-vue.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"对Vue不得不吐槽的事"}],["meta",{"property":"og:description","content":"对Vue不得不吐槽的事 为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了! 总结一下,我对Vue生态不满的地方在于: Vue总是破坏性升级,新技术完全不管旧用户的体验 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Frontend"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-07-30T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"对Vue不得不吐槽的事\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-30T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.42,"words":126},"filePathRelative":"daily/things-I-have-to-vent-about-vue.md","localizedDate":"2023年7月30日","excerpt":"

对Vue不得不吐槽的事

\\n

为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

\\n

总结一下,我对Vue生态不满的地方在于:

\\n
    \\n
  1. Vue总是破坏性升级,新技术完全不管旧用户的体验
  2. \\n
  3. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
  4. \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/unit-testing-overview.html-90b924eb.js b/assets/unit-testing-overview.html-8f4834a6.js similarity index 87% rename from assets/unit-testing-overview.html-90b924eb.js rename to assets/unit-testing-overview.html-8f4834a6.js index 6eacde31..4e9f7529 100644 --- a/assets/unit-testing-overview.html-90b924eb.js +++ b/assets/unit-testing-overview.html-8f4834a6.js @@ -1,4 +1,4 @@ -import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,a as n,b as s,d as t,f as l,e}from"./app-ee4d23bf.js";const u={},r=n("h1",{id:"单元测试概述",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#单元测试概述","aria-hidden":"true"},"#"),s(" 单元测试概述")],-1),d=n("h2",{id:"why",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#why","aria-hidden":"true"},"#"),s(" Why")],-1),k=n("p",null,"为什么要做单元测试?或者说,为什么要写测试代码?",-1),v=n("p",null,"个人总结为以下两点:",-1),m={href:"https://www.stickyminds.com/article/shift-left-approach-software-testing",target:"_blank",rel:"noopener noreferrer"},h=n("br",null,null,-1),b=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1690532448643-e09bebb0-66f2-49f9-8686-d4a8c6b5d590.png",alt:""},null,-1),f=n("li",null,"形成资产,方便回归测试,后续迭代重构、维护有保障",-1),g=e('

以上两点,是研发人员写测试代码的本质理由,无论什么类型的测试代码、研发人员用的什么语言、框架都适用。

What

写测试代码究竟是写什么?

个人认为测试代码主要是为了搞清楚两件事:

  1. 源码到底会不会在目标环境执行?
  2. 源码的执行结果是否符合预期?

第一件事,引出了 code coverage 代码覆盖率的概念;第二件事,则引出了 assert 断言的概念。

How

测试代码的风格

',8),_={href:"https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80",target:"_blank",rel:"noopener noreferrer"},w=e(`
  1. 组装参数
  2. 执行目标方法
  3. 执行断言
  @Test
+import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as i,a as n,b as s,d as t,e as l,f as e}from"./app-dcf5468f.js";const u={},r=n("h1",{id:"单元测试概述",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#单元测试概述","aria-hidden":"true"},"#"),s(" 单元测试概述")],-1),d=n("h2",{id:"why",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#why","aria-hidden":"true"},"#"),s(" Why")],-1),k=n("p",null,"为什么要做单元测试?或者说,为什么要写测试代码?",-1),v=n("p",null,"个人总结为以下两点:",-1),m={href:"https://www.stickyminds.com/article/shift-left-approach-software-testing",target:"_blank",rel:"noopener noreferrer"},h=n("br",null,null,-1),b=n("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1690532448643-e09bebb0-66f2-49f9-8686-d4a8c6b5d590.png",alt:""},null,-1),f=n("li",null,"形成资产,方便回归测试,后续迭代重构、维护有保障",-1),g=e('

以上两点,是研发人员写测试代码的本质理由,无论什么类型的测试代码、研发人员用的什么语言、框架都适用。

What

写测试代码究竟是写什么?

个人认为测试代码主要是为了搞清楚两件事:

  1. 源码到底会不会在目标环境执行?
  2. 源码的执行结果是否符合预期?

第一件事,引出了 code coverage 代码覆盖率的概念;第二件事,则引出了 assert 断言的概念。

How

测试代码的风格

',8),_={href:"https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80",target:"_blank",rel:"noopener noreferrer"},w=e(`
  1. 组装参数
  2. 执行目标方法
  3. 执行断言
  @Test
   public void testHash() throws Exception {
     // Arrange
     String plainText = JSON.toJSONString(licenseRequest);
diff --git a/assets/unit-testing-overview.html-5d5018ec.js b/assets/unit-testing-overview.html-d4b6190c.js
similarity index 66%
rename from assets/unit-testing-overview.html-5d5018ec.js
rename to assets/unit-testing-overview.html-d4b6190c.js
index c5fe2a7d..fa7499c6 100644
--- a/assets/unit-testing-overview.html-5d5018ec.js
+++ b/assets/unit-testing-overview.html-d4b6190c.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-113531b4","path":"/software-testing/unit-testing-overview.html","title":"单元测试概述","lang":"zh-CN","frontmatter":{"date":"2023-07-28T00:00:00.000Z","tag":["Testing"],"description":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/unit-testing-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"单元测试概述"}],["meta",{"property":"og:description","content":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-07-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"单元测试概述\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"How","slug":"how","link":"#how","children":[{"level":3,"title":"测试代码的风格","slug":"测试代码的风格","link":"#测试代码的风格","children":[]},{"level":3,"title":"测试难点","slug":"测试难点","link":"#测试难点","children":[]},{"level":3,"title":"常用工具","slug":"常用工具","link":"#常用工具","children":[]}]},{"level":2,"title":"Bad Examples","slug":"bad-examples","link":"#bad-examples","children":[{"level":3,"title":"没有测试类","slug":"没有测试类","link":"#没有测试类","children":[]},{"level":3,"title":"没有断言","slug":"没有断言","link":"#没有断言","children":[]},{"level":3,"title":"无法重复执行","slug":"无法重复执行","link":"#无法重复执行","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.97,"words":892},"filePathRelative":"software-testing/unit-testing-overview.md","localizedDate":"2023年7月28日","excerpt":"

单元测试概述

\\n

Why

\\n

为什么要做单元测试?或者说,为什么要写测试代码?

\\n

个人总结为以下两点:

\\n
    \\n
  1. 测试左移,降低修复bug的成本
    \\n\\"\\"
  2. \\n
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. \\n
\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-113531b4","path":"/software-testing/unit-testing-overview.html","title":"单元测试概述","lang":"zh-CN","frontmatter":{"date":"2023-07-28T00:00:00.000Z","tag":["Testing"],"description":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/unit-testing-overview.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"单元测试概述"}],["meta",{"property":"og:description","content":"单元测试概述 Why 为什么要做单元测试?或者说,为什么要写测试代码? 个人总结为以下两点: 测试左移,降低修复bug的成本 形成资产,方便回归测试,后续迭代重构、维护有保障"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-07-28T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"单元测试概述\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-07-28T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"How","slug":"how","link":"#how","children":[{"level":3,"title":"测试代码的风格","slug":"测试代码的风格","link":"#测试代码的风格","children":[]},{"level":3,"title":"测试难点","slug":"测试难点","link":"#测试难点","children":[]},{"level":3,"title":"常用工具","slug":"常用工具","link":"#常用工具","children":[]}]},{"level":2,"title":"Bad Examples","slug":"bad-examples","link":"#bad-examples","children":[{"level":3,"title":"没有测试类","slug":"没有测试类","link":"#没有测试类","children":[]},{"level":3,"title":"没有断言","slug":"没有断言","link":"#没有断言","children":[]},{"level":3,"title":"无法重复执行","slug":"无法重复执行","link":"#无法重复执行","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.97,"words":892},"filePathRelative":"software-testing/unit-testing-overview.md","localizedDate":"2023年7月28日","excerpt":"

单元测试概述

\\n

Why

\\n

为什么要做单元测试?或者说,为什么要写测试代码?

\\n

个人总结为以下两点:

\\n
    \\n
  1. 测试左移,降低修复bug的成本
    \\n\\"\\"
  2. \\n
  3. 形成资产,方便回归测试,后续迭代重构、维护有保障
  4. \\n
\\n","autoDesc":true}');export{e as data}; diff --git a/assets/use-RestAssured-for-api-testing.html-0af1208b.js b/assets/use-RestAssured-for-api-testing.html-69c1f333.js similarity index 75% rename from assets/use-RestAssured-for-api-testing.html-0af1208b.js rename to assets/use-RestAssured-for-api-testing.html-69c1f333.js index c0451728..c5fb3ce9 100644 --- a/assets/use-RestAssured-for-api-testing.html-0af1208b.js +++ b/assets/use-RestAssured-for-api-testing.html-69c1f333.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-65b23736","path":"/software-testing/use-RestAssured-for-api-testing.html","title":"使用 RestAssured 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-06-09T00:00:00.000Z","tag":["Java","Testing"],"description":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-RestAssured-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 RestAssured 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-06-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 RestAssured 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-06-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"为什么不用Postman","slug":"为什么不用postman","link":"#为什么不用postman","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"快速上手","slug":"快速上手","link":"#快速上手","children":[]},{"level":2,"title":"通用设置","slug":"通用设置","link":"#通用设置","children":[]},{"level":2,"title":"请求示例","slug":"请求示例","link":"#请求示例","children":[]},{"level":2,"title":"接口依赖","slug":"接口依赖","link":"#接口依赖","children":[]},{"level":2,"title":"上传示例","slug":"上传示例","link":"#上传示例","children":[]},{"level":2,"title":"下载示例","slug":"下载示例","link":"#下载示例","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他问题","slug":"其他问题","link":"#其他问题","children":[{"level":3,"title":"为什么不用 Pytest","slug":"为什么不用-pytest","link":"#为什么不用-pytest","children":[]},{"level":3,"title":"这也是单元测试吗","slug":"这也是单元测试吗","link":"#这也是单元测试吗","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":6.99,"words":2096},"filePathRelative":"software-testing/use-RestAssured-for-api-testing.md","localizedDate":"2023年6月9日","excerpt":"

使用 RestAssured 进行 API 测试

\\n

前言

\\n

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

\\n

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

\\n

What

\\n

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

\\n

Why

\\n

为什么要做 API 测试呢?

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-65b23736","path":"/software-testing/use-RestAssured-for-api-testing.html","title":"使用 RestAssured 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-06-09T00:00:00.000Z","tag":["Java","Testing"],"description":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-RestAssured-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 RestAssured 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 RestAssured 进行 API 测试 前言 本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。 本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。 What 什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。 Why 为什么要做 API 测试呢?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-06-09T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 RestAssured 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-06-09T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"What","slug":"what","link":"#what","children":[]},{"level":2,"title":"Why","slug":"why","link":"#why","children":[]},{"level":2,"title":"为什么不用Postman","slug":"为什么不用postman","link":"#为什么不用postman","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"快速上手","slug":"快速上手","link":"#快速上手","children":[]},{"level":2,"title":"通用设置","slug":"通用设置","link":"#通用设置","children":[]},{"level":2,"title":"请求示例","slug":"请求示例","link":"#请求示例","children":[]},{"level":2,"title":"接口依赖","slug":"接口依赖","link":"#接口依赖","children":[]},{"level":2,"title":"上传示例","slug":"上传示例","link":"#上传示例","children":[]},{"level":2,"title":"下载示例","slug":"下载示例","link":"#下载示例","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他问题","slug":"其他问题","link":"#其他问题","children":[{"level":3,"title":"为什么不用 Pytest","slug":"为什么不用-pytest","link":"#为什么不用-pytest","children":[]},{"level":3,"title":"这也是单元测试吗","slug":"这也是单元测试吗","link":"#这也是单元测试吗","children":[]}]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.99,"words":2096},"filePathRelative":"software-testing/use-RestAssured-for-api-testing.md","localizedDate":"2023年6月9日","excerpt":"

使用 RestAssured 进行 API 测试

\\n

前言

\\n

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

\\n

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

\\n

What

\\n

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

\\n

Why

\\n

为什么要做 API 测试呢?

","autoDesc":true}');export{e as data}; diff --git a/assets/use-RestAssured-for-api-testing.html-12bcc171.js b/assets/use-RestAssured-for-api-testing.html-9aa3329d.js similarity index 99% rename from assets/use-RestAssured-for-api-testing.html-12bcc171.js rename to assets/use-RestAssured-for-api-testing.html-9aa3329d.js index 78ec6113..6dff06a5 100644 --- a/assets/use-RestAssured-for-api-testing.html-12bcc171.js +++ b/assets/use-RestAssured-for-api-testing.html-9aa3329d.js @@ -1,4 +1,4 @@ -import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as c,c as u,a as s,b as n,d as t,e as p}from"./app-ee4d23bf.js";const l={},i=p('

使用 RestAssured 进行 API 测试

前言

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

What

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

Why

为什么要做 API 测试呢?

考虑有过这样的场景:

  • 加一个新功能,自测没问题,结果被测试人员发现一个旧模块出了问题,感到措手不及
  • 后端写好了接口,前端还没开发好界面,于是感觉不方便自测,因为没有界面,只好催前端快去做页面

API 测试就是来解决上述问题的。做 API 测试的原因有:

  • 必要性:做回归测试,避免添加新功能时破坏旧功能。
  • 便利性:方便本地调试,不用部署到线上,依赖界面去测试。
  • 资产化:让测试用例变成资产,与团队共享。

当然,要做好 API 测试,还要接受这样的认知: 接口自动化测试并不仅仅是测试人员事情,研发人员也有责任把它做好。 否则,研发人员难免会觉得这不关我的事, 从而不愿意写这种代码。 建议研发人员从以下方便思考其好处,提升行动的积极性:

  • 减少阻塞,接口自测不再依赖前端
  • 提高效率,本地就能自测,不用把应用部署到线上环境
  • 提高质量,减少部署到研发环境、前端一调用接口就 500 的情况

为什么不用Postman

Postman 确实是符合直觉的接口调试的第一选项。 但注意,调试不等于测试。

Postman 在实践过程中,最大的问题在于,无法将测试用例有效地资产化:

  • 你会在 Postman 里写断言吗?很少吧,你其实是在用肉眼去检查接口成功与否,这本质还是手工测试
  • 你的 Postman 数据能与团队共享吗?不能吧,大多数人的 Postman 数据是在本地的,也不会去付费创建一个团队以共享数据
  • 你的 Postman 数据在有版本管理吗?没有吧,大多数人的 Postman 数据是与源代码分离的,不利于维护与管理
',18),k={href:"https://github.com/postmanlabs/newman",target:"_blank",rel:"noopener noreferrer"},r=p(`

考虑源代码是 Java,使用 RestAssured,编写 API 测试代码用同一种语言,可以减少使用者的心智负担较轻;并且与源代码放在同一个 Git 仓库中,易于管理。

因此,我仍然会使用 Postman,但更多是把它应用在出现线上问题时,直接复制一个 cURL 用来复现、排查问题的情况。

安装

下面将介绍如何用 Maven 安装 RestAssured。

复制以下内容到 pom.xml 即可。

    <!-- RestAssured for api testing -->
+import{_ as o}from"./plugin-vue_export-helper-c27b6911.js";import{r as e,o as c,c as u,a as s,b as n,d as t,f as p}from"./app-dcf5468f.js";const l={},i=p('

使用 RestAssured 进行 API 测试

前言

本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

What

什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

Why

为什么要做 API 测试呢?

考虑有过这样的场景:

  • 加一个新功能,自测没问题,结果被测试人员发现一个旧模块出了问题,感到措手不及
  • 后端写好了接口,前端还没开发好界面,于是感觉不方便自测,因为没有界面,只好催前端快去做页面

API 测试就是来解决上述问题的。做 API 测试的原因有:

  • 必要性:做回归测试,避免添加新功能时破坏旧功能。
  • 便利性:方便本地调试,不用部署到线上,依赖界面去测试。
  • 资产化:让测试用例变成资产,与团队共享。

当然,要做好 API 测试,还要接受这样的认知: 接口自动化测试并不仅仅是测试人员事情,研发人员也有责任把它做好。 否则,研发人员难免会觉得这不关我的事, 从而不愿意写这种代码。 建议研发人员从以下方便思考其好处,提升行动的积极性:

  • 减少阻塞,接口自测不再依赖前端
  • 提高效率,本地就能自测,不用把应用部署到线上环境
  • 提高质量,减少部署到研发环境、前端一调用接口就 500 的情况

为什么不用Postman

Postman 确实是符合直觉的接口调试的第一选项。 但注意,调试不等于测试。

Postman 在实践过程中,最大的问题在于,无法将测试用例有效地资产化:

  • 你会在 Postman 里写断言吗?很少吧,你其实是在用肉眼去检查接口成功与否,这本质还是手工测试
  • 你的 Postman 数据能与团队共享吗?不能吧,大多数人的 Postman 数据是在本地的,也不会去付费创建一个团队以共享数据
  • 你的 Postman 数据在有版本管理吗?没有吧,大多数人的 Postman 数据是与源代码分离的,不利于维护与管理
',18),k={href:"https://github.com/postmanlabs/newman",target:"_blank",rel:"noopener noreferrer"},r=p(`

考虑源代码是 Java,使用 RestAssured,编写 API 测试代码用同一种语言,可以减少使用者的心智负担较轻;并且与源代码放在同一个 Git 仓库中,易于管理。

因此,我仍然会使用 Postman,但更多是把它应用在出现线上问题时,直接复制一个 cURL 用来复现、排查问题的情况。

安装

下面将介绍如何用 Maven 安装 RestAssured。

复制以下内容到 pom.xml 即可。

    <!-- RestAssured for api testing -->
     <dependency>
         <groupId>io.rest-assured</groupId>
         <artifactId>rest-assured</artifactId>
diff --git a/assets/use-claude2-instead-of-chatgpt.html-69dff4f8.js b/assets/use-claude2-instead-of-chatgpt.html-22369824.js
similarity index 63%
rename from assets/use-claude2-instead-of-chatgpt.html-69dff4f8.js
rename to assets/use-claude2-instead-of-chatgpt.html-22369824.js
index aad684da..f9ec0497 100644
--- a/assets/use-claude2-instead-of-chatgpt.html-69dff4f8.js
+++ b/assets/use-claude2-instead-of-chatgpt.html-22369824.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-404740fa","path":"/daily/use-claude2-instead-of-chatgpt.html","title":"再见ChatGPT,我选择Claude2!","lang":"zh-CN","frontmatter":{"date":"2023-08-08T00:00:00.000Z","tag":["Video","AI","Tool"],"description":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/use-claude2-instead-of-chatgpt.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再见ChatGPT,我选择Claude2!"}],["meta",{"property":"og:description","content":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2023-08-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再见ChatGPT,我选择Claude2!\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.76,"words":1128},"filePathRelative":"daily/use-claude2-instead-of-chatgpt.md","localizedDate":"2023年8月8日","excerpt":"

再见ChatGPT,我选择Claude2!

\\n

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

\\n

首先要评测的当然是ChatGPT了,因为最早用的就是它。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-404740fa","path":"/daily/use-claude2-instead-of-chatgpt.html","title":"再见ChatGPT,我选择Claude2!","lang":"zh-CN","frontmatter":{"date":"2023-08-08T00:00:00.000Z","tag":["Video","AI","Tool"],"description":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/use-claude2-instead-of-chatgpt.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"再见ChatGPT,我选择Claude2!"}],["meta",{"property":"og:description","content":"再见ChatGPT,我选择Claude2! 大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。 首先要评测的当然是ChatGPT了,因为最早用的就是它。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:tag","content":"AI"}],["meta",{"property":"article:tag","content":"Tool"}],["meta",{"property":"article:published_time","content":"2023-08-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"再见ChatGPT,我选择Claude2!\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.76,"words":1128},"filePathRelative":"daily/use-claude2-instead-of-chatgpt.md","localizedDate":"2023年8月8日","excerpt":"

再见ChatGPT,我选择Claude2!

\\n

大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

\\n

首先要评测的当然是ChatGPT了,因为最早用的就是它。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/use-claude2-instead-of-chatgpt.html-8a0e6b21.js b/assets/use-claude2-instead-of-chatgpt.html-e336cea0.js similarity index 98% rename from assets/use-claude2-instead-of-chatgpt.html-8a0e6b21.js rename to assets/use-claude2-instead-of-chatgpt.html-e336cea0.js index dea63889..ff41069a 100644 --- a/assets/use-claude2-instead-of-chatgpt.html-8a0e6b21.js +++ b/assets/use-claude2-instead-of-chatgpt.html-e336cea0.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as m,f as l,d as a,a as e,b as t,e as d}from"./app-ee4d23bf.js";const s={},g=e("h1",{id:"再见chatgpt-我选择claude2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#再见chatgpt-我选择claude2","aria-hidden":"true"},"#"),t(" 再见ChatGPT,我选择Claude2!")],-1),h=e("p",null,"大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。",-1),b=e("p",null,"首先要评测的当然是ChatGPT了,因为最早用的就是它。",-1),p=e("figure",null,[e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497563137-33a17053-c9a2-47bf-bc2e-25f4d567d406.jpeg",alt:"",width:"200",tabindex:"0"}),e("figcaption")],-1),u=d('

现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

当然要说口语练习的话,可以用手机App Call Annie。

不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

Claude 2 有着超长的token,100K。


并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

',9),_={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},f=e("br",null,null,-1),w=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497388132-d255ee3f-f1e8-4ae7-8870-5cf51d9906a9.png",alt:""},null,-1),k=e("br",null,null,-1),v={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},y=e("br",null,null,-1),C=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691141726927-7d6cb97a-8e17-4687-abe1-22e46ed3945a.png",alt:""},null,-1),B=e("p",null,[t("最后,来看看面对恶意提问,Claude2是如何回答的:"),e("br"),e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691499213415-94b6c6b0-b59b-43b9-96b5-b346a2ee7e1f.png",alt:""})],-1);function I(N,A){const c=n("BiliBili"),i=n("ExternalLinkIcon");return o(),m("div",null,[g,h,b,l(" more "),p,a(c,{bvid:"BV1yj411z7zr"}),u,e("p",null,[t("那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是"),e("a",_,[t("poe.com"),a(i)]),t("。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。"),f,w,k,t(" 这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是"),e("a",v,[t("poe.com"),a(i)]),t(" + claude2,这将是一个体验极佳的使用方案。"),y,C]),B])}const G=r(s,[["render",I],["__file","use-claude2-instead-of-chatgpt.html.vue"]]);export{G as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as n,o,c as m,e as l,d as a,a as e,b as t,f as d}from"./app-dcf5468f.js";const s={},g=e("h1",{id:"再见chatgpt-我选择claude2",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#再见chatgpt-我选择claude2","aria-hidden":"true"},"#"),t(" 再见ChatGPT,我选择Claude2!")],-1),h=e("p",null,"大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。",-1),b=e("p",null,"首先要评测的当然是ChatGPT了,因为最早用的就是它。",-1),p=e("figure",null,[e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497563137-33a17053-c9a2-47bf-bc2e-25f4d567d406.jpeg",alt:"",width:"200",tabindex:"0"}),e("figcaption")],-1),u=d('

现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

当然要说口语练习的话,可以用手机App Call Annie。

不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

Claude 2 有着超长的token,100K。


并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

',9),_={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},f=e("br",null,null,-1),w=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691497388132-d255ee3f-f1e8-4ae7-8870-5cf51d9906a9.png",alt:""},null,-1),k=e("br",null,null,-1),v={href:"https://poe.com/login",target:"_blank",rel:"noopener noreferrer"},y=e("br",null,null,-1),C=e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691141726927-7d6cb97a-8e17-4687-abe1-22e46ed3945a.png",alt:""},null,-1),B=e("p",null,[t("最后,来看看面对恶意提问,Claude2是如何回答的:"),e("br"),e("img",{src:"https://raw.githubusercontent.com/levy9527/image-holder/main/md-image-kit/1691499213415-94b6c6b0-b59b-43b9-96b5-b346a2ee7e1f.png",alt:""})],-1);function I(N,A){const c=n("BiliBili"),i=n("ExternalLinkIcon");return o(),m("div",null,[g,h,b,l(" more "),p,a(c,{bvid:"BV1yj411z7zr"}),u,e("p",null,[t("那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是"),e("a",_,[t("poe.com"),a(i)]),t("。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。"),f,w,k,t(" 这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是"),e("a",v,[t("poe.com"),a(i)]),t(" + claude2,这将是一个体验极佳的使用方案。"),y,C]),B])}const G=r(s,[["render",I],["__file","use-claude2-instead-of-chatgpt.html.vue"]]);export{G as default}; diff --git a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-6b7d6b97.js b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-0a2b5bae.js similarity index 99% rename from assets/use-command-line-tool-to-manage-gitlab-merge-request.html-6b7d6b97.js rename to assets/use-command-line-tool-to-manage-gitlab-merge-request.html-0a2b5bae.js index e866d12c..37260706 100644 --- a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-6b7d6b97.js +++ b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-0a2b5bae.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r,o,c as l,a,b as e,d as n,e as i}from"./app-ee4d23bf.js";const c={},d=i('

操作 Gitlab MR 的命令行工具

背景

为什么开发这个工具?主要解决以下问题:

  1. 提测、上 UAT 时,避免漏合代码。
  2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

安装

解压zip

下载并解压文件:

',8),p={href:"https://r0e715v8ejr.feishu.cn/file/IxH4bYAOkowK08xSid1crXcSnRo",target:"_blank",rel:"noopener noreferrer"},g={href:"https://r0e715v8ejr.feishu.cn/file/ORa3buA3donF3TxxPVwcHSYnnQb",target:"_blank",rel:"noopener noreferrer"},m=a("h3",{id:"安装git-bash",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#安装git-bash","aria-hidden":"true"},"#"),e(" 安装git bash")],-1),u=a("p",null,[e("Windows系统才要安装。"),a("br"),e(" 如果 git bash 版本不足 2.41.0,最好安装最新版本。")],-1),h={href:"https://gitforwindows.org/",target:"_blank",rel:"noopener noreferrer"},b=i(`

配置

新增文件

vi ~/.mr-config.json
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r,o,c as l,a,b as e,d as n,f as i}from"./app-dcf5468f.js";const c={},d=i('

操作 Gitlab MR 的命令行工具

背景

为什么开发这个工具?主要解决以下问题:

  1. 提测、上 UAT 时,避免漏合代码。
  2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

安装

解压zip

下载并解压文件:

',8),p={href:"https://r0e715v8ejr.feishu.cn/file/IxH4bYAOkowK08xSid1crXcSnRo",target:"_blank",rel:"noopener noreferrer"},g={href:"https://r0e715v8ejr.feishu.cn/file/ORa3buA3donF3TxxPVwcHSYnnQb",target:"_blank",rel:"noopener noreferrer"},m=a("h3",{id:"安装git-bash",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#安装git-bash","aria-hidden":"true"},"#"),e(" 安装git bash")],-1),u=a("p",null,[e("Windows系统才要安装。"),a("br"),e(" 如果 git bash 版本不足 2.41.0,最好安装最新版本。")],-1),h={href:"https://gitforwindows.org/",target:"_blank",rel:"noopener noreferrer"},b=i(`

配置

新增文件

vi ~/.mr-config.json
 

复制以下内容:

{
   "gitlab_url":"https://your-gitlab.com",
   "gitlab_token":"your-token",
diff --git a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-a9aaa8d1.js b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-faa5903e.js
similarity index 73%
rename from assets/use-command-line-tool-to-manage-gitlab-merge-request.html-a9aaa8d1.js
rename to assets/use-command-line-tool-to-manage-gitlab-merge-request.html-faa5903e.js
index 5b141cc1..57a1cb3d 100644
--- a/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-a9aaa8d1.js
+++ b/assets/use-command-line-tool-to-manage-gitlab-merge-request.html-faa5903e.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-071be141","path":"/git/use-command-line-tool-to-manage-gitlab-merge-request.html","title":"操作 Gitlab MR 的命令行工具","lang":"zh-CN","frontmatter":{"date":"2023-03-23T00:00:00.000Z","tag":["Git","GitLab","Python"],"description":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/use-command-line-tool-to-manage-gitlab-merge-request.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"操作 Gitlab MR 的命令行工具"}],["meta",{"property":"og:description","content":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"操作 Gitlab MR 的命令行工具\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-23T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[{"level":3,"title":"解压zip","slug":"解压zip","link":"#解压zip","children":[]},{"level":3,"title":"安装git bash","slug":"安装git-bash","link":"#安装git-bash","children":[]}]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[{"level":3,"title":"gitlab_token","slug":"gitlab-token","link":"#gitlab-token","children":[]},{"level":3,"title":"codebases","slug":"codebases","link":"#codebases","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"IDEA","slug":"idea","link":"#idea","children":[]}]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"创建MR","slug":"创建mr","link":"#创建mr","children":[]},{"level":3,"title":"查看MR","slug":"查看mr","link":"#查看mr","children":[]},{"level":3,"title":"合并MR","slug":"合并mr","link":"#合并mr","children":[]},{"level":3,"title":"冲突处理","slug":"冲突处理","link":"#冲突处理","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":4.42,"words":1325},"filePathRelative":"git/use-command-line-tool-to-manage-gitlab-merge-request.md","localizedDate":"2023年3月23日","excerpt":"

操作 Gitlab MR 的命令行工具

\\n

背景

\\n

为什么开发这个工具?主要解决以下问题:

\\n
    \\n
  1. 提测、上 UAT 时,避免漏合代码。
  2. \\n
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. \\n
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. \\n
\\n

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
\\n并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
\\n对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-071be141","path":"/git/use-command-line-tool-to-manage-gitlab-merge-request.html","title":"操作 Gitlab MR 的命令行工具","lang":"zh-CN","frontmatter":{"date":"2023-03-23T00:00:00.000Z","tag":["Git","GitLab","Python"],"description":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。","head":[["meta",{"property":"og:url","content":"https://levy.vip/git/use-command-line-tool-to-manage-gitlab-merge-request.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"操作 Gitlab MR 的命令行工具"}],["meta",{"property":"og:description","content":"操作 Gitlab MR 的命令行工具 背景 为什么开发这个工具?主要解决以下问题: 提测、上 UAT 时,避免漏合代码。 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?” 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。 可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。 并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。 对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Git"}],["meta",{"property":"article:tag","content":"GitLab"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:published_time","content":"2023-03-23T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"操作 Gitlab MR 的命令行工具\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-03-23T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[{"level":3,"title":"解压zip","slug":"解压zip","link":"#解压zip","children":[]},{"level":3,"title":"安装git bash","slug":"安装git-bash","link":"#安装git-bash","children":[]}]},{"level":2,"title":"配置","slug":"配置","link":"#配置","children":[{"level":3,"title":"gitlab_token","slug":"gitlab-token","link":"#gitlab-token","children":[]},{"level":3,"title":"codebases","slug":"codebases","link":"#codebases","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"IDEA","slug":"idea","link":"#idea","children":[]}]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"创建MR","slug":"创建mr","link":"#创建mr","children":[]},{"level":3,"title":"查看MR","slug":"查看mr","link":"#查看mr","children":[]},{"level":3,"title":"合并MR","slug":"合并mr","link":"#合并mr","children":[]},{"level":3,"title":"冲突处理","slug":"冲突处理","link":"#冲突处理","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":4.42,"words":1325},"filePathRelative":"git/use-command-line-tool-to-manage-gitlab-merge-request.md","localizedDate":"2023年3月23日","excerpt":"

操作 Gitlab MR 的命令行工具

\\n

背景

\\n

为什么开发这个工具?主要解决以下问题:

\\n
    \\n
  1. 提测、上 UAT 时,避免漏合代码。
  2. \\n
  3. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
  4. \\n
  5. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。
  6. \\n
\\n

可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
\\n并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
\\n对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

","autoDesc":true}');export{e as data}; diff --git a/assets/use-cypress-for-e2e-testing.html-34aa8b61.js b/assets/use-cypress-for-e2e-testing.html-4617f19d.js similarity index 75% rename from assets/use-cypress-for-e2e-testing.html-34aa8b61.js rename to assets/use-cypress-for-e2e-testing.html-4617f19d.js index a6349333..d75f9efa 100644 --- a/assets/use-cypress-for-e2e-testing.html-34aa8b61.js +++ b/assets/use-cypress-for-e2e-testing.html-4617f19d.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-c488ac58","path":"/software-testing/use-cypress-for-e2e-testing.html","title":"使用 Cypress 进行端对端测试","lang":"zh-CN","frontmatter":{"date":"2020-12-08T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-cypress-for-e2e-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Cypress 进行端对端测试"}],["meta",{"property":"og:description","content":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2020-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Cypress 进行端对端测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么写端对端测试","slug":"为什么写端对端测试","link":"#为什么写端对端测试","children":[]},{"level":2,"title":"为什么用 Cypress","slug":"为什么用-cypress","link":"#为什么用-cypress","children":[]},{"level":2,"title":"快速开始","slug":"快速开始","link":"#快速开始","children":[{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":3,"title":"加速下载","slug":"加速下载","link":"#加速下载","children":[]},{"level":3,"title":"目录结构","slug":"目录结构","link":"#目录结构","children":[]},{"level":3,"title":"与 Jest 协同工作","slug":"与-jest-协同工作","link":"#与-jest-协同工作","children":[]},{"level":3,"title":"检查依赖及生产安装依赖命令","slug":"检查依赖及生产安装依赖命令","link":"#检查依赖及生产安装依赖命令","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"更复杂的示例","slug":"更复杂的示例","link":"#更复杂的示例","children":[]}]},{"level":2,"title":"结合TypeScript","slug":"结合typescript","link":"#结合typescript","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"直接运行 Cypress","slug":"直接运行-cypress","link":"#直接运行-cypress","children":[]},{"level":3,"title":"使用 start-server-and-test","slug":"使用-start-server-and-test","link":"#使用-start-server-and-test","children":[]},{"level":3,"title":"This job is stuck","slug":"this-job-is-stuck","link":"#this-job-is-stuck","children":[]},{"level":3,"title":"Cypress Dashbord","slug":"cypress-dashbord","link":"#cypress-dashbord","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"拓展阅读","slug":"拓展阅读","link":"#拓展阅读","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":8.04,"words":2413},"filePathRelative":"software-testing/use-cypress-for-e2e-testing.md","localizedDate":"2020年12月8日","excerpt":"

使用 Cypress 进行端对端测试

\\n

为什么写端对端测试

\\n

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

\\n

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

\\n

为什么用 Cypress

\\n

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-c488ac58","path":"/software-testing/use-cypress-for-e2e-testing.html","title":"使用 Cypress 进行端对端测试","lang":"zh-CN","frontmatter":{"date":"2020-12-08T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-cypress-for-e2e-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Cypress 进行端对端测试"}],["meta",{"property":"og:description","content":"使用 Cypress 进行端对端测试 为什么写端对端测试 写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。 谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。 为什么用 Cypress 文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2020-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Cypress 进行端对端测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2020-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"为什么写端对端测试","slug":"为什么写端对端测试","link":"#为什么写端对端测试","children":[]},{"level":2,"title":"为什么用 Cypress","slug":"为什么用-cypress","link":"#为什么用-cypress","children":[]},{"level":2,"title":"快速开始","slug":"快速开始","link":"#快速开始","children":[{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":3,"title":"加速下载","slug":"加速下载","link":"#加速下载","children":[]},{"level":3,"title":"目录结构","slug":"目录结构","link":"#目录结构","children":[]},{"level":3,"title":"与 Jest 协同工作","slug":"与-jest-协同工作","link":"#与-jest-协同工作","children":[]},{"level":3,"title":"检查依赖及生产安装依赖命令","slug":"检查依赖及生产安装依赖命令","link":"#检查依赖及生产安装依赖命令","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"更复杂的示例","slug":"更复杂的示例","link":"#更复杂的示例","children":[]}]},{"level":2,"title":"结合TypeScript","slug":"结合typescript","link":"#结合typescript","children":[]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"直接运行 Cypress","slug":"直接运行-cypress","link":"#直接运行-cypress","children":[]},{"level":3,"title":"使用 start-server-and-test","slug":"使用-start-server-and-test","link":"#使用-start-server-and-test","children":[]},{"level":3,"title":"This job is stuck","slug":"this-job-is-stuck","link":"#this-job-is-stuck","children":[]},{"level":3,"title":"Cypress Dashbord","slug":"cypress-dashbord","link":"#cypress-dashbord","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"拓展阅读","slug":"拓展阅读","link":"#拓展阅读","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":8.04,"words":2413},"filePathRelative":"software-testing/use-cypress-for-e2e-testing.md","localizedDate":"2020年12月8日","excerpt":"

使用 Cypress 进行端对端测试

\\n

为什么写端对端测试

\\n

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

\\n

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

\\n

为什么用 Cypress

\\n

文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypress

","autoDesc":true}');export{e as data}; diff --git a/assets/use-cypress-for-e2e-testing.html-b5ce58b8.js b/assets/use-cypress-for-e2e-testing.html-9e7fedb6.js similarity index 99% rename from assets/use-cypress-for-e2e-testing.html-b5ce58b8.js rename to assets/use-cypress-for-e2e-testing.html-9e7fedb6.js index 49f25142..09045dbc 100644 --- a/assets/use-cypress-for-e2e-testing.html-b5ce58b8.js +++ b/assets/use-cypress-for-e2e-testing.html-9e7fedb6.js @@ -1,4 +1,4 @@ -import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c,a as s,b as n,d as e,e as t}from"./app-ee4d23bf.js";const l={},r=t('

使用 Cypress 进行端对端测试

为什么写端对端测试

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

为什么用 Cypress

',5),d={href:"https://docs.cypress.io/guides/overview/why-cypress.html",target:"_blank",rel:"noopener noreferrer"},u=t(`

缺点:全英文档

快速开始

安装

yarn add cypress -D
+import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c,a as s,b as n,d as e,f as t}from"./app-dcf5468f.js";const l={},r=t('

使用 Cypress 进行端对端测试

为什么写端对端测试

写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

为什么用 Cypress

',5),d={href:"https://docs.cypress.io/guides/overview/why-cypress.html",target:"_blank",rel:"noopener noreferrer"},u=t(`

缺点:全英文档

快速开始

安装

yarn add cypress -D
 
`,4),v={href:"https://docs.cypress.io/guides/getting-started/installing-cypress.html#npm-install",target:"_blank",rel:"noopener noreferrer"},m=t(`

一般而言,国内用户都会在上述过程中卡住,最好在命令行设置网络代理后再下载(懂的自然懂)。

export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
 

如果是在 CI 环境,记得缓存 cypress binary。

安装完后,修改 package.json

  "scripts": {
     "e2e": "cypress open"
diff --git a/assets/use-jest-for-test-driven-development.html-134958bf.js b/assets/use-jest-for-test-driven-development.html-5db91cb1.js
similarity index 72%
rename from assets/use-jest-for-test-driven-development.html-134958bf.js
rename to assets/use-jest-for-test-driven-development.html-5db91cb1.js
index 6c9da1bb..679532ed 100644
--- a/assets/use-jest-for-test-driven-development.html-134958bf.js
+++ b/assets/use-jest-for-test-driven-development.html-5db91cb1.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-efcacba2","path":"/software-testing/use-jest-for-test-driven-development.html","title":"使用 Jest 实践测试驱动开发","lang":"zh-CN","frontmatter":{"date":"2019-04-21T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-jest-for-test-driven-development.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Jest 实践测试驱动开发"}],["meta",{"property":"og:description","content":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2019-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Jest 实践测试驱动开发\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"环境搭建","slug":"环境搭建","link":"#环境搭建","children":[]},{"level":2,"title":"开发","slug":"开发","link":"#开发","children":[{"level":3,"title":"文件初始化","slug":"文件初始化","link":"#文件初始化","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"第二个用例","slug":"第二个用例","link":"#第二个用例","children":[]},{"level":3,"title":"第三个用例","slug":"第三个用例","link":"#第三个用例","children":[]},{"level":3,"title":"第四个用例","slug":"第四个用例","link":"#第四个用例","children":[]},{"level":3,"title":"第五个用例","slug":"第五个用例","link":"#第五个用例","children":[]},{"level":3,"title":"重构","slug":"重构","link":"#重构","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":6.26,"words":1879},"filePathRelative":"software-testing/use-jest-for-test-driven-development.md","localizedDate":"2019年4月21日","excerpt":"

使用 Jest 实践测试驱动开发

\\n

前言

\\n

本文将使用jest进行测试驱动开发的示例,源码在github
\\n旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-efcacba2","path":"/software-testing/use-jest-for-test-driven-development.html","title":"使用 Jest 实践测试驱动开发","lang":"zh-CN","frontmatter":{"date":"2019-04-21T00:00:00.000Z","tag":["Node.js","Testing"],"description":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-jest-for-test-driven-development.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Jest 实践测试驱动开发"}],["meta",{"property":"og:description","content":"使用 Jest 实践测试驱动开发 前言 本文将使用jest进行测试驱动开发的示例,源码在github。 旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2019-04-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Jest 实践测试驱动开发\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2019-04-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"环境搭建","slug":"环境搭建","link":"#环境搭建","children":[]},{"level":2,"title":"开发","slug":"开发","link":"#开发","children":[{"level":3,"title":"文件初始化","slug":"文件初始化","link":"#文件初始化","children":[]},{"level":3,"title":"第一个用例","slug":"第一个用例","link":"#第一个用例","children":[]},{"level":3,"title":"第二个用例","slug":"第二个用例","link":"#第二个用例","children":[]},{"level":3,"title":"第三个用例","slug":"第三个用例","link":"#第三个用例","children":[]},{"level":3,"title":"第四个用例","slug":"第四个用例","link":"#第四个用例","children":[]},{"level":3,"title":"第五个用例","slug":"第五个用例","link":"#第五个用例","children":[]},{"level":3,"title":"重构","slug":"重构","link":"#重构","children":[]}]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":6.26,"words":1879},"filePathRelative":"software-testing/use-jest-for-test-driven-development.md","localizedDate":"2019年4月21日","excerpt":"

使用 Jest 实践测试驱动开发

\\n

前言

\\n

本文将使用jest进行测试驱动开发的示例,源码在github
\\n旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

","autoDesc":true}');export{e as data}; diff --git a/assets/use-jest-for-test-driven-development.html-c1ba0d4d.js b/assets/use-jest-for-test-driven-development.html-be06b9cd.js similarity index 99% rename from assets/use-jest-for-test-driven-development.html-c1ba0d4d.js rename to assets/use-jest-for-test-driven-development.html-be06b9cd.js index b79d55d5..e79f6dcc 100644 --- a/assets/use-jest-for-test-driven-development.html-c1ba0d4d.js +++ b/assets/use-jest-for-test-driven-development.html-be06b9cd.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,a as s,b as n,d as e,e as c}from"./app-ee4d23bf.js";const i={},u=s("h1",{id:"使用-jest-实践测试驱动开发",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用-jest-实践测试驱动开发","aria-hidden":"true"},"#"),n(" 使用 Jest 实践测试驱动开发")],-1),r=s("h2",{id:"前言",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),n(" 前言")],-1),d={href:"https://jestjs.io/docs/en/getting-started",target:"_blank",rel:"noopener noreferrer"},k={href:"https://github.com/levy9527/jest-tdd-demo",target:"_blank",rel:"noopener noreferrer"},v=s("br",null,null,-1),m=c(`

本文的重点是过程以及思维方法,框架以及用法不是重点。

本文使用的编程语言是javascript,思路对其他语言也是适用的。

本文主要以函数作为测试对象。

环境搭建

假设项目结构为

.
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o,c as l,a as s,b as n,d as e,f as c}from"./app-dcf5468f.js";const i={},u=s("h1",{id:"使用-jest-实践测试驱动开发",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#使用-jest-实践测试驱动开发","aria-hidden":"true"},"#"),n(" 使用 Jest 实践测试驱动开发")],-1),r=s("h2",{id:"前言",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),n(" 前言")],-1),d={href:"https://jestjs.io/docs/en/getting-started",target:"_blank",rel:"noopener noreferrer"},k={href:"https://github.com/levy9527/jest-tdd-demo",target:"_blank",rel:"noopener noreferrer"},v=s("br",null,null,-1),m=c(`

本文的重点是过程以及思维方法,框架以及用法不是重点。

本文使用的编程语言是javascript,思路对其他语言也是适用的。

本文主要以函数作为测试对象。

环境搭建

假设项目结构为

.
 ├── README.md
 ├── package.json
 ├── src
diff --git a/assets/use-playwright-for-ui-testing.html-261af66a.js b/assets/use-playwright-for-ui-testing.html-62764232.js
similarity index 80%
rename from assets/use-playwright-for-ui-testing.html-261af66a.js
rename to assets/use-playwright-for-ui-testing.html-62764232.js
index 2fde1fc4..55e7d52e 100644
--- a/assets/use-playwright-for-ui-testing.html-261af66a.js
+++ b/assets/use-playwright-for-ui-testing.html-62764232.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-0be1af08","path":"/software-testing/use-playwright-for-ui-testing.html","title":"下一代 UI 自动化测试工具 Playwright","lang":"zh-CN","frontmatter":{"date":"2023-05-07T00:00:00.000Z","tag":["Node.js","Python","Testing"],"description":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-playwright-for-ui-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"下一代 UI 自动化测试工具 Playwright"}],["meta",{"property":"og:description","content":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-05-07T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"下一代 UI 自动化测试工具 Playwright\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-07T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"代码生成","slug":"代码生成","link":"#代码生成","children":[]},{"level":3,"title":"修改代码","slug":"修改代码","link":"#修改代码","children":[]},{"level":3,"title":"执行用例","slug":"执行用例","link":"#执行用例","children":[]},{"level":3,"title":"调试用例","slug":"调试用例","link":"#调试用例","children":[]},{"level":3,"title":"查看报告","slug":"查看报告","link":"#查看报告","children":[]}]},{"level":2,"title":"常见场景与解决方案","slug":"常见场景与解决方案","link":"#常见场景与解决方案","children":[{"level":3,"title":"应用登录","slug":"应用登录","link":"#应用登录","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"超时时间","slug":"超时时间","link":"#超时时间","children":[]},{"level":3,"title":"元素选择","slug":"元素选择","link":"#元素选择","children":[]},{"level":3,"title":"声明断言 && 检查元素是否存在","slug":"声明断言-检查元素是否存在","link":"#声明断言-检查元素是否存在","children":[]},{"level":3,"title":"获取第n个元素","slug":"获取第n个元素","link":"#获取第n个元素","children":[]},{"level":3,"title":"遍历元素","slug":"遍历元素","link":"#遍历元素","children":[]},{"level":3,"title":"获取元素属性","slug":"获取元素属性","link":"#获取元素属性","children":[]},{"level":3,"title":"判断子元素数量","slug":"判断子元素数量","link":"#判断子元素数量","children":[]},{"level":3,"title":"鼠标悬浮","slug":"鼠标悬浮","link":"#鼠标悬浮","children":[]},{"level":3,"title":"操作剪贴板","slug":"操作剪贴板","link":"#操作剪贴板","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"setup.py bdist_wheel did not run successfully","slug":"setup-py-bdist-wheel-did-not-run-successfully","link":"#setup-py-bdist-wheel-did-not-run-successfully","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":10,"words":3001},"filePathRelative":"software-testing/use-playwright-for-ui-testing.md","localizedDate":"2023年5月7日","excerpt":"

下一代 UI 自动化测试工具 Playwright

\\n

前言

\\n

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

\\n
    \\n
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. \\n
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. \\n
\\n

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-0be1af08","path":"/software-testing/use-playwright-for-ui-testing.html","title":"下一代 UI 自动化测试工具 Playwright","lang":"zh-CN","frontmatter":{"date":"2023-05-07T00:00:00.000Z","tag":["Node.js","Python","Testing"],"description":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-playwright-for-ui-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"下一代 UI 自动化测试工具 Playwright"}],["meta",{"property":"og:description","content":"下一代 UI 自动化测试工具 Playwright 前言 Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有: 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件) 另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:published_time","content":"2023-05-07T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"下一代 UI 自动化测试工具 Playwright\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-05-07T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":2,"title":"使用","slug":"使用","link":"#使用","children":[{"level":3,"title":"代码生成","slug":"代码生成","link":"#代码生成","children":[]},{"level":3,"title":"修改代码","slug":"修改代码","link":"#修改代码","children":[]},{"level":3,"title":"执行用例","slug":"执行用例","link":"#执行用例","children":[]},{"level":3,"title":"调试用例","slug":"调试用例","link":"#调试用例","children":[]},{"level":3,"title":"查看报告","slug":"查看报告","link":"#查看报告","children":[]}]},{"level":2,"title":"常见场景与解决方案","slug":"常见场景与解决方案","link":"#常见场景与解决方案","children":[{"level":3,"title":"应用登录","slug":"应用登录","link":"#应用登录","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"超时时间","slug":"超时时间","link":"#超时时间","children":[]},{"level":3,"title":"元素选择","slug":"元素选择","link":"#元素选择","children":[]},{"level":3,"title":"声明断言 && 检查元素是否存在","slug":"声明断言-检查元素是否存在","link":"#声明断言-检查元素是否存在","children":[]},{"level":3,"title":"获取第n个元素","slug":"获取第n个元素","link":"#获取第n个元素","children":[]},{"level":3,"title":"遍历元素","slug":"遍历元素","link":"#遍历元素","children":[]},{"level":3,"title":"获取元素属性","slug":"获取元素属性","link":"#获取元素属性","children":[]},{"level":3,"title":"判断子元素数量","slug":"判断子元素数量","link":"#判断子元素数量","children":[]},{"level":3,"title":"鼠标悬浮","slug":"鼠标悬浮","link":"#鼠标悬浮","children":[]},{"level":3,"title":"操作剪贴板","slug":"操作剪贴板","link":"#操作剪贴板","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]},{"level":2,"title":"其他","slug":"其他","link":"#其他","children":[{"level":3,"title":"setup.py bdist_wheel did not run successfully","slug":"setup-py-bdist-wheel-did-not-run-successfully","link":"#setup-py-bdist-wheel-did-not-run-successfully","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":10,"words":3001},"filePathRelative":"software-testing/use-playwright-for-ui-testing.md","localizedDate":"2023年5月7日","excerpt":"

下一代 UI 自动化测试工具 Playwright

\\n

前言

\\n

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

\\n
    \\n
  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. \\n
  3. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)
  4. \\n
\\n

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

","autoDesc":true}');export{e as data}; diff --git a/assets/use-playwright-for-ui-testing.html-f210cda9.js b/assets/use-playwright-for-ui-testing.html-893da16f.js similarity index 97% rename from assets/use-playwright-for-ui-testing.html-f210cda9.js rename to assets/use-playwright-for-ui-testing.html-893da16f.js index 70d3f9aa..168d2e9f 100644 --- a/assets/use-playwright-for-ui-testing.html-f210cda9.js +++ b/assets/use-playwright-for-ui-testing.html-893da16f.js @@ -1,4 +1,4 @@ -import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as i,c as l,a as s,b as n,d as a,w as u,e}from"./app-ee4d23bf.js";const r={},d=e(`

下一代 UI 自动化测试工具 Playwright

前言

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

综上所述,笔者认为 Playwright 是值得在研发过程中引入的一款测试工具,它可以帮助研发、测试团队较平滑地走上自动化测试之路。它适用的典型场景之一,就是做回归测试——测试人员再也不用在界面上使用鼠标进行“点点点”,解放双手,提高测试效率。

安装

yarn create playwright
+import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as i,c as l,a as s,b as n,d as a,w as u,f as e}from"./app-dcf5468f.js";const r={},d=e(`

下一代 UI 自动化测试工具 Playwright

前言

Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

  1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
  2. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)

另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

综上所述,笔者认为 Playwright 是值得在研发过程中引入的一款测试工具,它可以帮助研发、测试团队较平滑地走上自动化测试之路。它适用的典型场景之一,就是做回归测试——测试人员再也不用在界面上使用鼠标进行“点点点”,解放双手,提高测试效率。

安装

yarn create playwright
 

根据命令提示,输入如下:
image.png
默认会下载所有浏览器,如果没有浏览器兼容性测试的需求,推荐如上图所示,手动安装一个浏览器。

以安装 chromium 为例,相应操作步骤如下:

  1. 修改配置
vi playwright.config.ts
 

注释掉以下内容:
image.png

  1. 安装浏览器
yarn playwright install --with-deps chromium
 

等待一段时间即可,如果失败,请重试。
image.png

`,16),k={href:"https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright",target:"_blank",rel:"noopener noreferrer"},g=e(`

使用

代码生成

虽然可以参考 example.spec.ts去编写测试用例,但这不是 Playwright 独特之处。Playwright 最引入注目的,是代码生成功能。

yarn playwright codegen
diff --git a/assets/use-postman-for-api-testing.html-bc2c854e.js b/assets/use-postman-for-api-testing.html-2115de7b.js
similarity index 77%
rename from assets/use-postman-for-api-testing.html-bc2c854e.js
rename to assets/use-postman-for-api-testing.html-2115de7b.js
index 75777a6f..266ea3c8 100644
--- a/assets/use-postman-for-api-testing.html-bc2c854e.js
+++ b/assets/use-postman-for-api-testing.html-2115de7b.js
@@ -1 +1 @@
-const e=JSON.parse('{"key":"v-75616b85","path":"/software-testing/use-postman-for-api-testing.html","title":"使用 Postman 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-09-13T00:00:00.000Z","tag":["Node.js","Daily"],"description":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-postman-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Postman 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-13T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Postman 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-13T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"本地调试","slug":"本地调试","link":"#本地调试","children":[{"level":3,"title":"接口集合","slug":"接口集合","link":"#接口集合","children":[]},{"level":3,"title":"从 cURL 导入","slug":"从-curl-导入","link":"#从-curl-导入","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"请求设置","slug":"请求设置","link":"#请求设置","children":[]}]},{"level":2,"title":"接口测试","slug":"接口测试","link":"#接口测试","children":[{"level":3,"title":"编写用例","slug":"编写用例","link":"#编写用例","children":[]},{"level":3,"title":"上传文件","slug":"上传文件","link":"#上传文件","children":[]},{"level":3,"title":"运行集合","slug":"运行集合","link":"#运行集合","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"导出接口","slug":"导出接口","link":"#导出接口","children":[]},{"level":3,"title":"导出环境变量","slug":"导出环境变量","link":"#导出环境变量","children":[]},{"level":3,"title":"复制要上传的文件","slug":"复制要上传的文件","link":"#复制要上传的文件","children":[]},{"level":3,"title":"提交到Git","slug":"提交到git","link":"#提交到git","children":[]},{"level":3,"title":"建立CI任务","slug":"建立ci任务","link":"#建立ci任务","children":[]}]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5.52,"words":1656},"filePathRelative":"software-testing/use-postman-for-api-testing.md","localizedDate":"2023年9月13日","excerpt":"

使用 Postman 进行 API 测试

\\n

前言

\\n

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

\\n

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

\\n

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-75616b85","path":"/software-testing/use-postman-for-api-testing.html","title":"使用 Postman 进行 API 测试","lang":"zh-CN","frontmatter":{"date":"2023-09-13T00:00:00.000Z","tag":["Node.js","Daily"],"description":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-postman-for-api-testing.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 Postman 进行 API 测试"}],["meta",{"property":"og:description","content":"使用 Postman 进行 API 测试 前言 虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。 而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。 还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Node.js"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-13T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 Postman 进行 API 测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-13T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"本地调试","slug":"本地调试","link":"#本地调试","children":[{"level":3,"title":"接口集合","slug":"接口集合","link":"#接口集合","children":[]},{"level":3,"title":"从 cURL 导入","slug":"从-curl-导入","link":"#从-curl-导入","children":[]},{"level":3,"title":"环境变量","slug":"环境变量","link":"#环境变量","children":[]},{"level":3,"title":"请求设置","slug":"请求设置","link":"#请求设置","children":[]}]},{"level":2,"title":"接口测试","slug":"接口测试","link":"#接口测试","children":[{"level":3,"title":"编写用例","slug":"编写用例","link":"#编写用例","children":[]},{"level":3,"title":"上传文件","slug":"上传文件","link":"#上传文件","children":[]},{"level":3,"title":"运行集合","slug":"运行集合","link":"#运行集合","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[{"level":3,"title":"导出接口","slug":"导出接口","link":"#导出接口","children":[]},{"level":3,"title":"导出环境变量","slug":"导出环境变量","link":"#导出环境变量","children":[]},{"level":3,"title":"复制要上传的文件","slug":"复制要上传的文件","link":"#复制要上传的文件","children":[]},{"level":3,"title":"提交到Git","slug":"提交到git","link":"#提交到git","children":[]},{"level":3,"title":"建立CI任务","slug":"建立ci任务","link":"#建立ci任务","children":[]}]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.52,"words":1656},"filePathRelative":"software-testing/use-postman-for-api-testing.md","localizedDate":"2023年9月13日","excerpt":"

使用 Postman 进行 API 测试

\\n

前言

\\n

虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

\\n

而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

\\n

还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/use-postman-for-api-testing.html-eec727f0.js b/assets/use-postman-for-api-testing.html-2175ffaf.js similarity index 96% rename from assets/use-postman-for-api-testing.html-eec727f0.js rename to assets/use-postman-for-api-testing.html-2175ffaf.js index a16efd6b..eaba6460 100644 --- a/assets/use-postman-for-api-testing.html-eec727f0.js +++ b/assets/use-postman-for-api-testing.html-2175ffaf.js @@ -1,4 +1,4 @@ -import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,f as e,a as n,b as a,e as o}from"./app-ee4d23bf.js";const c={},i=n("h1",{id:"使用-postman-进行-api-测试",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#使用-postman-进行-api-测试","aria-hidden":"true"},"#"),a(" 使用 Postman 进行 API 测试")],-1),u=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),l=n("p",null,"虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。",-1),r=n("p",null,"而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。",-1),d=n("p",null,"还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:",-1),k=o(`
// 为什么不设置 Map<String, String> ? 
+import{_ as s}from"./plugin-vue_export-helper-c27b6911.js";import{o as t,c as p,e,a as n,b as a,f as o}from"./app-dcf5468f.js";const c={},i=n("h1",{id:"使用-postman-进行-api-测试",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#使用-postman-进行-api-测试","aria-hidden":"true"},"#"),a(" 使用 Postman 进行 API 测试")],-1),u=n("h2",{id:"前言",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),l=n("p",null,"虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。",-1),r=n("p",null,"而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。",-1),d=n("p",null,"还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:",-1),k=o(`
// 为什么不设置 Map<String, String> ? 
 // 因为 menu 有个字段的类型是 Integer,使用 String 接收运行时会报错。
 Optional<Map<String, Object>> foundMenu = menuList.stream().filter(v -> {
   String code = (String) v.get("name"); // 这种类型转换代码实在多余!
diff --git a/assets/use-pytest-for-regression-testing-in-llm-app.html-9748b2d8.js b/assets/use-pytest-for-regression-testing-in-llm-app.html-14ddbd85.js
similarity index 99%
rename from assets/use-pytest-for-regression-testing-in-llm-app.html-9748b2d8.js
rename to assets/use-pytest-for-regression-testing-in-llm-app.html-14ddbd85.js
index d530e228..910509c1 100644
--- a/assets/use-pytest-for-regression-testing-in-llm-app.html-9748b2d8.js
+++ b/assets/use-pytest-for-regression-testing-in-llm-app.html-14ddbd85.js
@@ -1,4 +1,4 @@
-import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as l,c as o,f as c,a as n,b as s,d as e,e as t}from"./app-ee4d23bf.js";const u={},r=n("h1",{id:"使用-pytest-为llm应用添加回归测试",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#使用-pytest-为llm应用添加回归测试","aria-hidden":"true"},"#"),s(" 使用 pytest 为LLM应用添加回归测试")],-1),d=n("h2",{id:"回归测试的必要性",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#回归测试的必要性","aria-hidden":"true"},"#"),s(" 回归测试的必要性")],-1),v=n("p",null,"基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。",-1),m=n("p",null,"因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。",-1),k=n("p",null,"而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。",-1),b=n("h2",{id:"pytest",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#pytest","aria-hidden":"true"},"#"),s(" pytest")],-1),h=n("h3",{id:"安装",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#安装","aria-hidden":"true"},"#"),s(" 安装")],-1),_=n("code",null,"venv",-1),g=n("code",null,"ModuleNotFoundError: No module named xxx",-1),y={href:"https://medium.com/@dirk.avery/pytest-modulenotfounderror-no-module-named-requests-a770e6926ac5",target:"_blank",rel:"noopener noreferrer"},f=t(`

正确的安装步骤:

  1. 新开一个 bash 终端
  2. pip uninstall pytest # 删除全局的 pytest
  3. cd xxx && source ./venv/Scripts/activate # 激活虚拟环境
  4. pip install pytest # 在虚拟环境中安装 pytest
  5. pytest # 启动测试

配置

在项目根目录新建 pytest.ini 文件,最简单的配置如下:

[pytest]
+import{_ as i}from"./plugin-vue_export-helper-c27b6911.js";import{r as p,o as l,c as o,e as c,a as n,b as s,d as e,f as t}from"./app-dcf5468f.js";const u={},r=n("h1",{id:"使用-pytest-为llm应用添加回归测试",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#使用-pytest-为llm应用添加回归测试","aria-hidden":"true"},"#"),s(" 使用 pytest 为LLM应用添加回归测试")],-1),d=n("h2",{id:"回归测试的必要性",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#回归测试的必要性","aria-hidden":"true"},"#"),s(" 回归测试的必要性")],-1),v=n("p",null,"基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。",-1),m=n("p",null,"因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。",-1),k=n("p",null,"而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。",-1),b=n("h2",{id:"pytest",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#pytest","aria-hidden":"true"},"#"),s(" pytest")],-1),h=n("h3",{id:"安装",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#安装","aria-hidden":"true"},"#"),s(" 安装")],-1),_=n("code",null,"venv",-1),g=n("code",null,"ModuleNotFoundError: No module named xxx",-1),y={href:"https://medium.com/@dirk.avery/pytest-modulenotfounderror-no-module-named-requests-a770e6926ac5",target:"_blank",rel:"noopener noreferrer"},f=t(`

正确的安装步骤:

  1. 新开一个 bash 终端
  2. pip uninstall pytest # 删除全局的 pytest
  3. cd xxx && source ./venv/Scripts/activate # 激活虚拟环境
  4. pip install pytest # 在虚拟环境中安装 pytest
  5. pytest # 启动测试

配置

在项目根目录新建 pytest.ini 文件,最简单的配置如下:

[pytest]
 log_cli = 1
 log_cli_level = INFO
 
`,5),x={href:"https://docs.pytest.org/en/stable/reference/customize.html",target:"_blank",rel:"noopener noreferrer"},q=t(`

用例

pytest 会自动收集测试用例,要求用例满足以下规范:

  1. 文件名以 test_ 开头,如:test_intention.py
  2. 用例名以 test_ 开头,如:def test_my_method():

为避免加载不到自定义的函数,需要包含以下代码:

import os
diff --git a/assets/use-pytest-for-regression-testing-in-llm-app.html-c87ac7e3.js b/assets/use-pytest-for-regression-testing-in-llm-app.html-372308ae.js
similarity index 72%
rename from assets/use-pytest-for-regression-testing-in-llm-app.html-c87ac7e3.js
rename to assets/use-pytest-for-regression-testing-in-llm-app.html-372308ae.js
index cb10703b..456156cc 100644
--- a/assets/use-pytest-for-regression-testing-in-llm-app.html-c87ac7e3.js
+++ b/assets/use-pytest-for-regression-testing-in-llm-app.html-372308ae.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-7ba1021b","path":"/software-testing/use-pytest-for-regression-testing-in-llm-app.html","title":"使用 pytest 为LLM应用添加回归测试","lang":"zh-CN","frontmatter":{"date":"2023-12-08T00:00:00.000Z","tag":["Python","Testing","Gitlab"],"description":"使用 pytest 为LLM应用添加回归测试 回归测试的必要性 基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。 因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。 而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-pytest-for-regression-testing-in-llm-app.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 pytest 为LLM应用添加回归测试"}],["meta",{"property":"og:description","content":"使用 pytest 为LLM应用添加回归测试 回归测试的必要性 基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。 因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。 而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:tag","content":"Gitlab"}],["meta",{"property":"article:published_time","content":"2023-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 pytest 为LLM应用添加回归测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"回归测试的必要性","slug":"回归测试的必要性","link":"#回归测试的必要性","children":[]},{"level":2,"title":"pytest","slug":"pytest","link":"#pytest","children":[{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":3,"title":"配置","slug":"配置","link":"#配置","children":[]},{"level":3,"title":"用例","slug":"用例","link":"#用例","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":2.9,"words":871},"filePathRelative":"software-testing/use-pytest-for-regression-testing-in-llm-app.md","localizedDate":"2023年12月8日","excerpt":"

使用 pytest 为LLM应用添加回归测试

\\n

回归测试的必要性

\\n

基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

\\n

因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

\\n

而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。

\\n","autoDesc":true}');export{t as data}; +const t=JSON.parse('{"key":"v-7ba1021b","path":"/software-testing/use-pytest-for-regression-testing-in-llm-app.html","title":"使用 pytest 为LLM应用添加回归测试","lang":"zh-CN","frontmatter":{"date":"2023-12-08T00:00:00.000Z","tag":["Python","Testing","Gitlab"],"description":"使用 pytest 为LLM应用添加回归测试 回归测试的必要性 基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。 因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。 而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。","head":[["meta",{"property":"og:url","content":"https://levy.vip/software-testing/use-pytest-for-regression-testing-in-llm-app.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"使用 pytest 为LLM应用添加回归测试"}],["meta",{"property":"og:description","content":"使用 pytest 为LLM应用添加回归测试 回归测试的必要性 基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。 因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。 而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Python"}],["meta",{"property":"article:tag","content":"Testing"}],["meta",{"property":"article:tag","content":"Gitlab"}],["meta",{"property":"article:published_time","content":"2023-12-08T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"使用 pytest 为LLM应用添加回归测试\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-12-08T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"回归测试的必要性","slug":"回归测试的必要性","link":"#回归测试的必要性","children":[]},{"level":2,"title":"pytest","slug":"pytest","link":"#pytest","children":[{"level":3,"title":"安装","slug":"安装","link":"#安装","children":[]},{"level":3,"title":"配置","slug":"配置","link":"#配置","children":[]},{"level":3,"title":"用例","slug":"用例","link":"#用例","children":[]}]},{"level":2,"title":"持续集成","slug":"持续集成","link":"#持续集成","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":2.9,"words":871},"filePathRelative":"software-testing/use-pytest-for-regression-testing-in-llm-app.md","localizedDate":"2023年12月8日","excerpt":"

使用 pytest 为LLM应用添加回归测试

\\n

回归测试的必要性

\\n

基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

\\n

因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

\\n

而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。

\\n","autoDesc":true}');export{t as data}; diff --git a/assets/using-enum-in-java.html-f7bb1962.js b/assets/using-enum-in-java.html-7c6ed84b.js similarity index 63% rename from assets/using-enum-in-java.html-f7bb1962.js rename to assets/using-enum-in-java.html-7c6ed84b.js index 07c5812d..361e9dd5 100644 --- a/assets/using-enum-in-java.html-f7bb1962.js +++ b/assets/using-enum-in-java.html-7c6ed84b.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-2ba0b01e","path":"/java/using-enum-in-java.html","title":"枚举的推荐实践","lang":"zh-CN","frontmatter":{"date":"2022-10-14T00:00:00.000Z","tag":["Java","Daily"],"description":"枚举的推荐实践 背景 定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。 而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。 本文推荐,不要在枚举中定义数字,直接使用枚举名即可! Java 极简实现 理想状态下,枚举就应该这样简单! public enum SEX { MALE, FEMALE; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/using-enum-in-java.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"枚举的推荐实践"}],["meta",{"property":"og:description","content":"枚举的推荐实践 背景 定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。 而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。 本文推荐,不要在枚举中定义数字,直接使用枚举名即可! Java 极简实现 理想状态下,枚举就应该这样简单! public enum SEX { MALE, FEMALE; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-10-14T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"枚举的推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-10-14T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"Java","slug":"java","link":"#java","children":[{"level":3,"title":"极简实现","slug":"极简实现","link":"#极简实现","children":[]},{"level":3,"title":"常见实现","slug":"常见实现","link":"#常见实现","children":[]},{"level":3,"title":"更好的方式","slug":"更好的方式","link":"#更好的方式","children":[]},{"level":3,"title":"为什么还要定义数字?","slug":"为什么还要定义数字","link":"#为什么还要定义数字","children":[]}]},{"level":2,"title":"数据库","slug":"数据库","link":"#数据库","children":[{"level":3,"title":"排序特点","slug":"排序特点","link":"#排序特点","children":[]},{"level":3,"title":"添加新值","slug":"添加新值","link":"#添加新值","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":5.59,"words":1676},"filePathRelative":"java/using-enum-in-java.md","localizedDate":"2022年10月14日","excerpt":"

枚举的推荐实践

\\n

背景

\\n

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

\\n

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

\\n

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

\\n

Java

\\n

极简实现

\\n

理想状态下,枚举就应该这样简单!

\\n
public enum SEX {\\n  MALE,\\n  FEMALE;\\n}\\n
","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-2ba0b01e","path":"/java/using-enum-in-java.html","title":"枚举的推荐实践","lang":"zh-CN","frontmatter":{"date":"2022-10-14T00:00:00.000Z","tag":["Java","Daily"],"description":"枚举的推荐实践 背景 定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。 而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。 本文推荐,不要在枚举中定义数字,直接使用枚举名即可! Java 极简实现 理想状态下,枚举就应该这样简单! public enum SEX { MALE, FEMALE; }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/using-enum-in-java.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"枚举的推荐实践"}],["meta",{"property":"og:description","content":"枚举的推荐实践 背景 定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。 而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。 本文推荐,不要在枚举中定义数字,直接使用枚举名即可! Java 极简实现 理想状态下,枚举就应该这样简单! public enum SEX { MALE, FEMALE; }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-10-14T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"枚举的推荐实践\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-10-14T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"Java","slug":"java","link":"#java","children":[{"level":3,"title":"极简实现","slug":"极简实现","link":"#极简实现","children":[]},{"level":3,"title":"常见实现","slug":"常见实现","link":"#常见实现","children":[]},{"level":3,"title":"更好的方式","slug":"更好的方式","link":"#更好的方式","children":[]},{"level":3,"title":"为什么还要定义数字?","slug":"为什么还要定义数字","link":"#为什么还要定义数字","children":[]}]},{"level":2,"title":"数据库","slug":"数据库","link":"#数据库","children":[{"level":3,"title":"排序特点","slug":"排序特点","link":"#排序特点","children":[]},{"level":3,"title":"添加新值","slug":"添加新值","link":"#添加新值","children":[]}]},{"level":2,"title":"总结","slug":"总结","link":"#总结","children":[]},{"level":2,"title":"参考资料","slug":"参考资料","link":"#参考资料","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":5.59,"words":1676},"filePathRelative":"java/using-enum-in-java.md","localizedDate":"2022年10月14日","excerpt":"

枚举的推荐实践

\\n

背景

\\n

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

\\n

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

\\n

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

\\n

Java

\\n

极简实现

\\n

理想状态下,枚举就应该这样简单!

\\n
public enum SEX {\\n  MALE,\\n  FEMALE;\\n}\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/using-enum-in-java.html-af5dab3a.js b/assets/using-enum-in-java.html-a110114b.js similarity index 99% rename from assets/using-enum-in-java.html-af5dab3a.js rename to assets/using-enum-in-java.html-a110114b.js index c8947635..0981068c 100644 --- a/assets/using-enum-in-java.html-af5dab3a.js +++ b/assets/using-enum-in-java.html-a110114b.js @@ -1,4 +1,4 @@ -import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as l,a as n,b as s,d as e,e as t}from"./app-ee4d23bf.js";const i={},u=t(`

枚举的推荐实践

背景

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

Java

极简实现

理想状态下,枚举就应该这样简单!

public enum SEX {
+import{_ as p}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as c,c as l,a as n,b as s,d as e,f as t}from"./app-dcf5468f.js";const i={},u=t(`

枚举的推荐实践

背景

定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

Java

极简实现

理想状态下,枚举就应该这样简单!

public enum SEX {
   MALE,
   FEMALE;
 }
diff --git a/assets/vim-creator-pass-away.html-d6b70963.js b/assets/vim-creator-pass-away.html-62bd3587.js
similarity index 91%
rename from assets/vim-creator-pass-away.html-d6b70963.js
rename to assets/vim-creator-pass-away.html-62bd3587.js
index b0e2cd81..dab41563 100644
--- a/assets/vim-creator-pass-away.html-d6b70963.js
+++ b/assets/vim-creator-pass-away.html-62bd3587.js
@@ -1 +1 @@
-import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as i,c as s,d as r,a as e,b as n}from"./app-ee4d23bf.js";const c={},_=e("h1",{id:"vim-作者离世",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vim-作者离世","aria-hidden":"true"},"#"),n(" Vim 作者离世")],-1),l=e("p",null,"R.I.P 🙏",-1),d=e("p",null,"Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。",-1),m=e("p",null,"这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。",-1);function h(p,f){const o=a("BiliBili");return i(),s("div",null,[_,l,d,m,r(o,{bvid:"BV1fu4y1q7qS"})])}const B=t(c,[["render",h],["__file","vim-creator-pass-away.html.vue"]]);export{B as default};
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as a,o as i,c as s,d as r,a as e,b as n}from"./app-dcf5468f.js";const c={},_=e("h1",{id:"vim-作者离世",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#vim-作者离世","aria-hidden":"true"},"#"),n(" Vim 作者离世")],-1),l=e("p",null,"R.I.P 🙏",-1),d=e("p",null,"Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。",-1),m=e("p",null,"这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。",-1);function h(p,f){const o=a("BiliBili");return i(),s("div",null,[_,l,d,m,r(o,{bvid:"BV1fu4y1q7qS"})])}const B=t(c,[["render",h],["__file","vim-creator-pass-away.html.vue"]]);export{B as default};
diff --git a/assets/vim-creator-pass-away.html-1636038d.js b/assets/vim-creator-pass-away.html-d16d8ec0.js
similarity index 60%
rename from assets/vim-creator-pass-away.html-1636038d.js
rename to assets/vim-creator-pass-away.html-d16d8ec0.js
index 69df449a..84c976ec 100644
--- a/assets/vim-creator-pass-away.html-1636038d.js
+++ b/assets/vim-creator-pass-away.html-d16d8ec0.js
@@ -1 +1 @@
-const t=JSON.parse('{"key":"v-f1efc11c","path":"/daily/vim-creator-pass-away.html","title":"Vim 作者离世","lang":"zh-CN","frontmatter":{"date":"2023-08-06T00:00:00.000Z","tag":["Daily","Video"],"description":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/vim-creator-pass-away.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Vim 作者离世"}],["meta",{"property":"og:description","content":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Vim 作者离世\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-06T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"daily/vim-creator-pass-away.md","localizedDate":"2023年8月6日","excerpt":"

Vim 作者离世

\\n

R.I.P 🙏

\\n

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

\\n

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

\\n","autoDesc":true}');export{t as data}; +const e=JSON.parse('{"key":"v-f1efc11c","path":"/daily/vim-creator-pass-away.html","title":"Vim 作者离世","lang":"zh-CN","frontmatter":{"date":"2023-08-06T00:00:00.000Z","tag":["Daily","Video"],"description":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/vim-creator-pass-away.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Vim 作者离世"}],["meta",{"property":"og:description","content":"Vim 作者离世 R.I.P 🙏 Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。 这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-06T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Vim 作者离世\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-06T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.24,"words":71},"filePathRelative":"daily/vim-creator-pass-away.md","localizedDate":"2023年8月6日","excerpt":"

Vim 作者离世

\\n

R.I.P 🙏

\\n

Vim是我入行以来,一直在用的工具,可以说整个编程生涯都离不开它。

\\n

这几天听闻Vim作者离世了,便想来聊聊我与Vim的故事,以表纪念。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/what-is-the-difference-between-sh-and-bash.html-d303f17a.js b/assets/what-is-the-difference-between-sh-and-bash.html-d2f76510.js similarity index 67% rename from assets/what-is-the-difference-between-sh-and-bash.html-d303f17a.js rename to assets/what-is-the-difference-between-sh-and-bash.html-d2f76510.js index 08730c31..48a70851 100644 --- a/assets/what-is-the-difference-between-sh-and-bash.html-d303f17a.js +++ b/assets/what-is-the-difference-between-sh-and-bash.html-d2f76510.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-daf302c6","path":"/devops/what-is-the-difference-between-sh-and-bash.html","title":"sh与bash的区别","lang":"zh-CN","frontmatter":{"date":"2023-08-03T00:00:00.000Z","tag":["Linux","DevOps","Video"],"description":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。 常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"sh与bash的区别"}],["meta",{"property":"og:description","content":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。 常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"sh与bash的区别\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-03T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.06,"words":318},"filePathRelative":"devops/what-is-the-difference-between-sh-and-bash.md","localizedDate":"2023年8月3日","excerpt":"

sh与bash的区别

\\n

结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

\\n

常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-daf302c6","path":"/devops/what-is-the-difference-between-sh-and-bash.html","title":"sh与bash的区别","lang":"zh-CN","frontmatter":{"date":"2023-08-03T00:00:00.000Z","tag":["Linux","DevOps","Video"],"description":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。 常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。","head":[["meta",{"property":"og:url","content":"https://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"sh与bash的区别"}],["meta",{"property":"og:description","content":"sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。 常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Linux"}],["meta",{"property":"article:tag","content":"DevOps"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-03T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"sh与bash的区别\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-03T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.06,"words":318},"filePathRelative":"devops/what-is-the-difference-between-sh-and-bash.md","localizedDate":"2023年8月3日","excerpt":"

sh与bash的区别

\\n

结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

\\n

常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/what-is-the-difference-between-sh-and-bash.html-e23437ca.js b/assets/what-is-the-difference-between-sh-and-bash.html-f0872766.js similarity index 97% rename from assets/what-is-the-difference-between-sh-and-bash.html-e23437ca.js rename to assets/what-is-the-difference-between-sh-and-bash.html-f0872766.js index 370b8850..762ed8bb 100644 --- a/assets/what-is-the-difference-between-sh-and-bash.html-e23437ca.js +++ b/assets/what-is-the-difference-between-sh-and-bash.html-f0872766.js @@ -1 +1 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as r,c as s,f as n,d,a as e,b as i}from"./app-ee4d23bf.js";const h={},l=e("h1",{id:"sh与bash的区别",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sh与bash的区别","aria-hidden":"true"},"#"),i(" sh与bash的区别")],-1),c=e("p",null,"结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。",-1),f=e("p",null,[i("常见问题:明明是存在的、可执行的shell脚本,却在容器报错 "),e("code",null,"No such file or directory"),i(",很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。")],-1),m=e("p",null,"视频里有实战演示:",-1),b=e("p",null,[i("以下是 ChatGPT 的相关回答:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691066962763-bfbe3c1a-cb4f-43bc-b181-062eabee9529.png#averageHue=%23e4e5e7&clientId=ue1ff8f12-10ba-4&from=paste&height=196&id=u6521b3f3&originHeight=392&originWidth=1428&originalType=binary&ratio=2&rotation=0&showTitle=false&size=235208&status=done&style=none&taskId=uef2009af-bd00-4e0e-a215-9cca4c37ed2&title=&width=714",alt:""}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067007160-ec9a619b-be1c-4947-9dfc-6578d7b95003.png#averageHue=%23e5e6e8&clientId=ue1ff8f12-10ba-4&from=paste&height=116&id=u443ed474&originHeight=232&originWidth=1354&originalType=binary&ratio=2&rotation=0&showTitle=false&size=75155&status=done&style=none&taskId=u999f43f5-eb71-425d-8651-fbe5098a7c9&title=&width=677",alt:""}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067065982-c0370621-ff74-41e2-a435-b21973c64be4.png#averageHue=%23e3e4e6&clientId=ue1ff8f12-10ba-4&from=paste&height=170&id=u00ae3745&originHeight=340&originWidth=1382&originalType=binary&ratio=2&rotation=0&showTitle=false&size=116797&status=done&style=none&taskId=u563107c4-dbc5-464b-bb87-ab1c1dea427&title=&width=691",alt:""}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067101217-a60e6bd8-600c-4c68-89d0-53675a11442c.png#averageHue=%23e5e5e8&clientId=ue1ff8f12-10ba-4&from=paste&height=118&id=u1559e5c7&originHeight=236&originWidth=1370&originalType=binary&ratio=2&rotation=0&showTitle=false&size=77383&status=done&style=none&taskId=u846749a0-6dcb-429b-ad44-0d6e77dea00&title=&width=685",alt:""})],-1);function g(u,p){const t=o("BiliBili");return r(),s("div",null,[l,c,f,n(" more "),m,d(t,{bvid:"BV1Dj411676U"}),b])}const w=a(h,[["render",g],["__file","what-is-the-difference-between-sh-and-bash.html.vue"]]);export{w as default}; +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as r,c as s,e as n,d,a as e,b as i}from"./app-dcf5468f.js";const h={},l=e("h1",{id:"sh与bash的区别",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#sh与bash的区别","aria-hidden":"true"},"#"),i(" sh与bash的区别")],-1),c=e("p",null,"结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。",-1),f=e("p",null,[i("常见问题:明明是存在的、可执行的shell脚本,却在容器报错 "),e("code",null,"No such file or directory"),i(",很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。")],-1),m=e("p",null,"视频里有实战演示:",-1),b=e("p",null,[i("以下是 ChatGPT 的相关回答:"),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691066962763-bfbe3c1a-cb4f-43bc-b181-062eabee9529.png#averageHue=%23e4e5e7&clientId=ue1ff8f12-10ba-4&from=paste&height=196&id=u6521b3f3&originHeight=392&originWidth=1428&originalType=binary&ratio=2&rotation=0&showTitle=false&size=235208&status=done&style=none&taskId=uef2009af-bd00-4e0e-a215-9cca4c37ed2&title=&width=714",alt:""}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067007160-ec9a619b-be1c-4947-9dfc-6578d7b95003.png#averageHue=%23e5e6e8&clientId=ue1ff8f12-10ba-4&from=paste&height=116&id=u443ed474&originHeight=232&originWidth=1354&originalType=binary&ratio=2&rotation=0&showTitle=false&size=75155&status=done&style=none&taskId=u999f43f5-eb71-425d-8651-fbe5098a7c9&title=&width=677",alt:""}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067065982-c0370621-ff74-41e2-a435-b21973c64be4.png#averageHue=%23e3e4e6&clientId=ue1ff8f12-10ba-4&from=paste&height=170&id=u00ae3745&originHeight=340&originWidth=1382&originalType=binary&ratio=2&rotation=0&showTitle=false&size=116797&status=done&style=none&taskId=u563107c4-dbc5-464b-bb87-ab1c1dea427&title=&width=691",alt:""}),e("br"),e("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1691067101217-a60e6bd8-600c-4c68-89d0-53675a11442c.png#averageHue=%23e5e5e8&clientId=ue1ff8f12-10ba-4&from=paste&height=118&id=u1559e5c7&originHeight=236&originWidth=1370&originalType=binary&ratio=2&rotation=0&showTitle=false&size=77383&status=done&style=none&taskId=u846749a0-6dcb-429b-ad44-0d6e77dea00&title=&width=685",alt:""})],-1);function g(u,p){const t=o("BiliBili");return r(),s("div",null,[l,c,f,n(" more "),m,d(t,{bvid:"BV1Dj411676U"}),b])}const w=a(h,[["render",g],["__file","what-is-the-difference-between-sh-and-bash.html.vue"]]);export{w as default}; diff --git a/assets/which-one-is-better-Boolean-or-boolean.html-7bae7bc2.js b/assets/which-one-is-better-Boolean-or-boolean.html-749fb6a0.js similarity index 66% rename from assets/which-one-is-better-Boolean-or-boolean.html-7bae7bc2.js rename to assets/which-one-is-better-Boolean-or-boolean.html-749fb6a0.js index b345a0b6..4b593529 100644 --- a/assets/which-one-is-better-Boolean-or-boolean.html-7bae7bc2.js +++ b/assets/which-one-is-better-Boolean-or-boolean.html-749fb6a0.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-386e94f1","path":"/java/which-one-is-better-Boolean-or-boolean.html","title":"Boolean 还是 boolean?","lang":"zh-CN","frontmatter":{"date":"2022-06-02T00:00:00.000Z","tag":["Java","Daily"],"description":"Boolean 还是 boolean? 在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢? 结论 先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。 原文如下:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/which-one-is-better-Boolean-or-boolean.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Boolean 还是 boolean?"}],["meta",{"property":"og:description","content":"Boolean 还是 boolean? 在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢? 结论 先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。 原文如下:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-06-02T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Boolean 还是 boolean?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-02T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]},{"level":2,"title":"争议","slug":"争议","link":"#争议","children":[]},{"level":2,"title":"实战","slug":"实战","link":"#实战","children":[{"level":3,"title":"简单例子","slug":"简单例子","link":"#简单例子","children":[]},{"level":3,"title":"复杂例子","slug":"复杂例子","link":"#复杂例子","children":[]}]},{"level":2,"title":"附","slug":"附","link":"#附","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.48,"words":1045},"filePathRelative":"java/which-one-is-better-Boolean-or-boolean.md","localizedDate":"2022年6月2日","excerpt":"

Boolean 还是 boolean?

\\n

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

\\n

结论

\\n

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

\\n

原文如下:
\\n\\"\\"

","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-386e94f1","path":"/java/which-one-is-better-Boolean-or-boolean.html","title":"Boolean 还是 boolean?","lang":"zh-CN","frontmatter":{"date":"2022-06-02T00:00:00.000Z","tag":["Java","Daily"],"description":"Boolean 还是 boolean? 在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢? 结论 先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。 原文如下:","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/which-one-is-better-Boolean-or-boolean.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Boolean 还是 boolean?"}],["meta",{"property":"og:description","content":"Boolean 还是 boolean? 在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢? 结论 先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。 原文如下:"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-06-02T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Boolean 还是 boolean?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-06-02T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]},{"level":2,"title":"争议","slug":"争议","link":"#争议","children":[]},{"level":2,"title":"实战","slug":"实战","link":"#实战","children":[{"level":3,"title":"简单例子","slug":"简单例子","link":"#简单例子","children":[]},{"level":3,"title":"复杂例子","slug":"复杂例子","link":"#复杂例子","children":[]}]},{"level":2,"title":"附","slug":"附","link":"#附","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.48,"words":1045},"filePathRelative":"java/which-one-is-better-Boolean-or-boolean.md","localizedDate":"2022年6月2日","excerpt":"

Boolean 还是 boolean?

\\n

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

\\n

结论

\\n

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

\\n

原文如下:
\\n\\"\\"

","autoDesc":true}');export{e as data}; diff --git a/assets/which-one-is-better-Boolean-or-boolean.html-0510842d.js b/assets/which-one-is-better-Boolean-or-boolean.html-98455639.js similarity index 99% rename from assets/which-one-is-better-Boolean-or-boolean.html-0510842d.js rename to assets/which-one-is-better-Boolean-or-boolean.html-98455639.js index 5cb13333..c85ddc54 100644 --- a/assets/which-one-is-better-Boolean-or-boolean.html-0510842d.js +++ b/assets/which-one-is-better-Boolean-or-boolean.html-98455639.js @@ -1,4 +1,4 @@ -import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c,a,b as n,d as s,e as o}from"./app-ee4d23bf.js";const p={},r=o('

Boolean 还是 boolean?

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

结论

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

原文如下:

红线处翻译:总结就是,当你有得选的时候,请务必使用基本类型,而非包装类型。

那什么时候使用包装类型呢?原文如下:

红线处翻译:当你没得选、被强制要求时,才使用包装类型。如:使用泛型(使用集合类、调用参数是泛型参数的方法),以及通过反射进行方法调用(使用 invoke 方法)

争议

',9),d={href:"https://github.com/alibaba/p3c/blob/master/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf",target:"_blank",rel:"noopener noreferrer"},u=a("br",null,null,-1),b=a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1654076977162-f5358442-5426-4bca-936b-fcedcff0d3d3.png",alt:""},null,-1),m=o(`

但注意,本文讨论的仅仅是布尔类型,不要发散话题。 那现在就来分析一下,布尔类型有没有必要考虑 null 的情况?

我认为是没有的必要的。理由如下:

  • 布尔类型就是二进制的,代码两种情况:1或0;真或假。使用包装类型,出现第三种情况 null,不但要注意空指针异常问题,还要兼容 null 的情况——此时到底是真还是假呢?
  • 如果 null 表示的既不是真也不假,而是第三种情况——就不该定义为布尔类型,而应定义为枚举类型,因为一共有三种情况。使用 Boolean 来表示三种情况,是设计上的偷懒。

实战

我们来看一下实际代码中,滥用 Boolean 类型导致的问题。

简单例子

有如下 Controller,使用的是 boolean:

@RestController
+import{_ as t}from"./plugin-vue_export-helper-c27b6911.js";import{r as l,o as i,c,a,b as n,d as s,f as o}from"./app-dcf5468f.js";const p={},r=o('

Boolean 还是 boolean?

在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

结论

先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

原文如下:

红线处翻译:总结就是,当你有得选的时候,请务必使用基本类型,而非包装类型。

那什么时候使用包装类型呢?原文如下:

红线处翻译:当你没得选、被强制要求时,才使用包装类型。如:使用泛型(使用集合类、调用参数是泛型参数的方法),以及通过反射进行方法调用(使用 invoke 方法)

争议

',9),d={href:"https://github.com/alibaba/p3c/blob/master/Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C(%E9%BB%84%E5%B1%B1%E7%89%88).pdf",target:"_blank",rel:"noopener noreferrer"},u=a("br",null,null,-1),b=a("img",{src:"https://raw.gitmirror.com/levy9527/image-holder/main/md-image-kit/1654076977162-f5358442-5426-4bca-936b-fcedcff0d3d3.png",alt:""},null,-1),m=o(`

但注意,本文讨论的仅仅是布尔类型,不要发散话题。 那现在就来分析一下,布尔类型有没有必要考虑 null 的情况?

我认为是没有的必要的。理由如下:

  • 布尔类型就是二进制的,代码两种情况:1或0;真或假。使用包装类型,出现第三种情况 null,不但要注意空指针异常问题,还要兼容 null 的情况——此时到底是真还是假呢?
  • 如果 null 表示的既不是真也不假,而是第三种情况——就不该定义为布尔类型,而应定义为枚举类型,因为一共有三种情况。使用 Boolean 来表示三种情况,是设计上的偷懒。

实战

我们来看一下实际代码中,滥用 Boolean 类型导致的问题。

简单例子

有如下 Controller,使用的是 boolean:

@RestController
 public class DemoController {
   @RequestMapping("/hello")
   public String hello(boolean bool) {
diff --git a/assets/which-one-is-better-forEach-or-map.html-0aa9847c.js b/assets/which-one-is-better-forEach-or-map.html-0aa9847c.js
new file mode 100644
index 00000000..41c35989
--- /dev/null
+++ b/assets/which-one-is-better-forEach-or-map.html-0aa9847c.js
@@ -0,0 +1 @@
+const a=JSON.parse('{"key":"v-0d6828c2","path":"/java/which-one-is-better-forEach-or-map.html","title":"forEach 还是 map?","lang":"zh-CN","frontmatter":{"date":"2022-04-05T00:00:00.000Z","tag":["Java","Daily"],"description":"forEach 还是 map? 背景 遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有: List<Type> result = new ArrayList<>(); list.forEach(src -> { Type target = BeanUtils.copyProperties(src, target); //省略代码 result.add(target); });","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/which-one-is-better-forEach-or-map.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"forEach 还是 map?"}],["meta",{"property":"og:description","content":"forEach 还是 map? 背景 遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有: List<Type> result = new ArrayList<>(); list.forEach(src -> { Type target = BeanUtils.copyProperties(src, target); //省略代码 result.add(target); });"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-04-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"forEach 还是 map?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]},{"level":2,"title":"解析","slug":"解析","link":"#解析","children":[]},{"level":2,"title":"实战","slug":"实战","link":"#实战","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.67,"words":1102},"filePathRelative":"java/which-one-is-better-forEach-or-map.md","localizedDate":"2022年4月5日","excerpt":"

forEach 还是 map?

\\n

背景

\\n

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

\\n
List<Type> result = new ArrayList<>();\\nlist.forEach(src -> {\\n    Type target = BeanUtils.copyProperties(src, target);\\n    //省略代码\\n    result.add(target);\\n});\\n
","autoDesc":true}');export{a as data}; diff --git a/assets/which-one-is-better-forEach-or-map.html-87f75424.js b/assets/which-one-is-better-forEach-or-map.html-87f75424.js deleted file mode 100644 index 39140736..00000000 --- a/assets/which-one-is-better-forEach-or-map.html-87f75424.js +++ /dev/null @@ -1 +0,0 @@ -const a=JSON.parse('{"key":"v-0d6828c2","path":"/java/which-one-is-better-forEach-or-map.html","title":"forEach 还是 map?","lang":"zh-CN","frontmatter":{"date":"2022-04-05T00:00:00.000Z","tag":["Java","Daily"],"description":"forEach 还是 map? 背景 遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有: List<Type> result = new ArrayList<>(); list.forEach(src -> { Type target = BeanUtils.copyProperties(src, target); //省略代码 result.add(target); });","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/which-one-is-better-forEach-or-map.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"forEach 还是 map?"}],["meta",{"property":"og:description","content":"forEach 还是 map? 背景 遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有: List<Type> result = new ArrayList<>(); list.forEach(src -> { Type target = BeanUtils.copyProperties(src, target); //省略代码 result.add(target); });"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2022-04-05T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"forEach 还是 map?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2022-04-05T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"结论","slug":"结论","link":"#结论","children":[]},{"level":2,"title":"解析","slug":"解析","link":"#解析","children":[]},{"level":2,"title":"实战","slug":"实战","link":"#实战","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.67,"words":1102},"filePathRelative":"java/which-one-is-better-forEach-or-map.md","localizedDate":"2022年4月5日","excerpt":"

forEach 还是 map?

\\n

背景

\\n

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

\\n
List<Type> result = new ArrayList<>();\\nlist.forEach(src -> {\\n    Type target = BeanUtils.copyProperties(src, target);\\n    //省略代码\\n    result.add(target);\\n});\\n
","autoDesc":true}');export{a as data}; diff --git a/assets/which-one-is-better-forEach-or-map.html-b566c49e.js b/assets/which-one-is-better-forEach-or-map.html-c0f0caf5.js similarity index 99% rename from assets/which-one-is-better-forEach-or-map.html-b566c49e.js rename to assets/which-one-is-better-forEach-or-map.html-c0f0caf5.js index f3dd7361..c55fa91b 100644 --- a/assets/which-one-is-better-forEach-or-map.html-b566c49e.js +++ b/assets/which-one-is-better-forEach-or-map.html-c0f0caf5.js @@ -1,4 +1,4 @@ -import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,e as p}from"./app-ee4d23bf.js";const t={},e=p(`

forEach 还是 map?

背景

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

List<Type> result = new ArrayList<>();
+import{_ as n}from"./plugin-vue_export-helper-c27b6911.js";import{o as s,c as a,f as p}from"./app-dcf5468f.js";const t={},e=p(`

forEach 还是 map?

背景

遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

List<Type> result = new ArrayList<>();
 list.forEach(src -> {
     Type target = BeanUtils.copyProperties(src, target);
     //省略代码
diff --git a/assets/why-i-prefer-fastjson-instead-of-jackson.html-9e436949.js b/assets/why-i-prefer-fastjson-instead-of-jackson.html-89359d71.js
similarity index 95%
rename from assets/why-i-prefer-fastjson-instead-of-jackson.html-9e436949.js
rename to assets/why-i-prefer-fastjson-instead-of-jackson.html-89359d71.js
index 73908880..66c8c295 100644
--- a/assets/why-i-prefer-fastjson-instead-of-jackson.html-9e436949.js
+++ b/assets/why-i-prefer-fastjson-instead-of-jackson.html-89359d71.js
@@ -1,4 +1,4 @@
-import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as p,c as i,f as r,a,b as n,d as e,e as l}from"./app-ee4d23bf.js";const d={},u=l(`

Jackson 经典异常 UnrecognizedPropertyException

原因是 json 包含的字段,多于 Java 实体类定义的字段。

解决方法很简单:

new ObjectMapper()
+import{_ as c}from"./plugin-vue_export-helper-c27b6911.js";import{r as s,o as p,c as i,e as r,a,b as n,d as e,f as l}from"./app-dcf5468f.js";const d={},u=l(`

Jackson 经典异常 UnrecognizedPropertyException

原因是 json 包含的字段,多于 Java 实体类定义的字段。

解决方法很简单:

new ObjectMapper()
   .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
 

或者为相关实体添加注解:

@JsonIgnoreProperties(ignoreUnknown = true)
 public class ObjectParseFromJsonString {  }
diff --git a/assets/why-i-prefer-fastjson-instead-of-jackson.html-d2a6015e.js b/assets/why-i-prefer-fastjson-instead-of-jackson.html-d2a6015e.js
deleted file mode 100644
index c23afd3b..00000000
--- a/assets/why-i-prefer-fastjson-instead-of-jackson.html-d2a6015e.js
+++ /dev/null
@@ -1 +0,0 @@
-const e=JSON.parse('{"key":"v-1aafac08","path":"/java/why-i-prefer-fastjson-instead-of-jackson.html","title":"Jackson 经典异常 UnrecognizedPropertyException","lang":"zh-CN","frontmatter":{"date":"2023-08-21T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-i-prefer-fastjson-instead-of-jackson.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Jackson 经典异常 UnrecognizedPropertyException"}],["meta",{"property":"og:description","content":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Jackson 经典异常 UnrecognizedPropertyException\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":0.36,"words":109},"filePathRelative":"java/why-i-prefer-fastjson-instead-of-jackson.md","localizedDate":"2023年8月21日","excerpt":"

Jackson 经典异常 UnrecognizedPropertyException

\\n

原因是 json 包含的字段,多于 Java 实体类定义的字段。

\\n

解决方法很简单:

\\n
new ObjectMapper()\\n  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\\n

或者为相关实体添加注解:

\\n
@JsonIgnoreProperties(ignoreUnknown = true)\\npublic class ObjectParseFromJsonString {  }\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/why-i-prefer-fastjson-instead-of-jackson.html-e3333c98.js b/assets/why-i-prefer-fastjson-instead-of-jackson.html-e3333c98.js new file mode 100644 index 00000000..7859ebfb --- /dev/null +++ b/assets/why-i-prefer-fastjson-instead-of-jackson.html-e3333c98.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-1aafac08","path":"/java/why-i-prefer-fastjson-instead-of-jackson.html","title":"Jackson 经典异常 UnrecognizedPropertyException","lang":"zh-CN","frontmatter":{"date":"2023-08-21T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-i-prefer-fastjson-instead-of-jackson.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"Jackson 经典异常 UnrecognizedPropertyException"}],["meta",{"property":"og:description","content":"Jackson 经典异常 UnrecognizedPropertyException 原因是 json 包含的字段,多于 Java 实体类定义的字段。 解决方法很简单: new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 或者为相关实体添加注解: @JsonIgnoreProperties(ignoreUnknown = true) public class ObjectParseFromJsonString { }"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-21T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"Jackson 经典异常 UnrecognizedPropertyException\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-21T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":0.36,"words":109},"filePathRelative":"java/why-i-prefer-fastjson-instead-of-jackson.md","localizedDate":"2023年8月21日","excerpt":"

Jackson 经典异常 UnrecognizedPropertyException

\\n

原因是 json 包含的字段,多于 Java 实体类定义的字段。

\\n

解决方法很简单:

\\n
new ObjectMapper()\\n  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)\\n

或者为相关实体添加注解:

\\n
@JsonIgnoreProperties(ignoreUnknown = true)\\npublic class ObjectParseFromJsonString {  }\\n
","autoDesc":true}');export{e as data}; diff --git a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-fa7d71af.js b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-e251503b.js similarity index 96% rename from assets/why-is-it-so-hard-to-upgrade-dependencies.html-fa7d71af.js rename to assets/why-is-it-so-hard-to-upgrade-dependencies.html-e251503b.js index 0494530a..ae247330 100644 --- a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-fa7d71af.js +++ b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-e251503b.js @@ -1 +1 @@ -import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as t,c as n,f as d,d as c,a as e,b as a,e as s}from"./app-ee4d23bf.js";const h={},l=e("h1",{id:"升个jar版本-怎么这么难",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#升个jar版本-怎么这么难","aria-hidden":"true"},"#"),a(" 升个jar版本,怎么这么难?")],-1),p=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),f=e("p",null,"无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。",-1),_=e("p",null,"通常来说,对 Web 应用而言,前端是消费者,后端是提供者。",-1),m=e("p",null,"今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?",-1),u=s('

接口调用示意图

已知信息

  1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
  2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

问题

infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

于是,想通过让使用方升级 jar 来解决,此方案可行吗?

答案可能出乎意料:不行!

因为:jar 升级版本的动作不在自己的掌控范围内。

说起来很简单:叫他们升一下就行。
但问题是:

  1. 他们是谁?
  2. 你说升就升?

问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

复盘

那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

有两种思路:

  1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
  2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
',16);function b(g,x){const i=o("BiliBili");return t(),n("div",null,[l,p,f,_,m,d(" more "),u,c(i,{bvid:"BV18G411d7gR"})])}const B=r(h,[["render",b],["__file","why-is-it-so-hard-to-upgrade-dependencies.html.vue"]]);export{B as default}; +import{_ as r}from"./plugin-vue_export-helper-c27b6911.js";import{r as o,o as t,c as n,e as d,d as c,a as e,b as a,f as s}from"./app-dcf5468f.js";const h={},l=e("h1",{id:"升个jar版本-怎么这么难",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#升个jar版本-怎么这么难","aria-hidden":"true"},"#"),a(" 升个jar版本,怎么这么难?")],-1),p=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),a(" 前言")],-1),f=e("p",null,"无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。",-1),_=e("p",null,"通常来说,对 Web 应用而言,前端是消费者,后端是提供者。",-1),m=e("p",null,"今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?",-1),u=s('

接口调用示意图

已知信息

  1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
  2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

问题

infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

于是,想通过让使用方升级 jar 来解决,此方案可行吗?

答案可能出乎意料:不行!

因为:jar 升级版本的动作不在自己的掌控范围内。

说起来很简单:叫他们升一下就行。
但问题是:

  1. 他们是谁?
  2. 你说升就升?

问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

复盘

那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

有两种思路:

  1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
  2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
',16);function b(g,x){const i=o("BiliBili");return t(),n("div",null,[l,p,f,_,m,d(" more "),u,c(i,{bvid:"BV18G411d7gR"})])}const B=r(h,[["render",b],["__file","why-is-it-so-hard-to-upgrade-dependencies.html.vue"]]);export{B as default}; diff --git a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-4951d610.js b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-f5a86225.js similarity index 68% rename from assets/why-is-it-so-hard-to-upgrade-dependencies.html-4951d610.js rename to assets/why-is-it-so-hard-to-upgrade-dependencies.html-f5a86225.js index 7abb0603..a9640a93 100644 --- a/assets/why-is-it-so-hard-to-upgrade-dependencies.html-4951d610.js +++ b/assets/why-is-it-so-hard-to-upgrade-dependencies.html-f5a86225.js @@ -1 +1 @@ -const e=JSON.parse('{"key":"v-ca672354","path":"/java/why-is-it-so-hard-to-upgrade-dependencies.html","title":"升个jar版本,怎么这么难?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-is-it-so-hard-to-upgrade-dependencies.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"升个jar版本,怎么这么难?"}],["meta",{"property":"og:description","content":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"升个jar版本,怎么这么难?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"接口调用示意图","slug":"接口调用示意图","link":"#接口调用示意图","children":[]},{"level":2,"title":"已知信息","slug":"已知信息","link":"#已知信息","children":[]},{"level":2,"title":"问题","slug":"问题","link":"#问题","children":[]},{"level":2,"title":"复盘","slug":"复盘","link":"#复盘","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":1.8,"words":540},"filePathRelative":"java/why-is-it-so-hard-to-upgrade-dependencies.md","localizedDate":"2023年8月26日","excerpt":"

升个jar版本,怎么这么难?

\\n

前言

\\n

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

\\n

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

\\n

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

\\n","autoDesc":true}');export{e as data}; +const e=JSON.parse('{"key":"v-ca672354","path":"/java/why-is-it-so-hard-to-upgrade-dependencies.html","title":"升个jar版本,怎么这么难?","lang":"zh-CN","frontmatter":{"date":"2023-08-26T00:00:00.000Z","tag":["Java","Daily","Video"],"description":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?","head":[["meta",{"property":"og:url","content":"https://levy.vip/java/why-is-it-so-hard-to-upgrade-dependencies.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"升个jar版本,怎么这么难?"}],["meta",{"property":"og:description","content":"升个jar版本,怎么这么难? 前言 无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。 通常来说,对 Web 应用而言,前端是消费者,后端是提供者。 今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Java"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:tag","content":"Video"}],["meta",{"property":"article:published_time","content":"2023-08-26T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"升个jar版本,怎么这么难?\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-08-26T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"接口调用示意图","slug":"接口调用示意图","link":"#接口调用示意图","children":[]},{"level":2,"title":"已知信息","slug":"已知信息","link":"#已知信息","children":[]},{"level":2,"title":"问题","slug":"问题","link":"#问题","children":[]},{"level":2,"title":"复盘","slug":"复盘","link":"#复盘","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":1.8,"words":540},"filePathRelative":"java/why-is-it-so-hard-to-upgrade-dependencies.md","localizedDate":"2023年8月26日","excerpt":"

升个jar版本,怎么这么难?

\\n

前言

\\n

无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

\\n

通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

\\n

今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-381fd837.js b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-c6128cbb.js similarity index 65% rename from assets/you-dont-need-to-add-tenant_id-to-every-table.html-381fd837.js rename to assets/you-dont-need-to-add-tenant_id-to-every-table.html-c6128cbb.js index c49ebf5d..0972131b 100644 --- a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-381fd837.js +++ b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-c6128cbb.js @@ -1 +1 @@ -const t=JSON.parse('{"key":"v-bd2b5fe8","path":"/daily/you-dont-need-to-add-tenant_id-to-every-table.html","title":"技术点评:别每张表都加tenant_id","lang":"zh-CN","frontmatter":{"date":"2023-09-18T00:00:00.000Z","tag":["Design","Daily"],"description":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/you-dont-need-to-add-tenant_id-to-every-table.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"技术点评:别每张表都加tenant_id"}],["meta",{"property":"og:description","content":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-04-29T11:34:38.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Design"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-04-29T11:34:38.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"技术点评:别每张表都加tenant_id\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-18T00:00:00.000Z\\",\\"dateModified\\":\\"2024-04-29T11:34:38.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1714390478000,"updatedTime":1714390478000,"contributors":[{"name":"levy","email":"897895407@qq.com","commits":1}]},"readingTime":{"minutes":3.26,"words":977},"filePathRelative":"daily/you-dont-need-to-add-tenant_id-to-every-table.md","localizedDate":"2023年9月18日","excerpt":"

技术点评:别每张表都加tenant_id

\\n

前言

\\n

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

\\n","autoDesc":true}');export{t as data}; +const e=JSON.parse('{"key":"v-bd2b5fe8","path":"/daily/you-dont-need-to-add-tenant_id-to-every-table.html","title":"技术点评:别每张表都加tenant_id","lang":"zh-CN","frontmatter":{"date":"2023-09-18T00:00:00.000Z","tag":["Design","Daily"],"description":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。","head":[["meta",{"property":"og:url","content":"https://levy.vip/daily/you-dont-need-to-add-tenant_id-to-every-table.html"}],["meta",{"property":"og:site_name","content":"levy"}],["meta",{"property":"og:title","content":"技术点评:别每张表都加tenant_id"}],["meta",{"property":"og:description","content":"技术点评:别每张表都加tenant_id 前言 系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。"}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:updated_time","content":"2024-05-28T23:22:52.000Z"}],["meta",{"property":"article:author","content":"levy"}],["meta",{"property":"article:tag","content":"Design"}],["meta",{"property":"article:tag","content":"Daily"}],["meta",{"property":"article:published_time","content":"2023-09-18T00:00:00.000Z"}],["meta",{"property":"article:modified_time","content":"2024-05-28T23:22:52.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"技术点评:别每张表都加tenant_id\\",\\"image\\":[\\"\\"],\\"datePublished\\":\\"2023-09-18T00:00:00.000Z\\",\\"dateModified\\":\\"2024-05-28T23:22:52.000Z\\",\\"author\\":[{\\"@type\\":\\"Person\\",\\"name\\":\\"levy\\"}]}"]]},"headers":[{"level":2,"title":"前言","slug":"前言","link":"#前言","children":[]},{"level":2,"title":"背景","slug":"背景","link":"#背景","children":[]},{"level":2,"title":"正文","slug":"正文","link":"#正文","children":[]}],"git":{"createdTime":1716938572000,"updatedTime":1716938572000,"contributors":[{"name":"levy","email":"chenriwei@deepexi.com","commits":1}]},"readingTime":{"minutes":3.26,"words":977},"filePathRelative":"daily/you-dont-need-to-add-tenant_id-to-every-table.md","localizedDate":"2023年9月18日","excerpt":"

技术点评:别每张表都加tenant_id

\\n

前言

\\n

系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

\\n","autoDesc":true}');export{e as data}; diff --git a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-e9a846fd.js b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-dd070117.js similarity index 98% rename from assets/you-dont-need-to-add-tenant_id-to-every-table.html-e9a846fd.js rename to assets/you-dont-need-to-add-tenant_id-to-every-table.html-dd070117.js index d42a2a32..3ecb1f59 100644 --- a/assets/you-dont-need-to-add-tenant_id-to-every-table.html-e9a846fd.js +++ b/assets/you-dont-need-to-add-tenant_id-to-every-table.html-dd070117.js @@ -1 +1 @@ -import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as n,c as d,f as i,a as e,b as t,e as r}from"./app-ee4d23bf.js";const c={},o=e("h1",{id:"技术点评-别每张表都加tenant-id",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#技术点评-别每张表都加tenant-id","aria-hidden":"true"},"#"),t(" 技术点评:别每张表都加tenant_id")],-1),s=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),_=e("p",null,"系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。",-1),h=r('

背景

在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



既然群友有疑问,我索性就整理了一下,把前因后果清楚。

正文

1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

',12);function p(l,m){return n(),d("div",null,[o,s,_,i(" more "),h])}const f=a(c,[["render",p],["__file","you-dont-need-to-add-tenant_id-to-every-table.html.vue"]]);export{f as default}; +import{_ as a}from"./plugin-vue_export-helper-c27b6911.js";import{o as n,c as d,e as i,a as e,b as t,f as r}from"./app-dcf5468f.js";const c={},o=e("h1",{id:"技术点评-别每张表都加tenant-id",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#技术点评-别每张表都加tenant-id","aria-hidden":"true"},"#"),t(" 技术点评:别每张表都加tenant_id")],-1),s=e("h2",{id:"前言",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#前言","aria-hidden":"true"},"#"),t(" 前言")],-1),_=e("p",null,"系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。",-1),h=r('

背景

在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



既然群友有疑问,我索性就整理了一下,把前因后果清楚。

正文

1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

',12);function p(l,m){return n(),d("div",null,[o,s,_,i(" more "),h])}const f=a(c,[["render",p],["__file","you-dont-need-to-add-tenant_id-to-every-table.html.vue"]]);export{f as default}; diff --git a/category/index.html b/category/index.html index 3b7745d4..d7306182 100644 --- a/category/index.html +++ b/category/index.html @@ -34,11 +34,11 @@ } - + -
跳至主要內容
- +
跳至主要內容
    + diff --git a/daily/a-vuepress2-entertaining-video.html b/daily/a-vuepress2-entertaining-video.html index 35ed4898..e906c037 100644 --- a/daily/a-vuepress2-entertaining-video.html +++ b/daily/a-vuepress2-entertaining-video.html @@ -5,7 +5,7 @@ - VuePress2 娱乐视频 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/a-warning-from-navicat.html b/daily/a-warning-from-navicat.html index 83240886..ca2c2021 100644 --- a/daily/a-warning-from-navicat.html +++ b/daily/a-warning-from-navicat.html @@ -5,7 +5,7 @@ - 来自Navicat的侵权警告 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html b/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html index 7f7648a9..9af0b30d 100644 --- a/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html +++ b/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html @@ -5,7 +5,7 @@ - Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci? | levy @@ -34,7 +34,7 @@ } - +
    跳至主要內容

    Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

    DailyVideoMySQL

    Beyond UTF-8, do you know utf8mb4 and utf8mb4_unicode_ci?

    Background

    Look at the DDL below, can you tell the meaning of CHARSET=utf8mb4 and COLLATE=utf8mb4_general_ci?

    CREATE TABLE `my_table` (
    @@ -47,7 +47,7 @@
     Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) 
     and (utf8mb4_general_ci,IMPLICIT) for operation '='
     

    and here's the solution, using the keyword COLLATE:

    SET @target_tenant_id := 'your_value' COLLATE utf8mb4_unicode_ci;
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/daily/claude-ai-in-action-extract-info-from-html.html b/daily/claude-ai-in-action-extract-info-from-html.html index 604ffdcf..8eb70501 100644 --- a/daily/claude-ai-in-action-extract-info-from-html.html +++ b/daily/claude-ai-in-action-extract-info-from-html.html @@ -5,7 +5,7 @@ - Claude AI应用案例,从HTML中抽取文本 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/copy-code-may-not-be-guilty.html b/daily/copy-code-may-not-be-guilty.html index 2db0c02c..66090334 100644 --- a/daily/copy-code-may-not-be-guilty.html +++ b/daily/copy-code-may-not-be-guilty.html @@ -5,7 +5,7 @@ - 复制代码也许不是罪 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    复制代码也许不是罪

    Daily

    复制代码也许不是罪

    前言

    熟悉我的人都知道,我对代码是有追求的。

    正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

    我早期认为:复制代码就是菜。

    后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

    而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

    编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

    正文

    我总结了一下,主要有以下原因:
    1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

    2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


    3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

    4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    复制代码也许不是罪

    Daily

    复制代码也许不是罪

    前言

    熟悉我的人都知道,我对代码是有追求的。

    正式参加工作后,我就知道,复制粘贴是坏的实践,自己一直极力避免做这样的事。要是遇到了别人复制粘贴,要么喷,要么自己改。

    我早期认为:复制代码就是菜。

    后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

    而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

    编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

    正文

    我总结了一下,主要有以下原因:
    1.深刻地理解了源码与测试用例之间的关系。只相信测试用例,而不是相信源码。如果你只改源码而不是补充相应的测试代码,那所谓的有追求是盲目、片面的。

    2.对具体的编码实现已经缺乏兴致了。尤其是有AI的情况下,真的是有手就会,何必要我来写呢?我负责设计,再进行 Code Review 不是更好吗?


    3.要考虑时间成本。如果别人已经做了一大半,我何必重头开始?

    4.提升对质量的认知。聚集于产出质量,而不仅仅是代码质量,那么你就会发现,要做的事还挺多,再考虑时间成本,需要做一个权衡取舍。

    上次编辑于:
    贡献者: levy
    + diff --git a/daily/dont-try-to-argue-with-a-sb.html b/daily/dont-try-to-argue-with-a-sb.html index 074f1ebc..cc4b924e 100644 --- a/daily/dont-try-to-argue-with-a-sb.html +++ b/daily/dont-try-to-argue-with-a-sb.html @@ -5,7 +5,7 @@ - 不要与傻逼进行争吵 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    不要与傻逼进行争吵

    DailyEmotionWorking ExperienceVideo

    不要与傻逼进行争吵

    这是个职场吐槽帖,也是个自我反思、情绪管理帖。

    我在沟通上,有以下可以改正的点:
    0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

    1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
    2. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
    3. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
    4. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!

    事情是这样的。

    我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

    对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

    这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

    电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

    接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
    tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

    我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
    他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

    这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

    于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

    这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

    最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

    这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

    我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

    下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

    想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

    经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

    同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
    比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

    总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
    还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    不要与傻逼进行争吵

    DailyEmotionWorking ExperienceVideo

    不要与傻逼进行争吵

    这是个职场吐槽帖,也是个自我反思、情绪管理帖。

    我在沟通上,有以下可以改正的点:
    0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

    1. 我要下班了,周一再回。改成:我身体不舒服,先回去了,等下回复你
    2. 你自己安排不合理,凭什么要我陪你加班?改成:放心,我配合你,有事就沟通
    3. 你看不懂代码吗?怎么当P7的?改成:你还需要什么,你说;你还有哪里不懂的,我给你解答
    4. 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!

    事情是这样的。

    我跟素未谋面的同事在工作上要有一些配合,主要是我提供技术方案、文档给他。

    对方工作能力不足就算了,时间管理也不行。8.3号一整天没来找我,8.4号白天也没来找我,快下班了,才打电话跟我说要资料。

    这是第一层不满:快下班了,没什么耐心了,我不想别人打扰我周五下班。

    电话中,他的语气带着责备,明显是甩锅的意味。找人办事,要别人配合还用这种语气,这是第二层不满。我开始怒他,我说,“兄弟,你冷静下。”

    接着,他又跟我扯淡,我一看时间,六点钟了,是时候下班。我就说,“有什么问题发钉钉,我周一给你解决。” 他急了,他说,“你不能下班,你不能走,你要陪我!”
    tmd,自己延误工期,还要我陪你加班,哪个这个道理?这是第三层不满,我有点破防了。我开始说一些不太理智、不太专业的话。

    我说,“凭什么要我陪你加班?V我50,我就陪你”。他说,“你在搞笑吧,不如让领导V你50?” 
    他接着说,“这个东西很急,客户今天晚上就要!” 我说,“那是你的事,你的客户,又不是我的客户。”

    这两句话一出,味道就有点不对了。本来全然是他不占理的,现在他抓住的我语言上漏洞,开始要拉我下水了。

    于是马上拉群、打字,向领导打小报告,一幅小学生向老师告状的模样。

    这情商,这行为,我们还能好好配合?我们接下来净在群里互怒,要么是甩锅,要么是暗示对方能力不行,没有一个人在解决问题。

    最后,我回去以后,还是带着不满加了班。不但自己加,还搞得我的 leader 也跟着加,让 leader 对那个人也无语,心情不好了。

    这一点,我有点自责,因为我连累了我的 leader。而这也是我为什么反思并把这件事情写出来的原因,自己不爽只是自己的事,连累到他人,我就过意不去了。

    我不希望这种事情再次发生!我应该能掌握事态,Let me take control of things!

    下次再有这种事,一定是先把工作上的问题解决,然后再远离傻逼。不要在傻逼身上浪费时间,多说一句话、甚至只是骂一句都不值得!

    想一想,已经几年没跟人因为工作的事情跟人发生明面上的不愉快了。我的情绪管理能力似乎下降了,这也是个契机,让我重新对这方面重视起来。

    经验上看,除了开头的句式转换以外,其实有一个心法值得学习。那就是永远不要跟人在明面上闹不愉快

    同时,结合“阳奉阴违”的技巧,你不用去做口舌之争,你去做你的事就行了。
    比如,我准备下班了,对方说,“你不要下班,你不能走。” 我可以回复,“好的,我不走,我上个厕所。” 然后我就挂掉电话,下班走人,这不是很简单吗?何必反问他“为什么我不能走?”没有意义啊!

    总之,明面上不与人闹翻,保持自己情绪的稳定,一方面是职业素养体现,另一方面也是自己涵养的体现。
    还有一方面,那就是有利于身心健康——生气是会对身体器官造成伤害的,如高血压、胃疼、肠胃不适!

    上次编辑于:
    贡献者: levy
    + diff --git a/daily/index.html b/daily/index.html index 6d509549..fe0c181a 100644 --- a/daily/index.html +++ b/daily/index.html @@ -34,10 +34,10 @@ } - + - + diff --git a/daily/iteration-retrospective-of-sanyuan.html b/daily/iteration-retrospective-of-sanyuan.html index bf5488b2..f31864aa 100644 --- a/daily/iteration-retrospective-of-sanyuan.html +++ b/daily/iteration-retrospective-of-sanyuan.html @@ -5,7 +5,7 @@ - 迭代复盘之三员管理 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    迭代复盘之三员管理

    DailyWorking Experience

    迭代复盘之三员管理

    前言

    本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

    这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

    那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

    动手前,至少梳理出接口清单

    太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

    更优的工作流应该是:

    1. 先进行业务梳理
    2. 整理出接口清单
    3. 再进行代码迁移
    4. 根据接口清单,逐个验证迁移的代码是否符合需求

    迁移时,采取结队编程

    看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

    实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

    所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

    迁移后,需要对自己负责的功能设计测试用例

    这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

    1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
    2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

    可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

    当然,由于时间的关系,做完善的测试一般都不太可能了。
    对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
    但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    迭代复盘之三员管理

    DailyWorking Experience

    迭代复盘之三员管理

    前言

    本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

    这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

    那么,我从工作流、方法论的角度,反思了自己可以改进的点,期望在这种迁移旧代码的实践中,抽取出能复用的经验。

    动手前,至少梳理出接口清单

    太轻信旧代码了,抱着代码迁移过来,跑起来就能用的态度,没能及时发现功能遗漏点,到了提测才发现有功能未实现,这是可以避免的。

    更优的工作流应该是:

    1. 先进行业务梳理
    2. 整理出接口清单
    3. 再进行代码迁移
    4. 根据接口清单,逐个验证迁移的代码是否符合需求

    迁移时,采取结队编程

    看到有 500 个文件要迁移,觉得用分治的思路去实现会更高效,但这种思维有一个误区:做得快不等于有效率,因为效率需要的是有用功,而不是无用功。

    实践表明,初期对业务的理解、代码的熟悉程度还不够深入,迁移代码遇到分歧点时,个人在独自处理时容易产生错误——而这种错误并不自知,这才是最蛋疼的地方。

    所以,我才建议,采取结队编程实践,两个人一起来迁移代码,这样子遇到分歧时可以讨论,而不是贸然行动。表面上看,这样的进程慢了,实际上却能提高对代码、业务的理解,提高了迁移的正确率,真正地提高效率。

    迁移后,需要对自己负责的功能设计测试用例

    这里的本质,是不能完全依赖别人。虽然测试人员会编写测试用例,也会进行用例评审,但这里会有两个问题:

    1. 前期评审时,自己的心思可能在理解业务、功能设计与改造以及代码迁移,没有多少心思能去 debug 用例的精细程度
    2. 测试也是人,也会有遗漏,并且没有接触源码,有些特殊的、极端的边界场景,自己作为研发人员,更容易发现

    可以看成,我们自己设计的用例是对测试人员给出的测试用例的补充。

    当然,由于时间的关系,做完善的测试一般都不太可能了。
    对于 legacy code,想补充 service 层的单元测试成本是很高的。写 controller 层的测试,可能也没有太多时间。
    但至少应该用 postman 进行调试,或者在用户界面上点几下,不能完全等着前端或测试人员反馈,认为没有反馈就是没有 bug。

    上次编辑于:
    贡献者: levy
    + diff --git a/daily/leverage-ai-to-boost-coding-productivity.html b/daily/leverage-ai-to-boost-coding-productivity.html index acf46787..e0fb96b8 100644 --- a/daily/leverage-ai-to-boost-coding-productivity.html +++ b/daily/leverage-ai-to-boost-coding-productivity.html @@ -5,7 +5,7 @@ - 都什么年代了,还在用传统方式写代码? | levy @@ -34,7 +34,7 @@ } - +
    跳至主要內容

    都什么年代了,还在用传统方式写代码?

    AIDaily

    都什么年代了,还在用传统方式写代码?

    前言

    还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

    本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

    本文将分享 AI 时代的编程新实践,教你如何从一个 "Ctrl + C"、 "Ctrl + V" 工程师,变成一个 "Tab + Enter" 工程师🤣。

    开发流程

    软件的一般研发流程为:

    1. 需求分析
    2. 程序设计
    3. 代码编写
    4. 软件测试
    5. 部署上线

    我们在这里主要关心步骤2~4,因为与 AI 结合得比较紧密。虽然需求分析也可以借助 AI,但不是本文的重点,故不做讨论。

    程序设计

    经过需求分析、逻辑梳理后,在编写实际代码前,需要进行程序设计。

    此环节的产物是设计文档,是什么类型的设计文档不重要,重要的是伪代码的输出。

    虽然《Code Complete》早就推荐过伪代码的实践,但对此人们容易有一个误区:认为写伪代码花的时间,已经够把实际代码写好了。但 AIGC 时代,此问题可以轻松破解:AI 写代码的速度肯定比人快,因此,只要能找到方法能让 AI 生成符合需求的代码,就值得花时间去研究。而伪代码,就是让 AI 快速生成符合期望的实际代码的最好方式。

    为什么这么说呢?因为想要让 AIGC 符合期望,恰当的 Prompt 必不可少。但如何写好这个 Prompt,需要提供多少上下文,才能让 AI 更好地理解我们的意图,这是需要技巧、需要调试的。而经过精心设计的伪代码,本身已经提供了足够的上下文,且意图足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。

    我们来看一下伪代码示例:

    plainText = JSON.stringify(data)
    @@ -154,7 +154,7 @@
             }
         }
     }
    -

    把上述代码 copy 下来,放到工程中,根据需要改造即可。

    这里特别要说下,强烈推荐使用原版 AI,而不是寻找平替,同样的 prompt,下图是某一中文平替输出的结果:
    只生成了函数声明,没有生成函数实现。二者对比,未免相形见绌。

    辅助编程工具

    改造的过程中,少不了 AI pair programming tools。对此,我推荐使用 Amazon 的 CodeWhisperer,原因很简单,跟 GitHub Copilot 相比,它是免费的😃。

    CodeWhisperer 的安装可以看文末的安装教程,我们先来看一下它是怎么辅助我们编码的。

    第一种方式是最简单的,那就是什么都不管,等待智能提示即可,就好像 IDEA 原来的提示一样,只不过更智能。

    下图示例中,要把原来的中文异常提示,修改成英文,而我只输入了两个字符 IM, 就得到了智能提示,补全了完整的英文字符串!

    可以注意到,上图的智能建议一共有 5 条,相应的快捷键为:

    1. 方向键 ->,查看下一条提示
    2. 方向键 <-,查看上一条提示
    3. Tab,采用该提示
    4. Esc,拒绝提示

    我们再来看第二种 CodeWhisperer 的使用方式,编写注释,获得编码建议。

    最后一种就是编写一个空函数,让 CodeWhisperer 根据函数名去猜测函数的实现,这种情况需要足够的上下文,才能得到令人满意的结果。

    软件测试

    AI 生成的内容,并不是完全可信任的,因此,单元测试的重要性变得尤为突出。

    对上述代码编写测试代码后,实际上并不能一次通过,因为前面 AI 生成的代码参数有误。

    此时需要一边执行单测,一边根据结果与 AI 进行交互:

    经过修改,最终测试用例通过👏!

    总结

    本文通过案例,展示了 AI 如何结合软件研发的流程,提升我们的编程效率的。

    其中,个人认为最重要的是编写伪代码与进行单元测试。有趣的是,这两样实践在 AIGC 时代之前,就已经被认为是最佳实践。这给我们启示:某些方法论、实践经得起时间的考验,技术更新迭代,它们历久弥新。

    对于AI编程的到底应该怎么看,其实 GitHub Copilot 的产品标语已经很好地概括了:Your AI pair programmer。在结队编程里,由两个人一起来进行编程活动。一个是 Navigator,负责指导、审查,另一个 Driver,负责具体的代码编写。AI 的出现,其实是取代了 Driver 的角色,而另一个充当 Navigator 角色的人,仍然是不可或缺的。

    最后,AI 是否能进一步渗透我们的工作流,还有待探索。此文作引抛砖引玉之用,期待大家的后续分享。

    附:CodeWhisperer 安装

    下载 2023 年的 IDEA,打开 Plugins Marketplace,找到 AWS Toolkit

    安装完成、重启 IDEA 后,点击左下角,按下图所示操作:

    如果第一次使用,就点击 1 处进行注册,如果已经有账号了,就点击 2 处使用自己的账号登录。

    注册、登录、授权成功后,出现如图所示页面,即可使用 CodeWhisperer。

    上次编辑于:
    贡献者: levy
    - +

    把上述代码 copy 下来,放到工程中,根据需要改造即可。

    这里特别要说下,强烈推荐使用原版 AI,而不是寻找平替,同样的 prompt,下图是某一中文平替输出的结果:
    只生成了函数声明,没有生成函数实现。二者对比,未免相形见绌。

    辅助编程工具

    改造的过程中,少不了 AI pair programming tools。对此,我推荐使用 Amazon 的 CodeWhisperer,原因很简单,跟 GitHub Copilot 相比,它是免费的😃。

    CodeWhisperer 的安装可以看文末的安装教程,我们先来看一下它是怎么辅助我们编码的。

    第一种方式是最简单的,那就是什么都不管,等待智能提示即可,就好像 IDEA 原来的提示一样,只不过更智能。

    下图示例中,要把原来的中文异常提示,修改成英文,而我只输入了两个字符 IM, 就得到了智能提示,补全了完整的英文字符串!

    可以注意到,上图的智能建议一共有 5 条,相应的快捷键为:

    1. 方向键 ->,查看下一条提示
    2. 方向键 <-,查看上一条提示
    3. Tab,采用该提示
    4. Esc,拒绝提示

    我们再来看第二种 CodeWhisperer 的使用方式,编写注释,获得编码建议。

    最后一种就是编写一个空函数,让 CodeWhisperer 根据函数名去猜测函数的实现,这种情况需要足够的上下文,才能得到令人满意的结果。

    软件测试

    AI 生成的内容,并不是完全可信任的,因此,单元测试的重要性变得尤为突出。

    对上述代码编写测试代码后,实际上并不能一次通过,因为前面 AI 生成的代码参数有误。

    此时需要一边执行单测,一边根据结果与 AI 进行交互:

    经过修改,最终测试用例通过👏!

    总结

    本文通过案例,展示了 AI 如何结合软件研发的流程,提升我们的编程效率的。

    其中,个人认为最重要的是编写伪代码与进行单元测试。有趣的是,这两样实践在 AIGC 时代之前,就已经被认为是最佳实践。这给我们启示:某些方法论、实践经得起时间的考验,技术更新迭代,它们历久弥新。

    对于AI编程的到底应该怎么看,其实 GitHub Copilot 的产品标语已经很好地概括了:Your AI pair programmer。在结队编程里,由两个人一起来进行编程活动。一个是 Navigator,负责指导、审查,另一个 Driver,负责具体的代码编写。AI 的出现,其实是取代了 Driver 的角色,而另一个充当 Navigator 角色的人,仍然是不可或缺的。

    最后,AI 是否能进一步渗透我们的工作流,还有待探索。此文作引抛砖引玉之用,期待大家的后续分享。

    附:CodeWhisperer 安装

    下载 2023 年的 IDEA,打开 Plugins Marketplace,找到 AWS Toolkit

    安装完成、重启 IDEA 后,点击左下角,按下图所示操作:

    如果第一次使用,就点击 1 处进行注册,如果已经有账号了,就点击 2 处使用自己的账号登录。

    注册、登录、授权成功后,出现如图所示页面,即可使用 CodeWhisperer。

    上次编辑于:
    贡献者: levy
    + diff --git a/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html b/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html index 78a9d2e1..3ec55646 100644 --- a/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html +++ b/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html @@ -5,7 +5,7 @@ - 微软中国CTO演讲观后感 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/testing-environments-should-be-consistent-with-production-environments.html b/daily/testing-environments-should-be-consistent-with-production-environments.html index 768033db..55ba5ce9 100644 --- a/daily/testing-environments-should-be-consistent-with-production-environments.html +++ b/daily/testing-environments-should-be-consistent-with-production-environments.html @@ -5,7 +5,7 @@ - 生产教训:测试环境要与生产环境一致 | levy @@ -34,11 +34,11 @@ } - +
    跳至主要內容

    生产教训:测试环境要与生产环境一致

    Daily

    生产教训:测试环境要与生产环境一致

    事件还原

    业务流程:

    1. app-a 上传文件
    2. app-b 下载文件后使用文件

    其他信息:

    1. 开发、测试环境使用 MinIO
    2. 生产环境使用 Amazon S3

    问题:

    1. app-a 上传文件成功
    2. app-b 使用文件报错

    逐步分析定位问题:

    1. app-a 与 app-b 配置是否一致?——确认都是使用 S3
    2. S3 是否正确配置?有没权限问题?——确认配置正确,没有权限问题
    3. app-a 是否真的上传成功?——确认文件已在 S3
    4. app-b 是否下载成功?——根据日志,判断下载失败,得到的信息是:文件不存在。

    所以问题就在于,为什么 app-b 的代码会判断文件不存在?

    • 是传过去的路径参数不对?
    • 还是 app-b 的逻辑有问题?

    经过确认,传参是正确的,那么只有一个推论: app-b 判断文件是否存在的逻辑有问题。

    具体是哪里有问题呢?

    原来文件下载前,有一个判断目录是否存在的逻辑,其实现是判断 S3 的对象是否存在,示例代码如下:

    return s3Client.doesObjectExist("/path/dir")
    -

    则该代码永远为 false。

    如何修复呢?改为调用 listObjects就可以了。如下图所示(左边是修改前,右边是修改后):

    分析

    很难想像,为什么在涉及对象存储的代码中,会有判断目录是否存在的逻辑。

    好在我是个善于为别人找理由的人。我观察了下代码库,发现接口与类结构如下:

    如果定义 IStorage 与实现 S3Storage 的代码的人并不相同(我相信很可能是这样),那么我更倾向于认为责任在定义 IStorage 的人身上,因为TA并没有合理地设计接口,导致后来者被迫实现没有意义的接口,从而出错。

    结论

    为什么开发、测试环境没问题?或者说,为什么 MinIO 没这个问题?那是因为实现 MinIOStorage 的人,没有踩这个坑。

    所以,本此事件给我来的经验教训是什么呢?既不是 S3 如何判断目录是否存在,也不是接口定义的重要性。而是:要让测试环境与生产环境尽可能保持一致,提前暴露问题。而不是测试环境是这样的配置,生产环境又是那样的配置——这就会加大生产环境出问题的可能性!

    上次编辑于:
    贡献者: levy
    - +

    则该代码永远为 false。

    如何修复呢?改为调用 listObjects就可以了。如下图所示(左边是修改前,右边是修改后):

    分析

    很难想像,为什么在涉及对象存储的代码中,会有判断目录是否存在的逻辑。

    好在我是个善于为别人找理由的人。我观察了下代码库,发现接口与类结构如下:

    如果定义 IStorage 与实现 S3Storage 的代码的人并不相同(我相信很可能是这样),那么我更倾向于认为责任在定义 IStorage 的人身上,因为TA并没有合理地设计接口,导致后来者被迫实现没有意义的接口,从而出错。

    结论

    为什么开发、测试环境没问题?或者说,为什么 MinIO 没这个问题?那是因为实现 MinIOStorage 的人,没有踩这个坑。

    所以,本此事件给我来的经验教训是什么呢?既不是 S3 如何判断目录是否存在,也不是接口定义的重要性。而是:要让测试环境与生产环境尽可能保持一致,提前暴露问题。而不是测试环境是这样的配置,生产环境又是那样的配置——这就会加大生产环境出问题的可能性!

    上次编辑于:
    贡献者: levy
    + diff --git a/daily/things-I-have-to-vent-about-vue.html b/daily/things-I-have-to-vent-about-vue.html index c97e6d46..d2c29b00 100644 --- a/daily/things-I-have-to-vent-about-vue.html +++ b/daily/things-I-have-to-vent-about-vue.html @@ -5,7 +5,7 @@ - 对Vue不得不吐槽的事 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    对Vue不得不吐槽的事

    FrontendDailyVideo

    对Vue不得不吐槽的事

    为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

    总结一下,我对Vue生态不满的地方在于:

    1. Vue总是破坏性升级,新技术完全不管旧用户的体验
    2. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    对Vue不得不吐槽的事

    FrontendDailyVideo

    对Vue不得不吐槽的事

    为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

    总结一下,我对Vue生态不满的地方在于:

    1. Vue总是破坏性升级,新技术完全不管旧用户的体验
    2. 你要新技术可以,别逼我跟着用,我不想破坏 CI 的稳定性
    上次编辑于:
    贡献者: levy
    + diff --git a/daily/use-claude2-instead-of-chatgpt.html b/daily/use-claude2-instead-of-chatgpt.html index 3ab87cf0..42162519 100644 --- a/daily/use-claude2-instead-of-chatgpt.html +++ b/daily/use-claude2-instead-of-chatgpt.html @@ -5,7 +5,7 @@ - 再见ChatGPT,我选择Claude2! | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    再见ChatGPT,我选择Claude2!

    VideoAITool

    再见ChatGPT,我选择Claude2!

    大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

    首先要评测的当然是ChatGPT了,因为最早用的就是它。

    现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

    它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

    当然要说口语练习的话,可以用手机App Call Annie。

    不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

    ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

    那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

    当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

    然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

    但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

    但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



    那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

    要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

    那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

    Claude 2 有着超长的token,100K。


    并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

    那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是poe.comopen in new window。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。

    这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是poe.comopen in new window + claude2,这将是一个体验极佳的使用方案。

    最后,来看看面对恶意提问,Claude2是如何回答的:

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    再见ChatGPT,我选择Claude2!

    VideoAITool

    再见ChatGPT,我选择Claude2!

    大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

    首先要评测的当然是ChatGPT了,因为最早用的就是它。

    现在来看呢,没有当初那么惊艳了,因为已经有很多的平替了,甚至是超替了。当然现在我还会用它的唯一原因呢,就是这个Voice Control插件。

    它不但可以让我进行语音输入,还可以让ChatGPT变成语音输出,那这一点很方便我进行英语口语练习。

    当然要说口语练习的话,可以用手机App Call Annie。

    不过呢,因为它要求iOS 16,我不想系统升级,所以没有用。另外呢,想练习的话,其实还是要去不断的对自己的词语、句子去做一个调整、优化的,那这一点我还是认为ChatGPT带有文字的输出会更适合我,方便我做笔记。

    ChatGPT 注册还是有门槛的。因为大家知道虽然它可以免费使用,但实际上要注册成功的话,要么是买一个账号,要么是买短信。实际上还是付费了。当初的话我是花了16块钱买了个短信,然后注册账号的,现在有很多其他的免费替代方案,所以现在是不推荐注册 ChatGPT 的,毕竟白嫖更香嘛。

    那下一个评测对象是 New Bing。New Bing 刚出来时很惊艳,差点成为大家的AI女友。后来微软做了很多调整,现在已经被束缚住了手脚。

    当然,从用户界面上来说 New Bing 是有着最佳交互体验的。现在可以上传图片,支持在线链接,也支持粘贴上传,体验感极佳。

    然后呢,对于New Bing呢,每回答一个问题,后面会有三个提示的快捷问题选项,可以让我们更方便地去问相关的问题,这也是一个很好的体验。

    但实际效果来看,对话有长度限制,当然这是小问题,30次回答已经不少了。

    但最蛋疼的是它的自我审察,面对敏感问题,它会避而不答,甚至终止对话。



    那这样的话就体验很差了,因此呢,我的评价是绣花枕头,中看不中用。

    要想使用 New Bing呢,之前是要去加入 wait list,等待微软的审批。现在据说是放开了,应该是可以更容易注册了。但是考虑到前面描述的不尽如人意的体验,我是认为没有必要去注册了,因为还有更好的。

    那么接下来我们要评测的就是ChatGPT的劲敌,原Open AI成员打造的Claude。

    Claude 2 有着超长的token,100K。


    并且与其他AI相比呢,它有一个特点是比较的健谈。也就是说回答了你一个问题后,他会说,In more detail,接着用更丰富的语言去回答一遍,这其实是一个很好的特性。

    那么有没有一款软件,它拥有上述AI的所有的优点呢?有啊,那就是poe.comopen in new window。对于还没有使用AI或者还在使用国产AI的同学呢,我非常推荐使用它,用谷歌账号即可注册使用。

    这里面有各种的AI任你挑选。同时呢,他也集成了 New Bing 的快捷回复的选项。所以说,我认为当前最佳的组合就是poe.comopen in new window + claude2,这将是一个体验极佳的使用方案。

    最后,来看看面对恶意提问,Claude2是如何回答的:

    上次编辑于:
    贡献者: levy
    + diff --git a/daily/vim-creator-pass-away.html b/daily/vim-creator-pass-away.html index 066ac709..726ca210 100644 --- a/daily/vim-creator-pass-away.html +++ b/daily/vim-creator-pass-away.html @@ -5,7 +5,7 @@ - Vim 作者离世 | levy @@ -34,10 +34,10 @@ } - + - - + + diff --git a/daily/you-dont-need-to-add-tenant_id-to-every-table.html b/daily/you-dont-need-to-add-tenant_id-to-every-table.html index 3be806b0..c638f77e 100644 --- a/daily/you-dont-need-to-add-tenant_id-to-every-table.html +++ b/daily/you-dont-need-to-add-tenant_id-to-every-table.html @@ -5,7 +5,7 @@ - 技术点评:别每张表都加tenant_id | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    技术点评:别每张表都加tenant_id

    DesignDaily

    技术点评:别每张表都加tenant_id

    前言

    系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

    背景

    在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



    既然群友有疑问,我索性就整理了一下,把前因后果清楚。

    正文

    1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

    2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

    3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
    3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
    3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

    要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

    更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

    md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

    4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

    5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

    6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    技术点评:别每张表都加tenant_id

    DesignDaily

    技术点评:别每张表都加tenant_id

    前言

    系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

    背景

    在维护旧系统时,踩了各种坑,终于忍不住在群里吐槽了下,于是有以下对话。



    既然群友有疑问,我索性就整理了一下,把前因后果清楚。

    正文

    1.首先,租户数据隔离级别应该如何,没有唯一标准,评价只有是否合适。因此,逻辑隔离、物理隔离,都不是吐槽点。

    2.原来的设计是,采取逻辑隔离方案,具体做法是,给每一张表都加上了tenant_id字段。问题就在于,有必要每一张表加吗?系统的权限设计是:先有租户,再有应用,用户、角色、资源、权限设置等内容都挂在应用下,所以,应用下的内容,关联 app_id 就行了,根本不需要tenant_id。

    3.冗余多一个字段,会出现什么问题呢?先不提查询性能、存储空间等细节,就说很实际的场景:
    3.1 每张表都加 tenant_id,几乎每条 sql 都要加 where tenant_id = ? ,那程序员会怎么做?首先想到的是用框架自动注入 sql
    3.2 在后台管理端,有一个超级管理员,能够查询出所有租户的内容,也就是说,此时查询不能带 where tenant_id = ?,也即不能让框架注入 sql

    要同时兼容上述逻辑,程序员又会怎么做呢?于是就引入了万恶的全局变量,类似于 injectSql = true,就添加 tenant_id 作为过滤条件。但默认是不是要 injectSql = true 呢?每个项目代码又不一样,你不运行,你都不知道。

    更恶心的是,需求变化后,是否需要带上 tenant_id 的逻辑与原来不一致时,你得在某行代码执行前,手动设置 inejctSql 的值,在该行代码之后,再手动复原——因为如果不复原,作为全局变量,会影响到后面的代码!

    md,这时候后你才会知道,还不如老老实实地设置 tenant_id,显示地设置,好过这种隐蔽的依赖。

    4.但这还不是最难搞的。因为上述的是代码问题,真正难搞的是数据问题。考虑一种场景:超级管理员在后台管理某租户的应用,手动为租户添加数据,请问,新增的数据 tenant_id 的值是什么,某租户的 id,还是超级管理员的id?按逻辑来说,应该是某租户的 tenant_id。但问题在于,由于理解不同,或由于疏忽让框架自动注入了 tenant_id,导致上述场景,有些数据的 tenant_id 是超级管理员的id。而又因为超级管理员进行查询时,是不带 tenant_id 作为过滤条件的,因此即使 tenant_id 的值设置错误,依然在界面上能显示,使得这个问题一直存在着,旧数据一直被保留并使用。

    5.现在,有需求要导出某个租户下的数据,结果发现 tenant_id 乱七八糟,你难不难受?

    6.那么,梳理完逻辑链条,我认为,虽然某些程序员的在实现上犯了低级错误,但不是主要原因,罪魁祸首应该是设计上的懒惰。设计精细一点,明确好 tenant_id 到哪张表为止,也就没有后面的 sql 注入、数据错误那么多事了。所以,我才说,给租户隔离等于给每张表加 tenant_id 的设计很傻逼!

    上次编辑于:
    贡献者: levy
    + diff --git a/devops/about-arm-things-you-need-to-know.html b/devops/about-arm-things-you-need-to-know.html index c336cae5..23181825 100644 --- a/devops/about-arm-things-you-need-to-know.html +++ b/devops/about-arm-things-you-need-to-know.html @@ -5,7 +5,7 @@ - 关于 Arm 你需要了解的三件事 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    关于 Arm 你需要了解的三件事

    DailyDevOpsLinux

    关于 Arm 你需要了解的三件事

    Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

    跟我们有什么关系呢?

    1. MacOS 的 M1 芯片是基于 Arm 的
    2. 云厂商及生态都在积极与 Arm 进行合作
    3. Docker 镜像的构建有注意事项

    构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
    这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。

    解决办法就是,想办法把相关命令前置,提前执行,再构建镜像。

    如注释掉 Dockerfile 里的 Run chmod 777,改成在构建镜像前执行。

    视频里有更详细的讲解:

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    关于 Arm 你需要了解的三件事

    DailyDevOpsLinux

    关于 Arm 你需要了解的三件事

    Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

    跟我们有什么关系呢?

    1. MacOS 的 M1 芯片是基于 Arm 的
    2. 云厂商及生态都在积极与 Arm 进行合作
    3. Docker 镜像的构建有注意事项

    构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
    这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。

    解决办法就是,想办法把相关命令前置,提前执行,再构建镜像。

    如注释掉 Dockerfile 里的 Run chmod 777,改成在构建镜像前执行。

    视频里有更详细的讲解:

    上次编辑于:
    贡献者: levy
    + diff --git a/devops/common-solutions-of-object-storage-for-static-assets.html b/devops/common-solutions-of-object-storage-for-static-assets.html index c5f93d74..722a0ce7 100644 --- a/devops/common-solutions-of-object-storage-for-static-assets.html +++ b/devops/common-solutions-of-object-storage-for-static-assets.html @@ -5,7 +5,7 @@ - 对象存储静态资源常见操作 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    对象存储静态资源常见操作

    FrontendDevOpsS3OBSOSS

    对象存储静态资源常见操作

    前言

    把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

    本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

    阿里云OSS

    对于新建的bucket,需要做一些设置,才能正常使用静态资源。

    绑定域名

    则使用自定义域名访问,可以解决访问 html 变成下载的问题。

    CNAME设置

    如果绑定的是同一个阿里云账号下的域名,则可以自动添加 CNAME 记录。否则需要手动添加。

    查看 bucket 外网地址:my-bucket.oss-cn-shenzhen.aliyuncs.comopen in new window

    则去域名解析供应商设置:
    static.domain.comopen in new window(自定义域名) -> CNAME -> my-bucket.oss-cn-shenzhen.aliyuncs.comopen in new window

    HTTPS证书托管

    上传证书,开启 HTTPS

    如果没有证书,查看教程获取:🔒免费开启HTTPSopen in new window

    公共读

    这样可以解决访问链接超时的问题。

    CORS跨域设置

    在基础设置下,找到跨域设置

    在来源中设置域名,或ip地址。下面给出最简单的示例为 *,实际可以根据需要填写允许的域名,一行一个。

    • 将allowed origins设置成 *
    • 将allowed methods设置成GET, POST, PUT, DELETE, HEAD
    • 将allowed headers设置成 *
    • 将expose headers设置成
      • etag
      • x-oss-request-id

    这样可以解决字体无法显示、JavaScript跨域的问题。

    华为云OBS

    跨域设置

    华为云的入口如下:

    具体规则的填写是类似阿里云OSS的。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    对象存储静态资源常见操作

    FrontendDevOpsS3OBSOSS

    对象存储静态资源常见操作

    前言

    把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

    本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

    阿里云OSS

    对于新建的bucket,需要做一些设置,才能正常使用静态资源。

    绑定域名

    则使用自定义域名访问,可以解决访问 html 变成下载的问题。

    CNAME设置

    如果绑定的是同一个阿里云账号下的域名,则可以自动添加 CNAME 记录。否则需要手动添加。

    查看 bucket 外网地址:my-bucket.oss-cn-shenzhen.aliyuncs.comopen in new window

    则去域名解析供应商设置:
    static.domain.comopen in new window(自定义域名) -> CNAME -> my-bucket.oss-cn-shenzhen.aliyuncs.comopen in new window

    HTTPS证书托管

    上传证书,开启 HTTPS

    如果没有证书,查看教程获取:🔒免费开启HTTPSopen in new window

    公共读

    这样可以解决访问链接超时的问题。

    CORS跨域设置

    在基础设置下,找到跨域设置

    在来源中设置域名,或ip地址。下面给出最简单的示例为 *,实际可以根据需要填写允许的域名,一行一个。

    • 将allowed origins设置成 *
    • 将allowed methods设置成GET, POST, PUT, DELETE, HEAD
    • 将allowed headers设置成 *
    • 将expose headers设置成
      • etag
      • x-oss-request-id

    这样可以解决字体无法显示、JavaScript跨域的问题。

    华为云OBS

    跨域设置

    华为云的入口如下:

    具体规则的填写是类似阿里云OSS的。

    上次编辑于:
    贡献者: levy
    + diff --git a/devops/docker-build-and-push-script.html b/devops/docker-build-and-push-script.html index a5682755..905fd883 100644 --- a/devops/docker-build-and-push-script.html +++ b/devops/docker-build-and-push-script.html @@ -5,7 +5,7 @@ - Docker 构建镜像、推送、启动实用脚本 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Docker 构建镜像、推送、启动实用脚本

    DevOpsLinux

    Docker 构建镜像、推送、启动实用脚本

    misc

    存储多份 docker 认证信息:

    mkdir "~/.project1"
    +    
    跳至主要內容

    Docker 构建镜像、推送、启动实用脚本

    DevOpsLinux

    Docker 构建镜像、推送、启动实用脚本

    misc

    存储多份 docker 认证信息:

    mkdir "~/.project1"
     mkdir "~/.project2"
     
     docker --config ~/.project1 login registry.example.com -u <username> -p <deploy_token>
    @@ -148,7 +148,7 @@
     echo "Container started!"
     
     docker logs -f $NAME
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/devops/index.html b/devops/index.html index 3fce40b6..c06052f5 100644 --- a/devops/index.html +++ b/devops/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Devops


    - +
    跳至主要內容

    Devops


    + diff --git a/devops/reduce-python-image-size.html b/devops/reduce-python-image-size.html index ea7d023a..2b6d7e18 100644 --- a/devops/reduce-python-image-size.html +++ b/devops/reduce-python-image-size.html @@ -5,7 +5,7 @@ - 缩减Python应用的镜像体积 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    缩减Python应用的镜像体积

    DevOpsPython

    缩减Python应用的镜像体积

    背景

    当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!

    能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

    原始Dockerfile

    先来看看 Dockerfile 的原始模样:

    FROM python:3.10.13
    +    
    跳至主要內容

    缩减Python应用的镜像体积

    DevOpsPython

    缩减Python应用的镜像体积

    背景

    当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!

    能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

    原始Dockerfile

    先来看看 Dockerfile 的原始模样:

    FROM python:3.10.13
     
     WORKDIR /app
     
    @@ -58,7 +58,7 @@
     RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt --ignore-installed
     

    安装依赖,应该一步到位,我们可以把这两行压缩成一行,以减少 docker layer 数量。

    另外,pip 安装依赖时,会在本地生成缓存,而这对于镜像来说是无用的,可以添加参数 --no-cache-dir禁止此行为。

    则上述两行代码优化如下:

    run pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip && \
         pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --ignore-installed --no-cache-dir -r requirements.txt
    -

    优化效果

    第一次优化,使用 slim 镜像:

    第二次优化,减少层数、取消本地缓存:

    小小的改动,大大的变化!

    参考

    https://icloudnative.io/posts/intro-guide-to-dockerfile-best-practices/open in new window
    https://icloudnative.io/posts/docker-images-part2-details-specific-to-different-languagesopen in new window
    https://www.ardanlabs.com/blog/2020/04/docker-images-part3-going-farther-reduce-image-sizeopen in new window

    上次编辑于:
    贡献者: levy
    - +

    优化效果

    第一次优化,使用 slim 镜像:

    第二次优化,减少层数、取消本地缓存:

    小小的改动,大大的变化!

    参考

    https://icloudnative.io/posts/intro-guide-to-dockerfile-best-practices/open in new window
    https://icloudnative.io/posts/docker-images-part2-details-specific-to-different-languagesopen in new window
    https://www.ardanlabs.com/blog/2020/04/docker-images-part3-going-farther-reduce-image-sizeopen in new window

    上次编辑于:
    贡献者: levy
    + diff --git a/devops/what-is-the-difference-between-sh-and-bash.html b/devops/what-is-the-difference-between-sh-and-bash.html index c539c6f2..274a2b8f 100644 --- a/devops/what-is-the-difference-between-sh-and-bash.html +++ b/devops/what-is-the-difference-between-sh-and-bash.html @@ -5,7 +5,7 @@ - sh与bash的区别 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    sh与bash的区别

    LinuxDevOpsVideo

    sh与bash的区别

    结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

    常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

    视频里有实战演示:

    以下是 ChatGPT 的相关回答:



    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    sh与bash的区别

    LinuxDevOpsVideo

    sh与bash的区别

    结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

    常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

    视频里有实战演示:

    以下是 ChatGPT 的相关回答:



    上次编辑于:
    贡献者: levy
    + diff --git a/english/contemporary-college-english-1.html b/english/contemporary-college-english-1.html index af121c95..fabb8755 100644 --- a/english/contemporary-college-english-1.html +++ b/english/contemporary-college-english-1.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第一册 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    现代大学英语精读(第2版)第一册

    English

    现代大学英语精读(第2版)第一册

    介绍

    全书链接:https://www.ximalaya.com/album/43891910open in new window

    虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

    虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

    值得一读的文章

    本册里面大部分是记叙文,阅读趣味性比较强。

    欧亨利不愧是大师,第一册里收录了两篇他的小说。

    另外,戏剧 The Monsters Are Due in Maple Street 也值得一读

    总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    现代大学英语精读(第2版)第一册

    English

    现代大学英语精读(第2版)第一册

    介绍

    全书链接:https://www.ximalaya.com/album/43891910open in new window

    虽然这是英语专业的大学教材,但不用担心难度——只要英语不是太差,上过大学的都能看,甚至刚参加完高考的学生就能看。

    虽然名字说是精读,但自己依然可以泛读、挑选着读。因此,本文记录的是经过个人挑选认为值得一读的文章,最主要的目的是,塑造全英文阅读的习惯。除此之外,也有开阔视野,增进对西方文化、经典文学作品的了解之意。

    值得一读的文章

    本册里面大部分是记叙文,阅读趣味性比较强。

    欧亨利不愧是大师,第一册里收录了两篇他的小说。

    另外,戏剧 The Monsters Are Due in Maple Street 也值得一读

    总结一下,本册难度较低,就是来建立信心的。如果想只阅读精华,那只看我挑选出的三篇文章就够了。适应了全英文阅读后,就可以开始看第二册的内容了。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/contemporary-college-english-2.html b/english/contemporary-college-english-2.html index 2fb055d5..15bd9d6c 100644 --- a/english/contemporary-college-english-2.html +++ b/english/contemporary-college-english-2.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第二册 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    现代大学英语精读(第2版)第二册

    English

    现代大学英语精读(第2版)第二册

    介绍

    全书链接:https://www.ximalaya.com/album/44290107open in new window

    本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

    值得一读的文章

    say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

    原文:https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdfopen in new window

    The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~

    原文:https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdfopen in new window

    button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?

    原文:https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdfopen in new window

    A Doctor's Dilemma 医生的困境

    the-oyster-and-the-pearl

    解析:https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/open in new window

    文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:

    1. 赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人
    2. 用好词,美化自己,把功劳往自己身上揽
    3. 强调双方关系: 我们是很友好的哦、我们是合作伙伴哦
    4. 回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!

    相关链接:

    另外,我摘录了一些觉得不错的句子:

    • When dealing with people, let us remember we are not dealing with creatures of logic.
    • We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity.
    • Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be
      understanding and forgiving.
    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    现代大学英语精读(第2版)第二册

    English

    现代大学英语精读(第2版)第二册

    介绍

    全书链接:https://www.ximalaya.com/album/44290107open in new window

    本册多了一些议论文,难度有所增加。同时记叙文也更加有内涵,立意高了不少。 另外就是,语言难度有所上升——比如一些骂人的话,单词能认识,变成句子就不懂了😂。

    值得一读的文章

    say-yes-by-tobias-wolff 第一次没读懂,看到最后,我还以为家里进小偷了 😂

    原文:https://www.missmccalister.com/uploads/3/0/9/3/30937509/lesson-7a-say-yes-by-tobias-wolff.pdfopen in new window

    The Dog of Pompeii 讲述了一个盲人男孩与一只小狗的故事,令人泪目。结尾比较含蓄,思考片刻明白后,就破防了~

    原文:https://www.acaedu.net/cms/lib3/TX01001550/Centricity/Domain/562/Week%206%20-%20The%20Dog%20of%20Pompeii.pdfopen in new window

    button button 人心拷问:如果按下按钮,你能得到一笔钱,但世上会有一人因此而死去,你会按下按钮吗?

    原文:https://christian_fuller.myteachersite.org/teacher/files/documents/button%20button.pdfopen in new window

    A Doctor's Dilemma 医生的困境

    the-oyster-and-the-pearl

    解析:https://schoolworkhelper.net/william-saroyans-the-oyster-and-the-pearl-summary-analysis/open in new window

    文册收录了两篇奥巴马的演讲,还是挺有意思的,能打动人心的部分当然有,同时也可以看出一些政治家演讲的特色:

    1. 赞美对方。糖衣炮弹过去,对方就伸手不打笑脸人
    2. 用好词,美化自己,把功劳往自己身上揽
    3. 强调双方关系: 我们是很友好的哦、我们是合作伙伴哦
    4. 回顾历史,展望未来:我们已经走了很长的路,做出了很多的改变,取得了很大的进步,为了下一代、为了未来,我们要加油鸭!

    相关链接:

    另外,我摘录了一些觉得不错的句子:

    • When dealing with people, let us remember we are not dealing with creatures of logic.
    • We are dealing with creatures of emotion, creatures bristling with prejudices, and motivated by pride and vanity.
    • Any fool can criticize, condemn and complain—and most fools do. But it takes character and self-control to be
      understanding and forgiving.
    上次编辑于:
    贡献者: levy
    + diff --git a/english/contemporary-college-english-3.html b/english/contemporary-college-english-3.html index ff76dadb..6de84278 100644 --- a/english/contemporary-college-english-3.html +++ b/english/contemporary-college-english-3.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第三册 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    现代大学英语精读(第2版)第三册

    English

    现代大学英语精读(第2版)第三册

    介绍

    全书链接:https://www.ximalaya.com/album/44439108open in new window

    阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

    这种转变,说明了两点:

    1. 我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了
    2. 我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读

    值得一读的文章

    A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:

    原文:https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdfopen in new window

    The Invisible Japanese Gentlemen

    原文:https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292open in new window

    My Grandmother, the Bag Lady 被这篇文章打动,泪目了

    The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇

    Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史

    the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过

    原文+分析(左边是内容,右边是分析):https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysisopen in new window

    The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状

    Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。

    电影:https://www.bilibili.com/bangumi/play/ep332629?theme=movieopen in new window

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    现代大学英语精读(第2版)第三册

    English

    现代大学英语精读(第2版)第三册

    介绍

    全书链接:https://www.ximalaya.com/album/44439108open in new window

    阅读本册,我在阅读上开始有了一定的厌倦感。这种厌倦感来自于,文章看得多了,不再首先关注是否完整读懂了文章,而是更关心文章是否有趣、是否吸引人——如果不能吸引我,我都不想去关心你在说什么。所以,有些说明议论文,我是跳着看的,因为某些段落让我觉得很无聊,内容不吸引我,行文呆板僵硬,充满了说教的味道。

    这种转变,说明了两点:

    1. 我开始应用我在中文阅读中培养出来的阅读喜爱,去评判英文文章了
    2. 我开始有意识地运用阅读技巧,恰当地泛读,不再是拿到一篇文章就逐字逐句地读

    值得一读的文章

    A-DILL-PICKLE 讲述的是曾经的恋人偶遇,欧亨利式结尾:

    原文:https://www.katherinemansfieldsociety.org/archive/www.katherinemansfieldsociety.org/assets/KM-Stories/A-DILL-PICKLE1917.pdfopen in new window

    The Invisible Japanese Gentlemen

    原文:https://www.ff.umb.sk/app/cmsFile.php?disposition=a&ID=4292open in new window

    My Grandmother, the Bag Lady 被这篇文章打动,泪目了

    The End of the Civil War 本册收录了挺多美国南北战争相关的内容,这是其中角度新颖的一篇

    Why Historians Disagree 科普了一下历史学是如何看待历史,有助于我们更理性地学习历史

    the-most-dangerous-game 可以说是三册中最精彩的一篇小说之一,不容错过

    原文+分析(左边是内容,右边是分析):https://www.litcharts.com/lit/the-most-dangerous-game/summary-and-analysisopen in new window

    The Bench 用一个黑人小哥故意坐在标明只能由欧洲人坐的板凳上的故事,以小见大,描绘了 Civil War 之后美国种族歧视现状

    Twelve Angry Men 十二怒汉,是本册的精彩内容之一。激发了对法治话题的兴趣,看得我直接睡不着,接着去B站看电影版。

    电影:https://www.bilibili.com/bangumi/play/ep332629?theme=movieopen in new window

    上次编辑于:
    贡献者: levy
    + diff --git a/english/contemporary-college-english-4.html b/english/contemporary-college-english-4.html index b4a40a22..52748dd9 100644 --- a/english/contemporary-college-english-4.html +++ b/english/contemporary-college-english-4.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第四册 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    现代大学英语精读(第2版)第四册

    English

    现代大学英语精读(第2版)第四册

    介绍

    全书链接:https://www.ximalaya.com/album/44641280open in new window

    本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

    值得一读的文章

    Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

    Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”

    The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”

    A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定

    The World House 马丁·路德·金 的文章

    Soldier's Heart 也叫PTSD,由战后士兵自述

    Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。

    The Rivals
    两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png
    强烈推荐,你一定也会“surprised”!

    Cord:一对母女的故事

    论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdfopen in new window

    The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章

    Man of the Moment 又到了令人享受的戏剧欣赏时间

    Is Everybody Happy 讨论了幸福的定义

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    现代大学英语精读(第2版)第四册

    English

    现代大学英语精读(第2版)第四册

    介绍

    全书链接:https://www.ximalaya.com/album/44641280open in new window

    本册立意又高了一个层次,开始讨论哲学、政治等上层建筑,并提供了新颖的视角。如果说,前几册是开阔视野,本册开始拓展思维了。

    值得一读的文章

    Groundless Beliefs 提醒我们,自认为正确的东西,很可能只是毫无根据的盲从

    Economic Growth Is a Path to Perdition, Not Prosperity 提醒我们更加理性地看待“增长”

    The Damned Human Race 其实我觉得本文并非“值得一读”,但还列举出来原因有二:一是毕竟是马克·吐温之作,不可怠慢;二是我认为其论点新颖,不愧为讽刺大师,但论据越到后面越无说服力,可列为“反面教材”

    A String of Beads 挺有趣的文章,揭示了人们普遍的一种心理:事情的真相相比,人们更在意的是故事的戏剧性,或是否符合自己心中的设定

    The World House 马丁·路德·金 的文章

    Soldier's Heart 也叫PTSD,由战后士兵自述

    Secrets 这是一篇可以让人由好奇变为感动的文章。我原以为讲的是主人公的 “aunt” 与其旧情人的故事,直到最后,“forgive”一词的出现,令我恍然大悟,瞬间泪目。为避免剧透,我不细说了,强烈推荐。

    The Rivals
    两个男的在言语上较劲,只觉得好笑,可看到后面,我心情如此图1657000860176_9DFDD7F8-C809-4aa4-A996-05F4C984C76A.png
    强烈推荐,你一定也会“surprised”!

    Cord:一对母女的故事

    论文分析。有一定难度,因为它是综合了同一个作者的三篇文章进行分析,需要挑选着读:https://www.yuque.com/office/yuque/0/2022/pdf/160590/1657283857640-11aa59bb-edc1-475f-a97f-37124a27afd9.pdfopen in new window

    The Never-Ending Fight “机器人学之父”阿西莫夫的反迷信文章

    Man of the Moment 又到了令人享受的戏剧欣赏时间

    Is Everybody Happy 讨论了幸福的定义

    上次编辑于:
    贡献者: levy
    + diff --git a/english/contemporary-college-english-5.html b/english/contemporary-college-english-5.html index c1122f93..be65d303 100644 --- a/english/contemporary-college-english-5.html +++ b/english/contemporary-college-english-5.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第五册 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    现代大学英语精读(第2版)第五册

    English

    现代大学英语精读(第2版)第五册

    介绍

    全书链接:https://www.ximalaya.com/album/49466046open in new window

    本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

    Who Are You and What Are You Doing Here

    原文链接:https://m.kekenet.com/daxue/201909/593904.shtmlopen in new window

    本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

    摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
    particular. To almost every university education is a means to an end. For students, that end is a good job.

    The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
    own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
    have the experience that Longinus associated with the sublime: You feel that you have actually created the text
    yourself. For somehow your predecessors are more yourself than you are.

    Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
    fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
    teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

    Two Kinds

    原文链接:https://m.kekenet.com/daxue/201909/595380.shtmlopen in new window

    本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。

    摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This
    wasn't China. I had listened to her before and look what happened. She was the stupid one.

    You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"

    "Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only
    one kind of daughter can live in this house. Obedient daughter!"

    "Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.

    Love is a Fallacy

    原文链接:https://www.ximalaya.com/sound/414753193open in new window

    这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。

    Rewriting American History

    原文链接:https://www.ximalaya.com/sound/414754775open in new window

    本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。

    Nobel Peace Price About Global Warming

    原文链接:https://www.ximalaya.com/sound/414756339open in new window

    The Bluest Eyes

    原文链接:https://www.ximalaya.com/sound/414757228open in new window

    这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。

    How News Becomes Options and Opinions Off-Limits

    原文链接:https://www.ximalaya.com/sound/414759448open in new window

    本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。

    摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without
    which all other freedoms would fall.

    In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and
    untrammeled.

    A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free
    societies are dynamic, noisy, turbulent and full of radical disagreements.

    The Indispensable Opposition

    原文链接:https://m.kekenet.com/daxue/202001/603745.shtmlopen in new window

    本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

    摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

    If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
    freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

    We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
    is to find the truth.

    The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
    the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
    is tolerated as constitutional but must be maintained because it is in fact indispensable.

    The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
    people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
    just as necessary that the party in power should never outrage the minority. That means that it must listen to the
    minority and be moved by the criticisms of the minority. That means that its measures must take account of the
    minority's objections, and that in administering measures it must remember that the minority may become the majority.

    The Danger of a Single Story

    原文链接:https://www.ximalaya.com/sound/414765357open in new window

    认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

    摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
    the story of another person, but to make it the definitive story of that person.

    All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
    overlook the many other stories that formed me.

    The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
    incomplete. They make one story become the only story.

    The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
    difficult. It emphasizes how we are different rather than how we are similar.

    Come Rain or Come Shine

    原文链接:https://www.ximalaya.com/sound/414767340open in new window

    看得不太懂,但故事倒挺有意思的。

    解析:https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652dbopen in new window

    Invisible Man

    原文链接:https://www.ximalaya.com/sound/414776047open in new window

    节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.

    You've Got to Find What You Love

    原文链接:https://www.ximalaya.com/sound/414769322open in new window

    Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

    精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
    that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
    believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
    you off the well-worn path. And that would make all the difference.

    Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
    me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
    your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
    you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
    looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
    relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

    I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
    what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
    something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
    choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
    failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
    going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
    There is no reason not to follow your heart.

    Where Do We Go from Here

    原文链接:https://www.ximalaya.com/sound/414771197open in new window

    本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。

    摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is
    the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or
    Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to
    the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.

    And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you
    can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may
    murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    现代大学英语精读(第2版)第五册

    English

    现代大学英语精读(第2版)第五册

    介绍

    全书链接:https://www.ximalaya.com/album/49466046open in new window

    本册开始,阅读难度再次上升,同时也更有思考的乐趣。另外,从本册开始,会在后面推荐一些著名的英文演讲,其中就有大家熟悉的乔布斯,激发读者的热情。

    Who Are You and What Are You Doing Here

    原文链接:https://m.kekenet.com/daxue/201909/593904.shtmlopen in new window

    本文分享了作者教育观,并阐述了上大学的意义,否定了上学是为了就业的观点。

    摘录如下: Education has one salient enemy in present-day America, and that enemy is education——university education in
    particular. To almost every university education is a means to an end. For students, that end is a good job.

    The best reason to read great writers is to see if they may know you better than you know yourself. You may find your
    own suppressed and rejected thoughts flowing back to you with an "alienated majesty." Reading the great writers, you may
    have the experience that Longinus associated with the sublime: You feel that you have actually created the text
    yourself. For somehow your predecessors are more yourself than you are.

    Trying to figure out whether the stuff you're reading is true or false and being open to having your life changed is a
    fraught, controversial activity. Doing so requires energy from the professors. This kind of perspective-altering
    teaching and learning can cause the things which administrators fear above all else: trouble, arguments, bad press, etc.

    Two Kinds

    原文链接:https://m.kekenet.com/daxue/201909/595380.shtmlopen in new window

    本文以女儿的视角讲述了母亲望女成凤而女儿叛逆的故事,文中母亲代表的中国式家庭教育观,我想不少人会感同身受。用现在的眼光看来,这只不过是一段充满了失败的沟通、无法双赢的家庭教育经历,借助非暴力沟通或双赢思维是能够避免的。

    摘录如下: I didn't budge. And then I decided. I didn't have to do what my mother said anymore. I wasn't her slave. This
    wasn't China. I had listened to her before and look what happened. She was the stupid one.

    You want me to be someone that I'm not!" I sobbed. "I will never be the kind of daughter you want me to be!"

    "Only two kinds of daughters," she shouted in Chinese. "Those who are obedient and those who follow their own mind! Only
    one kind of daughter can live in this house. Obedient daughter!"

    "Then I wish I wasn't your daughter, I wish you weren't my mother," I shouted.

    Love is a Fallacy

    原文链接:https://www.ximalaya.com/sound/414753193open in new window

    这多篇看下来,这篇是目前最有意思、最有趣、最令人忍俊不禁的。为避免剧透,我就少说一点。本文首尾响应,完美闭环,看完后我直呼我直呼,哈哈哈哈。就说到这了,总之墙裂推荐。

    Rewriting American History

    原文链接:https://www.ximalaya.com/sound/414754775open in new window

    本文讲述的是美国历史教材的一些变化及背后的思考。我认可其中的一个做法:不是对历史给一个所谓正统的结论,而是充分准备材料,从不同的角度陈述观点,引导人思考。

    Nobel Peace Price About Global Warming

    原文链接:https://www.ximalaya.com/sound/414756339open in new window

    The Bluest Eyes

    原文链接:https://www.ximalaya.com/sound/414757228open in new window

    这是个记叙文,然而,我认为确是这么多篇文章中,最难懂的。

    How News Becomes Options and Opinions Off-Limits

    原文链接:https://www.ximalaya.com/sound/414759448open in new window

    本文说明了当代一种社会现代:人们在看新闻,不是在看“事实”,而是在看“观点”,作者认为,现在的新闻跟小说并非没有相似之处了。然而,作者对这种现象并非完全持否定态度,他认为这是言论自由的一种方式。最后,作者还表达了在自由社会对“尊重”一词的看法。

    摘录如下: In any version of a free society, the value of free speech must rank the highest, for that is the freedom without
    which all other freedoms would fall.

    In free societies, you must have the free play of ideas. There must be arguments, and it must be impassioned and
    untrammeled.

    A free society is not a calm and eventless place——that is the kind of static, dead society dictators try to create. Free
    societies are dynamic, noisy, turbulent and full of radical disagreements.

    The Indispensable Opposition

    原文链接:https://m.kekenet.com/daxue/202001/603745.shtmlopen in new window

    本文所表达的观点,属于民主与自由思想的经典内容:不可缺少的反对派(或忠诚的反对派)。

    摘录如下: I wholly disapprove of what you say, but will defend to the death your right to say it.

    If we truly wish to understand why freedom is necessary in a civilized society, we must begin by realizing that, because
    freedom of discussion improves our own opinions, the liberties of other men are our own vital necessity.

    We must insist that free oratory is only the beginning of free speech; it is not the end, but a means to an end. The end
    is to find the truth.

    The only reason for dwelling on all this is that if we are to preserve democracy we must understand its principles. And
    the principle which distinguishes it from all other forms of government is that in a democracy the opposition not only
    is tolerated as constitutional but must be maintained because it is in fact indispensable.

    The democratic system cannot be operated without effective opposition. For, in making the great experiment of governing
    people by consent rather than by coercion, it is not sufficient that the party in power should have a majority. It is
    just as necessary that the party in power should never outrage the minority. That means that it must listen to the
    minority and be moved by the criticisms of the minority. That means that its measures must take account of the
    minority's objections, and that in administering measures it must remember that the minority may become the majority.

    The Danger of a Single Story

    原文链接:https://www.ximalaya.com/sound/414765357open in new window

    认识到硬币有正反面,并坚持凡事至少从两个角度去看待与分析,那么本篇文章就没白读。

    摘录如下: It is impossible to talk about the single story without talking about power. Power is the ability not just to tell
    the story of another person, but to make it the definitive story of that person.

    All of these stories make me who l am. But to insist on only these negative stories is to flatten my experience and to
    overlook the many other stories that formed me.

    The single story creates stereotypes,and the problem with stereotypes is not that theyare untrue, but that they are
    incomplete. They make one story become the only story.

    The consequence of the single story is this: It robs people of dignity. It makes our recognition of our equal humanity
    difficult. It emphasizes how we are different rather than how we are similar.

    Come Rain or Come Shine

    原文链接:https://www.ximalaya.com/sound/414767340open in new window

    看得不太懂,但故事倒挺有意思的。

    解析:https://medium.com/the-afterglow/come-rain-or-come-shine-by-kazuo-ishiguro-finding-the-moment-b09e418652dbopen in new window

    Invisible Man

    原文链接:https://www.ximalaya.com/sound/414776047open in new window

    节选自同名小说(有改编),telling a story about a black teenager present his speech before white people.

    You've Got to Find What You Love

    原文链接:https://www.ximalaya.com/sound/414769322open in new window

    Steve Jobs 在 Standford 的演讲,没想到 Jobs 声音这么年轻!演讲分享三个故事,表达主旨“follow your heart”。

    精彩摘录如下: You can't connect the dots looking forward: You can only connect them looking backwards. So you have to trust
    that the dots will somehow connect in your future. You have to trust in something-your destiny,life,whatever. Because
    believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads
    you off the well-worn path. And that would make all the difference.

    Sometimes life is gonna hit you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept
    me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for
    your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what
    you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep
    looking, and don't settle. As with all matters of the heart, you'll know when you find it. And like any great
    relationship, it just gets better and better as the years roll on. So keep looking. Don't settle.

    I have looked in the mirror every morning and asked myself: "If today were the last day of my life,would I want to do
    what l am about to do today?" And whenever the answer has been "No" for too many days in a row, l know I need to change
    something. Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big
    choices in life. Because almost everything-all external expectations, all pride, all fear of embarrassment or
    failure-these things just fall away in the face of death,leaving only what is truly important. Remembering that you are
    going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked.
    There is no reason not to follow your heart.

    Where Do We Go from Here

    原文链接:https://www.ximalaya.com/sound/414771197open in new window

    本文是马丁·路德·金的另一篇呼吁黑人平等的演讲,从中可以看到与《I Have a Dream》的影子。

    摘录如下: As long as the mind is enslaved, the body can never be free. Psychological freedom,a firm sense of self-esteem, is
    the most powerful weapon against the long night of physical slavery. No Lincolnian emancipation proclamation or
    Johnsonian civil rights bill can totally bring this kind of freedom. The Negro will only be free when he reaches down to
    the inner depths of his own being and signs with the pen and ink of assertive manhood his own emancipation proclamation.

    And so I say to you today that I still stand by nonviolence. For through violence you may murder a murderer but you
    can't murder murder. Through violence you may murder a liar but you can't establish truth. Through violence you may
    murder a hater,but you can't murder hate. Darkness cannot put out darkness. Only light can do that.

    上次编辑于:
    贡献者: levy
    + diff --git a/english/contemporary-college-english-6.html b/english/contemporary-college-english-6.html index 1120d629..d88aa4ea 100644 --- a/english/contemporary-college-english-6.html +++ b/english/contemporary-college-english-6.html @@ -5,7 +5,7 @@ - 现代大学英语精读(第2版)第六册 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    现代大学英语精读(第2版)第六册

    English

    现代大学英语精读(第2版)第六册

    前言

    全书链接:https://www.ximalaya.com/album/49468954open in new window

    本册是整个系列的最后一册了,完结撒花🎉

    Paper Tigers

    原文链接:https://www.ximalaya.com/sound/414998175open in new window

    探讨了 Asian American 的教育经历与社会成就不符的现象。

    摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
    Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
    Damn earnest, striving middle-class servility.

    Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
    initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
    traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
    Hyun, who wrote a book called Breaking the Bamboo Ceiling.

    At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
    talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
    corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

    Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

    注:Chua
    也就是《虎妈战歌》的作者:https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7open in new window

    What Is News

    原文链接:https://www.ximalaya.com/sound/414999846open in new window

    提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

    摘录如下: The news is made rather than gather.

    We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
    accept their notions.

    “What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
    the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
    “come-on" to keep the viewer's attention until the commercials come.

    All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
    And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
    chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
    is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
    found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
    and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
    problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

    At War with the Planet

    原文链接:https://www.ximalaya.com/sound/415001340open in new window

    本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。

    How to Get the Poor off Our Conscience

    原文链接:https://www.ximalaya.com/sound/415004615open in new window

    本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。

    Housewifely Arts

    原文链接:https://www.ximalaya.com/sound/415006607open in new window

    本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:

    1. 冷幽默,毕竟有几处地方让我笑了
    2. 引以为戒,千万别学女主

    The One Against The Many

    原文链接:https://www.ximalaya.com/sound/415007539open in new window

    本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

    精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
    of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
    detailed, more comprehensive, more dogmatic.

    An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
    fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
    from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
    tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

    Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
    understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
    thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
    are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
    infallible priesthood.

    The American tradition has found this view of human history repugnant and false, against the belief in the
    all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
    that all answers to political and social problems can be found in the back of some sacred book, against the
    deterministic interpretation of history.

    Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
    they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
    the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
    a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
    certainty of absolute abuse.

    The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
    Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
    infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
    limitations of the human intellect and the infirmity of the human spirit.

    Notes on the English Character

    原文链接:https://www.ximalaya.com/sound/415017440open in new window

    本文对英格兰人的性格特征进行了简单的探讨。

    The Death of a Pig

    原文链接:https://www.ximalaya.com/sound/415019607open in new window

    本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。

    原文链接:https://www.ximalaya.com/sound/415020743open in new window

    Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。

    The Accidental Universe

    原文链接:https://www.ximalaya.com/sound/415022329open in new window

    本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。

    Rowling's Speech at Harvard

    原文链接:https://www.ximalaya.com/sound/415018537open in new window

    罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    现代大学英语精读(第2版)第六册

    English

    现代大学英语精读(第2版)第六册

    前言

    全书链接:https://www.ximalaya.com/album/49468954open in new window

    本册是整个系列的最后一册了,完结撒花🎉

    Paper Tigers

    原文链接:https://www.ximalaya.com/sound/414998175open in new window

    探讨了 Asian American 的教育经历与社会成就不符的现象。

    摘录如下: Let me summarize my feelings toward Asian values: Damn filial piety. Damn grade grubbing. Damn Ivy League mania.
    Damn deference to authority. Damn humility and hard work. Damn harmonious relations. Damn sacrificing for the future.
    Damn earnest, striving middle-class servility.

    Maybe a traditionally Asian upbringing is the problem. In order to be a leader, you must have followers. Associates are
    initially judged on how well they do the work they are assigned. But being a leader requires different skill sets. “The
    traits that got you to where you are won't necessarily take you to the next level," says the diversity consultant Jane
    Hyun, who wrote a book called Breaking the Bamboo Ceiling.

    At Yale, Chua made the connection between her upbringing and her adult dissatisfaction. “My parents didn't sit around
    talking about politics and philosophy at the dinner table," she told the students. Even after she had escaped from
    corporate law and made it onto a law faculty,“I was kind of lost. I just didn't feel the passion."

    Chua's Chinese education had gotten her through an elite schooling, but it left her unprepared for the real world.

    注:Chua
    也就是《虎妈战歌》的作者:https://baike.baidu.com/item/%E8%94%A1%E7%BE%8E%E5%84%BF/3424632#7open in new window

    What Is News

    原文链接:https://www.ximalaya.com/sound/414999846open in new window

    提示我们如何看待新闻,与前面的如何看待历史可谓是姐妹篇。

    摘录如下: The news is made rather than gather.

    We see what we expect to see. We focus on what we are paid to see. And those who pay us to see usually expect us to
    accept their notions.

    “What is news?" News,we might say, may be history in its first and best form, or the stuff of literature, or a record of
    the condition of a society, or the expression of things, but in its worst form it can also be mainly a “filler,” a
    “come-on" to keep the viewer's attention until the commercials come.

    All of which leads us to reiterate, first, that there are no simple answers to the question “What is news?"
    And, second, that it is not our purpose to tell you what you ought to believe about the question. The purpose of this
    chapter is to arouse your interest in thinking about the question. Your answers are to be found by knowing what you feel
    is significant and how your sense of the significant conforms with or departs from that of others. Answers are to be
    found in your ideas about the purpose of public communication, and in your judgment of the kind of society you live in
    and wish to live in. We cannot provide answers to these questions. But you also need to know something about the
    problems, limitations, traditions, motivations, and, yes, even the delusions of the television news industry.

    At War with the Planet

    原文链接:https://www.ximalaya.com/sound/415001340open in new window

    本文为了我们科普了人类社会与自然环境的特征,提供了一个新颖而深刻的视角来看待人与自然的关系,启发我们在采取人与自然和谐相处的措施时,不要走极端。

    How to Get the Poor off Our Conscience

    原文链接:https://www.ximalaya.com/sound/415004615open in new window

    本文讨论了面对一直存在的贫富差距现象,社会思想、政府措施在历史上经历了怎样的变化。

    Housewifely Arts

    原文链接:https://www.ximalaya.com/sound/415006607open in new window

    本文主要表达的是对母亲的回忆,其实属于可看可不看类型,之所以还放上来,原因有二:

    1. 冷幽默,毕竟有几处地方让我笑了
    2. 引以为戒,千万别学女主

    The One Against The Many

    原文链接:https://www.ximalaya.com/sound/415007539open in new window

    本文主要是讲美国文明的,没有一个地方出现China,但几乎处处都能看到China的影子。绝对不容错过的好文,可以说是反洗脑的基础。

    精彩摘录: It is important here to insist on the distinction between ideals and ideology. Ideals refer to the long-run goals
    of a nation and the spirit in which these goals are pursued. Ideology is something different, more systematic, more
    detailed, more comprehensive, more dogmatic.

    An ideology, in other words, is an abstraction from reality. There is nothing wrong with abstractions or models. In
    fact, we could not conduct discourse without them. The ideological fallacy is to forget that ideology is an abstraction
    from reality and to regard it as reality itself. The besetting sin of the ideologist, in short, is to confuse his own
    tidy models with the vast, turbulent, unpredictable, and untidy reality which is the stuff of human experience.

    Consider for a moment the ideologist's view of history. The ideologist contends that the mysteries of history can be
    understood in terms of a clear-cut, absolute, social creed which explains the past and forecasts the future. Ideology
    thus presupposes a closed universe whose history is determined, whose principles are fixed, whose values and objectives
    are deducible from a central body of social dogma and often whose central dogma is confided to the custody of an
    infallible priesthood.

    The American tradition has found this view of human history repugnant and false, against the belief in the
    all-encompassing power of a single explanation, against the commitment to the absolutism of ideology, against the notion
    that all answers to political and social problems can be found in the back of some sacred book, against the
    deterministic interpretation of history.

    Ideologists are afraid of the free flow of ideas, even of deviant ideas within their own ideology. They are convinced
    they have a monopoly on the Truth. Therefore they always feel that they are only saving the world when they slaughter
    the heretics. Their objective remains that of making the world over in the image of their dogmatic ideology. The goal is
    a monolithic world, organized on the principle of infallibility-but the only certainty in an absolute system is the
    certainty of absolute abuse.

    The goal of free men is quite different. Free men know many truths, but they doubt whether any mortal man knows the
    Truth. Their religious and their intellectual heritage join in leading them to suspect fellow men who lay claim to
    infallibility. They believe that there is no greater delusion than for man to mistake himself for God. They accept the
    limitations of the human intellect and the infirmity of the human spirit.

    Notes on the English Character

    原文链接:https://www.ximalaya.com/sound/415017440open in new window

    本文对英格兰人的性格特征进行了简单的探讨。

    The Death of a Pig

    原文链接:https://www.ximalaya.com/sound/415019607open in new window

    本文主人公本计划养一只猪来吃,却养死了。为何把这篇文章收录进来呢,可能是因为里面细节写得好吧,把主人公的病急乱投医的场景写得较为生动。

    原文链接:https://www.ximalaya.com/sound/415020743open in new window

    Princeton学长回校演讲,核心观点是:你以为你的成功全靠实力,然而其中隐含着运气。

    The Accidental Universe

    原文链接:https://www.ximalaya.com/sound/415022329open in new window

    本文是物理相关的说明文,讲了多重宇宙假设,提出一种观点:我们所在的宇宙是出于偶然。

    Rowling's Speech at Harvard

    原文链接:https://www.ximalaya.com/sound/415018537open in new window

    罗琳的英音很好听,而且本次演讲也很幽默,建议边看边听。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/everyone-can-learn-english-1-overview.html b/english/everyone-can-learn-english-1-overview.html index a2428988..6825d07f 100644 --- a/english/everyone-can-learn-english-1-overview.html +++ b/english/everyone-can-learn-english-1-overview.html @@ -5,7 +5,7 @@ - 人人都能学会的英语1:开篇 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    人人都能学会的英语1:开篇

    English

    人人都能学会的英语1:开篇

    为什么学

    不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

    也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

    • 出国
    • 去外企
    • 有国际化需求
    • 学习专业领域的前沿知识

    我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

    此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

    怎么学

    关于怎么学的问题,我认为需要内外兼修。

    建设心态

    在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

    如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

    其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

    有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

    与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

    提升认知

    一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

    所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

    整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

    以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

    明确意义

    需要问一下自己,英语对自己而言到底是什么?

    是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

    上面只是举例,并且例子之间并不冲突,是可以兼容的。

    总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

    制定计划

    整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

    说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

    培养习惯

    注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

    建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

    注重方法

    前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

    至于学习方法,因学习内容而异。按照知识体系划可大致分为:

    • 基础知识:
      • 音标
      • 单词
      • 语法
    • 综合能力
      • 听说
      • 读写

    基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

    • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
    • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

    今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    人人都能学会的英语1:开篇

    English

    人人都能学会的英语1:开篇

    为什么学

    不可否认,英语始终在工作、生活中扮演着不可或缺的角色。可能有人并未意识到,我们的科学技术、政治经济、文化娱乐等领域都深受着英语世界的影响。掌握英语,运用英语,始终是非常必要的。

    也许有人会反驳:我又不出国,我又不去外企上班,学英语有啥用?嗯,说得有道理,这属于实用主义。那么按此逻辑,但凡有以下需求,就不得不承认要学英语:

    • 出国
    • 去外企
    • 有国际化需求
    • 学习专业领域的前沿知识

    我们尤其关注最后一点,它是与本职业相关的。以程序员为例,很多新的技术都起源于西方世界,往往文档资料都是先有英文版的,想要中文版至少要半年甚至更久,要想技术快人一步,职业发展更上一层楼,英语能力必不可少。

    此时也许有人会担心,自己已错过了最佳的学习时机,现在太迟了吧?非也。种一棵树最好的时机是10年前,其次是现在。如果把人生当成马拉松,那么现在开始,一定来得及,绝不会晚。记住,Better late than never!

    怎么学

    关于怎么学的问题,我认为需要内外兼修。

    建设心态

    在意识到、并承认掌握英语的必要性后,我们需要真正地相信:现在行动并不晚。

    如果这点不能相信,那说什么都没用,因为人会找各种借口让自己放弃,如“现在没时间,还有更重要的事”、“性价比太低了”、“以后再说吧”、“用中文也行”。如果是这样,那事情注定是要失败的。

    其次,要学会耐心。人们常常高估一个月所能做的事,却低估一年所能做的事。不要浮躁,学会接受每天进步一点点:

    有了耐心,才能在整个过程中,抵制住营销号“神奇方法”的诱惑。记住,耐心是美德,这在别的领域也适用。

    与此同时,在学习过程中,注意建立自己的信心。从怀疑自己是否能,到相信自己能,最后内化成不再需要强调这件事。

    提升认知

    一定要搞明白:英语是技能,不只是知识。技能意味着,不仅要输入,还要输出,且需要反复地练习。

    所以,在学习过程中,一定要让输入与输出闭环:如果想提高听力,就要辅以口语练习;如果想提高阅读能力,就要做相应的写作练习。

    整个过程中,强调的是实践,是行动力,而不是记忆力。不要妄想把大量的知识装入脑子里——那是没用的,因为它不属于你。只有经过练习、能说出来或写出来的东西,才是你的,才会内化成自己的能力。

    以上认知很重要,如果不能达成共识,那后面的方法对你是无效的。

    明确意义

    需要问一下自己,英语对自己而言到底是什么?

    是考试、考证上分的手段,还是升职加薪的工具,抑或是丰富自己、了解另一种文化、开阔视野的方式?

    上面只是举例,并且例子之间并不冲突,是可以兼容的。

    总之,对这个问题的回答,反映了个人动机,对行动力有很大的影响。

    制定计划

    整个学习过程是超过一个月的,对于这种时长的事件,是需要制定计划的。

    说直白了,就是要拆分目标,评估工作量,然后按计划执行,并能在实践中结合自身情况动态调整。这种能力,不仅是学英语,学任何东西都是适用的。

    培养习惯

    注意培养自己的习惯,利用惯性帮助自己坚持,减少半途而废的可能。

    建议固定时间、固定地点来学习,可以结合自己的日常作息规律,养成一个新的习惯。如每天早上起来,或晚上洗完澡后,固定抽时间来学习。

    注重方法

    前面说了那么多,都是内功,放在前面,是因为我认为心态建设、认知及习惯比方法更重要。

    至于学习方法,因学习内容而异。按照知识体系划可大致分为:

    • 基础知识:
      • 音标
      • 单词
      • 语法
    • 综合能力
      • 听说
      • 读写

    基础知识是一定要掌握的,而对于综合能力,基于实用主义,不同目的可有不同的侧重。简单来说就是:

    • 如果需要利用英文资料学习、工作,或想阅读英文原著,或只需与人进行文字交流,应侧重读写
    • 如果需要与人进行即时言语沟通,或想看“生肉”,或想听英文有声读物,应侧重听说

    今天主要是作为开篇,做个介绍,具体的方法,会在后续的内容中逐步展开。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/everyone-can-learn-english-2-pronunciation.html b/english/everyone-can-learn-english-2-pronunciation.html index ec3f4ca9..f026de5f 100644 --- a/english/everyone-can-learn-english-2-pronunciation.html +++ b/english/everyone-can-learn-english-2-pronunciation.html @@ -5,7 +5,7 @@ - 人人都能学会的英语2:音标 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    人人都能学会的英语2:音标

    English

    人人都能学会的英语2:音标

    方法

    音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

    我推荐根据赖世雄的《美语音标》进行学习:

    • 在微信公众号 常春藤英语集团 买相关书籍
    • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
    • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

    有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

    我给出了我之前的学习进度记录,仅供参考
    image.png

    学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:https://www.bilibili.com/video/BV1qE411v7oEopen in new window
    (我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)

    再给出关于音标的一些资料:

    讨论

    这里我补充几点,虽然不是核心,却与主题息息相关:

    1. 是否要买实体书?
    2. 为什么是美语,而不是“英语”
    3. 关于口音(accent)

    建议买实体书

    现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

    这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

    而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

    不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

    • 捐书
    • 送人

    美语是主流

    也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

    一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

    不要纠结口音

    在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

    我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

    语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

    总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    人人都能学会的英语2:音标

    English

    人人都能学会的英语2:音标

    方法

    音标与发音是最基础也是最重要的环节,侧重听说的同学,一定要掌握好;侧重读写的人,也不能懈怠,因为这跟后面的单词学习有关系。

    我推荐根据赖世雄的《美语音标》进行学习:

    • 在微信公众号 常春藤英语集团 买相关书籍
    • 在 喜马拉雅 或 B站 听相关音频,进行跟读练习
    • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格

    有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。

    我给出了我之前的学习进度记录,仅供参考
    image.png

    学完之后,如果有兴趣,可以了解下进阶内容。American pronunciation workshop:https://www.bilibili.com/video/BV1qE411v7oEopen in new window
    (我特意找了没有字幕的视频,建议就这样看。如果是侧重读写的同学,直接跳过就好)

    再给出关于音标的一些资料:

    讨论

    这里我补充几点,虽然不是核心,却与主题息息相关:

    1. 是否要买实体书?
    2. 为什么是美语,而不是“英语”
    3. 关于口音(accent)

    建议买实体书

    现在网络这么发达,信息随处可得,所以很可能你不愿意买实体书。

    这样也行,但我仍然建议买书,反正我买了全套,因为我认为从某种意义上讲,实体书有助于提升行动力。还记得前面我们讲到过,整个学习过程强调的是实践,行动力或执行力是很重要的。而购买的动作,本身就是执行力的一种表现。

    而相对的,白嫖的行为背后,隐含着不愿意付出的意味,这种思维本身是不可取的。当然,这也许是我过度解读,你可以继续白嫖。

    不想买实体书的另一个原因是,书多了不方便,比如搬家很麻烦。对这个问题,我是这样处理的,仅供参考:

    • 捐书
    • 送人

    美语是主流

    也许有人认为英音更好听,某些时候,我也赞同这个观点。但我们还是实用主义,现在西方世界谁说话最大声,大家都清楚。毕竟“日不落”已然衰落,现在还是美刀更好使(还在说刀的事,手动滑稽)!所以,美语才是主流。

    一定要学英音的,建议通关后重修发音,或现在就另找门派学习。

    不要纠结口音

    在国内的网络平台上,很常见的场景是一群人对某个中国人的口音评头论足,说什么“这个不正宗、不地道啊”,“你这个也不像美音呀”、”工地散装英语哈哈哈“。

    我都不知道这些人在纠结什么。谁说话没口音呢?你作为中国人,能跟新闻联播主持人一样说普通话吗?你不也带点地方特色?就算是美国人,不同的地区,也有不同的口音,你说谁才是正宗的呢?

    语言最重要的功能是沟通,能让别人听懂是首要需求——如果你的口音并不影响别人听懂并理解你,那就不用纠结它。这也是前面为什么我建议下载翻译软件的原因,让机器来判断你发音是否正确,免去担心自己口音的困扰。

    总结一下,今天分享了音标学习的方法与经验,后面将分享单词的学习。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/everyone-can-learn-english-3-words.html b/english/everyone-can-learn-english-3-words.html index 4fd7b2ed..feb7ebb3 100644 --- a/english/everyone-can-learn-english-3-words.html +++ b/english/everyone-can-learn-english-3-words.html @@ -5,7 +5,7 @@ - 人人都能学会的英语3:单词 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    人人都能学会的英语3:单词

    English

    人人都能学会的英语3:单词

    方法

    单词是听说读写的基础,是有必要花时间去学习的。

    然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

    1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
    2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
    3. 只推荐利用词根记忆(当然并非百分百有效)
    4. 学习软件:欧路词典——免费,全平台通用

    以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
    image.png
    注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
    image.png
    就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

    提醒

    必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

    回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

    网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

    再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

    以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

    我之前在某个词汇量评测网站做过评估:http://testyourvocab.com/result?user=13229458open in new window
    image.png

    4360 是网站为我估算的单词数量。

    为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
    image.png
    上图的含义为:

    • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
    • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
    • 学习中 4122,具体又可细分为:
      • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
      • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
      • 不认识:给了例句也看不懂

    从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

    我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

    总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

    记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

    延伸阅读:刷7000个单词的经验总结

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    人人都能学会的英语3:单词

    English

    人人都能学会的英语3:单词

    方法

    单词是听说读写的基础,是有必要花时间去学习的。

    然而,我不太喜欢使用“背单词”这样的描述,因为这会显得是在死记硬背、追求机械的记忆。对于单词的学习,我有以下建议:

    1. 请务必打好音标基础。一个单词如果你不会念、你听不出来,那就没有真正的掌握
    2. 结合上下文理解。一定要把单词放到句子里去学习,而不要只看英文单词选中文含义(或反之),因为英文单词与中文词汇并非一对一的关系,并且这样只是记忆,没有获得理解。
    3. 只推荐利用词根记忆(当然并非百分百有效)
    4. 学习软件:欧路词典——免费,全平台通用

    以手机欧路词典为例,打开后,点击“学习”,选择适合自己的单词本(或先做测试让软件来推荐),进行学习即可。
    image.png
    注意结合个人情况,进行学习设置,例如学习新词数量及、复习数量。
    image.png
    就我个人而言,我的做法是把全部单词刷一遍,过滤掉我完全掌握的,然后再进行新词的复习。

    提醒

    必须要给大家一个提醒,避免进入单词学习的陷阱:那就是忍受不了单词学习进度缓慢、记了又忘的情况,于是追求“超级记忆术”,把单词一股脑装在脑子里。

    回顾前面所说,学习英语强调的是实践,而不是记忆。所以,不论记忆方法有多高明,它只能解决输入,不能解决输出的问题。因此,我们的重点应该放在输入与输出闭环上,而不是追求单方面“多快好省”。

    网络平台上很多鼓吹类似于“一天背六千个单词”的营销内容,就是抓住了人们没有正确理解的情况下贪多求快的心理,一定要警惕。

    再者,单词与阅读理解,并非简单的线性对应关系,即不是单词量学得越多,阅读理解越深。另一方面来说,也不是只有单词量“爆表”,才能进行全英文阅读。

    以我自身为例,我可以阅读原著,但我的词汇量不过四千左右。

    我之前在某个词汇量评测网站做过评估:http://testyourvocab.com/result?user=13229458open in new window
    image.png

    4360 是网站为我估算的单词数量。

    为求精确,我还找了一本考研(包含了高考、四级、六级、考研词汇)的单词本,把上面的所有单词刷了一遍,结果如下:
    image.png
    上图的含义为:

    • 0 个未学习,意思是单词本的所有单词我都遇到了一遍了,并且我都进行了掌握程度判断
    • 已掌握 3844,意是这些单词我一看就能知道是什么意思,并且能做翻译
    • 学习中 4122,具体又可细分为:
      • 认识:一词多义,仅知道部分的中文含义,当其在另一个句子中体现另一种意思时,自己不知道
      • 模糊:原先不知道中文含义,但根据例句能隐约推断出其含义
      • 不认识:给了例句也看不懂

    从上面我对学习中的单词的分类就可以看出,单词这件事,不是非黑即白的。而把单词掌握数量直接与英语能力挂钩,也是不可取的。

    我在学生时代,其实没有买过一本单词本,也没有刻意地去背过单词(例如每天固定背50个),所以上述词汇量,还是挺客观的。就以词汇量而言,可能很多人都比我强——既然单词对我来说不是困扰,那对大家来说,应该也不是。我想这点信心,大家是可以有的。

    总之,我想说的是,单词重要,却也不用刻意强调“背”这件事。把单词与英语能力直接挂钩,追求神奇的背单词的方法,这是很多人学英语的误区。而在这种误区下,就催生了速成的方法论,迎合了浮躁社会下急功近利的心态。但其实,强调实践,讲究输入输出闭环,才是更符合客观规律的学习方式。

    记住:重要的不是你记住了多少单词,而是你能运用多少单词来造句。

    延伸阅读:刷7000个单词的经验总结

    上次编辑于:
    贡献者: levy
    + diff --git a/english/everyone-can-learn-english-4-listening-and-speaking.html b/english/everyone-can-learn-english-4-listening-and-speaking.html index 7549727a..4c782d6a 100644 --- a/english/everyone-can-learn-english-4-listening-and-speaking.html +++ b/english/everyone-can-learn-english-4-listening-and-speaking.html @@ -5,7 +5,7 @@ - 人人都能学会的英语4:听说 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    人人都能学会的英语4:听说

    English

    人人都能学会的英语4:听说

    前言

    听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

    方法

    听说属于综合能力,建议遵循以下学习步骤:

    1. 激发兴趣,建立信心
    2. 明确方向
    3. 脚踏实地,坚持输入并输出

    1.建立信心

    首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

    前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。
    两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。

    记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。

    2.明确方向

    在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?

    当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。

    Daily English 我推荐:https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversationopen in new window
    这个视频如果看不了,可以看我写的教程:https://www.yuque.com/levy/blog/how-to-surfopen in new window

    Business English,我推荐 eslpod 的教材:

    1. Using English at Workopen in new window
    2. English for Business Meetingsopen in new window
    3. Interview Questions Answeredopen in new window

    这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

    以上三本教材我自己买了,有需要的可以私信我发你。

    另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

    3.坚持输入并输出

    有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

    1. 不看课本(字幕),全部语音听完
    2. 看课本(字幕),做笔记,学习单词与短语
    3. 慢速跟读
      1. 看课本(字幕),播一句,暂停,跟读一句
      2. 不看课本(字幕),播一句,暂停,跟读一句
      3. 看课本(字幕),不暂停,课程跟读
    4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
    5. 脱稿表达(此步骤可选)

    在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

    其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

    以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    人人都能学会的英语4:听说

    English

    人人都能学会的英语4:听说

    前言

    听说篇相对独立,与读写篇没有依赖关系,但需要确保前面的基础已打好。

    方法

    听说属于综合能力,建议遵循以下学习步骤:

    1. 激发兴趣,建立信心
    2. 明确方向
    3. 脚踏实地,坚持输入并输出

    1.建立信心

    首先找到有趣的音视频材料,利用生动的内容增加学习的动力。同时应注意材料的难度不应过高,有助于自己建立信心——已经很有自信的同学可跳过此步骤。我推荐以下材料:

    前者是西游记英文动画版,后者是BBC的职场英语动画片,任选其一即可(如果是实用主义,当然选第二个啦)。
    两个材料都是只有英文字幕的,要逐渐习惯这种模式。这就是为什么说学习时要注重心理建设,因为需要走出舒适区。

    记住,上面给的材料,主要是帮助自己建立自信的, 学到什么不重要,重要的是你觉得自己行了,可以进行下一步了。

    2.明确方向

    在进行深入学习前,我们需要明确自己的学习方向,究竟是 Daily English 还是 Business English?

    当然可以全部都学,但那是结果,在过程中总要分个前后。对这个问题的回答,取决于读者自己。

    Daily English 我推荐:https://www.youtube.com/c/EnglishSpeakingCourses/search?query=conversationopen in new window
    这个视频如果看不了,可以看我写的教程:https://www.yuque.com/levy/blog/how-to-surfopen in new window

    Business English,我推荐 eslpod 的教材:

    1. Using English at Workopen in new window
    2. English for Business Meetingsopen in new window
    3. Interview Questions Answeredopen in new window

    这里特别说下,eslpod 的创始人是英语方面的语言学及教育学的博士,可以说没有人比他更懂英语教学(手动滑稽)!跟着他学,我成功地喜欢上了美语,从此以后听加州口音会觉得最舒服。

    以上三本教材我自己买了,有需要的可以私信我发你。

    另外,赖世雄的《高级美语》也极力推荐,它是属于综合性的教材,内容方面起到开阔视野的作用。而我推荐它的原因是,本书北美外教的发音尤为悦耳,朗读课文时简直“说得比唱得还好听”,练习听说的同学一定不要错过。

    3.坚持输入并输出

    有了材料还远远不够,只是在听、光在那里看,结果还是哑巴英语。因此,对于无论什么材料,我建议这样使用:

    1. 不看课本(字幕),全部语音听完
    2. 看课本(字幕),做笔记,学习单词与短语
    3. 慢速跟读
      1. 看课本(字幕),播一句,暂停,跟读一句
      2. 不看课本(字幕),播一句,暂停,跟读一句
      3. 看课本(字幕),不暂停,课程跟读
    4. 独自念出来,并使用手机软件(如微信)录音,听一听自己说的效果,并进行语音翻译,以检测自己的输出正确率
    5. 脱稿表达(此步骤可选)

    在这里分享一个我遇到的“困境”。在进行不看课本的慢速跟读的练习时,播一句跟读一句的形式,让我很容易犯困。但我没有因此就放弃,也没想办法强行提神,而是转而把它当作催眠工具——困了就睡觉。这样过了两周,有一天晚上,我没有犯困,做完了训练。而那一天也是我走出“困境”的日子,后面的日子里,再也没犯困过!

    其实我不止有一次类似的“犯困”经历,都是与学习新东西有关的。我想,这可能是大脑在生理上对学习的抗拒。对于新领域的知识或不同寻常的学习方式,是需要走出舒适区的,而这种“犯困”可能是大脑潜意识在拒绝走出舒适区。对此,应对方法很简单,那就是坚持下去,每天固定时间执行相应的学习动作,直到大脑潜意识“屈服”,转而适应新的东西。

    以上就是今天要分享的关于听说练习的内容,下一期将分享英语读写的学习经验。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/everyone-can-learn-english-5-reading-and-writing.html b/english/everyone-can-learn-english-5-reading-and-writing.html index 58bed434..a33419e9 100644 --- a/english/everyone-can-learn-english-5-reading-and-writing.html +++ b/english/everyone-can-learn-english-5-reading-and-writing.html @@ -5,7 +5,7 @@ - 人人都能学会的英语5:读写 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    人人都能学会的英语5:读写

    English

    人人都能学会的英语5:读写

    前言

    读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

    阅读能力升级之旅

    我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

    1. 看到英文网站,第一反应是点击切换中文版
    2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
    3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
    4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
    5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
    6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

    这是怎么做到的呢?我主要采用了以下方法。

    阅读方法

    建立信心

    如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

    • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
    • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

    第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

    我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

    • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
    • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

    之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
    image.png

    根据蓝思值选书

    有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

    核心思路是评估自己的阅读蓝思值open in new window,再找到适合自己的阅读材料。什么是蓝思值呢?
    image.png
    假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。
    image.png

    综上所述,推荐按以下步骤提升阅读能力:

    1. 评估自己的阅读能力 https://readtheory.org/open in new window image.png
    2. 找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 https://hub.lexile.com/find-a-book/book-resultsopen in new window

    相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。
    lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg

    巧查生词

    阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

    首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

    再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

    因此,对生词,我建议:一页只查一个生词。它的意义是:

    1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
    2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
    3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

    在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

    • 有些形容词或副词不认识,并不影响理解句子的大意
    • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
    • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

    阅读材料推荐

    如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接点击查看

    写作

    阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。

    读书笔记示例:

    indispensable-opposition.png
    indispensable-opposition.png

    这是我购买 eslpod.comopen in new window 教材时发的邮件:
    image.png

    这是我申请 JetBrains 正版授权时发的邮件:
    image.png

    还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。
    这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。

    总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    人人都能学会的英语5:读写

    English

    人人都能学会的英语5:读写

    前言

    读写篇相对独立,与听说篇没有依赖关系,但需要确保前面的基础已打好。

    阅读能力升级之旅

    我先给出自己经历过的全英阅读能力的变化过程,仅供参考:

    1. 看到英文网站,第一反应是点击切换中文版
    2. 一些技术资料、技术文章,往往是英文,没有翻译,只能硬着头皮借助浏览器的翻译或词典进行全英阅读
    3. 适应了阅读英文技术文档、英文技术博客,但看非技术领域的内容,如在 wikipedia 上查某个人物、某部电影,第一反应是切换中文版(此时对英文的阅读只停留在自己的专业领域)
    4. 尝试阅读全英书籍。其实看得明白,但总觉得太慢了、是不是在浪费时间、好想看中文版,需要耐着性子阅读
    5. 阅读量到达一定后,开始对内容挑剔,如认为议论文没有生动的例子不吸引人,或内容充满说教不想看,于是开始学会挑重点,跳过不重要或没兴趣的内容,不再逐字逐句阅读
    6. 阅读英文在感观上跟阅读中文没有重大的区别,也即看到英文内容,不会再想“这是英文,不是中文哦”——翻阅内容,看就完事了

    这是怎么做到的呢?我主要采用了以下方法。

    阅读方法

    建立信心

    如果之前读者并没有全英文的阅读体验,建议先根据兴趣选一本书(或阅读材料)后,在两周内读完它——无论用什么手段,一定要在该时间段内完成。此举似小实大,有两点内涵:

    • 时长再拉长,你很可能读了后面忘前面;时间越长,越拖延,则两相结合,很可能最终放弃了阅读
    • 在较短的时间内读完,能快速建立信心,为后续阅读打下基础

    第一本书或阅读材料很关键,感兴趣是最重要的,当然也可以结合后面提到的蓝思值进行选材。

    我当初使用的是 轻听英语 app,在上面看完了英文文字加语音版的死亡笔记。看完后感受如下:

    • 一开始的阅读速度比音频的播放速度还要慢,如果没有兴趣,很可能就没耐心了
    • 因为是动漫的文字版,除了画外音(内心的独白),几乎全是人物的对话,这些内容是很容易理解的

    之后我信心大涨,用一周时间,把魔兽世界官方小说《Arthas: Rise of the Lich King》原版看完了。
    image.png

    根据蓝思值选书

    有了信心之后,我们就可以科学地、有节奏地培养自己的英文阅读能力了。

    核心思路是评估自己的阅读蓝思值open in new window,再找到适合自己的阅读材料。什么是蓝思值呢?
    image.png
    假设你的阅读水平是 600L,那么蓝思值在 550L~700L 的阅读材料比较适合你。再低就太简单,达不到提升的效果;再高就超出阅读能力太多,很可能读不下去(因为我们在讲全英文阅读,所以我就不翻译了)。
    image.png

    综上所述,推荐按以下步骤提升阅读能力:

    1. 评估自己的阅读能力 https://readtheory.org/open in new window image.png
    2. 找到自己感兴趣的读物,通过网站判定书籍的蓝思值是否合适 https://hub.lexile.com/find-a-book/book-resultsopen in new window

    相关的 app 我推荐: 轻听英语,里面的书都是免费的。当然大家也可以推荐别的 app,直接有标明蓝思值的就最好不过了。
    lADPJw1WS0BSWVnNBQDNBMw_1228_1280.jpg

    巧查生词

    阅读的一个拦路虎是生词。大家的第一反应是认为生词太多,看不懂,很容易放弃。

    首先,我推荐欧路词典。如果是在电脑上看 pdf,鼠标选中区域就能查词或翻译,非常方便。

    再者,表达一个观点:有些单词不认识,甚至有些句子无法理解,并不影响对情节的核心内容的理解。事实上,除去欧亨利式结尾的短篇小说,书的内容越多,其核心内容越不容易受生词影响。因为主题是贯穿全文的,前面因为生词没看懂,后文换了个说法,你就能看得懂了!

    因此,对生词,我建议:一页只查一个生词。它的意义是:

    1. 你不会因为频繁查生词而影响阅读体验。事实上,试图把每一个生词都查一遍,是阅读不下去的主要原因!
    2. 因为查词机会的“稀缺性”,你把机会留给你认为最有价值的生词
    3. 强迫你的大脑思考,如何在带着许多“迷雾”的情况下,去理解句子、段落,抓住故事的核心情节,理解书籍的中心思想

    在阅读《Arthas: Rise of the Lich King》的时候,我的体验就是:

    • 有些形容词或副词不认识,并不影响理解句子的大意
    • 阅读几章后,为加快阅读速度,只出现一次的生词,我不再查字典——我只查出现过至少两次的单词
    • 再看到后面,有些单词不认识,我都懒得查了,反正不影响我对整体剧情的理解

    阅读材料推荐

    如果实在找不到全英阅读材料,我推荐看《现代大学英语精读(第2版)》, 我已经挑选了值得一读的文章,英语高考有120分或过了四级的人,应该可以直接点击查看

    写作

    阅读是输入,写作是输出,二者是相辅相成的。 对于写的能力,推荐从读书笔记开始做练习 (就像看完中文书那样做笔记),也可以找机会与人发邮件。

    读书笔记示例:

    indispensable-opposition.png
    indispensable-opposition.png

    这是我购买 eslpod.comopen in new window 教材时发的邮件:
    image.png

    这是我申请 JetBrains 正版授权时发的邮件:
    image.png

    还有一种方法, 那就是利用软件的翻译功能,把自己写的中文笔记,翻译成英文,再去校对、修改。很多文本类 Web 应用都有此功能,可以利用起来。
    这种方法本质就是,自己一开始不知道英文文章写什么,那就从校对、修改开始做起;自己不能写完整的英文文章,那就从摘取段落开始做起。适应这个过程后,写作的障碍就减少了。

    总结一下,英文读写的提升需要踏出舒适区,要耐着性子,不要怕慢。坚持去做,量变终会有质变!

    上次编辑于:
    贡献者: levy
    + diff --git a/english/how-to-self-evaluate-english-level.html b/english/how-to-self-evaluate-english-level.html index 2f73681f..9fbf4a7e 100644 --- a/english/how-to-self-evaluate-english-level.html +++ b/english/how-to-self-evaluate-english-level.html @@ -5,7 +5,7 @@ - 英文能力评测手把手教学 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    英文能力评测手把手教学

    English

    英文能力评测手把手教学

    前言

    之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

    单词量

    进入网站:https://preply.com/en/learn/english/test-your-vocabopen in new window

    看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
    image.png
    在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
    image.png
    对于该结果,网站有以下值得注意的解释:

    1. 结果偏差在正负10%左右:

    Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

    1. 英语非母语者,用单词量作为能力标准通常划分如下:
    image.png
    image.png
    1. 结果一年测一次最有效
    image.png
    image.png

    阅读能力

    评估阅读能力也就是评估蓝思值。进入网站:https://readtheory.org/open in new window

    右上角点击注册:
    image.png

    选择作为学生:
    image.png

    可以直接使用谷歌账号注册:
    image.png
    如果不能谷歌,可以看我写的谷歌教程:https://www.yuque.com/levy/blog/how-to-surfopen in new window
    或者自己填表单,进入下一步。

    确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
    image.png

    开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
    image.png

    做完后,点击确认:
    image.png

    点击下图红框处:
    image.png

    往下翻阅,即可看到自己的蓝思值初步评分:
    image.png

    再给出通用的蓝思值能力对照表:
    image.png
    以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    英文能力评测手把手教学

    English

    英文能力评测手把手教学

    前言

    之前有提到过两个英文能力评测的网站,分别用于评估蓝思值与单词量。实践中我发现有些细节可能会被大家忽略,故写下此文,以作手把手教学之用。

    单词量

    进入网站:https://preply.com/en/learn/english/test-your-vocabopen in new window

    看到如图所示区域,开始勾选单词,规则很简单:一眼看上去认识的就勾选,不认识、犹豫的都跳过,之后点击红框处继续。注意,千万不要勾选的过程中去查单词。
    image.png
    在下一页,重复上面的步骤,之后点击 Continue,等待几秒钟,就会出现评估的单词量。
    image.png
    对于该结果,网站有以下值得注意的解释:

    1. 结果偏差在正负10%左右:

    Your vocabulary count has a margin of error of approximately ±10%. We also round results above 10,000 to the nearest 100, and results above 300 to the nearest 10.

    1. 英语非母语者,用单词量作为能力标准通常划分如下:
    image.png
    image.png
    1. 结果一年测一次最有效
    image.png
    image.png

    阅读能力

    评估阅读能力也就是评估蓝思值。进入网站:https://readtheory.org/open in new window

    右上角点击注册:
    image.png

    选择作为学生:
    image.png

    可以直接使用谷歌账号注册:
    image.png
    如果不能谷歌,可以看我写的谷歌教程:https://www.yuque.com/levy/blog/how-to-surfopen in new window
    或者自己填表单,进入下一步。

    确保自己至少有20分钟的空闲时间,之后点击已经准备好了:
    image.png

    开始做题,一共8道,看不懂就乱选,后面的题目会根据你的答题情况自动调整:
    image.png

    做完后,点击确认:
    image.png

    点击下图红框处:
    image.png

    往下翻阅,即可看到自己的蓝思值初步评分:
    image.png

    再给出通用的蓝思值能力对照表:
    image.png
    以上就是今天的英文能力评测手把手教学啦,希望对大家有所帮助。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/index.html b/english/index.html index ca7d38f5..f77e142d 100644 --- a/english/index.html +++ b/english/index.html @@ -34,10 +34,10 @@ } - + - + diff --git a/english/learning-7000-words-task-completed.html b/english/learning-7000-words-task-completed.html index b585ca33..d59c0414 100644 --- a/english/learning-7000-words-task-completed.html +++ b/english/learning-7000-words-task-completed.html @@ -5,7 +5,7 @@ - 完成刷7k单词任务 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    完成刷7k单词任务

    English

    完成刷7k单词任务

    image.png
    这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

    首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

    再者,说明下为什么要做这件事。主要原因有三:

    1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
    2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
    3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

    最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

    很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

    为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

    对单词的掌握程度区分优先级:

    • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
    • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
    • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
    • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

    对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

    • 结合相应的例句,一次只记一个意思
    • 常见的词意,优先记住
    • 冷门的词意,可以跳过
    • 另外,有些中文词意解释很牵强,可以找英英释义

    对词汇进行分类掌握:识别出单词属于哪个领域的

    • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
    • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

    当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

    还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

    最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    完成刷7k单词任务

    English

    完成刷7k单词任务

    image.png
    这周算是完成了今年定下的刷单词的任务,写篇文章总结下,为这件事划上一个句号,也给有需要的人一定的参考价值。

    首先,解释下为什么完成度不是100%,我也称之为完成任务。因为软件出问题了(又或是我使用方式问题),剩下的单词无法进行学习了,进度条止步于此。我也不想纠结于这一点,就这样吧,算是残缺美。

    再者,说明下为什么要做这件事。主要原因有三:

    1. 主动扩充词汇量,与英文阅读相辅相成。诚然,可以一边阅读一边积累词汇,但这种方式我认为是“被动”的,并且有看我之前英文阅读经验分享的朋友,会知道我阅读时查词的频率很低的,如此增加词汇量的效率太慢。另外,词汇又有高频与低频、重点与非重点之分,而这是单词本的强项,因此利用单词本是接触“精华词汇”的高效方式。
    2. 我想知道自己确切的词汇量。虽然在之前分享的在线工具中,我评测过自己的词汇量(7000),但自己感觉有点“虚”,一方面误差有10%,另一方面我觉得波动太大了(两年前测是4000),不太相信,于是打算用刷单词本的“笨方法”去确认自己的词汇量。
    3. 我想体验一下刷单词本的经历。这点我在之前的文章中也透露过,我在学生时代是没有买过单词本、也没有拿着单词本去背诵过,基本都是靠音标+词根,边学课文时边记忆,讲究的是一个自然,没有刻意。然而,这隐约有种“不踏实”之感,没有经过刻意练习,似乎得来的单词“不会珍惜”,于是便趁这个机会把这个经历补上。

    最后,我想强调的是,注意我“刷单词”,而不是“背单词”这种描述上的差别。“刷”重在强调,过一遍,有个印象、能认识;“背”强调的是死记硬背,机械地记忆——虽然我认为练习需要刻意,但却不认为它等同于死记。

    很多人在学习单词觉得很痛苦,半途而废,很大的原因是强调背,把内容强塞进脑子里很让人痛苦;而背了又忘,更是让人感到强烈的挫败感。在这里,我想分享的经验是,此时需要进行思维的转换:你允许自己忘记某些单词。可以把新单词想像成陌生人,总有人能与你成为好友,总有人不能给你留下深刻印象,总有人与你相处不愉快——这么多单词,你先认识容易认识的,很难“相处”、容易忘记的单词,后面再说呗,没必要一口气吃成胖子。

    为了减少痛苦,我有以下经验,它们是属于启发式的(heuristic),仅供参考。每个人应总结符合自己的经验,其核心思想是分层次、分类,不要一刀切。

    对单词的掌握程度区分优先级:

    • 读:阅读时能看懂是什么意思;就算第一眼看不出单词意思,结合上下文能推测出意思才行
    • 听:别人正常语速、慢速说时,自己能听出这是哪个单词
    • 说:我不对此有强制要求,看着音标能发音即可,因为当前我的英语实践场景中,并不侧重说
    • 写:此优先级最低,因为写作不是考试,我完全可以查词典,因此有些词很难记我就索性不记,如官僚主义(bureaucratism)、资产阶级(bourgeois)——我能听、阅读,但放弃掌握写

    对词意进行分类掌握:有的词十几个意思,你哪能一下子全记住?

    • 结合相应的例句,一次只记一个意思
    • 常见的词意,优先记住
    • 冷门的词意,可以跳过
    • 另外,有些中文词意解释很牵强,可以找英英释义

    对词汇进行分类掌握:识别出单词属于哪个领域的

    • 计算机、法律、经济、日常生活、文学等领域的词汇,我放在较高优先级,尽量去掌握
    • 医学领域、非考试重点词汇,我不太重视,能认识就认识,觉得认识上有困难就跳过

    当然,单词不能纯刷,要结合阅读一起来做。我选择的是外国语学院的英语专业教材《现代大学英语精读》,二者结合,相辅相成。

    还有就是,最好固定时间段,以便形成习惯,如上班坐地铁,或晚饭后等较规律的时间段。

    最后,我想说的是,刷了这本单词本,并不意味着我就 master 7,000 English words。它对我而言,更多的是在英语学习经历上,具有里程碑的意义。回顾一下,从2022年2月份开始到,到现在历经大约7个月,我算是把英语单词这件事的“遗憾”给补上了,此事至此告一段落,我可以开始新的旅程了🎉。

    上次编辑于:
    贡献者: levy
    + diff --git a/english/let-chatgpt-be-your-foreign-language-teacher.html b/english/let-chatgpt-be-your-foreign-language-teacher.html index f7348eee..abfc3081 100644 --- a/english/let-chatgpt-be-your-foreign-language-teacher.html +++ b/english/let-chatgpt-be-your-foreign-language-teacher.html @@ -5,7 +5,7 @@ - 让 ChatGPT 成为你的外语私教 | levy @@ -34,13 +34,13 @@ } - +
    跳至主要內容

    让 ChatGPT 成为你的外语私教

    EnglishAI

    让 ChatGPT 成为你的外语私教

    前言

    有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

    本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

    准备工作

    在开始之前,要准备好几样东西:

    1. ChatGPTopen in new window, 如果没有账号或不能上网,请查看上网教程
    2. Chrome 浏览器插件 voice-control-for-chatgptopen in new window
    3. 口语练习题,根据个人需求查找即可,下文将以雅思open in new window为例进行说明

    安装好插件后,打开 chatGPT 界面,下方就会出现语音输入按钮。
    image.png

    常用Prompt

    下面总结了常用的 Prompt,可以有根据需要进行使用或调整。

    设置角色:

    1. Please act as an English teacher.
    2. Please act as an English-speaking test examiner.
    3. Please act as IELTS speaking test examiner.

    进入一问一答模式:

    1. You're supposed to asked me questions and wait for my answer. The next question is: xxx

    对回答进行完善:

    1. Please revise my answer
    2. Please modify my answer to make it more fluent

    对回答进行评分:

    1. Please rate my answer

    进行对话

    第一句话,是设置好 AI 的角色,让它扮演口语考官。

    可以使用以下 prompt:

    act as an English-speaking test examiner
     

    image.png
    可以看出,语音转文字出现错误,单词 IELTS 始终未能正确识别,但 ChatGPT 却能明白其中的意思。

    开启语音插件的意义在于,如果语音识别不了自己说的话,很有可能是自己的发音有问题,起到提醒自己纠正发音的作用。另外,ChatGPT 回复的文字,也会转换成语音输出,顺便练习了听力。

    根据练习材料,让 AI 问自己问题。
    image.png
    记得让 AI 对自己的回答评分,可以使用以下 prompt:

    please rate my answer after I answer the question each time
     
    image.png
    image.png

    进行下一个问题:
    image.png
    上述回答不太好,AI 给出了理由:
    image.png

    修改后再回答,有所进步
    image.png

    再问下一个问题:
    image.png
    这个回答同样不理想,但看了提示也不知道要怎么改:
    image.png

    此时可以新建一个聊天窗口,让 AI 提供示例回答:
    image.png

    根据示例答案,结合关键词,重新组织语言,切换回原聊天窗口,再回答一次:
    image.png
    有所进步!

    有时对话长了,AI 会“糊涂”如下所示:
    image.png

    此时要重新强调它扮演的角色,让其回忆起上下文,可以使用以下 prompt:

    focus on the speaking test and assume that you ask me this question
    -
    image.png
    image.png

    除此之外,就没啥值得注意的了。重复上述过程,不断练习即可。

    记录回答

    做完了练习,还要作笔记。但在记录回答之前,还要润色一下,毕竟口语表达的时候,可能会存在语法错误。

    进入 https://quillbot.com/open in new window,把答案复制上去,先进行语法检查:
    image.png
    再进行流畅度润色:
    image.png
    最后保存到笔记本上即可。

    上次编辑于:
    贡献者: levy
    - +
    image.png
    image.png

    除此之外,就没啥值得注意的了。重复上述过程,不断练习即可。

    记录回答

    做完了练习,还要作笔记。但在记录回答之前,还要润色一下,毕竟口语表达的时候,可能会存在语法错误。

    进入 https://quillbot.com/open in new window,把答案复制上去,先进行语法检查:
    image.png
    再进行流畅度润色:
    image.png
    最后保存到笔记本上即可。

    上次编辑于:
    贡献者: levy
    + diff --git a/frontend/index.html b/frontend/index.html index 72a1c12e..9b1b7bd8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -34,10 +34,10 @@ } - + - - +
    跳至主要內容

    Frontend


    + diff --git a/frontend/old-articles.html b/frontend/old-articles.html index 33fefd76..b13e3975 100644 --- a/frontend/old-articles.html +++ b/frontend/old-articles.html @@ -5,7 +5,7 @@ - 旧文章精选 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    旧文章精选

    Frontend

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    旧文章精选

    Frontend

    上次编辑于:
    贡献者: levy
    + diff --git a/frontend/performance-optimization-in-action.html b/frontend/performance-optimization-in-action.html index 578f1205..fc558969 100644 --- a/frontend/performance-optimization-in-action.html +++ b/frontend/performance-optimization-in-action.html @@ -5,7 +5,7 @@ - 前端项目性能优化实战 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    前端项目性能优化实战

    Frontend

    前端项目性能优化实战

    本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

    检测

    使用两个工具分析项目首页性能情况:

    1. https://developers.google.com/speed/pagespeed/insights/open in new window
    2. https://tools.pingdom.com/open in new window

    得到结果如下:


    可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。

    图片优化

    关于图片,PageSpeed 的优化建议如下:

    根据文章《把图片优化指南做成一个组件:v-imgopen in new window》,找到首页的图片相关的代码:

    1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
    2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

    注意:img-url 应该是 oss 的链接,并且是 https 协议。
    如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

    如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

    则在此环节,一次性做到了上图中的三个优化点。

    提高TTFB时间

    来看下一条优化建议:

    因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

    经过分析,上述代码存在两个问题:

    1. 可在客户端执行却放在了服务端
    2. 可并行执行却写成了串行

    修改如下:


    通过分析请求日志发现,有一个请求应该是在客户端发送,代码本意也是在客户端执行,却在服务端也执行了。
    找到发送请求的代码:

    原来是代码写在了 created 里,这是个经典的案例:为了让请求更早一点发送,不写在 mounted 钩子,而写在 created 里,导致请求分别在 server-side 与 client-side 都执行了。具体说明请看 vue ssr 官方文档open in new window

    修改如下:

    移除未使用的 Javascript

    来看下一条优化建议:

    因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

    此时注意用到以下基本操作:

    1. google/github 查询库的用途
    2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

    延迟静态资源的加载

    来看另一个相关的建议:

    如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

    以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

    const srcs = [
    +    
    跳至主要內容

    前端项目性能优化实战

    Frontend

    前端项目性能优化实战

    本文将分享常用的 Web 页面性能分析工具,以及一个前端项目性能优化的实战经验。

    检测

    使用两个工具分析项目首页性能情况:

    1. https://developers.google.com/speed/pagespeed/insights/open in new window
    2. https://tools.pingdom.com/open in new window

    得到结果如下:


    可以看到,首页超过50%的请求都与图片有空,优化空间比较大,因此第一步应该是优化图片加载。

    图片优化

    关于图片,PageSpeed 的优化建议如下:

    根据文章《把图片优化指南做成一个组件:v-imgopen in new window》,找到首页的图片相关的代码:

    1. <img>  元素修改成 <v-img/> ,注意设置 width 或 height
    2. <div ``style="background-image: url(img-url)"``></div> 修改成 <div v-img="{src: img-url}"></div>

    注意:img-url 应该是 oss 的链接,并且是 https 协议。
    如果是 http 协议,或不指定协议 //img-url,则很可能会出现下图的情况:

    如果图片是放在项目中,且项目并没有部署到 oss,则无法享受自动加载 webp 格式图片的福利。

    则在此环节,一次性做到了上图中的三个优化点。

    提高TTFB时间

    来看下一条优化建议:

    因为项目是服务端渲染的,有些请求是在服务端做了。找到相关代码:

    经过分析,上述代码存在两个问题:

    1. 可在客户端执行却放在了服务端
    2. 可并行执行却写成了串行

    修改如下:


    通过分析请求日志发现,有一个请求应该是在客户端发送,代码本意也是在客户端执行,却在服务端也执行了。
    找到发送请求的代码:

    原来是代码写在了 created 里,这是个经典的案例:为了让请求更早一点发送,不写在 mounted 钩子,而写在 created 里,导致请求分别在 server-side 与 client-side 都执行了。具体说明请看 vue ssr 官方文档open in new window

    修改如下:

    移除未使用的 Javascript

    来看下一条优化建议:

    因为经过多次迭代,有可能某些功能曾经上线过,后来被下线,但当时代码没删干净,所以留下一些现在没用的第三方库。根据建议,找到这些引入的 js,确保不影响正常功能后,删除即可。

    此时注意用到以下基本操作:

    1. google/github 查询库的用途
    2. git history/annotate 查看是何人何时引入、并在何处使用的,如何代码不能表明意图,则最好找到相关人员询问是有何意图。

    延迟静态资源的加载

    来看另一个相关的建议:

    如果有些第三方 js 确实有用到,但却不是关键资源,则可以延迟其加载或解析时机,缩短阻塞时间。

    以一些第三方代码为例,它们并不是关键资源,可以在 window.onload 事件触发后,再加载

    const srcs = [
       // 百度统计
       {
         src: 'https://hm.baidu.com/hm.js',
    @@ -67,7 +67,7 @@
     

    启用文本压缩

    首先是启用文本压缩,采用 gzip 是最简单的方式。对应的请求头是:Content-Encoding

    因为静态资源是放到对象储存服务上的,故应该修改相关的配置。

    这里要注意的是,对于静态资源的访问,最好使用自定义域名,而不是存储桶的域名,方便CDN做加速优化。

    优化缓存策略

    下一条相关的优化建议是缓存策略,对应的请求头是:Cache-Control

    简单来说,就是在静态资源(html 除外)的 HTTP 响应头中设置如下字段:

    Cache-Control: max-age=31536000
     

    以阿里云 oss 为例进行说明,其他静态资源存储如 obs、S3 都是同理。

    对于少量的资源,可以进行手工操作。打开 oss-browseropen in new window,找到相应资源:

    本中使用的 oss-browser 版本,一次只能对一个资源进行 HTTP 头的设置,操作十分不便,可以登录阿里云控制台进行批量操作open in new window

    当然,最根本的解决办法,是使用 阿里云oss命令行工具open in new window 上传的时候就进行设置。

    /user/ossutil64 cp -r -f -u ./dist oss://$bucket/$file_path/ --meta=Cache-Control:max-age=31536000
     /user/ossutil64 set-meta oss://$bucket/$file_path/index.html Cache-Control:no-cache --update
    -

    注意,在命令行里别乱设置 Content-Encoding:gzip,否则会出现下面的情况,页面都打不开,具体说明查看详情open in new window

    上次编辑于:
    贡献者: levy
    - +

    注意,在命令行里别乱设置 Content-Encoding:gzip,否则会出现下面的情况,页面都打不开,具体说明查看详情open in new window

    上次编辑于:
    贡献者: levy
    + diff --git a/git/git-best-pratices.html b/git/git-best-pratices.html index c7d65874..3ed7dbaf 100644 --- a/git/git-best-pratices.html +++ b/git/git-best-pratices.html @@ -5,7 +5,7 @@ - Git最佳实践 | levy @@ -34,13 +34,13 @@ } - + -
    跳至主要內容

    Git最佳实践

    Git

    2020-09-21

    Git最佳实践

    精简提交

    一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
    如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

    频繁提交

    一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
    经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

    此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

    不要提交不完整的改动

    虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

    如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

    提交前测试那些改动

    不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

    版本控制不是备份系统

    版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

    Github实例

    一个功能对应一个分支

    下面是好的示例: 格式化代码,也应该单独一个PR
    下面是不好的示例:因为一个PR修改了不同的主题内容

    提交“瘦”的PR

    参考文章:https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendlyopen in new window
    其中最重要的一点:不要一次提交一个很大改动的PR,否则别人很难 review,要学会拆分步骤。
    下面是一个 PR 示例:
    拆分前,包含了35个改动,很难 review

    下图是拆分后:

    单个PR的改动文件只有11个
    每个 PR 改动的文件少了,这样 review 起来就更容易了。

    使用正确的标题

    相关规范看这里open in new window

    另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

    • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
    • 优先使用正面肯定语句,而不是否定句。

    好的示例:docs: extraQuery 的正确使用方法
    不好的示例:docs: 更新不直观的例子

    根据模板填写PR描述

    这是我们 Github 的 PR 模板,融合了我们的最佳实践
    下面是实际的好的例子

    自动关闭issue

    image.png
    image.png
    git commit -m 'fix #6'
    +    
    跳至主要內容

    Git最佳实践

    Git

    2020-09-21

    Git最佳实践

    精简提交

    一次只提交一个“瘦”的功能,同时只包含相关改动文件。例如,对于两个错误的修复应该进行两次不同的提交。
    如果发现写提交信息时,需要写两点以上;  则可以考虑拆分提交。

    频繁提交

    一次提交应只对应一个“瘦”的功能。从而达到频繁提交的目标。
    经常性地提交改动可以确保不会出现特别庞大的提交,同时也可以比较精准地对应到所需要的改动上。

    此外,通过频繁地提交也可以比较快速地和其他开发人员来共享你的改动。同样也会避免在整合代码时出现过多的合并冲突。相反的,非常庞大的提交会加大整合代码时出现冲突的风险,解决这些冲突也会非常复杂。

    不要提交不完整的改动

    虽然原则上来说不要提交一些还没有完成的改动,但是对于一个非常庞大的新功能来说,也并不意味着你必须整体完成这个功能后才可以提交。恰恰相反,你必须把那些改动正确地分割成一些有意义的逻辑模块来进行频繁地提交。

    如果你仅仅是因为急着想要下班,或者是想要得到一个干净的工作副本(比如想要切换到另一个分支上),你可以利用 Git 所提供的储藏(Stash)功能来解决这些问题。切记不要把那些不完整的改动提交到仓库中。

    提交前测试那些改动

    不要理所当然地认为自己完成的改动都是正确的。所有的改动一定要通过彻底地测试才表示它真正地被完成了。

    版本控制不是备份系统

    版本控制系统具有一个很强大的附带功能,那就是服务器端的备份功能。但是千万不要把 VCS 仅仅当成一个备份系统。特别需要注意的是,只能提交那些有意义的改动。

    Github实例

    一个功能对应一个分支

    下面是好的示例: 格式化代码,也应该单独一个PR
    下面是不好的示例:因为一个PR修改了不同的主题内容

    提交“瘦”的PR

    参考文章:https://deliveroo.engineering/2017/09/06/play-pull-request-roulette.html#ideas-to-make-your-prs-more-review-friendlyopen in new window
    其中最重要的一点:不要一次提交一个很大改动的PR,否则别人很难 review,要学会拆分步骤。
    下面是一个 PR 示例:
    拆分前,包含了35个改动,很难 review

    下图是拆分后:

    单个PR的改动文件只有11个
    每个 PR 改动的文件少了,这样 review 起来就更容易了。

    使用正确的标题

    相关规范看这里open in new window

    另外,请回答:出于什么原因需要进行这次修改?具体改动了些什么?

    • 使用一定要使用现在时祈使句(例如要使用 change ,而不是 changed 或 changes)。
    • 优先使用正面肯定语句,而不是否定句。

    好的示例:docs: extraQuery 的正确使用方法
    不好的示例:docs: 更新不直观的例子

    根据模板填写PR描述

    这是我们 Github 的 PR 模板,融合了我们的最佳实践
    下面是实际的好的例子

    自动关闭issue

    image.png
    image.png
    git commit -m 'fix #6'
     # 或
     git commit -m 'close #6'
    -

    当pr合并时,将自动close issue

    1+2 review 规则

    1 是指发起 PR 的人,2 是指进行 code review 的人。也即,每一个 PR,至少要经过两个团队成员 approve 才能合并。

    上面是针对 github 的协作,项目组中可酌情变为 1+1 规则


    礼貌提问

    在 github 向人提问时,需要有礼貌。当提出 feature request时,还要说明自己的情况,尽可能提供更多的信息给对方。

    上面的示例有三个重点:

    1. 开头表达感谢
    2. 中间说明己方的使用情况,并给出相应链接
    3. 最后参考业界已有实现,给出一个方案设想,并给出相应链接

    学习资源

    上次编辑于:
    贡献者: levy
    - +

    当pr合并时,将自动close issue

    1+2 review 规则

    1 是指发起 PR 的人,2 是指进行 code review 的人。也即,每一个 PR,至少要经过两个团队成员 approve 才能合并。

    上面是针对 github 的协作,项目组中可酌情变为 1+1 规则


    礼貌提问

    在 github 向人提问时,需要有礼貌。当提出 feature request时,还要说明自己的情况,尽可能提供更多的信息给对方。

    上面的示例有三个重点:

    1. 开头表达感谢
    2. 中间说明己方的使用情况,并给出相应链接
    3. 最后参考业界已有实现,给出一个方案设想,并给出相应链接

    学习资源

    上次编辑于:
    贡献者: levy
    + diff --git a/git/git-definitive-guide-to-merge-code.html b/git/git-definitive-guide-to-merge-code.html index 2dab9f35..9c100352 100644 --- a/git/git-definitive-guide-to-merge-code.html +++ b/git/git-definitive-guide-to-merge-code.html @@ -5,7 +5,7 @@ - Git代码合并指南 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Git代码合并指南

    Git

    Git代码合并指南

    前言

    合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
    在说明问题前,先定义一些概念:

    • feat:指代功能分支
    • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
      • 受保护,不能直接推送
      • 不会被删除
      • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • MR:merge request。代码合并请求

    以及说明本文解决冲突涉及到的工具及平台:

    • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
    • 使用 GitLab 托管代码

    功能分支合并长驻分支冲突

    这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
    下面先给出解决思路,再给出图文操作步骤。

    解决思路

    1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
    2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
    3. 推送 conflict/resolved 分支
    4. 提交 MR:conflict/resolved -> dev

    操作步骤

    1. 本地切换到 dev 分支,更新代码
    2. 合并相应的 feat 分支
    1. 弹出冲突提示,点击合并
    1. 首先处理无冲突的代码,点击下图红框处
    1. 再根据情况,选择合并代码或丢弃代码。
    1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
    1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

    功能分支被污染

    分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
    这种场景的出现可能有多种原因:

    1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
    2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

    解决思路

    1. 基于目标分支如 (test 分支)切一个干净的分支 clean
    2. 使用 cherry-pick,挑选自己想要的提交
    3. 再提交MR(clean -> test)

    注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

    操作步骤

    1. 更新目标分支(在这里是 test)
    1. 基于 test 切新分支,这里示例命名为:clean
    1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
    2. 右键,点击 Cherry-Pick
    3. 则相应的提交记录就会合并到 clean 分支
    4. 推送 clean,提交MR(clean -> test)

    挑选别的分支部分代码合并

    有可能会出现这样一种场景:

    • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
    • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
    • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

    此时该如何是好?

    解决思路

    其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
    feat 分支就算被删了,只要提交记录还在,那也没关系:

    • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
    • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

    注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

    操作步骤

    此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    Git代码合并指南

    Git

    Git代码合并指南

    前言

    合并时代码常见问题是冲突、提交错代码以及合并错分支,本文将说明这些问题的解决方案,为代码合并打下坚实的基础,以应对未来可能出现的分支模型多样化、协作流程复杂化的场景。
    在说明问题前,先定义一些概念:

    • feat:指代功能分支
    • dev 与 test:指代两条不同的长驻分支,它们具有以下特点:
      • 受保护,不能直接推送
      • 不会被删除
      • 二者之间不直接合并,也即合并方式一般是 feat -> dev,feat -> test
    • MR:merge request。代码合并请求

    以及说明本文解决冲突涉及到的工具及平台:

    • 使用 IDEA 解决冲突(JetBrains系列的工具都适用)
    • 使用 GitLab 托管代码

    功能分支合并长驻分支冲突

    这是最常见的场景:feat1 与 feat2 并行开发,当提交MR( feat1 -> dev )时,发现冲突了,无法合并。
    下面先给出解决思路,再给出图文操作步骤。

    解决思路

    1. 因为合并的方向是 feat -> dev,所以解决冲突应该是在本地 dev 合并 feat
    2. 又因为本地 dev 不能向远程推送,因而需要基于 dev 切一个新分支 conflict/resolved
    3. 推送 conflict/resolved 分支
    4. 提交 MR:conflict/resolved -> dev

    操作步骤

    1. 本地切换到 dev 分支,更新代码
    2. 合并相应的 feat 分支
    1. 弹出冲突提示,点击合并
    1. 首先处理无冲突的代码,点击下图红框处
    1. 再根据情况,选择合并代码或丢弃代码。
    1. 在 dev 分支上切出新分支,推荐命名为 conflict/xxx
    1. 推送代码,提交MR(conflict -> dev),记得勾选合并后删除

    功能分支被污染

    分支一多,人难免失误,很可能造成 feat 分支被污染,即当提MR(feat -> test)时,出现不想合并到 test的代码或提交记录。
    这种场景的出现可能有多种原因:

    1. 研发过程中出现误操作,如出现了 dev -> feat 的合并
    2. feat 分支的基线分支搞错了,如从 dev 切出了 feat

    解决思路

    1. 基于目标分支如 (test 分支)切一个干净的分支 clean
    2. 使用 cherry-pick,挑选自己想要的提交
    3. 再提交MR(clean -> test)

    注意的是,要按提交顺序进行 cherry-pick,以避免遗漏或出错。

    操作步骤

    1. 更新目标分支(在这里是 test)
    1. 基于 test 切新分支,这里示例命名为:clean
    1. 在拥有最新代码的分支(这里是 feat) 找到并选中相应的提交记录
    2. 右键,点击 Cherry-Pick
    3. 则相应的提交记录就会合并到 clean 分支
    4. 推送 clean,提交MR(clean -> test)

    挑选别的分支部分代码合并

    有可能会出现这样一种场景:

    • 最新的生产代码里,假设版本为v1.3.0,包含了 feat 分支的代码
    • 为了减少分支的冗余,代码一旦上生产后,就会清除相应的功能分支,也即此时仓库里没有 feat 分支了
    • 客户方部署的版本代码为 v1.1.0,而客户不想升级到最新的版本,只想要 feat 分支相应的功能

    此时该如何是好?

    解决思路

    其实只要触发“挑选”关键字,就可以考虑使用 cherry-pick。
    feat 分支就算被删了,只要提交记录还在,那也没关系:

    • 在v1.3.0 的代码库中,按分支筛选,找出 feat 分支对应的提交记录
    • 通过 cherry-pick 把 feat 分支的代码合并到客户方的代码分支即可

    注意:毕竟跨越了版本,无法保证合并过去后的代码一定能正确工作,需要进行充分地测试。

    操作步骤

    此操作本质还是 cherry-pick,参考前面 cherry-pick 的示例即可。

    上次编辑于:
    贡献者: levy
    + diff --git a/git/git-history-two-tricks-in-idea.html b/git/git-history-two-tricks-in-idea.html index 2c028682..d38eb1c3 100644 --- a/git/git-history-two-tricks-in-idea.html +++ b/git/git-history-two-tricks-in-idea.html @@ -5,7 +5,7 @@ - Git查看历史记录小技巧 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Git查看历史记录小技巧

    Git

    Git查看历史记录小技巧

    分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

    这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

    第一个是叫 annotate with git blame
    在IDEA的行号这个位置,右键,再点击即可。如图所示:

    效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

    在合并冲突的时候也可以用这个技巧。

    对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

    这样就能提供更多的信息帮助解决冲突。

    就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

    当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

    之所以这样,可能会有以下的原因:

    1. 代码格式化
    2. 移动代码,比如说拷贝代码、迁移代码
    3. 合并代码,解决冲突

    上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

    这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

    这就引出了第二个小技巧了: git show history

    点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

    就会出现如图所示的这样的一个 git log的 界面。

    那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

    这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    Git查看历史记录小技巧

    Git

    Git查看历史记录小技巧

    分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

    这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。

    第一个是叫 annotate with git blame
    在IDEA的行号这个位置,右键,再点击即可。如图所示:

    效果就是,每一行代码都会显示,该行代码是由谁提交的、 什么时候提交的。

    在合并冲突的时候也可以用这个技巧。

    对左右两边进行git blame一下, 然后就可以看到如图所示的情况:

    这样就能提供更多的信息帮助解决冲突。

    就算冲突无法自己解决,也至少能知道提交代码的是谁,可以找到作者去进行沟通。

    当然git blame 是有一些注意点的。因为它本质上显示的是某一行代码的最后提交人, 也就是last modified的一个概念,而有些时候这并不意味着最后的修改人就是代码的原作者。

    之所以这样,可能会有以下的原因:

    1. 代码格式化
    2. 移动代码,比如说拷贝代码、迁移代码
    3. 合并代码,解决冲突

    上述操作都会改变最后修改人的这个属性,但此时显然最后修改人并非原作者。

    这说明,有时候仅知道了某一行代码是由谁最近修改的还不够,还需要知道某一个文件经过了怎么样的修改。

    这就引出了第二个小技巧了: git show history

    点击IDEA某个文件的空白处,然后右键,选择git,然后点击 show history。

    就会出现如图所示的这样的一个 git log的 界面。

    那么这样就可以看到这个文件从最初到至今经历过了怎样的修改、 有过哪些人在上面修改, 从而更好的进行记录追踪。

    这两个技巧, 其实是越有经验就对你帮助越大的。因为你,年限越长,你看别人的代码的机会就越; 而如果你年限尚浅的话,更多的是你的代码被别人 review。

    上次编辑于:
    贡献者: levy
    + diff --git a/git/git-useful-commands.html b/git/git-useful-commands.html index a142ece5..674f315d 100644 --- a/git/git-useful-commands.html +++ b/git/git-useful-commands.html @@ -5,7 +5,7 @@ - Git常用命令 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Git常用命令

    Git

    Git常用命令

    前言

    本文将列举Git常见场景,并给出相应解决方案。

    约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

    推荐: 图形化交互式Git教程open in new window

    配置

    Mac/Linux 用户 执行以下操作

    vi ~/.gitconfig
    +    
    跳至主要內容

    Git常用命令

    Git

    Git常用命令

    前言

    本文将列举Git常见场景,并给出相应解决方案。

    约定: 下文代码块中${}里面表示的是变量,具体值视情况而定,其余的都是正确可执行的命令。

    推荐: 图形化交互式Git教程open in new window

    配置

    Mac/Linux 用户 执行以下操作

    vi ~/.gitconfig
     

    Windows用户在桌面用户文件夹下有个.gitconfig隐藏文件,直接修改即可

    补充以下内容

    [alias]
       st = status
       cm = commit
    @@ -137,7 +137,7 @@
     

    根据文件搜索历史

    git log --all --full-history -- package-lock.json
     

    从所有提交中删除一个文件

    git filter-branch --tree-filter "rm -rf package-lock.json" --prune-empty -- --all
     

    如果代码已经推送到了远程仓库,还需要强制推送

    git push -f
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/git/gitlab-ci.html b/git/gitlab-ci.html index 8c47c152..53434e70 100644 --- a/git/gitlab-ci.html +++ b/git/gitlab-ci.html @@ -5,7 +5,7 @@ - GitLab CI | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    GitLab CI

    GitGitLabJavaNode.js

    GitLab CI

    前言

    GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

    安装与配置

    GitLab Runner 安装

    进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

    这里推荐使用 docker 的方式安装,复制以下命令执行即可:

    docker run -d --name gitlab-runner --restart always \
    +    
    跳至主要內容

    GitLab CI

    GitGitLabJavaNode.js

    GitLab CI

    前言

    GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。

    安装与配置

    GitLab Runner 安装

    进行 Gitlab CI 的第一步是要安装 GitLab Runner。如果公司、团队内部已安装过,可以跳过这一步。

    这里推荐使用 docker 的方式安装,复制以下命令执行即可:

    docker run -d --name gitlab-runner --restart always \
       -v /var/run/docker.sock:/var/run/docker.sock \
       -v /srv/gitlab-runner/config:/etc/gitlab-runner \
       gitlab/gitlab-runner:latest
    @@ -146,7 +146,7 @@
     sudo systemctl start docker
     sudo systemctl status docker
     

    本地成功,流水线失败

    如果流水线编译报错,本地编译通过,不用怀疑,一定是本地的问题。

    本地之所以能编译通过,是因为有缓存。如果 pom.xml 没有设置 <updatePolicy>always</updatePolicy>,编译时很可能使用的是缓存。

    清除缓存拉取最新的包即可。

    mvn -U clean install
    -

    参考文档

    上次编辑于:
    贡献者: levy
    - +

    参考文档

    上次编辑于:
    贡献者: levy
    + diff --git a/git/index.html b/git/index.html index 72eddc39..af627d69 100644 --- a/git/index.html +++ b/git/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Git


    - +
    跳至主要內容

    Git


    + diff --git a/git/rethinking-git-flow.html b/git/rethinking-git-flow.html index 04418551..2c8132af 100644 --- a/git/rethinking-git-flow.html +++ b/git/rethinking-git-flow.html @@ -5,7 +5,7 @@ - 再论Git Flow | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    再论Git Flow

    Git

    再论Git Flow

    背景

    团队目前使用的 Git 协作模式是:

    1. 对每个功能建立相应的 feat 分支
    2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
    3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

    这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

    1. 某 feat 合并至 dev 后,并不想合并至 test
    2. 某 feat 合并至 test 后,并不想合并至 uat

    本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

    动机

    为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

    1. feat -> dev,解决冲突
    2. feat -> test,又要解决冲突
    3. feat -> uat,还要解决冲突

    正如此文章open in new window所说,“把时间浪费在解决不必要的冲突上”。

    再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

    理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

    为此,本文思考是否存在另一种分支协作的方式。

    分析

    首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

    1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
    2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
    3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

    剔除代码

    先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

    想“剔除某 feat 分支的代码”,可操作方式如下,更多请参考此文章open in new window

    1. git rebase
    2. git cherry-pick

    git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

    git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

    再次提交

    真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

    觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

    比较优劣

    要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

    还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

    当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

    cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

    再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

    也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

    至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

    实例

    下面举例说明,如何应用上述分析结果。

    分支模型

    长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

    一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

    • 按版本,如: release/v2.15
    • 按上线日期,如:release/04-26

    功能提交

    每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

    每次需要集成发布时,正常的分支合并操作如下:

    1. feat -> dev
    2. feat -> release
    3. release -> test
    4. release -> uat

    则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

    为什么一个 feat 要合并两次?

    因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

    并且这样也能适应不同的功能分批提测的研发节奏。

    功能回撤

    当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

    则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

    1. checkout rollback 分支
    2. 进行回滚提交,或 revert,或屏蔽功能入口
    3. 请求合并至 UAT(不合并至 release/分支):

    后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

    结论

    通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

    这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    再论Git Flow

    Git

    再论Git Flow

    背景

    团队目前使用的 Git 协作模式是:

    1. 对每个功能建立相应的 feat 分支
    2. 上研发、测试、UAT环境时,分别把相应的 feat 分支合并进入长驻 dev/test/uat
    3. 如有冲突,则在本地更新长驻分支 dev/test/uat,merge feat into current branch,之后 checkout 一个新分支,作为 conflict resolved 分支,推送并合并至远程长驻分支

    这个模式简单好懂,且业界流行,最直观的好处是,可以满足以下需求:

    1. 某 feat 合并至 dev 后,并不想合并至 test
    2. 某 feat 合并至 test 后,并不想合并至 uat

    本文暂且不讨论该交付理念的优劣,毕竟每个团队研发情况、交付理念都不一样。 本文关注的是,在满足上述需求的情况下,是否有更好的分支协作方式。

    动机

    为什么要寻求更好的方式?因为上述分支协作模式,会导致代码冲突的噩梦:

    1. feat -> dev,解决冲突
    2. feat -> test,又要解决冲突
    3. feat -> uat,还要解决冲突

    正如此文章open in new window所说,“把时间浪费在解决不必要的冲突上”。

    再者,功能已经通过测试了,准备上UAT环境时,居然还要解决一大堆曾经解决过的冲突,实在不想接受这种“惊喜”(或者说“惊吓”更合适)——合错代码了怎么办?并且,这么多分支,遗漏了怎么办?这些问题可以解决,但难免有为了解决一个问题,引入更多问题之嫌。

    理想中的研发流程是,测试通过后,上 UAT 的体验是平滑的,是不用担心出错的。

    为此,本文思考是否存在另一种分支协作的方式。

    分析

    首先分析一下,“某功能测试通过但不上 UAT”的可操作方法有哪些:

    1. 要上 UAT 的 feat 分支逐个依次合并至长驻分支,也即当前的做法
    2. 在原计划要上的功能的代码集合中,剔除掉相应 feat 的代码,再上 UAT
    3. 相应分支再次提交代码,或提交 revert commit,或屏蔽相应的功能及入口,变相达到目的

    剔除代码

    先来看第2种方法。filter by branch,这是最先想到且符合直觉的方式,可惜实际上 Git 并没有此功能。

    想“剔除某 feat 分支的代码”,可操作方式如下,更多请参考此文章open in new window

    1. git rebase
    2. git cherry-pick

    git rebase要求 commit 是连续的,这对于实际不可行,因为集成分支里各个 feat 分支的提交记录掺杂在一起。

    git cherry-pick是可行的。不过其思路是挑捡想要的 commit,放到目标分支,本质上并不是剔除的逻辑。

    再次提交

    真正的剔除逻辑,存在于第3种方法中。提交一个 revert commit,就可以把之前的代码干掉了(如果想恢复代码,需要 revert "revert commit")。

    觉得 revert 可能会对后续恢复代码造成困扰的话,也可以再提交代码,屏蔽相应功能及入口。这种方式适合于功能入口少,功能本身具有类似开关特性的场景。

    比较优劣

    要比较上述方案优劣,本文倾向于使用功利主义的最佳实践作为指导思想——认为痛苦存在更多共同点,因而为避免消极而努力。换言之,本文关注的是,哪种方案最令人痛苦,则优先淘汰它。

    还有一个指导思想:麻烦、辛苦的事情放前面;前面可以多做,后面期望少做。

    当前的方式,存在最难受的问题:解决过的冲突,需要重复地解决。涉及范围:全部分支。涉及人员:所有参与研发的人员,即使他们在别的 feat 分支提交代码。

    cherry-pick 依然存在要重复解决冲突的问题,且涉及范围同样为全部分支,但涉及人员减少为单人,因为只需要一个做 cherry-pick 的工作,由其解决 cherry-pick 遇到的冲突(当然,很可能需要他人协助)。

    再次提交,冲突的可能性将大大减少,涉及范围:相应的 feat 分支。涉及人员:相应的 feat 分支研发人员。

    也即使用再次提交的方案,痛苦将降低至最小。这也是符合直觉的:谁出问题,谁负责。某功能不上线了,这也算是“问题”的一种,则相应的负责人去处理,尽可能不影响到其他人。

    至于再次提交是使用 revert 还是屏蔽功能及入口,则具体情况具体分析。

    实例

    下面举例说明,如何应用上述分析结果。

    分支模型

    长驻分支: dev/test/uat,分别对应环境:研发/测试/UAT

    一个月一次的迭代开始时,都建立相应的  release 分支,命名规则可以:

    • 按版本,如: release/v2.15
    • 按上线日期,如:release/04-26

    功能提交

    每个研发人员根据相应功能,从 uat checkout 相应的 feat 分支。

    每次需要集成发布时,正常的分支合并操作如下:

    1. feat -> dev
    2. feat -> release
    3. release -> test
    4. release -> uat

    则冲突大多数情况只发生在第前两步,解决之后,后续上测试环境、上 UAT 环境,基本无需担心冲突。

    为什么一个 feat 要合并两次?

    因为要保证 release 的功能是较为完整的, 至少经过开发人员在 dev 环境的自测。

    并且这样也能适应不同的功能分批提测的研发节奏。

    功能回撤

    当 release 合并至 test 分支后,得到通知,某功能(分支涉及 feat/unwanted)不上 UAT。

    则此时,feat/unwanted 相应的研发人员,为了进行功能回滚,操作如下:

    1. checkout rollback 分支
    2. 进行回滚提交,或 revert,或屏蔽功能入口
    3. 请求合并至 UAT(不合并至 release/分支):

    后续要恢复功能,在 rollback 分支操作,再合并至 UAT 即可。

    结论

    通过分析与比较,本文推荐使用“再次提交”的方式,来满足某 feat 分支合进 test,不合进 uat 分支的需求。

    这样做,将改动涉及范围减至最小,涉及人员降为单人,大大减少上 UAT 时合并代码的痛苦,达到平稳上线的目的。

    上次编辑于:
    贡献者: levy
    + diff --git a/git/use-command-line-tool-to-manage-gitlab-merge-request.html b/git/use-command-line-tool-to-manage-gitlab-merge-request.html index c530eeed..52ab4f3c 100644 --- a/git/use-command-line-tool-to-manage-gitlab-merge-request.html +++ b/git/use-command-line-tool-to-manage-gitlab-merge-request.html @@ -5,7 +5,7 @@ - 操作 Gitlab MR 的命令行工具 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    操作 Gitlab MR 的命令行工具

    GitGitLabPython

    操作 Gitlab MR 的命令行工具

    背景

    为什么开发这个工具?主要解决以下问题:

    1. 提测、上 UAT 时,避免漏合代码。
    2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
    3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

    可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
    并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
    对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

    安装

    解压zip

    下载并解压文件:

    安装git bash

    Windows系统才要安装。
    如果 git bash 版本不足 2.41.0,最好安装最新版本。

    安装地址:https://gitforwindows.org/open in new window

    配置

    新增文件

    vi ~/.mr-config.json
    +    
    跳至主要內容

    操作 Gitlab MR 的命令行工具

    GitGitLabPython

    操作 Gitlab MR 的命令行工具

    背景

    为什么开发这个工具?主要解决以下问题:

    1. 提测、上 UAT 时,避免漏合代码。
    2. 代码冲突时,团队成员不用再问“解决这个冲突要怎么切分支?”
    3. 一个 feature 分支要向多个保护分支提交合并请求时,减少烦琐而易错的选取分支的界面操作。

    可能会有人问:为什么会漏合代码?当你在某一个迭代需要来回在不同的 feature 分支切换、一个 feature 横跨多个项目,同时你偶尔还要兼顾 bug 修复的时候,你极容易丢失上下文。
    并且,不同的 feature 研发进度不一致,可能出现的一种情况是:feature A 只是合并到 test 分支,但 feature B 却已经合并到了 uat。
    对此,有人问你代码到底合并了没,你怎么确认?一个个项目去相应的主干分支里查看提交历史吗?就是因为不想再这样做了,这才有了这个工具。

    安装

    解压zip

    下载并解压文件:

    安装git bash

    Windows系统才要安装。
    如果 git bash 版本不足 2.41.0,最好安装最新版本。

    安装地址:https://gitforwindows.org/open in new window

    配置

    新增文件

    vi ~/.mr-config.json
     

    复制以下内容:

    {
       "gitlab_url":"https://your-gitlab.com",
       "gitlab_token":"your-token",
    @@ -62,7 +62,7 @@
     

    {mr_url} 的值可以根据以下方式来获取:

    1. create 命令成功后的输出
    2. list 命令的输出
    3. gitlab web界面上 MR 的 url

    合并前会有确认提示:
    image.png

    可以取消,防止误合并:
    image.png

    冲突处理

    解决冲突,切换分支,是很麻烦的事情,故本工具为解决冲突提供了一些辅助功能。

    注意:命令行只做拉取代码、切合分支等必要操作,冲突的解决仍需要人工介入,工具不会自动合并代码的。

    合并冲突状态的MR:

    mr merge {conflict_mr_url}
     

    出现提示,是否自动切换分支为解决冲突作准备:
    image.png
    当然在此之前,要保证工作目录是干净的,如果有修改未提交,会中止切换分支操作:
    image.png

    可以使用 git stash保存修改,合并冲突后,再 git stash pop

    命令执行成功时,会切换到 conflict/ 开头的分支。
    此时,打开 IDE 或 Git 管理工具,根据提示把相应的分支合并到 conflict/ 分支即可。
    image.png

    以 IDEA 为例:
    image.png

    解决冲突后,再切回命令行,此时有两种选择:

    1. 创建 MR,适用于自己没有权限合并的场景
    2. 合并 MR,适用于自己有权限合并的场景

    如果是创建,再次执行 create 命令即可:

    mr create
     

    image.png
    创建的 MR 合并时会自动删除 conflict/ 分支。
    image.png

    如果是合并,同样再次执行 merge 命令即可,此时不用带参数:

    mr merge
    -

    image.png
    52fee749bc6d270f9ccab3eb0e04208b.png

    上次编辑于:
    贡献者: levy
    - +

    image.png
    52fee749bc6d270f9ccab3eb0e04208b.png

    上次编辑于:
    贡献者: levy
    + diff --git a/index.html b/index.html index fa3107f4..81d71723 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - levy's blog | levy @@ -34,10 +34,16 @@ } - + -
    跳至主要內容

    levy's blog

    思考,表达,练习,创造

    使用Ragas评估LLM应用

    使用Ragas评估LLM应用

    +
    跳至主要內容

    levy's blog

    思考,表达,练习,创造

    大语言模型赋能备案审查

    大语言模型赋能备案审查

    +

    业务背景

    +

    备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
    +在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
    +因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

    +

    核心流程

    +

    levy大约 2 分钟llm
    使用Ragas评估LLM应用

    使用Ragas评估LLM应用

    说明

    对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

    注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

    @@ -104,11 +110,8 @@

    前言

    后来认为:复制代码可能不是菜,而是懒,没有素养,自我要求。

    而现在:代码其实也没那么重要;某些情况下复制粘贴是可以接受的。

    编码经过七个年头,我思想上为何会有如此改变?难道这就是传说中的七年之痒?

    -

    levy大约 2 分钟Daily
    技术点评:别每张表都加tenant_id

    技术点评:别每张表都加tenant_id

    -

    前言

    -

    系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。

    -

    levy大约 3 分钟DesignDaily
    2
    3
    4
    5
    ...
    8
    - +

    levy大约 2 分钟Daily
    2
    3
    4
    5
    ...
    8
    + diff --git a/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html b/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html index 406df868..20c1ddc2 100644 --- a/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html +++ b/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html @@ -5,7 +5,7 @@ - IDEA常见问题与解决方案 | levy @@ -34,16 +34,16 @@ } - + -
    跳至主要內容

    IDEA常见问题与解决方案

    JavaDaily

    IDEA常见问题与解决方案

    启动参数过长

    Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
    解决方案:

    1. 编辑 .idea/workspace.xml
    2. 找到 PropertiesComponent
    3. 添加:

    或者这样:

    "dynamic.classpath": "true",
    +    
    跳至主要內容

    IDEA常见问题与解决方案

    JavaDaily

    IDEA常见问题与解决方案

    启动参数过长

    Error running OrderStartupApplication. Command line is too long. Shorten the command line and rerun.
    解决方案:

    1. 编辑 .idea/workspace.xml
    2. 找到 PropertiesComponent
    3. 添加:

    或者这样:

    "dynamic.classpath": "true",
     

    设置JDK版本

    相关报错:

    解决方案如下。

    1.先确保已安装 jdk。

    2.修改运行设置


    3.修改外部依赖设置

    lombok 编译报错

    前提:lombok 有maven依赖后,还要安装IDE插件open in new window

    相关报错:
    class lombok.javac.apt.LombokProcessor (in unnamed module @0x29f3e3c7) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing to unnamed module @0x29f3e3c7open in new window

    解决方案:找到相应的 pom.xml,更新依赖版本(如果没有,则添加依赖)

    <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.18.20</version>
     </dependency>
    -

    当然,还要确保项目 JDK 版本正确
    open in new window

    设置启动参数

    Run -> Edit Configurations
    注意是 VM options

    注入环境变量:spring.profiles.active=local

    也可以设置 VM options,不过要带上 -D:-Dspring.profiles.active=local

    栈溢出

    maven build "Exception in thread "main" java.lang.StackOverflowError"

    -Xss40m

    不是maven的编译选项在下面

    内存不足


    -Xmx4011m


    相关报错:
    java: java.lang.OutOfMemoryError: GC overhead limit exceeded

    解决方案:需要进行如图所示修改设置

    热加载

    相关文章:https://cloud.tencent.com/developer/article/1683029open in new window

    提示:不用追求自动重新编译,手动按 build 即可。

    终端加载环境变量

    相关问答:https://stackoverflow.com/questions/36592226/bashrc-not-sourced-on-intellij-ideas-terminal/59138750#59138750open in new window

    注意两点:

    1. shell 命令带上 -i
    2. 根据 shell 的版本,使用 .bashrc 或 .zshrc

    添加外部jar作为依赖

    如下图所示:

    打开相应文件夹,选中jar即可。

    文件找不到——依赖冲突

    相关报错:nested exception is java.io.FileNotFoundException
    这一般是 jar 包冲突。

    首先确保 pom.xml 的修改已生效,再利用 Maven Helper 插件,寻找冲突的依赖,根据报错信息,把不想的包 exclude 掉,重新加载 pom.xml。

    如果报错的包根本不在冲突列表里,也有可能是以下情况:

    • 版本不对, 则 google 一下相关报错,设置成正确的版本
    • 引入了多余的包,执行了不想要的逻辑

    exclue掉:

    重新加载:

    自动import

    文件乱码

    如图所示,根据情况修改即可:

    autowired 提示变量未赋值

    这是因为我使用的是社区版open in new window,需要手动设置下open in new window

    该方法可以放心使用。
    虽然说的是 suppress unsed warning,其实是 suppress never assigned warning, unsed warning 还是会生效的。

    上次编辑于:
    贡献者: levy
    - +

    当然,还要确保项目 JDK 版本正确
    open in new window

    设置启动参数

    Run -> Edit Configurations
    注意是 VM options

    注入环境变量:spring.profiles.active=local

    也可以设置 VM options,不过要带上 -D:-Dspring.profiles.active=local

    栈溢出

    maven build "Exception in thread "main" java.lang.StackOverflowError"

    -Xss40m

    不是maven的编译选项在下面

    内存不足


    -Xmx4011m


    相关报错:
    java: java.lang.OutOfMemoryError: GC overhead limit exceeded

    解决方案:需要进行如图所示修改设置

    热加载

    相关文章:https://cloud.tencent.com/developer/article/1683029open in new window

    提示:不用追求自动重新编译,手动按 build 即可。

    终端加载环境变量

    相关问答:https://stackoverflow.com/questions/36592226/bashrc-not-sourced-on-intellij-ideas-terminal/59138750#59138750open in new window

    注意两点:

    1. shell 命令带上 -i
    2. 根据 shell 的版本,使用 .bashrc 或 .zshrc

    添加外部jar作为依赖

    如下图所示:

    打开相应文件夹,选中jar即可。

    文件找不到——依赖冲突

    相关报错:nested exception is java.io.FileNotFoundException
    这一般是 jar 包冲突。

    首先确保 pom.xml 的修改已生效,再利用 Maven Helper 插件,寻找冲突的依赖,根据报错信息,把不想的包 exclude 掉,重新加载 pom.xml。

    如果报错的包根本不在冲突列表里,也有可能是以下情况:

    • 版本不对, 则 google 一下相关报错,设置成正确的版本
    • 引入了多余的包,执行了不想要的逻辑

    exclue掉:

    重新加载:

    自动import

    文件乱码

    如图所示,根据情况修改即可:

    autowired 提示变量未赋值

    这是因为我使用的是社区版open in new window,需要手动设置下open in new window

    该方法可以放心使用。
    虽然说的是 suppress unsed warning,其实是 suppress never assigned warning, unsed warning 还是会生效的。

    上次编辑于:
    贡献者: levy
    + diff --git a/java/Resolving-Common-Problems-in-Maven.md.html b/java/Resolving-Common-Problems-in-Maven.md.html index 858a3c83..86e8466d 100644 --- a/java/Resolving-Common-Problems-in-Maven.md.html +++ b/java/Resolving-Common-Problems-in-Maven.md.html @@ -5,7 +5,7 @@ - Maven常见问题与解决方案 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Maven常见问题与解决方案

    JavaDaily

    Maven常见问题与解决方案

    运行 class 找不到主类

    maven compile
    +    
    跳至主要內容

    Maven常见问题与解决方案

    JavaDaily

    Maven常见问题与解决方案

    运行 class 找不到主类

    maven compile
     

    得到 class 文件后

    cd /my-app/target/com/mycompany/app
     java App
     

    报错:

    错误: 找不到或无法加载主类 App
    原因: java.lang.NoClassDefFoundError: com/mycompany/app/App (wrong name: App)

    这是因为主类并非在默认包下,故需要在正确的路径下调用全限定名。

    cd /my-app/target
    @@ -78,7 +78,7 @@
     </repository>
     
     

    或命令行强制不使用依赖:

    mvn -U clean install
    -

    参考资料

    官网:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.htmlopen in new window
    书籍:《Maven实战》

    上次编辑于:
    贡献者: levy
    - +

    参考资料

    官网:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.htmlopen in new window
    书籍:《Maven实战》

    上次编辑于:
    贡献者: levy
    + diff --git a/java/avoid-sending-password-in-plaintext.html b/java/avoid-sending-password-in-plaintext.html index 433c9ca9..adfe9fe0 100644 --- a/java/avoid-sending-password-in-plaintext.html +++ b/java/avoid-sending-password-in-plaintext.html @@ -5,7 +5,7 @@ - 避免密码明文传输 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    避免密码明文传输

    JavaJavaScriptDaily

    避免密码明文传输

    说明

    密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

    本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

    流程说明:前端加密,后端解密。

    当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

    前端代码

    记得安装相应的 npm 模块。

    /*******************************
    +    
    跳至主要內容

    避免密码明文传输

    JavaJavaScriptDaily

    避免密码明文传输

    说明

    密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

    本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

    流程说明:前端加密,后端解密。

    当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。

    前端代码

    记得安装相应的 npm 模块。

    /*******************************
     Description: aes加解密工具方法
     ********************************/
     import AES from 'crypto-js/aes'
    @@ -118,7 +118,7 @@
         }
     }
     
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/java/check-if-name-exists.html b/java/check-if-name-exists.html index c6943d57..02af9123 100644 --- a/java/check-if-name-exists.html +++ b/java/check-if-name-exists.html @@ -5,7 +5,7 @@ - 检查名字是否重复 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    检查名字是否重复

    JavaMySQLDaily

    检查名字是否重复

    检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

    推荐做法

    借助数据库的来实现,执行以下语句:

    ALTER TABLE my_table ADD UNIQUE(name);
    +    
    跳至主要內容

    检查名字是否重复

    JavaMySQLDaily

    检查名字是否重复

    检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。

    推荐做法

    借助数据库的来实现,执行以下语句:

    ALTER TABLE my_table ADD UNIQUE(name);
     

    然后,在程序里添加全局异常处理类:

    @Slf4j
     @RestControllerAdvice
     public class GlobalExceptionHandler {
    @@ -62,7 +62,7 @@
     
    • 修改接口
      public boolean update(Request req) {
         if (exists(req)) throw ApplicationException("Duplication!")
       }
    -

    实践的经验表明:改得多,错的多!

    上次编辑于:
    贡献者: levy
    - +

    实践的经验表明:改得多,错的多!

    上次编辑于:
    贡献者: levy
    + diff --git a/java/common-practices-for-handling-excel.html b/java/common-practices-for-handling-excel.html index 7618da3b..570bee2f 100644 --- a/java/common-practices-for-handling-excel.html +++ b/java/common-practices-for-handling-excel.html @@ -5,7 +5,7 @@ - Excel处理常用实践 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Excel处理常用实践

    JavaDaily

    Excel处理常用实践

    基础知识

    导入需要用到对象,MultipartFile。

    @PostMapping("/import")
    +    
    跳至主要內容

    Excel处理常用实践

    JavaDaily

    Excel处理常用实践

    基础知识

    导入需要用到对象,MultipartFile。

    @PostMapping("/import")
     public boolean importLicense(
           @RequestParam("file") MultipartFile file,
           @RequestParam("tenantId") @NotBlank String tenantId,
    @@ -157,7 +157,7 @@
         if (parameterNames[i].equals("file")) continue;
         parameters.put(parameterNames[i], parameterValues[i]);
     }
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/java/how-to-convert-snapshot-into-release-jar-without-source-code.html b/java/how-to-convert-snapshot-into-release-jar-without-source-code.html index dc56cd8f..4c5f4c87 100644 --- a/java/how-to-convert-snapshot-into-release-jar-without-source-code.html +++ b/java/how-to-convert-snapshot-into-release-jar-without-source-code.html @@ -5,7 +5,7 @@ - 奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包 | levy @@ -34,14 +34,14 @@ } - + -
    跳至主要內容

    奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

    Java

    奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

    背景

    项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

    问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

    下载


    首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


    找到关键的三个 jar:

    • x.jar
    • x-sources.jar
    • x.pom

    分别点击进入详情

    依次点击 Path,下载到本地。

    修改


    首先修改名字,如图所示。

    再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


    修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


    再点击 maven 文件夹


    修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

    上传

    把前面解压出来的文件重新打包成 jar

    jar cvf my-1.4.1.jar com META-INF
    +    
    跳至主要內容

    奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

    Java

    奇技淫巧:在没有源码的情况下,把 snapshot 转成 release 包

    背景

    项目中依赖了一个旧的 snapshot.jar,有人提出要求必须使用 release.jar,不能使用 snapshot。

    问题来了,该 jar 所属的源码不知所踪,那还怎么发布 release.jar 呢?这就是本文要解决的问题。

    下载


    首先我们登录仓库,输入 ArtifactId,找到对应的 snapshot jar 包,并点击进入详情。


    找到关键的三个 jar:

    • x.jar
    • x-sources.jar
    • x.pom

    分别点击进入详情

    依次点击 Path,下载到本地。

    修改


    首先修改名字,如图所示。

    再使用任意工具解压 x.jar,提取出两个文件夹。进入 META-INF


    修改 MANIFEST.MF,把里面的 snapshot 字符串去掉(如果有的话)


    再点击 maven 文件夹


    修改 pom.xml、pom.properties 文件,把里面的 snapshot 字符串去掉。

    上传

    把前面解压出来的文件重新打包成 jar

    jar cvf my-1.4.1.jar com META-INF
     
     # you can also use zip 
     # zip -r my-1.4.1.jar com META-INF
    -

    然后上传仓库

    在Upload中,点击 maven-releases


    把三个文件添加上去,点击 Upload。


    可以看到,新的 release 版本的 jar 包已经在仓库中了,可以被安装使用了。

    上次编辑于:
    贡献者: levy
    - +

    然后上传仓库

    在Upload中,点击 maven-releases


    把三个文件添加上去,点击 Upload。


    可以看到,新的 release 版本的 jar 包已经在仓库中了,可以被安装使用了。

    上次编辑于:
    贡献者: levy
    + diff --git a/java/index.html b/java/index.html index 941fb348..ef0be2c7 100644 --- a/java/index.html +++ b/java/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Java


    - +
    跳至主要內容

    Java


    + diff --git a/java/recommend-practices-for-collections-naming-convention.html b/java/recommend-practices-for-collections-naming-convention.html index 8aecc1b9..fd8c19a8 100644 --- a/java/recommend-practices-for-collections-naming-convention.html +++ b/java/recommend-practices-for-collections-naming-convention.html @@ -5,7 +5,7 @@ - 集合命名推荐 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    集合命名推荐

    JavaDaily

    集合命名推荐

    概述

    建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

    当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?

    这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

    List

    List 的变量,一般以 List 或 s 结尾, 如 idList 或 ids。这点易于理解,大家也容易遵守。

    坏的示例:

    nodeType.forEach(t -> {
    +    
    跳至主要內容

    集合命名推荐

    JavaDaily

    集合命名推荐

    概述

    建议给常用集合类的变量命名时,后缀带上相应的集合信息,以提高可读性。

    当然,在此之前要回答一个问题:当把鼠标放到变量上面时,IDE 会提示变量的类型,为什么还要在命名上做文章?

    这是因为,有时并不在 IDE 上阅读代码,比如进行 GitHub 或 GitLab 进行 code review,此时无法获得提示,需要通过命名的规范来帮助理解。

    List

    List 的变量,一般以 List 或 s 结尾, 如 idList 或 ids。这点易于理解,大家也容易遵守。

    坏的示例:

    nodeType.forEach(t -> {
         // 省略代码
     });
     

    第一眼看到这代码的时候,不知道读者是什么反应?

    按照习惯,nodeType 通常要么是字符串、数字、或枚举,但上述居然能调用 forEach 方法?我不禁愣了一下,赶紧去看了下定义,才发现原来是List。

    好的示例:

    nodeTypes.forEach(t -> {
    @@ -82,7 +82,7 @@
         String tableId = nodeId2TableIdMap.get(tableNode.getNodeId());  //修改了这行
         tableNode.setNodeId(tableId);
     });
    -

    现在是不是好懂很多了:

    • str2TableIdMap 存储的 str -> tableId 的映射, 其中 str 是由某种规则拼接而成的字符串,具体规则封装在了 getTableNodeId这个函数里,我们暂时可以不用关心
    • nodeId2TableIdMap 存储的是 nodeId -> tableId 的映射

    仅仅修改变量名,可读性就有大大提高,效果立竿见影!

    上次编辑于:
    贡献者: levy
    - +

    现在是不是好懂很多了:

    • str2TableIdMap 存储的 str -> tableId 的映射, 其中 str 是由某种规则拼接而成的字符串,具体规则封装在了 getTableNodeId这个函数里,我们暂时可以不用关心
    • nodeId2TableIdMap 存储的是 nodeId -> tableId 的映射

    仅仅修改变量名,可读性就有大大提高,效果立竿见影!

    上次编辑于:
    贡献者: levy
    + diff --git a/java/recommend-practices-for-query-by-date-range.html b/java/recommend-practices-for-query-by-date-range.html index 20217895..11fd0434 100644 --- a/java/recommend-practices-for-query-by-date-range.html +++ b/java/recommend-practices-for-query-by-date-range.html @@ -5,7 +5,7 @@ - 根据时间范围查询推荐实践 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    根据时间范围查询推荐实践

    JavaDaily

    根据时间范围查询推荐实践

    背景

    不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

    虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

    本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

    需求

    删除某个时间段以前的日志。类似于消除浏览记录:

    分析

    上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

    • 开始时间
    • 结束时间

    如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

    实现

    MySQL

    对于 MySQL,推荐使用,因为简单直观,且方便:

    SELECT * FROM operation_logs 
    +    
    跳至主要內容

    根据时间范围查询推荐实践

    JavaDaily

    根据时间范围查询推荐实践

    背景

    不敢说是最佳实践,因为受限于特定技术、框架,并且带上了个人偏好。

    虽然原理简单,但细节很多,不想每次搞来搞去,因此还是有记录的价值。

    本文用到的技术栈为:MySQL、MyBatis、Java 8、Jackson

    需求

    删除某个时间段以前的日志。类似于消除浏览记录:

    分析

    上图只是UI示例,为了适应UI的变化,最好把接口设置成接收两个参数:

    • 开始时间
    • 结束时间

    如果UI如上图所示,则选择过去 7 天时,开始时间就是 1970 年 1 月 1 日,结束时间就是过去第 7 天。

    实现

    MySQL

    对于 MySQL,推荐使用,因为简单直观,且方便:

    SELECT * FROM operation_logs 
     WHERE created_time BETWEEN '2023-09-12 11:44:26' AND '2023-09-12 13:54:52';
     

    另一种方式:

    SELECT * FROM operation_logs 
     WHERE created_time >= '2023-09-12 11:44:26' AND created_time <= '2023-09-12 13:54:52';
    @@ -95,7 +95,7 @@
         }
     }
     
    -

    结语

    好了,终于搞完了,我的评价是:

    上次编辑于:
    贡献者: levy
    - +

    结语

    好了,终于搞完了,我的评价是:

    上次编辑于:
    贡献者: levy
    + diff --git a/java/recommend-practices-for-writing-good-functions.html b/java/recommend-practices-for-writing-good-functions.html index aa50d865..133013e8 100644 --- a/java/recommend-practices-for-writing-good-functions.html +++ b/java/recommend-practices-for-writing-good-functions.html @@ -5,7 +5,7 @@ - 编写函数的最佳实践 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    编写函数的最佳实践

    JavaDaily

    编写函数的最佳实践

    前言

    编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

    本文将推荐一些编写函数的最佳实践,以供参数。

    减少重复

    这是在遵守 Don't repeat yourselfopen in new window (DRY) 原则。

    实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

    然而,有时代码只是相似,不完全相同,不能简单地使用 IDEA 右键 + Refactor + Extract Method 来抽取函数。
    此时,为减少重复,需要进行一些思考。

    可以把程序的划分成三个部分:

    Program = Control + Logic + Data Structure
    +    
    跳至主要內容

    编写函数的最佳实践

    JavaDaily

    编写函数的最佳实践

    前言

    编写函数的目的,最根本的目的是提高可维护性,从而提高研发效率。

    本文将推荐一些编写函数的最佳实践,以供参数。

    减少重复

    这是在遵守 Don't repeat yourselfopen in new window (DRY) 原则。

    实践中可以采取一个简单的判断方法:当相同的代码段第二次出现时,就是需要把代码封装成函数的契机。

    然而,有时代码只是相似,不完全相同,不能简单地使用 IDEA 右键 + Refactor + Extract Method 来抽取函数。
    此时,为减少重复,需要进行一些思考。

    可以把程序的划分成三个部分:

    Program = Control + Logic + Data Structure
     

    一般而言,函数的入参都是数据变量,也即 Data Structure。
    而 Java 8 以后,lambda 表达式(也即函数)可以作为入参,其代表的是 Logic。
    因此,最抽象的函数,是只定义了 Control、把 Logic 及 Data Structure 都作为入参的函数。当遇到类似却不完全相同的代码、想封装函数有遇难时,可以借助上述思路来梳理逻辑。

    隐藏细节

    隐藏细节,是为了减少使用者的心智负担,方便其调用。

    有一个简单的判断标准:如果调用者需要频繁查看函数内部情况,以确定函数的目的或实现细节,那么隐藏细节的意图是失败的。

    建议

    为了达到前文所述的目的,如以下实践建议。需要指出的是,以下提倡的是建议,并非金科玉律;只适用于一般情况,并非所有情况,特殊情况是可以特殊处理的。

    优先根据业务命名

    一般而言,函数名最好是根据业务逻辑、结合业务领域来命名,而不是根据程序逻辑来命名。

    示例:

    // bad
     String getString(UserDTO user);
     
    @@ -98,7 +98,7 @@
     setupNodeTableInfo(node, nodeId2Table);
     
     setupEdgeCrossLayer(edges, Edge::getSourceId, Edge::getTargetId);
    -

    参考资料

    上次编辑于:
    贡献者: levy
    - +

    参考资料

    上次编辑于:
    贡献者: levy
    + diff --git a/java/using-enum-in-java.html b/java/using-enum-in-java.html index c28d8e27..1aaacbc7 100644 --- a/java/using-enum-in-java.html +++ b/java/using-enum-in-java.html @@ -5,7 +5,7 @@ - 枚举的推荐实践 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    枚举的推荐实践

    JavaDaily

    枚举的推荐实践

    背景

    定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

    而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

    本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

    Java

    极简实现

    理想状态下,枚举就应该这样简单!

    public enum SEX {
    +    
    跳至主要內容

    枚举的推荐实践

    JavaDaily

    枚举的推荐实践

    背景

    定义枚举的动机在于,可以作为常量,避免魔法值的出现。并且具有相应的类型,方便检索、与代码提示。

    而在使用过程中,一种符合直觉的想法是,期望枚举在具备基本的 key-value 的功能外,还能够承载更多的信息。

    本文推荐,不要在枚举中定义数字,直接使用枚举名即可!

    Java

    极简实现

    理想状态下,枚举就应该这样简单!

    public enum SEX {
       MALE,
       FEMALE;
     }
    @@ -110,7 +110,7 @@
     -- select * from database.shirts_tmp;
     
     DROP TABLE database.shirts_tmp;
    -

    如果在 Navicat 或 DBeaver等图形工具上看不出表结构的变化,请刷新数据库。

    总结

    本文主要目的是想消除枚举中对魔法数字的误用,试图让 Java 代码、数据库数据、以及前端 API 传参都使用可理解、可读性强的枚举值。

    在数据库层面,对于枚举类型字段的注意点,本文也做了说明。如果实在不想每次添加新的枚举值都执行 alter table语句,贪图省事,使用 varchar 存储也未尝不可。

    总之,本文想强调的是 Java 代码与数据库数据展示内容的一致性,至于数据库的存储格式,是见仁见智的。

    参考资料

    上次编辑于:
    贡献者: levy
    - +

    如果在 Navicat 或 DBeaver等图形工具上看不出表结构的变化,请刷新数据库。

    总结

    本文主要目的是想消除枚举中对魔法数字的误用,试图让 Java 代码、数据库数据、以及前端 API 传参都使用可理解、可读性强的枚举值。

    在数据库层面,对于枚举类型字段的注意点,本文也做了说明。如果实在不想每次添加新的枚举值都执行 alter table语句,贪图省事,使用 varchar 存储也未尝不可。

    总之,本文想强调的是 Java 代码与数据库数据展示内容的一致性,至于数据库的存储格式,是见仁见智的。

    参考资料

    上次编辑于:
    贡献者: levy
    + diff --git a/java/which-one-is-better-Boolean-or-boolean.html b/java/which-one-is-better-Boolean-or-boolean.html index 033c1e10..393b1f33 100644 --- a/java/which-one-is-better-Boolean-or-boolean.html +++ b/java/which-one-is-better-Boolean-or-boolean.html @@ -5,7 +5,7 @@ - Boolean 还是 boolean? | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Boolean 还是 boolean?

    JavaDaily

    Boolean 还是 boolean?

    在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

    结论

    先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

    原文如下:

    红线处翻译:总结就是,当你有得选的时候,请务必使用基本类型,而非包装类型。

    那什么时候使用包装类型呢?原文如下:

    红线处翻译:当你没得选、被强制要求时,才使用包装类型。如:使用泛型(使用集合类、调用参数是泛型参数的方法),以及通过反射进行方法调用(使用 invoke 方法)

    争议

    阿里的《Java开发手册》open in new window有提到,类属性强制使用包装类型。

    但注意,本文讨论的仅仅是布尔类型,不要发散话题。 那现在就来分析一下,布尔类型有没有必要考虑 null 的情况?

    我认为是没有的必要的。理由如下:

    • 布尔类型就是二进制的,代码两种情况:1或0;真或假。使用包装类型,出现第三种情况 null,不但要注意空指针异常问题,还要兼容 null 的情况——此时到底是真还是假呢?
    • 如果 null 表示的既不是真也不假,而是第三种情况——就不该定义为布尔类型,而应定义为枚举类型,因为一共有三种情况。使用 Boolean 来表示三种情况,是设计上的偷懒。

    实战

    我们来看一下实际代码中,滥用 Boolean 类型导致的问题。

    简单例子

    有如下 Controller,使用的是 boolean:

    @RestController
    +    
    跳至主要內容

    Boolean 还是 boolean?

    JavaDaily

    Boolean 还是 boolean?

    在 Java 中,对于布尔类型的变量、对象属性或方法参数的定义,到底是用包装类型 Boolean 还是基本类型 boolean 呢?

    结论

    先说结论:根据《Effective Java》(第三版),始终尽可能地使用基本类型。故应该使用 boolean。

    原文如下:

    红线处翻译:总结就是,当你有得选的时候,请务必使用基本类型,而非包装类型。

    那什么时候使用包装类型呢?原文如下:

    红线处翻译:当你没得选、被强制要求时,才使用包装类型。如:使用泛型(使用集合类、调用参数是泛型参数的方法),以及通过反射进行方法调用(使用 invoke 方法)

    争议

    阿里的《Java开发手册》open in new window有提到,类属性强制使用包装类型。

    但注意,本文讨论的仅仅是布尔类型,不要发散话题。 那现在就来分析一下,布尔类型有没有必要考虑 null 的情况?

    我认为是没有的必要的。理由如下:

    • 布尔类型就是二进制的,代码两种情况:1或0;真或假。使用包装类型,出现第三种情况 null,不但要注意空指针异常问题,还要兼容 null 的情况——此时到底是真还是假呢?
    • 如果 null 表示的既不是真也不假,而是第三种情况——就不该定义为布尔类型,而应定义为枚举类型,因为一共有三种情况。使用 Boolean 来表示三种情况,是设计上的偷懒。

    实战

    我们来看一下实际代码中,滥用 Boolean 类型导致的问题。

    简单例子

    有如下 Controller,使用的是 boolean:

    @RestController
     public class DemoController {
       @RequestMapping("/hello")
       public String hello(boolean bool) {
    @@ -52,7 +52,7 @@
     if (Boolean.FALSE.equals(query.getWithTable2Api())) {
        
     }
    -

    这就是布尔值使用 Boolean 类型在实战中最大的问题——你不能任意地进行真或假的判断,而必须兼容上下文中隐式对 null 赋予的含义。

    而多人协作过程中,外部调用是很难控制的,因此,此时使用 Boolean,只增加了无谓的编码负担。

    Joshua Bloch - Effective Java (3rd) - 2018.pdfopen in new window

    上次编辑于:
    贡献者: levy
    - +

    这就是布尔值使用 Boolean 类型在实战中最大的问题——你不能任意地进行真或假的判断,而必须兼容上下文中隐式对 null 赋予的含义。

    而多人协作过程中,外部调用是很难控制的,因此,此时使用 Boolean,只增加了无谓的编码负担。

    Joshua Bloch - Effective Java (3rd) - 2018.pdfopen in new window

    上次编辑于:
    贡献者: levy
    + diff --git a/java/which-one-is-better-forEach-or-map.html b/java/which-one-is-better-forEach-or-map.html index 48298287..3b2f3370 100644 --- a/java/which-one-is-better-forEach-or-map.html +++ b/java/which-one-is-better-forEach-or-map.html @@ -5,7 +5,7 @@ - forEach 还是 map? | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    forEach 还是 map?

    JavaDaily

    forEach 还是 map?

    背景

    遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

    List<Type> result = new ArrayList<>();
    +    
    跳至主要內容

    forEach 还是 map?

    JavaDaily

    forEach 还是 map?

    背景

    遍历一个集合,在里面执行某种操作后,再依次返回每一个元素,常见的实现方式有:

    List<Type> result = new ArrayList<>();
     list.forEach(src -> {
         Type target = BeanUtils.copyProperties(src, target);
         //省略代码
    @@ -107,7 +107,7 @@
         Type5 var5 = Stream.of(nodes, edges).filter(v -> filterVar5(v));
         
     }
    -

    效果有大大的不同!

    现在想追查哪个变量,简单轻松好多啦!

    上次编辑于:
    贡献者: levy
    - +

    效果有大大的不同!

    现在想追查哪个变量,简单轻松好多啦!

    上次编辑于:
    贡献者: levy
    + diff --git a/java/why-i-prefer-fastjson-instead-of-jackson.html b/java/why-i-prefer-fastjson-instead-of-jackson.html index 2f5fde9a..166eb21d 100644 --- a/java/why-i-prefer-fastjson-instead-of-jackson.html +++ b/java/why-i-prefer-fastjson-instead-of-jackson.html @@ -5,7 +5,7 @@ - Jackson 经典异常 UnrecognizedPropertyException | levy @@ -34,14 +34,14 @@ } - + -
    跳至主要內容

    Jackson 经典异常 UnrecognizedPropertyException

    JavaDailyVideo

    Jackson 经典异常 UnrecognizedPropertyException

    原因是 json 包含的字段,多于 Java 实体类定义的字段。

    解决方法很简单:

    new ObjectMapper()
    +    
    跳至主要內容

    Jackson 经典异常 UnrecognizedPropertyException

    JavaDailyVideo

    Jackson 经典异常 UnrecognizedPropertyException

    原因是 json 包含的字段,多于 Java 实体类定义的字段。

    解决方法很简单:

    new ObjectMapper()
       .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
     

    或者为相关实体添加注解:

    @JsonIgnoreProperties(ignoreUnknown = true)
     public class ObjectParseFromJsonString {  }
    -

    可是,如果用 fastjson,根本不会有这种错误。使用起来也简单,文档在这里open in new window

    所以,为什么不用 fastjson 呢?

    上次编辑于:
    贡献者: levy
    - +

    可是,如果用 fastjson,根本不会有这种错误。使用起来也简单,文档在这里open in new window

    所以,为什么不用 fastjson 呢?

    上次编辑于:
    贡献者: levy
    + diff --git a/java/why-is-it-so-hard-to-upgrade-dependencies.html b/java/why-is-it-so-hard-to-upgrade-dependencies.html index f462cfc1..be24a312 100644 --- a/java/why-is-it-so-hard-to-upgrade-dependencies.html +++ b/java/why-is-it-so-hard-to-upgrade-dependencies.html @@ -5,7 +5,7 @@ - 升个jar版本,怎么这么难? | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    升个jar版本,怎么这么难?

    JavaDailyVideo

    升个jar版本,怎么这么难?

    前言

    无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

    通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

    今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

    接口调用示意图

    已知信息

    1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
    2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

    问题

    infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

    于是,想通过让使用方升级 jar 来解决,此方案可行吗?

    答案可能出乎意料:不行!

    因为:jar 升级版本的动作不在自己的掌控范围内。

    说起来很简单:叫他们升一下就行。
    但问题是:

    1. 他们是谁?
    2. 你说升就升?

    问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

    复盘

    那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

    有两种思路:

    1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
    2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
    上次编辑于:
    贡献者: levy
    - +
    跳至主要內容

    升个jar版本,怎么这么难?

    JavaDailyVideo

    升个jar版本,怎么这么难?

    前言

    无论是 C/S 还是 B/S 模式下的应用通过是 API 来交互的。而 API 的消费者与提供者,可以看成一枚硬币的正反面。

    通常来说,对 Web 应用而言,前端是消费者,后端是提供者。

    今天就站在服务提供方的视角来看下,升级依赖版本,遇到的难点有哪些?

    接口调用示意图

    已知信息

    1. common.jar 的版本是 1.0.0-SNAPSHOT,实际上 common.jar 的最新稳定版已经是 5.x 了。比较了下版本内容,发现二者是不兼容的。
    2. 公司有多个业务系统依赖 infra-service,但它们隶属于别的团队

    问题

    infra-service 的某个接口,现在要修改,多返回一个字段,但因为之前的代码健壮性不够,新接口 common.jar 处理会报错。

    于是,想通过让使用方升级 jar 来解决,此方案可行吗?

    答案可能出乎意料:不行!

    因为:jar 升级版本的动作不在自己的掌控范围内。

    说起来很简单:叫他们升一下就行。
    但问题是:

    1. 他们是谁?
    2. 你说升就升?

    问题本身在技术层面很简单,但在现实的执行层面,有着超出技术范畴的难点。这便是服务提供者,需要面对的。而这是作为服务的消费者,容易忽视的一点。

    复盘

    那么,在技术层面,是否能做得更好,避免重蹈覆辙呢?其实是可以的。

    有两种思路:

    1. 只对外发布稳定版,要么强制自己的接口一直向后兼容;要么一旦有接口不兼容,在保留旧接口的情况下,发布新接口、新 jar。
    2. 利用好 SNAPSHOT 版本 jar 会不断覆盖的特性,同时对外发布 1.0.0-SNAPSHOT 版本,以及每次迭代的稳定版。也即,通过使得 1.0.0-SNAPSHOT 永远与最新的稳定版本相同,让使用者及时升级。
    上次编辑于:
    贡献者: levy
    + diff --git a/llm/evaluate-llm-app-with-ragas.html b/llm/evaluate-llm-app-with-ragas.html new file mode 100644 index 00000000..380f29c8 --- /dev/null +++ b/llm/evaluate-llm-app-with-ragas.html @@ -0,0 +1,143 @@ + + + + + + + + 使用Ragas评估LLM应用 | levy + + + + + + +
    跳至主要內容

    使用Ragas评估LLM应用

    Pythonllm

    使用Ragas评估LLM应用

    说明

    对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

    注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

    安装

    pip install ragas
    +

    数据说明

    对以下数据进行评估。

    事实:Einstein was born in 1879 in Germany.
    提问:

    1. When did Einstein born?
    2. Where did Einstein born?

    正确答案:

    1. Einstein was born in 1879.
    2. Einstein was born in Germany.

    正确性❌

    from dotenv import load_dotenv
    +load_dotenv()
    +
    +from datasets import Dataset 
    +from ragas.metrics import answer_correctness
    +from ragas import evaluate
    +
    +data_samples = {
    +    'question': [
    +        'When did Einstein born?', 
    +        'Where did Einstein born?', 
    +                 ],
    +    'answer': [
    +               'Einstein was born in 1879.',
    +               'Einstein was born in Germany.',
    +               #'Einstein was born in 1879 in Germany.'
    +               ],
    +    'ground_truth': [
    +        'Einstein was born in 1879 in Germany.',
    +        'Einstein was born in 1879 in Germany.',
    +        ]
    +}
    +dataset = Dataset.from_dict(data_samples)
    +score = evaluate(dataset,metrics=[answer_correctness])
    +print(
    +score.to_pandas()
    +)
    +
    +


    这是不能令人满意的——正确的回答,得到的指标分数却不足0.8。
    这是因为,正确性的评估,还依赖了相似度。

    忠实度✔

    from dotenv import load_dotenv
    +load_dotenv()
    +
    +from datasets import Dataset
    +from ragas.metrics import faithfulness
    +from ragas import evaluate
    +
    +data_samples = {
    +    'question': [
    +        'When did Einstein born?',
    +        'Where did Einstein born?',
    +                 ],
    +    'answer': [
    +               'Einstein was born in 1879.',
    +               'Einstein was born in Germany.',
    +               #'Einstein was born in 1879 in Germany.'
    +               ],
    +    'contexts': [
    +        ['Einstein was born in 1879 in Germany.'],
    +        ['Einstein was born in 1879 in Germany.'],
    +        ]
    +}
    +dataset = Dataset.from_dict(data_samples)
    +score = evaluate(dataset,metrics=[faithfulness])
    +print(
    +score.to_pandas()
    +)
    +
    +


    符合预期,满足要求!

    实战演示

    准备好样例问题

    sample_questions = [
    +  '一级地类中,面积哪个最大,哪个最小?',
    +  '林地中最小的地类,与耕地中最大的地类面积相差多少?',
    +  '建设用地总面积是多少?',
    +  '2021年哪个月预审面积最大?',
    +  '去年建设供地总面积,与前年比相差多少 ',
    +  '过去几年用地审批面积趋势',
    +]
    +

    准备好正确答案

    ground_truths = [
    +  '一级地类中面积最大的是林地,面积为1609.53万公顷;面积最小的是湿地,面积为12.72万公顷。',
    +  '林地中最小的地类是竹林地,面积为38.55万公顷。耕地中最大的地类是旱地,面积为167.17万公顷。它们的面积差距为167.17 - 38.55 = 128.62万公顷。',
    +  '建设用地总面积为132.86万公顷。',
    +  '2021年预审面积最大的月份是12月,预审面积为15875.50公顷',
    +  '去年建设供地总面积为26,883.06公顷,前年建设供地总面积为29,670.19公顷。两年的差值为2,787.13公顷。',
    +  '2021年的用地审批面积为57147.14公顷, 2022年的用地审批面积为50901.37公顷, 2023年的用地审批面积为17408.21公顷',
    +]
    +
    +

    编写回答函数

    async def get_answer_from_ai(question: str) -> str:
    +    # 填充你的程序逻辑
    +

    进行答案评估

    async def evaluation():
    +  llm_answers = []
    +  for i in range(len(sample_questions)):
    +    llm_answers.append(await get_answer_from_ai(sample_questions[i]))
    +
    +  data_samples = {
    +    'question': sample_questions,
    +    'answer': llm_answers,
    +    'contexts': list(map(lambda x: [x], ground_truths))
    +
    +  }
    +  dataset = Dataset.from_dict(data_samples)
    +  score = evaluate(dataset, metrics=[faithfulness])
    +  result_df = score.to_pandas()[['question', 'faithfulness']]
    +  result_table = result_df.values.tolist()
    +
    +  print(tabulate(result_table, headers=result_df.columns, tablefmt='simple'))
    +
    +  print(f"Accuracy Rate: {result_df['faithfulness'].eq(1).sum()/len(sample_questions) * 100}%")
    +
    +
    +if __name__ == '__main__':
    +  asyncio.run(evaluation())
    +
    +

    效果如下:

    上次编辑于:
    贡献者: levy
    + + + diff --git a/llm/index.html b/llm/index.html new file mode 100644 index 00000000..3044abc1 --- /dev/null +++ b/llm/index.html @@ -0,0 +1,43 @@ + + + + + + + + Llm | levy + + + + + + +
    跳至主要內容

    Llm


    + + + diff --git a/llm/llm-with-recordation-review-of-regulations.html b/llm/llm-with-recordation-review-of-regulations.html new file mode 100644 index 00000000..07ebd2ab --- /dev/null +++ b/llm/llm-with-recordation-review-of-regulations.html @@ -0,0 +1,43 @@ + + + + + + + + 大语言模型赋能备案审查 | levy + + + + + + +
    跳至主要內容

    大语言模型赋能备案审查

    llm

    大语言模型赋能备案审查

    业务背景

    备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
    在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
    因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

    核心流程

    项目难点

    1. 海量文件,必须自动化分片,不可能人工处理,也即手工插入分隔符是不可行的

    解决方案:写代码处理

    1. 通过下位法法条,能找到对应的上位法法条

    解决方案:在分片质量有所保证的提前下,在常规语义检索的基础上,通过结构化数据筛选,提高匹配率

    1. 判断下位法是否与上位法相抵触

    解决方案:参考经典案例,学习其判断逻辑;指示AI,无法判断时不要乱下结论。

    经验总结

    数据很重要,一定要了解业务数据。

    数据要分类:

    1. 参考的数据要与验证的数据区分开来。一定要在前期就找客户要参考案例,并问清楚如何验证。问清楚后,要么让客户给验证数据,要么自己造验证数据,千万不能连怎么验证都不知道就动手。
    2. 基于场景分类,如下位法对上位法范围扩大、范围缩小,它们就要区分开来。

    参考资料

    什么是上位法open in new window
    论大语言模型在规范性文件备案审查中的应用open in new window
    备案审查案例选编open in new window

    上次编辑于:
    贡献者: levy
    + + + diff --git a/mysql/index.html b/mysql/index.html index a2302448..267afde1 100644 --- a/mysql/index.html +++ b/mysql/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Mysql


    - +
    跳至主要內容

    Mysql


    + diff --git a/mysql/mysql-backup-case-study-mysqldump-in-action.html b/mysql/mysql-backup-case-study-mysqldump-in-action.html index 724808a3..c63f0156 100644 --- a/mysql/mysql-backup-case-study-mysqldump-in-action.html +++ b/mysql/mysql-backup-case-study-mysqldump-in-action.html @@ -5,7 +5,7 @@ - 数据备份案例:mysqldump实战 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    数据备份案例:mysqldump实战

    DailyMySQL

    数据备份案例:mysqldump实战

    背景

    前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

    并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

    在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

    架构


    注意到:

    1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
    2. 关于 sql 的编写在另一文中已有提及,就不重复了
    3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

    安装

    首先要在跳板机安装 mysqldump。

    如果够幸运,可以用包管理工具安装:

    sudo apt update
    +    
    跳至主要內容

    数据备份案例:mysqldump实战

    DailyMySQL

    数据备份案例:mysqldump实战

    背景

    前面有讲数据迁移的案例(mysql-a -> mysql-b),其实在迁移前还少不了备份。

    并且,因为不想停机迁移,因此还要新起一个数据库实例,记为 mysql-b',复制 mysql-b 的相关数据。这样就能在 mysql-b' 里验证迁移SQL的正确性,以确保 mysql-b 能不宕机完成数据迁移。

    在这种情况下,就需要用到我们今天的主角,数据备份工具 mysqldump。

    架构


    注意到:

    1. 从 mysql-b -> mysql-b',就要用到工具 mysqldump
    2. 关于 sql 的编写在另一文中已有提及,就不重复了
    3. 只能通过跳板机在终端连接数据库实例,因此无法使用图例界面操作。

    安装

    首先要在跳板机安装 mysqldump。

    如果够幸运,可以用包管理工具安装:

    sudo apt update
     
     sudo apt install mysql-client
     
    sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
    @@ -61,7 +61,7 @@
     

    接着我们根据业务,分析哪些表的数据是必要的,哪些表的数据可以不用导出。添加以下参数把不需要导出的表忽略掉:

    --ignore-table
     

    示例命令:

    mysqldump -u your_username -p your_database --ignore-table=table_name1 --ignore-table=table_name2 > dump.sql
     

    注意:该参数一次只能忽略一张表,故忽略多张表需要声明多次。

    导入

    导入命令类似导出,只不过此时使用的是 mysql 客户端,并且重定向符号不同。

    mysql -u your_username -p your_database_name < dump.sql
    -

    为什么不?

    现在来回答为什么不直接复制MySQL的磁盘文件。

    根据前面我们知道,也许我们的需求是部分备份,而不是全量备份,则直接拷贝磁盘文件,在数据量大的情况下,只会造成传输负担,反而“欲速则不达”。

    另外,复制磁盘文件,为保证数据一致性,要求MySQL必须停止运行。主要有以下原因:

    1. Ative Transactions: Copying database files directly may lead to data inconsistency if there are active transactions or changes happening in the database while the files are being copied.
    2. Flush and Sync: The MySQL server may buffer data in memory and write it to disk periodically. When you copy files directly, you may capture data that is in memory and not yet written to disk.
    3. File Locks: Some storage engines, such as MyISAM, may use file-level locks, which can prevent you from copying files while the server is running. InnoDB, on the other hand, uses a different mechanism (tablespace files), but copying InnoDB files still carries the risk of data inconsistency.

    因此,纵然有直接复制MySQL磁盘文件的奇技淫巧,还是不建议使用,我就也不向大家展示了。

    上次编辑于:
    贡献者: levy
    - +

    为什么不?

    现在来回答为什么不直接复制MySQL的磁盘文件。

    根据前面我们知道,也许我们的需求是部分备份,而不是全量备份,则直接拷贝磁盘文件,在数据量大的情况下,只会造成传输负担,反而“欲速则不达”。

    另外,复制磁盘文件,为保证数据一致性,要求MySQL必须停止运行。主要有以下原因:

    1. Ative Transactions: Copying database files directly may lead to data inconsistency if there are active transactions or changes happening in the database while the files are being copied.
    2. Flush and Sync: The MySQL server may buffer data in memory and write it to disk periodically. When you copy files directly, you may capture data that is in memory and not yet written to disk.
    3. File Locks: Some storage engines, such as MyISAM, may use file-level locks, which can prevent you from copying files while the server is running. InnoDB, on the other hand, uses a different mechanism (tablespace files), but copying InnoDB files still carries the risk of data inconsistency.

    因此,纵然有直接复制MySQL磁盘文件的奇技淫巧,还是不建议使用,我就也不向大家展示了。

    上次编辑于:
    贡献者: levy
    + diff --git a/mysql/mysql-data-migration-case-study-add-auto-increment.html b/mysql/mysql-data-migration-case-study-add-auto-increment.html index 0ab62f59..f94394ba 100644 --- a/mysql/mysql-data-migration-case-study-add-auto-increment.html +++ b/mysql/mysql-data-migration-case-study-add-auto-increment.html @@ -5,7 +5,7 @@ - 数据迁移案例:表AUTO_INCREMENT加10w | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    数据迁移案例:表AUTO_INCREMENT加10w

    DailyMySQL

    数据迁移案例:表AUTO_INCREMENT加10w

    背景

    项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

    问题分析:

    1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
    2. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。

    迁移思路:

    1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
    2. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
    3. 记得动手前先确保数据已备份

    注意:

    • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
    • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。

    备份

    迁移前一定要做好备份。备份的技巧在另一篇文章里有讲,就不在此赘述了。

    SQL编写

    mysql-a 可以先 insert:

    insert into table_a(id, other_fields...)
    +    
    跳至主要內容

    数据迁移案例:表AUTO_INCREMENT加10w

    DailyMySQL

    数据迁移案例:表AUTO_INCREMENT加10w

    背景

    项目要做数据迁移,要把 mysql-a 的数据,迁移至 mysql-b,同时 mysql-b 的数据不能丢失。

    问题分析:

    1. 两个 mysql 实例的表的主键都是自增的,若直接合并,必然造成主键冲突。
    2. 可以修改某一方的主键后再迁移,但要注意后续不会因主键增长而发生冲突。

    迁移思路:

    1. 由于 mysql-b 的数据更重要、且数据量更大,故决定修改 mysql-a 的数据的主键,方案是增加 10w(mysql-b 的单表数据不超过 10w条)
    2. mysql-b 的相应的表 AUTO_INCREMENT 加 10w
    3. 记得动手前先确保数据已备份

    注意:

    • mysql-b 的数据量比 mysql-a 的大,所以 mysql-b 也直接设置 AUTO_INCREMENT 加10w。
    • 但如果 mysql-a的数量比较大,那是不可行的,此时 mysql-b 需要先 select 出每张表的最大id,作为需要增加的 AUTO_INCREMENT。

    备份

    迁移前一定要做好备份。备份的技巧在另一篇文章里有讲,就不在此赘述了。

    SQL编写

    mysql-a 可以先 insert:

    insert into table_a(id, other_fields...)
     select id+100000, other_fields... from table_a
     where is_deleted=0;
     

    再导出新增的数据:

    select * 
    @@ -68,7 +68,7 @@
     DELIMITER ;
     
    CALL AddAutoIncrementValue('table_a', 100000);
     CALL AddAutoIncrementValue('tablb_b', 100000);
    -

    上述代码可复用,直接复制粘贴即可,有需要的请自取。

    上次编辑于:
    贡献者: levy
    - +

    上述代码可复用,直接复制粘贴即可,有需要的请自取。

    上次编辑于:
    贡献者: levy
    + diff --git a/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html b/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html index c4071f12..01bf204c 100644 --- a/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html +++ b/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html @@ -5,7 +5,7 @@ - MySQL 命令行执行SQL的细节 | levy @@ -34,14 +34,14 @@ } - + -
    跳至主要內容

    MySQL 命令行执行SQL的细节

    DailyMySQL

    MySQL 命令行执行SQL的细节

    背景

    经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

    但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

    环境说明

    先说明下我们的环境信息。

    我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

    则我们执行SQL语句的方式有两种:

    1. 执行导出的SQL语句文件
    2. 复制粘贴SQL语句执行

    当然,在执行前,我们要确保已进行了数据备份。

    执行SQL文件

    执行SQL文件是最简单的,一般实践也是在命令行批量执行SQL文件。

    相关的命令与恢复备份的命令一致:

    mysql -h your-ip -u your-username -p${password} your-database <  script.sql
    +    
    跳至主要內容

    MySQL 命令行执行SQL的细节

    DailyMySQL

    MySQL 命令行执行SQL的细节

    背景

    经过调试与验证,我们可以确信自己编写的SQL是正确的,是时候到目标库执行SQL了!

    但要小心,在正式环境中执行 SQL,也许会有意想不到的坑!

    环境说明

    先说明下我们的环境信息。

    我们只能通过跳板机的终端连接 mysql、执行SQL,没有DBeaver、Navicat等工具可用。

    则我们执行SQL语句的方式有两种:

    1. 执行导出的SQL语句文件
    2. 复制粘贴SQL语句执行

    当然,在执行前,我们要确保已进行了数据备份。

    执行SQL文件

    执行SQL文件是最简单的,一般实践也是在命令行批量执行SQL文件。

    相关的命令与恢复备份的命令一致:

    mysql -h your-ip -u your-username -p${password} your-database <  script.sql
     

    这是推荐的方式,因为执行语句一旦出错,就会停下,并告知是第几行的语句出错。

    但出于某些原因,你可能不想把所有SQL语句都合并到一个 script.sql 文件中。
    另外,上传文件到跳板机,可能也比较麻烦,于是,你想采用第二种方式。

    复制粘贴执行

    通过 mysql 客户端直接上 MySQL 后,在命令上执行 SQL 语句会有一个问题:错误的语句不会中断后续的执行。

    更可怕的是在命令行里,很可能你SQL语句包括的中文字符串会被过滤掉,变成空字符串。如:

    • '创建人' -> ''
    • '中英en混杂' -> 'en'

    这真是血的教训😭。

    我们先来看下错误是否中断的实验。

    新建一个只有两行的SQL文件,其中第一行语句是错误的。

    ss;
     select 1;
     

    使用导入命令:

    mysql -h your-ip -u your-username -p${password} your-database < test.sql
    -

    提示第一行有错误,第二行未执行。这是符合期望的。

    但如果连接 mysql 后,在交互式命令行里执行 source 命令:

    错误出现并不会中断SQL的执行。
    复制语句,粘贴到命令行,表现也是如此:错误只提示,不中断执行。

    那么,能否在交互式命令行里执行SQL语句,一旦错误就中断呢?

    我们在MySQL官方论坛里找到了相关的帖子open in new window

    最终得到的答案是,使用:\e。
    进行类vi界面,在这里粘贴 SQL(这里就不会有中文被过滤的问题)

    保存退出后,输入 ;则执行 SQL 语句,Ctrl + C 则不执行。

    可以看到,这种方式执行 SQL 语句,也是可以遇到错误就中断的。

    如果要删除错误的数据怎么办?

    尽管经过测试,但也不敢说语句的执行能百分之分成功,因此,这里给个温馨提醒。

    如果插入、更新了错误的数据,确实要执行 DELETE 语句,那么请做好以下 checklist:

    1. 确保已备份数据
    2. 删除前先查询,也即先写 select from,确认一下是目标数据,再改写成 select 改写成 delete
    3. 一定要写 where 语句,并且精确到主键,最好只写类似这种语句 where id = 1 或者 where id in (1,2)
    上次编辑于:
    贡献者: levy
    - +

    提示第一行有错误,第二行未执行。这是符合期望的。

    但如果连接 mysql 后,在交互式命令行里执行 source 命令:

    错误出现并不会中断SQL的执行。
    复制语句,粘贴到命令行,表现也是如此:错误只提示,不中断执行。

    那么,能否在交互式命令行里执行SQL语句,一旦错误就中断呢?

    我们在MySQL官方论坛里找到了相关的帖子open in new window

    最终得到的答案是,使用:\e。
    进行类vi界面,在这里粘贴 SQL(这里就不会有中文被过滤的问题)

    保存退出后,输入 ;则执行 SQL 语句,Ctrl + C 则不执行。

    可以看到,这种方式执行 SQL 语句,也是可以遇到错误就中断的。

    如果要删除错误的数据怎么办?

    尽管经过测试,但也不敢说语句的执行能百分之分成功,因此,这里给个温馨提醒。

    如果插入、更新了错误的数据,确实要执行 DELETE 语句,那么请做好以下 checklist:

    1. 确保已备份数据
    2. 删除前先查询,也即先写 select from,确认一下是目标数据,再改写成 select 改写成 delete
    3. 一定要写 where 语句,并且精确到主键,最好只写类似这种语句 where id = 1 或者 where id in (1,2)
    上次编辑于:
    贡献者: levy
    + diff --git a/python/add-logging-for-llm-app.html b/python/add-logging-for-llm-app.html index 223d1a3a..8af14b50 100644 --- a/python/add-logging-for-llm-app.html +++ b/python/add-logging-for-llm-app.html @@ -5,7 +5,7 @@ - 给LLM应用添加日志 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    给LLM应用添加日志

    Python

    给LLM应用添加日志

    logging替代print

    目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。

    print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。

    打包成 Docker 镜像时,Python 应用很可能看不到 print 输出open in new window。使用 pytest 执行测试用例时,默认也是看不到 printopen in new window 输出的。再加上 print 太简陋了,输出没有时间,没有级别分类,建议还是弃用,改用专门的日志模块。

    使用 Python 内置的日志模块 logging,直接 import 即可使用:

    import logging
    +    
    跳至主要內容

    给LLM应用添加日志

    Python

    给LLM应用添加日志

    logging替代print

    目前公司的LLM应用开发使用的是 Python 技术栈,观察源码,发现没有多少日志,纵使有,也是用的 print。

    print 的作用,就相当于 Java 的 System.out.print,相当于 Node.js 的 console.log,一般只适合在本地调试,不适合作为日志输出的。

    打包成 Docker 镜像时,Python 应用很可能看不到 print 输出open in new window。使用 pytest 执行测试用例时,默认也是看不到 printopen in new window 输出的。再加上 print 太简陋了,输出没有时间,没有级别分类,建议还是弃用,改用专门的日志模块。

    使用 Python 内置的日志模块 logging,直接 import 即可使用:

    import logging
     # 在入口函数添加以下一行
     logging.basicConfig(level=logging.INFO)
     
    @@ -114,7 +114,7 @@
             log.info("不需要检查插件");
         }
     }
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/python/evaluate-llm-app-with-ragas.html b/python/evaluate-llm-app-with-ragas.html deleted file mode 100644 index 0275b88a..00000000 --- a/python/evaluate-llm-app-with-ragas.html +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - - - 使用Ragas评估LLM应用 | levy - - - - - - -
    跳至主要內容

    使用Ragas评估LLM应用

    Pythonllm

    使用Ragas评估LLM应用

    说明

    对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

    注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

    安装

    pip install ragas
    -

    数据说明

    对以下数据进行评估。

    事实:Einstein was born in 1879 in Germany.
    提问:

    1. When did Einstein born?
    2. Where did Einstein born?

    正确答案:

    1. Einstein was born in 1879.
    2. Einstein was born in Germany.

    正确性❌

    from dotenv import load_dotenv
    -load_dotenv()
    -
    -from datasets import Dataset 
    -from ragas.metrics import answer_correctness
    -from ragas import evaluate
    -
    -data_samples = {
    -    'question': [
    -        'When did Einstein born?', 
    -        'Where did Einstein born?', 
    -                 ],
    -    'answer': [
    -               'Einstein was born in 1879.',
    -               'Einstein was born in Germany.',
    -               #'Einstein was born in 1879 in Germany.'
    -               ],
    -    'ground_truth': [
    -        'Einstein was born in 1879 in Germany.',
    -        'Einstein was born in 1879 in Germany.',
    -        ]
    -}
    -dataset = Dataset.from_dict(data_samples)
    -score = evaluate(dataset,metrics=[answer_correctness])
    -print(
    -score.to_pandas()
    -)
    -
    -


    这是不能令人满意的——正确的回答,得到的指标分数却不足0.8。
    这是因为,正确性的评估,还依赖了相似度。

    忠实度✔

    from dotenv import load_dotenv
    -load_dotenv()
    -
    -from datasets import Dataset
    -from ragas.metrics import faithfulness
    -from ragas import evaluate
    -
    -data_samples = {
    -    'question': [
    -        'When did Einstein born?',
    -        'Where did Einstein born?',
    -                 ],
    -    'answer': [
    -               'Einstein was born in 1879.',
    -               'Einstein was born in Germany.',
    -               #'Einstein was born in 1879 in Germany.'
    -               ],
    -    'contexts': [
    -        ['Einstein was born in 1879 in Germany.'],
    -        ['Einstein was born in 1879 in Germany.'],
    -        ]
    -}
    -dataset = Dataset.from_dict(data_samples)
    -score = evaluate(dataset,metrics=[faithfulness])
    -print(
    -score.to_pandas()
    -)
    -
    -


    符合预期,满足要求!

    实战演示

    准备好样例问题

    sample_questions = [
    -  '一级地类中,面积哪个最大,哪个最小?',
    -  '林地中最小的地类,与耕地中最大的地类面积相差多少?',
    -  '建设用地总面积是多少?',
    -  '2021年哪个月预审面积最大?',
    -  '去年建设供地总面积,与前年比相差多少 ',
    -  '过去几年用地审批面积趋势',
    -]
    -

    准备好正确答案

    ground_truths = [
    -  '一级地类中面积最大的是林地,面积为1609.53万公顷;面积最小的是湿地,面积为12.72万公顷。',
    -  '林地中最小的地类是竹林地,面积为38.55万公顷。耕地中最大的地类是旱地,面积为167.17万公顷。它们的面积差距为167.17 - 38.55 = 128.62万公顷。',
    -  '建设用地总面积为132.86万公顷。',
    -  '2021年预审面积最大的月份是12月,预审面积为15875.50公顷',
    -  '去年建设供地总面积为26,883.06公顷,前年建设供地总面积为29,670.19公顷。两年的差值为2,787.13公顷。',
    -  '2021年的用地审批面积为57147.14公顷, 2022年的用地审批面积为50901.37公顷, 2023年的用地审批面积为17408.21公顷',
    -]
    -
    -

    编写回答函数

    async def get_answer_from_ai(question: str) -> str:
    -    # 填充你的程序逻辑
    -

    进行答案评估

    async def evaluation():
    -  llm_answers = []
    -  for i in range(len(sample_questions)):
    -    llm_answers.append(await get_answer_from_ai(sample_questions[i]))
    -
    -  data_samples = {
    -    'question': sample_questions,
    -    'answer': llm_answers,
    -    'contexts': list(map(lambda x: [x], ground_truths))
    -
    -  }
    -  dataset = Dataset.from_dict(data_samples)
    -  score = evaluate(dataset, metrics=[faithfulness])
    -  result_df = score.to_pandas()[['question', 'faithfulness']]
    -  result_table = result_df.values.tolist()
    -
    -  print(tabulate(result_table, headers=result_df.columns, tablefmt='simple'))
    -
    -  print(f"Accuracy Rate: {result_df['faithfulness'].eq(1).sum()/len(sample_questions) * 100}%")
    -
    -
    -if __name__ == '__main__':
    -  asyncio.run(evaluation())
    -
    -

    效果如下:

    上次编辑于:
    贡献者: levy
    - - - diff --git a/python/export-mysql-table-into-excel.html b/python/export-mysql-table-into-excel.html index 78add1c4..76fb76e3 100644 --- a/python/export-mysql-table-into-excel.html +++ b/python/export-mysql-table-into-excel.html @@ -5,7 +5,7 @@ - Python 导出 MySQL 库表信息到 Excel | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Python 导出 MySQL 库表信息到 Excel

    Python

    Python 导出 MySQL 库表信息到 Excel

    需求

    查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

    代码

    import mysql.connector
    +    
    跳至主要內容

    Python 导出 MySQL 库表信息到 Excel

    Python

    Python 导出 MySQL 库表信息到 Excel

    需求

    查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。

    代码

    import mysql.connector
     import xlsxwriter
     
     # 根据实际修改下面的变量
    @@ -154,7 +154,7 @@
       cnx.close()
       workbook.close()
     
    -

    其他细节

    上述代码由于库的原因,只能生成新文件或覆盖文件,不能修改原有文件。
    执行代码时必须关闭生成的文件,否则报错。
    sheet的名字不能超过 31 个字符。
    RBG 转 Hex 工具open in new window,给单元格、文字上颜色时会用到,因为 Excel 显示的是 RBG,但代码里是 Hex。

    上次编辑于:
    贡献者: levy
    - +

    其他细节

    上述代码由于库的原因,只能生成新文件或覆盖文件,不能修改原有文件。
    执行代码时必须关闭生成的文件,否则报错。
    sheet的名字不能超过 31 个字符。
    RBG 转 Hex 工具open in new window,给单元格、文字上颜色时会用到,因为 Excel 显示的是 RBG,但代码里是 Hex。

    上次编辑于:
    贡献者: levy
    + diff --git a/python/index.html b/python/index.html index 0cffb7a1..0ae7b36a 100644 --- a/python/index.html +++ b/python/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Python


    - +
    跳至主要內容

    Python


    + diff --git a/python/mr.py.html b/python/mr.py.html index bef1a3ef..104f3a0b 100644 --- a/python/mr.py.html +++ b/python/mr.py.html @@ -5,7 +5,7 @@ - mr.py | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    mr.py

    GitPython

    mr.pyopen in new window

    操作 Gitlab MR 的命令行工具的源码与测试代码。

    源文件:main.pyopen in new window

    import re
    +    
    跳至主要內容

    mr.py

    GitPython

    mr.pyopen in new window

    操作 Gitlab MR 的命令行工具的源码与测试代码。

    源文件:main.pyopen in new window

    import re
     import sys
     import os
     import requests
    @@ -410,7 +410,7 @@
       del config['codebases']['default']
       assert check_config(config) != 0
     
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/rss.xml b/rss.xml index 054084bb..1b297975 100644 --- a/rss.xml +++ b/rss.xml @@ -6,168 +6,10 @@ https://levy.vip/ levy&apos;s blog zh-CN - Mon, 29 Apr 2024 11:35:42 GMT - Mon, 29 Apr 2024 11:35:42 GMT + Tue, 28 May 2024 23:24:31 GMT + Tue, 28 May 2024 23:24:31 GMT vuepress-plugin-feed2 https://validator.w3.org/feed/docs/rss2.html - - 关于 Arm 你需要了解的三件事 - https://levy.vip/devops/about-arm-things-you-need-to-know.html - https://levy.vip/devops/about-arm-things-you-need-to-know.html - 关于 Arm 你需要了解的三件事 - 关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项 构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error。 这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。 - Wed, 02 Aug 2023 00:00:00 GMT - 关于 Arm 你需要了解的三件事 -

    Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

    -

    跟我们有什么关系呢?

    -
      -
    1. MacOS 的 M1 芯片是基于 Arm 的
    2. -
    3. 云厂商及生态都在积极与 Arm 进行合作
    4. -
    5. Docker 镜像的构建有注意事项
    6. -
    -

    构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
    -这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。
    -

    -

    解决办法就是,想办法把相关命令前置,提前执行,再构建镜像。

    -

    如注释掉 Dockerfile 里的 Run chmod 777,改成在构建镜像前执行。
    -

    -

    视频里有更详细的讲解:

    -]]>
    - -
    - - 对象存储静态资源常见操作 - https://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html - https://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html - 对象存储静态资源常见操作 - 对象存储静态资源常见操作 前言 把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。 本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。 - Fri, 05 Apr 2019 00:00:00 GMT - 对象存储静态资源常见操作 -

    前言

    -

    把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

    -

    本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

    - -

    阿里云OSS

    -

    对于新建的bucket,需要做一些设置,才能正常使用静态资源。

    -

    绑定域名

    -
    -
    -

    则使用自定义域名访问,可以解决访问 html 变成下载的问题。

    -

    CNAME设置

    -

    如果绑定的是同一个阿里云账号下的域名,则可以自动添加 CNAME 记录。否则需要手动添加。

    -

    查看 bucket 外网地址:my-bucket.oss-cn-shenzhen.aliyuncs.com
    -

    -

    则去域名解析供应商设置:
    -static.domain.com(自定义域名) -> CNAME -> my-bucket.oss-cn-shenzhen.aliyuncs.com

    -

    HTTPS证书托管

    -

    上传证书,开启 HTTPS
    -
    -

    -

    如果没有证书,查看教程获取:🔒免费开启HTTPS

    -

    公共读

    -
    -

    这样可以解决访问链接超时的问题。

    -

    CORS跨域设置

    -

    在基础设置下,找到跨域设置
    -
    -

    -

    在来源中设置域名,或ip地址。下面给出最简单的示例为 *,实际可以根据需要填写允许的域名,一行一个。

    -
      -
    • 将allowed origins设置成 *
    • -
    • 将allowed methods设置成GET, POST, PUT, DELETE, HEAD
    • -
    • 将allowed headers设置成 *
    • -
    • 将expose headers设置成 -
        -
      • etag
      • -
      • x-oss-request-id
      • -
      -
    • -
    -

    这样可以解决字体无法显示、JavaScript跨域的问题。

    -

    华为云OBS

    -

    跨域设置

    -

    华为云的入口如下:
    -
    -具体规则的填写是类似阿里云OSS的。
    -

    -]]>
    - -
    - - Docker 构建镜像、推送、启动实用脚本 - https://levy.vip/devops/docker-build-and-push-script.html - https://levy.vip/devops/docker-build-and-push-script.html - Docker 构建镜像、推送、启动实用脚本 - Docker 构建镜像、推送、启动实用脚本 misc 存储多份 docker 认证信息: mkdir &quot;~/.project1&quot; mkdir &quot;~/.project2&quot; docker --config ~/.project1 login registry.example.com -u &lt;username&gt; -p &lt;deploy_token&gt; docker --config ~/.project2 login registry.example.com -u &lt;username&gt; -p &lt;deploy_token&gt; - Fri, 08 Dec 2023 00:00:00 GMT - Docker 构建镜像、推送、启动实用脚本 -

    misc

    -

    存储多份 docker 认证信息:

    -

    使用:

    -

    get-version.sh

    -

    build-image.sh

    -

    support command:

    -

    build-image.sh (remember to replace xxx with true value):

    -

    startup.sh

    -

    startup.sh

    -
    ]]>
    -
    - - 缩减Python应用的镜像体积 - https://levy.vip/devops/reduce-python-image-size.html - https://levy.vip/devops/reduce-python-image-size.html - 缩减Python应用的镜像体积 - 缩减Python应用的镜像体积 背景 当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G! 能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。 - Sat, 09 Dec 2023 00:00:00 GMT - 缩减Python应用的镜像体积 -

    背景

    -

    当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!
    -
    -能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

    - -

    原始Dockerfile

    -

    先来看看 Dockerfile 的原始模样:

    -

    使用slim镜像

    -

    最简单快捷的优化方式,是修改第一行代码,使用 slim 镜像。

    -

    重点是:为什么用 slim,而不是 alpine?

    -

    这里我们就要搞清楚,Alpine 是 Linux 众多发行版中的一员,与 CentOS、Ubuntu、Archlinux 之类一样,只是一个发行版的名字,主打一个小巧安全。

    -

    然而,alpine 镜像有一个陷阱,它使用的标准库与大多数发行版不同,它使用的是 musl libc,与常用的标准库 glibc并不兼容。

    -

    而在 alpine 镜像中,使用 Wheel 文件(后缀为 .whl) 安装 Python 依赖时,可能会出现兼容性问题,因为 Wheel 会与 C 语言的扩展库有关联,而这些 C extensions 不一定与 musl libc兼容,尤其是使用到了 NumPy、 Pandas 的时候。

    -

    那么为什么 slim 镜像又可以呢?因为 slim 镜像是基于 Debian 的, 使用的是 glibc,通过删除了许多非必需的软件包方式,优化了体积。

    -

    因此,在 Python 中,最稳妥的做法是,做 slim 镜像,而不是 alpine。

    -

    减少层数、取消本地缓存

    -

    来看这两行代码:

    -

    安装依赖,应该一步到位,我们可以把这两行压缩成一行,以减少 docker layer 数量。

    -

    另外,pip 安装依赖时,会在本地生成缓存,而这对于镜像来说是无用的,可以添加参数 --no-cache-dir禁止此行为。

    -

    则上述两行代码优化如下:

    -

    优化效果

    -

    第一次优化,使用 slim 镜像:
    -

    -

    第二次优化,减少层数、取消本地缓存:
    -
    -小小的改动,大大的变化!

    -

    参考

    -

    https://icloudnative.io/posts/intro-guide-to-dockerfile-best-practices/
    -https://icloudnative.io/posts/docker-images-part2-details-specific-to-different-languages
    -https://www.ardanlabs.com/blog/2020/04/docker-images-part3-going-farther-reduce-image-size

    -]]>
    - -
    - - sh与bash的区别 - https://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html - https://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html - sh与bash的区别 - sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。 常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。 - Thu, 03 Aug 2023 00:00:00 GMT - sh与bash的区别 -

    结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

    -

    常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

    - -

    视频里有实战演示:

    -]]>
    - -
    VuePress2 娱乐视频 https://levy.vip/daily/a-vuepress2-entertaining-video.html @@ -1510,6 +1352,164 @@ limitations of the human intellect and the infirmity of the human spirit.

    ]]>
    + + 关于 Arm 你需要了解的三件事 + https://levy.vip/devops/about-arm-things-you-need-to-know.html + https://levy.vip/devops/about-arm-things-you-need-to-know.html + 关于 Arm 你需要了解的三件事 + 关于 Arm 你需要了解的三件事 Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。 跟我们有什么关系呢? MacOS 的 M1 芯片是基于 Arm 的 云厂商及生态都在积极与 Arm 进行合作 Docker 镜像的构建有注意事项 构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error。 这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。 + Wed, 02 Aug 2023 00:00:00 GMT + 关于 Arm 你需要了解的三件事 +

    Arm 是另一种CPU架构(CISC),与常见的 x86 有所不同(RISC)。

    +

    跟我们有什么关系呢?

    +
      +
    1. MacOS 的 M1 芯片是基于 Arm 的
    2. +
    3. 云厂商及生态都在积极与 Arm 进行合作
    4. +
    5. Docker 镜像的构建有注意事项
    6. +
    +

    构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
    +这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。
    +

    +

    解决办法就是,想办法把相关命令前置,提前执行,再构建镜像。

    +

    如注释掉 Dockerfile 里的 Run chmod 777,改成在构建镜像前执行。
    +

    +

    视频里有更详细的讲解:

    +]]>
    + +
    + + 对象存储静态资源常见操作 + https://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html + https://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html + 对象存储静态资源常见操作 + 对象存储静态资源常见操作 前言 把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。 本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。 + Fri, 05 Apr 2019 00:00:00 GMT + 对象存储静态资源常见操作 +

    前言

    +

    把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

    +

    本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。

    + +

    阿里云OSS

    +

    对于新建的bucket,需要做一些设置,才能正常使用静态资源。

    +

    绑定域名

    +
    +
    +

    则使用自定义域名访问,可以解决访问 html 变成下载的问题。

    +

    CNAME设置

    +

    如果绑定的是同一个阿里云账号下的域名,则可以自动添加 CNAME 记录。否则需要手动添加。

    +

    查看 bucket 外网地址:my-bucket.oss-cn-shenzhen.aliyuncs.com
    +

    +

    则去域名解析供应商设置:
    +static.domain.com(自定义域名) -> CNAME -> my-bucket.oss-cn-shenzhen.aliyuncs.com

    +

    HTTPS证书托管

    +

    上传证书,开启 HTTPS
    +
    +

    +

    如果没有证书,查看教程获取:🔒免费开启HTTPS

    +

    公共读

    +
    +

    这样可以解决访问链接超时的问题。

    +

    CORS跨域设置

    +

    在基础设置下,找到跨域设置
    +
    +

    +

    在来源中设置域名,或ip地址。下面给出最简单的示例为 *,实际可以根据需要填写允许的域名,一行一个。

    +
      +
    • 将allowed origins设置成 *
    • +
    • 将allowed methods设置成GET, POST, PUT, DELETE, HEAD
    • +
    • 将allowed headers设置成 *
    • +
    • 将expose headers设置成 +
        +
      • etag
      • +
      • x-oss-request-id
      • +
      +
    • +
    +

    这样可以解决字体无法显示、JavaScript跨域的问题。

    +

    华为云OBS

    +

    跨域设置

    +

    华为云的入口如下:
    +
    +具体规则的填写是类似阿里云OSS的。
    +

    +]]>
    + +
    + + Docker 构建镜像、推送、启动实用脚本 + https://levy.vip/devops/docker-build-and-push-script.html + https://levy.vip/devops/docker-build-and-push-script.html + Docker 构建镜像、推送、启动实用脚本 + Docker 构建镜像、推送、启动实用脚本 misc 存储多份 docker 认证信息: mkdir &quot;~/.project1&quot; mkdir &quot;~/.project2&quot; docker --config ~/.project1 login registry.example.com -u &lt;username&gt; -p &lt;deploy_token&gt; docker --config ~/.project2 login registry.example.com -u &lt;username&gt; -p &lt;deploy_token&gt; + Fri, 08 Dec 2023 00:00:00 GMT + Docker 构建镜像、推送、启动实用脚本 +

    misc

    +

    存储多份 docker 认证信息:

    +

    使用:

    +

    get-version.sh

    +

    build-image.sh

    +

    support command:

    +

    build-image.sh (remember to replace xxx with true value):

    +

    startup.sh

    +

    startup.sh

    +
    ]]>
    +
    + + 缩减Python应用的镜像体积 + https://levy.vip/devops/reduce-python-image-size.html + https://levy.vip/devops/reduce-python-image-size.html + 缩减Python应用的镜像体积 + 缩减Python应用的镜像体积 背景 当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G! 能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。 + Sat, 09 Dec 2023 00:00:00 GMT + 缩减Python应用的镜像体积 +

    背景

    +

    当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!
    +
    +能不能减少镜像体积,缩短打包时间啊?本文将分享两招实用的技巧,让 Python 应用的镜像体积减少 50%。

    + +

    原始Dockerfile

    +

    先来看看 Dockerfile 的原始模样:

    +

    使用slim镜像

    +

    最简单快捷的优化方式,是修改第一行代码,使用 slim 镜像。

    +

    重点是:为什么用 slim,而不是 alpine?

    +

    这里我们就要搞清楚,Alpine 是 Linux 众多发行版中的一员,与 CentOS、Ubuntu、Archlinux 之类一样,只是一个发行版的名字,主打一个小巧安全。

    +

    然而,alpine 镜像有一个陷阱,它使用的标准库与大多数发行版不同,它使用的是 musl libc,与常用的标准库 glibc并不兼容。

    +

    而在 alpine 镜像中,使用 Wheel 文件(后缀为 .whl) 安装 Python 依赖时,可能会出现兼容性问题,因为 Wheel 会与 C 语言的扩展库有关联,而这些 C extensions 不一定与 musl libc兼容,尤其是使用到了 NumPy、 Pandas 的时候。

    +

    那么为什么 slim 镜像又可以呢?因为 slim 镜像是基于 Debian 的, 使用的是 glibc,通过删除了许多非必需的软件包方式,优化了体积。

    +

    因此,在 Python 中,最稳妥的做法是,做 slim 镜像,而不是 alpine。

    +

    减少层数、取消本地缓存

    +

    来看这两行代码:

    +

    安装依赖,应该一步到位,我们可以把这两行压缩成一行,以减少 docker layer 数量。

    +

    另外,pip 安装依赖时,会在本地生成缓存,而这对于镜像来说是无用的,可以添加参数 --no-cache-dir禁止此行为。

    +

    则上述两行代码优化如下:

    +

    优化效果

    +

    第一次优化,使用 slim 镜像:
    +

    +

    第二次优化,减少层数、取消本地缓存:
    +
    +小小的改动,大大的变化!

    +

    参考

    +

    https://icloudnative.io/posts/intro-guide-to-dockerfile-best-practices/
    +https://icloudnative.io/posts/docker-images-part2-details-specific-to-different-languages
    +https://www.ardanlabs.com/blog/2020/04/docker-images-part3-going-farther-reduce-image-size

    +]]>
    + +
    + + sh与bash的区别 + https://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html + https://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html + sh与bash的区别 + sh与bash的区别 结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。 常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。 + Thu, 03 Aug 2023 00:00:00 GMT + sh与bash的区别 +

    结论:如果可移植性很重要,那么应该使用 sh!一般编写 Dockerfile 时,有关的脚本优先使用 sh。

    +

    常见问题:明明是存在的、可执行的shell脚本,却在容器报错 No such file or directory,很可能是因为shell脚本开头声明了bash,但容器里只能执行 sh。

    + +

    视频里有实战演示:

    +]]>
    + +
    旧文章精选 https://levy.vip/frontend/old-articles.html @@ -3101,6 +3101,91 @@ Stream 的引入,不仅带来新的语法,也带来了函数式编程的思 ]]> + + 使用Ragas评估LLM应用 + https://levy.vip/llm/evaluate-llm-app-with-ragas.html + https://levy.vip/llm/evaluate-llm-app-with-ragas.html + 使用Ragas评估LLM应用 + 使用Ragas评估LLM应用 说明 对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。 注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。 安装 pip install ragas + Wed, 03 Apr 2024 00:00:00 GMT + 使用Ragas评估LLM应用 +

    说明

    +

    对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

    +

    注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

    +

    安装

    +

    数据说明

    +

    对以下数据进行评估。

    +

    事实:Einstein was born in 1879 in Germany.
    +提问:

    +
      +
    1. When did Einstein born?
    2. +
    3. Where did Einstein born?
    4. +
    +

    正确答案:

    +
      +
    1. Einstein was born in 1879.
    2. +
    3. Einstein was born in Germany.
    4. +
    +

    正确性❌

    +


    +这是不能令人满意的——正确的回答,得到的指标分数却不足0.8。
    +这是因为,正确性的评估,还依赖了相似度。
    +

    +

    忠实度✔

    +


    +符合预期,满足要求!

    +

    实战演示

    +

    准备好样例问题

    +

    准备好正确答案

    +

    编写回答函数

    +

    进行答案评估

    +

    效果如下:
    +

    +]]>
    + +
    + + 大语言模型赋能备案审查 + https://levy.vip/llm/llm-with-recordation-review-of-regulations.html + https://levy.vip/llm/llm-with-recordation-review-of-regulations.html + 大语言模型赋能备案审查 + 大语言模型赋能备案审查 业务背景 备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。 在这个过程中,最重要的就是确保下位法不会与上位法相抵触。 因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。 核心流程 + Wed, 29 May 2024 00:00:00 GMT + 大语言模型赋能备案审查 +

    业务背景

    +

    备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
    +在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
    +因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

    +

    核心流程

    +
    +

    项目难点

    +
      +
    1. 海量文件,必须自动化分片,不可能人工处理,也即手工插入分隔符是不可行的
    2. +
    +

    解决方案:写代码处理

    +
      +
    1. 通过下位法法条,能找到对应的上位法法条
    2. +
    +

    解决方案:在分片质量有所保证的提前下,在常规语义检索的基础上,通过结构化数据筛选,提高匹配率

    +
      +
    1. 判断下位法是否与上位法相抵触
    2. +
    +

    解决方案:参考经典案例,学习其判断逻辑;指示AI,无法判断时不要乱下结论。

    +

    经验总结

    +

    数据很重要,一定要了解业务数据。

    +

    数据要分类:

    +
      +
    1. 参考的数据要与验证的数据区分开来。一定要在前期就找客户要参考案例,并问清楚如何验证。问清楚后,要么让客户给验证数据,要么自己造验证数据,千万不能连怎么验证都不知道就动手。
    2. +
    3. 基于场景分类,如下位法对上位法范围扩大、范围缩小,它们就要区分开来。
    4. +
    +

    参考资料

    +

    什么是上位法
    +论大语言模型在规范性文件备案审查中的应用
    +备案审查案例选编
    +

    +]]>
    + +
    数据备份案例:mysqldump实战 https://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html @@ -3327,49 +3412,6 @@ Stream 的引入,不仅带来新的语法,也带来了函数式编程的思

    示例:

    ]]>
    - - 使用Ragas评估LLM应用 - https://levy.vip/python/evaluate-llm-app-with-ragas.html - https://levy.vip/python/evaluate-llm-app-with-ragas.html - 使用Ragas评估LLM应用 - 使用Ragas评估LLM应用 说明 对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。 注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。 安装 pip install ragas - Wed, 03 Apr 2024 00:00:00 GMT - 使用Ragas评估LLM应用 -

    说明

    -

    对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

    -

    注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

    -

    安装

    -

    数据说明

    -

    对以下数据进行评估。

    -

    事实:Einstein was born in 1879 in Germany.
    -提问:

    -
      -
    1. When did Einstein born?
    2. -
    3. Where did Einstein born?
    4. -
    -

    正确答案:

    -
      -
    1. Einstein was born in 1879.
    2. -
    3. Einstein was born in Germany.
    4. -
    -

    正确性❌

    -


    -这是不能令人满意的——正确的回答,得到的指标分数却不足0.8。
    -这是因为,正确性的评估,还依赖了相似度。
    -

    -

    忠实度✔

    -


    -符合预期,满足要求!

    -

    实战演示

    -

    准备好样例问题

    -

    准备好正确答案

    -

    编写回答函数

    -

    进行答案评估

    -

    效果如下:
    -

    -]]>
    - -
    Python 导出 MySQL 库表信息到 Excel https://levy.vip/python/export-mysql-table-into-excel.html diff --git a/sitemap.xml b/sitemap.xml index 2b69cd27..89fc137d 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,3 +1,3 @@ -https://levy.vip/2024-04-29T11:34:38.000Zdailyhttps://levy.vip/about.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/devops/about-arm-things-you-need-to-know.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/devops/docker-build-and-push-script.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/devops/reduce-python-image-size.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/a-vuepress2-entertaining-video.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/a-warning-from-navicat.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/claude-ai-in-action-extract-info-from-html.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/copy-code-may-not-be-guilty.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/dont-try-to-argue-with-a-sb.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/iteration-retrospective-of-sanyuan.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/leverage-ai-to-boost-coding-productivity.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/testing-environments-should-be-consistent-with-production-environments.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/things-I-have-to-vent-about-vue.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/use-claude2-instead-of-chatgpt.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/vim-creator-pass-away.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/daily/you-dont-need-to-add-tenant_id-to-every-table.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/contemporary-college-english-1.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/contemporary-college-english-2.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/contemporary-college-english-3.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/contemporary-college-english-4.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/contemporary-college-english-5.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/contemporary-college-english-6.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-1-overview.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-2-pronunciation.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-3-words.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-4-listening-and-speaking.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-5-reading-and-writing.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/how-to-self-evaluate-english-level.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/learning-7000-words-task-completed.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/english/let-chatgpt-be-your-foreign-language-teacher.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/frontend/old-articles.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/frontend/performance-optimization-in-action.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/git-best-pratices.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/git-definitive-guide-to-merge-code.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/git-history-two-tricks-in-idea.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/git-useful-commands.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/gitlab-ci.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/rethinking-git-flow.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/git/use-command-line-tool-to-manage-gitlab-merge-request.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/Resolving-Common-Problems-in-Maven.md.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/avoid-sending-password-in-plaintext.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/check-if-name-exists.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/common-practices-for-handling-excel.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/how-to-convert-snapshot-into-release-jar-without-source-code.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/recommend-practices-for-collections-naming-convention.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/recommend-practices-for-query-by-date-range.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/recommend-practices-for-writing-good-functions.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/using-enum-in-java.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/which-one-is-better-Boolean-or-boolean.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/which-one-is-better-forEach-or-map.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/why-i-prefer-fastjson-instead-of-jackson.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/java/why-is-it-so-hard-to-upgrade-dependencies.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/mysql/mysql-data-migration-case-study-add-auto-increment.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/python/add-logging-for-llm-app.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/python/evaluate-llm-app-with-ragas.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/python/export-mysql-table-into-excel.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/python/mr.py.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/unit-testing-overview.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/use-RestAssured-for-api-testing.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/use-cypress-for-e2e-testing.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/use-jest-for-test-driven-development.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/use-playwright-for-ui-testing.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/use-postman-for-api-testing.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/software-testing/use-pytest-for-regression-testing-in-llm-app.html2024-04-29T11:34:38.000Zdailyhttps://levy.vip/tools/how-to-connect-to-internet.html2024-04-29T11:34:38.000Zdaily \ No newline at end of file +https://levy.vip/2024-05-28T23:22:52.000Zdailyhttps://levy.vip/about.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/a-vuepress2-entertaining-video.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/a-warning-from-navicat.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/beyond-utf8-do-you-know-utf8mb4-and-collation.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/claude-ai-in-action-extract-info-from-html.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/copy-code-may-not-be-guilty.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/dont-try-to-argue-with-a-sb.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/iteration-retrospective-of-sanyuan.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/leverage-ai-to-boost-coding-productivity.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/reflections-on-a-speech-by-cto-of-microsoft-china.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/testing-environments-should-be-consistent-with-production-environments.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/things-I-have-to-vent-about-vue.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/use-claude2-instead-of-chatgpt.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/vim-creator-pass-away.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/daily/you-dont-need-to-add-tenant_id-to-every-table.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/contemporary-college-english-1.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/contemporary-college-english-2.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/contemporary-college-english-3.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/contemporary-college-english-4.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/contemporary-college-english-5.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/contemporary-college-english-6.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-1-overview.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-2-pronunciation.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-3-words.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-4-listening-and-speaking.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/everyone-can-learn-english-5-reading-and-writing.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/how-to-self-evaluate-english-level.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/learning-7000-words-task-completed.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/english/let-chatgpt-be-your-foreign-language-teacher.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/devops/about-arm-things-you-need-to-know.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/devops/common-solutions-of-object-storage-for-static-assets.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/devops/docker-build-and-push-script.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/devops/reduce-python-image-size.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/devops/what-is-the-difference-between-sh-and-bash.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/frontend/old-articles.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/frontend/performance-optimization-in-action.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/git-best-pratices.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/git-definitive-guide-to-merge-code.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/git-history-two-tricks-in-idea.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/git-useful-commands.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/gitlab-ci.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/rethinking-git-flow.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/git/use-command-line-tool-to-manage-gitlab-merge-request.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/Resolving-Common-Problems-in-IntelliJ-IDEA.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/Resolving-Common-Problems-in-Maven.md.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/avoid-sending-password-in-plaintext.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/check-if-name-exists.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/common-practices-for-handling-excel.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/how-to-convert-snapshot-into-release-jar-without-source-code.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/recommend-practices-for-collections-naming-convention.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/recommend-practices-for-query-by-date-range.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/recommend-practices-for-writing-good-functions.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/using-enum-in-java.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/which-one-is-better-Boolean-or-boolean.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/which-one-is-better-forEach-or-map.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/why-i-prefer-fastjson-instead-of-jackson.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/java/why-is-it-so-hard-to-upgrade-dependencies.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/llm/evaluate-llm-app-with-ragas.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/llm/llm-with-recordation-review-of-regulations.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/mysql/mysql-backup-case-study-mysqldump-in-action.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/mysql/mysql-data-migration-case-study-add-auto-increment.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/mysql/mysql-details-you-should-know-when-execute-sql-in-command-line.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/python/add-logging-for-llm-app.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/python/export-mysql-table-into-excel.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/python/mr.py.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/unit-testing-overview.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/use-RestAssured-for-api-testing.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/use-cypress-for-e2e-testing.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/use-jest-for-test-driven-development.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/use-playwright-for-ui-testing.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/use-postman-for-api-testing.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/software-testing/use-pytest-for-regression-testing-in-llm-app.html2024-05-28T23:22:52.000Zdailyhttps://levy.vip/tools/how-to-connect-to-internet.html2024-05-28T23:22:52.000Zdaily \ No newline at end of file diff --git a/software-testing/index.html b/software-testing/index.html index 327e920a..66c6109f 100644 --- a/software-testing/index.html +++ b/software-testing/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    Software Testing


    - +
    跳至主要內容

    Software Testing


    + diff --git a/software-testing/unit-testing-overview.html b/software-testing/unit-testing-overview.html index 22047091..c1332cfa 100644 --- a/software-testing/unit-testing-overview.html +++ b/software-testing/unit-testing-overview.html @@ -5,7 +5,7 @@ - 单元测试概述 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    单元测试概述

    Testing

    单元测试概述

    Why

    为什么要做单元测试?或者说,为什么要写测试代码?

    个人总结为以下两点:

    1. 测试左移open in new window,降低修复bug的成本
    2. 形成资产,方便回归测试,后续迭代重构、维护有保障

    以上两点,是研发人员写测试代码的本质理由,无论什么类型的测试代码、研发人员用的什么语言、框架都适用。

    What

    写测试代码究竟是写什么?

    个人认为测试代码主要是为了搞清楚两件事:

    1. 源码到底会不会在目标环境执行?
    2. 源码的执行结果是否符合预期?

    第一件事,引出了 code coverage 代码覆盖率的概念;第二件事,则引出了 assert 断言的概念。

    How

    测试代码的风格

    AAAopen in new window 风格:

    1. 组装参数
    2. 执行目标方法
    3. 执行断言
      @Test
    +    
    跳至主要內容

    单元测试概述

    Testing

    单元测试概述

    Why

    为什么要做单元测试?或者说,为什么要写测试代码?

    个人总结为以下两点:

    1. 测试左移open in new window,降低修复bug的成本
    2. 形成资产,方便回归测试,后续迭代重构、维护有保障

    以上两点,是研发人员写测试代码的本质理由,无论什么类型的测试代码、研发人员用的什么语言、框架都适用。

    What

    写测试代码究竟是写什么?

    个人认为测试代码主要是为了搞清楚两件事:

    1. 源码到底会不会在目标环境执行?
    2. 源码的执行结果是否符合预期?

    第一件事,引出了 code coverage 代码覆盖率的概念;第二件事,则引出了 assert 断言的概念。

    How

    测试代码的风格

    AAAopen in new window 风格:

    1. 组装参数
    2. 执行目标方法
    3. 执行断言
      @Test
       public void testHash() throws Exception {
         // Arrange
         String plainText = JSON.toJSONString(licenseRequest);
    @@ -82,7 +82,7 @@
         ;
       }
     
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/software-testing/use-RestAssured-for-api-testing.html b/software-testing/use-RestAssured-for-api-testing.html index 7e98bebc..417ecdf9 100644 --- a/software-testing/use-RestAssured-for-api-testing.html +++ b/software-testing/use-RestAssured-for-api-testing.html @@ -5,7 +5,7 @@ - 使用 RestAssured 进行 API 测试 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    使用 RestAssured 进行 API 测试

    JavaTesting

    使用 RestAssured 进行 API 测试

    前言

    本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

    本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

    What

    什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

    Why

    为什么要做 API 测试呢?

    考虑有过这样的场景:

    • 加一个新功能,自测没问题,结果被测试人员发现一个旧模块出了问题,感到措手不及
    • 后端写好了接口,前端还没开发好界面,于是感觉不方便自测,因为没有界面,只好催前端快去做页面

    API 测试就是来解决上述问题的。做 API 测试的原因有:

    • 必要性:做回归测试,避免添加新功能时破坏旧功能。
    • 便利性:方便本地调试,不用部署到线上,依赖界面去测试。
    • 资产化:让测试用例变成资产,与团队共享。

    当然,要做好 API 测试,还要接受这样的认知: 接口自动化测试并不仅仅是测试人员事情,研发人员也有责任把它做好。 否则,研发人员难免会觉得这不关我的事, 从而不愿意写这种代码。 建议研发人员从以下方便思考其好处,提升行动的积极性:

    • 减少阻塞,接口自测不再依赖前端
    • 提高效率,本地就能自测,不用把应用部署到线上环境
    • 提高质量,减少部署到研发环境、前端一调用接口就 500 的情况

    为什么不用Postman

    Postman 确实是符合直觉的接口调试的第一选项。 但注意,调试不等于测试。

    Postman 在实践过程中,最大的问题在于,无法将测试用例有效地资产化:

    • 你会在 Postman 里写断言吗?很少吧,你其实是在用肉眼去检查接口成功与否,这本质还是手工测试
    • 你的 Postman 数据能与团队共享吗?不能吧,大多数人的 Postman 数据是在本地的,也不会去付费创建一个团队以共享数据
    • 你的 Postman 数据在有版本管理吗?没有吧,大多数人的 Postman 数据是与源代码分离的,不利于维护与管理

    另外,如果要与 CI 结合,Postman 的数据更适合使用 Node.js 的 Newmanopen in new window

    考虑源代码是 Java,使用 RestAssured,编写 API 测试代码用同一种语言,可以减少使用者的心智负担较轻;并且与源代码放在同一个 Git 仓库中,易于管理。

    因此,我仍然会使用 Postman,但更多是把它应用在出现线上问题时,直接复制一个 cURL 用来复现、排查问题的情况。

    安装

    下面将介绍如何用 Maven 安装 RestAssured。

    复制以下内容到 pom.xml 即可。

        <!-- RestAssured for api testing -->
    +    
    跳至主要內容

    使用 RestAssured 进行 API 测试

    JavaTesting

    使用 RestAssured 进行 API 测试

    前言

    本文将借助 RestAssured 工具,向大家介绍如何进行 API 测试,从而在团队中开启接口自动化之路。

    本文的示例代码使用的是 Java 语言。尽管本文的首要读者是 Java 研发人员,但道理是相通的,其他语言的研发人员也能从中受益。

    What

    什么是 API 测试?简单来说,可以认为是针对 Controller 层的测试,但不是 Mock,而是会真实地处理请求,与数据库或外部服务进行交互。

    Why

    为什么要做 API 测试呢?

    考虑有过这样的场景:

    • 加一个新功能,自测没问题,结果被测试人员发现一个旧模块出了问题,感到措手不及
    • 后端写好了接口,前端还没开发好界面,于是感觉不方便自测,因为没有界面,只好催前端快去做页面

    API 测试就是来解决上述问题的。做 API 测试的原因有:

    • 必要性:做回归测试,避免添加新功能时破坏旧功能。
    • 便利性:方便本地调试,不用部署到线上,依赖界面去测试。
    • 资产化:让测试用例变成资产,与团队共享。

    当然,要做好 API 测试,还要接受这样的认知: 接口自动化测试并不仅仅是测试人员事情,研发人员也有责任把它做好。 否则,研发人员难免会觉得这不关我的事, 从而不愿意写这种代码。 建议研发人员从以下方便思考其好处,提升行动的积极性:

    • 减少阻塞,接口自测不再依赖前端
    • 提高效率,本地就能自测,不用把应用部署到线上环境
    • 提高质量,减少部署到研发环境、前端一调用接口就 500 的情况

    为什么不用Postman

    Postman 确实是符合直觉的接口调试的第一选项。 但注意,调试不等于测试。

    Postman 在实践过程中,最大的问题在于,无法将测试用例有效地资产化:

    • 你会在 Postman 里写断言吗?很少吧,你其实是在用肉眼去检查接口成功与否,这本质还是手工测试
    • 你的 Postman 数据能与团队共享吗?不能吧,大多数人的 Postman 数据是在本地的,也不会去付费创建一个团队以共享数据
    • 你的 Postman 数据在有版本管理吗?没有吧,大多数人的 Postman 数据是与源代码分离的,不利于维护与管理

    另外,如果要与 CI 结合,Postman 的数据更适合使用 Node.js 的 Newmanopen in new window

    考虑源代码是 Java,使用 RestAssured,编写 API 测试代码用同一种语言,可以减少使用者的心智负担较轻;并且与源代码放在同一个 Git 仓库中,易于管理。

    因此,我仍然会使用 Postman,但更多是把它应用在出现线上问题时,直接复制一个 cURL 用来复现、排查问题的情况。

    安装

    下面将介绍如何用 Maven 安装 RestAssured。

    复制以下内容到 pom.xml 即可。

        <!-- RestAssured for api testing -->
         <dependency>
             <groupId>io.rest-assured</groupId>
             <artifactId>rest-assured</artifactId>
    @@ -260,7 +260,7 @@
         System.out.println(result);
         Assert.assertEquals(5,result.split("\n").length);
     }
    -

    看到全部用例都执行成功,非常爽快!
    resetassured-download

    持续集成

    以集成 Gitlab CI 为例,其核心思路就是在 CI 环境运行 mvn test

    具体做法可以参考笔者的Gitlab CI文章

    其他问题

    为什么不用 Pytest

    如果编码代码的人员是测试人员,那可能首选 Pytest。但本文面向的读者的 Java 研发——既写 API,也写相应的测试代码。故选型理由参考前面 为什么不用Postman 的回答。

    这也是单元测试吗

    不是。运行上述测试代码,如果是测试本地接口,需要先在本地启动 Spring 容器;如果是测试线上接口,则需要先把应用部署到线上。因此,这是集成测试。

    参考资料

    官方文档:https://github.com/rest-assured/rest-assured/wiki/Usageopen in new window

    上次编辑于:
    贡献者: levy
    - +

    看到全部用例都执行成功,非常爽快!
    resetassured-download

    持续集成

    以集成 Gitlab CI 为例,其核心思路就是在 CI 环境运行 mvn test

    具体做法可以参考笔者的Gitlab CI文章

    其他问题

    为什么不用 Pytest

    如果编码代码的人员是测试人员,那可能首选 Pytest。但本文面向的读者的 Java 研发——既写 API,也写相应的测试代码。故选型理由参考前面 为什么不用Postman 的回答。

    这也是单元测试吗

    不是。运行上述测试代码,如果是测试本地接口,需要先在本地启动 Spring 容器;如果是测试线上接口,则需要先把应用部署到线上。因此,这是集成测试。

    参考资料

    官方文档:https://github.com/rest-assured/rest-assured/wiki/Usageopen in new window

    上次编辑于:
    贡献者: levy
    + diff --git a/software-testing/use-cypress-for-e2e-testing.html b/software-testing/use-cypress-for-e2e-testing.html index 141d2d0f..e35b945b 100644 --- a/software-testing/use-cypress-for-e2e-testing.html +++ b/software-testing/use-cypress-for-e2e-testing.html @@ -5,7 +5,7 @@ - 使用 Cypress 进行端对端测试 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    使用 Cypress 进行端对端测试

    Node.jsTesting

    使用 Cypress 进行端对端测试

    为什么写端对端测试

    写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

    谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

    为什么用 Cypress

    文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypressopen in new window

    缺点:全英文档

    快速开始

    安装

    yarn add cypress -D
    +    
    跳至主要內容

    使用 Cypress 进行端对端测试

    Node.jsTesting

    使用 Cypress 进行端对端测试

    为什么写端对端测试

    写端对端测试代码的最大好处就是,把相关的用例变成可执行的代码,成为项目的资产;结合CI系统,可在后续研发维护过程中,将一部分测试过程自动化,减少重复的手工劳动,既保障质量,又提高效率。

    谁来写呢?本文的目标读者是前端研发人员,因而相关测试代码是由前端同学去编写的。

    为什么用 Cypress

    文档齐全,生态好,对 JavaScript 友好,可简单上手。更多详见:why-cypressopen in new window

    缺点:全英文档

    快速开始

    安装

    yarn add cypress -D
     

    下载完依赖后,cypress 还会再从网络下载二进制执行包。安装完成后会在本地全局缓存一份二进制执行包,那么这台机器上所有项目都可以使用这份缓存。文档参考open in new window

    一般而言,国内用户都会在上述过程中卡住,最好在命令行设置网络代理后再下载(懂的自然懂)。

    export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
     

    如果是在 CI 环境,记得缓存 cypress binary。

    安装完后,修改 package.json

      "scripts": {
         "e2e": "cypress open"
    @@ -239,7 +239,7 @@
         - docker
     

    Cypress Dashbord

    Cypress 官方提供了一个测试记录托管服务。在 CI 命令中,只需要加上 --record --key $key 即可。

    script:
       - cypress run --record --key $key
    -

    CI 日志如下:
    image.png
    更多介绍请查阅官方文档open in new window

    总结

    cypress 比较适合写一个流程测试。一般情况下,只需要把整个正常流程操作使用 cypress 记录下来即可。

    一个流程可能长这样:创建->验证->修改->验证->删除->验证。那我们就可以根据该流程,模拟填写合法数据,模拟点击提交按钮,检查页面是否有相应内容即可。

    这样,每次开发新功能后,编写测试用例,再跑 Cypress,就能把一部分的回归测试自动化了,保证完成新功能的同时,原有功能最低限度地保持可用。

    拓展阅读

    上次编辑于:
    贡献者: levy
    - +

    CI 日志如下:
    image.png
    更多介绍请查阅官方文档open in new window

    总结

    cypress 比较适合写一个流程测试。一般情况下,只需要把整个正常流程操作使用 cypress 记录下来即可。

    一个流程可能长这样:创建->验证->修改->验证->删除->验证。那我们就可以根据该流程,模拟填写合法数据,模拟点击提交按钮,检查页面是否有相应内容即可。

    这样,每次开发新功能后,编写测试用例,再跑 Cypress,就能把一部分的回归测试自动化了,保证完成新功能的同时,原有功能最低限度地保持可用。

    拓展阅读

    上次编辑于:
    贡献者: levy
    + diff --git a/software-testing/use-jest-for-test-driven-development.html b/software-testing/use-jest-for-test-driven-development.html index cec77930..bc3519fa 100644 --- a/software-testing/use-jest-for-test-driven-development.html +++ b/software-testing/use-jest-for-test-driven-development.html @@ -5,7 +5,7 @@ - 使用 Jest 实践测试驱动开发 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    使用 Jest 实践测试驱动开发

    Node.jsTesting

    使用 Jest 实践测试驱动开发

    前言

    本文将使用jestopen in new window进行测试驱动开发的示例,源码在githubopen in new window
    旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

    本文的重点是过程以及思维方法,框架以及用法不是重点。

    本文使用的编程语言是javascript,思路对其他语言也是适用的。

    本文主要以函数作为测试对象。

    环境搭建

    假设项目结构为

    .
    +    
    跳至主要內容

    使用 Jest 实践测试驱动开发

    Node.jsTesting

    使用 Jest 实践测试驱动开发

    前言

    本文将使用jestopen in new window进行测试驱动开发的示例,源码在githubopen in new window
    旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。

    本文的重点是过程以及思维方法,框架以及用法不是重点。

    本文使用的编程语言是javascript,思路对其他语言也是适用的。

    本文主要以函数作为测试对象。

    环境搭建

    假设项目结构为

    .
     ├── README.md
     ├── package.json
     ├── src
    @@ -208,7 +208,7 @@
       return !filename.startsWith('.')  && filename.substr(filename.lastIndexOf(".")) == '.sh';
     };
     

    测试通过,提交代码

    git commit -am 'refactor: 优化逻辑'
    -

    注意,这个重构示例的重点是:

    1. 先完成功能,再重构
    2. 重构必须要有测试用例,且确保重构后全部测试用例通过

    至于其他方面,见仁见智,并不是重点。

    结论

    本文通过代码实例,践行了测试先行的理念。

    文中的代码实现不是重点,而是开发过程。

    文中 文件初始化第一个用例 的内容,尤其值得回味,它体现了两个思路:

    • 总是在有一个失败的单元测试后才开始编码
    • 用必要的最小代码让测试通过

    总的来看,TDD总是处于一个循环中:

    1. 编写用例
    2. 测试失败
    3. 编写代码
    4. 测试成功
    5. 提交代码
    6. 重复以上

    通过这样,功能的实现每次都是最小成本的,功能也是有步骤地、通过迭代完成的,而不是一步登天。

    更关键的是,完善的测试用例,是开发者的“守护天使”,有了它们,以后在添加新功能时,修改/重构代码都有了可靠的保障,让开发者可以充满信心,code with confidence😎!

    另外,测试用例延伸出的思考还有:

    1. 不需要追求完美软件,不用过分考虑将来的变化:先设计能符合当前需求的用例,再编码通过测试用例即可。将来有变化,重构代码即可,因为有用例,不用担心改错了。
    2. 重构的前提,是存在完善的测试用例。如果没有用例,只有源码,是不敢谈轻易重构,否则就是在走钢丝。
    上次编辑于:
    贡献者: levy
    - +

    注意,这个重构示例的重点是:

    1. 先完成功能,再重构
    2. 重构必须要有测试用例,且确保重构后全部测试用例通过

    至于其他方面,见仁见智,并不是重点。

    结论

    本文通过代码实例,践行了测试先行的理念。

    文中的代码实现不是重点,而是开发过程。

    文中 文件初始化第一个用例 的内容,尤其值得回味,它体现了两个思路:

    • 总是在有一个失败的单元测试后才开始编码
    • 用必要的最小代码让测试通过

    总的来看,TDD总是处于一个循环中:

    1. 编写用例
    2. 测试失败
    3. 编写代码
    4. 测试成功
    5. 提交代码
    6. 重复以上

    通过这样,功能的实现每次都是最小成本的,功能也是有步骤地、通过迭代完成的,而不是一步登天。

    更关键的是,完善的测试用例,是开发者的“守护天使”,有了它们,以后在添加新功能时,修改/重构代码都有了可靠的保障,让开发者可以充满信心,code with confidence😎!

    另外,测试用例延伸出的思考还有:

    1. 不需要追求完美软件,不用过分考虑将来的变化:先设计能符合当前需求的用例,再编码通过测试用例即可。将来有变化,重构代码即可,因为有用例,不用担心改错了。
    2. 重构的前提,是存在完善的测试用例。如果没有用例,只有源码,是不敢谈轻易重构,否则就是在走钢丝。
    上次编辑于:
    贡献者: levy
    + diff --git a/software-testing/use-playwright-for-ui-testing.html b/software-testing/use-playwright-for-ui-testing.html index 8c43dabd..58fc7f7d 100644 --- a/software-testing/use-playwright-for-ui-testing.html +++ b/software-testing/use-playwright-for-ui-testing.html @@ -5,7 +5,7 @@ - 下一代 UI 自动化测试工具 Playwright | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    下一代 UI 自动化测试工具 Playwright

    Node.jsPythonTesting

    下一代 UI 自动化测试工具 Playwright

    前言

    Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

    1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
    2. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)

    另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

    综上所述,笔者认为 Playwright 是值得在研发过程中引入的一款测试工具,它可以帮助研发、测试团队较平滑地走上自动化测试之路。它适用的典型场景之一,就是做回归测试——测试人员再也不用在界面上使用鼠标进行“点点点”,解放双手,提高测试效率。

    安装

    yarn create playwright
    +    
    跳至主要內容

    下一代 UI 自动化测试工具 Playwright

    Node.jsPythonTesting

    下一代 UI 自动化测试工具 Playwright

    前言

    Playwright 是微软于 2020 年发布的一款 E2E testing 工具,跟社区成熟的 Cypress 相比,稍显年轻。然而 Playwright 的主要优势有:

    1. 支持多语言:Node.js、Java、Python,也即它并非是前端工程师专属的工具
    2. 开箱即用的代码生成功能(Cypress 现在也支持,不过要修改配置或安装插件)

    另外,Playwright 的安装没什么门槛,不像 Cypress 可能需要黑魔法。

    综上所述,笔者认为 Playwright 是值得在研发过程中引入的一款测试工具,它可以帮助研发、测试团队较平滑地走上自动化测试之路。它适用的典型场景之一,就是做回归测试——测试人员再也不用在界面上使用鼠标进行“点点点”,解放双手,提高测试效率。

    安装

    yarn create playwright
     

    根据命令提示,输入如下:
    image.png
    默认会下载所有浏览器,如果没有浏览器兼容性测试的需求,推荐如上图所示,手动安装一个浏览器。

    以安装 chromium 为例,相应操作步骤如下:

    1. 修改配置
    vi playwright.config.ts
     

    注释掉以下内容:
    image.png

    1. 安装浏览器
    yarn playwright install --with-deps chromium
     

    等待一段时间即可,如果失败,请重试。
    image.png

    推荐再安装 VS Code 插件open in new window,获取更好的使用体验。

    使用

    代码生成

    虽然可以参考 example.spec.ts去编写测试用例,但这不是 Playwright 独特之处。Playwright 最引入注目的,是代码生成功能。

    yarn playwright codegen
    @@ -176,7 +176,7 @@
     # or
     pip3 install playwright
     
    playwright install --with-deps chromium
    -
    上次编辑于:
    贡献者: levy
    - +
    上次编辑于:
    贡献者: levy
    + diff --git a/software-testing/use-postman-for-api-testing.html b/software-testing/use-postman-for-api-testing.html index d7813ff6..0427676b 100644 --- a/software-testing/use-postman-for-api-testing.html +++ b/software-testing/use-postman-for-api-testing.html @@ -5,7 +5,7 @@ - 使用 Postman 进行 API 测试 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    使用 Postman 进行 API 测试

    Node.jsDaily

    使用 Postman 进行 API 测试

    前言

    虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

    而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

    还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

    // 为什么不设置 Map<String, String> ? 
    +    
    跳至主要內容

    使用 Postman 进行 API 测试

    Node.jsDaily

    使用 Postman 进行 API 测试

    前言

    虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

    而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

    还有就是,Java 的类型检查,在写接口测试时十分束缚手脚。如下述代码:

    // 为什么不设置 Map<String, String> ? 
     // 因为 menu 有个字段的类型是 Integer,使用 String 接收运行时会报错。
     Optional<Map<String, Object>> foundMenu = menuList.stream().filter(v -> {
       String code = (String) v.get("name"); // 这种类型转换代码实在多余!
    @@ -104,7 +104,7 @@
         - your-gitlab-runner #记得把这里修改成你的 runner 名字
       only:
         - /dev|test|uat/
    -

    推送代码,即可看到流水线

    结果如下:

    上次编辑于:
    贡献者: levy
    - +

    推送代码,即可看到流水线

    结果如下:

    上次编辑于:
    贡献者: levy
    + diff --git a/software-testing/use-pytest-for-regression-testing-in-llm-app.html b/software-testing/use-pytest-for-regression-testing-in-llm-app.html index e684d189..a4491a63 100644 --- a/software-testing/use-pytest-for-regression-testing-in-llm-app.html +++ b/software-testing/use-pytest-for-regression-testing-in-llm-app.html @@ -5,7 +5,7 @@ - 使用 pytest 为LLM应用添加回归测试 | levy @@ -34,10 +34,10 @@ } - + -
    跳至主要內容

    使用 pytest 为LLM应用添加回归测试

    PythonTestingGitlab

    使用 pytest 为LLM应用添加回归测试

    回归测试的必要性

    基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

    因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

    而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。

    pytest

    安装

    pytest 的安装就有坑,如果是使用虚拟环境 venv,安装姿势不正确的话,就会在执行测试用例的时候报错:ModuleNotFoundError: No module named xxx,具体原因参考这篇文章open in new window

    正确的安装步骤:

    1. 新开一个 bash 终端
    2. pip uninstall pytest # 删除全局的 pytest
    3. cd xxx && source ./venv/Scripts/activate # 激活虚拟环境
    4. pip install pytest # 在虚拟环境中安装 pytest
    5. pytest # 启动测试

    配置

    在项目根目录新建 pytest.ini 文件,最简单的配置如下:

    [pytest]
    +    
    跳至主要內容

    使用 pytest 为LLM应用添加回归测试

    PythonTestingGitlab

    使用 pytest 为LLM应用添加回归测试

    回归测试的必要性

    基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

    因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

    而回归测试,当然是自动化执行效率才高。本文交分享如何使用 pytest 对 LLM 应用进行自动化的回归测试。

    pytest

    安装

    pytest 的安装就有坑,如果是使用虚拟环境 venv,安装姿势不正确的话,就会在执行测试用例的时候报错:ModuleNotFoundError: No module named xxx,具体原因参考这篇文章open in new window

    正确的安装步骤:

    1. 新开一个 bash 终端
    2. pip uninstall pytest # 删除全局的 pytest
    3. cd xxx && source ./venv/Scripts/activate # 激活虚拟环境
    4. pip install pytest # 在虚拟环境中安装 pytest
    5. pytest # 启动测试

    配置

    在项目根目录新建 pytest.ini 文件,最简单的配置如下:

    [pytest]
     log_cli = 1
     log_cli_level = INFO
     

    更多的配置可参考文档open in new window

    用例

    pytest 会自动收集测试用例,要求用例满足以下规范:

    1. 文件名以 test_ 开头,如:test_intention.py
    2. 用例名以 test_ 开头,如:def test_my_method():

    为避免加载不到自定义的函数,需要包含以下代码:

    import os
    @@ -112,7 +112,7 @@
         - ./build-image.sh
       tags:
         - your-gitlab-runner
    -

    提交代码后就会触发自动化测试、构建镜像并推送。效果截图如下:

    上次编辑于:
    贡献者: levy
    - +

    提交代码后就会触发自动化测试、构建镜像并推送。效果截图如下:

    上次编辑于:
    贡献者: levy
    + diff --git a/star/index.html b/star/index.html index 03d76235..559c8a1b 100644 --- a/star/index.html +++ b/star/index.html @@ -34,11 +34,11 @@ } - + -
    跳至主要內容
    - +
    跳至主要內容
    + diff --git a/tag/ai/index.html b/tag/ai/index.html index 4dc43be1..54372344 100644 --- a/tag/ai/index.html +++ b/tag/ai/index.html @@ -34,10 +34,10 @@ } - + -
    跳至主要內容
    都什么年代了,还在用传统方式写代码?

    都什么年代了,还在用传统方式写代码?

    +
    跳至主要內容
    都什么年代了,还在用传统方式写代码?

    都什么年代了,还在用传统方式写代码?

    前言

    还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?如果是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动滑稽)。

    本文将搁置争议,秉持实用主义,讨论在 AI 可以辅助我们编码的情况下,应采取什么样的实践,从而利用好工具,提高工作效率。

    @@ -57,7 +57,7 @@

    准备工作

  • Chrome 浏览器插件 voice-control-for-chatgpt
  • 口语练习题,根据个人需求查找即可,下文将以雅思为例进行说明

  • levy大约 3 分钟EnglishAI
    - +都能撸的“全干工程师”
    72文章

      + diff --git a/tag/daily/index.html b/tag/daily/index.html index 3267ae07..5c5db78c 100644 --- a/tag/daily/index.html +++ b/tag/daily/index.html @@ -34,10 +34,10 @@ } - + -
      跳至主要內容
      生产教训:测试环境要与生产环境一致

      生产教训:测试环境要与生产环境一致

      +
      跳至主要內容

      levy大约 5 分钟JavaDaily
      2
      3
      4
      - +都能撸的“全干工程师”
      72文章

        + diff --git a/tag/design/index.html b/tag/design/index.html index b0e8532c..2d8f28b7 100644 --- a/tag/design/index.html +++ b/tag/design/index.html @@ -34,14 +34,14 @@ } - + -
        跳至主要內容
        技术点评:别每张表都加tenant_id

        技术点评:别每张表都加tenant_id

        +
        跳至主要內容
        技术点评:别每张表都加tenant_id

        技术点评:别每张表都加tenant_id

        前言

        系统满足多租户需求,是很常见的场景。本文主要聊一下在维护旧系统过程中,发现的前人多租户方案中设计、实现不合理的地方。


        levy大约 3 分钟DesignDaily
        - +都能撸的“全干工程师”
        72文章

          + diff --git a/tag/devops/index.html b/tag/devops/index.html index 6ef7c2fb..0170cac8 100644 --- a/tag/devops/index.html +++ b/tag/devops/index.html @@ -34,10 +34,10 @@ } - + -
          跳至主要內容
          缩减Python应用的镜像体积

          缩减Python应用的镜像体积

          +
          跳至主要內容
          缩减Python应用的镜像体积

          缩减Python应用的镜像体积

          背景

          当你为 LLM 应用构建镜像时,发现整个过程很慢,一看镜像体积:好家伙,1.76 G!

          @@ -68,7 +68,7 @@

          前言

          把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

          本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。


          levy大约 3 分钟FrontendDevOpsS3OBSOSS
          - +都能撸的“全干工程师”
          72文章

            + diff --git a/tag/emotion/index.html b/tag/emotion/index.html index 13ff6b0c..6fc0dc3b 100644 --- a/tag/emotion/index.html +++ b/tag/emotion/index.html @@ -34,10 +34,10 @@ } - + -
            跳至主要內容
            不要与傻逼进行争吵

            不要与傻逼进行争吵

            +
            跳至主要內容
            不要与傻逼进行争吵

            不要与傻逼进行争吵

            这是个职场吐槽帖,也是个自我反思、情绪管理帖。

            我在沟通上,有以下可以改正的点:
            0. 这个东西我也不太懂,要不你先自己看一下。改成:我帮你问一下,等下搞清楚了回复你

            @@ -48,7 +48,7 @@
          • 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!

          • levy大约 4 分钟DailyEmotionWorking ExperienceVideo
            - +都能撸的“全干工程师”
            72文章

              + diff --git a/tag/english/index.html b/tag/english/index.html index 44106aad..6b1977e0 100644 --- a/tag/english/index.html +++ b/tag/english/index.html @@ -34,10 +34,10 @@ } - + -
              跳至主要內容
              让 ChatGPT 成为你的外语私教

              让 ChatGPT 成为你的外语私教

              +
              跳至主要內容
              让 ChatGPT 成为你的外语私教

              让 ChatGPT 成为你的外语私教

              前言

              有了 ChatGPT 后,练习外语口语的门槛再次降低,没有外语环境再也不是问题了——AI 就是你的专属私教。

              本文将分享借助 AI 进行口语练习的一些工具、方法与实践经验,仅供参考。

              @@ -120,7 +120,7 @@

              方法

            • 下载 谷歌翻译 app(或别的app),设置成 英 => 中 翻译,检查自己发音正确,翻译软件能正确识别就算合格
            • 有人可能会问,赖世雄是谁,没听说过啊。这是可以理解的,因为在进行这样的学习之前,我也没听过,但他却是中国十大名师之一。他最励志的经历是,一个在高考英语中只能得7分(满分100分)的人,经过学习,成为英语教学硕士。这是典型的后天成才的前辈,跟着他学,经过本人亲自验证,绝对靠谱。


              levy大约 4 分钟English
              2
              - +都能撸的“全干工程师”
              72文章

                + diff --git a/tag/frontend/index.html b/tag/frontend/index.html index 2ba9c7be..5ae29c6b 100644 --- a/tag/frontend/index.html +++ b/tag/frontend/index.html @@ -34,10 +34,10 @@ } - + -
                跳至主要內容
                VuePress2 娱乐视频

                VuePress2 娱乐视频

                +
                跳至主要內容
                VuePress2 娱乐视频

                VuePress2 娱乐视频

                参考《原神,启动》的梗,做的一个娱乐向视频。


                levy小于 1 分钟DailyFrontendVideo
                对Vue不得不吐槽的事

                对Vue不得不吐槽的事

                为了完善博客,不得不升级至 Vuepress2。期间开发层面的各种问题我都忍了,但最终CI环节报错,我真的受不了了!

                @@ -63,7 +63,7 @@

                前言

                把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

                本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。


                levy大约 3 分钟FrontendDevOpsS3OBSOSS
                - +都能撸的“全干工程师”
                72文章

                  + diff --git a/tag/git/index.html b/tag/git/index.html index 05d17d4c..98f21ef7 100644 --- a/tag/git/index.html +++ b/tag/git/index.html @@ -34,10 +34,10 @@ } - + -
                  跳至主要內容
                  Git查看历史记录小技巧

                  Git查看历史记录小技巧

                  +
                  跳至主要內容
                  Git查看历史记录小技巧

                  Git查看历史记录小技巧

                  分享两个Git的小技巧, 都是关于在 IDEA 里查看Git的历史记录的。

                  这两个技巧,简单却实用。面对年代久远、团队人员流失严重的代码,靠的就是这两个技巧, 从提交记录里品读岁月史书,从蛛丝马迹中寻找遗失的真相。


                  levy大约 2 分钟Git
                  mr.py

                  mr.py

                  @@ -94,7 +94,7 @@

                  配置

                  Mac/Linux 用户 执行以下操作

                  vi ~/.gitconfig
                   

                  levy大约 5 分钟Git
                  - +都能撸的“全干工程师”
                  72文章

                    + diff --git a/tag/gitlab/index.html b/tag/gitlab/index.html index 9ded8142..f71aca1d 100644 --- a/tag/gitlab/index.html +++ b/tag/gitlab/index.html @@ -34,10 +34,10 @@ } - + -
                    跳至主要內容
                    操作 Gitlab MR 的命令行工具

                    操作 Gitlab MR 的命令行工具

                    +
                    跳至主要內容
                    操作 Gitlab MR 的命令行工具

                    操作 Gitlab MR 的命令行工具

                    背景

                    为什么开发这个工具?主要解决以下问题:

                      @@ -51,7 +51,7 @@

                      背景

                      前言

                      GitLab 在企业内部还是比较通用的,其 CI 用起来个人也觉得比 Jenkins 顺手,因此在这里分享一下相关的实践经验。


                    levy大约 7 分钟GitGitLabJavaNode.js
                    - +都能撸的“全干工程师”
                    72文章

                      + diff --git a/tag/index.html b/tag/index.html index b36c3614..887edb17 100644 --- a/tag/index.html +++ b/tag/index.html @@ -34,11 +34,11 @@ } - + -
                      跳至主要內容
                      - +
                      跳至主要內容
                      + diff --git a/tag/java/index.html b/tag/java/index.html index 27078021..eccd04da 100644 --- a/tag/java/index.html +++ b/tag/java/index.html @@ -34,10 +34,10 @@ } - + -
                      跳至主要內容
                      避免密码明文传输

                      避免密码明文传输

                      +
                      跳至主要內容
                      避免密码明文传输

                      避免密码明文传输

                      说明

                      密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

                      本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

                      @@ -94,7 +94,7 @@

                      运行 class 找不到主类

                      cd /my-app/target/com/mycompany/app
                       java App
                       

                      levy大约 3 分钟JavaDaily
                      2
                      - +都能撸的“全干工程师”
                      72文章

                        + diff --git a/tag/javascript/index.html b/tag/javascript/index.html index b59f258a..a359b90f 100644 --- a/tag/javascript/index.html +++ b/tag/javascript/index.html @@ -34,17 +34,17 @@ } - + -
                        跳至主要內容
                        避免密码明文传输

                        避免密码明文传输

                        +
                        跳至主要內容
                        避免密码明文传输

                        避免密码明文传输

                        说明

                        密码加密是很常见的安全性需求,但由于涉及前后端,前后端分离的情况下,开发人员容易只关心自己熟悉的领域,最终导致“知道要加密,实际还是没明文”的情况发生。

                        本文分享实际可运行的前后端代码,以减轻大家实现密码密文传输的负担。

                        流程说明:前端加密,后端解密。

                        当然,数据库存储的肯定是密文。这里后端解密的意思是:需要使用密码的时候,如获取数据库的连接,由后端解密后使用。


                        levy大约 2 分钟JavaJavaScriptDaily
                        - +都能撸的“全干工程师”
                        72文章

                          + diff --git a/tag/linux/index.html b/tag/linux/index.html index bc411430..3db61076 100644 --- a/tag/linux/index.html +++ b/tag/linux/index.html @@ -34,10 +34,10 @@ } - + -
                          跳至主要內容
                          Docker 构建镜像、推送、启动实用脚本

                          Docker 构建镜像、推送、启动实用脚本

                          +
                          跳至主要內容
                          Docker 构建镜像、推送、启动实用脚本

                          Docker 构建镜像、推送、启动实用脚本

                          misc

                          存储多份 docker 认证信息:

                          mkdir "~/.project1"
                          @@ -59,7 +59,7 @@ 

                          misc

                          构建镜像时,为 Arm 平台构建镜像时,常见的问题:exec user process caused: exec format error
                          这是因为试图在 x86 机器上执行对平台有依赖的命令,如 shell 命令。


                          levy小于 1 分钟DailyDevOpsLinux
                          - +都能撸的“全干工程师”
                          72文章

                            + diff --git a/tag/llm/index.html b/tag/llm/index.html index 740903f7..d91015ea 100644 --- a/tag/llm/index.html +++ b/tag/llm/index.html @@ -34,17 +34,23 @@ } - + -
                            跳至主要內容
                            使用Ragas评估LLM应用

                            使用Ragas评估LLM应用

                            +
                            跳至主要內容
                            大语言模型赋能备案审查

                            大语言模型赋能备案审查

                            +

                            业务背景

                            +

                            备案审查是指规范性文件在制定颁布后,按法定期限报同级或上一级人大常委会备案,由接受备案的人大常委会在法定期限内依照法定标准和程序对其进行监督审查的活动。
                            +在这个过程中,最重要的就是确保下位法不会与上位法相抵触。
                            +因规范性文件众多,而具备审查资格的专家又极少,故工作进程较为缓慢,现期望借助AI的能力,提高效率。

                            +

                            核心流程

                            +

                            levy大约 2 分钟llm
                            使用Ragas评估LLM应用

                            使用Ragas评估LLM应用

                            说明

                            对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

                            注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

                            安装

                            pip install ragas
                             

                            levy大约 3 分钟Pythonllm
                            - +都能撸的“全干工程师”
                            72文章

                              + diff --git a/tag/mysql/index.html b/tag/mysql/index.html index b74baacd..ff86e857 100644 --- a/tag/mysql/index.html +++ b/tag/mysql/index.html @@ -34,10 +34,10 @@ } - + -
                              跳至主要內容
                              检查名字是否重复

                              检查名字是否重复

                              +
                              跳至主要內容
                              检查名字是否重复

                              检查名字是否重复

                              检查名字是否重复是很常用的业务需求,本文推荐一种更省心、更少bug的做法。


                              levy大约 1 分钟JavaMySQLDaily
                              MySQL 命令行执行SQL的细节

                              MySQL 命令行执行SQL的细节

                              背景

                              @@ -79,7 +79,7 @@

                              Background

                              ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

                              That is the knowledge that today I want to share with you.


                              levy大约 2 分钟DailyVideoMySQL
                              - +都能撸的“全干工程师”
                              72文章

                                + diff --git a/tag/node.js/index.html b/tag/node.js/index.html index 32d9f7d1..39c6141b 100644 --- a/tag/node.js/index.html +++ b/tag/node.js/index.html @@ -34,10 +34,10 @@ } - + -
                                跳至主要內容
                                使用 Postman 进行 API 测试

                                使用 Postman 进行 API 测试

                                +
                                跳至主要內容
                                使用 Postman 进行 API 测试

                                使用 Postman 进行 API 测试

                                前言

                                虽然之前分享过 RestAssured 进行接口测试的教程,但实践起来,会有阻碍:研发同学还是对 Postman 更熟悉,更倾向于使用 Postman 调试接口,而不是写 Java 代码对 Controller 层进行测试。

                                而笔者在针对旧的 Java 项目添加接口测试时,又遇到了另一个问题:那就是由于模块依赖,进行接口测试时,还在把旧的测试代码一并带上。虽然说有办法解决,但究竟是麻烦不断。

                                @@ -61,7 +61,7 @@

                                为什么用 Cypress

                                前言

                                本文将使用jest进行测试驱动开发的示例,源码在github
                                旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。


                                levy大约 6 分钟Node.jsTesting
                                - +都能撸的“全干工程师”
                                72文章

                                  + diff --git a/tag/obs/index.html b/tag/obs/index.html index c5bb740c..85fd6366 100644 --- a/tag/obs/index.html +++ b/tag/obs/index.html @@ -34,15 +34,15 @@ } - + -
                                  跳至主要內容
                                  对象存储静态资源常见操作

                                  对象存储静态资源常见操作

                                  +
                                  跳至主要內容
                                  对象存储静态资源常见操作

                                  对象存储静态资源常见操作

                                  前言

                                  把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

                                  本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。


                                  levy大约 3 分钟FrontendDevOpsS3OBSOSS
                                  - +都能撸的“全干工程师”
                                  72文章

                                    + diff --git a/tag/oss/index.html b/tag/oss/index.html index f0c0e487..61a778fd 100644 --- a/tag/oss/index.html +++ b/tag/oss/index.html @@ -34,15 +34,15 @@ } - + -
                                    跳至主要內容
                                    对象存储静态资源常见操作

                                    对象存储静态资源常见操作

                                    +
                                    跳至主要內容
                                    对象存储静态资源常见操作

                                    对象存储静态资源常见操作

                                    前言

                                    把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

                                    本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。


                                    levy大约 3 分钟FrontendDevOpsS3OBSOSS
                                    - +都能撸的“全干工程师”
                                    72文章

                                      + diff --git a/tag/python/index.html b/tag/python/index.html index 996f29a8..a80a45b5 100644 --- a/tag/python/index.html +++ b/tag/python/index.html @@ -34,10 +34,10 @@ } - + -
                                      跳至主要內容
                                      使用Ragas评估LLM应用

                                      使用Ragas评估LLM应用

                                      +
                                      跳至主要內容
                                      使用Ragas评估LLM应用

                                      使用Ragas评估LLM应用

                                      说明

                                      对于已知问题有正确答案的场景,适合使用 ragas 的 faithfulness 指标对 GenAI 应用响应结果进行评估,方便进行回归测试。

                                      注意:本文提到的方法,只适用于对已知问题的评估。对于线上运行时,用户提的随机的、不在测试集范围内的问题,不适合用此方法评估。

                                      @@ -80,7 +80,7 @@

                                      背景

                                      需求

                                      查询 MySQL 某个库的全部表的元信息,输出成 Excel,每一张表一个 sheet。


                                      levy大约 1 分钟Python
                                      - +都能撸的“全干工程师”
                                      72文章

                                        + diff --git a/tag/s3/index.html b/tag/s3/index.html index ab21cb33..019fbb44 100644 --- a/tag/s3/index.html +++ b/tag/s3/index.html @@ -34,15 +34,15 @@ } - + -
                                        跳至主要內容
                                        对象存储静态资源常见操作

                                        对象存储静态资源常见操作

                                        +
                                        跳至主要內容
                                        对象存储静态资源常见操作

                                        对象存储静态资源常见操作

                                        前言

                                        把静态资源放到云厂商的对象存储服务中托管是很常见的实践,但由于涉及的事项较多,故记录下来,方便查阅。

                                        本文主要以阿里云OSS的控制台界面作为操作示例,其逻辑同样适用于华为云OBS、Amazon S3,只是可能界面上有差异,具体需要看相关的官方文档。


                                        levy大约 3 分钟FrontendDevOpsS3OBSOSS
                                        - +都能撸的“全干工程师”
                                        72文章

                                          + diff --git a/tag/testing/index.html b/tag/testing/index.html index 60dee44f..8fd17c88 100644 --- a/tag/testing/index.html +++ b/tag/testing/index.html @@ -34,10 +34,10 @@ } - + -
                                          跳至主要內容
                                          使用 pytest 为LLM应用添加回归测试

                                          使用 pytest 为LLM应用添加回归测试

                                          +
                                          跳至主要內容
                                          使用 pytest 为LLM应用添加回归测试

                                          使用 pytest 为LLM应用添加回归测试

                                          回归测试的必要性

                                          基于 LLM 的 Chat 应用大量依赖了 Prompt Engineering,而用户的输入又千奇百怪,调整了 Prompt 模板,很可能会有意想不到的效果:满足了新需求,却破坏了旧功能。

                                          因此,LLM应用比任何时候都需要回归测试,确保在迭代过程中,不破坏旧功能、不让已修复的bug复现。

                                          @@ -74,7 +74,7 @@

                                          为什么用 Cypress

                                          前言

                                          本文将使用jest进行测试驱动开发的示例,源码在github
                                          旨在说明在开发中引入单元测试后开发过程,以及测试先行的开发思路。


                                          levy大约 6 分钟Node.jsTesting
                                          - +都能撸的“全干工程师”
                                          72文章

                                            + diff --git a/tag/tool/index.html b/tag/tool/index.html index da224a15..bf2521d6 100644 --- a/tag/tool/index.html +++ b/tag/tool/index.html @@ -34,10 +34,10 @@ } - + -
                                            跳至主要內容
                                            再见ChatGPT,我选择Claude2!

                                            再见ChatGPT,我选择Claude2!

                                            +
                                            跳至主要內容
                                            再见ChatGPT,我选择Claude2!

                                            再见ChatGPT,我选择Claude2!

                                            大家好,今天给大家来评测一下几个AI工具,然后做一个推荐。

                                            首先要评测的当然是ChatGPT了,因为最早用的就是它。


                                            levy大约 4 分钟VideoAITool
                                            - +都能撸的“全干工程师”
                                            72文章

                                              + diff --git a/tag/video/index.html b/tag/video/index.html index 20feecc8..cd1b56dc 100644 --- a/tag/video/index.html +++ b/tag/video/index.html @@ -34,10 +34,10 @@ } - + -
                                              跳至主要內容
                                              微软中国CTO演讲观后感

                                              微软中国CTO演讲观后感

                                              +
                                              跳至主要內容
                                              微软中国CTO演讲观后感

                                              微软中国CTO演讲观后感

                                              看看大佬的演讲,还是有很多启发的。别的不说,推荐的书单就很有价值。


                                              levy小于 1 分钟DailyVideo
                                              VuePress2 娱乐视频

                                              VuePress2 娱乐视频

                                              参考《原神,启动》的梗,做的一个娱乐向视频。


                                              levy小于 1 分钟DailyFrontendVideo
                                              2
                                              - +都能撸的“全干工程师”
                                              72文章

                                                + diff --git a/tag/working-experience/index.html b/tag/working-experience/index.html index 4ebe489b..a31013c5 100644 --- a/tag/working-experience/index.html +++ b/tag/working-experience/index.html @@ -34,10 +34,10 @@ } - + -
                                                跳至主要內容
                                                迭代复盘之三员管理

                                                迭代复盘之三员管理

                                                +
                                                跳至主要內容
                                                迭代复盘之三员管理

                                                迭代复盘之三员管理

                                                前言

                                                本次迭代做的工作主要是回收项目能力,具体做法是把 fork 出去的代码合并回来。

                                                这次迭代因为各种原因,延期了快一个星期(周六还加了班)。

                                                @@ -53,7 +53,7 @@

                                                前言

                                              • 最后,全程忍住反问的冲动,不要使用反问句,尽量改成陈述句。陈述句使人冷静,反问句只会让矛盾升级,加剧愤怒!

                                              • levy大约 4 分钟DailyEmotionWorking ExperienceVideo
                                                - +都能撸的“全干工程师”
                                                72文章

                                                  + diff --git a/timeline/index.html b/timeline/index.html index 3e36c0e1..60f25f38 100644 --- a/timeline/index.html +++ b/timeline/index.html @@ -34,11 +34,11 @@ } - + -
                                                  跳至主要內容
                                                  - +
                                                  跳至主要內容
                                                  + diff --git a/tools/how-to-connect-to-internet.html b/tools/how-to-connect-to-internet.html index f8ea13cd..c43d0124 100644 --- a/tools/how-to-connect-to-internet.html +++ b/tools/how-to-connect-to-internet.html @@ -5,7 +5,7 @@ - 科学上网 | levy @@ -34,14 +34,14 @@ } - + -
                                                  跳至主要內容

                                                  科学上网

                                                  Tool

                                                  科学上网

                                                  说明

                                                  AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

                                                  本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

                                                  购买指南

                                                  点击进入服务页面open in new window, 看到如下页面:
                                                  image.png

                                                  点击注册账户, 会出现如图提示:
                                                  image.png

                                                  点击购买产品,就会出现查看产品列表面(为什么要这么绕,因为这是保护措施,避免产品主页被黑)。

                                                  推荐购买 Basic 套餐,一个人用的话,学习工作、娱乐看视频,每月 50GB 足够了。多个客户端可以同时在线,反正就是随便玩!算下来,一天不到7毛钱,很划算了。
                                                  image.png

                                                  点击购买后,选择包年,即可享受优惠价格,如下图所示:
                                                  image.png

                                                  支持支付宝,购买非常方便。

                                                  客户端

                                                  支持全平台客户端:

                                                  导入配置

                                                  以 Shadowsocks 为例。

                                                  根据客户端复制相应的订阅地址:
                                                  image.png

                                                  先禁用系统代理:
                                                  image.png

                                                  点击在线配置:
                                                  image.png

                                                  输入URL,点击更新:
                                                  image.png

                                                  选择一个服务器:
                                                  image.png
                                                  image.png

                                                  再恢复系统代理,选择PAC模式:
                                                  image.png

                                                  就可以愉快地上网啦!

                                                  其他

                                                  500 内部代理错误

                                                  image.png
                                                  image.png

                                                  出现此问题时,一般是内网自定义域名不允许走代理,需要禁用掉系统代理:
                                                  image.png

                                                  修改PAC文件

                                                  PAC模式是指:根据规则识别某网站是否需要使用代理访问。

                                                  什么时候需要修改PAC文件呢?

                                                  • 当某个网站不想走代理
                                                  • 设置某网站一定走代理

                                                  操作如下(以Shadowsocks为例):

                                                  按下图所示,模仿添加,即可实现遇到下列网站时选择直连:

                                                  不走代理的示例修改:

                                                   "@@||company.yuque.com",
                                                  +    
                                                  跳至主要內容

                                                  科学上网

                                                  Tool

                                                  科学上网

                                                  说明

                                                  AI时代,学会正确上网是必备的技能。不然,谷歌用不了你还能忍,但 New Bing 跟 ChatGPT 都用不了,你还能忍?

                                                  本文讲教大家如何购买稳定的包年上网套餐,为使用各种 AI 工具打下基础。

                                                  购买指南

                                                  点击进入服务页面open in new window, 看到如下页面:
                                                  image.png

                                                  点击注册账户, 会出现如图提示:
                                                  image.png

                                                  点击购买产品,就会出现查看产品列表面(为什么要这么绕,因为这是保护措施,避免产品主页被黑)。

                                                  推荐购买 Basic 套餐,一个人用的话,学习工作、娱乐看视频,每月 50GB 足够了。多个客户端可以同时在线,反正就是随便玩!算下来,一天不到7毛钱,很划算了。
                                                  image.png

                                                  点击购买后,选择包年,即可享受优惠价格,如下图所示:
                                                  image.png

                                                  支持支付宝,购买非常方便。

                                                  客户端

                                                  支持全平台客户端:

                                                  导入配置

                                                  以 Shadowsocks 为例。

                                                  根据客户端复制相应的订阅地址:
                                                  image.png

                                                  先禁用系统代理:
                                                  image.png

                                                  点击在线配置:
                                                  image.png

                                                  输入URL,点击更新:
                                                  image.png

                                                  选择一个服务器:
                                                  image.png
                                                  image.png

                                                  再恢复系统代理,选择PAC模式:
                                                  image.png

                                                  就可以愉快地上网啦!

                                                  其他

                                                  500 内部代理错误

                                                  image.png
                                                  image.png

                                                  出现此问题时,一般是内网自定义域名不允许走代理,需要禁用掉系统代理:
                                                  image.png

                                                  修改PAC文件

                                                  PAC模式是指:根据规则识别某网站是否需要使用代理访问。

                                                  什么时候需要修改PAC文件呢?

                                                  • 当某个网站不想走代理
                                                  • 设置某网站一定走代理

                                                  操作如下(以Shadowsocks为例):

                                                  按下图所示,模仿添加,即可实现遇到下列网站时选择直连:

                                                  不走代理的示例修改:

                                                   "@@||company.yuque.com",
                                                   

                                                  必走代理的示例修改:

                                                  "*.openai.com",
                                                   "*.bing.com",
                                                   

                                                  保存后记得重启软件。

                                                  使用 New Bing

                                                  再给一个配置 Clash 使用 New Bing 的示例:
                                                  image.png
                                                  编辑文件:
                                                  image.png
                                                  添加如下设置:

                                                   - DOMAIN-KEYWORD,bing,🚀 节点选择
                                                  -

                                                  效果如下图:
                                                  image.png
                                                  之后再重新加载配置,即可打开 New Bing 页面。

                                                  但要让 New Bing 回答问题,还要设置全局模式,并选择正确的节点,如图所示。
                                                  image.png

                                                  申请美区apple id

                                                  在官网创建申请: https://appleid.apple.com/accountopen in new window

                                                  不要用 qq邮箱, 注册不会的成功的。

                                                  需要注意的是,地区请选择:Alaska,否则后续充值的话要交税😅

                                                  相关的地址信息,可以使用美国地址生成器open in new window,按如图所示选择,再点击生成即可获得注册时的必要信息。这样就不需要输入信用卡信息。
                                                  image.png

                                                  上次编辑于:
                                                  贡献者: levy
                                                  - +

                                                  效果如下图:
                                                  image.png
                                                  之后再重新加载配置,即可打开 New Bing 页面。

                                                  但要让 New Bing 回答问题,还要设置全局模式,并选择正确的节点,如图所示。
                                                  image.png

                                                  申请美区apple id

                                                  在官网创建申请: https://appleid.apple.com/accountopen in new window

                                                  不要用 qq邮箱, 注册不会的成功的。

                                                  需要注意的是,地区请选择:Alaska,否则后续充值的话要交税😅

                                                  相关的地址信息,可以使用美国地址生成器open in new window,按如图所示选择,再点击生成即可获得注册时的必要信息。这样就不需要输入信用卡信息。
                                                  image.png

                                                  上次编辑于:
                                                  贡献者: levy
                                                  + diff --git a/tools/index.html b/tools/index.html index 630eb9ef..a9d7f733 100644 --- a/tools/index.html +++ b/tools/index.html @@ -34,10 +34,10 @@ } - + - - + +