跳到主要内容

EditorConfig, ESLint, Prettier 之间的关系

· 阅读需 1 分钟

Gemini Deep research: https://gemini.google.com/share/2881e5f26082

EditorConfig

EditorConfig 是为了解决不同的操作系统,编辑器之间差异的。如 换行符,tab 字符的长度。对任何文本格式的文件生效。如 .md,Makefile,.yaml,.sh

从 JSLint 到 ESLint

在不运行代码的前提下,解析代码(AST 静态分析), 发现语法逻辑问题(未定义变量,类型错误等)

JSLint,不可配置 JSHint, 可配置,不支持第三方插件 ESLint, 一切皆可插拔

ESLint 要求每一个规则都是原子性的。

Prettier

Prettier 是为了解决代码格式化问题的。

  • 最大行宽
  • 括号对齐
  • 标签闭合
  • ...

eslint-config-prettier

解决 eslint 与 prettier 职责交叉的问题。

Oxlint

Rust 开发,性能更好。比 ESLint 快 50~100 倍

git log 命令过滤提交记录

· 阅读需 1 分钟
git log --grep="feat" --grep="接口" -i --oneline --all-match
  1. 单独使用 --grep="feat" --grep="接口" 表示过滤出 git commit message 包含 feat接口 的提交记录(OR 的逻辑). 如果加上 --all-match 表示要包含 feat接口。(AND 的逻辑
  2. -i 忽略大小写
  3. `--oneline

Markdown 中的 mermaid 语法

· 阅读需 3 分钟

注释: %%

Flowchart 流程图

  • TD 表示 "top-down"

  • LR 表示 "left-right"

节点形状

  • [矩形]
  • (圆角矩形)
  • {菱形} 用于判断条件
  • ((圆))

连接线

  • --> 带箭头的实线
  • --- 实线
  • ==> 加粗
  • -.-> 带箭头的虚线
  • |文字|,在线上添加文字, 或 --text-->
  • <--> 双向箭头

Sequence Diagram 时序图

参与者

  • participant 名字 参与者
  • actor 名字 将参与者显示为小人图标

消息交互

  • A->>B: 文字 实现箭头 请求
  • B-->>A: 文字 虚线剪头 响应

生命周期

  • activate A 激活 deactivate A 取消激活 表示一个参与者处于“处理中”的状态
  • ->>+ 激活 -->>- 取消激活

注释

  • Note right of A: 文字 在 A 的右侧添加注释

扫码登录

flowchart TD
Start((开始)) --> GetQR[PC端请求登录]
GetQR --> ShowQR[展示二维码]

subgraph Mobile [手机端操作]
Scan[用户打开App扫码] --> Auth{是否授权?}
Auth -->|是| AppConfirm[点击确认登录]
Auth -->|否| AppCancel[点击取消]
end

ShowQR -- 轮询/Scoket --> CheckState{检查二维码状态}

Scan -.-> CheckState
AppConfirm -.-> CheckState

CheckState -->|wait| ShowQR
CheckState -->|scan| Prompt[请在手机端确认]
Prompt --> CheckState

CheckState -->|过期| Expired[二维码失效]
Expired --> Refresh[点击刷新] --> GetQR

CheckState -->|登录成功| Success[获取 token/跳转首页]
Success --> End((结束))

subgraph ... end 表示把这一块逻辑框在一起,在视觉上把他们看成一个整体。

sequenceDiagram
autonumber
participant PC as 浏览器
participant Server as 服务器
participant Phone as 手机APP

Note over PC,Server: 阶段一:初始化二维码
PC->>Server: 1. 请求生成二维码(GET /qrcode)
Server-->>PC: 2. 返回二维码ID(UUID)

Note over PC,Server: 阶段二:轮询检测状态
loop 每隔2秒查询一次
PC->>Server: 3. 询问 UUID状态(GET /check?uuid=xyz)
alt 状态: 已扫码(WAITING)
Server-->>PC: keep waiting
else 状态: 已扫码(SACNNED)
Phone->>Server: 4. 扫码请求(POST /scan)
Server-->>PC: scanned(PC显示用户头像)
else 状态:已确认(CONFIRMED)
Phone->>Server: 5. 登录确认(POST /confirm)
Server-->>PC: token(登录凭证)
end
end

Note over PC: 阶段三: 登录完成
PC->>PC: 写入token并跳转首页
  1. autonumber 表示自动给箭头添加序号
  2. loop...end 表示循环块
  3. alt...else...end 表示 if -else if - else
  4. Note over ... 跨越多个参与者的注释,用于划分流程/阶段。

yaml 的数组语法

· 阅读需 1 分钟

块格式

fruits:
- Apple
- Orange
numbers:
- 1
- 2

流格式

fruits: ["Apple", "Orange"]
numbers: [1, 2]

对象数组

users:
- name: Alice
job: Engineer
active: true
- name: Bob
job: Designer
active: false

- 表示一个新的数组元素的开始。 name,hob,active 属于同一个对象。

多维数组

matrix_flow:
- [1, 2, 3]
- [11, 22, 33]
- [111, 222, 333]

yaml 的长文本语法

· 阅读需 1 分钟

保留换行 |

script: |
echo "hello wrold"
date

| 会保留每次换行。 (末尾有多个换行符时,只会保留一个)

{"script": "echo \"Hello World\"\ndate\n"}

折叠换行 >

description: >
aaa1
bbb2
ccc3

ddd4

> 会将换行符替换为空格。 如果包含空行,会保留一个换行符

{"description":"aaa1 bbb2 ccc3\ndddd4\n"}

剪裁控制

|- 删除末尾的 \n

|+ 保留末尾的 \n

MySQL with 的用法

· 阅读需 1 分钟

使用 with 创建临时结果集(Common Table Expression - CTE)。

  1. 简单 CTE
WITH sales_summary AS (
SELECT
product_id,
SUM(amount) as total_sales
FROM orders
WHERE order_date >= '2026-01-01'
GROUP BY product_id
)
SELECT
p.product_name,
s.total_sales
FROM products p
JOIN sales_summary s ON p.id = s.product_id;
  1. 多个 CTE
WITH
regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
),
top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT
region,
product,
SUM(quantity) AS product_units
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;
  1. 递归 CTE, 类比为递归函数
WITH RECURSIVE factorial AS (
SELECT 1 AS n, 1 AS fact
UNION ALL
SELECT n + 1, (n + 1) * fact
FROM factorial
WHERE n < 1
)
SELECT * FROM factorial;
WITH RECURSIVE number_seq (num, squared) AS (
-- Anchor: 初始值
SELECT 1, 1

UNION ALL

-- Recursive: num < 5 是终止条件
SELECT num + 1, (num + 1) * (num + 1)
FROM number_seq
WHERE num < 2
)
SELECT * FROM number_seq;

partition by 与 group by 的区别

· 阅读需 1 分钟

group by x1,x2 ...

将除了 x1,x2 以外的字段通过聚合函数 “压缩” 成一行。

select department,avg(salary) from employees group by department;

窗口函数() over (partition by 分组列 order by 排序列)

根据分组列进行分组,然后对每个组通过排序列进行排序,最后通过窗口函数计算每列的值。

select name,department,avg(salary) over (partition by department) as '平均工资',salary - avg(salary) over (partition by department) as '和平均差距' from employees;

Java 泛型擦除机制(Type Reasure)

· 阅读需 2 分钟

Java 编译器在编译时会移除所有的泛型类型信息。

case1: 编译时 vs 运行时

// 编译时(源代码)
List<String> stringList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();

// 运行时(字节码)- 泛型信息被擦除
List stringList = new ArrayList(); // 变成 Object
List intList = new ArrayList(); // 变成 Object

// 验证:运行时类型相同
System.out.println(stringList.getClass() == intList.getClass()); // true

case2: 泛型类型被替换

// 源代码
public class Box<T> {
private T value;

public T getValue() {
return value;
}
}

// 编译后等价于
public class Box {
private Object value; // T 被擦除为 Object

public Object getValue() {
return value;
}
}

case3: 有上界的泛型

// 源代码
public class NumberBox<T extends Number> {
private T value;

public T getValue() {
return value;
}
}

// 编译后等价于
public class NumberBox {
private Number value; // T 被擦除为上界类型 Number

public Number getValue() {
return value;
}
}

带来的问题:

  1. 无法获取泛型的类型
public class Test {
public <T> void printType(List<T> list) {
// ❌ 编译错误:无法获取 T 的实际类型
// System.out.println(T.class);

// ❌ 运行时只能获取到 List,不知道 T 是什么
System.out.println(list.getClass()); // class java.util.ArrayList
}
}
  1. 无法重载
public class Example {
// ❌ 编译错误:方法签名冲突
public void method(List<String> list) { }
public void method(List<Integer> list) { } // 擦除后签名相同
}

解决方案: 使用 hutoolcn.hutool.core.util.TypeUtil#getTypeArgument(java.lang.reflect.Type, int) 方法可以获取到泛型类型。

需要注意的是:Lambda 会丢失泛型信息,具体匿名内部类和实现类(public xxx implement xxx)不会。

学习方法

· 阅读需 1 分钟

Append-only 记笔记方法

回想起来,好像在让 AI 写一个简单的工具方法之后,一直没仔细研究下它是如何实现的,只是能用就行。这是个坏习惯

要反思下,这简单的功能为什么要让 AI 来实现呢?

是只是简单的重复性的工作?

还是在找记不起名字的 API?

还是你根本没有思路? 如果是这个情况,就需要仔细 review AI 给的代码。