Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61bbf1db05 | ||
|
|
688337f7c4 | ||
|
|
8ab24fca04 | ||
|
|
e043a91f07 | ||
|
|
448544786b | ||
|
|
f5859dbe08 | ||
|
|
c83a2cd983 | ||
|
|
b3c5ad948b | ||
|
|
a0cd51951e | ||
|
|
0198bcc55b | ||
|
|
35d659397a | ||
|
|
b08b26b691 | ||
|
|
4b56ab4ed6 | ||
|
|
5bfd19a828 | ||
|
|
5e75ab4be0 | ||
|
|
7f1ec4b1c6 | ||
|
|
2214f4faad | ||
|
|
a625f6f24c | ||
|
|
a66c2fb578 | ||
|
|
1662fad4ae | ||
|
|
600563222d | ||
|
|
cbc75a5353 | ||
|
|
4cb5a8400a | ||
|
|
b7c0ee0700 | ||
|
|
9455f3b7d3 | ||
|
|
a639f81e7b | ||
|
|
3bbea7136f | ||
|
|
868900fb76 | ||
|
|
9e470a441a | ||
|
|
b1a43d4843 | ||
|
|
9cb731cb36 | ||
|
|
93828e08b2 | ||
|
|
0b06c714fa | ||
|
|
cf5cc265cb | ||
|
|
366c6c90c3 | ||
|
|
98cc6ace48 | ||
|
|
751d78be0a | ||
|
|
acfb7fd442 | ||
|
|
750e2cec32 | ||
|
|
d01463fc9e | ||
|
|
534e5a0f49 | ||
|
|
3b2b2adde4 | ||
|
|
e905d37dab | ||
|
|
14dbde8615 | ||
|
|
62afd07694 | ||
|
|
933862c0cd | ||
|
|
d4d87b029f | ||
|
|
ed6a7d10c2 | ||
|
|
6e36e81748 | ||
|
|
f3a50ce6f7 | ||
|
|
c8c9f7c7d9 | ||
|
|
7eeecc34d2 | ||
|
|
c8448215c7 | ||
|
|
050f00b3e5 | ||
|
|
b661e2329a | ||
|
|
8691358638 | ||
|
|
8116ed1f72 | ||
|
|
6ccc12acfe | ||
|
|
41a9d364a0 | ||
|
|
499a766252 | ||
|
|
af0f3dd04d | ||
|
|
3cd0bb8761 | ||
|
|
fa6d19f415 | ||
|
|
7fce362c17 | ||
|
|
fe93fce942 | ||
|
|
088df6eda6 | ||
|
|
62ed443234 | ||
|
|
49fd947437 | ||
|
|
d4eb4ee606 | ||
|
|
04c67e183d | ||
|
|
86ccf45f85 | ||
|
|
3f9febce36 | ||
|
|
140da78305 | ||
|
|
12c85b9418 | ||
|
|
4cee1951da | ||
|
|
548fde2f85 | ||
|
|
1ed6ac5c48 | ||
|
|
4b1fa0c7c6 | ||
|
|
d0609b0e77 | ||
|
|
0cc317e9f1 | ||
|
|
8dad1ef1af | ||
|
|
e81a26600f | ||
|
|
18a495ec39 | ||
|
|
713c88518f | ||
|
|
a7fa58f620 | ||
|
|
50e042c90d | ||
|
|
b2c02f7892 | ||
|
|
b91e09f170 | ||
|
|
68cd8d1966 | ||
|
|
fa28d1513c | ||
|
|
14f93ddd71 | ||
|
|
da81242d9b | ||
|
|
89c50fc032 | ||
|
|
c81446eff5 | ||
|
|
d5f96ff5a3 | ||
|
|
db3449ace1 | ||
|
|
1dddebbb1f | ||
|
|
77e2da3f2d | ||
|
|
019076e87f | ||
|
|
18e058af5b | ||
|
|
fe7b930b95 | ||
|
|
1a9be6aaf8 | ||
|
|
3110990b90 | ||
|
|
e5e2d49ae1 | ||
|
|
94381de60b | ||
|
|
70f20e6861 | ||
|
|
fab11f31d2 | ||
|
|
8af572d9f9 | ||
|
|
0c542af0ab | ||
|
|
4b3aae675d | ||
|
|
532d3bd6f0 | ||
|
|
cb20d65c2c | ||
|
|
07b6017451 | ||
|
|
2c5f8ca4f3 | ||
|
|
cd543b1fd4 | ||
|
|
88560b6626 | ||
|
|
1cf5e339c9 | ||
|
|
4ac67508f4 | ||
|
|
d2044c4636 | ||
|
|
59629228f9 | ||
|
|
eeb4272d49 | ||
|
|
e8ae8557d2 | ||
|
|
f0ab10ac09 | ||
|
|
b7b0409cd5 | ||
|
|
13963fbdd2 | ||
|
|
9b9d02b9de | ||
|
|
ec65f684bb | ||
|
|
022b1fdbbd | ||
|
|
ff121c7e1b | ||
|
|
a04380c1c1 | ||
|
|
3646b36db5 | ||
|
|
aaaa28953b | ||
|
|
9609101870 | ||
|
|
afb601a303 | ||
|
|
c72477ed7d | ||
|
|
dc09550e0c | ||
|
|
dc66e76dd1 | ||
|
|
cecef87d13 | ||
|
|
d0ca3a348f | ||
|
|
de4e309f39 | ||
|
|
ea56ac10ce | ||
|
|
90660c6d34 | ||
|
|
fcf04083a5 | ||
|
|
a1ea12a736 | ||
|
|
02a795bb8a | ||
|
|
6bffed8eb5 | ||
|
|
ddd5ceb74a | ||
|
|
3c6cee2f49 | ||
|
|
fc2f0c6f68 | ||
|
|
0dcf208315 | ||
|
|
7730834e83 | ||
|
|
91875ab232 | ||
|
|
d81e6d7813 | ||
|
|
3e299d85e9 | ||
|
|
455e1693f4 | ||
|
|
2a17e62107 | ||
|
|
0a090cf0a7 | ||
|
|
c13f0af435 | ||
|
|
8ee7995ddc | ||
|
|
0e8c749e68 | ||
|
|
ee0450ffb2 | ||
|
|
e0d8be6916 | ||
|
|
7c4d37a428 | ||
|
|
eeaebad903 | ||
|
|
c624e7e607 | ||
|
|
0f724a20cf | ||
|
|
c7922c4a0e | ||
|
|
9d98f72fd3 | ||
|
|
db9a256efa | ||
|
|
aedfdd4b91 | ||
|
|
99d5c51d42 | ||
|
|
086b733288 | ||
|
|
6f9ccaef9e | ||
|
|
dba92f52b5 | ||
|
|
61512fba55 | ||
|
|
542faafe04 | ||
|
|
70468eae8a | ||
|
|
9e0c2027d2 | ||
|
|
6ac4e965bb | ||
|
|
5edddb5890 | ||
|
|
706ac612ff | ||
|
|
444a56e333 | ||
|
|
2e966d717d | ||
|
|
237d856cfe | ||
|
|
d12c076774 | ||
|
|
3e3cc53715 | ||
|
|
ea4d30385a | ||
|
|
1b03871be8 | ||
|
|
42e068aab7 | ||
|
|
280efe4545 | ||
|
|
9b54c138a7 | ||
|
|
c253b7d60f | ||
|
|
367bba2692 | ||
|
|
e888c4e22c | ||
|
|
8a3425b5e3 | ||
|
|
9081bde65e | ||
|
|
4f783678da | ||
|
|
299a60a881 | ||
|
|
7e651219b0 | ||
|
|
e1e7bc6f57 | ||
|
|
beed8434ec | ||
|
|
ab13166c0a | ||
|
|
71a48cde3b | ||
|
|
c603ac0d1d | ||
|
|
316d3d98aa | ||
|
|
0465cf43db | ||
|
|
65de19ddb6 | ||
|
|
9c0f11cc55 | ||
|
|
754ceb3d5a | ||
|
|
dcc5247ba0 | ||
|
|
02de198a8b | ||
|
|
b7cfc4c6e3 | ||
|
|
46a094e711 | ||
|
|
b5cd1b2379 | ||
|
|
cfe94e6aa7 | ||
|
|
3362f25775 | ||
|
|
87f9704831 | ||
|
|
6c7084dfe1 | ||
|
|
164590159d | ||
|
|
b8ab46d26b | ||
|
|
183b4fc41a | ||
|
|
2b7ab34978 | ||
|
|
2ca7132569 | ||
|
|
aac9aa34f8 | ||
|
|
ad8a645462 | ||
|
|
2dbe4d6368 | ||
|
|
d76253ef75 | ||
|
|
46b68f1933 | ||
|
|
8aab27bc3f | ||
|
|
22f118e1b7 | ||
|
|
922eb6dc59 | ||
|
|
20526273d3 | ||
|
|
f3222c646b | ||
|
|
9ae28c5e85 | ||
|
|
e72318adb4 | ||
|
|
2682dc0504 | ||
|
|
8b4611b47a | ||
|
|
25938c60a6 | ||
|
|
9920f0dbb7 | ||
|
|
90f7d89db7 | ||
|
|
6ddbdbbbb2 | ||
|
|
3c8c9fd5ef | ||
|
|
91a9bf82fd | ||
|
|
4603ac77d8 | ||
|
|
998940af1b | ||
|
|
24ed724eab | ||
|
|
33b71628ab | ||
|
|
2dbf1429ba | ||
|
|
9867c1ee9d | ||
|
|
e8a0539984 | ||
|
|
685e8ba32a | ||
|
|
6c9493df7b | ||
|
|
1994438792 | ||
|
|
60be3e354b | ||
|
|
9d0bb5c996 | ||
|
|
304dc5fc36 | ||
|
|
819b6d7452 | ||
|
|
b066c8a5d7 | ||
|
|
80fb60c6fb | ||
|
|
6a2031899d | ||
|
|
aedcc2fd56 | ||
|
|
757eb403ac | ||
|
|
87c2745395 | ||
|
|
0d3d8ef54c | ||
|
|
53d5ee1960 | ||
|
|
92a8dfc55c | ||
|
|
c59f46cc1d | ||
|
|
ef3904a2f6 | ||
|
|
994c5844d9 | ||
|
|
3881e51b08 | ||
|
|
815ae1367a | ||
|
|
84ed47dbdf | ||
|
|
252980724a | ||
|
|
225aab476d | ||
|
|
f065539266 | ||
|
|
f8d81fc3f5 | ||
|
|
43e264cc07 | ||
|
|
957737959f | ||
|
|
000e6511e6 | ||
|
|
a652284bbc | ||
|
|
1b88e5ac06 | ||
|
|
79ba9ec6a1 | ||
|
|
a4acf09d8b | ||
|
|
872ef2e346 | ||
|
|
8795ae0aaa | ||
|
|
23c4190d7e | ||
|
|
75d0109102 | ||
|
|
893bb2d97a | ||
|
|
0c2c8c6c03 | ||
|
|
97a313ca1e | ||
|
|
3fd57e26b8 | ||
|
|
aee6f6d6bb | ||
|
|
00fd6e541a | ||
|
|
6cc1af4ca9 | ||
|
|
ed27df0fe2 | ||
|
|
14ebdf19ca | ||
|
|
e89a1b0c7e | ||
|
|
a1bd0ec7a5 | ||
|
|
82d3665606 | ||
|
|
67a289e750 | ||
|
|
1b0a7ed8ea | ||
|
|
21ba3e0640 | ||
|
|
d5159b914e | ||
|
|
c45a3d09d5 | ||
|
|
c3ea6f2b17 | ||
|
|
fb98d1febc | ||
|
|
036a9779eb | ||
|
|
beb89cf31b | ||
|
|
5d65a777bd | ||
|
|
115d108404 | ||
|
|
f573e16473 | ||
|
|
ac53e3a325 | ||
|
|
d3dad3b151 | ||
|
|
7843682b1e | ||
|
|
11c34a9edc | ||
|
|
923c3990f7 | ||
|
|
b091d40925 | ||
|
|
5c76c9d567 | ||
|
|
0ad9a98d33 | ||
|
|
b9fb7c80f2 | ||
|
|
d8f2e0977d | ||
|
|
f4e75f5e21 |
@@ -0,0 +1,174 @@
|
||||
本系列是 SQL 系列的开篇,介绍一些宏观与基础的内容。
|
||||
|
||||
## SQL 是什么?
|
||||
|
||||
SQL 是一种结构化查询语言,用于管理关系型数据库,我们 90% 接触的都是查询语法,但其实它包含完整的增删改查和事物处理功能。
|
||||
|
||||
## 声明式特性
|
||||
|
||||
SQL 属于声明式编程语言,而现代通用编程语言一般都是命令式的。但是不要盲目崇拜声明式语言,比如说它未来会代替低级的命令式语言,因为声明式本身也有它的缺点,它与命令式语言也有相通的地方。
|
||||
|
||||
为什么我们觉得声明式编程语言更高级?因为声明式语言抽象程度更高,比如 `select * from table1` 仅描述了要从 table1 查询数据,但查询的具体步骤的完全没提,这背后可能存在复杂的索引优化与锁机制,但我们都无需关心,这简直是编程的最高境界。
|
||||
|
||||
那为什么现在所有通用业务代码都是命令式呢?因为 **命令式给了我们描述具体实现的机会** ,而通用领域的编程正需要建立在严谨的实现细节上。比如校验用户权限这件事,即便 AI 编程提供了将 “登陆用户仅能访问有权限的资源” 转化为代码的能力,我们也不清楚资源具体指哪些,以及在权限转移过程中的资源所有权属于谁。
|
||||
|
||||
SQL 之所以能保留声明式特性,完全因为锁定了关系型数据管理这个特定领域,而恰恰对这个领域的需求是标准化且可枚举的,才使声明式成为可能。
|
||||
|
||||
基于命令式语言也完全可拓展出声明式能力,比如许多 ORM 提供了类似 `select({}).from({}).where({})` 之类的语法,甚至一个 `login()` 函数也是声明式编程的体现,因为调用者无需关心是如何登陆的,总之调用一下就完成了登陆,这不就是声明式的全部精髓吗?
|
||||
|
||||
## 语法分类
|
||||
|
||||
作为关系型数据库管理工具,SQL 需要定义、操纵与控制数据。
|
||||
|
||||
数据定义即修改数据库与表级别结构,这些是数据结构,或者是数据元信息,它不代表具体数据,但描述数据的属性。
|
||||
|
||||
数据操纵即修改一行行具体数据,增删改查。
|
||||
|
||||
数据控制即对事务、用户权限的管理与控制。
|
||||
|
||||
### 数据定义
|
||||
|
||||
DDL(Data Definition Language)数据定义,包括 `CREATE` `DROP` `ALTER` 方法。
|
||||
|
||||
### 数据操纵
|
||||
|
||||
DML(Data Manipulation Language)数据操纵,包括 `SELECT` `INSERT` `UPDATE` `DELETE` 方法。
|
||||
|
||||
### 数据控制
|
||||
|
||||
DCL(Data Control Language)数据控制,包括 `COMMIT`、`ROLLBACK` 等。
|
||||
|
||||
所有 SQL 操作都围绕这三种类型,其中数据操纵几乎占了 90% 的代码量,毕竟数据查询的诉求远大于写,数据写入对应数据采集,而数据查询对应数据分析,数据分析领域能玩出的花样远比数据采集要多。
|
||||
|
||||
PS:有些情况下,会把最重要的 `SELECT` 提到 DQL(Data Query Language)分类下,这样分类就变成了四个。
|
||||
|
||||
## 集合运算
|
||||
|
||||
SQL 世界的第一公民是集合,就像 JAVA 世界第一公民是对象。我们只有以集合的视角看待 SQL,才能更好的理解它。
|
||||
|
||||
何为集合视角,即所有的查询、操作都是二维数据结构中进行的,而非小学算术里的单个数字间加减乘除关系。
|
||||
|
||||
集合的运算一般有 `UNION` 并集、`EXCEPT` 差集、`INTERSECT` 交集,这些都是以行为单位的操作,而各种 JOIN 语句则是以列为单位的集合运算,也是后面提到的连接查询。
|
||||
|
||||
只要站在二维数据结构中进行思考,运算无非是横向或纵向的操作。
|
||||
|
||||
## 数据范式
|
||||
|
||||
数据范式分为五层,每层要求都比上一层更严苛,因此是一个可以逐步遵循的范式。数据范式要求数据越来越解耦,减少冗余。
|
||||
|
||||
比如第一范式要求每列都具有原子性,即都是不可分割的最小数据单元。如果数据采集时,某一列作为字符串存储,并且以 "|" 分割表示省市区,那么它就不具有原子性。
|
||||
|
||||
当然实际生产过程往往不都遵循这种标准,因为表不是孤立的,在数据处理流中,可能在某个环节再把列原子化,而原始数据为了压缩体积,进行列合并处理。
|
||||
|
||||
希望违反范式的还不仅是底层表,现在大数据处理场景下,越来越多的业务采用大宽表结构,甚至故意进行数据冗余以提升查询效率,列存储引擎就是针对这种场景设计的,所以数据范式在大数据场景下是可以变通的,但依然值得学习。
|
||||
|
||||
## 聚合
|
||||
|
||||
当采用 GROUP BY 分组聚合数据时,如希望针对聚合值筛选,就不能用 WHERE 限定条件了,因为 WHERE 是基于行的筛选,而不是针对组合的。(GROUP BY 对数据进行分组,我们称这些组为 “组合”),所以需要使用针对组合的筛选语句 HAVING:
|
||||
|
||||
```sql
|
||||
SELECT SUM(pv) FROM table
|
||||
GROUP BY city
|
||||
HAVING AVG(uv) > 100
|
||||
```
|
||||
|
||||
这个例子中,如果 HAVING 换成 WHERE 就没有意义,因为 WHERE 加聚合条件时,需要对所有数据进行合并,不符合当前视图的详细级别。(关于视图详细级别,在我之前写的 [精读《什么是 LOD 表达式》](https://github.com/ascoders/weekly/blob/master/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF/215.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%BB%80%E4%B9%88%E6%98%AF%20LOD%20%E8%A1%A8%E8%BE%BE%E5%BC%8F%E3%80%8B.md) 有详细说明)。
|
||||
|
||||
聚合如此重要,是因为我们分析数据必须在高 LEVEL 视角看,明细数据是看不出趋势的。而复杂的需求往往伴随着带有聚合的筛选条件,明白 SQL 是如何支持的非常重要。
|
||||
|
||||
## CASE 表达式
|
||||
|
||||
CASE 表达式分为简单与搜索 CASE 表达式,简单表达式:
|
||||
|
||||
```sql
|
||||
SELECT CASE pv WHEN 1 THEN 'low' ELSE 'high' END AS quality
|
||||
```
|
||||
|
||||
上面的例子利用 CASE 简单表达式形成了一个新字段,这种模式等于生成了业务自定义临时字段,在对当前表进行数据加工时非常有用。搜索 CASE 表达式能力完全覆盖简单 CASE 表达式:
|
||||
|
||||
```sql
|
||||
SELECT CASE WHEN pv < 100 THEN 'low' ELSE 'high' END AS quality
|
||||
```
|
||||
|
||||
可以看到,搜索 CASE 表达式可以用 “表达式” 描述条件,可以轻松完成更复杂的任务,甚至可以在表达式里使用子查询、聚合等手段,这些都是高手写 SQL 的惯用技巧,所以 CASE 表达式非常值得深入学习。
|
||||
|
||||
## 复杂查询
|
||||
|
||||
SELECT 是 SQL 最复杂的部分,其中就包含三种复杂查询模式,分别是连接查询与子查询。
|
||||
|
||||
### 连接查询
|
||||
|
||||
指 JOIN 查询,比如 LEFT JOIN、RIGHT JOIN、INNER JOIN。
|
||||
|
||||
在介绍聚合时我们提到了,连接查询本质上就是对列进行拓展,而两个表之间不会无缘无故合成一个,所以必须有一个外键作为关系纽带:
|
||||
|
||||
```sql
|
||||
SELECT A.pv, B.uv
|
||||
FROM table1 as t1 LEFT JOIN table2 AS P t2
|
||||
ON t1.productId = t2.productId
|
||||
```
|
||||
|
||||
连接查询不仅拓展了列,还会随之拓展行,而拓展方式与连接的查询的类型有关。除了连接查询别的表,还可以连接查询自己,比如:
|
||||
|
||||
```sql
|
||||
SELECT t1.pv AS pv1, P2.pv AS pv2
|
||||
FROM tt t1, tt t2
|
||||
```
|
||||
|
||||
这种子连接查询结果就是自己对自己的笛卡尔积,可通过 WHERE 筛选去重,后面会有文章专门介绍。
|
||||
|
||||
### 子查询与视图
|
||||
|
||||
子查询就是 SELECT 里套 SELECT,一般来说 SELECT 会从内到外执行,只有在关联子查询模式下,才会从外到内执行。
|
||||
|
||||
而如果把子查询保存下来,就是一个视图,这个视图并不是实体表,所以很灵活,且数据会随着原始表数据而变化:
|
||||
|
||||
```sql
|
||||
CREATE VIEW countryGDP (country, gdp)
|
||||
AS
|
||||
SELECT country, SUM(gdp)
|
||||
FROM tt
|
||||
GROUP BY country
|
||||
```
|
||||
|
||||
之后 `countryGDP` 这个视图就可以作为临时表来用了。
|
||||
|
||||
这种模式其实有点违背 SQL 声明式的特点,因为定义视图类似于定义变量,如果继续写下去,势必会形成一定命令式思维逻辑,但这是无法避免的。
|
||||
|
||||
## 事务
|
||||
|
||||
当 SQL 执行一连串操作时,难免遇到不执行完就会出现脏数据的问题,所以事务可以保证操作的原子性。一般来说每个 DML 操作都是一个内置事务,而 SQL 提供的 START TRANSACTION 就是让我们可以自定义事务范围,使一连串业务操作都可以包装在一起,成为一个原子性操作。
|
||||
|
||||
对 SQL 来说,原子性操作是非常安全的,即失败了不会留下任何痕迹,成功了会全部成功,不会存在中间态。
|
||||
|
||||
## OLAP
|
||||
|
||||
OLAP(OnLine Analytical Processing)即实时数据分析,是 BI 工具背后计算引擎实现的基础。
|
||||
|
||||
现在越来越多的 SQL 数据库支持了窗口函数实现,用于实现业务上的 runningSum 或 runningAvg 等功能,这些都是数据分析中很常见的。
|
||||
|
||||
以 runningSum 为例,比如双十一实时表的数据是以分钟为单位的实时 GMV,而我们要做一张累计到当前时间的 GMV 汇总折线图,Y 轴就需要支持 `running_sum(GMV)` 这样的表达式,而这背后可能就是通过窗口函数实现的。
|
||||
|
||||
当然也不是所有业务函数都由 SQL 直接提供,业务层仍需实现大量内存函数,在 JAVA 层计算,这其中一部分是需要下推到 SQL 执行的,只有内存函数与下推函数结合在一起,才能形成我们在 BI 工具看到的复杂计算字段效果。
|
||||
|
||||
## 总结
|
||||
|
||||
SQL 是一种声明式语言,一个看似简单的查询语句,在引擎层往往对应着复杂的实现,这就是 SQL 为何如此重要却又如此普及的原因。
|
||||
|
||||
虽然 SQL 容易上手,但要系统的理解它,还得从结构化数据与集合的概念开始进行思想转变。
|
||||
|
||||
不要小看 CASE 语法,它不仅与容易与编程语言的 CASE 语法产生混淆,本身结合表达式进行条件分支判断,是许多数据分析师在日常工作中最长用的套路。
|
||||
|
||||
现在使用简单 SQL 创建应用的场景越来越少了,但 BI 场景下,基于 SQL 的增强表达式场景越来越多了,本系列我就是以理解 BI 场景下查询表达式为目标创建的,希望能够学以致用。
|
||||
|
||||
> 讨论地址是:[精读《SQL 入门》· Issue #398 · ascoders/weekly](https://github.com/ascoders/weekly/issues/398)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/ascoders/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
SQL 为什么要支持聚合查询呢?
|
||||
|
||||
这看上去是个幼稚的问题,但我们还是一步步思考一下。数据以行为粒度存储,最简单的 SQL 语句是 `select * from test`,拿到的是整个二维表明细,但仅做到这一点远远不够,出于以下两个目的,需要 SQL 提供聚合函数:
|
||||
|
||||
1. 明细数据没有统计意义,比如我想知道今天的营业额一共有多少,而不太关心某桌客人消费了多少。
|
||||
2. 虽然可以先把数据查到内存中再聚合,但在数据量非常大的情况下很容易把内存撑爆,可能一张表一天的数据量就有 10TB,而 10TB 数据就算能读到内存里,聚合计算可能也会慢到难以接受。
|
||||
|
||||
另外聚合本身也有一定逻辑复杂度,而 SQL 提供了聚合函数与分组聚合能力,可以方便快速的统计出有业务价值的聚合数据,这奠定了 SQL 语言的分析价值,因此大部分分析软件直接采用 SQL 作为直接面向用户的表达式。
|
||||
|
||||
## 聚合函数
|
||||
|
||||
常见的聚合函数有:
|
||||
|
||||
- COUNT:计数。
|
||||
- SUM:求和。
|
||||
- AVG:求平均值。
|
||||
- MAX:求最大值。
|
||||
- MIN:求最小值。
|
||||
|
||||
### COUNT
|
||||
|
||||
COUNT 用来计算有多少条数据,比如我们看 id 这一列有多少条:
|
||||
|
||||
```sql
|
||||
SELECT COUNT(id) FROM test
|
||||
```
|
||||
|
||||
但我们发现其实查任何一列的 COUNT 都是一样的,那传入 id 有什么意义呢?没必要特殊找一个具体列指代呀,所以也可以写成:
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*) FROM test
|
||||
```
|
||||
|
||||
但这两者存在微妙差异。SQL 存在一种很特殊的值类型 `NULL`,如果 COUNT 指定了具体列,则统计时会跳过此列值为 `NULL` 的行,而 `COUNT(*)` 由于未指定具体列,所以就算包含了 `NULL`,甚至某一行所有列都为 `NULL`,也都会包含进来。所以 `COUNT(*)` 查出的结果一定大于等于 `COUNT(c1)`。
|
||||
|
||||
当然任何聚合函数都可以跟随查询条件 WHERE,比如:
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*) FROM test
|
||||
WHERE is_gray = 1
|
||||
```
|
||||
|
||||
### SUM
|
||||
|
||||
SUM 求和所有项,因此必须作用于数值字段,而不能用于字符串。
|
||||
|
||||
```sql
|
||||
SELECT SUM(cost) FROM test
|
||||
```
|
||||
|
||||
SUM 遇到 NULL 值时当 0 处理,因为这等价于忽略。
|
||||
|
||||
### AVG
|
||||
|
||||
AVG 求所有项均值,因此必须作用于数值字段,而不能用于字符串。
|
||||
|
||||
```sql
|
||||
SELECT AVG(cost) FROM test
|
||||
```
|
||||
|
||||
AVG 遇到 NULL 值时采用了最彻底的忽略方式,即 NULL 完全不参与分子与分母的计算,就像这一行数据不存在一样。
|
||||
|
||||
### MAX、MIN
|
||||
|
||||
MAX、MIN 分别求最大与最小值,与上面不同的是,也可以作用于字符串上,因此可以根据字母判断大小,从大到小依次对应 `a-z`,但即便能算,也没有实际意义且不好理解,因此不建议对字符串求极值。
|
||||
|
||||
```sql
|
||||
SELECT MAX(cost) FROM test
|
||||
```
|
||||
|
||||
### 多个聚合字段
|
||||
|
||||
虽然都是聚合函数,但 MAX、MIN 严格意义上不算是聚合函数,因为它们只是寻找了满足条件的行。可以看看下面两段查询结果的对比:
|
||||
|
||||
```sql
|
||||
SELECT MAX(cost), id FROM test -- id: 100
|
||||
SELECT SUM(cost), id FROM test -- id: 1
|
||||
```
|
||||
|
||||
第一条查询可以找到最大值那一行的 id,而第二条查询的 id 是无意义的,因为不知道归属在哪一行,所以只返回了第一条数据的 id。
|
||||
|
||||
当然,如果同时计算 MAX、MIN,那么此时 id 也只返回第一条数据的值,因为这个查询结果对应了复数行:
|
||||
|
||||
```sql
|
||||
SELECT MAX(cost), MIN(cost), id FROM test -- id: 1
|
||||
```
|
||||
|
||||
基于这些特性,最好不要混用聚合与非聚合,也就是一条查询一旦有一个字段是聚合的,那么所有字段都要聚合。
|
||||
|
||||
现在很多 BI 引擎的自定义字段都有这条限制,因为混用聚合与非聚合在自定义内存计算时处理起来边界情况很多,虽然 SQL 能支持,但业务自定义的函数可能不支持。
|
||||
|
||||
## 分组聚合
|
||||
|
||||
分组聚合就是 GROUP BY,其实可以把它当作一种高级的条件语句。
|
||||
|
||||
举个例子,查询每个国家的 GDP 总量:
|
||||
|
||||
```sql
|
||||
SELECT SUM(GDP) FROM amazing_table
|
||||
GROUP BY country
|
||||
```
|
||||
|
||||
返回的结果就会按照国家进行分组,这时,聚合函数就变成了在组内聚合。
|
||||
|
||||
其实如果我们只想看中、美的 GDP,用非分组也可以查,只是要分成两条 SQL:
|
||||
|
||||
```sql
|
||||
SELECT SUM(GDP) FROM amazing_table
|
||||
WHERE country = '中国'
|
||||
|
||||
SELECT SUM(GDP) FROM amazing_table
|
||||
WHERE country = '美国'
|
||||
```
|
||||
|
||||
所以 GROUP BY 也可理解为,将某个字段的所有可枚举的情况都查了出来,并整合成一张表,每一行代表了一种枚举情况,不需要分解为一个个 WHERE 查询了。
|
||||
|
||||
### 多字段分组聚合
|
||||
|
||||
GROUP BY 可以对多个维度使用,含义等价于表格查询时行/列拖入多个维度。
|
||||
|
||||
上面是 BI 查询工具视角,如果没有上下文,可以看下面这个递进描述:
|
||||
|
||||
- 按照多个字段进行分组聚合。
|
||||
- 多字段组合起来成为唯一 Key,即 `GROUP BY a,b` 表示 a,b 合在一起描述一个组。
|
||||
- `GROUP BY a,b,c` 查询结果第一列可能看到许多重复的 a 行,第二列看到重复 b 行,但在同一个 a 值内不会重复,c 在 b 行中同理。
|
||||
|
||||
下面是一个例子:
|
||||
|
||||
```sql
|
||||
SELECT SUM(GDP) FROM amazing_table
|
||||
GROUP BY province, city, area
|
||||
```
|
||||
|
||||
查询结果为:
|
||||
|
||||
```text
|
||||
浙江 杭州 余杭区
|
||||
浙江 杭州 西湖区
|
||||
浙江 宁波 海曙区
|
||||
浙江 宁波 江北区
|
||||
北京 .........
|
||||
```
|
||||
|
||||
### GROUP BY + WHERE
|
||||
|
||||
WHERE 是根据行进行条件筛选的。因此 GROUP BY + WHERE 并不是在组内做筛选,而是对整体做筛选。
|
||||
|
||||
但由于按行筛选,其实组内或非组内结果都完全一样,所以我们几乎无法感知这种差异:
|
||||
|
||||
```sql
|
||||
SELECT SUM(GDP) FROM amazing_table
|
||||
GROUP BY province, city, area
|
||||
WHERE industry = 'internet'
|
||||
```
|
||||
|
||||
然而,忽略这个差异会导致我们在聚合筛选时碰壁。
|
||||
|
||||
比如要筛选出平均分大于 60 学生的成绩总和,如果不使用子查询,是无法在普通查询中在 WHERE 加聚合函数实现的,比如下面就是一个语法错误的例子:
|
||||
|
||||
```sql
|
||||
SELECT SUM(score) FROM amazing_table
|
||||
WHERE AVG(score) > 60
|
||||
```
|
||||
|
||||
不要幻想上面的 SQL 可以执行成功,不要在 WHERE 里使用聚合函数。
|
||||
|
||||
### GROUP BY + HAVING
|
||||
|
||||
HAVING 是根据组进行条件筛选的。因此可以在 HAVING 使用聚合函数:
|
||||
|
||||
```sql
|
||||
SELECT SUM(score) FROM amazing_table
|
||||
GROUP BY class_name
|
||||
HAVING AVG(score) > 60
|
||||
```
|
||||
|
||||
上面的例子中可以正常查询,表示按照班级分组看总分,且仅筛选出平均分大于 60 的班级。
|
||||
|
||||
所以为什么 HAVING 可以使用聚合条件呢?因为 HAVING 筛选的是组,所以可以对组聚合后过滤掉不满足条件的组,这样是有意义的。而 WHERE 是针对行粒度的,聚合后全表就只有一条数据,无论过滤与否都没有意义。
|
||||
|
||||
但要注意的是,GROUP BY 生成派生表是无法利用索引筛选的,所以 WHERE 可以利用给字段建立索引优化性能,而 HAVING 针对索引字段不起作用。
|
||||
|
||||
## 总结
|
||||
|
||||
聚合函数 + 分组可以实现大部分简单 SQL 需求,在写 SQL 表达式时,需要思考这样的表达式是如何计算的,比如 `MAX(c1), c2` 是合理的,而 `SUM(c1), c2` 这个 `c2` 就是无意义的。
|
||||
|
||||
最后记住 WHERE 是 GROUP BY 之前执行的,HAVING 针对组进行筛选。
|
||||
|
||||
> 讨论地址是:[精读《SQL 聚合查询》· Issue #401 · ascoders/weekly](https://github.com/ascoders/weekly/issues/401)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/ascoders/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
SQL 复杂查询指的就是子查询。
|
||||
|
||||
为什么子查询叫做复杂查询呢?因为子查询相当于查询嵌套查询,因为嵌套导致复杂度几乎可以被无限放大(无限嵌套),因此叫复杂查询。下面是一个最简单的子查询例子:
|
||||
|
||||
```sql
|
||||
SELECT pv FROM (
|
||||
SELECT pv FROM test
|
||||
)
|
||||
```
|
||||
|
||||
上面的例子等价于 `SELECT pv FROM test`,但因为把表的位置替换成了一个新查询,所以摇身一变成为了复杂查询!所以复杂查询不一定真的复杂,甚至可能写出和普通查询等价的复杂查询,要避免这种无意义的行为。
|
||||
|
||||
我们也要借此机会了解为什么子查询可以这么做。
|
||||
|
||||
### 理解查询的本质
|
||||
|
||||
当我们查一张表时,数据库认为我们在查什么?
|
||||
|
||||
这点很重要,因为下面两个语句都是合法的:
|
||||
|
||||
```sql
|
||||
SELECT pv FROM test
|
||||
|
||||
SELECT pv FROM (
|
||||
SELECT pv FROM test
|
||||
)
|
||||
```
|
||||
|
||||
为什么数据库可以把子查询当作表呢?为了统一理解这些概念,我们有必要对查询内容进行抽象理解:**任意查询位置都是一条或多条记录**。
|
||||
|
||||
比如 `test` 这张表,显然是多条记录(当然只有一行就是一条记录),而 `SELECT pv FROM test` 也是多条记录,然而因为 `FROM` 后面可以查询任意条数的记录,所以这两种语法都支持。
|
||||
|
||||
不仅是 `FROM` 可以跟单条或多条记录,甚至 `SELECT`、`GROUP BY`、`WHERE`、`HAVING` 后都可以跟多条记录,这个后面再说。
|
||||
|
||||
说到这,也就很好理解子查询的变种了,比如我们可以在子查询内使用 `WHERE` 或 `GROUP BY` 等等,因为无论如何,只要查询结果是多条记录就行了:
|
||||
|
||||
```sql
|
||||
SELECT sum(people) as allPeople, sum(gdp), city FROM (
|
||||
SELECT people, gdp, city FROM test
|
||||
GROUP BY city
|
||||
HAVING sum(gdp) > 10000
|
||||
)
|
||||
```
|
||||
|
||||
这个例子就有点业务含义了。子查询是从内而外执行的,因此我们先看内部的逻辑:按照城市分组,筛选出总 GDP 超过一万的所有地区的人口数量明细。外层查询再把人口数加总,这样就能对比每个 GDP 超过一万的地区,总人口和总 GDP 分别是多少,方便对这些重点城市做对比。
|
||||
|
||||
不过这个例子看起来还是不太自然,因为我们没必要写成复杂查询,其实简单查询也是等价的:
|
||||
|
||||
```sql
|
||||
SELECT sum(people) as allPeople, sum(gdp), city FROM test
|
||||
GROUP BY city
|
||||
HAVING sum(gdp) > 10000
|
||||
```
|
||||
|
||||
那为什么要多此一举呢?因为复杂查询的真正用法并不在这里。
|
||||
|
||||
### 视图
|
||||
|
||||
正因为子查询的存在,我们才可能以类似抽取变量的方式,抽取子查询,这个抽取出来的抽象就是视图:
|
||||
|
||||
```sql
|
||||
CREATE VIEW my_table(people, gdp, city)
|
||||
AS
|
||||
SELECT sum(people) as allPeople, sum(gdp), city FROM test
|
||||
GROUP BY city
|
||||
HAVING sum(gdp) > 10000
|
||||
|
||||
SELECT sum(people) as allPeople, sum(gdp), city FROM my_table
|
||||
```
|
||||
|
||||
这样的好处是,这个视图可以被多条 SQL 语句复用,不仅可维护性变好了,执行时也仅需查询一次。
|
||||
|
||||
要注意的是,SELECT 可以使用任何视图,但 INSERT、DELETE、UPDATE 用于视图时,需要视图满足一下条件:
|
||||
|
||||
1. 未使用 DISTINCT 去重。
|
||||
2. FROM 单表。
|
||||
3. 未使用 GROUP BY 和 HAVING。
|
||||
|
||||
因为上面几种模式都会导致视图成为聚合后的数据,不方便做除了查以外的操作。
|
||||
|
||||
另外一个知识点就是物化视图,即使用 MATERIALIZED 描述视图:
|
||||
|
||||
```sql
|
||||
CREATE MATERIALIZED VIEW my_table(people, gdp, city)
|
||||
AS ...
|
||||
```
|
||||
|
||||
这种视图会落盘,为什么要支持这个特性呢?因为普通视图作为临时表,无法利用索引等优化手段,查询性能较低,所以物化视图是较为常见的性能优化手段。
|
||||
|
||||
说到性能优化手段,还有一些比较常见的理念,即把读的复杂度分摊到写的时候,比如提前聚合新表落盘或者对 CASE 语句固化为字段等,这里先不展开。
|
||||
|
||||
### 标量子查询
|
||||
|
||||
上面说了,WHERE 也可以跟子查询,比如:
|
||||
|
||||
```sql
|
||||
SELECT city FROM test
|
||||
WHERE gdp > (
|
||||
SELECT avg(gdp) from test
|
||||
)
|
||||
```
|
||||
|
||||
这样可以查询出 gdp 大于平均值的城市。
|
||||
|
||||
那为什么不能直接这么写呢?
|
||||
|
||||
```sql
|
||||
SELECT city FROM test
|
||||
WHERE gdp > avg(gdp) -- 报错,WHERE 无法使用聚合函数
|
||||
```
|
||||
|
||||
看上去很美好,但其实第一篇我们就介绍了,WHERE 不能跟聚合查询,因为这样会把整个父查询都聚合起来。那为什么子查询可以?因为子查询聚合的是子查询啊,父查询并没有被聚合,所以这才符合我们的意图。
|
||||
|
||||
所以上面例子不合适的地方在于,直接在当前查询使用 `avg(gdp)` 会导致聚合,而我们并不想聚合当前查询,但又要通过聚合拿到平均 GDP,所以就要使用子查询了!
|
||||
|
||||
回过头来看,为什么这一节叫标量子查询?标量即单一值,因为 `avg(gdp)` 聚合出来的只有一个值,所以 WHERE 可以把它当做一个单一数值使用。反之,如果子查询没有使用聚合函数,或 GROUP BY 分组,那么就不能使用 `WHERE >` 这种语法,但可以使用 `WHERE IN`,这涉及到单条与多条记录的思考,我们接着看下一节。
|
||||
|
||||
### 单条和多条记录
|
||||
|
||||
介绍标量子查询时说到了,`WHERE >` 的值必须时单一值。但其实 WHERE 也可以跟返回多条记录的子查询结果,只要使用合理的条件语句,比如 IN:
|
||||
|
||||
```sql
|
||||
SELECT area FROM test
|
||||
WHERE gdp IN (
|
||||
SELECT max(gdp) from test
|
||||
GROUP BY city
|
||||
)
|
||||
```
|
||||
|
||||
上面的例子,子查询按照城市分组,并找到每一组 GDP 最大的那条记录,所以如果数据粒度是区域,那么我们就查到了每个城市 GDP 最大的那些记录,然后父查询通过 WHERE IN 找到 gdp 符合的复数结果,所以最后就把每个城市最大 gdp 的区域列了出来。
|
||||
|
||||
但实际上 `WHERE >` 语句跟复数查询结果也不会报错,但没有任何意义,所以我们要理解查询结果是单条还是多条,在 WHERE 判断时选择合适的条件。WHERE 适合跟复数查询结果的语法有:`WHERE IN`、`WHERE SOME`、`WHERE ANY`。
|
||||
|
||||
### 关联子查询
|
||||
|
||||
所谓关联子查询,即父子查询间存在关联,既然如此,子查询肯定不能单独优先执行,毕竟和父查询存在关联嘛,所以关联子查询是先执行外层查询,再执行内层查询的。要注意的是,对每一行父查询,子查询都会执行一次,因此性能不高(当然 SQL 会对相同参数的子查询结果做缓存)。
|
||||
|
||||
那这个关联是什么呢?关联的是每一行父查询时,对子查询执行的条件。这么说可能有点绕,举个例子:
|
||||
|
||||
```sql
|
||||
SELECT * FROM test where gdp > (
|
||||
select avg(gdp) from test
|
||||
group by city
|
||||
)
|
||||
```
|
||||
|
||||
对这个例子来说,想要查找 gdp 大于按城市分组的平均 gdp,比如北京地区按北京比较,上海地区按上海比较。但很可惜这样做是不行的,因为父子查询没有关联,SQL 并不知道要按照相同城市比较,因此只要加一个 WHERE 条件,就变成关联子查询了:
|
||||
|
||||
```sql
|
||||
SELECT * FROM test as t1 where gdp > (
|
||||
select avg(gdp) from test as t2 where t1.city = t2.city
|
||||
group by city
|
||||
)
|
||||
```
|
||||
|
||||
就是在每次判断 `WHERE gdp >` 条件时,重新计算子查询结果,将平均值限定在相同的城市,这样就符合需求了。
|
||||
|
||||
## 总结
|
||||
|
||||
学会灵活运用父子查询,就掌握了复杂查询了。
|
||||
|
||||
SQL 第一公民是集合,所以所谓父子查询就是父子集合的灵活组合,这些集合可以出现在几乎任何位置,根据集合的数量、是否聚合、关联条件,就派生出了标量查询、关联子查询。
|
||||
|
||||
更深入的了解就需要大量实战案例了,但万变不离其宗,掌握了复杂查询后,就可以理解大部分 SQL 案例了。
|
||||
|
||||
> 讨论地址是:[精读《SQL 复杂查询》· Issue #403 · ascoders/weekly](https://github.com/ascoders/weekly/issues/403)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/ascoders/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
CASE 表达式分为简单表达式与搜索表达式,其中搜索表达式可以覆盖简单表达式的全部能力,我也建议只写搜索表达式,而不要写简单表达式。
|
||||
|
||||
简单表达式:
|
||||
|
||||
```sql
|
||||
SELECT CASE city
|
||||
WHEN '北京' THEN 1
|
||||
WHEN '天津' THEN 2
|
||||
ELSE 0
|
||||
END AS abc
|
||||
FROM test
|
||||
```
|
||||
|
||||
搜索表达式:
|
||||
|
||||
```sql
|
||||
SELECT CASE
|
||||
WHEN city = '北京' THEN 1
|
||||
WHEN city = '天津' THEN 2
|
||||
ELSE 0
|
||||
END AS abc
|
||||
FROM test
|
||||
```
|
||||
|
||||
明显可以看出,简单表达式只是搜索表达式 `a = b` 的特例,因为无法书写任何符号,只要条件换成 `a > b` 就无法胜任了,而搜索表达式不但可以轻松胜任,甚至可以写聚合函数。
|
||||
|
||||
## CASE 表达式里的聚合函数
|
||||
|
||||
为什么 CASE 表达式里可以写聚合函数?
|
||||
|
||||
因为本身表达式就支持聚合函数,比如下面的语法,我们不会觉得奇怪:
|
||||
|
||||
```sql
|
||||
SELECT sum(pv), avg(uv) from test
|
||||
```
|
||||
|
||||
本身 SQL 就支持多种不同的聚合方式同时计算,所以将其用在 CASE 表达式里,也是顺其自然的:
|
||||
|
||||
```sql
|
||||
SELECT CASE
|
||||
WHEN count(city) = 100 THEN 1
|
||||
WHEN sum(dau) > 200 THEN 2
|
||||
ELSE 0
|
||||
END AS abc
|
||||
FROM test
|
||||
```
|
||||
|
||||
只要 SQL 表达式中存在聚合函数,那么整个表达式都聚合了,此时访问非聚合变量没有任何意义。所以上面的例子,即便在 CASE 表达式中使用了聚合,其实也不过是聚合了一次后,按照条件进行判断罢了。
|
||||
|
||||
这个特性可以解决很多实际问题,比如将一些复杂聚合判断条件的结果用 SQL 结构输出,那么很可能是下面这种写法:
|
||||
|
||||
```sql
|
||||
SELECT CASE
|
||||
WHEN 聚合函数(字段) 符合什么条件 THEN xxx
|
||||
... 可能有 N 个
|
||||
ELSE NULL
|
||||
END AS abc
|
||||
FROM test
|
||||
```
|
||||
|
||||
这也可以认为是一种行转列的过程,即 **把行聚合后的结果通过一条条 CASE 表达式形成一个个新的列**。
|
||||
|
||||
## 聚合与非聚合不能混用
|
||||
|
||||
我们希望利用 CASE 表达式找出那些 pv 大于平均值的行,以下这种想当然的写法是错误的:
|
||||
|
||||
```sql
|
||||
SELECT CASE
|
||||
WHEN pv > avg(pv) THEN 'yes'
|
||||
ELSE 'no'
|
||||
END AS abc
|
||||
FROM test
|
||||
```
|
||||
|
||||
原因是,只要 SQL 中存在聚合表达式,那么整条 SQL 就都是聚合的,所以返回的结果只有一条,而我们期望查询结果不聚合,只是判断条件用到了聚合结果,那么就要使用子查询。
|
||||
|
||||
为什么子查询可以解决问题?因为子查询的聚合发生在子查询,而不影响当前父查询,理解了这一点,就知道为什么下面的写法才是正确的了:
|
||||
|
||||
```sql
|
||||
SELECT CASE
|
||||
WHEN pv > ( SELECT avg(pv) from test ) THEN 'yes'
|
||||
ELSE 'no'
|
||||
END AS abc
|
||||
FROM test
|
||||
```
|
||||
|
||||
这个例子也说明了 CASE 表达式里可以使用子查询,因为子查询是先计算的,所以查询结果在哪儿都能用,CASE 表达式也不例外。
|
||||
|
||||
## WHERE 中的 CASE
|
||||
|
||||
WHERE 后面也可以跟 CASE 表达式的,用来做一些需要特殊枚举处理的筛选。
|
||||
|
||||
比如下面的例子:
|
||||
|
||||
```sql
|
||||
SELECT * FROM demo WHERE
|
||||
CASE
|
||||
WHEN city = '北京' THEN true
|
||||
ELSE ID > 5
|
||||
END
|
||||
```
|
||||
|
||||
本来我们要查询 ID 大于 5 的数据,但我想对北京这个城市特别对待,那么就可以在判断条件中再进行 CASE 分支判断。
|
||||
|
||||
这个场景在 BI 工具里等价于,创建一个 CASE 表达式字段,可以拖入筛选条件生效。
|
||||
|
||||
## GROUP BY 中的 CASE
|
||||
|
||||
想不到吧,GROUP BY 里都可以写 CASE 表达式:
|
||||
|
||||
```sql
|
||||
SELECT isPower, sum(gdp) FROM test GROUP BY CASE
|
||||
WHEN isPower = 1 THEN city, area
|
||||
ELSE city
|
||||
END
|
||||
```
|
||||
|
||||
上面例子表示,计算 GDP 时,对于非常发达的城市,按照每个区粒度查看聚合结果,也就是看的粒度更细一些,而对于欠发达地区,本身 gdp 也不高,直接按照城市粒度看聚合结果。
|
||||
|
||||
这样,就按照不同的条件对数据进行了分组聚合。由于返回行结果是混在一起的,像这个例子,可以根据 isPower 字段是否为 1 判断,是否按照城市、区域进行了聚合,如果没有其他更显著的标识,可能导致无法区分不同行的聚合粒度,因此谨慎使用。
|
||||
|
||||
## ORDER BY 中的 CASE
|
||||
|
||||
同样,ORDER BY 使用 CASE 表达式,会将排序结果按照 CASE 分类进行分组,每组按照自己的规则排序,比如:
|
||||
|
||||
```sql
|
||||
SELECT * FROM test ORDER BY CASE
|
||||
WHEN isPower = 1 THEN gdp
|
||||
ELSE people
|
||||
END
|
||||
```
|
||||
|
||||
上面的例子,对发达地区采用 gdp 排序,否则采用人口数量排序。
|
||||
|
||||
## 总结
|
||||
|
||||
CASE 表达式总结一下有如下特点:
|
||||
|
||||
1. 支持简单与搜索两种写法,推荐搜索写法。
|
||||
2. 支持聚合与子查询,需要注意不同情况的特点。
|
||||
3. 可以写在 SQL 查询的几乎任何地方,只要是可以写字段的地方,基本上就可以替换为 CASE 表达式。
|
||||
4. 除了 SELECT 外,CASE 表达式还广泛应用在 INSERT 与 UPDATE,其中 UPDATE 的妙用是不用将 SQL 拆分为多条,所以不用担心数据变更后对判断条件的二次影响。
|
||||
|
||||
> 讨论地址是:[精读《SQL CASE 表达式》· Issue #404 · ascoders/weekly](https://github.com/ascoders/weekly/issues/404)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/ascoders/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
窗口函数形如:
|
||||
|
||||
```sql
|
||||
表达式 OVER (PARTITION BY 分组字段 ORDER BY 排序字段)
|
||||
```
|
||||
|
||||
有两个能力:
|
||||
|
||||
1. 当表达式为 `rank()` `dense_rank()` `row_number()` 时,拥有分组排序能力。
|
||||
2. 当表达式为 `sum()` 等聚合函数时,拥有累计聚合能力。
|
||||
|
||||
无论何种能力,**窗口函数都不会影响数据行数,而是将计算平摊在每一行**。
|
||||
|
||||
这两种能力需要区分理解。
|
||||
|
||||
## 底表
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/03/26/qdUL60.png">
|
||||
|
||||
以上是示例底表,共有 8 条数据,城市1、城市2 两个城市,下面各有地区1~4,每条数据都有该数据的人口数。
|
||||
|
||||
## 分组排序
|
||||
|
||||
如果按照人口排序,`ORDER BY people` 就行了,但如果我们想在城市内排序怎么办?
|
||||
|
||||
此时就要用到窗口函数的分组排序能力:
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/03/26/qddfIg.png">
|
||||
|
||||
```sql
|
||||
SELECT *, rank() over (PARTITION BY city ORDER BY people) FROM test
|
||||
```
|
||||
|
||||
该 SQL 表示在 city 组内按照 people 进行排序。
|
||||
|
||||
其实 PARTITION BY 也是可选的,如果我们忽略它:
|
||||
|
||||
```sql
|
||||
SELECT *, rank() over (ORDER BY people) FROM test
|
||||
```
|
||||
|
||||
也是生效的,但该语句与普通 ORDER BY 等价,因此利用窗口函数进行分组排序时,一般都会使用 PARTITION BY。
|
||||
|
||||
### 各分组排序函数的差异
|
||||
|
||||
我们将 `rank()` `dense_rank()` `row_number()` 的结果都打印出来:
|
||||
|
||||
```sql
|
||||
SELECT *,
|
||||
rank() over (PARTITION BY city ORDER BY people),
|
||||
dense_rank() over (PARTITION BY city ORDER BY people),
|
||||
row_number() over (PARTITION BY city ORDER BY people)
|
||||
FROM test
|
||||
```
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/03/26/qd0uh4.png">
|
||||
|
||||
其实从结果就可以猜到,这三个函数在处理排序遇到相同值时,对排名统计逻辑有如下差异:
|
||||
|
||||
1. `rank()`: 值相同时排名相同,但占用排名数字。
|
||||
2. `dense_rank()`: 值相同时排名相同,但不占用排名数字,整体排名更加紧凑。
|
||||
3. `row_number()`: 无论值是否相同,都强制按照行号展示排名。
|
||||
|
||||
上面的例子可以优化一下,因为所有窗口逻辑都是相同的,我们可以利用 WINDOW AS 提取为一个变量:
|
||||
|
||||
```sql
|
||||
SELECT *,
|
||||
rank() over wd, dense_rank() over wd, row_number() over wd
|
||||
FROM test
|
||||
WINDOW wd as (PARTITION BY city ORDER BY people)
|
||||
```
|
||||
|
||||
## 累计聚合
|
||||
|
||||
我们之前说过,凡事使用了聚合函数,都会让查询变成聚合模式。如果不用 GROUP BY,聚合后返回行数会压缩为一行,即使用了 GROUP BY,返回的行数一般也会大大减少,因为分组聚合了。
|
||||
|
||||
然而使用窗口函数的聚合却不会导致返回行数减少,那么这种聚合是怎么计算的呢?我们不如直接看下面的例子:
|
||||
|
||||
```sql
|
||||
SELECT *,
|
||||
sum(people) over (PARTITION BY city ORDER BY people)
|
||||
FROM test
|
||||
```
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/03/26/qdsDJS.png">
|
||||
|
||||
可以看到,在每个 city 分组内,按照 people 排序后进行了 **累加**(相同的值会合并在一起),这就是 BI 工具一般说的 RUNNGIN_SUM 的实现思路,当然一般我们排序规则使用绝对不会重复的日期,所以不会遇到第一个红框中合并计算的问题。
|
||||
|
||||
累计函数还有 `avg()` `min()` 等等,这些都一样可以作用于窗口函数,其逻辑可以按照下图理解:
|
||||
|
||||
<img width=400 src="https://s1.ax1x.com/2022/03/26/qd6or9.png">
|
||||
|
||||
你可能有疑问,直接 `sum(上一行结果,下一行)` 不是更方便吗?为了验证猜想,我们试试 `avg()` 的结果:
|
||||
|
||||
<img width=400 src="https://s1.ax1x.com/2022/03/26/qdciIP.png">
|
||||
|
||||
可见,如果直接利用上一行结果的缓存,那么 avg 结果必然是不准确的,所以窗口累计聚合是每行重新计算的。当然也不排除对于 sum、max、min 做额外性能优化的可能性,但 avg 只能每行重头计算。
|
||||
|
||||
### 与 GROUP BY 组合使用
|
||||
|
||||
窗口函数是可以与 GROUP BY 组合使用的,遵循的规则是,窗口范围对后面的查询结果生效,所以其实并不关心是否进行了 GROUP BY。我们看下面的例子:
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/03/26/qdgMOH.png">
|
||||
|
||||
按照地区分组后进行累加聚合,是对 GROUP BY 后的数据行粒度进行的,而不是之前的明细行。
|
||||
|
||||
## 总结
|
||||
|
||||
窗口函数在计算组内排序或累计 GVM 等场景非常有用,我们只要牢记两个知识点就行了:
|
||||
|
||||
1. 分组排序要结合 PARTITION BY 才有意义。
|
||||
2. 累计聚合作用于查询结果行粒度,支持所有聚合函数。
|
||||
|
||||
> 讨论地址是:[精读《SQL 窗口函数》· Issue #405 · ascoders/weekly](https://github.com/ascoders/weekly/issues/405)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/ascoders/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
SQL grouping 解决 OLAP 场景总计与小计问题,其语法分为几类,但要解决的是同一个问题:
|
||||
|
||||
ROLLUP 与 CUBE 是封装了规则的 GROUPING SETS,而 GROUPING SETS 则是最原始的规则。
|
||||
|
||||
为了方便理解,让我们从一个问题入手,层层递进吧。
|
||||
|
||||
## 底表
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/03/26/qdUL60.png">
|
||||
|
||||
以上是示例底表,共有 8 条数据,城市1、城市2 两个城市,下面各有地区1~4,每条数据都有该数据的人口数。
|
||||
|
||||
现在想计算人口总计,以及各城市人口小计。在没有掌握 grouping 语法前,我们只能通过两个 select 语句 union 后得到:
|
||||
|
||||
```sql
|
||||
SELECT city, sum(people) FROM test GROUP BY city
|
||||
union
|
||||
SELECT '合计' as city, sum(people) FROM test
|
||||
```
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/04/04/qbKPRs.png">
|
||||
|
||||
但两条 select 语句聚合了两次,性能是一个不小的开销,因此 SQL 提供了 GROUPING SETS 语法解决这个问题。
|
||||
|
||||
## GROUPING SETS
|
||||
|
||||
GROUP BY GROUPING SETS 可以指定任意聚合项,比如我们要同时计算总计与分组合计,就要按照空内容进行 GROUP BY 进行一次 sum,再按照 city 进行 GROUP BY 再进行一次 sum,换成 GROUPING SETS 描述就是:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
city, area,
|
||||
sum(people)
|
||||
FROM test
|
||||
GROUP BY GROUPING SETS((), (city, area))
|
||||
```
|
||||
|
||||
其中 `GROUPING SETS((), (city, area))` 表示分别按照 `()`、`(city, area)` 聚合计算总计。返回结果是:
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/04/04/qbRnWF.png">
|
||||
|
||||
可以看到,值为 NULL 的行就是我们要的总计,其值是没有任何 GROUP BY 限制算出来的。
|
||||
|
||||
类似的,我们还可以写 `GROUPING SETS((), (city), (city, area), (area))` 等任意数量、任意组合的 GROUP BY 条件。
|
||||
|
||||
通过这种规则计算的数据我们称为 “超级分组记录”。我们发现 “超级分组记录” 产生的 NULL 值很容易和真正的 NULL 值弄混,所以 SQL 提供了 GROUPING 函数解决这个问题。
|
||||
|
||||
## 函数 GROUPING
|
||||
|
||||
对于超级分组记录产生的 NULL,是可以被 `GROUPING()` 函数识别为 1 的:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
GROUPING(city),
|
||||
GROUPING(area),
|
||||
sum(people)
|
||||
FROM test
|
||||
GROUP BY GROUPING SETS((), (city, area))
|
||||
```
|
||||
|
||||
具体效果见下图:
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/04/04/qbRLpF.png">
|
||||
|
||||
可以看到,但凡是超级分组计算出来的字段都会识别为 1,我们利用之前学习的 [SQL CASE 表达式](https://github.com/ascoders/weekly/blob/master/SQL/234.SQL%20CASE%20%E8%A1%A8%E8%BE%BE%E5%BC%8F.md) 将其转换为总计、小计字样,就可以得出一张数据分析表了:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
CASE WHEN GROUPING(city) = 1 THEN '总计' ELSE city END,
|
||||
CASE WHEN GROUPING(area) = 1 THEN '小计' ELSE area END,
|
||||
sum(people)
|
||||
FROM test
|
||||
GROUP BY GROUPING SETS((), (city, area))
|
||||
```
|
||||
|
||||
<img width=500 src="https://s1.ax1x.com/2022/04/04/qbRz01.png">
|
||||
|
||||
然后前端表格展示时,将第一行 “总计”、“小计” 单元格合并为 “总计”,就完成了总计这个 BI 可视化分析功能。
|
||||
|
||||
## ROLLUP
|
||||
|
||||
ROLLUP 是卷起的意思,是一种特定规则的 GROUPING SETS,以下两种写法是等价的:
|
||||
|
||||
```sql
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY ROLLUP(city)
|
||||
|
||||
-- 等价于
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY GROUPING SETS((), (city))
|
||||
```
|
||||
|
||||
再看一组等价描述:
|
||||
|
||||
```sql
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY ROLLUP(city, area)
|
||||
|
||||
-- 等价于
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY GROUPING SETS((), (city), (city, area))
|
||||
```
|
||||
|
||||
发现规律了吗?ROLLUP 会按顺序把 GROUP BY 内容 “一个个卷起来”。用 GROUPING 函数判断超级分组记录对 ROLLUP 同样适用。
|
||||
|
||||
## CUBE
|
||||
|
||||
CUBE 又有所不同,它对内容进行了所有可能性展开(所以叫 CUBE)。
|
||||
|
||||
类比上面的例子,我们再写两组等价的展开:
|
||||
|
||||
```sql
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY CUBE(city)
|
||||
|
||||
-- 等价于
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY GROUPING SETS((), (city))
|
||||
```
|
||||
|
||||
上面的例子因为只有一项还看不出来,下面两项分组就能看出来了:
|
||||
|
||||
```sql
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY CUBE(city, area)
|
||||
|
||||
-- 等价于
|
||||
SELECT sum(people) FROM test
|
||||
GROUP BY GROUPING SETS((), (city), (area), (city, area))
|
||||
```
|
||||
|
||||
所谓 CUBE,是一种多维形状的描述,二维时有 2^1 种展开,三维时有 2^2 种展开,四维、五维依此类推。可以想象,如果用 CUBE 描述了很多组合,复杂度会爆炸。
|
||||
|
||||
## 总结
|
||||
|
||||
学习了 GROUPING 语法,以后前端同学的你不会再纠结这个问题了吧:
|
||||
|
||||
> 产品开启了总计、小计,我们是额外取一次数还是放到一起获取啊?
|
||||
|
||||
这个问题的标准答案和原理都在这篇文章里了。PS:对于不支持 GROUPING 语法数据库,要想办法屏蔽,就像前端 polyfill 一样,是一种降级方案。至于如何屏蔽,参考文章开头提到的两个 SELECT + UNION。
|
||||
|
||||
> 讨论地址是:[精读《SQL grouping》· Issue #406 · ascoders/weekly](https://github.com/ascoders/weekly/issues/406)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/ascoders/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 487 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 818 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 556 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 414 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 203 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 396 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 392 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 294 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 312 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 62 KiB |
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 发布辅助脚本
|
||||
* @author 黄子毅
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const dirs = [
|
||||
"前沿技术",
|
||||
"设计模式",
|
||||
"编译原理",
|
||||
"源码解读",
|
||||
"商业思考",
|
||||
"算法",
|
||||
"SQL"
|
||||
];
|
||||
|
||||
dirs.forEach((dir) => {
|
||||
const readDir = fs.readdirSync(`./${dir}`);
|
||||
|
||||
console.log(`### ${dir}\n`);
|
||||
|
||||
readDir
|
||||
.sort((left, right) => left.split(".")[0] - right.split(".")[0])
|
||||
.forEach((dirName) => {
|
||||
console.log(
|
||||
`- <a href="./${dir}/${encodeURIComponent(dirName)}">${dirName.replace(
|
||||
".md",
|
||||
""
|
||||
)}</a>`
|
||||
);
|
||||
});
|
||||
|
||||
console.log("");
|
||||
});
|
||||
@@ -17,6 +17,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/dt-fe/weekly#readme",
|
||||
"dependencies": {
|
||||
"esm": "^3.2.25",
|
||||
"husky": "^3.0.4",
|
||||
"lint-md-cli": "^0.1.1"
|
||||
},
|
||||
@@ -25,4 +26,4 @@
|
||||
"pre-commit": "npx lint-md ./"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,282 @@
|
||||
# 前端精读
|
||||
|
||||
<a href="https://travis-ci.org/dt-fe/weekly">
|
||||
<img src="https://travis-ci.org/dt-fe/weekly.svg?branch=v2" alt="CircleCI Status">
|
||||
<a href="https://travis-ci.org/ascoders/weekly">
|
||||
<img src="https://travis-ci.org/ascoders/weekly.svg?branch=v2" alt="CircleCI Status">
|
||||
</a>
|
||||
|
||||
前端界的好文精读,每周更新!
|
||||
|
||||
- [周刊参考池](https://github.com/dt-fe/weekly/issues/2)
|
||||
最新精读:<a href="./前沿技术/239.%E7%B2%BE%E8%AF%BB%E3%80%8AJS%20%E6%95%B0%E7%BB%84%E7%9A%84%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0%E3%80%8B.md">239.精读《JS 数组的内部实现》</a>
|
||||
|
||||
素材来源:[周刊参考池](https://github.com/ascoders/weekly/issues/2)
|
||||
|
||||
现已涵盖:
|
||||
|
||||
- 结合大厂工作经验解读的 [前沿技术](./前沿技术),[源码解读](./源码解读)。
|
||||
- 逐渐加入一些后端技术解读。
|
||||
- 一些 [商业思考](./商业思考)。
|
||||
- 已完成 [编译原理](./编译原理)、[设计模式](./设计模式) 两大基础模块。
|
||||
|
||||
### 前沿技术
|
||||
|
||||
- <a href="./前沿技术/1.%E7%B2%BE%E8%AF%BB%E3%80%8Ajs%20%E6%A8%A1%E5%9D%97%E5%8C%96%E5%8F%91%E5%B1%95%E3%80%8B.md">1.精读《js 模块化发展》</a>
|
||||
- <a href="./前沿技术/2.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%A8%A1%E6%80%81%E6%A1%86%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E3%80%8B.md">2.精读《模态框的最佳实践》</a>
|
||||
- <a href="./前沿技术/3.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E5%90%8E%E7%AB%AF%E6%B8%B2%E6%9F%93%E4%B9%8B%E4%BA%89%E3%80%8B.md">3.精读《前后端渲染之争》</a>
|
||||
- <a href="./前沿技术/4.%E7%B2%BE%E8%AF%BB%E3%80%8AAsyncAwait%20%E4%BC%98%E8%B6%8A%E4%B9%8B%E5%A4%84%E3%80%8B.md">4.精读《AsyncAwait 优越之处》</a>
|
||||
- <a href="./前沿技术/5.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%B0%91%E5%B7%A5%E5%8F%94%E5%8D%95%E9%A1%B5%E6%95%B0%E6%8D%AE%E6%B5%81%E6%96%B9%E6%A1%88%E3%80%8B.md">5.精读《民工叔单页数据流方案》</a>
|
||||
- <a href="./前沿技术/6.%E7%B2%BE%E8%AF%BB%E3%80%8AJavaScript%20%E9%94%99%E8%AF%AF%E5%A0%86%E6%A0%88%E5%A4%84%E7%90%86%E3%80%8B.md">6.精读《JavaScript 错误堆栈处理》</a>
|
||||
- <a href="./前沿技术/7.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AF%B7%E5%81%9C%E6%AD%A2%20css-in-js%20%E7%9A%84%E8%A1%8C%E4%B8%BA%E3%80%8B.md">7.精读《请停止 css-in-js 的行为》</a>
|
||||
- <a href="./前沿技术/8.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%85%A5%E5%9D%91%20React%20%E5%89%8D%E6%B2%A1%E6%9C%89%E4%BA%BA%E4%BC%9A%E5%91%8A%E8%AF%89%E4%BD%A0%E7%9A%84%E4%BA%8B%E3%80%8B.md">8.精读《入坑 React 前没有人会告诉你的事》</a>
|
||||
- <a href="./前沿技术/9.%E7%B2%BE%E8%AF%BB%E3%80%8AImmutable%20%E7%BB%93%E6%9E%84%E5%85%B1%E4%BA%AB%E3%80%8B.md">9.精读《Immutable 结构共享》</a>
|
||||
- <a href="./前沿技术/10.%E7%B2%BE%E8%AF%BB%E3%80%8AWeb%20Components%20%E7%9A%84%E5%9B%B0%E5%A2%83%E3%80%8B.md">10.精读《Web Components 的困境》</a>
|
||||
- <a href="./前沿技术/11.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E8%B0%83%E8%AF%95%E6%8A%80%E5%B7%A7%E3%80%8B.md">11.精读《前端调试技巧》</a>
|
||||
- <a href="./前沿技术/12.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6%E3%80%8B.md">12.精读《React 高阶组件》</a>
|
||||
- <a href="./前沿技术/13.%E7%B2%BE%E8%AF%BB%E3%80%8AThis%20%E5%B8%A6%E6%9D%A5%E7%9A%84%E5%9B%B0%E6%83%91%E3%80%8B.md">13.精读《This 带来的困惑》</a>
|
||||
- <a href="./前沿技术/14.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1%E4%B9%8B%20DCI%E3%80%8B.md">14.精读《架构设计之 DCI》</a>
|
||||
- <a href="./前沿技术/15.%E7%B2%BE%E8%AF%BB%E3%80%8ATC39%20%E4%B8%8E%20ECMAScript%20%E6%8F%90%E6%A1%88%E3%80%8B.md">15.精读《TC39 与 ECMAScript 提案》</a>
|
||||
- <a href="./前沿技术/16.%E7%B2%BE%E8%AF%BB%E3%80%8ACSS%20Animations%20vs%20Web%20Animations%20API%E3%80%8B.md">16.精读《CSS Animations vs Web Animations API》</a>
|
||||
- <a href="./前沿技术/17.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E5%AE%89%E5%85%A8%E5%9C%B0%E4%BD%BF%E7%94%A8%20React%20context%E3%80%8B.md">17.精读《如何安全地使用 React context》</a>
|
||||
- <a href="./前沿技术/18.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E5%AE%8C%E7%BE%8E%E7%9A%84%E6%97%A5%E6%9C%9F%E9%80%89%E6%8B%A9%E5%99%A8%E3%80%8B.md">18.精读《设计完美的日期选择器》</a>
|
||||
- <a href="./前沿技术/19.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%9C%80%E4%BD%B3%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98%E3%80%8B%E5%8F%8A%E9%9D%A2%E8%AF%95%E5%AE%98%E6%8A%80%E5%B7%A7.md">19.精读《最佳前端面试题》及面试官技巧</a>
|
||||
- <a href="./前沿技术/20.%E7%B2%BE%E8%AF%BB%E3%80%8ANestjs%E3%80%8B%E6%96%87%E6%A1%A3.md">20.精读《Nestjs》文档</a>
|
||||
- <a href="./前沿技术/21.%E7%B2%BE%E8%AF%BB%E3%80%8AWeb%20fonts%3A%20when%20you%20need%20them%2C%20when%20you%20don%E2%80%99t%E3%80%8B.md">21.精读《Web fonts: when you need them, when you don’t》</a>
|
||||
- <a href="./前沿技术/22.%E7%B2%BE%E8%AF%BB%E3%80%8AV8%20%E5%BC%95%E6%93%8E%E7%89%B9%E6%80%A7%E5%B8%A6%E6%9D%A5%E7%9A%84%E7%9A%84%20JS%20%E6%80%A7%E8%83%BD%E5%8F%98%E5%8C%96%E3%80%8B.md">22.精读《V8 引擎特性带来的的 JS 性能变化》</a>
|
||||
- <a href="./前沿技术/23.%E7%B2%BE%E8%AF%BB%E3%80%8AAPI%20%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99%E3%80%8B.md">23.精读《API 设计原则》</a>
|
||||
- <a href="./前沿技术/24.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%8E%B0%E4%BB%A3%20JavaScript%20%E6%A6%82%E8%A7%88%E3%80%8B.md">24.精读《现代 JavaScript 概览》</a>
|
||||
- <a href="./前沿技术/25.%E7%B2%BE%E8%AF%BB%E3%80%8Anull%20%3E%3D%200%3F%E3%80%8B.md">25.精读《null >= 0?》</a>
|
||||
- <a href="./前沿技术/26.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%8A%A0%E5%AF%86%E5%AA%92%E4%BD%93%E6%89%A9%E5%B1%95%E3%80%8B.md">26.精读《加密媒体扩展》</a>
|
||||
- <a href="./前沿技术/27.%E7%B2%BE%E8%AF%BB%E3%80%8Acss-in-js%20%E6%9D%80%E9%B8%A1%E7%94%A8%E7%89%9B%E5%88%80%E3%80%8B.md">27.精读《css-in-js 杀鸡用牛刀》</a>
|
||||
- <a href="./前沿技术/28.%E7%B2%BE%E8%AF%BB%E3%80%8A2017%20%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%A4%87%E5%BF%98%E5%BD%95%E3%80%8B.md">28.精读《2017 前端性能优化备忘录》</a>
|
||||
- <a href="./前沿技术/29.%E7%B2%BE%E8%AF%BB%E3%80%8AJS%20%E4%B8%AD%E7%9A%84%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E3%80%8B.md">29.精读《JS 中的内存管理》</a>
|
||||
- <a href="./前沿技术/30.%E7%B2%BE%E8%AF%BB%E3%80%8AJavascript%20%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF%E4%B8%8E%E5%BC%82%E6%AD%A5%E3%80%8B.md">30.精读《Javascript 事件循环与异步》</a>
|
||||
- <a href="./前沿技术/31.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%88%91%E4%B8%8D%E5%86%8D%E4%BD%BF%E7%94%A8%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6%E3%80%8B.md">31.精读《我不再使用高阶组件》</a>
|
||||
- <a href="./前沿技术/32.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Router4.0%20%E8%BF%9B%E9%98%B6%E6%A6%82%E5%BF%B5%E3%80%8B.md">32.精读《React Router4.0 进阶概念》</a>
|
||||
- <a href="./前沿技术/33.%E7%B2%BE%E8%AF%BB%E3%80%8A30%20%E8%A1%8C%20js%20%E4%BB%A3%E7%A0%81%E5%88%9B%E5%BB%BA%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E3%80%8B.md">33.精读《30 行 js 代码创建神经网络》</a>
|
||||
- <a href="./前沿技术/34.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93%E3%80%8B.md">34.精读《React 代码整洁之道》</a>
|
||||
- <a href="./前沿技术/35.%E7%B2%BE%E8%AF%BB%E3%80%8Adob%20-%20%E6%A1%86%E6%9E%B6%E5%AE%9E%E7%8E%B0%E3%80%8B.md">35.精读《dob - 框架实现》</a>
|
||||
- <a href="./前沿技术/36.%E7%B2%BE%E8%AF%BB%E3%80%8AWhen%20You%20%E2%80%9CGit%E2%80%9D%20in%20Trouble-%20a%20Version%20Control%20Story%E3%80%8B.md">36.精读《When You “Git” in Trouble- a Version Control Story》</a>
|
||||
- <a href="./前沿技术/37.%E7%B2%BE%E8%AF%BB%E3%80%8Ahow%20we%20position%20and%20what%20we%20compare%E3%80%8B.md">37.精读《how we position and what we compare》</a>
|
||||
- <a href="./前沿技术/38.%E7%B2%BE%E8%AF%BB%E3%80%8Adob%20-%20%E6%A1%86%E6%9E%B6%E4%BD%BF%E7%94%A8%E3%80%8B.md">38.精读《dob - 框架使用》</a>
|
||||
- <a href="./前沿技术/39.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%85%A8%E9%93%BE%E8%B7%AF%E4%BD%93%E9%AA%8C%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8C%96%E7%9F%BF%E3%80%8B.md">39.精读《全链路体验浏览器挖矿》</a>
|
||||
- <a href="./前沿技术/40.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%88%9D%E6%8E%A2%20Reason%20%E4%B8%8E%20GraphQL%E3%80%8B.md">40.精读《初探 Reason 与 GraphQL》</a>
|
||||
- <a href="./前沿技术/41.%E7%B2%BE%E8%AF%BB%E3%80%8AAnt%20Design%203.0%20%E8%83%8C%E5%90%8E%E7%9A%84%E6%95%85%E4%BA%8B%E3%80%8B.md">41.精读《Ant Design 3.0 背后的故事》</a>
|
||||
- <a href="./前沿技术/42.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E6%95%B0%E6%8D%AE%E6%B5%81%E5%93%B2%E5%AD%A6%E3%80%8B.md">42.精读《前端数据流哲学》</a>
|
||||
- <a href="./前沿技术/43.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A2%9E%E5%BC%BA%E7%8E%B0%E5%AE%9E%E4%B8%8E%E5%8F%AF%E8%A7%86%E5%8C%96%E3%80%8B.md">43.精读《增强现实与可视化》</a>
|
||||
- <a href="./前沿技术/44.%E7%B2%BE%E8%AF%BB%E3%80%8ARekit%20Studio%E3%80%8B.md">44.精读《Rekit Studio》</a>
|
||||
- <a href="./前沿技术/45.%E7%B2%BE%E8%AF%BB%E3%80%8AReact's%20new%20Context%20API%E3%80%8B.md">45.精读《React's new Context API》</a>
|
||||
- <a href="./前沿技术/46.%E7%B2%BE%E8%AF%BB%E3%80%8Areact-rxjs%E3%80%8B.md">46.精读《react-rxjs》</a>
|
||||
- <a href="./前沿技术/47.%E7%B2%BE%E8%AF%BB%E3%80%8Awebpack4.0%20%E5%8D%87%E7%BA%A7%E6%8C%87%E5%8D%97%E3%80%8B.md">47.精读《webpack4.0 升级指南》</a>
|
||||
- <a href="./前沿技术/49.%E7%B2%BE%E8%AF%BB%E3%80%8ACompilers%20are%20the%20New%20Frameworks%E3%80%8B.md">49.精读《Compilers are the New Frameworks》</a>
|
||||
- <a href="./前沿技术/50.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B%E6%9E%84%E5%BB%BA%20ARKit%20%E5%BA%94%E7%94%A8%E3%80%8B.md">50.精读《快速上手构建 ARKit 应用》</a>
|
||||
- <a href="./前沿技术/51.%E7%B2%BE%E8%AF%BB%E3%80%8AElements%20of%20Web%20Dev%E3%80%8B.md">51.精读《Elements of Web Dev》</a>
|
||||
- <a href="./前沿技术/52.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%9B%BE%E8%A7%A3%20ES%20%E6%A8%A1%E5%9D%97%E3%80%8B.md">52.精读《图解 ES 模块》</a>
|
||||
- <a href="./前沿技术/53.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%8F%92%E4%BB%B6%E5%8C%96%E6%80%9D%E7%BB%B4%E3%80%8B.md">53.精读《插件化思维》</a>
|
||||
- <a href="./前沿技术/54.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%9C%A8%E6%B5%8F%E8%A7%88%E5%99%A8%E8%BF%90%E8%A1%8C%20serverRender%E3%80%8B.md">54.精读《在浏览器运行 serverRender》</a>
|
||||
- <a href="./前沿技术/55.%E7%B2%BE%E8%AF%BB%E3%80%8Aasync%20await%20%E6%98%AF%E6%8A%8A%E5%8F%8C%E5%88%83%E5%89%91%E3%80%8B.md">55.精读《async await 是把双刃剑》</a>
|
||||
- <a href="./前沿技术/56.%E7%B2%BE%E8%AF%BB%E3%80%8A%E9%87%8D%E6%96%B0%E6%80%9D%E8%80%83%20Redux%E3%80%8B.md">56.精读《重新思考 Redux》</a>
|
||||
- <a href="./前沿技术/57.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%8E%B0%E4%BB%A3%20js%20%E6%A1%86%E6%9E%B6%E5%AD%98%E5%9C%A8%E7%9A%84%E6%A0%B9%E6%9C%AC%E5%8E%9F%E5%9B%A0%E3%80%8B.md">57.精读《现代 js 框架存在的根本原因》</a>
|
||||
- <a href="./前沿技术/58.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript2.0%20-%202.9%E3%80%8B.md">58.精读《Typescript2.0 - 2.9》</a>
|
||||
- <a href="./前沿技术/59.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E5%88%A9%E7%94%A8%20Nodejs%20%E7%9B%91%E5%90%AC%E6%96%87%E4%BB%B6%E5%A4%B9%E3%80%8B.md">59.精读《如何利用 Nodejs 监听文件夹》</a>
|
||||
- <a href="./前沿技术/60.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E5%9C%A8%20nodejs%20%E4%BD%BF%E7%94%A8%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E3%80%8B.md">60.精读《如何在 nodejs 使用环境变量》</a>
|
||||
- <a href="./前沿技术/61.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20%E5%85%AB%E7%A7%8D%E6%9D%A1%E4%BB%B6%E6%B8%B2%E6%9F%93%E3%80%8B.md">61.精读《React 八种条件渲染》</a>
|
||||
- <a href="./前沿技术/62.%E7%B2%BE%E8%AF%BB%E3%80%8AJS%20%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E4%B9%8B%20Shapes%20and%20Inline%20Caches%E3%80%8B.md">62.精读《JS 引擎基础之 Shapes and Inline Caches》</a>
|
||||
- <a href="./前沿技术/63.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20%E7%9A%84%E5%A4%9A%E6%80%81%E6%80%A7%E3%80%8B.md">63.精读《React 的多态性》</a>
|
||||
- <a href="./前沿技术/68.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%A1%A1%E9%87%8F%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C%E3%80%8B.md">68.精读《衡量用户体验》</a>
|
||||
- <a href="./前沿技术/69.%E7%B2%BE%E8%AF%BB%E3%80%8ASQL%20vs%20Flux%E3%80%8B.md">69.精读《SQL vs Flux》</a>
|
||||
- <a href="./前沿技术/72.%E7%B2%BE%E8%AF%BB%E3%80%8AREST%2C%20GraphQL%2C%20Webhooks%2C%20%26%20gRPC%20%E5%A6%82%E4%BD%95%E9%80%89%E5%9E%8B%E3%80%8B.md">72.精读《REST, GraphQL, Webhooks, & gRPC 如何选型》</a>
|
||||
- <a href="./前沿技术/74.%E7%B2%BE%E8%AF%BB%E3%80%8A12%20%E4%B8%AA%E8%AF%84%E4%BC%B0%20JS%20%E5%BA%93%E4%BD%A0%E9%9C%80%E8%A6%81%E5%85%B3%E5%BF%83%E7%9A%84%E4%BA%8B%E3%80%8B.md">74.精读《12 个评估 JS 库你需要关心的事》</a>
|
||||
- <a href="./前沿技术/76.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%B0%88%E8%B0%88%20Web%20Workers%E3%80%8B.md">76.精读《谈谈 Web Workers》</a>
|
||||
- <a href="./前沿技术/77.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%94%A8%20Reduce%20%E5%AE%9E%E7%8E%B0%20Promise%20%E4%B8%B2%E8%A1%8C%E6%89%A7%E8%A1%8C%E3%80%8B.md">77.精读《用 Reduce 实现 Promise 串行执行》</a>
|
||||
- <a href="./前沿技术/79.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Hooks%E3%80%8B.md">79.精读《React Hooks》</a>
|
||||
- <a href="./前沿技术/80.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%80%8E%E4%B9%88%E7%94%A8%20React%20Hooks%20%E9%80%A0%E8%BD%AE%E5%AD%90%E3%80%8B.md">80.精读《怎么用 React Hooks 造轮子》</a>
|
||||
- <a href="./前沿技术/81.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%BD%BF%E7%94%A8%20CSS%20%E5%B1%9E%E6%80%A7%E9%80%89%E6%8B%A9%E5%99%A8%E3%80%8B.md">81.精读《使用 CSS 属性选择器》</a>
|
||||
- <a href="./前沿技术/83.%E7%B2%BE%E8%AF%BB%E3%80%8AReact16%20%E6%96%B0%E7%89%B9%E6%80%A7%E3%80%8B.md">83.精读《React16 新特性》</a>
|
||||
- <a href="./前沿技术/84.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%203.2%20%E6%96%B0%E7%89%B9%E6%80%A7%E3%80%8B.md">84.精读《Typescript 3.2 新特性》</a>
|
||||
- <a href="./前沿技术/86.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%9B%BD%E9%99%85%E5%8C%96%E5%B8%83%E5%B1%80%20-%20Logical%20Properties%E3%80%8B.md">86.精读《国际化布局 - Logical Properties》</a>
|
||||
- <a href="./前沿技术/87.%E7%B2%BE%E8%AF%BB%E3%80%8AsetState%20%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%E3%80%8B.md">87.精读《setState 做了什么》</a>
|
||||
- <a href="./前沿技术/88.%E7%B2%BE%E8%AF%BB%E3%80%8ACaches%20API%E3%80%8B.md">88.精读《Caches API》</a>
|
||||
- <a href="./前沿技术/89.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E7%BC%96%E8%AF%91%E5%89%8D%E7%AB%AF%E9%A1%B9%E7%9B%AE%E4%B8%8E%E7%BB%84%E4%BB%B6%E3%80%8B.md">89.精读《如何编译前端项目与组件》</a>
|
||||
- <a href="./前沿技术/91.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%AD%A3%E5%88%99%20ES2018%E3%80%8B.md">91.精读《正则 ES2018》</a>
|
||||
- <a href="./前沿技术/94.%E7%B2%BE%E8%AF%BB%E3%80%8AServerless%20%E7%BB%99%E5%89%8D%E7%AB%AF%E5%B8%A6%E6%9D%A5%E4%BA%86%E4%BB%80%E4%B9%88%E3%80%8B.md">94.精读《Serverless 给前端带来了什么》</a>
|
||||
- <a href="./前沿技术/95.%E7%B2%BE%E8%AF%BB%E3%80%8AFunction%20VS%20Class%20%E7%BB%84%E4%BB%B6%E3%80%8B.md">95.精读《Function VS Class 组件》</a>
|
||||
- <a href="./前沿技术/96.%E7%B2%BE%E8%AF%BB%E3%80%8AuseEffect%20%E5%AE%8C%E5%85%A8%E6%8C%87%E5%8D%97%E3%80%8B.md">96.精读《useEffect 完全指南》</a>
|
||||
- <a href="./前沿技术/97.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%BC%96%E5%86%99%E6%9C%89%E5%BC%B9%E6%80%A7%E7%9A%84%E7%BB%84%E4%BB%B6%E3%80%8B.md">97.精读《编写有弹性的组件》</a>
|
||||
- <a href="./前沿技术/99.%E7%B2%BE%E8%AF%BB%E3%80%8AScheduling%20in%20React%E3%80%8B.md">99.精读《Scheduling in React》</a>
|
||||
- <a href="./前沿技术/100.%E7%B2%BE%E8%AF%BB%E3%80%8AV8%20%E5%BC%95%E6%93%8E%20Lazy%20Parsing%E3%80%8B.md">100.精读《V8 引擎 Lazy Parsing》</a>
|
||||
- <a href="./前沿技术/101.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90%20vs%20%E6%8C%81%E7%BB%AD%E4%BA%A4%E4%BB%98%20vs%20%E6%8C%81%E7%BB%AD%E9%83%A8%E7%BD%B2%E3%80%8B.md">101.精读《持续集成 vs 持续交付 vs 持续部署》</a>
|
||||
- <a href="./前沿技术/102.%E7%B2%BE%E8%AF%BB%E3%80%8AMonorepo%20%E7%9A%84%E4%BC%98%E5%8A%BF%E3%80%8B.md">102.精读《Monorepo 的优势》</a>
|
||||
- <a href="./前沿技术/104.%E7%B2%BE%E8%AF%BB%E3%80%8AFunction%20Component%20%E5%85%A5%E9%97%A8%E3%80%8B.md">104.精读《Function Component 入门》</a>
|
||||
- <a href="./前沿技术/105.%E7%B2%BE%E8%AF%BB%E3%80%8AWhat's%20new%20in%20javascript%E3%80%8B.md">105.精读《What's new in javascript》</a>
|
||||
- <a href="./前沿技术/107.%E7%B2%BE%E8%AF%BB%E3%80%8AOptional%20chaining%E3%80%8B.md">107.精读《Optional chaining》</a>
|
||||
- <a href="./前沿技术/109.%E7%B2%BE%E8%AF%BB%E3%80%8AVue3.0%20Function%20API%E3%80%8B.md">109.精读《Vue3.0 Function API》</a>
|
||||
- <a href="./前沿技术/111.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E6%9C%AA%E6%9D%A5%E5%B1%95%E6%9C%9B%E3%80%8B.md">111.精读《前端未来展望》</a>
|
||||
- <a href="./前沿技术/112.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E3%80%8B.md">112.精读《源码学习》</a>
|
||||
- <a href="./前沿技术/113.%E7%B2%BE%E8%AF%BB%E3%80%8ANodejs%20V12%E3%80%8B.md">113.精读《Nodejs V12》</a>
|
||||
- <a href="./前沿技术/117.%E7%B2%BE%E8%AF%BB%E3%80%8ATableau%20%E6%8E%A2%E7%B4%A2%E5%BC%8F%E6%A8%A1%E5%9E%8B%E3%80%8B.md">117.精读《Tableau 探索式模型》</a>
|
||||
- <a href="./前沿技术/118.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%BD%BF%E7%94%A8%20css%20%E5%8F%98%E9%87%8F%E7%94%9F%E6%88%90%E9%A2%9C%E8%89%B2%E4%B8%BB%E9%A2%98%E3%80%8B.md">118.精读《使用 css 变量生成颜色主题》</a>
|
||||
- <a href="./前沿技术/119.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E6%B7%B1%E6%B0%B4%E5%8C%BA%E3%80%8B.md">119.精读《前端深水区》</a>
|
||||
- <a href="./前沿技术/120.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Hooks%20%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5%E3%80%8B.md">120.精读《React Hooks 最佳实践》</a>
|
||||
- <a href="./前沿技术/121.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E4%B8%8E%20BI%E3%80%8B.md">121.精读《前端与 BI》</a>
|
||||
- <a href="./前沿技术/123.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%94%A8%20Babel%20%E5%88%9B%E9%80%A0%E8%87%AA%E5%AE%9A%E4%B9%89%20JS%20%E8%AF%AD%E6%B3%95%E3%80%8B.md">123.精读《用 Babel 创造自定义 JS 语法》</a>
|
||||
- <a href="./前沿技术/124.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%94%A8%20css%20grid%20%E9%87%8D%E6%96%B0%E6%80%9D%E8%80%83%E5%B8%83%E5%B1%80%E3%80%8B.md">124.精读《用 css grid 重新思考布局》</a>
|
||||
- <a href="./前沿技术/125.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%20-%20%E5%87%BD%E6%95%B0%E5%BC%8F%E4%B9%8B%E7%BE%8E%E3%80%8B.md">125.精读《深度学习 - 函数式之美》</a>
|
||||
- <a href="./前沿技术/126.%E7%B2%BE%E8%AF%BB%E3%80%8ANuxtjs%E3%80%8B.md">126.精读《Nuxtjs》</a>
|
||||
- <a href="./前沿技术/127.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Conf%202019%20-%20Day1%E3%80%8B.md">127.精读《React Conf 2019 - Day1》</a>
|
||||
- <a href="./前沿技术/129.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Conf%202019%20-%20Day2%E3%80%8B.md">129.精读《React Conf 2019 - Day2》</a>
|
||||
- <a href="./前沿技术/132.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%AD%A3%E4%BA%A4%E7%9A%84%20React%20%E7%BB%84%E4%BB%B6%E3%80%8B.md">132.精读《正交的 React 组件》</a>
|
||||
- <a href="./前沿技术/133.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%AF%BB%E6%89%BE%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1%E7%9A%84%E5%B9%B3%E8%A1%A1%E7%82%B9%E3%80%8B.md">133.精读《寻找框架设计的平衡点》</a>
|
||||
- <a href="./前沿技术/134.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%88%91%E5%9C%A8%E9%98%BF%E9%87%8C%E6%95%B0%E6%8D%AE%E4%B8%AD%E5%8F%B0%E5%A4%A7%E5%89%8D%E7%AB%AF%E3%80%8B.md">134.精读《我在阿里数据中台大前端》</a>
|
||||
- <a href="./前沿技术/138.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%B2%BE%E9%80%9A%20console.log%E3%80%8B.md">138.精读《精通 console.log》</a>
|
||||
- <a href="./前沿技术/139.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20JSON%20Parser%E3%80%8B.md">139.精读《手写 JSON Parser》</a>
|
||||
- <a href="./前沿技术/140.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%BB%93%E5%90%88%20React%20%E4%BD%BF%E7%94%A8%E5%8E%9F%E7%94%9F%20Drag%20Drop%20API%E3%80%8B.md">140.精读《结合 React 使用原生 Drag Drop API》</a>
|
||||
- <a href="./前沿技术/141.%E7%B2%BE%E8%AF%BB%E3%80%8AuseRef%20%E4%B8%8E%20createRef%20%E7%9A%84%E5%8C%BA%E5%88%AB%E3%80%8B.md">141.精读《useRef 与 createRef 的区别》</a>
|
||||
- <a href="./前沿技术/142.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E5%81%9A%E5%A5%BD%20CodeReview%E3%80%8B.md">142.精读《如何做好 CodeReview》</a>
|
||||
- <a href="./前沿技术/143.%E7%B2%BE%E8%AF%BB%E3%80%8ASuspense%20%E6%94%B9%E5%8F%98%E5%BC%80%E5%8F%91%E6%96%B9%E5%BC%8F%E3%80%8B.md">143.精读《Suspense 改变开发方式》</a>
|
||||
- <a href="./前沿技术/144.%E7%B2%BE%E8%AF%BB%E3%80%8AWebpack5%20%E6%96%B0%E7%89%B9%E6%80%A7%20-%20%E6%A8%A1%E5%9D%97%E8%81%94%E9%82%A6%E3%80%8B.md">144.精读《Webpack5 新特性 - 模块联邦》</a>
|
||||
- <a href="./前沿技术/145.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Router%20v6%E3%80%8B.md">145.精读《React Router v6》</a>
|
||||
- <a href="./前沿技术/146.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Hooks%20%E6%95%B0%E6%8D%AE%E6%B5%81%E3%80%8B.md">146.精读《React Hooks 数据流》</a>
|
||||
- <a href="./前沿技术/147.%20%E7%B2%BE%E8%AF%BB%E3%80%8A%40types%20react%20%E5%80%BC%E5%BE%97%E6%B3%A8%E6%84%8F%E7%9A%84%20TS%20%E6%8A%80%E5%B7%A7%E3%80%8B.md">147. 精读《@types react 值得注意的 TS 技巧》</a>
|
||||
- <a href="./前沿技术/148.%20%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Error%20Boundaries%E3%80%8B.md">148. 精读《React Error Boundaries》</a>
|
||||
- <a href="./前沿技术/149.%20%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20%E6%80%A7%E8%83%BD%E8%B0%83%E8%AF%95%E3%80%8B.md">149. 精读《React 性能调试》</a>
|
||||
- <a href="./前沿技术/150.%20%E7%B2%BE%E8%AF%BB%E3%80%8ADeno%201.0%20%E4%BD%A0%E9%9C%80%E8%A6%81%E4%BA%86%E8%A7%A3%E7%9A%84%E3%80%8B.md">150. 精读《Deno 1.0 你需要了解的》</a>
|
||||
- <a href="./前沿技术/152.%20%E7%B2%BE%E8%AF%BB%E3%80%8Arecoil%E3%80%8B.md">152. 精读《recoil》</a>
|
||||
- <a href="./前沿技术/153.%20%E7%B2%BE%E8%AF%BB%E3%80%8Asnowpack%E3%80%8B.md">153. 精读《snowpack》</a>
|
||||
- <a href="./前沿技术/154.%20%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%94%A8%20React%20%E5%81%9A%E6%8C%89%E9%9C%80%E6%B8%B2%E6%9F%93%E3%80%8B.md">154. 精读《用 React 做按需渲染》</a>
|
||||
- <a href="./前沿技术/157.%20%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A6%82%E4%BD%95%E6%AF%94%E8%BE%83%20Object%20%E5%AF%B9%E8%B1%A1%E3%80%8B.md">157. 精读《如何比较 Object 对象》</a>
|
||||
- <a href="./前沿技术/158.%20%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%204%E3%80%8B.md">158. 精读《Typescript 4》</a>
|
||||
- <a href="./前沿技术/159.%20%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%AF%B9%E4%BD%8E%E4%BB%A3%E7%A0%81%E6%90%AD%E5%BB%BA%E7%9A%84%E7%90%86%E8%A7%A3%E3%80%8B.md">159. 精读《对低代码搭建的理解》</a>
|
||||
- <a href="./前沿技术/160.%20%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%87%BD%E6%95%B0%E7%BC%93%E5%AD%98%E3%80%8B.md">160. 精读《函数缓存》</a>
|
||||
- <a href="./前沿技术/161.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%8F%AF%E8%A7%86%E5%8C%96%E6%90%AD%E5%BB%BA%E6%80%9D%E8%80%83%20-%20%E5%AF%8C%E6%96%87%E6%9C%AC%E6%90%AD%E5%BB%BA%E3%80%8B.md">161.精读《可视化搭建思考 - 富文本搭建》</a>
|
||||
- <a href="./前沿技术/162.%E7%B2%BE%E8%AF%BB%E3%80%8ATasks%2C%20microtasks%2C%20queues%20and%20schedules%E3%80%8B.md">162.精读《Tasks, microtasks, queues and schedules》</a>
|
||||
- <a href="./前沿技术/163.%E7%B2%BE%E8%AF%BB%E3%80%8ASpring%20%E6%A6%82%E5%BF%B5%E3%80%8B.md">163.精读《Spring 概念》</a>
|
||||
- <a href="./前沿技术/164.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%95%B0%E6%8D%AE%E6%90%AD%E5%BB%BA%E5%BC%95%E6%93%8E%20bi-designer%20API-%E8%AE%BE%E8%AE%A1%E5%99%A8%E3%80%8B.md">164.精读《数据搭建引擎 bi-designer API-设计器》</a>
|
||||
- <a href="./前沿技术/165.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%95%B0%E6%8D%AE%E6%90%AD%E5%BB%BA%E5%BC%95%E6%93%8E%20bi-designer%20API-%E7%BB%84%E4%BB%B6%E3%80%8B.md">165.精读《数据搭建引擎 bi-designer API-组件》</a>
|
||||
- <a href="./前沿技术/166.%E7%B2%BE%E8%AF%BB%E3%80%8ABI%20%E6%90%AD%E5%BB%BA%20-%20%E7%AD%9B%E9%80%89%E6%9D%A1%E4%BB%B6%E3%80%8B.md">166.精读《BI 搭建 - 筛选条件》</a>
|
||||
- <a href="./前沿技术/190.%E7%B2%BE%E8%AF%BB%E3%80%8ADOM%20diff%20%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3%E3%80%8B.md">190.精读《DOM diff 原理详解》</a>
|
||||
- <a href="./前沿技术/191.%E7%B2%BE%E8%AF%BB%E3%80%8A%E9%AB%98%E6%80%A7%E8%83%BD%E8%A1%A8%E6%A0%BC%E3%80%8B.md">191.精读《高性能表格》</a>
|
||||
- <a href="./前沿技术/192.%E7%B2%BE%E8%AF%BB%E3%80%8ADOM%20diff%20%E6%9C%80%E9%95%BF%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97%E3%80%8B.md">192.精读《DOM diff 最长上升子序列》</a>
|
||||
- <a href="./前沿技术/193.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Server%20Component%E3%80%8B.md">193.精读《React Server Component》</a>
|
||||
- <a href="./前沿技术/194.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E3%80%8B.md">194.精读《算法基础数据结构》</a>
|
||||
- <a href="./前沿技术/195.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%96%B0%E4%B8%80%E4%BB%A3%E5%89%8D%E7%AB%AF%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7%E5%AF%B9%E6%AF%94%E3%80%8B.md">195.精读《新一代前端构建工具对比》</a>
|
||||
- <a href="./前沿技术/196.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E8%81%8C%E4%B8%9A%E8%A7%84%E5%88%92%20-%202021%20%E5%B9%B4%E3%80%8B.md">196.精读《前端职业规划 - 2021 年》</a>
|
||||
- <a href="./前沿技术/197.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%BD%8E%E4%BB%A3%E7%A0%81%E9%80%BB%E8%BE%91%E7%BC%96%E6%8E%92%E3%80%8B.md">197.精读《低代码逻辑编排》</a>
|
||||
- <a href="./前沿技术/202.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%2018%E3%80%8B.md">202.精读《React 18》</a>
|
||||
- <a href="./前沿技术/204.%E7%B2%BE%E8%AF%BB%E3%80%8A%E9%BB%98%E8%AE%A4%E3%80%81%E5%91%BD%E5%90%8D%E5%AF%BC%E5%87%BA%E7%9A%84%E5%8C%BA%E5%88%AB%E3%80%8B.md">204.精读《默认、命名导出的区别》</a>
|
||||
- <a href="./前沿技术/205.%E7%B2%BE%E8%AF%BB%E3%80%8AJS%20with%20%E8%AF%AD%E6%B3%95%E3%80%8B.md">205.精读《JS with 语法》</a>
|
||||
- <a href="./前沿技术/206.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%B8%80%E7%A7%8D%20Hooks%20%E6%95%B0%E6%8D%AE%E6%B5%81%E7%AE%A1%E7%90%86%E6%96%B9%E6%A1%88%E3%80%8B.md">206.精读《一种 Hooks 数据流管理方案》</a>
|
||||
- <a href="./前沿技术/207.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%20infer%20%E5%85%B3%E9%94%AE%E5%AD%97%E3%80%8B.md">207.精读《Typescript infer 关键字》</a>
|
||||
- <a href="./前沿技术/208.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%204.4%E3%80%8B.md">208.精读《Typescript 4.4》</a>
|
||||
- <a href="./前沿技术/209.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%8D%95%E8%8E%B7%E6%89%80%E6%9C%89%E5%BC%82%E6%AD%A5%20error%E3%80%8B.md">209.精读《捕获所有异步 error》</a>
|
||||
- <a href="./前沿技术/210.%E7%B2%BE%E8%AF%BB%E3%80%8Aclass%20static%20block%E3%80%8B.md">210.精读《class static block》</a>
|
||||
- <a href="./前沿技术/211.%E7%B2%BE%E8%AF%BB%E3%80%8AMicrosoft%20Power%20Fx%E3%80%8B.md">211.精读《Microsoft Power Fx》</a>
|
||||
- <a href="./前沿技术/212.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%8F%AF%E7%BB%B4%E6%8A%A4%E6%80%A7%E6%80%9D%E8%80%83%E3%80%8B.md">212.精读《可维护性思考》</a>
|
||||
- <a href="./前沿技术/213.%E7%B2%BE%E8%AF%BB%E3%80%8APrisma%20%E7%9A%84%E4%BD%BF%E7%94%A8%E3%80%8B.md">213.精读《Prisma 的使用》</a>
|
||||
- <a href="./前沿技术/214.%E7%B2%BE%E8%AF%BB%E3%80%8Aweb%20streams%E3%80%8B.md">214.精读《web streams》</a>
|
||||
- <a href="./前沿技术/215.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%BB%80%E4%B9%88%E6%98%AF%20LOD%20%E8%A1%A8%E8%BE%BE%E5%BC%8F%E3%80%8B.md">215.精读《什么是 LOD 表达式》</a>
|
||||
- <a href="./前沿技术/216.%E7%B2%BE%E8%AF%BB%E3%80%8A15%20%E5%A4%A7%20LOD%20%E8%A1%A8%E8%BE%BE%E5%BC%8F%20-%20%E4%B8%8A%E3%80%8B.md">216.精读《15 大 LOD 表达式 - 上》</a>
|
||||
- <a href="./前沿技术/217.%E7%B2%BE%E8%AF%BB%E3%80%8A15%20%E5%A4%A7%20LOD%20%E8%A1%A8%E8%BE%BE%E5%BC%8F%20-%20%E4%B8%8B%E3%80%8B.md">217.精读《15 大 LOD 表达式 - 下》</a>
|
||||
- <a href="./前沿技术/218.%E7%B2%BE%E8%AF%BB%E3%80%8ARust%20%E6%98%AF%20JS%20%E5%9F%BA%E5%BB%BA%E7%9A%84%E6%9C%AA%E6%9D%A5%E3%80%8B.md">218.精读《Rust 是 JS 基建的未来》</a>
|
||||
- <a href="./前沿技术/219.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3%E7%8E%B0%E4%BB%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%80%E3%80%8B.md">219.精读《深入了解现代浏览器一》</a>
|
||||
- <a href="./前沿技术/220.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3%E7%8E%B0%E4%BB%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E4%BA%8C%E3%80%8B.md">220.精读《深入了解现代浏览器二》</a>
|
||||
- <a href="./前沿技术/221.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3%E7%8E%B0%E4%BB%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%89%E3%80%8B.md">221.精读《深入了解现代浏览器三》</a>
|
||||
- <a href="./前沿技术/222.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3%E7%8E%B0%E4%BB%A3%E6%B5%8F%E8%A7%88%E5%99%A8%E5%9B%9B%E3%80%8B.md">222.精读《深入了解现代浏览器四》</a>
|
||||
- <a href="./前沿技术/223.%E7%B2%BE%E8%AF%BB%E3%80%8ARecords%20%26%20Tuples%20%E6%8F%90%E6%A1%88%E3%80%8B.md">223.精读《Records & Tuples 提案》</a>
|
||||
- <a href="./前沿技术/224.%E7%B2%BE%E8%AF%BB%E3%80%8ARecords%20%26%20Tuples%20for%20React%E3%80%8B.md">224.精读《Records & Tuples for React》</a>
|
||||
- <a href="./前沿技术/225.%E7%B2%BE%E8%AF%BB%E3%80%8AExcel%20JS%20API%E3%80%8B.md">225.精读《Excel JS API》</a>
|
||||
- <a href="./前沿技术/226.%E7%B2%BE%E8%AF%BB%E3%80%8A2021%20%E5%89%8D%E7%AB%AF%E6%96%B0%E7%A7%80%E5%9B%9E%E9%A1%BE%E3%80%8B.md">226.精读《2021 前端新秀回顾》</a>
|
||||
- <a href="./前沿技术/228.%E7%B2%BE%E8%AF%BB%E3%80%8Apipe%20operator%20for%20JavaScript%E3%80%8B.md">228.精读《pipe operator for JavaScript》</a>
|
||||
- <a href="./前沿技术/230.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%AF%B9%20Markdown%20%E7%9A%84%E6%80%9D%E8%80%83%E3%80%8B.md">230.精读《对 Markdown 的思考》</a>
|
||||
- <a href="./前沿技术/237.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%204.5-4.6%20%E6%96%B0%E7%89%B9%E6%80%A7%E3%80%8B.md">237.精读《Typescript 4.5-4.6 新特性》</a>
|
||||
- <a href="./前沿技术/238.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%B8%8D%E5%86%8D%E9%9C%80%E8%A6%81%20JS%20%E5%81%9A%E7%9A%84%205%20%E4%BB%B6%E4%BA%8B%E3%80%8B.md">238.精读《不再需要 JS 做的 5 件事》</a>
|
||||
- <a href="./前沿技术/239.%E7%B2%BE%E8%AF%BB%E3%80%8AJS%20%E6%95%B0%E7%BB%84%E7%9A%84%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0%E3%80%8B.md">239.精读《JS 数组的内部实现》</a>
|
||||
|
||||
### 设计模式
|
||||
|
||||
- <a href="./设计模式/167.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Abstract%20Factory%20%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E3%80%8B.md">167.精读《设计模式 - Abstract Factory 抽象工厂》</a>
|
||||
- <a href="./设计模式/168.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Builder%20%E7%94%9F%E6%88%90%E5%99%A8%E3%80%8B.md">168.精读《设计模式 - Builder 生成器》</a>
|
||||
- <a href="./设计模式/169.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Factory%20Method%20%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E3%80%8B.md">169.精读《设计模式 - Factory Method 工厂方法》</a>
|
||||
- <a href="./设计模式/170.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Prototype%20%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%8B.md">170.精读《设计模式 - Prototype 原型模式》</a>
|
||||
- <a href="./设计模式/171.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Singleton%20%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%E3%80%8B.md">171.精读《设计模式 - Singleton 单例模式》</a>
|
||||
- <a href="./设计模式/172.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Adapter%20%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F%E3%80%8B.md">172.精读《设计模式 - Adapter 适配器模式》</a>
|
||||
- <a href="./设计模式/173.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Bridge%20%E6%A1%A5%E6%8E%A5%E6%A8%A1%E5%BC%8F%E3%80%8B.md">173.精读《设计模式 - Bridge 桥接模式》</a>
|
||||
- <a href="./设计模式/174.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Composite%20%E7%BB%84%E5%90%88%E6%A8%A1%E5%BC%8F%E3%80%8B.md">174.精读《设计模式 - Composite 组合模式》</a>
|
||||
- <a href="./设计模式/175.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Decorator%20%E8%A3%85%E9%A5%B0%E5%99%A8%E6%A8%A1%E5%BC%8F%E3%80%8B.md">175.精读《设计模式 - Decorator 装饰器模式》</a>
|
||||
- <a href="./设计模式/176.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Facade%20%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F%E3%80%8B.md">176.精读《设计模式 - Facade 外观模式》</a>
|
||||
- <a href="./设计模式/177.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Flyweight%20%E4%BA%AB%E5%85%83%E6%A8%A1%E5%BC%8F%E3%80%8B.md">177.精读《设计模式 - Flyweight 享元模式》</a>
|
||||
- <a href="./设计模式/178.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Proxy%20%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F%E3%80%8B.md">178.精读《设计模式 - Proxy 代理模式》</a>
|
||||
- <a href="./设计模式/179.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Chain%20of%20Responsibility%20%E8%81%8C%E8%B4%A3%E9%93%BE%E6%A8%A1%E5%BC%8F%E3%80%8B.md">179.精读《设计模式 - Chain of Responsibility 职责链模式》</a>
|
||||
- <a href="./设计模式/180.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Command%20%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F%E3%80%8B.md">180.精读《设计模式 - Command 命令模式》</a>
|
||||
- <a href="./设计模式/181.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Interpreter%20%E8%A7%A3%E9%87%8A%E5%99%A8%E6%A8%A1%E5%BC%8F%E3%80%8B.md">181.精读《设计模式 - Interpreter 解释器模式》</a>
|
||||
- <a href="./设计模式/182.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Iterator%20%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F%E3%80%8B.md">182.精读《设计模式 - Iterator 迭代器模式》</a>
|
||||
- <a href="./设计模式/183.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Mediator%20%E4%B8%AD%E4%BB%8B%E8%80%85%E6%A8%A1%E5%BC%8F%E3%80%8B.md">183.精读《设计模式 - Mediator 中介者模式》</a>
|
||||
- <a href="./设计模式/184.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Memoto%20%E5%A4%87%E5%BF%98%E5%BD%95%E6%A8%A1%E5%BC%8F%E3%80%8B.md">184.精读《设计模式 - Memoto 备忘录模式》</a>
|
||||
- <a href="./设计模式/185.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Observer%20%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F%E3%80%8B.md">185.精读《设计模式 - Observer 观察者模式》</a>
|
||||
- <a href="./设计模式/186.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20State%20%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F%E3%80%8B.md">186.精读《设计模式 - State 状态模式》</a>
|
||||
- <a href="./设计模式/187.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Strategy%20%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E3%80%8B.md">187.精读《设计模式 - Strategy 策略模式》</a>
|
||||
- <a href="./设计模式/188.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Template%20Method%20%E6%A8%A1%E7%89%88%E6%A8%A1%E5%BC%8F%E3%80%8B.md">188.精读《设计模式 - Template Method 模版模式》</a>
|
||||
- <a href="./设计模式/189.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20-%20Visitor%20%E8%AE%BF%E9%97%AE%E8%80%85%E6%A8%A1%E5%BC%8F%E3%80%8B.md">189.精读《设计模式 - Visitor 访问者模式》</a>
|
||||
|
||||
### 编译原理
|
||||
|
||||
- <a href="./编译原理/64.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md">64.精读《手写 SQL 编译器 - 词法分析》</a>
|
||||
- <a href="./编译原理/65.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%96%87%E6%B3%95%E4%BB%8B%E7%BB%8D%E3%80%8B.md">65.精读《手写 SQL 编译器 - 文法介绍》</a>
|
||||
- <a href="./编译原理/66.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md">66.精读《手写 SQL 编译器 - 语法分析》</a>
|
||||
- <a href="./编译原理/67.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E5%9B%9E%E6%BA%AF%E3%80%8B.md">67.精读《手写 SQL 编译器 - 回溯》</a>
|
||||
- <a href="./编译原理/70.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E6%A0%91%E3%80%8B.md">70.精读《手写 SQL 编译器 - 语法树》</a>
|
||||
- <a href="./编译原理/71.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E9%94%99%E8%AF%AF%E6%8F%90%E7%A4%BA%E3%80%8B.md">71.精读《手写 SQL 编译器 - 错误提示》</a>
|
||||
- <a href="./编译原理/78.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B9%8B%E7%BC%93%E5%AD%98%E3%80%8B.md">78.精读《手写 SQL 编译器 - 性能优化之缓存》</a>
|
||||
- <a href="./编译原理/85.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA%E3%80%8B.md">85.精读《手写 SQL 编译器 - 智能提示》</a>
|
||||
|
||||
### 源码解读
|
||||
|
||||
- <a href="./源码解读/48.%E7%B2%BE%E8%AF%BB%E3%80%8AImmer.js%E3%80%8B%E6%BA%90%E7%A0%81.md">48.精读《Immer.js》源码</a>
|
||||
- <a href="./源码解读/73.%E7%B2%BE%E8%AF%BB%E3%80%8Asqorn%20%E6%BA%90%E7%A0%81%E3%80%8B.md">73.精读《sqorn 源码》</a>
|
||||
- <a href="./源码解读/75.%E7%B2%BE%E8%AF%BB%E3%80%8AEpitath%20%E6%BA%90%E7%A0%81%20-%20renderProps%20%E6%96%B0%E7%94%A8%E6%B3%95%E3%80%8B.md">75.精读《Epitath 源码 - renderProps 新用法》</a>
|
||||
- <a href="./源码解读/82.%E7%B2%BE%E8%AF%BB%E3%80%8AHtm%20-%20Hyperscript%20%E6%BA%90%E7%A0%81%E3%80%8B.md">82.精读《Htm - Hyperscript 源码》</a>
|
||||
- <a href="./源码解读/92.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20PowerPlug%20%E6%BA%90%E7%A0%81%E3%80%8B.md">92.精读《React PowerPlug 源码》</a>
|
||||
- <a href="./源码解读/93.%E7%B2%BE%E8%AF%BB%E3%80%8Asyntax-parser%20%E6%BA%90%E7%A0%81%E3%80%8B.md">93.精读《syntax-parser 源码》</a>
|
||||
- <a href="./源码解读/98.%E7%B2%BE%E8%AF%BB%E3%80%8Areact-easy-state%20%E6%BA%90%E7%A0%81%E3%80%8B.md">98.精读《react-easy-state 源码》</a>
|
||||
- <a href="./源码解读/110.%E7%B2%BE%E8%AF%BB%E3%80%8AInject%20Instance%20%E6%BA%90%E7%A0%81%E3%80%8B.md">110.精读《Inject Instance 源码》</a>
|
||||
- <a href="./源码解读/122.%E7%B2%BE%E8%AF%BB%E3%80%8Arobot%20%E6%BA%90%E7%A0%81%20-%20%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA%E3%80%8B.md">122.精读《robot 源码 - 有限状态机》</a>
|
||||
- <a href="./源码解读/128.%E7%B2%BE%E8%AF%BB%E3%80%8AHooks%20%E5%8F%96%E6%95%B0%20-%20swr%20%E6%BA%90%E7%A0%81%E3%80%8B.md">128.精读《Hooks 取数 - swr 源码》</a>
|
||||
- <a href="./源码解读/130.%E7%B2%BE%E8%AF%BB%E3%80%8Aunstated%20%E4%B8%8E%20unstated-next%20%E6%BA%90%E7%A0%81%E3%80%8B.md">130.精读《unstated 与 unstated-next 源码》</a>
|
||||
- <a href="./源码解读/151.%20%E7%B2%BE%E8%AF%BB%E3%80%8A%40umijs%20use-request%E3%80%8B%E6%BA%90%E7%A0%81.md">151. 精读《@umijs use-request》源码</a>
|
||||
- <a href="./源码解读/155.%20%E7%B2%BE%E8%AF%BB%E3%80%8Ause-what-changed%20%E6%BA%90%E7%A0%81%E3%80%8B.md">155. 精读《use-what-changed 源码》</a>
|
||||
- <a href="./源码解读/156.%20%E7%B2%BE%E8%AF%BB%E3%80%8Areact-intersection-observer%20%E6%BA%90%E7%A0%81%E3%80%8B.md">156. 精读《react-intersection-observer 源码》</a>
|
||||
- <a href="./源码解读/227.%20%E7%B2%BE%E8%AF%BB%E3%80%8Azustand%20%E6%BA%90%E7%A0%81%E3%80%8B.md">227. 精读《zustand 源码》</a>
|
||||
- <a href="./源码解读/229.%E7%B2%BE%E8%AF%BB%E3%80%8Avue-lit%20%E6%BA%90%E7%A0%81%E3%80%8B.md">229.精读《vue-lit 源码》</a>
|
||||
|
||||
### 商业思考
|
||||
|
||||
- <a href="./商业思考/90.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%9E%81%E5%AE%A2%E5%85%AC%E5%9B%AD%202019%E3%80%8B.md">90.精读《极客公园 2019》</a>
|
||||
- <a href="./商业思考/103.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%93%E5%AE%B6%E4%B8%8D%E5%86%8D%E5%85%B3%E5%BF%83%E6%8A%80%E6%9C%AF%E7%BB%86%E8%8A%82%E3%80%8B.md">103.精读《为什么专家不再关心技术细节》</a>
|
||||
- <a href="./商业思考/106.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%95%B0%E6%8D%AE%E4%B9%8B%E4%B8%8A%C2%B7%E6%99%BA%E6%85%A7%E4%B9%8B%E5%85%89%20-%202018%E3%80%8B.md">106.精读《数据之上·智慧之光 - 2018》</a>
|
||||
- <a href="./商业思考/108.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%99%BA%E8%83%BD%E5%95%86%E4%B8%9A%E3%80%8B.md">108.精读《智能商业》</a>
|
||||
- <a href="./商业思考/114.%E7%B2%BE%E8%AF%BB%E3%80%8A%E8%B0%81%E5%9C%A8%E4%B8%96%E7%95%8C%E4%B8%AD%E5%BF%83%E3%80%8B.md">114.精读《谁在世界中心》</a>
|
||||
- <a href="./商业思考/115.%E7%B2%BE%E8%AF%BB%E3%80%8ATableau%20%E5%85%A5%E9%97%A8%E3%80%8B.md">115.精读《Tableau 入门》</a>
|
||||
- <a href="./商业思考/116.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%88%B7%E6%96%B0%E3%80%8B.md">116.精读《刷新》</a>
|
||||
- <a href="./商业思考/131.%E7%B2%BE%E8%AF%BB%E3%80%8A%E4%BB%8E%200%20%E5%88%B0%201%E3%80%8B.md">131.精读《从 0 到 1》</a>
|
||||
- <a href="./商业思考/135.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%9E%81%E5%AE%A2%E5%85%AC%E5%9B%AD%20IFX%20-%20%E4%B8%8A%E3%80%8B.md">135.精读《极客公园 IFX - 上》</a>
|
||||
- <a href="./商业思考/136.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%9E%81%E5%AE%A2%E5%85%AC%E5%9B%AD%20IFX%20-%20%E4%B8%8B%E3%80%8B.md">136.精读《极客公园 IFX - 下》</a>
|
||||
- <a href="./商业思考/137.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%BD%93%E6%88%91%E5%9C%A8%E5%88%86%E4%BA%AB%E7%9A%84%E6%97%B6%E5%80%99%EF%BC%8C%E6%88%91%E5%9C%A8%E5%81%9A%E4%BB%80%E4%B9%88%EF%BC%9F%E3%80%8B.md">137.精读《当我在分享的时候,我在做什么?》</a>
|
||||
|
||||
### 算法
|
||||
|
||||
- <a href="./算法/198.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%AE%97%E6%B3%95%20-%20%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E3%80%8B.md">198.精读《算法 - 动态规划》</a>
|
||||
- <a href="./算法/199.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%AE%97%E6%B3%95%20-%20%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E3%80%8B.md">199.精读《算法 - 滑动窗口》</a>
|
||||
- <a href="./算法/200.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%AE%97%E6%B3%95%20-%20%E5%9B%9E%E6%BA%AF%E3%80%8B.md">200.精读《算法 - 回溯》</a>
|
||||
- <a href="./算法/201.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%AE%97%E6%B3%95%20-%20%E4%BA%8C%E5%8F%89%E6%A0%91%E3%80%8B.md">201.精读《算法 - 二叉树》</a>
|
||||
- <a href="./算法/203.%E7%B2%BE%E8%AF%BB%E3%80%8A%E7%AE%97%E6%B3%95%20-%20%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E3%80%8B.md">203.精读《算法 - 二叉搜索树》</a>
|
||||
|
||||
### SQL
|
||||
|
||||
- <a href="./SQL/231.SQL%20%E5%85%A5%E9%97%A8.md">231.SQL 入门</a>
|
||||
- <a href="./SQL/232.SQL%20%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2.md">232.SQL 聚合查询</a>
|
||||
- <a href="./SQL/233.SQL%20%E5%A4%8D%E6%9D%82%E6%9F%A5%E8%AF%A2.md">233.SQL 复杂查询</a>
|
||||
- <a href="./SQL/234.SQL%20CASE%20%E8%A1%A8%E8%BE%BE%E5%BC%8F.md">234.SQL CASE 表达式</a>
|
||||
- <a href="./SQL/235.SQL%20%E7%AA%97%E5%8F%A3%E5%87%BD%E6%95%B0.md">235.SQL 窗口函数</a>
|
||||
- <a href="./SQL/236.SQL%20grouping.md">236.SQL grouping</a>
|
||||
|
||||
## 关注前端精读微信公众号
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
## Special Sponsors
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://e.coding.net/?utm_source=weekly" target="_blank">
|
||||
<img width="300" src="https://img.alicdn.com/tfs/TB107D.QbrpK1RjSZTEXXcWAVXa-1000-332.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
# 1 引言
|
||||
|
||||
<img src="assets/1/cube.jpeg" alt="logo" width="500" />
|
||||
<img src="https://img.alicdn.com/imgextra/i4/O1CN01mvDKCM1owPSsLDBmI_!!6000000005289-2-tps-475-297.png" alt="logo" width="500" />
|
||||
|
||||
> 如今,Javascript 模块化规范非常方便、自然,但这个新规范仅执行了 2 年,就在 4 年前,js 的模块化还停留在运行时支持,10 年前,通过后端模版定义、注释定义模块依赖。对经历过来的人来说,历史的模块化方式还停留在脑海中,反而新上手的同学会更快接受现代的模块化规范。
|
||||
|
||||
@@ -81,7 +81,7 @@ YUI3 的 sandbox 像极了差不多同时出现的 AMD 规范,但早期 yahoo
|
||||
|
||||
对于 js 模块化,最近出现的 `<script type="module">` 方式,虽然还没有得到浏览器原生支持,但也是我比较看好的未来趋势,这样就连 webpack 的拆包都不需要了,直接把源代码传到服务器,配合 http2.0 完美抛开预编译的枷锁。
|
||||
|
||||
上述三中方案都不依赖预编译,分别实现了 html、css、js 模块化,相信这就是未来。
|
||||
上述三种方案都不依赖预编译,分别实现了 html、css、js 模块化,相信这就是未来。
|
||||
|
||||
### 模块化标准推进速度仍然缓慢
|
||||
|
||||
@@ -103,7 +103,7 @@ YUI3 的 sandbox 像极了差不多同时出现的 AMD 规范,但早期 yahoo
|
||||
|
||||
> 看到大家基本都提到了 HTTP/2,对这项技术解决前端模块化及资源打包等工程问题抱有非常大的期待。很多人也认为 HTTP/2 普及后,基本就没有 Webpack 什么事情了。
|
||||
|
||||
不过 Webpack 作者 @sokra 在他的文章 [webpack & HTTP/2](https://medium.com/webpack/webpack-http-2-7083ec3f3ce6#.zdo4juvgo) 里提到了一个新的 Webpack 插件 `AggressiveSplittingPlugin`。简单的说,这款插件就是为了充分利用 HTTP/2 的文件缓存能力,将你的业务代码自动拆分成若干个数十 KB 的小文件。后续若其中任意一个文件发生变化,可以保证其他的小 chunck 不需要重新下载。
|
||||
不过 Webpack 作者 @sokra 在他的文章 [webpack & HTTP/2](https://medium.com/webpack/webpack-http-2-7083ec3f3ce6#.zdo4juvgo) 里提到了一个新的 Webpack 插件 `AggressiveSplittingPlugin`。简单的说,这款插件就是为了充分利用 HTTP/2 的文件缓存能力,将你的业务代码自动拆分成若干个数十 KB 的小文件。后续若其中任意一个文件发生变化,可以保证其他的小 chunk 不需要重新下载。
|
||||
|
||||
可见,**即使不断的有新技术出现,也依然需要配套的工具来将前端工程问题解决方案推向极致。**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
# 1 引言
|
||||
|
||||
<img src="assets/11/coffee.jpg" width="500" alt="logo" />
|
||||
<img src="https://img.alicdn.com/imgextra/i2/O1CN01JC1TZ51Nxn24teojP_!!6000000001637-2-tps-1024-1296.png" width="500" alt="logo" />
|
||||
|
||||
梵高这幅画远景漆黑一片,近景的咖啡店色彩却反差很大,他只是望着黑夜中温暖的咖啡馆,交织着矛盾与孤独。代码不可能没有 BUG,调试与开发也始终交织在一起,我们在这两种矛盾中不断成长。
|
||||
|
||||
@@ -105,7 +105,7 @@ Css 不像 Js 一样方便分析规则是否存在冗余,Chrome 帮我们做
|
||||
|
||||
Chrome 会记录最后插入的 5 个元素,分别以 `$0` ~ `$4` 的方式在控制台直接输出。
|
||||
|
||||
<img src="assets/11/last-item.png" width="500" alt="last-items" />
|
||||
<img src="https://img.alicdn.com/imgextra/i4/O1CN01xsKHb822gkXhDDcCA_!!6000000007150-2-tps-417-516.png" width="500" alt="last-items" />
|
||||
|
||||
### Console.table
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## 概述
|
||||
|
||||
原文对于深水区的想法,讲的很清楚,还是建议读者去读一下原文。
|
||||
对比 2010 年,整个前端生态已经翻新了好几遍,直到近几年的 Node BFF、IDE Cloud,抑或是客户端 AI,还是 Serverless 的建设,,前端想要深度参与的话,单纯依靠原来的 HTML/CSS/JS 三件套技能也远远不够了。再抛开技术,整个互联网创业生态也重构了好几遍。无论是技术层面还是意识层面,如今的前端开发已经进入深水区。
|
||||
对比 2010 年,整个前端生态已经翻新了好几遍,直到近几年的 Node BFF、IDE Cloud,抑或是客户端 AI,还是 Serverless 的建设,前端想要深度参与的话,单纯依靠原来的 HTML/CSS/JS 三件套技能也远远不够了。再抛开技术,整个互联网创业生态也重构了好几遍。无论是技术层面还是意识层面,如今的前端开发已经进入深水区。
|
||||
|
||||
- 深水区需要哪些技能
|
||||

|
||||
@@ -1,6 +1,6 @@
|
||||
# 1 引言
|
||||
|
||||
<img src="assets/13/logo.jpeg" width="500" alt="logo" />
|
||||
<img src="https://img.alicdn.com/imgextra/i2/O1CN014VGV7a1x3ILYqK9OD_!!6000000006387-2-tps-1024-732.png" width="500" alt="logo" />
|
||||
|
||||
javascript 的 this 是个头痛的话题,本期精读的文章更是引出了一个观点,避免使用 this。我们来看看是否有道理。
|
||||
|
||||
@@ -53,7 +53,7 @@ getName(person3) // Name: Sarah Doe
|
||||
getGreetingCallback(person3)('Jeff') // Hello Jeff, I'm Sarah Doe
|
||||
```
|
||||
|
||||
<img src="assets/13/1.png" width="500" alt="demo1" />
|
||||
<img src="https://img.alicdn.com/imgextra/i3/O1CN017Kw37u1oOyYHXGlqC_!!6000000005216-2-tps-1338-338.png" width="500" alt="demo1" />
|
||||
|
||||
这样 person 实例是个纯对象,没有将方法挂载到原型链上,简单易懂。
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
## 1 引言
|
||||
|
||||
当下互联网行业里面最流行的就是 ABC:
|
||||
|
||||
> A: AI 人工智能 B: BIG DATA C: CLOUD
|
||||
|
||||
而阿里经济体中的 ABC,其中的 BIG DATA,即是我们 DT https://dt.alibaba.com/ ,我们用大数据赋能商业,创造价值。
|
||||
|
||||
而我们说数据中台,其实阿里提出的中台只有两个:业务中台与数据中台。业务中台的目的是让业务能够快速落地,数据中台的目的是完成数据的采集、建设、管理、使用这四个环节,让数据从生产到使用过程变得丝般顺滑,不仅不让数据资产成为累赘,还会最大限度发挥出数据潜藏的价值。
|
||||
|
||||
笔者所在的就是数据中台的大前端团队,既为阿里经济体提供数据服务,又着力为上云企业打造属于自己的数据中台,处在前端技术、商业模式、产品设计的最前沿,且听我慢慢道来。
|
||||
|
||||
## 2 精读
|
||||
|
||||
### 全链路数据能力
|
||||
|
||||
从能力上看,数据中台处理数据的方方面面,从数据产生开始就进行追踪,不仅打通了数据采集、存储、处理、查询、消费的全链路,还用以下几种方式赋能业务:研发数据管理平台并监控数据质量,研发生意参谋等数据分析产品直接服务大、中、小商家,提供统一数据服务标准化数据使用流程,将数据分析的算法能力服务化,将支撑内部的数据服务上云搭建客户自己的数据中台,研发 BI 平台完成数据决策的最后一环。
|
||||
|
||||
### 全链路数据技术
|
||||
|
||||
从技术架构上看,从底层的数据采集技术开始,逐步向上建设了数据计算与管理能力、数据服务、数据平台、数据应用与数据安全。
|
||||
|
||||
从使用者角度来看,现在的公司对数据的诉求可以概括为以下几点:
|
||||
|
||||
1. 数据从哪来,如何完全数字化:对应全链路数据采集服务。
|
||||
2. 如何得到想要的数据:数据计算、建模与管理服务。
|
||||
3. 如何使用数据:统一数据服务平台。
|
||||
4. 如何利用数据做商业决策:BI 平台。
|
||||
5. 如何保障数据安全:数据安全服务。
|
||||
|
||||
对阿里而言,还会额外考虑下面几点:
|
||||
|
||||
1. 如何让数据服务横向支撑所有业务线:数据服务平台化,数据智能化服务平台与 BI 平台。
|
||||
2. 如何让数据服务普惠到每一个企业:数据服务全面上云。
|
||||
3. 如何让数据服务更有价值:打通阿里经济体的数据体系,让数据相互产生化学反应。
|
||||
|
||||
当然,挑战性也非常大,首先是数据壁垒的挑战,要说服其他团队将数据交给你管理绝非易事。其次是价值挑战,如何证明数据中台存在的价值,并做到肉眼可见的业务增值。最后是技术挑战,对前端来说,几十款数据产品的搭建、几十万张数据报表的搭建,需要一个足够好用的数据产品搭建平台来支持;数据分析产品的下一代探索式分析也对 BI 引擎提出了新的要求;数据可视化远比普通可视化复杂,不仅要考虑大数据下的性能与可读性,还要理解商业,做出能体现数据分析价值的图表。
|
||||
|
||||
不论是数据搭建还是数据可视化,都是前端垂直领域的另一条好赛道,不仅有沉甸甸的业务价值,还有全新数据领域的的前端技术挑战,而且随着数据中台影响力的持续扩大,我们的前端技术也会带来业界越来越大的影响力。
|
||||
|
||||
### 如何建设和管理数据
|
||||
|
||||
想要数据用的好,首先要管的好,在大数据时代,企业必须建立一套自己的标准数仓系统对数据的采集、运维调度做全链路管理,让大数据变成好数据,让好数据可以发挥价值。
|
||||
|
||||

|
||||
|
||||
> Dataphin 数仓建设平台。
|
||||
|
||||
数仓的建设需要从物理空间与逻辑空间,也就是底层的表开始整理,通过对数据的采集、清洗、结构化,产出一套规范的数据定义。
|
||||
|
||||
所谓规范的数据定义即口径、算法、命名均一致的数据规范,降低数据二义性,提升数据查找效率与准确性。之后对数据建模,建模即是对数据的进一步抽象,可能是抽象为一个 Cube 模型,这样在顶层认知上,所有数据都是不同维度的 Cube,方便统一理解。
|
||||
|
||||
最后通过对数据进行在线的、离线的调度计算,产出数据资产。
|
||||
|
||||
### 如何看数据
|
||||
|
||||
或导出一个 Excel 文件仔细品味,或如双十一媒体大屏般夺目,或如股票操盘手般紧盯着屏幕,或随时随地的手机浏览。在哪看,怎么看,看什么,决定着同一份数据可带来不同的效果,产生不同的价值。
|
||||
|
||||
稳:双十一大屏,零点起得来,24 点收得住,每个彩蛋的出现,每个数字的跳动,如丝般顺滑,这不是播放 VCR,每一帧画面都是真实的数据展现。容:即是生意参谋用户的浏览器兼容,又是多端用户的兼容,也是 BI 分析结果的数据大容量。有容乃大,方显前端功底。
|
||||
|
||||
**“如何看数据” 这恰是做为数据前端人的使命和责任。** 不同的人,不同的端,不同的需求,这恰是给数据前端的挑战。而让用户透过数据创造价值,也正是数据前端人的价值。
|
||||
|
||||
### 如何分析数据
|
||||
|
||||
大数据浪潮之下,必然会诞生各式各样的数据产品,产品化的方式可以降低数据应用的门槛。我们希望人人都能成为数据分析师,于是 BI (商业智能)产品应运而生,作为大数据行业中的一个重要领域,BI 产品用大数据的方式解决了企业的业务分析需求,支撑企业进行数字化转型,从经验驱动决策转变为数据驱动决策,进而给企业带来超额收益。
|
||||
|
||||

|
||||
|
||||
> QuickBI 数据分析工具。
|
||||
|
||||
**人人都是数据分析师的情况在不断增强。**
|
||||
|
||||
根据 Gartner 对 2020 年 BI 产品发展趋势预测:
|
||||
|
||||
1. 到 2020 年,为用户提供对内部和外部数据策划目录的访问权限的组织将从分析投资中获得两倍的业务价值。
|
||||
2. 到 2020 年,业务部门的数据和分析专家数量的增速将是 IT 部门专家的 3 倍,这会迫使企业重新考虑其组织模式和技能。
|
||||
3. 到 2021 年,自然语言处理和会话分析这两个功能,会在新用户、特别是一线工作人员中,将分析和商业智能产品的使用率从 35% 提升到 50% 以上。
|
||||
|
||||
**快速增涨的市场规模。**
|
||||
|
||||
根据中国电子信息产业发展研究院发布的《中国大数据产业发展水平评估报告》,预计 2019 年我国大数据核心产业规模突破 5700 亿元,未来 2-3 年的市场规模的增长率仍将保持 35% 左右。未来切入这部分应用环节,BI 商业智能的潜在市场规模将在数百亿的市场空间。
|
||||
|
||||
**大数据与前端。**
|
||||
|
||||
前端的职业发展除了提升自己的技能技术储备之外,选择合适行业方向和研究领域也尤为重要。如果用路和车的关系来比喻的话,把前端技能比作车的话,各个行业都是路,有的路是乡间小路,有的路是城乡公路,而大数据行业当之无愧是行业中的上高速公路,路况更好,路面更宽,如果你拥有一辆好车,为什么不来高速公路上飞驰呢?
|
||||
|
||||
大数据下的前端面临哪些挑战?以 BI 为例,BI 领域的四大方向:数据集、渲染引擎、数据模型与可视化都有许多可以做深的技术点,每一块都需要深入沉淀几年技术经验才能做好,需要大量优秀人才通力协作才有可能做好。你也可以阅读 [精读《前端与 BI》](https://github.com/dt-fe/weekly/blob/v2/121.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%89%8D%E7%AB%AF%E4%B8%8E%20BI%E3%80%8B.md) 了解更多 BI 相关知识。
|
||||
|
||||
### 我们是数据中台大前端
|
||||
|
||||
> “ 前端不是因为我们用 JavaScript,而是因为我们站在业务最前端,解决业务端的问题,所以我们是前端 ”。
|
||||
|
||||
BI 分析产品、做数据可视化、做产品搭建 .. 我们早已经跳出了“前端”的传统概念范畴。我们做大数据表格优化、 Web Excel、 SQL 编辑器、智能可视化。在数据中台,我们有着天然的复杂业务场景和海量数据优势,迫使你向自己提出更大的挑战来解决业务上的问题。如果你热爱挑战、热爱技术,请加入我们吧。
|
||||
|
||||
**在这里,你可以愉快的使用 React、TypesScript 写业务代码,尝试最新、最炫酷的 React Hooks 新特性,我们团队一直走在前端技术路线的最前沿,渴求技术创新。** 你也不需要担心伙伴的代码风格问题,因为我们有着严格的代码规;你不必担心每个人的代码都是一座孤岛,因为我们会对每一行代码做严格的 review;你不必担心你的成长空间,我们有定期的技术分享、团队内小竞赛,还有足够复杂的业务场景支撑;你也不必担心你会因工作日渐消瘦,下午茶和海量小零食等你来!
|
||||
|
||||
## 4 总结
|
||||
|
||||
**大数据前端人才缺口在 100 人以上,由于业务增长非常非常迅猛,春节前条件放宽、特批急召!**
|
||||
|
||||
如果你对我们感兴趣,请立刻把简历发送到邮箱 **ziyi.hzy@alibaba-inc.com** 吧!绝无仅有的好机会,响应速度绝对超乎你的想象!
|
||||
|
||||
> 讨论地址是:[精读《我在阿里数据中台大前端》 · Issue #224 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/224)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
@@ -0,0 +1,123 @@
|
||||
## 1 引言
|
||||
|
||||
本周精读的文章是 [Mastering JS console.log like a Pro](https://medium.com/javascript-in-plain-english/mastering-js-console-log-like-a-pro-1c634e6393f9),一起来更全面的认识 console 吧!
|
||||
|
||||
## 2 概述 & 精读
|
||||
|
||||
console 的功能主要在于控制台打印,它可以打印任何字符、对象、甚至 DOM 元素和系统信息,下面一一介绍。
|
||||
|
||||
### console.log( ) | info( ) | debug( ) | warn( ) | error( )
|
||||
|
||||
直接打印字符,区别在于展示形态的不同:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1xZ_WveH2gK0jSZFEXXcqMpXa-1492-566.png">
|
||||
|
||||
新版 chrome 控制台可以将打印信息分类:
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB1fZ2Vvhn1gK0jSZKPXXXvUXXa-420-446.png">
|
||||
|
||||
`log()` 与 `info()` 都对应 `info`,`warn()` 对应 `warnings`,`error()` 对应 `errors`,而 `debug()` 对应 `verbose`,因此建议在合适的场景使用合适的打印习惯,这样排查问题时也可以有针对性的筛选。
|
||||
|
||||
比如调试信息可以用 `console.debug` 仅在调试环境下输出,调试者即便开启了调试参数也不会影响正常 `info` 的查看,因为调试信息都输出在 `verbose` 中。
|
||||
|
||||
### 使用占位符
|
||||
|
||||
- %o — 对象
|
||||
- %s — 字符串
|
||||
- %d — 数字
|
||||
|
||||
如下所示,可通过占位符在一行中插入不同类型的值:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1GtL3vlr0gK0jSZFnXXbRRXXa-1840-504.png">
|
||||
|
||||
### 添加 CSS 样式
|
||||
|
||||
- %c - 样式
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1eK23vlr0gK0jSZFnXXbRRXXa-1832-978.png">
|
||||
|
||||
可以总结出,**console 支持输出复杂的内容,其输出能力堪比 HTML,但输入能力太弱,仅为字符串,因此采用了占位符 + 多入参修饰的设计模式解决这个问题。**
|
||||
|
||||
### console.dir( )
|
||||
|
||||
按 JSON 模式输出。笔者在这里也补充一句:`console.log()` 会自动判断类型,如果内容是 DOM 属性,则输出 DOM 树,但 `console.dir` 会强制以 JSON 模式输出,用在 DOM 对象时可强制转换为 JSON 输出。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1KQY1vbj1gK0jSZFuXXcrHpXa-922-302.png">
|
||||
|
||||
### 输出 HTML 元素
|
||||
|
||||
按照 HTML ELements 结构输出:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1mZ61va61gK0jSZFlXXXDKFXa-920-255.png">
|
||||
|
||||
这种输出结构和 Elements 打印形式是一致的,如果要看详细属性,可以使用 `console.dir()`。
|
||||
|
||||
### console.table
|
||||
|
||||
在控制台打印一个表格,属于功能增强。虽然仅文本也可以在控制台打印出漂亮的表格,但浏览器调试控制台的功能更强大,`console.table` 只是其富文本能力的一个体现。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1WldouKbviK0jSZFNXXaApXXa-928-742.png">
|
||||
|
||||
### console.group( ) & console.groupEnd( )
|
||||
|
||||
接下来是另一个富文本能力,按分组输出:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1UV6UvXY7gK0jSZKzXXaikpXa-919-377.png">
|
||||
|
||||
这种带有副作用的 API 显然是为方便阅读而设计的,然而在需要输出大量动态结构化数据的场景下,还需要进行结构转换,是比较麻烦的地方。
|
||||
|
||||
### console.count( )
|
||||
|
||||
`count()` 用来打印调用次数,一般用在循环或递归函数中。接收一个 `label` 参数以定制输出,默认直接输出 `1 2 3` 数字。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1ELLVveL2gK0jSZPhXXahvXXa-917-500.png">
|
||||
|
||||
### console.assert( )
|
||||
|
||||
`console` 版断言工具,当且仅当第一个参数值为 `false` 时才打印第二个参数作为输出。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1HEDUvfb2gK0jSZK9XXaEgFXa-1842-548.png">
|
||||
|
||||
这种输出结果为 error,所以也可被 `console.error` + 代码级别断言所取代。
|
||||
|
||||
### console.trace( )
|
||||
|
||||
打印此时的调用栈,在打印辅助调试信息时非常有用。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1Jh_YvkL0gK0jSZFAXXcA9pXa-1840-1096.png">
|
||||
|
||||
### console.time( )
|
||||
|
||||
打印代码执行时间,性能优化和监控场景比较常见。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1wAT2vbj1gK0jSZFuXXcrHpXa-1612-524.png">
|
||||
|
||||
### console.memory
|
||||
|
||||
打印内存使用情况。
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1tPHYvkL0gK0jSZFAXXcA9pXa-1842-440.png">
|
||||
|
||||
### console.clear( )
|
||||
|
||||
清空控制台输出。
|
||||
|
||||
## 3 总结
|
||||
|
||||
`console` 提供了如此多的输出规范,其实也是在变相制定开发规范,毕竟离开发者最近的就是调试控制台,如果你的项目打印规范与标准规范有差异,那么调试时信息看起来就会很别扭。
|
||||
|
||||
可以看到,大部分开源库都良好的遵循了这套规范,比如三方库绝不会输出 `log()`,而且将错误、警告与调试信息正确分开,并尽量少的用 CSS 样式、分组、`table` 等功能,因为这些功能干扰性较强,不能保证所有用户都可接受。
|
||||
|
||||
相对的,项目源码就比较适合使用一些醒目的自定义规范,只要这套规则能被很好的执行起来。
|
||||
|
||||
最后留下一个讨论点:`console` 可以作为调试、招聘信息、隐藏菜单的投放点,你还看到过哪些有意思的 `console` 使用方式呢?欢迎留言。
|
||||
|
||||
> 讨论地址是:[精读《精通 console.log》 · Issue #228 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/228)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
@@ -0,0 +1,279 @@
|
||||
## 1 引言
|
||||
|
||||
`JSON.parse` 是浏览器内置的 API,但如果面试官让你实现一个怎么办?好在有人已经帮忙做了这件事,本周我们一起精读这篇 [JSON Parser with Javascript](https://lihautan.com/json-parser-with-javascript/) 文章吧,再温习一遍大学时编译原理相关知识。
|
||||
|
||||
## 2 概述 & 精读
|
||||
|
||||
要解析 JSON 首先要理解语法概念,之前的 [精读《手写 SQL 编译器 - 语法分析》](https://github.com/dt-fe/weekly/blob/v2/066.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md) 系列也有介绍过,不过本文介绍的更形象,看下面这个语法图:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1EbjfvQL0gK0jSZFtXXXQCXXa-1837-857.png">
|
||||
|
||||
这是关于 Object 类型的语法描述图,从左向右看,根据箭头指向只要能走出这个迷宫就属于正确语法。
|
||||
|
||||
比如第一行 `{` → `whitespace` → `}` 表示 `{ }` 属于合法的 JSON 语法。
|
||||
|
||||
再比如观察向下的一条最长路线:`{` → `whitespace` → `string` → `whitespace` → `:` → `value` → `}` 表示 `{ string : value }` 属于合法的 JSON 语法。
|
||||
|
||||
你可能会问,双引号去哪儿了?这就是语法树最核心的概念了,这张图是关于 Object 类型的 **产生式**,同理还有 string、value 的产生式,产生式中可以嵌套其他产生式,甚至形成环路,以此拥有描述纷繁多变语法的能力。
|
||||
|
||||
最后我们再看一个环路,即 `{` → `whitespace` → `string` ... `,` → `whitespace` → `string` ... `,` ... `}`,我们发现,只要不走回头路,这条路是可以一直 “绕圈” 下去的,因此 Object 类型拥有了任意数量子字段的能力,只是每形成一个子字段,必须经过 `,` 号分割。
|
||||
|
||||
### 实现 Parser
|
||||
|
||||
首先实现一个基本结构:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
let i = 0;
|
||||
// TODO
|
||||
}
|
||||
```
|
||||
|
||||
`i` 表示访问字符的下标,当 `i` 走到字符串结尾表示遍历结束。
|
||||
|
||||
然后是下一步,用几个函数描述解析语法的过程:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
let i = 0;
|
||||
function parseObject() {
|
||||
if (str[i] === '{') {
|
||||
i++;
|
||||
skipWhitespace();
|
||||
|
||||
// if it is not '}',
|
||||
// we take the path of string -> whitespace -> ':' -> value -> ...
|
||||
while (str[i] !== '}') {
|
||||
const key = parseString();
|
||||
skipWhitespace();
|
||||
eatColon();
|
||||
const value = parseValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 `skipWhitespace` 表示匹配并跳过空格,所谓匹配意味着匹配成功,此时 `i` 下标可以继续后移,否则匹配失败。下一步则判断如果 `i` 不是结束标志 `}`,则按照 `parseString` 匹配字符串 → `skipWhitespace` 跳过空格 → `eatColon` 吃掉冒号 → `parseValue` 匹配值,这个链路循环。其中吃掉冒号表示 “匹配冒号但不会产生任何结果,所以就像吃掉了一样”,吃这个动作还可以用在其他场景,比如吃掉尾分号。
|
||||
|
||||
> 对于看到这儿的小伙伴,笔者要友情提示一下,原文的思路是一种定制语法解析思路,无论是 `eatColon` 还是 `parseValue` 都仅具备解析 JSON 的通用性,但不具备解析任意语法的通用性。如果你想做一个具备解析任何通用语法的解析器,读入的内容应该是语法描述,处理方式必须更加通用,如果感兴趣可以阅读 [精读《手写 SQL 编译器 - 语法分析》](https://github.com/dt-fe/weekly/blob/v2/066.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md) 系列文章了解更多。
|
||||
|
||||
由于 Object 第一个元素前面不允许加逗号,因此可以利用 `initial` 做一个初始化判定,在初始时机不会吃掉逗号:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
let i = 0;
|
||||
function parseObject() {
|
||||
if (str[i] === '{') {
|
||||
i++;
|
||||
skipWhitespace();
|
||||
|
||||
let initial = true;
|
||||
// if it is not '}',
|
||||
// we take the path of string -> whitespace -> ':' -> value -> ...
|
||||
while (str[i] !== '}') {
|
||||
if (!initial) {
|
||||
eatComma();
|
||||
skipWhitespace();
|
||||
}
|
||||
const key = parseString();
|
||||
skipWhitespace();
|
||||
eatColon();
|
||||
const value = parseValue();
|
||||
initial = false;
|
||||
}
|
||||
// move to the next character of '}'
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
那么当第一个子元素前面存在逗号时,由于没有 “吃掉逗号” 这个功能,所以读到逗号会报错,语法解析提前结束。
|
||||
|
||||
吃逗号和吃冒号的代码都非常简单,即判断当前字符串必须是 “要吃的那个元素”,并且在吃掉后将 `i` 下标自增 1:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
// ...
|
||||
function eatComma() {
|
||||
if (str[i] !== ',') {
|
||||
throw new Error('Expected ",".');
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
function eatColon() {
|
||||
if (str[i] !== ':') {
|
||||
throw new Error('Expected ":".');
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在有了基本判定功能后,`fakeParseJSON` 需要返回 Object,因此我们只需在每个循环中对 Object 赋值,最后一并 return 即可:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
let i = 0;
|
||||
function parseObject() {
|
||||
if (str[i] === '{') {
|
||||
i++;
|
||||
skipWhitespace();
|
||||
|
||||
const result = {};
|
||||
|
||||
let initial = true;
|
||||
// if it is not '}',
|
||||
// we take the path of string -> whitespace -> ':' -> value -> ...
|
||||
while (str[i] !== '}') {
|
||||
if (!initial) {
|
||||
eatComma();
|
||||
skipWhitespace();
|
||||
}
|
||||
const key = parseString();
|
||||
skipWhitespace();
|
||||
eatColon();
|
||||
const value = parseValue();
|
||||
result[key] = value;
|
||||
initial = false;
|
||||
}
|
||||
// move to the next character of '}'
|
||||
i++;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
解析 Object 的代码就完成了。
|
||||
|
||||
接着试着解析 Array,下面是 Array 的语法图:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1FvYjvKH2gK0jSZFEXXcqMpXa-1837-479.png">
|
||||
|
||||
我们只需要吃逗号和 `parseValue` 即可:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
// ...
|
||||
function parseArray() {
|
||||
if (str[i] === '[') {
|
||||
i++;
|
||||
skipWhitespace();
|
||||
|
||||
const result = [];
|
||||
let initial = true;
|
||||
while (str[i] !== ']') {
|
||||
if (!initial) {
|
||||
eatComma();
|
||||
}
|
||||
const value = parseValue();
|
||||
result.push(value);
|
||||
initial = false;
|
||||
}
|
||||
// move to the next character of ']'
|
||||
i++;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
接下来到了有趣的 `value` 语法图,可以看到 `value` 是许多种基础类型的 “或” 关系组成的:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB1uGrmvND1gK0jSZFyXXciOVXa-1836-1293.png">
|
||||
|
||||
我们只需要继续拆解分析即可:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
// ...
|
||||
function parseValue() {
|
||||
skipWhitespace();
|
||||
const value =
|
||||
parseString() ??
|
||||
parseNumber() ??
|
||||
parseObject() ??
|
||||
parseArray() ??
|
||||
parseKeyword('true', true) ??
|
||||
parseKeyword('false', false) ??
|
||||
parseKeyword('null', null);
|
||||
skipWhitespace();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
其中 `parseKeyword` 函数用来解析一些保留关键字,比如将 `"true"` 解析成布尔类型 `true`:
|
||||
|
||||
```js
|
||||
function fakeParseJSON(str) {
|
||||
// ...
|
||||
function parseKeyword(name, value) {
|
||||
if (str.slice(i, i + name.length) === name) {
|
||||
i += name.length;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如上所示,只要在 name 与对应字符相等时,返回第二个传入参数即可。
|
||||
|
||||
### 处理异常输入
|
||||
|
||||
一个完整的语法解析功能需要包含错误处理,错误的情况主要分两种:
|
||||
|
||||
1. 非法字符。
|
||||
2. 非正常结尾。
|
||||
|
||||
原文提到的 JSON 错误提示优化非常棒,想想你在开发中突然看到下面的提示,是不是很蒙圈:
|
||||
|
||||
```text
|
||||
Unexpected token "a"
|
||||
```
|
||||
|
||||
既然我们是自己写的 JSON 解析器,就可以进行更友好的异常提示,比如:
|
||||
|
||||
```text
|
||||
// show
|
||||
{ "b"a
|
||||
^
|
||||
JSON_ERROR_001 Unexpected token "a".
|
||||
Expecting a ":" over here, eg:
|
||||
{ "b": "bar" }
|
||||
^
|
||||
You can learn more about valid JSON string in http://goo.gl/xxxxx
|
||||
```
|
||||
|
||||
更多 Demo 可以查看 [原文](https://lihautan.com/json-parser-with-javascript/)。
|
||||
|
||||
## 3 总结
|
||||
|
||||
这篇文章通过一个具体的例子解释如何做语法分析,对于词法解析入门非常直观,如果你想更深入理解语法解析,或者写一个通用语法解析器,可以阅读语法解析系列入门文章,笔者通过实际例子带你一步一步做一个完备的词法解析工具!
|
||||
|
||||
语法解析入门系列文章,建议阅读顺序:
|
||||
|
||||
- [精读《手写 SQL 编译器 - 词法分析》](https://github.com/dt-fe/weekly/blob/v2/064.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 文法介绍》](https://github.com/dt-fe/weekly/blob/v2/065.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%96%87%E6%B3%95%E4%BB%8B%E7%BB%8D%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 语法分析》](https://github.com/dt-fe/weekly/blob/v2/066.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 回溯》](https://github.com/dt-fe/weekly/blob/v2/067.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E5%9B%9E%E6%BA%AF%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 语法树》](https://github.com/dt-fe/weekly/blob/v2/070.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%AD%E6%B3%95%E6%A0%91%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 错误提示》](https://github.com/dt-fe/weekly/blob/v2/071.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E9%94%99%E8%AF%AF%E6%8F%90%E7%A4%BA%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 性能优化之缓存》](https://github.com/dt-fe/weekly/blob/v2/078.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B9%8B%E7%BC%93%E5%AD%98%E3%80%8B.md)
|
||||
- [精读《手写 SQL 编译器 - 智能提示》](https://github.com/dt-fe/weekly/blob/v2/085.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA%E3%80%8B.md)
|
||||
|
||||
[syntax-parser](https://github.com/ascoders/syntax-parser) 这个零依赖的通用语法解析库就是根据上述文章一步一步完成的,看完了上面文章,就彻底理解了这个库的源码。
|
||||
|
||||
> 讨论地址是:[精读《手写 JSON Parser》 · Issue #233 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/233)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
@@ -0,0 +1,233 @@
|
||||
## 1 引言
|
||||
|
||||
拖拽是前端非常常见的交互操作,但显然拖拽是强 DOM 交互的,而 React 绕过了 DOM 这一层,那么基于 React 的拖拽方案就必定值得聊一聊。
|
||||
|
||||
结合 [How To Use The HTML Drag-And-Drop API In React](https://www.smashingmagazine.com/2020/02/html-drag-drop-api-react/) 这篇文章,让我们谈谈 React 拖拽这些事。
|
||||
|
||||
## 2 概述
|
||||
|
||||
原文说的比较简单,笔者先快速介绍其中重点部分。
|
||||
|
||||
首先拖拽主要的 API 有 4 个:`dragEnter` `dragLeave` `dragOver` `drop`,分别对应拖入、拖出、正在当前元素范围内拖拽、完成拖入动作。
|
||||
|
||||
基于这些 API,我们可以利用 React 实现一个拖入区域:
|
||||
|
||||
```jsx
|
||||
import React from "react";
|
||||
|
||||
const DragAndDrop = props => {
|
||||
const handleDragEnter = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleDragLeave = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleDragOver = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleDrop = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={"drag-drop-zone"}
|
||||
onDrop={e => handleDrop(e)}
|
||||
onDragOver={e => handleDragOver(e)}
|
||||
onDragEnter={e => handleDragEnter(e)}
|
||||
onDragLeave={e => handleDragLeave(e)}
|
||||
>
|
||||
<p>Drag files here to upload</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default DragAndDrop;
|
||||
```
|
||||
|
||||
`preventDefault` 指的是阻止默认响应,这个响应可能是跳转页面之类的,`stopPropagation` 是阻止冒泡,这样同样监听了事件的父元素就不会收到响应,我们可以精准作用于嵌套的子元素。
|
||||
|
||||
接下来是拖拽状态管理,提到了 `useReducer`,顺便复习一下用法:
|
||||
|
||||
```jsx
|
||||
...
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'SET_DROP_DEPTH':
|
||||
return { ...state, dropDepth: action.dropDepth }
|
||||
case 'SET_IN_DROP_ZONE':
|
||||
return { ...state, inDropZone: action.inDropZone };
|
||||
case 'ADD_FILE_TO_LIST':
|
||||
return { ...state, fileList: state.fileList.concat(action.files) };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
const [data, dispatch] = React.useReducer(
|
||||
reducer, { dropDepth: 0, inDropZone: false, fileList: [] }
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
最后一个关键点在于拖入后的处理,利用 `dispatch` 增加拖入文件、设置拖入状态即可:
|
||||
|
||||
```js
|
||||
const handleDrop = e => {
|
||||
...
|
||||
let files = [...e.dataTransfer.files];
|
||||
|
||||
if (files && files.length > 0) {
|
||||
const existingFiles = data.fileList.map(f => f.name)
|
||||
files = files.filter(f => !existingFiles.includes(f.name))
|
||||
|
||||
dispatch({ type: 'ADD_FILE_TO_LIST', files });
|
||||
e.dataTransfer.clearData();
|
||||
dispatch({ type: 'SET_DROP_DEPTH', dropDepth: 0 });
|
||||
dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: false });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
`e.dataTransfer.clearData` 函数用于清除拖拽过程中产生的临时变量,这些临时变量可以通过 `e.dataTransfer.xxx =` 的方式赋值,一般用于拖拽过程中值的传递。
|
||||
|
||||
总结一下,利用 HTML5 的 API 将拖拽转化为状态,最终通过状态映射到 UI。
|
||||
|
||||
原文内容还是比较简单的,笔者在精读部分再拓展一些更体系化的内容。
|
||||
|
||||
## 3 精读
|
||||
|
||||
现阶段拖拽主要分为两种,一种是 HTML5 原生规范的拖拽,这种方式在拖拽过程中不会影响 DOM 结构。另一种是完全所见即所得的拖拽方式,拖拽过程中 DOM 位置会随之变动,好处是可以立即反馈拖拽结果,当然缺点是华而不实,一旦用在生产环境,这种拖拽过程可能导致页面结构频繁跳动,反而看不清拖拽效果。
|
||||
|
||||
由于本文也采用了第一种拖拽方案,因为笔者再重新整理一遍自己的封装思路。
|
||||
|
||||
从使用角度反推,假设我们拥有一个拖拽库,那必定要拥有两个 API:
|
||||
|
||||
```jsx
|
||||
import { DragContainer, DropContainer } from 'dnd'
|
||||
|
||||
const DragItem = (
|
||||
<DragContainer>
|
||||
{({ dragProps }) => (
|
||||
<div {...dragProps} />
|
||||
)}
|
||||
</DragContainer>
|
||||
)
|
||||
|
||||
const DropItem = (
|
||||
<DropContainer>
|
||||
{({ dropProps }) => (
|
||||
<div {...dropProps} />
|
||||
)}
|
||||
</DropContainer>
|
||||
)
|
||||
```
|
||||
|
||||
`DragContainer` 包裹可以被拖拽的元素,`DropContainer` 包裹可以被拖入的元素,而至于 `dragProps` 与 `dropProps` 需要透传到子元素的 dom 节点,是为了利用 DOM API 控制拖拽效果,这也是拖拽唯一对 DOM 的要求,双方元素都需要有实体 DOM 承载。
|
||||
|
||||
而上面例子中给出 `dragProps` 与 `dropProps` 的方式属于 RenderProps,我们可以将 `children` 当作函数执行以达到效果:
|
||||
|
||||
```jsx
|
||||
const DragContainer = ({ children, componentId }) => {
|
||||
const { dragProps } = useDnd(componentId)
|
||||
|
||||
return children({
|
||||
dragProps
|
||||
})
|
||||
}
|
||||
|
||||
const DropContainer = ({ children, componentId }) => {
|
||||
const { dropProps } = useDnd(componentId)
|
||||
|
||||
return children({
|
||||
dropProps
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
那么这里创建了一个自定义 Hook `useDnd` 接收 `dragProps` 与 `dropProps`,这个自定义 Hook 可以这么写:
|
||||
|
||||
```jsx
|
||||
const useDnd = ({ componentId }) => {
|
||||
const dragProps = {}
|
||||
const dropProps = {}
|
||||
|
||||
return { dragProps, dropProps }
|
||||
}
|
||||
```
|
||||
|
||||
接下来,我们就要分别实现 `drag` 与 `drop` 了。
|
||||
|
||||
对 `drag` 来说,只要实现 `onDragStart` 与 `onDragEnd` 即可:
|
||||
|
||||
```jsx
|
||||
const dragProps = {
|
||||
onDragStart: ev => {
|
||||
ev.stopPropagation()
|
||||
ev.dataTransfer.setData('componentId', componentId)
|
||||
},
|
||||
onDragEnd: ev => {
|
||||
// 做一些拖拽结束的清理工作
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`stopPropagation` 的作用在原文简介中已经介绍过了,`setData` 则是通知拖拽方,当前拖拽的组件 id 是什么,**这是由于拖拽由 `drag` 发起而由 `drop` 响应,因此必须有个数据传输过程,而 `dataTransfer` 就最适合做这件事。**
|
||||
|
||||
对于 `drop` 来说,只要实现 `onDragOver` 与 `onDrop` 即可:
|
||||
|
||||
```jsx
|
||||
const dropProps = {
|
||||
onDragOver: ev => {
|
||||
// 做一些样式处理,提示用户此时松手会将元素放置在何处
|
||||
},
|
||||
onDrop: ev => {
|
||||
ev.stopPropagation()
|
||||
const componentId = ev.dataTransfer.getData('componentId')
|
||||
// 通过 componentId 修改数据,通过 React Rerender 刷新 UI
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
重点在 `onDrop`,它是实现拖拽效果的 “真正执行处”,最终通过修改 UI 的方式更新数据。
|
||||
|
||||
存在一种场景,一个容器既可以被拖动,也可以被拖入,这种情况一般这个组件是个容器,但这个容器可以被拖入到其他容器中,可以自由嵌套。
|
||||
|
||||
实现这种场景的方式就是将 `DragContainer` 与 `DropContainer` 作用到一个组件上:
|
||||
|
||||
```jsx
|
||||
const Box = (
|
||||
<DragContainer>
|
||||
{({ dragProps }) => (
|
||||
<DropContainer>
|
||||
{({ dropProps }) => {
|
||||
<div {...dragProps} {...dropProps} />
|
||||
}}
|
||||
</DropContainer>
|
||||
)}
|
||||
</DragContainer>
|
||||
)
|
||||
```
|
||||
|
||||
之所以能嵌套,在于 HTML5 的 API 允许一个元素同时拥有 `onDragStart`、`onDrop` 这两种属性,而上面的语法不过是同时将这两种属性传给组件 DOM。
|
||||
|
||||
所以,动手实现一个拖拽库就是这么简单,只要活用 HTML5 的拖拽 API,结合 React 一些特殊语法便够了。
|
||||
|
||||
## 4 总结
|
||||
|
||||
最后留下一个思考题,许多具有拖拽功能的系统都具备 “拖拽 placeholder” 的功能,即拖拽元素的过程中,在其 “落点” 位置展示一条横线或竖线,引导出松手后元素位置落点,如图所示:
|
||||
|
||||
<img width=400 src="https://img.alicdn.com/tfs/TB11H04wbY1gK0jSZTEXXXDQVXa-1434-384.png">
|
||||
|
||||
那么这条辅助线是通过什么方式实现的呢?欢迎在评论区留言!如果你有辅助线实现方案解析的文章,欢迎分享,也可以期待笔者未来专门写一篇 “拖拽 placeholder” 实现剖析的精读。
|
||||
|
||||
> 讨论地址是:[精读《手写 JSON Parser》 · Issue #233 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/233)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||
@@ -0,0 +1,108 @@
|
||||
## 1 引言
|
||||
|
||||
`useRef` 是常用的 API,但还有一个 `createRef` 的 API,你知道他们的区别吗?通过 [React.useRef and React.createRef: The Difference](https://blog.bitsrc.io/react-useref-and-react-createref-the-difference-afedb9877d0f) 这篇文章,你可以了解到何时该使用它们。
|
||||
|
||||
## 2 概述
|
||||
|
||||
其实原文就阐述了这样一个事实:`useRef` 仅能用在 FunctionComponent,`createRef` 仅能用在 ClassComponent。
|
||||
|
||||
第一句话是显然的,因为 Hooks 不能用在 ClassComponent。
|
||||
|
||||
第二句话的原因是,`createRef` 并没有 Hooks 的效果,其值会随着 FunctionComponent 重复执行而不断被初始化:
|
||||
|
||||
```tsx
|
||||
function App() {
|
||||
// 错误用法,永远也拿不到 ref
|
||||
const valueRef = React.createRef();
|
||||
return <div ref={valueRef} />;
|
||||
}
|
||||
```
|
||||
|
||||
上述 `valueRef` 会随着 App 函数的 Render 而重复初始化,**这也是 Hooks 的独特之处,虽然用在普通函数中,但在 React 引擎中会得到超出普通函数的表现,比如初始化仅执行一次,或者引用不变**。
|
||||
|
||||
为什么 `createRef` 可以在 ClassComponent 正常运行呢?这是因为 ClassComponent 分离了生命周期,使例如 `componentDidMount` 等初始化时机仅执行一次。
|
||||
|
||||
原文完。
|
||||
|
||||
## 3 精读
|
||||
|
||||
那么知道如何正确创建 Ref 后,还知道如何正确更新 Ref 吗?
|
||||
|
||||
由于 Ref 是贯穿 FunctionComponent 所有渲染周期的实例,理论上在任何地方都可以做修改,比如:
|
||||
|
||||
```tsx
|
||||
function App() {
|
||||
const valueRef = React.useRef();
|
||||
|
||||
valueRef.current += 1;
|
||||
|
||||
return <div />;
|
||||
}
|
||||
```
|
||||
|
||||
但其实上面的修改方式是不规范的,React 官方文档里要求我们避免在 Render 函数中直接修改 Ref,请先看下面的 FunctionComponent 生命周期图:
|
||||
|
||||
<img width=600 src="https://img.alicdn.com/tfs/TB12aHDwQL0gK0jSZFtXXXQCXXa-3300-2550.png">
|
||||
|
||||
从图中可以发现,在 `Render phase` 阶段是不允许做 “side effects” 的,也就是写副作用代码,这是因为这个阶段可能会被 React 引擎随时取消或重做。
|
||||
|
||||
修改 Ref 属于副作用操作,因此不适合在这个阶段进行。我们可以看到,在 `Commit phase` 阶段可以做这件事,或者在回调函数中做(脱离了 React 生命周期)。
|
||||
|
||||
当然有一种情况是可以的,即 [懒初始化](https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily):
|
||||
|
||||
```ts
|
||||
function Image(props) {
|
||||
const ref = useRef(null);
|
||||
|
||||
// ✅ IntersectionObserver is created lazily once
|
||||
function getObserver() {
|
||||
if (ref.current === null) {
|
||||
ref.current = new IntersectionObserver(onIntersect);
|
||||
}
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
// When you need it, call getObserver()
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
懒初始化的情况下,副作用最多执行一次,而且仅用于初始化赋值,所以这种行为是被允许的。
|
||||
|
||||
为什么对副作用限制的如此严格?因为 FunctionComponent 增加了内置调度系统,为了优先响应用户操作,可能会暂定某个 React 组件的渲染,具体可以看第 99 篇精读:[精读《Scheduling in React》](https://github.com/dt-fe/weekly/blob/v2/099.%E7%B2%BE%E8%AF%BB%E3%80%8AScheduling%20in%20React%E3%80%8B.md)
|
||||
|
||||
Ref 不仅可以拿到组件引用、创建一个 Mutable 副作用对象,还可以配合 `useEffect` 存储一个较老的值,最常用来拿到 `previousProps`,React 官方利用 Ref 封装了一个简单的 Hooks 拿到上一次的值:
|
||||
|
||||
```tsx
|
||||
function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
return ref.current;
|
||||
}
|
||||
```
|
||||
|
||||
由于 `useEffect` 在 Render 完毕后才执行,因此 `ref` 的值在当前 Render 中永远是上一次 Render 时候的,我们可以利用它拿到上一次 Props:
|
||||
|
||||
```tsx
|
||||
function App(props) {
|
||||
const preProps = usePrevious(props);
|
||||
}
|
||||
```
|
||||
|
||||
要实现这个功能,还是要归功于 `ref` 可以将值 “在各个不同的 Render 闭包中传递的特性”。最后,不要滥用 Ref,Mutable 引用越多,对 React 来说可维护性一般会越差。
|
||||
|
||||
## 4 总结
|
||||
|
||||
你还挖掘了 `useRef` 哪些有意思的使用方式?欢迎在评论区留言。
|
||||
|
||||
> 讨论地址是:[精读《useRef 与 createRef 的区别》 · Issue #236 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/236)
|
||||
|
||||
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
|
||||
|
||||
> 关注 **前端精读微信公众号**
|
||||
|
||||
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
|
||||
|
||||
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))
|
||||