— 配置改变后,主节点新的 IP 和地址都是指定的。这是大多数外部用户感兴趣的消息。
-- +tilt — 进入 Tilt 模式。
-- -tilt — 退出 Tilt 模式。
-
-### BUSY 状态的处理
-
-当一个 Lua 脚本的运行时间超过了配置中指定的 Lua 脚本时间限制,Redis 实例将返回 -BUSY 错误。当这个发生的时候,在触发故障转移之前 Redis Sentinel 将尝试发送 SCRIPT KILL 命令,如果脚本是只读的,就会成功。
-
-如果在这个尝试后,实例仍然处于失败情况,它最后会开始故障转移。
-
-#### 从节点优先
-
-Redis 实例有个配置参数叫 slave-priority。这个信息在 Redis 从节点实例的 INFO 输出中展示出来,并且 Sentinel 使用它来选择一个从节点在一次故障转移中:
-
-1. 如果从节点的优先级被设置为 0,这个从节点永远不会被提升为主节点。
-2. Sentinel 首选一个由更低( lower)优先级的从节点。
-
-比如在当前主节点的同一个数据中心有一个从节点 S1,并且有另外的从节点 S2 在另外的数据中心,可以将 S1 优先级设置为 10,S2 优先级设置为 100,如果主节点挂掉了并且 S1 和 S2 都是可用的,S1 将是首选的。
-
-查看关于从节点选举的更多信息,请查看本文章的 slave selection and priority 章节。
-
-#### Sentinel 和 Redis 权限
-
-当主节点被配置为从客户端需要密码,作为一个安全措施,从节点也需要知道这个密码为了主节点认证并且创建主-从连接用于异步复制协议。
-
-使用下列的配置选项来实现:
-
-- requirepass 在主节点中,为了设置认证密码,并且确保实例不会处理来自没有认证的客户端的请求。
-- masterauth 在从节点中,为了取得主节点的认证,来从主节点正确的复制 数据。
-
-当 Sentinel 使用的时候,没有一个单独的主节点,因为一次故障转移过后,从节点将扮演主节点的角色,并且老的主节点被重新配置作为一个从节点,所以你要做的是在全部的实例中设置上面的选项,包括主节点和从节点。
-
-这通常是一个理智的设置,因为你不想要仅仅在主节点中保护你的数据,在从节点中有同样的数据。
-
-然而,在罕见的情况下,你需要一个从节点是可进入的而不需要认证,你可以设置一个优先级为 0 的从节点来实现,阻止这个从节点被提升为主节点,配置这个从节点的 masterauth 选项,不要使用 requirepass 选项,以便数据可以被读在没有认证的情况下。
-
-#### Sentinel 客户端实现
-
-Sentinel 需要显式的客户端支持,除非系统配置为执行脚本来执行一个透明的重定向对于所有的主节点实例的请求(虚拟 IP 或类似的系统)。可以参考文档 Sentinel clients guidelines。
-
-## 更高级的概念
-
-下面的章节是关于 Sentinel 怎么工作的一些细节,没有付诸于实现的想法和算法在文章的最后章节。
-
-### SDOWN 和 ODOWN 失败状态
-
-Redis Sentine 有两个不同概念的下线,一个被称为主观下线(Subjectively Down )条件(SDOWN),是一个本地 Sentinel 实例下线条件。另一个被称为客观下线(Objectively Down )条件(ODOWN),是当足够的 Sentinels 具有 SDOWN 条件就满足 ODOWN,并且从其他的 Sentinels 使用 SENTINEL is-master-down-by-addr 命令得到反馈。
-
-从一个 Sentinel 的角度来看,满足一个 SDOWN 条件就是在指定的时间内对于 PING 请求不能收到有效的回复,这个时间在配置文件中是 is-master-down-after-milliseconds 参数。
-
-一个 PING 请求可接受的回复是下列之一:
-
-回复+PONG。
-回复 -LOADING 错误。
-回复-MASTERDOWN 错误。
-其他的回复(或根本没有回复)被认为是无效的。注意一个合理的主节点在 INFO 输出中通知他自己是一个从节点被认为是下线的。
-
-注意 SDOWN 需要在配置中整个的时间间隔都没有收到有效的回复,因此对于实例如果时间间隔是 30000 毫秒,并且我们每隔 29 秒收到有效的回复,这个实例就被认为在工作。
-
-SDOWN 还不够触发故障转移:它仅仅意味着一个单独的 Sentinel 相信一个 Redis 实例不可达。要触发故障转移,必须达到 ODOWN 状态。
-
-从 SDOWN 转换到 ODOWN,没有使用强一致性算法,而仅仅是 gossip 的形式:如果一个 Sentinel 在一个给定的时间范围内从足够的 Sentinels 得到一个报告说一个主节点没有在工作,SDOWN 被提升为 ODOWN。如果这个确认稍候消失,这个标识也会清除。
-
-一个更加严格的授权是使用大多数需要为了真正的开始故障转移,但是在达到 ODOWN 状态之前不会触发故障转移。
-
-ODOWN 条件只适用于主节点。对于其他类型的实例,Sentinel 不需要采取行动,所以对于从节点和其他的 sentinels 来说 ODOWN 状态永远不可能达到,而仅仅只有 SDOWN 状态。
-
-然而 SDOWN 也有语义的影响,比如一个从节点在 SDOWN 状态不会被选举来提升来执行一个故障转移。
-
-### Sentinels 和从节点自动发现
-
-Sentinels 和其他的 Sentinels 保持连接为了互相之间检查是否可达和交换消息。然而你不需要在每个运行的 Sentinel 实例中配置其他的 Sentinel 地址列表,Sentinel 使用 Redis 实例的发布/订阅能力来发现其他的监控相同的主节点和从节点的 Sentinels。
-
-通过往名称为**sentinel**:hello 的通道发送 hello 消息(hello messages)来实现这个特性。
-
-同样的,你不需要配置一个主节点关联的从节点的列表,Sentinel 也会自动发现这个列表通过问询 Redis:
-
-- 每隔两秒,每个 Sentinel 向每个监控的主节点和从节点的发布/订阅通道**sentinel**:hello 来公布一个消息,宣布它自己的 IP,端口,id。
-- 每个 Sentinel 都订阅每个主节点和从节点的发布/订阅通道**sentinel**:hello,寻找未知的 sentinels。当新的 sentinels 被检测到,他们增加这个主节点的 sentinels。
-- Hello 消息也包含主节点的全部配置信息,如果接收的 Sentinel 有一个更旧的配置,它会立即更新它的配置。
-- 在增加一个主节点的新的 sentinel 之前,Sentinel 总是要检查是否已经有一个有相同的 id、地址的 sentinel。在这种情况下,所有匹配的 sentinels 被移除,新的被增加。
-
-### 故障转移之外重新配置
-
-即使没有故障转移,Sentinels 将尝试设置当前的配置到监控的实例上面。
-
-特别的:
-
-- 从节点声称为主节点,将被作为从节点配置来复制当前的主节点。
-- 从节点连接了一个错误的主节点,也会被重新配置来复制正确的主节点。
-
-Sentinels 重新配置从节点,错误的配置在一段时间内应该被观察到,比在广播新的配置的时候要好得多。
-
-这个阻止了有一个过时配置(比如说从一个分区中重新加入)的 Sentinels 在收到更新之前去交换从节点的配置。
-
-同样注意:
-
-- 主节点的故障转移被重新配置作为从节点当他们返回可用的时候
-- 在一个网络分区中,从节点一旦可达,被重新配置。
-
-本章最重要的教训就是:Sentinels 是每个进程总是尝试去把最后的配置施加到监控的实例上的一个系统。
-
-### 从节点选举和优先级
-
-当一个 Sentinel 实例准备执行故障转移,因为主节点在 ODOWN 状态下并且 Sentinel 从大多数已知的 Sentinel 实例中收到了授权开始故障转移,一个合适的从节点要被选举出来。
-
-从节点选举过程评估从节点的下列信息:
-
-1. 与主节点断开的时间
-2. 从节点优先级
-3. 复制偏移处理
-4. 运行 ID
-
-一个从节点被发现从主节点断开超过主节点配置超时(down-after-milliseconds 选项)时间十倍以上,加上从正在执行故障转移的 Sentinel 的角度看主节点不可用的时间,将被认为是不合适的并且会被跳过。
-
-在更严格的条件下,一个从节点的 INFO 输出建议了从主节点断开超过:
-
-```
-(down-after-milliseconds \* 10) + milliseconds_since_master_is_in_SDOWN_state
-```
-
-被认为是不可靠的并且会被无视。
-
-从节点选举只会考虑通过了上述测试的从节点,并根据上面的条件进行排序,以下列顺序:
-
-1. 根据 Redis 实例中的 redis.conf 文件中配置的 slave-priority 进行排序,更低的优先级会被优先。
-2. 如果优先级相同,检查复制偏移处理,从主节点收到更加新的数据的从节点会被选择。
-3. 如果多个从节点有相同的优先级和数据偏移,执行进一步检查,选择有着更小运行 ID 的从节点。有一个更小的 ID 并不是具有正真的优点,但是对于从节点选举来说更确定,而不是随机选择一个从节点。
-
-如果有机器是首选,Redis 主节点、从节点必须被配置一个 slave-priority。否则,所有的实例都有一个默认的 ID。
-
-一个 Redis 实例可以被配置指定 slave-priority 为 0 为了永远不被 Sentinels 选择为新的主节点。然而一个这样配置的从节点会被 Sentinels 重新配置,为了在一次故障转移后复制新的主节点,唯一不同的是,它永远不会成为主节点。
-
-## 算法和内部结构
-
-下面的章节,我们将会探索 Sentinel 特性的细节。对于使用者来说,并不需要知道全部的细节,但是一个更深入的理解可能会帮助部署和操作 Sentinel 以一个更加有效的方式。
-
-### Quorum
-
-前面的章节展示了每个被 Sentinel 监控的主节点和一个配置的 quorum 相关联。它指定了需要同意主节点是不可达或者错误的 Sentinel 进程的数量为了触发一次故障转移。
-
-可是,在故障转移触发后,为了真正地执行故障转移,至少大多数的 Sentinels 必须授权一个 Sentinel 开始故障转移。当只有小部分的 Sentinels 存在的一个网络分区中,故障转移永远不会执行。
-
-我们尝试让这件事更加清晰:
-
-- Quorum:为了把一个主节点标记成 ODOWN,需要的 Sentinel 进程数量来发现错误条件。
-- ODOWN 状态触发故障转移。
-- 一旦故障转移被触发,Sentinel 尝试向大多数的 Sentinels 请求授权。
-
-不同之处看起来很微妙,但是实际上很简单地理解和使用。如果你又 5 个 Sentinel 实例,quorum 被设置为 2,一旦 2 个 Sentinel 认为主节点不可达,故障转移就会被触发。然而 2 个 Sentinels 中的一个得到 3 个 Sentinels 的授权才开始故障转移。
-
-把 quorum 设置为 5,必须所有的 Sentinels 同意主节点失败,并为了开始故障转移,需要得到所有 Sentinels 的授权。
-
-这意味着 quorum 在两方面可以被用来调整 Sentinel:
-
-- 如果 quorum 被设置为小于我们部署的 Sentinels 大多数,我们使 Sentinel 对主节点失败更加敏感,并一旦少数的 Sentinels 不再和主节点交流就会触发故障转移。
-- 如果 quorum 被设置为大于我们部署的 Sentinels 大多数,仅仅当大多数连接良好的 Sentinels 同意主节点挂掉的时候,Sentinel 才能开始故障转移。
-
-### 配置 epochs
-
-为了开始故障转移,Sentinels 需要从大多数得到授权,有下面几个重要的原因:
-
-当一个 Sentinel 被授权,它为故障转移的主节点获得一个独一无二的配置 epoch。这将用来标识新的配置的版本在故障转移完成之后。因为大多数同意一个版本被分配给指定的 Sentinel,没有其他的 Sentinel 可以使用它。这意味着,每次故障转移的配置都有一个独一无二的版本号。我们将看到这为什么是很重要的。
-
-此外 Sentinels 有一个规则:如果一个 Sentinel 投票给其他的 Sentinel 在一次故障转移中,它将等待一段时间再次尝试故障转移这个主节点,你可以在 sentinel.conf 中配置这个延迟时间 failover-timeout。这意味着 Sentinels 在相同的时间内不会尝试故障转移相同的主节点,第一次请求授权的将会尝试,如果失败了,另一个将会在一段时间后尝试,等等。
-
-Redis Sentinel 保证了活性(liveness)性质,如果大多数 Sentinels 能够交流。如果主节点挂了,最后将有一个会被授权开始故障转移。
-
-Redis Sentinel 同样也保证了安全(safety )性质,每个 Sentinel 将使用不同的配置 epoch(configuration epoch)来故障转移同一个主节点。
-
-### 配置传播
-
-一旦一个 Sentinel 能成功的故障转移一个主节点,它将开始广播新的配置以便其他的 Sentinels 更新他们关于主节点的信息。
-
-为了认定一次故障转移是成功的,它需要 Sentinel 能发送 SLAVEOF NO ONE 指令给选举的从节点,并且切换为主节点,稍后能在主节点的 INFO 输出中观察到。
-
-这时候,即使从节点的重新配置正在进行,故障转移也被认为是成功的,并且所有的 Sentinels 需要开始报告新的配置。
-
-一个新的配置广播的方式,就是为什么我们需要每次 Sentinel 被授权故障转移时有一个不同的版本号的原因。
-
-每个 Sentinel 使用 Redis 发布/订阅消息来连续不断的广播它的一个主节点的配置的版本号,所有的从节点和主节点。同时,所有的 Sentinels 等待消息来查看其他的 Sentinels 广播的配置。
-
-配置在**sentinel**:hello 发布/订阅频道中被广播。
-
-因为每个配置都有一个不同的版本号,大的版本号总是赢得小的版本号。
-
-例如 一开始所有的 Sentinels 认为主节点 mymaster 的配置为 192.168.1.50:6379。这个配置的版本号为 1。一段时间后,一个被授权开始故障转移有版本号 2,如果故障转移成功,它将广播新的配置,它说是 192.168.1.50:9000,版本号为 2,。所有其他的实例将看到这个配置并更新他们的配置,因为新的配置有更高的版本号。
-
-这意味着 Sentinel 保证第二个活性属性:一个 Sentinels 集合能互相交流并且把配置信息收敛到一个更高的版本号。
-
-基本上,如果网络是分区的,每个分区将收敛到一个更高的本地配置。没有网络分区的特殊性情况下,只有一个分区并且每个 Sentinel 将同意配置。
-
-分区下的一致性
-
-Redis Sentinel 配置是最终一致的,所以每个分区将收敛到更高的可用的配置。然而在使用 Sentinel 的真实世界的系统中,有三个不同的角色:
-
-- Redis 实例
-- Sentinel 实例
-- 客户端
-
-为了定义系统的行为,我们考虑所有的三种。
-
-下面是一个简单的有三个节点的网络,每个都运行一个 Redis 实例和一个 Sentinel 实例:
-
-```
- +-------------+
- | Sentinel 1 |----- Client A
- | Redis 1 (M) |
- +-------------+
- |
- |
-+-------------+ | +------------+
-| Sentinel 2 |-----+-- // ----| Sentinel 3 |----- Client B
-| Redis 2 (S) | | Redis 3 (M)|
-+-------------+ +------------+
-```
-
-在这个系统中,原始状态是 Redis3 是主节点,Redis1 和 Redis2 是从节点。一个网络分区发生隔离了旧的主节点。Sentinels1 和 2 开始一个故障转移过程提升 Sentinel 1 为新的主节点。
-
-Sentinel 的属性保证 Sentinel 1 和 2 现在有了一个主节点的新的配置。可是 Sentinel 3 依然有旧的配置因为它在一个不同的分区中存活。
-
-我们知道 Sentinel 3 将得到他的配置更新当网络分区治愈的时候,但是如果有客户端和旧的主节点在一起,分区时会发生什么呢?
-
-客户端仍然可以向 Redis 3 写入数据。当网络分区治愈,Redis 3 变成 Reids 1 的一个从节点,在分区期间写入的数据都会丢失。
-
-根据你的配置,你可以想或不想让这种情况发生:
-
-- 如果你使用 Redis 作为缓存,客户端 B 仍然可以向旧的主节点写入数据是很方便的,即使数据将会丢失。
-- 如果你使用 Redis 作为存储,这是不好的,你需要配置系统为了部分的阻止这个问题。
-
-因为 Redis 是异步复制的,这种情况下,没有办法完全阻止数据丢失,但是你可以使用下面的 Redis 配置选项来限制 Redis 3 和 Redis 1 之间的分歧:
-
-```
-min-slaves-to-write 1
-min-slaves-max-lag 10
-```
-
-当一个 Redis 有上面的配置,当作为主节点的时候,如果他不能向至少一个从节点写入数据,将会停止接受写入请求。因为复制是异步的,不能写(not being able to write )意味着从节点都是分离的,或者没有发送异步确认超过了指定的 max-lag 的时间。
-
-使用这个配置,上面的例子中的 Redis 3 将会在 10 秒之后变得不可用。当分区治愈,Sentinel 3 的配置将会是新的,Client B 能获取到一个有效的配置并继续工作。
-
-总之, Redis + Sentinel 是一个最终一致性系统( eventually consistent system),功能是最后一个故障转移获胜( last failover wins)。旧节点中的数据会被丢弃,从当前主节点复制数据,所以总有一个丢失确认写的窗口。这是由于 Redis 的异步复制和系统的“虚拟”合并功能的丢弃性质。注意,Sentinel 本身没有限制,如果你把故障转移编排起来,相同的属性仍然适用,仅仅有两种方式来避免丢失写入确认:
-
-使用同步复制
-使用一个最终一致的系统,相同物体的不同版本能被合并
-Redis 现在不能使用上面的任何系统,是目前的发展目标。可是有一个代理实现解决方案 2 在 Redis 存储之上,比如说 SoundCloud Roshi,或者 Netflix Dynomite。
-
-### Sentinel 持久化状态
-
-Sentinel 状态保存在 sentinel 配置文件中。例如,每次一个收到一个新的配置,主节点,配置和配置 epoch 一起被保存在磁盘上。这意味着停止和重启 Sentinel 进程是很安全的。
-
-### TILT 模式
-
-Redis Sentinel 严重依赖电脑时间:例如为了推断一个实例是否可达,它会记住最后一次成功回复 PING 命令的时间,并和当前时间比较来推断哪个是旧的。
-
-可是,如果电脑时间意外改变了,或者电脑非常繁忙,或进程由于某些原因阻塞。Sentinel 或许开始表现意外的行为。
-
-TILT 模式是一个特殊的“保护”模式,当发现奇怪的事情可能降低系统的可靠性,一个 Sentinel 可以进入这个模式。Sentinel 定时中断调用每秒 10 次,所以我们期待定时中断调用之间间隔 100 毫米左右。
-
-Sentinel 所做的就是登记之前的中断调用时间,并和当前的调用时间比较:如果结果是负数或意外的数,将会进入 TILT 模式。
-
-当处于 Sentinel 模式 Sentinel 将会继续监控每件事,但是:
-
-- 停止一切动作
-- 开始回复负数给 SENTINEL is-master-down-by-addr 请求让检测失败不再有信
-- 如果 30 秒内每件事都表现正常,将退出 TILT 模式。
-
-注意某些情况下,使用许多内核提供的单调时钟 API 代替 TILT 模式。可是它仍然是不清晰的如果这是一个很好的解决方案,因为在进程只是仅仅挂起或调度很长时间没有执行的情况下,当前的系统会避免这个问题。
diff --git "a/database/nosql/redis/Redis\345\244\215\345\210\266.md" "b/database/nosql/redis/Redis\345\244\215\345\210\266.md"
deleted file mode 100644
index dccc57201f..0000000000
--- "a/database/nosql/redis/Redis\345\244\215\345\210\266.md"
+++ /dev/null
@@ -1,145 +0,0 @@
----
-title: Redis 复制
-date: 2018/06/19
-categories:
-- database
-tags:
-- database
-- nosql
----
-
-# Redis 复制
-
-
-
-- [Redis 复制要点](#redis-复制要点)
-- [当主服务器不进行持久化时复制的安全性](#当主服务器不进行持久化时复制的安全性)
-- [Redis 主从复制是如何工作的](#redis-主从复制是如何工作的)
-- [部分重新同步](#部分重新同步)
-- [无磁盘复制](#无磁盘复制)
-- [配置](#配置)
-- [只读从服务器](#只读从服务器)
-- [设置从服务器到主服务器验证](#设置从服务器到主服务器验证)
-- [限制有 N 个以上从服务器才允许写入](#限制有-n-个以上从服务器才允许写入)
-- [资料](#资料)
-
-
-
-## Redis 复制要点
-
-- Redis 使用异步复制。但从 Redis 2.8 开始,从服务器会周期性的应答从复制流中处理的数据量。
-- 一个主服务器(master)可以有多个从服务器(slave)。
-- 从服务器也可以接受其他从服务器的连接。除了多个从服务器连接到一个主服务器之外,多个从服务器也可以连接到一个从服务器上,形成一个图状结构
-- Redis 主从复制不阻塞主服务器端。也就是说当若干个从服务器在进行初始同步时,主服务器仍然可以处理请求。
-- 主从复制也不阻塞从服务器端。当从服务器进行初始同步时,它使用旧版本的数据来应对查询请求,假设你在 redis.conf 配置文件是这么配置的。否则的话,你可以配置当复制流关闭时让从服务器给客户端返回一个错误。但是,当初始同步完成后,需要删除旧的数据集和加载新的数据集,在这个短暂的时间内,从服务器会阻塞连接进来的请求。
-- 主从复制可以用来增强扩展性,使用多个从服务器来处理只读的请求(比如,繁重的排序操作可以放到从服务器去做),也可以简单的用来做数据冗余。
-- 使用主从复制可以为主服务器免除把数据写入磁盘的消耗:在主服务器的 redis.conf 文件中配置“避免保存”(注释掉所有“保存“命令),然后连接一个配置为“进行保存”的从服务器即可。但是这个配置要确保主服务器不会自动重启(要获得更多信息请阅读下一段)
-
-## 当主服务器不进行持久化时复制的安全性
-
-在使用 Redis 复制功能时的设置中,强烈建议在 master 和在 slave 中启用持久化。当不可能启用时,例如由于非常慢的磁盘性能而导致的延迟问题,**应该配置实例来避免重置后自动重启**。
-
-关闭了持久化并配置了自动重启的 master 是危险的,原因如下:
-
-- 我们设置节点 A 为 master 并关闭它的持久化设置,节点 B 和 C 从 节点 A 复制数据。
-- 节点 A 崩溃,但是他有一些自动重启的系统可以重启进程。但是由于持久化被关闭了,节点重启后其数据集合为空。
-- 节点 B 和 节点 C 会从节点 A 复制数据,但是节点 A 的数据集是空的,因此复制的结果是它们会销毁自身之前的数据副本。
-
-当在高可用系统中使用 Redis Sentinel,关闭了主服务器的持久化,并且允许自动重启,这种情况是很危险的。比如主服务器可能在很短的时间就完成了重启,以至于 Sentinel 都无法检测到这次失败,那么上面说的这种失败的情况就发生了。
-
-如果数据比较重要,并且在使用主从复制时关闭了主服务器持久化功能的场景中,都应该禁止实例自动重启。
-
-## Redis 主从复制是如何工作的
-
-如果你设置了一个从服务器,在连接时它发送了一个 SYNC 命令,不管它是第一次连接还是再次连接都没有关系。
-
-然后主服务器**开始后台存储**,并且开始缓存新连接进来的修改数据的命令。**当后台存储完成后,主服务器把数据文件发送到从服务器,从服务器将其保存在磁盘上,然后加载到内存中**。然后主服务器把刚才缓存的命令发送到从服务器。这是作为命令流来完成的,并且和 Redis 协议本身格式相同。
-
-你可以通过 telnet 自己尝试一下。在 Redis 服务器工作时连接到 Redis 端口,发送 SYNC 命令,会看到一个批量的传输,并且主服务器接收的每一个命令都会通过 telnet 会话重新发送一遍。
-
-当主从服务器之间的连接由于某些原因断开时,从服务器可以自动进行重连接。当有多个从服务器同时请求同步时,主服务器只进行一个后台存储。
-
-当连接断开又重新连上之后,一般都会进行一个完整的重新同步,但是从 Redis2.8 开始,只重新同步一部分也可以。
-
-## 部分重新同步
-
-从 Redis 2.8 开始,如果遭遇连接断开,重新连接之后可以从中断处继续进行复制,而不必重新同步。
-
-它的工作原理是这样,**主服务器端为复制流维护一个内存缓冲区(in-memory backlog)。主从服务器都维护一个复制偏移量(replication offset)和 master run id ,当连接断开时,从服务器会重新连接上主服务器,然后请求继续复制,假如主从服务器的两个 master run id 相同,并且指定的偏移量在内存缓冲区中还有效,复制就会从上次中断的点开始继续**。如果其中一个条件不满足,就会进行完全重新同步(在 2.8 版本之前就是直接进行完全重新同步)。因为主运行 id 不保存在磁盘中,如果从服务器重启了的话就只能进行完全同步了。
-
-部分重新同步这个新特性内部使用 PSYNC 命令,旧的实现中使用 SYNC 命令。Redis2.8 版本可以检测出它所连接的服务器是否支持 PSYNC 命令,不支持的话使用 SYNC 命令。
-
-## 无磁盘复制
-
-通常来讲,一个完全重新同步需要在磁盘上创建一个 RDB 文件,然后加载这个文件以便为从服务器发送数据。
-
-如果使用比较低速的磁盘,这种操作会给主服务器带来较大的压力。Redis 从 2.8.18 版本开始尝试支持无磁盘的复制。使用这种设置时,子进程直接将 RDB 通过网络发送给从服务器,不使用磁盘作为中间存储。
-
-这一特性目前只是实验性的。
-
-## 配置
-
-主从复制的配置十分简单:把下面这行加入到从服务器的配置文件中即可。
-
-```
-slaveof 192.168.1.1 6379
-```
-
-当然你需要把其中的 192.168.1.1 6379 替换为你自己的主服务器 IP(或者主机名 hostname)和端口。另外你可以调用 SLAVEOF 命令,主服务器就会开始与从服务器同步。
-
-关于部分重新同步,还有一些针对复制内存缓冲区的优化参数。查看 Redis 介质中的 Redis.conf 示例获得更多信息。
-
-使用 repl-diskless-sync 配置参数来启动无磁盘复制。使用 repl-diskless-sync-delay 参数来配置传输开始的延迟时间,以便等待更多的从服务器连接上来。查看 Redis 介质中的 Redis.conf 示例获得更多信息。
-
-## 只读从服务器
-
-从 Redis 2.6 开始,从服务器支持只读模式,并且是默认模式。这个行为是由 Redis.conf 文件中的 slave-read-only 参数控制的,可以在运行中通过 CONFIG SET 来启用或者禁用。
-
-只读的从服务器会拒绝所有写命令,所以对从服务器不会有误写操作。但这不表示可以把从服务器实例暴露在危险的网络环境下,因为像 DEBUG 或者 CONFIG 这样的管理命令还是可以运行的。不过你可以通过使用 rename-command 命令来为这些命令改名来增加安全性。
-
-你可能想知道为什么只读限制还可以被还原,使得从服务器还可以进行写操作。虽然当主从服务器进行重新同步或者从服务器重启后,这些写操作都会失效,还是有一些使用场景会想从服务器中写入临时数据的,但将来这个特性可能会被去掉。
-
-## 设置从服务器到主服务器验证
-
-如果主服务器设置了密码,配置从服务器在所有同步中使用这个密码十分简单。
-
-对于运行中的实例,使用 redis-cli 并输入:
-
-```xml
-
-config set masterauth
-
-```
-
-要使配置永久生效,把如下命令加入到配置文件中:
-
-```xml
-
-masterauth
-
-```
-
-## 限制有 N 个以上从服务器才允许写入
-
-从 Redis 2.8 版本开始,可以配置主服务器连接 N 个以上从服务器才允许对主服务器进行写操作。但是,因为 Redis 使用的是异步主从复制,没办法确保从服务器确实收到了要写入的数据,所以还是有一定的数据丢失的可能性。
-
-这一特性的工作原理如下:
-
-- 从服务器每秒钟 ping 一次主服务器,确认处理的复制流数量。
-- 主服务器记住每个从服务器最近一次 ping 的时间。
-- 用户可以配置最少要有 N 个服务器有小于 M 秒的确认延迟。
-
-如果有 N 个以上从服务器,并且确认延迟小于 M 秒,主服务器接受写操作。
-
-你可以把这看做是 CAP 原则(一致性,可用性,分区容错性)不严格的一致性实现,虽然不能百分百确保一致性,但至少保证了丢失的数据不会超过 M 秒内的数据量。
-
-如果条件不满足,主服务器会拒绝写操作并返回一个错误。
-
-- min-slaves-to-write(最小从服务器数)
-- min-slaves-max-lag(从服务器最大确认延迟)
-
-查看 Redis 介质中的 Redis.conf 示例获得更多信息。
-
-## 资料
-
-- http://ifeve.com/redis-replication/
diff --git "a/database/nosql/redis/Redis\346\214\201\344\271\205\345\214\226.md" "b/database/nosql/redis/Redis\346\214\201\344\271\205\345\214\226.md"
deleted file mode 100644
index 86d7a1851c..0000000000
--- "a/database/nosql/redis/Redis\346\214\201\344\271\205\345\214\226.md"
+++ /dev/null
@@ -1,257 +0,0 @@
----
-title: Redis 持久化
-date: 2018/06/11
-categories:
-- database
-tags:
-- database
-- nosql
-- key-value
----
-
-# Redis 持久化
-
-> Redis 支持持久化,即把数据存储到硬盘中。
->
-> Redis 提供了两种持久化方式:
->
-> **RDB 快照(snapshot)** - 将存在于某一时刻的所有数据都写入到硬盘中。
->
-> **只追加文件(append-only file,AOF)** - 它会在执行写命令时,将被执行的写命令复制到硬盘中。
->
-> 这两种持久化方式既可以同时使用,也可以单独使用。
->
-> 将内存中的数据存储到硬盘的一个主要原因是为了在之后重用数据,或者是为了防止系统故障而将数据备份到一个远程位置。另外,存储在 Redis 里面的数据有可能是经过长时间计算得出的,或者有程序正在使用 Redis 存储的数据进行计算,所以用户会希望自己可以将这些数据存储起来以便之后使用,这样就不必重新计算了。
-
-
-
-- [快照](#快照)
- - [快照的原理](#快照的原理)
- - [快照的配置](#快照的配置)
- - [快照的优点](#快照的优点)
- - [快照的缺点](#快照的缺点)
-- [AOF](#aof)
- - [AOF 的原理](#aof-的原理)
- - [AOF 的配置](#aof-的配置)
- - [重写/压缩 AOF](#重写压缩-aof)
- - [AOF 的优点](#aof-的优点)
- - [AOF 的缺点](#aof-的缺点)
-- [选择持久化方式](#选择持久化方式)
- - [怎样从快照方式切换为 AOF 方式](#怎样从快照方式切换为-aof-方式)
- - [AOF 和快照之间的相互作用](#aof-和快照之间的相互作用)
-- [备份](#备份)
- - [容灾备份](#容灾备份)
- - [Redis 复制的启动过程](#redis-复制的启动过程)
-- [资料](#资料)
-
-
-
-Redis 提供了两种持久方式:RDB 和 AOF。你可以同时开启两种持久化方式。在这种情况下, 当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。
-
-## 快照
-
-Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。在创建快照之后,用户可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,还可以将快好留在原地以便重启服务器时使用。
-
-根据配置,快照将被写入 `dbfilename` 选项指定的文件里面,并存储在 `dir` 选项指定的路径上面。
-
-创建快照的方法:
-
-- 客户端可以通过向 Redis 发送 BGSAVE 命令来创建一个快照。对于支持 BGSAVE 命令的平台来说,Redis 会创建一个子进程,然后子进程负责将快照写入到硬盘,而父进程则继续处理命令请求。
-- 客户端还可以通过向 Redis 发送 SAVE 命令来创建一个快照。接到 SAVE 命令的 Redis 服务器在快照创建完毕之前将不再响应任何其他命令。
-- 如果用户设置了 save 配置选项,比如 save 60 10000,当这个条件被满足时,Redis 就会自动触发 BGSAVE 命令。如果用户设置了多个 save 配置选项所设置的条件被满足时,Redis 就会触发一次 BGSAVE 命令。
-- 当 Redis 通过 SHUTDOWN 命令接受到关闭服务器的请求时,或者接收到标准 TERM 信号时,会执行一个 SAVE 命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在 SAVE 命令执行完毕之后关闭服务器。
-- 当一个 Redis 服务器连接另一个 Redis 服务器,并向对方发送 SYNC 命令来开始一次复制操作的时候,如果主服务器目前没有在执行 BGSAVE 操作,或者主服务器并非刚刚执行完 BGSAVE 操作,那么主服务器就会执行 BGSAVE 命令。
-
-快照持久化方式能够在指定的时间间隔能对整个数据进行快照存储。
-
-使用快照持久化来保存数据是,需要记住:**如果系统真的发生崩溃,用户将丢失最近一次生成快照之后更改的所有数据。**
-
-快照配置:
-
-```
-save 60 1000
-stop-writes-on-bgsave-error no
-rdbcompression yes
-dbfilename dump.rdb
-```
-
-### 快照的原理
-
-在默认情况下,Redis 将数据库快照保存在名字为 dump.rdb 的二进制文件中。你可以对 Redis 进行设置, 让它在“N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。你也可以通过调用 SAVE 或者 BGSAVE,手动让 Redis 进行数据集保存操作。这种持久化方式被称为快照。
-
-当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:
-
-- Redis 创建一个子进程。
-- 子进程将数据集写入到一个临时快照文件中。
-- 当子进程完成对新 快照文件的写入时,Redis 用新快照文件替换原来的快照文件,并删除旧的快照文件。
-
-这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。
-
-### 快照的配置
-
-比如说, 在 redis.conf 中添加如下配置,表示让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:
-
-```
-save 60 10000
-```
-
-### 快照的优点
-
-- RDB 是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份。比如你可以在每个小时报保存一下过去 24 小时内的数据,同时每天保存过去 30 天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
-- RDB 是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的 S3(可能加密),非常适用于灾难恢复。
-- 快照在保存 RDB 文件时父进程唯一需要做的就是 fork 出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他 IO 操作,所以快照持久化方式可以最大化 redis 的性能。
-- 与 AOF 相比,在恢复大的数据集的时候,DB 方式会更快一些。
-
-### 快照的缺点
-
-- 如果你希望在 redis 意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么 快照不适合你。虽然你可以配置不同的 save 时间点(例如每隔 5 分钟并且对数据集有 100 个写的操作),是 Redis 要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔 5 分钟或者更久做一次完整的保存,万一在 Redis 意外宕机,你可能会丢失几分钟的数据。
-- 快照需要经常 fork 子进程来保存数据集到硬盘上。当数据集比较大的时候,fork 的过程是非常耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且 CPU 性能不是很好的情况下,这种情况会持续 1 秒。AOF 也需要 fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。
-
-## AOF
-
-AOF 持久化方式记录每次对服务器执行的写操作。当服务器重启的时候会重新执行这些命令来恢复原始的数据。
-
-AOF 命令以 redis 协议追加保存每次写的操作到文件末尾。Redis 还能对 AOF 文件进行后台重写。使得 AOF 文件的体积不至于过大。
-
-```
-appendonly no
-appendfsync everysec
-no-appendfsync-on-rewrite no
-auto-aof-rewrite-percentage 100
-auto-aof-rewrite-min-size 64mb
-```
-
-### AOF 的原理
-
-- Redis 创建一个子进程。
-- 子进程开始将新 AOF 文件的内容写入到临时文件。
-- 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
-- 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
-- 搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。
-
-### AOF 的配置
-
-AOF 持久化通过在 `redis.conf` 中的 `appendonly yes` 配置选项来开启。
-
-可以通过 `appendfsync` 配置选项来设置同步频率:
-
-- **always** - 每个 Redis 写命令都要同步写入硬盘。这样做会严重降低 Redis 的速度。
-- **everysec** - 每秒执行一次同步,显示地将多个写命令同步到硬盘。
-- **no** - 让操作系统来决定应该何时进行同步。
-
-为了兼顾数据安全和写入性能,推荐使用 `appendfsync everysec` 选项。Redis 每秒同步一次 AOF 文件时的性能和不使用任何持久化特性时的性能相差无几。
-
-### 重写/压缩 AOF
-
-随着 Redis 不断运行,AOF 的体积也会不断增长,这将导致两个问题:
-
-1. AOF 耗尽磁盘可用空间。
-2. Redis 重启后需要执行 AOF 文件记录的所有写命令来还原数据集,如果 AOF 过大,则还原操作执行的时间就会非常长。
-
-这个问题的解决方法:
-
-执行 `BGREWRITEAOF` 命令,这个命令会通过移除 AOF 中的冗余命令来重写 AOF 文件,使 AOF 文件的体积尽可能地小。
-
-`BGREWRITEAOF` 命令与 `BGSAVE` 原理类似:通过创建一个子进程,然后由子进程负责对 AOF 文件进行重写。
-
-可以通过设置 `auto-aof-rewrite-percentage` 和 `auto-aof-rewrite-min-size`,使得 Redis 在满足条件时,自动执行 `BGREWRITEAOF`。
-
-假设配置如下:
-
-```
-auto-aof-rewrite-percentage 100
-auto-aof-rewrite-min-size 64mb
-```
-
-表明,当 AOF 大于 64MB,且 AOF 体积比上一次重写后的体积大了至少 100% 时,执行 `BGREWRITEAOF`。
-
-### AOF 的优点
-
-- 使用 AOF 会让你的 Redis 更加耐久: 你可以使用不同的 fsync 策略:无 fsync;每秒 fsync;每次写的时候 fsync。使用默认的每秒 fsync 策略,Redis 的性能依然很好(fsync 是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失 1 秒的数据。
-- AOF 文件是一个只进行追加的日志文件,所以不需要写入 seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用 redis-check-aof 工具修复这些问题。
-- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
-- AOF 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis 协议的格式保存。因此 AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单。举个例子,如果你不小心执行了 FLUSHALL 命令,但只要 AOF 文件未被重写,那么只要停止服务器,移除 AOF 文件末尾的 FLUSHALL 命令,并重启 Redis ,就可以将数据集恢复到 FLUSHALL 执行之前的状态。
-
-### AOF 的缺点
-
-- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
-- 根据所使用的 fsync 策略,AOF 的速度可能会慢于快照。在一般情况下,每秒 fsync 的性能依然非常高,而关闭 fsync 可以让 AOF 的速度和快照一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,快照可以提供更有保证的最大延迟时间(latency)。
-
-## 选择持久化方式
-
-如果你只希望你的数据在服务器运行的时候存在,你可以不使用任何持久化方式。
-
-如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 快照持久化。
-
-如果你不能承受数分钟以内的数据丢失,那么你可以同时使用快照持久化和 AOF 持久化。
-
-有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份,并且快照恢复数据集的速度也要比 AOF 恢复的速度要快,除此之外,使用快照还可以避免之前提到的 AOF 程序的 bug 。
-
-### 怎样从快照方式切换为 AOF 方式
-
-在 Redis 2.2 或以上版本,可以在不重启的情况下,从快照切换到 AOF :
-
-- 为最新的 dump.rdb 文件创建一个备份。
-- 将备份放到一个安全的地方。
-- 执行以下两条命令:
-- redis-cli config set appendonly yes
-- redis-cli config set save “”
-- 确保写命令会被正确地追加到 AOF 文件的末尾。
-- 执行的第一条命令开启了 AOF 功能: Redis 会阻塞直到初始 AOF 文件创建完成为止, 之后 Redis 会继续处理命令请求, 并开始将写入命令追加到 AOF 文件末尾。
-
-执行的第二条命令用于关闭快照功能。 这一步是可选的, 如果你愿意的话, 也可以同时使用快照和 AOF 这两种持久化功能。
-
-重要:别忘了在 redis.conf 中打开 AOF 功能! 否则的话, 服务器重启之后, 之前通过 CONFIG SET 设置的配置就会被遗忘, 程序会按原来的配置来启动服务器。
-
-### AOF 和快照之间的相互作用
-
-在版本号大于等于 2.4 的 Redis 中, BGSAVE 执行的过程中, 不可以执行 BGREWRITEAOF 。 反过来说, 在 BGREWRITEAOF 执行的过程中, 也不可以执行 BGSAVE。这可以防止两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。
-
-如果 BGSAVE 正在执行, 并且用户显示地调用 BGREWRITEAOF 命令, 那么服务器将向用户回复一个 OK 状态, 并告知用户, BGREWRITEAOF 已经被预定执行: 一旦 BGSAVE 执行完毕, BGREWRITEAOF 就会正式开始。 当 Redis 启动时, 如果快照持久化和 AOF 持久化都被打开了, 那么程序会优先使用 AOF 文件来恢复数据集, 因为 AOF 文件所保存的数据通常是最完整的。
-
-## 备份
-
-**务必确保你的数据有完整的备份。**
-
-磁盘故障、节点失效,诸如此类的问题都可能让你的数据消失不见,不进行备份是非常危险的。
-
-备份 Redis 数据建议采用如下策略:
-
-备份 Redis 数据建议采用快照方式。RDB 文件一旦创建,就不会进行任何修改,所以十分安全。
-
-Redis 快照备份过程:
-
-- 创建一个定期任务(cron job),每小时将一个 RDB 文件备份到一个文件夹,并且每天将一个 RDB 文件备份到另一个文件夹。
-- 确保快照的备份都带有相应的日期和时间信息,每次执行定期任务脚本时,使用 find 命令来删除过期的快照:比如说,你可以保留最近 48 小时内的每小时快照,还可以保留最近一两个月的每日快照。
-- 至少每天一次,将 RDB 备份到你的数据中心之外,或者至少是备份到你运行 Redis 服务器的物理机器之外。
-
-### 容灾备份
-
-Redis 的容灾备份基本上就是对数据进行备份,并将这些备份传送到多个不同的外部数据中心。
-
-容灾备份可以在 Redis 运行并产生快照的主数据中心发生严重的问题时,仍然让数据处于安全状态。
-
-以下是一些实用的容灾备份方法:
-
-- Amazon S3 ,以及其他类似 S3 的服务,是一个构建灾难备份系统的好地方。 最简单的方法就是将你的每小时或者每日 RDB 备份加密并传送到 S3 。对数据的加密可以通过 gpg -c 命令来完成(对称加密模式)。记得把你的密码放到几个不同的、安全的地方去(比如你可以把密码复制给你组织里最重要的人物)。同时使用多个储存服务来保存数据文件,可以提升数据的安全性。
-- 传送快照可以使用 SCP 来完成(SSH 的组件)。 以下是简单并且安全的传送方法: 买一个离你的数据中心非常远的 VPS ,装上 SSH ,创建一个无口令的 SSH 客户端 key ,并将这个 key 添加到 VPS 的 authorized_keys 文件中,这样就可以向这个 VPS 传送快照备份文件了。为了达到最好的数据安全性,至少要从两个不同的提供商那里各购买一个 VPS 来进行数据容灾备份。
-- 需要注意的是,这类容灾系统如果没有小心地进行处理的话,是很容易失效的。最低限度下,你应该在文件传送完毕之后,检查所传送备份文件的体积和原始快照文件的体积是否相同。如果你使用的是 VPS ,那么还可以通过比对文件的 SHA1 校验和来确认文件是否传送完整。
-
-另外, 你还需要一个独立的警报系统, 让它在负责传送备份文件的传送器(transfer)失灵时通知你。
-
-### Redis 复制的启动过程
-
-
-
-
-
-当多个从服务器尝试连接同一个主服务器时:
-
-- 上图步骤 3 尚未执行:所有从服务器都会接收到相同的快照文件和相同的缓冲区写命令。
-- 上图步骤 3 正在执行或已经执行完毕:当主服务器与较早进行连接的从服务器执行完复制所需的 5 个步骤之后,主服务器会与新连接的从服务器执行一次新的步骤 1 至步骤 5。
-
-## 资料
-
-- [Redis 官网](https://redis.io/)
-- [Redis 实战](https://item.jd.com/11791607.html)
-- [Redis Persistence](https://redis.io/topics/persistence)
diff --git "a/database/nosql/redis/Redis\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/database/nosql/redis/Redis\346\225\260\346\215\256\347\261\273\345\236\213.md"
deleted file mode 100644
index 91d23fa9e6..0000000000
--- "a/database/nosql/redis/Redis\346\225\260\346\215\256\347\261\273\345\236\213.md"
+++ /dev/null
@@ -1,243 +0,0 @@
----
-title: Redis 数据类型
-date: 2018/06/09
-categories:
-- database
-tags:
-- database
-- nosql
----
-
-# Redis 数据类型
-
-
-
-- [STRING](#string)
-- [LIST](#list)
-- [SET](#set)
-- [HASH](#hash)
-- [ZSET](#zset)
-
-
-
-| 数据类型 | 可以存储的值 | 操作 |
-| :------: | :--------------------: | :--------------------------------------------------------------------------------------------------------------: |
-| STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作 对整数和浮点数执行自增或者自减操作 |
-| LIST | 列表 | 从两端压入或者弹出元素 读取单个或者多个元素 进行修剪,只保留一个范围内的元素 |
-| SET | 无序集合 | 添加、获取、移除单个元素 检查一个元素是否存在于集合中 计算交集、并集、差集 从集合里面随机获取元素 |
-| HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对 获取所有键值对 检查某个键是否存在 |
-| ZSET | 有序集合 | 添加、获取、删除元素 根据分值范围或者成员来获取元素 计算一个键的排名 |
-
-## STRING
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ---- | -------------------------------------------------- |
-| GET | 获取存储在给定键中的值 |
-| SET | 设置存储在给定键中的值 |
-| DEL | 删除存储在给定键中的值(这个命令可以用于所有类型) |
-
-示例:
-
-```py
-127.0.0.1:6379> set name jack
-OK
-127.0.0.1:6379> get name
-"jack"
-127.0.0.1:6379> del name
-(integer) 1
-127.0.0.1:6379> get name
-(nil)
-```
-
-## LIST
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ------ | ---------------------------------------- |
-| RPUSH | 将给定值推入列表的右端 |
-| LRANGE | 获取列表在给定范围上的所有值 |
-| LINDEX | 获取列表在给定位置上的单个元素 |
-| LPOP | 从列表的左端弹出一个值,并返回被弹出的值 |
-
-示例:
-
-```py
-127.0.0.1:6379> rpush list item1
-(integer) 1
-127.0.0.1:6379> rpush list item2
-(integer) 2
-127.0.0.1:6379> rpush list item3
-(integer) 3
-127.0.0.1:6379> lrange list 0 -1
-1) "item1"
-2) "item2"
-3) "item3"
-127.0.0.1:6379> lindex list 1
-"item2"
-127.0.0.1:6379> lpop list
-"item1"
-127.0.0.1:6379> lrange list 0 -1
-1) "item2"
-2) "item3"
-```
-
-## SET
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| --------- | -------------------------------------------- |
-| SADD | 将给定元素添加到集合 |
-| SMEMBERS | 返回集合包含的所有元素 |
-| SISMEMBER | 检查给定元素是否存在于集合中 |
-| SREM | 如果给定的元素存在于集合中,那么移除这个元素 |
-
-示例:
-
-```py
-127.0.0.1:6379> sadd set item1
-(integer) 1
-127.0.0.1:6379> sadd set item2
-(integer) 1
-127.0.0.1:6379> sadd set item3
-(integer) 1
-127.0.0.1:6379> sadd set item3
-(integer) 0
-
-127.0.0.1:6379> smembers set
-1) "item3"
-2) "item2"
-3) "item1"
-
-127.0.0.1:6379> sismember set item2
-(integer) 1
-127.0.0.1:6379> sismember set item6
-(integer) 0
-
-127.0.0.1:6379> srem set item2
-(integer) 1
-127.0.0.1:6379> srem set item2
-(integer) 0
-
-127.0.0.1:6379> smembers set
-1) "item3"
-2) "item1"
-```
-
-## HASH
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ------- | ---------------------------------------- |
-| HSET | 在散列里面关联起给定的键值对 |
-| HGET | 获取指定散列键的值 |
-| HGETALL | 获取散列包含的所有键值对 |
-| HDEL | 如果给定键存在于散列里面,那么移除这个键 |
-
-示例:
-
-```py
-127.0.0.1:6379> hset myhash key1 value1
-(integer) 1
-127.0.0.1:6379> hset myhash key2 value2
-(integer) 1
-127.0.0.1:6379> hset myhash key3 value3
-(integer) 1
-127.0.0.1:6379> hset myhash key3 value2
-(integer) 0
-
-127.0.0.1:6379> hgetall myhash
-1) "key1"
-2) "value1"
-3) "key2"
-4) "value2"
-5) "key3"
-6) "value2"
-
-127.0.0.1:6379> hdel myhash key2
-(integer) 1
-127.0.0.1:6379> hdel myhash key2
-(integer) 0
-
-127.0.0.1:6379> hget myhash key2
-(nil)
-
-127.0.0.1:6379> hgetall myhash
-1) "key1"
-2) "value1"
-3) "key3"
-4) "value2"
-127.0.0.1:6379>
-```
-
-## ZSET
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ------------- | ---------------------------------------------------------- |
-| ZADD | 将一个带有给定分值得成员添加到有序集合里面 |
-| ZRANGE | 根据元素在有序排列中所处的位置,从有序集合里面获取多个元素 |
-| ZRANGEBYSCORE | 获取有序集合在给定分值范围内的所有元素 |
-| ZREM | 如果给定成员存在于有序集合,那么移除这个成员 |
-
-示例:
-
-```py
-127.0.0.1:6379> zadd zset 1 redis
-(integer) 1
-127.0.0.1:6379> zadd zset 2 mongodb
-(integer) 1
-127.0.0.1:6379> zadd zset 3 mysql
-(integer) 1
-127.0.0.1:6379> zadd zset 3 mysql
-(integer) 0
-127.0.0.1:6379> zadd zset 4 mysql
-(integer) 0
-
-127.0.0.1:6379> zrange zset 0 -1 withscores
-1) "redis"
-2) "1"
-3) "mongodb"
-4) "2"
-5) "mysql"
-6) "4"
-
-127.0.0.1:6379> zrangebyscore zset 0 2 withscores
-1) "redis"
-2) "1"
-3) "mongodb"
-4) "2"
-
-127.0.0.1:6379> zrem zset mysql
-(integer) 1
-127.0.0.1:6379> zrange zset 0 -1 withscores
-1) "redis"
-2) "1"
-3) "mongodb"
-4) "2"
-```
diff --git "a/database/nosql/redis/Redis\347\274\223\345\255\230\346\267\230\346\261\260\347\255\226\347\225\245.md" "b/database/nosql/redis/Redis\347\274\223\345\255\230\346\267\230\346\261\260\347\255\226\347\225\245.md"
deleted file mode 100644
index f2f6cace8d..0000000000
--- "a/database/nosql/redis/Redis\347\274\223\345\255\230\346\267\230\346\261\260\347\255\226\347\225\245.md"
+++ /dev/null
@@ -1,52 +0,0 @@
-# Redis 缓存淘汰策略
-
-## 概述
-
-- 最大缓存
-
-Redis 允许通过 maxmemory 参数来设置内存最大值。
-
-- 主键失效
-
-作为一种定期清理无效数据的重要机制,在 Redis 提供的诸多命令中,EXPIRE、EXPIREAT、PEXPIRE、PEXPIREAT 以及 SETEX 和 PSETEX 均可以用来设置一条 Key-Value 对的失效时间,而一条 Key-Value 对一旦被关联了失效时间就会在到期后自动删除(或者说变得无法访问更为准确)。
-
-- 淘汰机制
-
-随着不断的向 redis 中保存数据,当内存剩余空间无法满足添加的数据时,redis 内就会施行数据淘汰策略,清除一部分内容然后保证新的数据可以保存到内存中。
-
-内存淘汰机制是为了更好的使用内存,用一定得 miss 来换取内存的利用率,保证 redis 缓存中保存的都是热点数据。
-
-
-- 非精准的 LRU
-
-实际上 Redis 实现的 LRU 并不是可靠的 LRU,也就是名义上我们使用 LRU 算法淘汰键,但是实际上被淘汰的键并不一定是真正的最久没用的。
-
-## 淘汰策略
-
-内存淘汰只是 Redis 提供的一个功能,为了更好地实现这个功能,必须为不同的应用场景提供不同的策略,内存淘汰策略讲的是为实现内存淘汰我们具体怎么做,要解决的问题包括淘汰键空间如何选择?在键空间中淘汰键如何选择?
-
-Redis 提供了下面几种淘汰策略供用户选择,其中默认的策略为 noeviction 策略:
-
-- **noeviction** - 当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
-- **allkeys-lru** - 在主键空间中,优先移除最近未使用的 key。
-- **allkeys-random** - 在主键空间中,随机移除某个 key。
-- **volatile-lru** - 在设置了过期时间的键空间中,优先移除最近未使用的 key。
-- **volatile-random** - 在设置了过期时间的键空间中,随机移除某个 key。
-- **volatile-ttl** - 在设置了过期时间的键空间中,具有更早过期时间的 key 优先移除。
-
-这里补充一下主键空间和设置了过期时间的键空间,举个例子,假设我们有一批键存储在 Redis 中,则有那么一个哈希表用于存储这批键及其值,如果这批键中有一部分设置了过期时间,那么这批键还会被存储到另外一个哈希表中,这个哈希表中的值对应的是键被设置的过期时间。设置了过期时间的键空间为主键空间的子集。
-
-## 如何选择淘汰策略
-
-- 如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru。
-- 如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random。
-- volatile-lru 策略和 volatile-random 策略适合我们将一个 Redis 实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个 Redis 实例来达到相同的效果。
-- 将 key 设置过期时间实际上会消耗更多的内存,因此我们建议使用 allkeys-lru 策略从而更有效率的使用内存。
-
-## 内部实现
-
-Redis 删除失效主键的方法主要有两种:
-
-- 消极方法(passive way),在主键被访问时如果发现它已经失效,那么就删除它。
-- 主动方法(active way),周期性地从设置了失效时间的主键中选择一部分失效的主键删除。
-- 主动删除:当前已用内存超过 maxmemory 限定时,触发主动清理策略,该策略由启动参数的配置决定主键具体的失效时间全部都维护在 expires 这个字典表中。
diff --git "a/database/nosql/redis/Redis\351\205\215\347\275\256.md" "b/database/nosql/redis/Redis\351\205\215\347\275\256.md"
deleted file mode 100644
index eadb16b100..0000000000
--- "a/database/nosql/redis/Redis\351\205\215\347\275\256.md"
+++ /dev/null
@@ -1,47 +0,0 @@
-# Redis 配置
-
-## 通过配置文件配置
-
-Redis 的配置文件名一般叫做:`redis.conf`。
-
-redis.conf 文件中的配置指令参数格式为:
-
-```
-keyword argument1 argument2 ... argumentN
-关键字 参数1 参数2 ... 参数N
-```
-
-## 通过命令行配置
-
-自 Redis2.6 起就可以直接通过命令行传递 Redis 配置参数。这种方法可以用于测试。
-
-例:这个例子配置一个新运行并以 6380 为端口的 Redis 实例,使配置它为 127.0.0.1:6379 Redis 实例的 slave。
-
-```
-./redis-server --port 6380 --slaveof 127.0.0.1 6379
-```
-
-## 动态修改配置
-
-Redis 允许在运行的过程中,在不重启服务器的情况下更改服务器配置,同时也支持 使用特殊的 [CONFIG SET](https://redis.io/commands/config-set) 和 [CONFIG GET](https://redis.io/commands/config-get) 命令用编程方式查询并设置配置。
-
-并非所有的配置指令都支持这种使用方式,但是大部分是支持的。
-
-## 配置 Redis 成为一个缓存
-
-如果你想把 Redis 当做一个缓存来用,所有的 key 都有过期时间,那么你可以考虑 使用以下设置(假设最大内存使用量为 2M):
-
-```
-maxmemory 2mb
-maxmemory-policy allkeys-lru
-```
-
-以上设置并不需要我们的应用使用 EXPIRE(或相似的命令)命令去设置每个 key 的过期时间,因为 只要内存使用量到达 2M,Redis 就会使用类 LRU 算法自动删除某些 key。
-
-相比使用额外内存空间存储多个键的过期时间,使用缓存设置是一种更加有效利用内存的方式。而且相比每个键固定的 过期时间,使用 LRU 也是一种更加推荐的方式,因为这样能使应用的热数据(更频繁使用的键) 在内存中停留时间更久。
-
-当我们把 Redis 当成缓存来使用的时候,如果应用程序同时也需要把 Redis 当成存储系统来使用,那么强烈建议 使用两个 Redis 实例。一个是缓存,使用上述方法进行配置,另一个是存储,根据应用的持久化需求进行配置,并且 只存储那些不需要被缓存的数据。
-
-## 资料
-
-- https://redis.io/topics/config
diff --git a/database/nosql/redis/install-redis.md b/database/nosql/redis/install-redis.md
deleted file mode 100644
index 54a7959216..0000000000
--- a/database/nosql/redis/install-redis.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Redis 安装
-
-
-
-- [安装](#安装)
-- [启动](#启动)
-- [脚本](#脚本)
-
-
-
-## 安装
-
-安装步骤如下:
-
-(1)下载并解压到本地
-
-进入官网下载地址:https://redis.io/download ,选择合适的版本下载。
-
-我选择的是最新稳定版本 4.0.8:http://download.redis.io/releases/redis-4.0.8.tar.gz
-
-我个人喜欢存放在:`/opt/redis`
-
-```
-wget -O /opt/redis/redis-4.0.8.tar.gz http://download.redis.io/releases/redis-4.0.8.tar.gz
-cd /opt/redis
-tar zxvf redis-4.0.8.tar.gz
-```
-
-(2)编译安装
-
-执行以下命令:
-
-```
-cd /opt/redis/redis-4.0.8
-make
-```
-
-## 启动
-
-**启动 redis 服务**
-
-```
-cd /opt/redis/redis-4.0.8/src
-./redis-server
-```
-
-**启动 redis 客户端**
-
-```
-cd /opt/redis/redis-4.0.8/src
-./redis-cli
-```
-
-## 脚本
-
-以上两种安装方式,我都写了脚本去执行:
-
-| [安装脚本](https://github.com/dunwu/linux/tree/master/codes/deploy/tool/redis) |
diff --git a/database/nosql/redis/redis-cheat-sheets.pdf b/database/nosql/redis/redis-cheat-sheets.pdf
deleted file mode 100644
index 25b679374a..0000000000
Binary files a/database/nosql/redis/redis-cheat-sheets.pdf and /dev/null differ
diff --git a/database/nosql/redis/redis-quickstart.md b/database/nosql/redis/redis-quickstart.md
deleted file mode 100644
index 19196adf29..0000000000
--- a/database/nosql/redis/redis-quickstart.md
+++ /dev/null
@@ -1,173 +0,0 @@
----
-title: Redis 快速入门
-date: 2017-12-01
-categories:
-- db
-tags:
-- database
-- key-value
-- cache
----
-
-# Redis 快速入门
-
-
-
-- [1. 概述](#1-概述)
- - [1.1. 什么是 Redis](#11-什么是-redis)
- - [1.2. 为什么用 Redis](#12-为什么用-redis)
-- [2. 安装](#2-安装)
- - [2.1. Window 下安装](#21-window-下安装)
- - [2.2. Linux 下安装](#22-linux-下安装)
- - [2.3. Ubuntu 下安装](#23-ubuntu-下安装)
- - [2.4. 启动 Redis](#24-启动-redis)
- - [2.5. 查看 redis 是否启动?](#25-查看-redis-是否启动)
-- [3. Redis 命令](#3-redis-命令)
-
-
-
-## 1. 概述
-
-### 1.1. 什么是 Redis
-
-Redis 是一个高性能的 key-value 数据库,也可用于缓存和消息代理。
-
-### 1.2. 为什么用 Redis
-
-与其它 key - value 数据库产品相比,具有以下优势:
-
-- 支持数据持久化——可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
-
-- 丰富的数据类型——Redis 支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
-- 支持数据的备份——数据备份采用 master-slave 模式。
-- 性能极高——Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 。
-- 原子性——Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC 指令包起来。
-
-## 2. 安装
-
-### 2.1. Window 下安装
-
-**下载地址:**。
-
-Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 **Redis-x64-xxx.zip**压缩包到 C 盘,解压后,将文件夹重新命名为 **redis**。
-
-打开一个 **cmd** 窗口 使用 cd 命令切换目录到 **C:\redis** 运行 **redis-server.exe redis.windows.conf** 。
-
-如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:
-
-这时候另启一个 cmd 窗口,原来的不要关闭,不然就无法访问服务端了。
-
-切换到 redis 目录下运行 **redis-cli.exe -h 127.0.0.1 -p 6379** 。
-
-设置键值对 **set myKey abc**
-
-取出键值对 **get myKey**
-
-### 2.2. Linux 下安装
-
-**下载地址:**,下载最新文档版本。
-
-本教程使用的最新文档版本为 2.8.17,下载并安装:
-
-```
-$ wget http://download.redis.io/releases/redis-2.8.17.tar.gz
-$ tar xzf redis-2.8.17.tar.gz
-$ cd redis-2.8.17
-$ make
-```
-
-make 完后 redis-2.8.17 目录下会出现编译后的 redis 服务程序 redis-server,还有用于测试的客户端程序 redis-cli,两个程序位于安装目录 src 目录下:
-
-下面启动 redis 服务.
-
-```
-$ cd src
-$ ./redis-server
-```
-
-注意这种方式启动 redis 使用的是默认配置。也可以通过启动参数告诉 redis 使用指定配置文件使用下面命令启动。
-
-```
-$ cd src
-$ ./redis-server redis.conf
-```
-
-redis.conf 是一个默认的配置文件。我们可以根据需要使用自己的配置文件。
-
-启动 redis 服务进程后,就可以使用测试客户端程序 redis-cli 和 redis 服务交互了。 比如:
-
-```
-$ cd src
-$ ./redis-cli
-redis> set foo bar
-OK
-redis> get foo
-"bar"
-```
-
-### 2.3. Ubuntu 下安装
-
-在 Ubuntu 系统安装 Redi 可以使用以下命令:
-
-```
-$sudo apt-get update
-$sudo apt-get install redis-server
-```
-
-### 2.4. 启动 Redis
-
-```
-$ redis-server
-```
-
-### 2.5. 查看 redis 是否启动?
-
-```
-$ redis-cli
-```
-
-以上命令将打开以下终端:
-
-```
-redis 127.0.0.1:6379>
-```
-
-127.0.0.1 是本机 IP ,6379 是 redis 服务端口。现在我们输入 PING 命令。
-
-```
-redis 127.0.0.1:6379> ping
-PONG
-```
-
-以上说明我们已经成功安装了 redis。
-
-## 3. Redis 命令
-
-Redis 命令用于在 redis 服务上执行操作。
-
-要在 redis 服务上执行命令,需要先进入 redis 客户端。
-
-进入 redis 客户端的方法:
-
-```
-$ redis-cli
-```
-
-远程进入 redis 客户端的方法:
-
-```
-$ redis-cli -h host -p port -a password
-```
-
-**实例**
-
-以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 pass 的 redis 服务上:
-
-```
-$redis-cli -h 127.0.0.1 -p 6379 -a "pass"
-redis 127.0.0.1:6379>
-redis 127.0.0.1:6379> PING
-PONG
-```
-
-更多命令行可以参考:[Redis 官方命令行字典](https://redis.io/commands)
diff --git a/database/nosql/redis/redis.md b/database/nosql/redis/redis.md
deleted file mode 100644
index 3675a43c70..0000000000
--- a/database/nosql/redis/redis.md
+++ /dev/null
@@ -1,625 +0,0 @@
----
-title: Redis
-date: 2018/06/09
-categories:
-- database
-tags:
-- database
-- key-value
-- cache
----
-
-# Redis
-
-
-
-- [1. 概述](#1-概述)
- - [1.1. Redis 简介](#11-redis-简介)
- - [1.2. Redis 的优势](#12-redis-的优势)
- - [1.3. Redis 与 Memcached](#13-redis-与-memcached)
-- [2. 数据类型](#2-数据类型)
- - [2.1. STRING](#21-string)
- - [2.2. LIST](#22-list)
- - [2.3. SET](#23-set)
- - [2.4. HASH](#24-hash)
- - [2.5. ZSET](#25-zset)
-- [3. 使用场景](#3-使用场景)
-- [4. Redis 管道](#4-redis-管道)
-- [5. 键的过期时间](#5-键的过期时间)
-- [6. 数据淘汰策略](#6-数据淘汰策略)
-- [7. 持久化](#7-持久化)
- - [7.1. 快照持久化](#71-快照持久化)
- - [7.2. AOF 持久化](#72-aof-持久化)
-- [8. 发布与订阅](#8-发布与订阅)
-- [9. 事务](#9-事务)
- - [9.1. EXEC](#91-exec)
- - [9.2. MULTI](#92-multi)
- - [9.3. DISCARD](#93-discard)
- - [9.4. WATCH](#94-watch)
-- [10. 事件](#10-事件)
- - [10.1. 文件事件](#101-文件事件)
- - [10.2. 时间事件](#102-时间事件)
- - [10.3. 事件的调度与执行](#103-事件的调度与执行)
-- [11. 集群](#11-集群)
- - [11.1. 复制](#111-复制)
- - [11.2. 哨兵](#112-哨兵)
- - [11.3. 分片](#113-分片)
-- [12. Redis Client](#12-redis-client)
-- [13. 资料](#13-资料)
-
-
-
-## 1. 概述
-
-### 1.1. Redis 简介
-
-Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。
-
-键的类型只能为字符串,值支持的五种类型数据类型为:字符串、列表、集合、有序集合、散列表。
-
-Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。
-
-### 1.2. Redis 的优势
-
-- 性能极高 – Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s。
-- 丰富的数据类型 - 支持字符串、列表、集合、有序集合、散列表。
-- 原子 - Redis 的所有操作都是原子性的。单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC 指令包起来。
-- 持久化 - Redis 支持数据的持久化。可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
-- 备份 - Redis 支持数据的备份,即 master-slave 模式的数据备份。
-- 丰富的特性 - Redis 还支持发布订阅, 通知, key 过期等等特性。
-
-### 1.3. Redis 与 Memcached
-
-Redis 与 Memcached 因为都可以用于缓存,所以常常被拿来做比较,二者主要有以下区别:
-
-**数据类型**
-
-- Memcached 仅支持字符串类型;
-- 而 Redis 支持五种不同种类的数据类型,使得它可以更灵活地解决问题。
-
-**数据持久化**
-
-- Memcached 不支持持久化;
-- Redis 支持两种持久化策略:RDB 快照和 AOF 日志。
-
-**分布式**
-
-- Memcached 不支持分布式,只能通过在客户端使用像一致性哈希这样的分布式算法来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。
-- Redis Cluster 实现了分布式的支持。
-
-**内存管理机制**
-
-- Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题,但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。
-- 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘。而 Memcached 的数据则会一直在内存中。
-
-## 2. 数据类型
-
-| 数据类型 | 可以存储的值 | 操作 |
-| -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------- |
-| STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作 对整数和浮点数执行自增或者自减操作 |
-| LIST | 列表 | 从两端压入或者弹出元素 读取单个或者多个元素 进行修剪,只保留一个范围内的元素 |
-| SET | 无序集合 | 添加、获取、移除单个元素 检查一个元素是否存在于集合中 计算交集、并集、差集 从集合里面随机获取元素 |
-| HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对 获取所有键值对 检查某个键是否存在 |
-| ZSET | 有序集合 | 添加、获取、删除元素 根据分值范围或者成员来获取元素 计算一个键的排名 |
-
-> [What Redis data structures look like](https://redislabs.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/)
-
-### 2.1. STRING
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ---- | -------------------------------------------------- |
-| GET | 获取存储在给定键中的值 |
-| SET | 设置存储在给定键中的值 |
-| DEL | 删除存储在给定键中的值(这个命令可以用于所有类型) |
-
-示例:
-
-```py
-127.0.0.1:6379> set name jack
-OK
-127.0.0.1:6379> get name
-"jack"
-127.0.0.1:6379> del name
-(integer) 1
-127.0.0.1:6379> get name
-(nil)
-```
-
-### 2.2. LIST
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ------ | -------------------------------------------------- |
-| RPUSH | 获取存储在给定键中的值 |
-| LRANGE | 设置存储在给定键中的值 |
-| LINDEX | 删除存储在给定键中的值(这个命令可以用于所有类型) |
-| LPOP | 删除存储在给定键中的值(这个命令可以用于所有类型) |
-
-示例:
-
-```py
-127.0.0.1:6379> rpush list item1
-(integer) 1
-127.0.0.1:6379> rpush list item2
-(integer) 2
-127.0.0.1:6379> rpush list item3
-(integer) 3
-127.0.0.1:6379> lrange list 0 -1
-1) "item1"
-2) "item2"
-3) "item3"
-127.0.0.1:6379> lindex list 1
-"item2"
-127.0.0.1:6379> lpop list
-"item1"
-127.0.0.1:6379> lrange list 0 -1
-1) "item2"
-2) "item3"
-```
-
-### 2.3. SET
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| --------- | -------------------------------- |
-| SADD | 添加一个或多个元素到集合里 |
-| SMEMBERS | 获取集合里面的所有元素 |
-| SISMEMBER | 确定一个给定的值是一个集合的成员 |
-| SREM | 从集合里删除一个或多个元素 |
-
-示例:
-
-```py
-127.0.0.1:6379> sadd set item1
-(integer) 1
-127.0.0.1:6379> sadd set item2
-(integer) 1
-127.0.0.1:6379> sadd set item3
-(integer) 1
-127.0.0.1:6379> sadd set item3
-(integer) 0
-
-127.0.0.1:6379> smembers set
-1) "item3"
-2) "item2"
-3) "item1"
-
-127.0.0.1:6379> sismember set item2
-(integer) 1
-127.0.0.1:6379> sismember set item6
-(integer) 0
-
-127.0.0.1:6379> srem set item2
-(integer) 1
-127.0.0.1:6379> srem set item2
-(integer) 0
-
-127.0.0.1:6379> smembers set
-1) "item3"
-2) "item1"
-```
-
-### 2.4. HASH
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ------- | -------------------------- |
-| HSET | 设置 hash 里面一个字段的值 |
-| HGET | 获取 hash 中域的值 |
-| HGETALL | 从 hash 中读取全部的域和值 |
-| HDEL | 删除一个或多个域 |
-
-示例:
-
-```py
-127.0.0.1:6379> hset myhash key1 value1
-(integer) 1
-127.0.0.1:6379> hset myhash key2 value2
-(integer) 1
-127.0.0.1:6379> hset myhash key3 value3
-(integer) 1
-127.0.0.1:6379> hset myhash key3 value2
-(integer) 0
-
-127.0.0.1:6379> hgetall myhash
-1) "key1"
-2) "value1"
-3) "key2"
-4) "value2"
-5) "key3"
-6) "value2"
-
-127.0.0.1:6379> hdel myhash key2
-(integer) 1
-127.0.0.1:6379> hdel myhash key2
-(integer) 0
-
-127.0.0.1:6379> hget myhash key2
-(nil)
-
-127.0.0.1:6379> hgetall myhash
-1) "key1"
-2) "value1"
-3) "key3"
-4) "value2"
-127.0.0.1:6379>
-```
-
-### 2.5. ZSET
-
-
-
-
-
-命令:
-
-| 命令 | 行为 |
-| ------------- | ------------------------------------------------------------- |
-| ZADD | 添加到有序 set 的一个或多个成员,或更新的分数,如果它已经存在 |
-| ZRANGE | 根据指定的 index 返回,返回 sorted set 的成员列表 |
-| ZRANGEBYSCORE | 返回有序集合中指定分数区间内的成员,分数由低到高排序。 |
-| ZREM | 从排序的集合中删除一个或多个成员 |
-
-示例:
-
-```py
-127.0.0.1:6379> zadd zset 1 redis
-(integer) 1
-127.0.0.1:6379> zadd zset 2 mongodb
-(integer) 1
-127.0.0.1:6379> zadd zset 3 mysql
-(integer) 1
-127.0.0.1:6379> zadd zset 3 mysql
-(integer) 0
-127.0.0.1:6379> zadd zset 4 mysql
-(integer) 0
-
-127.0.0.1:6379> zrange zset 0 -1 withscores
-1) "redis"
-2) "1"
-3) "mongodb"
-4) "2"
-5) "mysql"
-6) "4"
-
-127.0.0.1:6379> zrangebyscore zset 0 2 withscores
-1) "redis"
-2) "1"
-3) "mongodb"
-4) "2"
-
-127.0.0.1:6379> zrem zset mysql
-(integer) 1
-127.0.0.1:6379> zrange zset 0 -1 withscores
-1) "redis"
-2) "1"
-3) "mongodb"
-4) "2"
-```
-
-## 3. 使用场景
-
-- **缓存** - 将热点数据放到内存中,设置内存的最大使用量以及过期淘汰策略来保证缓存的命中率。
-- **计数器** - Redis 这种内存数据库能支持计数器频繁的读写操作。
-- **应用限流** - 限制一个网站访问流量。
-- **消息队列** - 使用 List 数据类型,它是双向链表。
-- **查找表** - 使用 HASH 数据类型。
-- **交集运算** - 使用 SET 类型,例如求两个用户的共同好友。
-- **排行榜** - 使用 ZSET 数据类型。
-- **分布式 Session** - 多个应用服务器的 Session 都存储到 Redis 中来保证 Session 的一致性。
-- **分布式锁** - 除了可以使用 SETNX 实现分布式锁之外,还可以使用官方提供的 RedLock 分布式锁实现。
-
-## 4. Redis 管道
-
-Redis 是一种基于 C/S 模型以及请求/响应协议的 TCP 服务。
-
-Redis 支持管道技术。管道技术允许请求以异步方式发送,即旧请求的应答还未返回的情况下,允许发送新请求。这种方式可以大大提高传输效率。
-
-使用管道发送命令时,服务器将被迫回复一个队列答复,占用很多内存。所以,如果你需要发送大量的命令,最好是把他们按照合理数量分批次的处理。
-
-## 5. 键的过期时间
-
-Redis 可以为每个键设置过期时间,当键过期时,会自动删除该键。
-
-对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。
-
-可以使用 `EXPIRE` 或 `EXPIREAT` 来为 key 设置过期时间。
-
-> 注意:当 `EXPIRE` 的时间如果设置的是负数,`EXPIREAT` 设置的时间戳是过期时间,将直接删除 key。
-
-示例:
-
-```py
-redis> SET mykey "Hello"
-"OK"
-redis> EXPIRE mykey 10
-(integer) 1
-redis> TTL mykey
-(integer) 10
-redis> SET mykey "Hello World"
-"OK"
-redis> TTL mykey
-(integer) -1
-redis>
-```
-
-## 6. 数据淘汰策略
-
-可以设置内存最大使用量,当内存使用量超过时施行淘汰策略,具体有 6 种淘汰策略。
-
-| 策略 | 描述 |
-| --------------- | ---------------------------------------------------- |
-| volatile-lru | 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 |
-| volatile-ttl | 从已设置过期时间的数据集中挑选将要过期的数据淘汰 |
-| volatile-random | 从已设置过期时间的数据集中任意选择数据淘汰 |
-| allkeys-lru | 从所有数据集中挑选最近最少使用的数据淘汰 |
-| allkeys-random | 从所有数据集中任意选择数据进行淘汰 |
-| noeviction | 禁止驱逐数据 |
-
-如果使用 Redis 来缓存数据时,要保证所有数据都是热点数据,可以将内存最大使用量设置为热点数据占用的内存量,然后启用 allkeys-lru 淘汰策略,将最近最少使用的数据淘汰。
-
-作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法(LRU、TTL)实际实现上并非针对所有 key,而是抽样一小部分 key 从中选出被淘汰 key,抽样数量可通过 maxmemory-samples 配置。
-
-## 7. 持久化
-
-Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。
-
-### 7.1. 快照持久化
-
-将某个时间点的所有数据都存放到硬盘上。
-
-可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。
-
-如果系统发生故障,将会丢失最后一次创建快照之后的数据。
-
-如果数据量很大,保存快照的时间会很长。
-
-### 7.2. AOF 持久化
-
-将写命令添加到 AOF 文件(Append Only File)的末尾。
-
-对硬盘的文件进行写入时,写入的内容首先会被存储到缓冲区,然后由操作系统决定什么时候将该内容同步到硬盘,用户可以调用 file.flush() 方法请求操作系统尽快将缓冲区存储的数据同步到硬盘。可以看出写入文件的数据不会立即同步到硬盘上,在将写命令添加到 AOF 文件时,要根据需求来保证何时同步到硬盘上。
-
-有以下同步选项:
-
-| 选项 | 同步频率 |
-| :------: | :----------------------: |
-| always | 每个写命令都同步 |
-| everysec | 每秒同步一次 |
-| no | 让操作系统来决定何时同步 |
-
-- always 选项会严重减低服务器的性能;
-- everysec 选项比较合适,可以保证系统奔溃时只会丢失一秒左右的数据,并且 Redis 每秒执行一次同步对服务器性能几乎没有任何影响;
-- no 选项并不能给服务器性能带来多大的提升,而且也会增加系统奔溃时数据丢失的数量。
-
-随着服务器写请求的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。
-
-## 8. 发布与订阅
-
-订阅者订阅了频道之后,发布者向频道发送字符串消息会被所有订阅者接收到。
-
-某个客户端使用 SUBSCRIBE 订阅一个频道,其它客户端可以使用 PUBLISH 向这个频道发送消息。
-
-发布与订阅模式和观察者模式有以下不同:
-
-- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。
-- 观察者模式是同步的,当事件触发时,主题会去调用观察者的方法;而发布与订阅模式是异步的;
-
-## 9. 事务
-
-MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务相关的命令。
-
-事务可以一次执行多个命令, 并且有以下两个重要的保证:
-
-- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
-- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
-
-### 9.1. EXEC
-
-EXEC 命令负责触发并执行事务中的所有命令:
-
-- 如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。
-- 另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。
-
-### 9.2. MULTI
-
-MULTI 命令用于开启一个事务,它总是返回 OK 。 MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC 命令被调用时, 所有队列中的命令才会被执行。
-
-### 9.3. DISCARD
-
-当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出。
-
-示例:
-
-```py
-> SET foo 1
-OK
-> MULTI
-OK
-> INCR foo
-QUEUED
-> DISCARD
-OK
-> GET foo
-"1"
-```
-
-### 9.4. WATCH
-
-WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。
-
-被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回 nil-reply 来表示事务已经失败。
-
-```
-WATCH mykey
-val = GET mykey
-val = val + 1
-MULTI
-SET mykey $val
-EXEC
-```
-
-使用上面的代码, 如果在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 mykey 的值, 那么当前客户端的事务就会失败。 程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞为止。
-
-这种形式的锁被称作乐观锁, 它是一种非常强大的锁机制。 并且因为大多数情况下, 不同的客户端会访问不同的键, 碰撞的情况一般都很少, 所以通常并不需要进行重试。
-
-WATCH 使得 EXEC 命令需要有条件地执行:事务只能在所有被监视键都没有被修改的前提下执行,如果这个前提不能满足的话,事务就不会被执行。
-
-WATCH 命令可以被调用多次。对键的监视从 WATCH 执行之后开始生效,直到调用 EXEC 为止。
-
-用户还可以在单个 WATCH 命令中监视任意多个键,例如:
-
-```py
-redis> WATCH key1 key2 key3
-OK
-```
-
-当 EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。
-
-另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。
-
-使用无参数的 UNWATCH 命令可以手动取消对所有键的监视。 对于一些需要改动多个键的事务, 有时候程序需要同时对多个键进行加锁, 然后检查这些键的当前值是否符合程序的要求。 当值达不到要求时, 就可以使用 UNWATCH 命令来取消目前对键的监视, 中途放弃这个事务, 并等待事务的下次尝试。
-
-## 10. 事件
-
-Redis 服务器是一个事件驱动程序。
-
-### 10.1. 文件事件
-
-服务器通过套接字与客户端或者其它服务器进行通信,文件事件就是对套接字操作的抽象。
-
-Redis 基于 Reactor 模式开发了自己的网络时间处理器,使用 I/O 多路复用程序来同时监听多个套接字,并将到达的时间传送给文件事件分派器,分派器会根据套接字产生的事件类型调用响应的时间处理器。
-
-### 10.2. 时间事件
-
-服务器有一些操作需要在给定的时间点执行,时间事件是对这类定时操作的抽象。
-
-时间事件又分为:
-
-- 定时事件:是让一段程序在指定的时间之内执行一次;
-- 周期性事件:是让一段程序每隔指定时间就执行一次。
-
-Redis 将所有时间事件都放在一个无序链表中,通过遍历整个链表查找出已到达的时间事件,并调用响应的事件处理器。
-
-### 10.3. 事件的调度与执行
-
-服务器需要不断监听文件事件的套接字才能得到待处理的文件事件,但是不能监听太久,否则时间事件无法在规定的时间内执行,因此监听时间应该根据距离现在最近的时间事件来决定。
-
-事件调度与执行由 aeProcessEvents 函数负责,伪代码如下:
-
-```python
-def aeProcessEvents():
-
- ## 获取到达时间离当前时间最接近的时间事件
- time_event = aeSearchNearestTimer()
-
- ## 计算最接近的时间事件距离到达还有多少毫秒
- remaind_ms = time_event.when - unix_ts_now()
-
- ## 如果事件已到达,那么 remaind_ms 的值可能为负数,将它设为 0
- if remaind_ms < 0:
- remaind_ms = 0
-
- ## 根据 remaind_ms 的值,创建 timeval
- timeval = create_timeval_with_ms(remaind_ms)
-
- ## 阻塞并等待文件事件产生,最大阻塞时间由传入的 timeval 决定
- aeApiPoll(timeval)
-
- ## 处理所有已产生的文件事件
- procesFileEvents()
-
- ## 处理所有已到达的时间事件
- processTimeEvents()
-```
-
-将 aeProcessEvents 函数置于一个循环里面,加上初始化和清理函数,就构成了 Redis 服务器的主函数,伪代码如下:
-
-```python
-def main():
-
- ## 初始化服务器
- init_server()
-
- ## 一直处理事件,直到服务器关闭为止
- while server_is_not_shutdown():
- aeProcessEvents()
-
- ## 服务器关闭,执行清理操作
- clean_server()
-```
-
-从事件处理的角度来看,服务器运行流程如下:
-
-## 11. 集群
-
-### 11.1. 复制
-
-通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。
-
-一个从服务器只能有一个主服务器,并且不支持主主复制。
-
-#### 12.1. 连接过程
-
-1. 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令;
-
-2. 从服务器丢弃所有旧数据,载入主服务器发来的快照文件,之后从服务器开始接受主服务器发来的写命令;
-
-3. 主服务器每执行一次写命令,就向从服务器发送相同的写命令。
-
-#### 12.2. 主从链
-
-随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器将导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。
-
-### 11.2. 哨兵
-
-Sentinel(哨兵)可以监听主服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。
-
-### 11.3. 分片
-
-分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,也可以从多台机器里面获取数据,这种方法在解决某些问题时可以获得线性级别的性能提升。
-
-假设有 4 个 Reids 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,... 等等,有不同的方式来选择一个指定的键存储在哪个实例中。最简单的方式是范围分片,例如用户 id 从 0\~1000 的存储到实例 R0 中,用户 id 从 1001\~2000 的存储到实例 R1 中,等等。但是这样需要维护一张映射范围表,维护操作代价很高。还有一种方式是哈希分片,使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道应该存储的实例。
-
-主要有三种分片方式:
-
-- 客户端分片:客户端使用一致性哈希等算法决定键应当分布到哪个节点。
-- 代理分片:将客户端请求发送到代理上,由代理转发请求到正确的节点上。
-- 服务器分片:Redis Cluster。
-
-## 12. Redis Client
-
-Redis 社区中有多种编程语言的客户端,可以在这里查找合适的客户端:https://redis.io/clients
-
-redis 官方推荐的 Java Redis Client:
-
-- [jedis](https://github.com/xetorthio/jedis)
-- [redisson](https://github.com/redisson/redisson)
-- [lettuce](https://github.com/lettuce-io/lettuce-core)
-
-## 13. 资料
-
-- [Redis 官网](https://redis.io/)
-- [awesome-redis](https://github.com/JamzyWang/awesome-redis)
-- [Redis 实战](https://item.jd.com/11791607.html)
diff --git a/database/sql/flyway.md b/database/sql/flyway.md
deleted file mode 100644
index 32ab1a7efc..0000000000
--- a/database/sql/flyway.md
+++ /dev/null
@@ -1,508 +0,0 @@
-# Flyway
-
-> Flyway 是一个数据迁移工具。
->
-> 关键词:
-
-
-
-- [简介](#简介)
- - [什么是 Flyway?](#什么是-flyway)
- - [为什么要使用数据迁移?](#为什么要使用数据迁移)
- - [Flyway 如何工作?](#flyway-如何工作)
-- [快速上手](#快速上手)
- - [命令行](#命令行)
- - [JAVA API](#java-api)
- - [Maven](#maven)
- - [Gradle](#gradle)
-- [入门篇](#入门篇)
- - [概念](#概念)
- - [命令](#命令)
- - [支持的数据库](#支持的数据库)
-- [资料](#资料)
-
-
-
-## 简介
-
-### 什么是 Flyway?
-
-**Flyway 是一个开源的数据库迁移工具。**
-
-### 为什么要使用数据迁移?
-
-为了说明数据迁移的作用,我们来举一个示例:
-
-(1)假设,有一个叫做 Shiny 的项目,它的架构是一个叫做 Shiny Soft 的 App 连接叫做 Shiny DB 的数据库。
-
-(2)对于大多数项目而言,最简单的持续集成场景如下所示:
-
-
-
-这意味着,我们不仅仅要处理一份环境中的修改,由此会引入一些版本冲突问题:
-
-在代码侧(即应用软件)的版本问题比较容易解决:
-
-- 有方便的版本控制工具
-- 有可复用的构建和持续集成
-- 规范的发布和部署过程
-
-那么,数据库层面的版本问题如何解决呢?
-
-目前仍然没有方便的数据库版本工具。许多项目仍使用 sql 脚本来解决版本冲突,甚至是遇到冲突问题时才想起用 sql 语句去解决。
-
-由此,引发一些问题:
-
-- 机器上的数据库是什么状态?
-- 脚本到底生效没有?
-- 生产环境修复的问题是否也在测试环境修复了?
-- 如何建立一个新的数据库实例?
-
-数据迁移就是用来搞定这些混乱的问题:
-
-- 通过草稿重建一个数据库。
-- 在任何时候都可以清楚的了解数据库的状态。
-- 以一种明确的方式将数据库从当前版本迁移到一个新版本。
-
-### Flyway 如何工作?
-
-最简单的场景是指定 Flyway 迁移到一个空的数据库。
-
-
-
-Flyway 会尝试查找它的 schema 历史表,如果数据库是空的,Flyway 就不再查找,而是直接创建数据库。
-
-现再你就有了一个仅包含一张空表的数据库,默认情况下,这张表叫 _flyway_schema_history_。
-
-
-
-这张表将被用于追踪数据库的状态。
-
-然后,Flyway 将开始扫描文件系统或应用 classpath 中的 **migrations**。这些 **migrations** 可以是 sql 或 java。
-
-这些 **migrations** 将根据他们的版本号进行排序。
-
-
-
-任意 migration 应用后,schema 历史表将更新。当元数据和初始状态替换后,可以称之为:迁移到新版本。
-
-Flyway 一旦扫描了文件系统或应用 classpath 下的 migrations,这些 migrations 会检查 schema 历史表。如果它们的版本号低于或等于当前的版本,将被忽略。保留下来的 migrations 是等待的 migrations,有效但没有应用。
-
-
-
-migrations 将根据版本号排序并按序执行。
-
-
-
-## 快速上手
-
-Flyway 有 4 种使用方式:
-
-- 命令行
-- JAVA API
-- Maven
-- Gradle
-
-### 命令行
-
-适用于非 Java 用户,无需构建。
-
-```shell
-> flyway migrate -url=... -user=... -password=...
-```
-
-(1)**下载解压**
-
-进入[官方下载页面](https://flywaydb.org/download/),选择合适版本,下载并解压到本地。
-
-(2)**配置 flyway**
-
-编辑 `/conf/flyway.conf`:
-
-```properties
-flyway.url=jdbc:h2:file:./foobardb
-flyway.user=SA
-flyway.password=
-```
-
-(3)**创建第一个 migration**
-
-在 `/sql` 目录下创建 `V1__Create_person_table.sql` 文件,内容如下:
-
-```sql
-create table PERSON (
- ID int not null,
- NAME varchar(100) not null
-);
-```
-
-(4)**迁移数据库**
-
-运行 Flyway 来迁移数据库:
-
-```bash
-flyway-5.1.4> flyway migrate
-```
-
-运行正常的情况下,应该可以看到如下结果:
-
-```
-Database: jdbc:h2:file:./foobardb (H2 1.4)
-Successfully validated 1 migration (execution time 00:00.008s)
-Creating Schema History table: "PUBLIC"."flyway_schema_history"
-Current version of schema "PUBLIC": << Empty Schema >>
-Migrating schema "PUBLIC" to version 1 - Create person table
-Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.033s)
-```
-
-(5)**添加第二个 migration**
-
-在 `/sql` 目录下创建 `V2__Add_people.sql` 文件,内容如下:
-
-```sql
-insert into PERSON (ID, NAME) values (1, 'Axel');
-insert into PERSON (ID, NAME) values (2, 'Mr. Foo');
-insert into PERSON (ID, NAME) values (3, 'Ms. Bar');
-```
-
-运行 Flyway
-
-```bash
-flyway-5.1.4> flyway migrate
-```
-
-运行正常的情况下,应该可以看到如下结果:
-
-```
-Database: jdbc:h2:file:./foobardb (H2 1.4)
-Successfully validated 2 migrations (execution time 00:00.018s)
-Current version of schema "PUBLIC": 1
-Migrating schema "PUBLIC" to version 2 - Add people
-Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.016s)
-```
-
-### JAVA API
-
-(1)**准备**
-
-- Java8+
-- Maven 3.x
-
-(2)**添加依赖**
-
-在 `pom.xml` 中添加依赖:
-
-```xml
-
- ...
-
-
- org.flywaydb
- flyway-core
- 5.1.4
-
-
- com.h2database
- h2
- 1.3.170
-
- ...
-
- ...
-
-```
-
-(3)**集成 Flyway**
-
-添加 `App.java` 文件,内容如下:
-
-```java
-import org.flywaydb.core.Flyway;
-
-public class App {
- public static void main(String[] args) {
- // Create the Flyway instance
- Flyway flyway = new Flyway();
-
- // Point it to the database
- flyway.setDataSource("jdbc:h2:file:./target/foobar", "sa", null);
-
- // Start the migration
- flyway.migrate();
- }
-}
-```
-
-(4)**创建第一个 migration**
-
-添加 `src/main/resources/db/migration/V1__Create_person_table.sql` 文件,内容如下:
-
-```sql
-create table PERSON (
- ID int not null,
- NAME varchar(100) not null
-);
-```
-
-(5)**执行程序**
-
-执行 `App#main`:
-
-运行正常的情况下,应该可以看到如下结果:
-
-```
-INFO: Creating schema history table: "PUBLIC"."flyway_schema_history"
-INFO: Current version of schema "PUBLIC": << Empty Schema >>
-INFO: Migrating schema "PUBLIC" to version 1 - Create person table
-INFO: Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.062s).
-```
-
-(6)**添加第二个 migration**
-
-添加 src/main/resources/db/migration/V2\_\_Add_people.sql 文件,内容如下:
-
-```sql
-insert into PERSON (ID, NAME) values (1, 'Axel');
-insert into PERSON (ID, NAME) values (2, 'Mr. Foo');
-insert into PERSON (ID, NAME) values (3, 'Ms. Bar');
-```
-
-运行正常的情况下,应该可以看到如下结果:
-
-```
-INFO: Current version of schema "PUBLIC": 1
-INFO: Migrating schema "PUBLIC" to version 2 - Add people
-INFO: Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.090s).
-```
-
-### Maven
-
-与 Java API 方式大体相同,区别在 **集成 Flyway** 步骤:
-
-Maven 方式使用插件来集成 Flyway:
-
-```xml
-
- ...
-
-
-
- org.flywaydb
- flyway-maven-plugin
- 5.1.4
-
- jdbc:h2:file:./target/foobar
- sa
-
-
-
- com.h2database
- h2
- 1.4.191
-
-
-
-
-
-
-```
-
-因为用的是插件,所以执行方式不再是运行 Java 类,而是执行 maven 插件:
-
-```bash
-> mvn flyway:migrate
-```
-
-> :point_right: 参考:[示例源码](https://github.com/dunwu/Database/tree/master/codes/middleware/flyway)
-
-### Gradle
-
-本人不用 Gradle,略。
-
-## 入门篇
-
-### 概念
-
-#### Migrations
-
-在 Flyway 中,对于数据库的任何改变都称之为 **Migrations**。
-
-Migrations 可以分为 Versioned migrations 和 Repeatable migrations。
-
-Versioned migrations 有 2 种形式:regular 和 undo。
-
-Versioned migrations 和 Repeatable migrations 都可以使用 SQL 或 JAVA 来编写。
-
-##### Versioned migrations
-
-由一个版本号(version)、一段描述(description)、一个校验(checksum)组成。版本号必须是惟一的。Versioned migrations 只能按顺序执行一次。
-
-一般用于:
-
-- 增删改 tables/indexes/foreign keys/enums/UDTs。
-- 引用数据更新
-- 用户数据校正
-
-Regular 示例:
-
-```sql
-CREATE TABLE car (
- id INT NOT NULL PRIMARY KEY,
- license_plate VARCHAR NOT NULL,
- color VARCHAR NOT NULL
-);
-
-ALTER TABLE owner ADD driver_license_id VARCHAR;
-
-INSERT INTO brand (name) VALUES ('DeLorean');
-```
-
-##### Undo migrations
-
-> 注:仅专业版支持
-
-Undo Versioned Migrations 负责撤销 Regular Versioned migrations 的影响。
-
-Undo 示例:
-
-```sql
-DELETE FROM brand WHERE name='DeLorean';
-
-ALTER TABLE owner DROP driver_license_id;
-
-DROP TABLE car;
-```
-
-##### Repeatable migrations
-
-由一段描述(description)、一个校验(checksum)组成。Versioned migrations 每次执行后,校验(checksum)会更新。
-
-Repeatable migrations 用于管理可以通过一个文件来维护版本控制的数据库对象。
-
-一般用于:
-
-- 创建(重建)views/procedures/functions/packages 等。
-- 大量引用数据重新插入
-
-示例:
-
-```sql
-CREATE OR REPLACE VIEW blue_cars AS
- SELECT id, license_plate FROM cars WHERE color='blue';
-```
-
-##### 基于 SQL 的 migrations
-
-migrations 最常用的编写形式就是 SQL。
-
-基于 SQL 的 migrations 一般用于:
-
-- DDL 变更(针对 TABLES,VIEWS,TRIGGERS,SEQUENCES 等的 CREATE/ALTER/DROP 操作)
-- 简单的引用数据变更(引用数据表中的 CRUD)
-- 简单的大量数据变更(常规数据表中的 CRUD)
-
-**命名规则**
-
-为了被 Flyway 自动识别,SQL migrations 的文件命名必须遵循规定的模式:
-
-
-
-- **Prefix** - `V` 代表 versioned migrations (可配置), `U` 代表 undo migrations (可配置)、 `R` 代表 repeatable migrations (可配置)
-- **Version** - 版本号通过`.`(点)或`_`(下划线)分隔 (repeatable migrations 不需要)
-- **Separator** - `__` (两个下划线) (可配置)
-- **Description** - 下划线或空格分隔的单词
-- **Suffix** - `.sql` (可配置)
-
-##### 基于 JAVA 的 migrations
-
-基于 JAVA 的 migrations 适用于使用 SQL 不容易表达的场景:
-
-- BLOB 和 CLOB 变更
-- 大量数据的高级变更(重新计算、高级格式变更)
-
-**命名规则**
-
-为了被 Flyway 自动识别,JAVA migrations 的文件命名必须遵循规定的模式:
-
-
-
-- **Prefix** - `V` 代表 versioned migrations (可配置), `U` 代表 undo migrations (可配置)、 `R` 代表 repeatable migrations (可配置)
-- **Version** - 版本号通过`.`(点)或`_`(下划线)分隔 (repeatable migrations 不需要)
-- **Separator** - `__` (两个下划线) (可配置)
-- **Description** - 下划线或空格分隔的单词
-
-> :point_right: 更多细节请参考:https://flywaydb.org/documentation/migrations
-
-#### Callbacks
-
-> 注:部分 events 仅专业版支持。
-
-尽管 Migrations 可能已经满足绝大部分场景的需要,但是某些情况下需要你一遍又一遍的执行相同的行为。这可能会重新编译存储过程,更新视图以及许多其他类型的开销。
-
-因为以上原因,Flyway 提供了 Callbacks,用于在 Migrations 生命周期中添加钩子。
-
-Callbacks 可以用 SQL 或 JAVA 来实现。
-
-##### SQL Callbacks
-
-SQL Callbacks 的命名规则为:event 名 + SQL migration。
-
-如: `beforeMigrate.sql`, `beforeEachMigrate.sql`, `afterEachMigrate.sql` 等。
-
-SQL Callbacks 也可以包含描述(description)。这种情况下,SQL Callbacks 文件名 = event 名 + 分隔符 + 描述 + 后缀。例:`beforeRepair__vacuum.sql`
-
-当同一个 event 有多个 SQL callbacks,将按照它们描述(description)的顺序执行。
-
-> **注:** Flyway 也支持你配置的 `sqlMigrationSuffixes`。
-
-##### JAVA Callbacks
-
-> 当 SQL Callbacks 不够方便时,才应考虑 JAVA Callbacks。
-
-JAVA Callbacks 有 3 种形式:
-
-1. **基于 Java 的 Migrations** - 实现 JdbcMigration、SpringJdbcMigration、MigrationInfoProvider、MigrationChecksumProvider、ConfigurationAware、FlywayConfiguration
-2. **基于 Java 的 Callbacks** - 实现 org.flywaydb.core.api.callback 接口。
-3. **自定义 Migration resolvers 和 executors** - 实现 MigrationResolver、MigrationExecutor、ConfigurationAware、FlywayConfiguration 接口。
-
-> :point_right: 更多细节请参考:https://flywaydb.org/documentation/callbacks
-
-#### Error Handlers
-
-> 注:仅专业版支持。
-
-(略)
-
-#### Dry Runs
-
-> 注:仅专业版支持。
-
-(略)
-
-### 命令
-
-Flyway 的功能主要围绕着 7 个基本命令:[Migrate](https://flywaydb.org/documentation/command/migrate)、[Clean](https://flywaydb.org/documentation/command/clean)、[Info](https://flywaydb.org/documentation/command/info)、[Validate](https://flywaydb.org/documentation/command/validate)、[Undo](https://flywaydb.org/documentation/command/undo)、[Baseline](https://flywaydb.org/documentation/command/baseline) 和 [Repair](https://flywaydb.org/documentation/command/repair)。
-
-注:各命令的使用方法细节请查阅官方文档。
-
-### 支持的数据库
-
-- [Oracle](https://flywaydb.org/documentation/database/oracle)
-- [SQL Server](https://flywaydb.org/documentation/database/sqlserver)
-- [DB2](https://flywaydb.org/documentation/database/db2)
-- [MySQL](https://flywaydb.org/documentation/database/mysql)
-- [MariaDB](https://flywaydb.org/documentation/database/mariadb)
-- [PostgreSQL](https://flywaydb.org/documentation/database/postgresql)
-- [Redshift](https://flywaydb.org/documentation/database/redshift)
-- [CockroachDB](https://flywaydb.org/documentation/database/cockroachdb)
-- [SAP HANA](https://flywaydb.org/documentation/database/saphana)
-- [Sybase ASE](https://flywaydb.org/documentation/database/sybasease)
-- [Informix](https://flywaydb.org/documentation/database/informix)
-- [H2](https://flywaydb.org/documentation/database/h2)
-- [HSQLDB](https://flywaydb.org/documentation/database/hsqldb)
-- [Derby](https://flywaydb.org/documentation/database/derby)
-- [SQLite](https://flywaydb.org/documentation/database/sqlite)
-
-## 资料
-
-| [Github](https://github.com/flyway/flyway) | [官方文档](https://flywaydb.org/) |
diff --git a/database/sql/h2.md b/database/sql/h2.md
deleted file mode 100644
index eab6d565d2..0000000000
--- a/database/sql/h2.md
+++ /dev/null
@@ -1,478 +0,0 @@
----
-title: H2 数据库
-date: 2018/08/22
-categories:
- - database
-tags:
- - database
- - sql
----
-
-# H2 数据库
-
-
-
-- [概述](#概述)
-- [使用说明](#使用说明)
-- [Spring 整合 H2](#spring-整合-h2)
-- [H2 SQL](#h2-sql)
-- [数据类型](#数据类型)
-- [集群](#集群)
-- [参考资料](#参考资料)
-
-
-
-## 概述
-
-H2 是一个开源的嵌入式数据库引擎,采用 java 语言编写,不受平台的限制。同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。
-
-## 使用说明
-
-### H2 控制台应用
-
-H2 允许用户通过浏览器接口方式访问 SQL 数据库。
-
-1. 进入[官方下载地址](http://www.h2database.com/html/download.html),选择合适版本,下载并安装到本地。
-2. 启动方式:在 bin 目录下,双击 jar 包;执行 `java -jar h2*.jar`;执行脚本:`h2.bat` 或 `h2.sh`。
-3. 在浏览器中访问:http://localhost:8082,应该可以看到下图中的页面:
-
-
-
-点击 **Connect** ,可以进入操作界面:
-
-
-
-操作界面十分简单,不一一细说。
-
-### 嵌入式应用
-
-#### JDBC API
-
-```java
-Connection conn = DriverManager.
- getConnection("jdbc:h2:~/test");
-conn.close();
-```
-
-> 详见:[Using the JDBC API](http://www.h2database.com/html/tutorial.html#connecting_using_jdbc)
-
-#### 连接池
-
-```java
-import org.h2.jdbcx.JdbcConnectionPool;
-JdbcConnectionPool cp = JdbcConnectionPool.
-create("jdbc:h2:~/test", "sa", "sa");
-Connection conn = cp.getConnection();
-conn.close(); cp.dispose();
-```
-
-> 详见:[Connection Pool](http://www.h2database.com/html/tutorial.html#connection_pool)
-
-#### Maven
-
-```xml
-
- com.h2database
- h2
- 1.4.197
-
-```
-
-> 详见:[Maven 2](http://www.h2database.com/html/build.html#maven2)
-
-#### Hibernate
-
-hibernate.cfg.xml (or use the HSQLDialect):
-
-```xml
-
- org.hibernate.dialect.H2Dialect
-
-```
-
-> 详见:[Hibernate](http://www.h2database.com/html/tutorial.html#using_hibernate)
-
-#### TopLink 和 Glassfish
-
-Datasource class: `org.h2.jdbcx.JdbcDataSource`
-`oracle.toplink.essentials.platform.database.H2Platform`
-
-> 详见:[TopLink and Glassfish](http://www.h2database.com/html/tutorial.html#using_toplink)
-
-### 运行方式
-
-#### 嵌入式
-
-数据库持久化存储为单个文件。
-
-连接字符串:`~/.h2/DBName` 表示数据库文件的存储位置,如果第一次连接则会自动创建数据库。
-
-- `jdbc:h2:~/test` - 'test' 在用户根目录下
-- `jdbc:h2:/data/test` - 'test' 在 /data 目录下
-- `jdbc:h2:test` - 'test' 在当前工作目录
-
-#### 内存式
-
-数据库只在内存中运行,关闭连接后数据库将被清空,适合测试环境
-
-连接字符串:`jdbc:h2:mem:DBName;DB_CLOSE_DELAY=-1`
-
-如果不指定 DBName,则以私有方式启动,只允许一个连接。
-
-- `jdbc:h2:mem:test` - 一个进程中有多个连接
-- `jdbc:h2:mem:` - 未命名的私有库,一个连接
-
-#### 服务模式
-
-H2 支持三种服务模式:
-
-- web server:此种运行方式支持使用浏览器访问 H2 Console
-- TCP server:支持客户端/服务器端的连接方式
-- PG server:支持 PostgreSQL 客户端
-
-启动 tcp 服务连接字符串示例:
-
-- `jdbc:h2:tcp://localhost/~/test` - 用户根目录
-- `jdbc:h2:tcp://localhost//data/test` - 绝对路径
-
-#### 启动服务
-
-执行 `java -cp *.jar org.h2.tools.Server`
-
-执行如下命令,获取选项列表及默认值
-
-```shell
-java -cp h2*.jar org.h2.tools.Server -?
-```
-
-常见的选项如下:
-
-- -web:启动支持 H2 Console 的服务
-- -webPort :服务启动端口,默认为 8082
-- -browser:启动 H2 Console web 管理页面
-- -tcp:使用 TCP server 模式启动
-- -pg:使用 PG server 模式启动
-
-#### 设置
-
-- `jdbc:h2:..;MODE=MySQL` 兼容模式(或 HSQLDB 等)
-- `jdbc:h2:..;TRACE_LEVEL_FILE=3` 记录到 `*.trace.db`
-
-#### 连接字符串参数
-
-- `DB_CLOSE_DELAY` - 要求最后一个正在连接的连接断开后,不要关闭数据库
-- `MODE=MySQL` - 兼容模式,H2 兼容多种数据库,该值可以为:DB2、Derby、HSQLDB、MSSQLServer、MySQL、Oracle、PostgreSQL
-- `AUTO_RECONNECT=TRUE` - 连接丢失后自动重新连接
-- `AUTO_SERVER=TRUE` - 启动自动混合模式,允许开启多个连接,该参数不支持在内存中运行模式
-- `TRACE_LEVEL_SYSTEM_OUT`、`TRACE_LEVEL_FILE` - 输出跟踪日志到控制台或文件, 取值 0 为 OFF,1 为 ERROR(默认值),2 为 INFO,3 为 DEBUG
-- `SET TRACE_MAX_FILE_SIZE mb` - 设置跟踪日志文件的大小,默认为 16M
-
-#### maven 方式
-
-此外,使用 maven 也可以启动 H2 服务。添加以下插件
-
-```xml
-
- org.codehaus.mojo
- exec-maven-plugin
-
-
-
- java
-
-
-
-
- org.h2.tools.Server
-
- -web
- -webPort
- 8090
- -browser
-
-
-
-```
-
-在命令行中执行如下命令启动 H2 Console
-
-```shell
-mvn exec:java
-```
-
-或者建立一个 bat 文件
-
-```shell
-@echo off
-call mvn exec:java
-pause
-```
-
-此操作相当于执行了如下命令:
-
-```shell
-java -jar h2-1.3.168.jar -web -webPort 8090 -browser
-```
-
-## Spring 整合 H2
-
-1. 添加依赖
-
-```xml
-
- com.h2database
- h2
- 1.4.194
-
-```
-
-2. spring 配置
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-## H2 SQL
-
-### SELECT
-
-
-
-### INSERT
-
-
-
-### UPDATE
-
-
-
-### DELETE
-
-
-
-### BACKUP
-
-
-
-### EXPLAIN
-
-
-
-7、MERGE
-
-
-### RUNSCRIPT
-
-运行 sql 脚本文件
-
-
-
-### SCRIPT
-
-根据数据库创建 sql 脚本
-
-
-
-### SHOW
-
-
-
-### ALTER
-
-#### ALTER INDEX RENAME
-
-
-
-#### ALTER SCHEMA RENAME
-
-
-
-#### ALTER SEQUENCE
-
-
-
-#### ALTER TABLE
-
-
-
-##### 增加约束
-
-
-
-##### 修改列
-
-
-
-##### 删除列
-
-
-
-##### 删除序列
-
-
-
-#### ALTER USER
-
-##### 修改用户名
-
-
-
-##### 修改用户密码
-
-
-
-#### ALTER VIEW
-
-
-
-### COMMENT
-
-
-
-### CREATE CONSTANT
-
-
-
-### CREATE INDEX
-
-
-
-### CREATE ROLE
-
-
-
-### CREATE SCHEMA
-
-
-
-### CREATE SEQUENCE
-
-
-
-### CREATE TABLE
-
-
-
-### CREATE TRIGGER
-
-
-
-### CREATE USER
-
-
-
-### CREATE VIEW
-
-
-
-### DROP
-
-
-
-### GRANT RIGHT
-
-给 schema 授权授权
-
-
-
-给 schema 授权给 schema 授权
-
-
-
-#### 复制角色的权限
-
-
-
-### REVOKE RIGHT
-
-#### 移除授权
-
-
-
-#### 移除角色具有的权限
-
-
-
-### ROLLBACK
-
-#### 从某个还原点(savepoint)回滚
-
-
-
-#### 回滚事务
-
-
-
-#### 创建 savepoint
-
-
-
-## 数据类型
-
-
-
-### INT Type
-
-
-
-## 集群
-
-H2 支持两台服务器运行两个数据库成为集群,两个数据库互为备份,如果一个服务器失效,另一个服务器仍然可以工作。另外只有服务模式支持集群配置。
-
-H2 可以通过 CreateCluster 工具创建集群,示例步骤如下(在在一台服务器上模拟两个数据库组成集群):
-
-- 创建目录
- - 创建两个服务器工作的目录
-- 启动 tcp 服务
- - 执行如下命令分别在 9101、9102 端口启动两个使用 tcp 服务模式的数据库
-- 使用 CreateCluster 工具创建集群
- - 如果两个数据库不存在,该命令将会自动创建数据库。如果一个数据库失效,可以先删除坏的数据库文件,重新启动数据库,然后重新运行 CreateCluster 工具
-- 连接数据库现在可以使用如下连接字符串连接集群数据库
- - 监控集群**运行状态**
- - 可以使用如下命令查看配置的集群服务器是否都在运行
-- 限制
- - H2 的集群并不支持针对事务的负载均衡,所以很多操作会使两个数据库产生不一致的结果
-- 执行如下操作时请小心:
- - 自动增长列和标识列不支持集群,当插入数据时,序列值需要手动创建不支持 SET AUTOCOMMIT FALSE 语句;
- - 如果需要设置成为不自动提交,可以执行方法 Connection.setAutoCommit(false)
-
-## 参考资料
-
-- [h2database 官网](http://www.h2database.com/html/main.html)
-- [Java 嵌入式数据库 H2 学习总结(一)——H2 数据库入门](https://www.cnblogs.com/xdp-gacl/p/4171024.html)
diff --git a/database/sql/mysql/install-mysql.md b/database/sql/mysql/install-mysql.md
deleted file mode 100644
index 4a9b994ccf..0000000000
--- a/database/sql/mysql/install-mysql.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# Mysql 安装
-
-
-
-- [安装配置](#安装配置)
- - [安装 mysql yum 源](#安装-mysql-yum-源)
- - [安装 mysql 服务器](#安装-mysql-服务器)
- - [启动 mysql 服务](#启动-mysql-服务)
- - [初始化数据库密码](#初始化数据库密码)
- - [配置远程访问](#配置远程访问)
- - [跳过登录认证](#跳过登录认证)
-- [参考资料](#参考资料)
-
-
-
-## 安装配置
-
-通过 rpm 包安装
-
-centos 的 yum 源中默认是没有 mysql 的,所以我们需要先去官网下载 mysql 的 repo 源并安装。
-
-### 安装 mysql yum 源
-
-官方下载地址:https://dev.mysql.com/downloads/repo/yum/
-
-(1)下载 yum 源
-
-```sh
-$ wget https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
-```
-
-(2)安装 yum repo 文件并更新 yum 缓存
-
-```sh
-$ rpm -ivh mysql80-community-release-el7-1.noarch.rpm
-```
-
-执行结果:
-
-会在 /etc/yum.repos.d/ 目录下生成两个 repo 文件
-
-```sh
-ls | grep mysql
-mysql-community.repo
-mysql-community-source.repo
-```
-
-更新 yum:
-
-```sh
-$ yum clean all
-$ yum makecache
-```
-
-(3)查看 rpm 安装状态
-
-```sh
-$ yum repolist enabled | grep mysql.*
-mysql-connectors-community/x86_64 MySQL Connectors Community 65
-mysql-tools-community/x86_64 MySQL Tools Community 69
-mysql80-community/x86_64 MySQL 8.0 Community Server 33
-```
-
-通过 yum 安装 mysql 有几个重要目录:
-
-```
-# 数据库目录
-/var/lib/mysql/
-# 配置文件
-/usr/share/mysql(mysql.server命令及配置文件)
-# 相关命令
-/usr/bin(mysqladmin mysqldump等命令)
-# 启动脚本
-/etc/rc.d/init.d/(启动脚本文件mysql的目录)
-# 配置文件
-/etc/my.cnf
-```
-
-### 安装 mysql 服务器
-
-```sh
-$ yum install mysql-community-server
-```
-
-### 启动 mysql 服务
-
-```sh
-# 启动 mysql 服务
-$ systemctl start mysqld.service
-
-# 查看运行状态
-$ systemctl status mysqld.service
-
-# 开机启动
-$ systemctl enable mysqld
-$ systemctl daemon-reload
-```
-
-### 初始化数据库密码
-
-查看一下初始密码
-
-```sh
-$ grep "password" /var/log/mysqld.log
-2018-09-30T03:13:41.727736Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: %:lt+srWu4k1
-```
-
-执行命令:
-
-```sh
-mysql -uroot -p
-```
-
-输入临时密码,进入 mysql
-
-```sh
-ALTER user 'root'@'localhost' IDENTIFIED BY 'Tw#123456';
-```
-
-注:密码强度默认为中等,大小写字母、数字、特殊符号,只有修改成功后才能修改配置再设置更简单的密码
-
-### 配置远程访问
-
-```
-GRANT ALL ON *.* TO 'root'@'localhost';
-FLUSH PRIVILEGES;
-```
-
-### 跳过登录认证
-
-```
-vim /etc/my.cnf
-```
-
-在 [mysqld] 下面加上 skip-grant-tables
-
-作用是登录时跳过登录认证,换句话说就是 root 什么密码都可以登录进去。
-
-执行 `service mysqld restart`,重启 mysql
-
-## 参考资料
-
-https://www.cnblogs.com/xiaopotian/p/8196464.html
-https://www.cnblogs.com/bigbrotherer/p/7241845.html
-https://blog.csdn.net/managementandjava/article/details/80039650
-http://www.manongjc.com/article/6996.html
-https://www.cnblogs.com/xyabk/p/8967990.html
diff --git a/database/sql/mysql/mysql-advanced.md b/database/sql/mysql/mysql-advanced.md
deleted file mode 100644
index e329c66f61..0000000000
--- a/database/sql/mysql/mysql-advanced.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# Mysql 进阶
-
-## mysql 命令行
-
-执行 `mysql -u root -p` 命令可以进入 Mysql 命令行模式。
-
-- `-u` 指定 mysql 用户
-- `-p` 后面跟的是密码,注意不要有空格
-
-## 备份与恢复
-
-Mysql 备份数据使用 mysqldump 命令。
-
-mysqldump 将数据库中的数据备份成一个文本文件,表的结构和表中的数据将存储在生成的文本文件中。
-
-### 备份
-
-(1)备份一个数据库
-
-语法:
-
-```
-mysqldump -u -p [ ...] > backup.sql
-```
-
-- username 数据库用户
-- dbname 数据库名称
-- table1 和 table2 参数表示需要备份的表的名称,为空则整个数据库备份;
-- BackupName.sql 参数表设计备份文件的名称,文件名前面可以加上一个绝对路径。通常将数据库被分成一个后缀名为 sql 的文件
-
-(2)备份多个数据库
-
-```
-mysqldump -u -p --databases ... > backup.sql
-```
-
-(3)备份所有数据库
-
-```
-mysqldump -u -p -all-databases > backup.sql
-```
-
-### 恢复
-
-Mysql 恢复数据使用 mysqldump 命令。
-
-语法:
-
-```
-mysql -u -p < backup.sql
-```
-
-## 问题
-
-### JDBC 与 Mysql 因 CST 时区协商无解导致偏差了14或13小时
-
-**现象**
-
-数据库中存储的 Timestamp 字段值比真实值少了 13 个小时。
-
-**原因**
-
-- 当 JDBC 与 MySQL 开始建立连接时,会获取服务器参数。
-- 当 MySQL 的 `time_zone` 值为 `SYSTEM` 时,会取 `system_time_zone` 值作为协调时区,若得到的是 `CST` 那么 Java 会误以为这是 `CST -0500` ,因此会给出错误的时区信息(国内一般是`CST +0800`,即东八区)。
-
-> 查看时区方法:
->
-> 通过 `show variables like '%time_zone%';` 命令查看 Mysql 时区配置:
->
-> ```
-> mysql> show variables like '%time_zone%';
-> +------------------+--------+
-> | Variable_name | Value |
-> +------------------+--------+
-> | system_time_zone | CST |
-> | time_zone | SYSTEM |
-> +------------------+--------+
-> ```
-
-**解决方案**
-
-方案一
-
-```
-mysql> set global time_zone = '+08:00';
-Query OK, 0 rows affected (0.00 sec)
-
-mysql> set time_zone = '+08:00';
-Query OK, 0 rows affected (0.00 sec)
-```
-
-方案二
-
-修改 `my.cnf` 文件,在 `[mysqld]` 节下增加 `default-time-zone = '+08:00'` ,然后重启。
\ No newline at end of file
diff --git a/database/sql/mysql/mysql.md b/database/sql/mysql/mysql.md
deleted file mode 100644
index 0d44cd1e61..0000000000
--- a/database/sql/mysql/mysql.md
+++ /dev/null
@@ -1,574 +0,0 @@
----
-title: Mysql
-date: 2018/07/02
-categories:
-- database
-tags:
-- database
-- mysql
----
-
-# Mysql
-
-> 关键词:存储引擎,数据类型,事务,MVCC,索引,执行计划,主从复制
-
-
-
-- [1. 存储引擎](#1-存储引擎)
- - [1.1. InnoDB](#11-innodb)
- - [1.2. MyISAM](#12-myisam)
- - [1.3. 选择存储引擎](#13-选择存储引擎)
-- [2. 数据类型](#2-数据类型)
- - [2.1. 整型](#21-整型)
- - [2.2. 浮点数](#22-浮点数)
- - [2.3. 字符串](#23-字符串)
- - [2.4. 时间和日期](#24-时间和日期)
-- [3. 事务](#3-事务)
- - [3.1. 事务隔离级别](#31-事务隔离级别)
- - [3.2. 死锁](#32-死锁)
-- [4. MVCC](#4-mvcc)
-- [5. 索引](#5-索引)
- - [5.1. 索引的优点和缺点](#51-索引的优点和缺点)
- - [5.2. 索引类型](#52-索引类型)
- - [5.3. 索引数据结构](#53-索引数据结构)
- - [5.4. 索引原则](#54-索引原则)
-- [6. 查询性能优化](#6-查询性能优化)
- - [6.1. 使用 Explain 进行分析](#61-使用-explain-进行分析)
- - [6.2. 优化数据访问](#62-优化数据访问)
- - [6.3. 重构查询方式](#63-重构查询方式)
-- [7. 复制](#7-复制)
- - [7.1. 主从复制](#71-主从复制)
- - [7.2. 读写分离](#72-读写分离)
-- [8. 参考资料](#8-参考资料)
-
-
-
-## 1. 存储引擎
-
-在文件系统中,Mysql 将每个数据库(也可以成为 schema)保存为数据目录下的一个子目录。创建表示,Mysql 会在数据库子目录下创建一个和表同名的 .frm 文件保存表的定义。因为 Mysql 使用文件系统的目录和文件来保存数据库和表的定义,大小写敏感性和具体平台密切相关。Windows 中大小写不敏感;类 Unix 中大小写敏感。**不同的存储引擎保存数据和索引的方式是不同的,但表的定义则是在 Mysql 服务层统一处理的。**
-
-### 1.1. InnoDB
-
-InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要 InnoDB 不支持的特性时,才考虑使用其它存储引擎。
-
-InnoDB 实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ 间隙锁(next-key locking)防止幻影读。
-
-主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。
-
-内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。
-
-支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。
-
-### 1.2. MyISAM
-
-MyISAM 设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用 MyISAM。
-
-MyISAM 提供了大量的特性,包括压缩表、空间数据索引等。
-
-不支持事务。
-
-不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。
-
-可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。
-
-如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。
-
-### 1.3. 选择存储引擎
-
-#### Mysql 内置的存储引擎
-
-- **InnoDB** - Mysql 的默认事务型存储引擎。性能不错且支持自动崩溃恢复。
-- **MyISAM** - Mysql 5.1 版本前的默认存储引擎。特性丰富但不支持事务,也没有崩溃恢复功能。
-- **CSV** - 可以将 CSV 文件作为 Mysql 的表来处理,但这种表不支持索引。
-- **Memory** - 适合快速访问数据,且数据不会被修改,重启丢失也没有关系。
-- **NDB** - 用于 Mysql 集群场景。
-
-#### 如何选择合适的存储引擎?
-
-大多数情况下,InnoDB 都是正确的选择,除非需要用到 InnoDB 不具备的特性。
-
-如果应用需要选择 InnoDB 以外的存储引擎,可以考虑以下因素:
-
-- 事务:如果需要支持事务,InnoDB 是首选。如果不需要支持事务,且主要是 SELECT 和 INSERT 操作,MyISAM 是不错的选择。
-- 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。所以,InnoDB 并发性能更高。
-- 外键:InnoDB 支持外键。
-- 备份:InnoDB 支持在线热备份。
-- 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
-- 其它特性:MyISAM 支持压缩表和空间数据索引。
-
-#### 转换表的存储引擎
-
-下面的语句可以将 mytable 表的引擎修改为 InnoDB
-
-```sql
-ALTER TABLE mytable ENGINE = InnoDB
-```
-
-## 2. 数据类型
-
-### 2.1. 整型
-
-TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。
-
-INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。
-
-### 2.2. 浮点数
-
-FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。
-
-FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。
-
-### 2.3. 字符串
-
-主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。
-
-VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。
-
-VARCHAR 会保留字符串末尾的空格,而 CHAR 会删除。
-
-### 2.4. 时间和日期
-
-MySQL 提供了两种相似的日期时间类型:DATATIME 和 TIMESTAMP。
-
-#### DATATIME
-
-能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。
-
-它与时区无关。
-
-默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATATIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。
-
-#### TIMESTAMP
-
-和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。
-
-它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。
-
-MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。
-
-默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。
-
-应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。
-
-## 3. 事务
-
-事务指的是满足 ACID 特性的一组操作。
-
-Mysql 中,使用 `START TRANSACTION` 语句开始一个事务;使用 `COMMIT` 语句提交所有的修改;使用 `ROLLBACK` 语句撤销所有的修改。
-
-Mysql 不是所有的存储引擎都实现了事务处理。支持事务的存储引擎有:InnoDB 和 NDB Cluster。
-
-用户可以根据业务是否需要事务处理(事务处理可以保证数据安全,但会增加系统开销),选择合适的存储引擎。
-
-Mysql 默认采用自动提交(AUTOCOMMIT)模式。
-
-### 3.1. 事务隔离级别
-
-InnoDB 支持 SQL 标准的四种隔离级别,默认的级别是可重复读。并且,通过间隙锁(next-key locking)策略防止幻读的出现。
-
-### 3.2. 死锁
-
-在 Mysql 中,锁的行为和顺序与存储引擎相关。
-
-InnoDB 中解决死锁问题的方法是:将持有最少行级排他锁的事务进行回滚。
-
-## 4. MVCC
-
-InnoDB 的 MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间。当然,存储的并不是实际的时间值,而是系统版本号。每开始一个新事务,系统版本号就会自动递增。事务开始时的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
-
-下面是在可重复读隔离级别下,MVCC 的具体操作:
-
-**SELECT**
-
-当开始新一个事务时,该事务的版本号肯定会大于当前所有数据行快照的创建版本号,理解这一点很关键。
-
-多个事务必须读取到同一个数据行的快照,并且这个快照是距离现在最近的一个有效快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。
-
-把没有对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于 T 的版本号,因为如果大于或者等于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。
-
-除了上面的要求,T 所要读取的数据行快照的删除版本号必须大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。
-
-**INSERT**
-
-将当前系统版本号作为数据行快照的创建版本号。
-
-**DELETE**
-
-将当前系统版本号作为数据行快照的删除版本号。
-
-**UPDATE**
-
-将当前系统版本号作为更新后的数据行快照的创建版本号,同时将当前系统版本号作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。
-
-## 5. 索引
-
-索引能够轻易将查询性能提升几个数量级。
-
-对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。对于中到大型的表,索引就非常有效。但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。
-
-索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。
-
-### 5.1. 索引的优点和缺点
-
-优点:
-
-- 大大减少了服务器需要扫描的数据行数。
-- 帮助服务器避免进行排序和创建临时表(B+Tree 索引是有序的,可以用来做 ORDER BY 和 GROUP BY 操作);
-- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,也就将相邻的数据都存储在一起)。
-
-缺点:
-
-1. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
-2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立组合索引那么需要的空间就会更大。
-3. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
-
-### 5.2. 索引类型
-
-MySQL 目前主要有以下几种索引类型:
-
-#### 普通索引
-
-普通索引:最基本的索引,没有任何限制。
-
-```sql
-CREATE TABLE `table` (
- ...
- INDEX index_name (title(length))
-)
-```
-
-#### 唯一索引
-
-唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
-
-```sql
-CREATE TABLE `table` (
- ...
- UNIQUE indexName (title(length))
-)
-```
-
-#### 主键索引
-
-主键索引:一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。
-
-```sql
-CREATE TABLE `table` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- ...
- PRIMARY KEY (`id`)
-)
-```
-
-#### 组合索引
-
-组合索引:多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
-
-```sql
-CREATE TABLE `table` (
- ...
- INDEX index_name (title(length), title(length), ...)
-)
-```
-
-#### 全文索引
-
-全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext 索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的 WHERE 语句的参数匹配。fulltext 索引配合 match against 操作使用,而不是一般的 WHERE 语句加 LIKE。它可以在 CREATE TABLE,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用 CREATE INDEX 创建 fulltext 索引,要比先为一张表建立 fulltext 然后再将数据写入的速度快很多。
-
-```sql
-CREATE TABLE `table` (
- `content` text CHARACTER NULL,
- ...
- FULLTEXT (content)
-)
-```
-
-### 5.3. 索引数据结构
-
-#### B+Tree 索引
-
-B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。
-
-因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。除了用于查找,还可以用于排序和分组。
-
-可以指定多个列作为索引列,多个索引列共同组成键。
-
-B+Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。
-
-如果不是按照索引列的顺序进行查找,则无法使用索引。
-
-InnoDB 的 B+Tree 索引分为主索引和辅助索引。
-
-主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
-
-
-
-
-
-辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。
-
-
-
-
-
-##### B Tree 原理
-
-###### B-Tree
-
-
-
-
-
-定义一条数据记录为一个二元组 [key, data],B-Tree 是满足下列条件的数据结构:
-
-- 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的;
-- 一个节点中的 key 从左到右非递减排列;
-- 如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不为 null,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。
-
-查找算法:首先在根节点进行二分查找,如果找到则返回对应节点的 data,否则在相应区间的指针指向的节点递归进行查找。
-
-由于插入删除新的数据记录会破坏 B-Tree 的性质,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作以保持 B-Tree 性质。
-
-###### B+Tree
-
-
-
-
-
-与 B-Tree 相比,B+Tree 有以下不同点:
-
-- 每个节点的指针上限为 2d 而不是 2d+1(d 为节点的出度);
-- 内节点不存储 data,只存储 key;
-- 叶子节点不存储指针。
-
-###### 顺序访问指针的 B+Tree
-
-
-
-
-
-一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
-
-###### 优势
-
-红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B Tree 作为索引结构,主要有以下两个原因:
-
-(一)更少的检索次数
-
-平衡树检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(logdN),其中 d 为每个节点的出度。
-
-红黑树的出度为 2,而 B Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B Tree 大非常多,因此检索的次数也就更多。
-
-B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,检索效率会更高。
-
-(二)利用计算机预读特性
-
-为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。
-
-操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,相邻的节点也能够被预先载入。
-
-更多内容请参考:[MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
-
-#### 哈希索引
-
-InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
-
-哈希索引能以 O(1) 时间进行查找,但是失去了有序性,它具有以下限制:
-
-- 无法用于排序与分组;
-- 只支持精确查找,无法用于部分查找和范围查找;
-
-#### 全文索引
-
-MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
-
-全文索引一般使用倒排索引实现,它记录着关键词到其所在文档的映射。
-
-InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
-
-#### 空间数据索引(R-Tree)
-
-MyISAM 存储引擎支持空间数据索引,可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
-
-必须使用 GIS 相关的函数来维护数据。
-
-### 5.4. 索引原则
-
-#### 最左前缀匹配原则
-
-mysql 会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。
-
-例如:`a = 1 and b = 2 and c > 3 and d = 4`,如果建立(a,b,c,d)顺序的索引,d 是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d 的顺序可以任意调整。
-
-让选择性最强的索引列放在前面,索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。
-
-例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
-
-```sql
-SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
-COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
-COUNT(*)
-FROM payment;
-```
-
-```
- staff_id_selectivity: 0.0001
-customer_id_selectivity: 0.0373
- COUNT(*): 16049
-```
-
-#### = 和 in 可以乱序
-
-比如 a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql 的查询优化器会帮你优化成索引可以识别的形式。
-
-#### 索引列不能参与计算
-
-在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。
-
-例如下面的查询不能使用 actor_id 列的索引:
-
-```
-SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
-```
-
-#### 尽量的扩展索引,不要新建索引
-
-比如表中已经有 a 的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
-
-#### 多列索引
-
-在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
-
-```
-SELECT film_id, actor_ id FROM sakila.film_actor
-WhERE actor_id = 1 AND film_id = 1;
-```
-
-#### 前缀索引
-
-对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
-
-对于前缀长度的选取需要根据索引选择性来确定。
-
-#### 覆盖索引
-
-索引包含所有需要查询的字段的值。
-
-具有以下优点:
-
-- 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。
-- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
-- 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
-
-## 6. 查询性能优化
-
-### 6.1. 使用 Explain 进行分析
-
-Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explain 结果来优化查询语句。
-
-比较重要的字段有:
-
-- select_type : 查询类型,有简单查询、联合查询、子查询等
-- key : 使用的索引
-- rows : 扫描的行数
-
-更多内容请参考:[MySQL 性能优化神器 Explain 使用分析](https://segmentfault.com/a/1190000008131735)
-
-### 6.2. 优化数据访问
-
-#### 减少请求的数据量
-
-(一)只返回必要的列
-
-最好不要使用 SELECT \* 语句。
-
-(二)只返回必要的行
-
-使用 WHERE 语句进行查询过滤,有时候也需要使用 LIMIT 语句来限制返回的数据。
-
-(三)缓存重复查询的数据
-
-使用缓存可以避免在数据库中进行查询,特别要查询的数据经常被重复查询,缓存可以带来的查询性能提升将会是非常明显的。
-
-#### 减少服务器端扫描的行数
-
-最有效的方式是使用索引来覆盖查询。
-
-### 6.3. 重构查询方式
-
-#### 切分大查询
-
-一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。
-
-```
-DELEFT FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH);
-```
-
-```
-rows_affected = 0
-do {
- rows_affected = do_query(
- "DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
-} while rows_affected > 0
-```
-
-#### 分解大连接查询
-
-将一个大连接查询(JOIN)分解成对每一个表进行一次单表查询,然后将结果在应用程序中进行关联,这样做的好处有:
-
-- 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
-- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
-- 减少锁竞争;
-- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可扩展。
-- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。
-
-```
-SELECT * FROM tag
-JOIN tag_post ON tag_post.tag_id=tag.id
-JOIN post ON tag_post.post_id=post.id
-WHERE tag.tag='mysql';
-```
-
-```
-SELECT * FROM tag WHERE tag='mysql';
-SELECT * FROM tag_post WHERE tag_id=1234;
-SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
-```
-
-## 7. 复制
-
-### 7.1. 主从复制
-
-主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。
-
-- **binlog 线程** :负责将主服务器上的数据更改写入二进制文件(binlog)中。
-- **I/O 线程** :负责从主服务器上读取二进制日志文件,并写入从服务器的中继日志中。
-- **SQL 线程** :负责读取中继日志并重放其中的 SQL 语句。
-
-
-
-
-
-### 7.2. 读写分离
-
-主服务器用来处理写操作以及实时性要求比较高的读操作,而从服务器用来处理读操作。
-
-读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。
-
-MySQL 读写分离能提高性能的原因在于:
-
-- 主从服务器负责各自的读和写,极大程度缓解了锁的争用;
-- 从服务器可以配置 MyISAM 引擎,提升查询性能以及节约系统开销;
-- 增加冗余,提高可用性。
-
-
-
-
-
-## 8. 参考资料
-
-- BaronScbwartz, PeterZaitsev, VadimTkacbenko 等. 高性能 MySQL[M]. 电子工业出版社, 2013.
-- 姜承尧. MySQL 技术内幕: InnoDB 存储引擎 [M]. 机械工业出版社, 2011.
-- [20+ 条 MySQL 性能优化的最佳经验](https://www.jfox.info/20-tiao-mysql-xing-nen-you-hua-de-zui-jia-jing-yan.html)
-- [How to create unique row ID in sharded databases?](https://stackoverflow.com/questions/788829/how-to-create-unique-row-id-in-sharded-databases)
-- [SQL Azure Federation – Introduction](http://geekswithblogs.net/shaunxu/archive/2012/01/07/sql-azure-federation-ndash-introduction.aspx)
diff --git a/database/sql/postgresql.md b/database/sql/postgresql.md
deleted file mode 100644
index a065de11bc..0000000000
--- a/database/sql/postgresql.md
+++ /dev/null
@@ -1,193 +0,0 @@
-# PostgreSQL 快速指南
-
-> [PostgreSQL](https://www.postgresql.org/) 是一个关系型数据库(RDBM)。
->
-> 关键词:Database, RDBM, psql
-
-
-
-
-
-- [安装](#安装)
-- [添加新用户和新数据库](#添加新用户和新数据库)
-- [登录数据库](#登录数据库)
-- [控制台命令](#控制台命令)
-- [数据库操作](#数据库操作)
-- [备份和恢复](#备份和恢复)
-- [参考资料](#参考资料)
-
-
-
-## 安装
-
-> 本文仅以运行在 Centos 环境下举例。
-
-进入[官方下载页面](https://www.postgresql.org/download/),根据操作系统选择合适版本。
-
-官方下载页面要求用户选择相应版本,然后动态的给出安装提示,如下图所示:
-
-
-
-前 3 步要求用户选择,后 4 步是根据选择动态提示的安装步骤
-
-(1)选择 PostgreSQL 版本
-
-(2)选择平台
-
-(3)选择架构
-
-(4)安装 PostgreSQL 的 rpm 仓库(为了识别下载源)
-
-```sh
-yum install https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-centos10-10-2.noarch.rpm
-```
-
-(5)安装客户端
-
-```sh
-yum install postgresql10
-```
-
-(6)安装服务端(可选的)
-
-```sh
-yum install postgresql10-server
-```
-
-(7)设置开机启动(可选的)
-
-```sh
-/usr/pgsql-10/bin/postgresql-10-setup initdb
-systemctl enable postgresql-10
-systemctl start postgresql-10
-```
-
-## 添加新用户和新数据库
-
-初次安装后,默认生成一个名为 postgres 的数据库和一个名为 postgres 的数据库用户。这里需要注意的是,同时还生成了一个名为 postgres 的 Linux 系统用户。
-
-首先,新建一个 Linux 新用户,可以取你想要的名字,这里为 dbuser。
-
-```
-sudo adduser dbuser
-```
-
-使用 psql 命令登录 PostgreSQL 控制台:
-
-```
-sudo -u postgres psql
-```
-
-这时相当于系统用户 postgres 以同名数据库用户的身份,登录数据库,这是不用输入密码的。如果一切正常,系统提示符会变为"postgres=#",表示这时已经进入了数据库控制台。以下的命令都在控制台内完成。
-
-(1)使用 `\password` 命令,为 postgres 用户设置一个密码。
-
-```
-postgres=# \password postgres
-```
-
-(2)创建数据库用户 dbuser(刚才创建的是 Linux 系统用户),并设置密码。
-
-```sql
-CREATE USER dbuser WITH PASSWORD 'password';
-```
-
-(3)创建用户数据库,这里为 exampledb,并指定所有者为 dbuser。
-
-```sql
-CREATE DATABASE exampledb OWNER dbuser;
-```
-
-(4)将 exampledb 数据库的所有权限都赋予 dbuser,否则 dbuser 只能登录控制台,没有任何数据库操作权限。
-
-```sql
-GRANT ALL PRIVILEGES ON DATABASE exampledb to dbuser;
-```
-
-(5)使用\q 命令退出控制台(也可以直接按 ctrl+D)。
-
-## 登录数据库
-
-添加新用户和新数据库以后,就要以新用户的名义登录数据库,这时使用的是 psql 命令。
-
-```
-psql -U dbuser -d exampledb -h 127.0.0.1 -p 5432
-```
-
-上面命令的参数含义如下:-U 指定用户,-d 指定数据库,-h 指定服务器,-p 指定端口。
-
-输入上面命令以后,系统会提示输入 dbuser 用户的密码。输入正确,就可以登录控制台了。
-
-psql 命令存在简写形式。如果当前 Linux 系统用户,同时也是 PostgreSQL 用户,则可以省略用户名(-U 参数的部分)。举例来说,我的 Linux 系统用户名为 ruanyf,且 PostgreSQL 数据库存在同名用户,则我以 ruanyf 身份登录 Linux 系统后,可以直接使用下面的命令登录数据库,且不需要密码。
-
-```
-psql exampledb
-```
-
-此时,如果 PostgreSQL 内部还存在与当前系统用户同名的数据库,则连数据库名都可以省略。比如,假定存在一个叫做 ruanyf 的数据库,则直接键入 psql 就可以登录该数据库。
-
-psql
-
-另外,如果要恢复外部数据,可以使用下面的命令。
-
-```
-psql exampledb < exampledb.sql
-```
-
-## 控制台命令
-
-除了前面已经用到的 \password 命令(设置密码)和 \q 命令(退出)以外,控制台还提供一系列其他命令。
-
-```
-\password 设置密码
-\q 退出
-\h 查看SQL命令的解释,比如\h select
-\? 查看psql命令列表
-\l 列出所有数据库
-\c [database_name] 连接其他数据库
-\d 列出当前数据库的所有表格
-\d [table_name] 列出某一张表格的结构
-\x 对数据做展开操作
-\du 列出所有用户
-```
-
-## 数据库操作
-
-基本的数据库操作,就是使用一般的 SQL 语言。
-
-```sql
-# 创建新表
-CREATE TABLE user_tbl(name VARCHAR(20), signup_date DATE);
-# 插入数据
-INSERT INTO user_tbl(name, signup_date) VALUES('张三', '2013-12-22');
-# 选择记录
-SELECT * FROM user_tbl;
-# 更新数据
-UPDATE user_tbl set name = '李四' WHERE name = '张三';
-# 删除记录
-DELETE FROM user_tbl WHERE name = '李四' ;
-# 添加栏位
-ALTER TABLE user_tbl ADD email VARCHAR(40);
-# 更新结构
-ALTER TABLE user_tbl ALTER COLUMN signup_date SET NOT NULL;
-# 更名栏位
-ALTER TABLE user_tbl RENAME COLUMN signup_date TO signup;
-# 删除栏位
-ALTER TABLE user_tbl DROP COLUMN email;
-# 表格更名
-ALTER TABLE user_tbl RENAME TO backup_tbl;
-# 删除表格
-DROP TABLE IF EXISTS backup_tbl;
-```
-
-## 备份和恢复
-
-```sh
-$ pg_dump --format=t -d db_name -U user_name -h 127.0.0.1 -O -W > dump.sql
-$ psql -h 127.0.0.1 -U user_name db_name < dump.sql
-```
-
-## 参考资料
-
-- https://www.postgresql.org/download/
-- http://www.ruanyifeng.com/blog/2013/12/getting_started_with_postgresql.html
diff --git a/database/sql/sql.md b/database/sql/sql.md
deleted file mode 100644
index 8ddc61e8fb..0000000000
--- a/database/sql/sql.md
+++ /dev/null
@@ -1,1161 +0,0 @@
----
-title: Sql
-date: 2015/05/01
-categories:
-- database
-tags:
-- database
-- sql
----
-
-# Sql
-
-> 关系型数据库 SQL 语句大同小异。本文以 Mysql 为例。
->
-> 本文的示例在 Mysql 5.7 下都可以测试通过。
-
-
-
-- [1. 概念](#1-概念)
-- [2. SQL 基础](#2-sql-基础)
-- [3. 增删改查](#3-增删改查)
-- [4. 过滤](#4-过滤)
-- [5. 函数](#5-函数)
-- [6. 排序和分组](#6-排序和分组)
-- [7. 子查询](#7-子查询)
-- [8. 连接和组合](#8-连接和组合)
-- [9. 数据定义](#9-数据定义)
-- [10. 约束](#10-约束)
-- [11. 事务处理](#11-事务处理)
-- [12. 权限控制](#12-权限控制)
-- [13. 存储过程](#13-存储过程)
-- [14. 游标](#14-游标)
-- [15. 触发器](#15-触发器)
-- [16. 知识点小结](#16-知识点小结)
-- [17. 参考资料](#17-参考资料)
-
-
-
-## 1. 概念
-
-- 数据库(database):保存有组织的数据的容器(通常是一个文件或一组文件)。
-- 数据表(table):某种特定类型数据的结构化清单。
-- 模式(schema):关于数据库和表的布局及特性的信息。模式定义了数据在表中如何存储,包含存储什么样的数据,数据如何分解,各部分信息如何命名等信息。数据库和表都有模式。
-- 列(column):表中的一个字段。所有表都是由一个或多个列组成的。
-- 行(row):表中的一个记录。
-
-## 2. SQL 基础
-
-> SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。
-
-### SQL 语法结构
-
-
-
-
-
-SQL 语法结构包括:
-
-- 子句,是语句和查询的组成成分。(在某些情况下,这些都是可选的。)
-- 表达式,可以产生任何标量值,或由列和行的数据库表
-- 谓词,给需要评估的 SQL 三值逻辑(3VL)(true/false/unknown)或布尔真值指定条件,并限制语句和查询的效果,或改变程序流程。
-- 查询,基于特定条件检索数据。这是 SQL 的一个重要组成部分。
-- 语句,可以持久地影响纲要和数据,也可以控制数据库事务、程序流程、连接、会话或诊断。
-
-### SQL 语法要点
-
-- SQL 语句不区分大小写,但是数据库表名、列名和值是否区分,依赖于具体的 DBMS 以及配置。
-
-例如:SELECT 与 select 、Select 是相同的。
-
-- 多条 SQL 语句必须以分号(;)分隔。
-
-- 处理 SQL 语句时,所有空格都被忽略。SQL 语句可以写成一行,也可以分写为多行。
-
-```sql
--- 一行 SQL 语句
-UPDATE user SET username='robot', password='robot' WHERE username = 'root';
-
--- 多行 SQL 语句
-UPDATE user
-SET username='robot', password='robot'
-WHERE username = 'root';
-```
-
-- SQL 支持三种注释
-
-```sql
-## 注释1
--- 注释2
-/* 注释3 */
-```
-
-### SQL 分类
-
-### 数据定义语言(DDL)
-
-数据定义语言(Data Definition Language,DDL)是 SQL 语言集中负责数据结构定义与数据库对象定义的语言。
-
-DDL 的主要功能是定义数据库对象。
-
-DDL 的核心指令是 `CREATE`、`ALTER`、`DROP`。
-
-#### 数据操纵语言(DML)
-
-数据操纵语言(Data Manipulation Language, DML)是用于数据库操作,对数据库其中的对象和数据运行访问工作的编程语句。
-
-DML 的主要功能是访问数据,因此其语法都是以读写数据库为主。
-
-DML 的核心指令是 `INSERT`、`UPDATE`、`DELETE`、`SELECT`。这四个指令合称 CRUD(Create, Read, Update, Delete),即增删改查。
-
-#### 数据控制语言(DCL)
-
-数据控制语言 (Data Control Language, DCL) 是一种可对数据访问权进行控制的指令,它可以控制特定用户账户对数据表、查看表、预存程序、用户自定义函数等数据库对象的控制权。
-
-DCL 的核心指令是 `GRANT`、`REVOKE`。
-
-DCL 以控制用户的访问权限为主,因此其指令作法并不复杂,可利用 DCL 控制的权限有:
-
-- CONNECT
-- SELECT
-- INSERT
-- UPDATE
-- DELETE
-- EXECUTE
-- USAGE
-- REFERENCES
-
-根据不同的 DBMS 以及不同的安全性实体,其支持的权限控制也有所不同。
-
-#### 事务控制语言(TCL)
-
-事务控制语言 (Transaction Control Language, TCL) 用于管理数据库中的事务。这些用于管理由 DML 语句所做的更改。它还允许将语句分组为逻辑事务。
-
-TCL 的核心指令是 `COMMIT`、`ROLLBACK`。
-
-## 3. 增删改查
-
-### 插入数据
-
-#### 要点
-
-- `INSERT INTO` 语句用于向表中插入新记录。
-
-#### 示例
-
-**插入完整的行**
-
-```sql
-INSERT INTO user
-VALUES (10, 'root', 'root', 'xxxx@163.com');
-```
-
-**插入行的一部分**
-
-```sql
-INSERT INTO user(username, password, email)
-VALUES ('admin', 'admin', 'xxxx@163.com');
-```
-
-**插入查询出来的数据**
-
-```sql
-INSERT INTO user(username)
-SELECT name
-FROM account;
-```
-
-### 更新数据
-
-#### 要点
-
-- `UPDATE` 语句用于更新表中的记录。
-
-#### 示例
-
-```sql
-UPDATE user
-SET username='robot', password='robot'
-WHERE username = 'root';
-```
-
-### 删除数据
-
-#### 要点
-
-- `DELETE` 语句用于删除表中的记录。
-- `TRUNCATE TABLE` 可以清空表,也就是删除所有行。
-
-#### 示例
-
-**删除表中的指定数据**
-
-```sql
-DELETE FROM user
-WHERE username = 'robot';
-```
-
-**清空表中的数据**
-
-```sql
-TRUNCATE TABLE user;
-```
-
-### 查询数据
-
-#### 要点
-
-- `SELECT` 语句用于从数据库中查询数据。
-- `DISTINCT` 用于返回唯一不同的值。它作用于所有列,也就是说所有列的值都相同才算相同。
-- `LIMIT` 限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。
- - `ASC` :升序(默认)
- - `DESC` :降序
-
-#### 示例
-
-**查询单列**
-
-```sql
-SELECT prod_name
-FROM products;
-```
-
-**查询多列**
-
-```sql
-SELECT prod_id, prod_name, prod_price
-FROM products;
-```
-
-**查询所有列**
-
-```sql
-ELECT *
-FROM products;
-```
-
-**查询不同的值**
-
-```sql
-SELECT DISTINCT
-vend_id FROM products;
-```
-
-**限制查询结果**
-
-```sql
--- 返回前 5 行
-SELECT * FROM mytable LIMIT 5;
-SELECT * FROM mytable LIMIT 0, 5;
--- 返回第 3 ~ 5 行
-SELECT * FROM mytable LIMIT 2, 3;
-```
-
-## 4. 过滤
-
-### WHERE
-
-#### 要点
-
-- `WHERE` 子句用于过滤记录,即缩小访问数据的范围。
-- `WHERE` 后跟一个返回 `true` 或 `false` 的条件。
-- `WHERE` 可以与 `SELECT`,`UPDATE` 和 `DELETE` 一起使用。
-- 可以在 `WHERE` 子句中使用的操作符
-
-| 运算符 | 描述 |
-| ------- | ------------------------------------------------------ |
-| = | 等于 |
-| <> | 不等于。注释:在 SQL 的一些版本中,该操作符可被写成 != |
-| > | 大于 |
-| < | 小于 |
-| >= | 大于等于 |
-| <= | 小于等于 |
-| BETWEEN | 在某个范围内 |
-| LIKE | 搜索某种模式 |
-| IN | 指定针对某个列的多个可能值 |
-
-#### 示例
-
-**`SELECT` 语句中的 `WHERE` 子句**
-
-```sql
-SELECT * FROM Customers
-WHERE cust_name = 'Kids Place';
-```
-
-**`UPDATE` 语句中的 `WHERE` 子句**
-
-```sql
-UPDATE Customers
-SET cust_name = 'Jack Jones'
-WHERE cust_name = 'Kids Place';
-```
-
-**`DELETE` 语句中的 `WHERE` 子句**
-
-```sql
-DELETE FROM Customers
-WHERE cust_name = 'Kids Place';
-```
-
-### IN 和 BETWEEN
-
-#### 要点
-
-- `IN` 操作符在 `WHERE` 子句中使用,作用是在指定的几个特定值中任选一个值。
-- `BETWEEN` 操作符在 `WHERE` 子句中使用,作用是选取介于某个范围内的值。
-
-#### 示例
-
-**IN 示例**
-
-```sql
-SELECT *
-FROM products
-WHERE vend_id IN ('DLL01', 'BRS01');
-```
-
-**BETWEEN 示例**
-
-```sql
-SELECT *
-FROM products
-WHERE prod_price BETWEEN 3 AND 5;
-```
-
-### AND、OR、NOT
-
-#### 要点
-
-- `AND`、`OR`、`NOT` 是用于对过滤条件的逻辑处理指令。
-- `AND` 优先级高于 `OR`,为了明确处理顺序,可以使用 `()`。
-- `AND` 操作符表示左右条件都要满足。
-- `OR` 操作符表示左右条件满足任意一个即可。
-- `NOT` 操作符用于否定一个条件。
-
-#### 示例
-
-**AND 示例**
-
-```sql
-SELECT prod_id, prod_name, prod_price
-FROM products
-WHERE vend_id = 'DLL01' AND prod_price <= 4;
-```
-
-**OR 示例**
-
-```sql
-SELECT prod_id, prod_name, prod_price
-FROM products
-WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';
-```
-
-**NOT 示例**
-
-```sql
-SELECT *
-FROM products
-WHERE prod_price NOT BETWEEN 3 AND 5;
-```
-
-### LIKE
-
-#### 要点
-
-- `LIKE` 操作符在 `WHERE` 子句中使用,作用是确定字符串是否匹配模式。
-- 只有字段是文本值时才使用 `LIKE`。
-- `LIKE` 支持两个通配符匹配选项:`%` 和 `_`。
-- 不要滥用通配符,通配符位于开头处匹配会非常慢。
-- `%` 表示任何字符出现任意次数。
-- `_` 表示任何字符出现一次。
-
-#### 示例
-
-**% 示例**
-
-```sql
-SELECT prod_id, prod_name, prod_price
-FROM products
-WHERE prod_name LIKE '%bean bag%';
-```
-
-**\_ 示例**
-
-```sql
-SELECT prod_id, prod_name, prod_price
-FROM products
-WHERE prod_name LIKE '__ inch teddy bear';
-```
-
-## 5. 函数
-
-各个 DBMS 的函数都是不相同的,因此不可移植。
-
-### 文本处理
-
-| 函数 | 说明 |
-| :-------------: | :--------------------: |
-| LEFT() RIGHT() | 左边或者右边的字符 |
-| LOWER() UPPER() | 转换为小写或者大写 |
-| LTRIM() RTIM() | 去除左边或者右边的空格 |
-| LENGTH() | 长度 |
-| SOUNDEX() | 转换为语音值 |
-
-其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。
-
-```sql
-SELECT *
-FROM mytable
-WHERE SOUNDEX(col1) = SOUNDEX('apple')
-```
-
-### 日期和时间处理
-
-- 日期格式:YYYY-MM-DD
-- 时间格式:HH:MM:SS
-
-| 函 数 | 说 明 |
-| :-----------: | :----------------------------: |
-| AddDate() | 增加一个日期(天、周等) |
-| AddTime() | 增加一个时间(时、分等) |
-| CurDate() | 返回当前日期 |
-| CurTime() | 返回当前时间 |
-| Date() | 返回日期时间的日期部分 |
-| DateDiff() | 计算两个日期之差 |
-| Date_Add() | 高度灵活的日期运算函数 |
-| Date_Format() | 返回一个格式化的日期或时间串 |
-| Day() | 返回一个日期的天数部分 |
-| DayOfWeek() | 对于一个日期,返回对应的星期几 |
-| Hour() | 返回一个时间的小时部分 |
-| Minute() | 返回一个时间的分钟部分 |
-| Month() | 返回一个日期的月份部分 |
-| Now() | 返回当前日期和时间 |
-| Second() | 返回一个时间的秒部分 |
-| Time() | 返回一个日期时间的时间部分 |
-| Year() | 返回一个日期的年份部分 |
-
-```sql
-mysql> SELECT NOW();
-```
-
-```
-2018-4-14 20:25:11
-```
-
-### 数值处理
-
-| 函数 | 说明 |
-| :----: | :----: |
-| SIN() | 正弦 |
-| COS() | 余弦 |
-| TAN() | 正切 |
-| ABS() | 绝对值 |
-| SQRT() | 平方根 |
-| MOD() | 余数 |
-| EXP() | 指数 |
-| PI() | 圆周率 |
-| RAND() | 随机数 |
-
-### 汇总
-
-| 函 数 | 说 明 |
-| :-----: | :--------------: |
-| AVG() | 返回某列的平均值 |
-| COUNT() | 返回某列的行数 |
-| MAX() | 返回某列的最大值 |
-| MIN() | 返回某列的最小值 |
-| SUM() | 返回某列值之和 |
-
-AVG() 会忽略 NULL 行。
-
-使用 DISTINCT 可以让汇总函数值汇总不同的值。
-
-```sql
-SELECT AVG(DISTINCT col1) AS avg_col
-FROM mytable
-```
-
-## 6. 排序和分组
-
-### ORDER BY
-
-#### 要点
-
-- `ORDER BY` 用于对结果集进行排序。
- - `ASC` :升序(默认)
- - `DESC` :降序
-- 可以按多个列进行排序,并且为每个列指定不同的排序方式
-
-#### 示例
-
-**指定多个列的排序方向**
-
-```sql
-SELECT * FROM products
-ORDER BY prod_price DESC, prod_name ASC;
-```
-
-### GROUP BY
-
-#### 要点
-
-- `GROUP BY` 子句将记录分组到汇总行中。
-- `GROUP BY` 为每个组返回一个记录。
-- `GROUP BY` 通常还涉及聚合:COUNT,MAX,SUM,AVG 等。
-- `GROUP BY` 可以按一列或多列进行分组。
-- `GROUP BY` 按分组字段进行排序后,`ORDER BY` 可以以汇总字段来进行排序。
-
-#### 示例
-
-**分组**
-
-```sql
-SELECT cust_name, COUNT(cust_address) AS addr_num
-FROM Customers GROUP BY cust_name;
-```
-
-**分组后排序**
-
-```sql
-SELECT cust_name, COUNT(cust_address) AS addr_num
-FROM Customers GROUP BY cust_name
-ORDER BY cust_name DESC;
-```
-
-### HAVING
-
-#### 要点
-
-- `HAVING` 用于对汇总的 `GROUP BY` 结果进行过滤。
-- `HAVING` 要求存在一个 `GROUP BY` 子句。
-- `WHERE` 和 `HAVING` 可以在相同的查询中。
-- `HAVING` vs `WHERE`
- - `WHERE` 和 `HAVING` 都是用于过滤。
- - `HAVING` 适用于汇总的组记录;而 WHERE 适用于单个记录。
-
-#### 示例
-
-**使用 WHERE 和 HAVING 过滤数据**
-
-```sql
-SELECT cust_name, COUNT(*) AS num
-FROM Customers
-WHERE cust_email IS NOT NULL
-GROUP BY cust_name
-HAVING COUNT(*) >= 1;
-```
-
-## 7. 子查询
-
-### 要点
-
-- 子查询是嵌套在较大查询中的 SQL 查询。
-- 子查询也称为**内部查询**或**内部选择**,而包含子查询的语句也称为**外部查询**或**外部选择**。
-- 子查询可以嵌套在 `SELECT`,`INSERT`,`UPDATE` 或 `DELETE` 语句内或另一个子查询中。
-- 子查询通常会在另一个 `SELECT` 语句的 `WHERE` 子句中添加。
-- 您可以使用比较运算符,如 `>`,`<`,或 `=`。比较运算符也可以是多行运算符,如 `IN`,`ANY` 或 `ALL`。
-- 子查询必须被圆括号 `()` 括起来。
-- 内部查询首先在其父查询之前执行,以便可以将内部查询的结果传递给外部查询。执行过程可以参考下图:
-
-
-
-
-#### 示例
-
-**子查询的子查询**
-
-```sql
-SELECT cust_name, cust_contact
-FROM customers
-WHERE cust_id IN (SELECT cust_id
- FROM orders
- WHERE order_num IN (SELECT order_num
- FROM orderitems
- WHERE prod_id = 'RGAN01'));
-```
-
-## 8. 连接和组合
-
-### 连接(JOIN)
-
-#### 要点
-
-- 如果一个 `JOIN` 至少有一个公共字段并且它们之间存在关系,则该 `JOIN` 可以在两个或多个表上工作。
-- 连接用于连接多个表,使用 `JOIN` 关键字,并且条件语句使用 `ON` 而不是 `WHERE`。
-- `JOIN` 保持基表(结构和数据)不变。
-- `JOIN` 有两种连接类型:内连接和外连接。
-- 内连接又称等值连接,使用 INNER `JOIN` 关键字。在没有条件语句的情况下返回笛卡尔积。
- - 自连接可以看成内连接的一种,只是连接的表是自身而已。
-- 自然连接是把同名列通过 = 测试连接起来的,同名列可以有多个。
-- 内连接 vs 自然连接
- - 内连接提供连接的列,而自然连接自动连接所有同名列。
-- 外连接返回一个表中的所有行,并且仅返回来自次表中满足连接条件的那些行,即两个表中的列是相等的。外连接分为左外连接、右外连接、全外连接(Mysql 不支持)。
- - 左外连接就是保留左表没有关联的行。
- - 右外连接就是保留右表没有关联的行。
-- 连接 vs 子查询
- - 连接可以替换子查询,并且比子查询的效率一般会更快。
-
-
-
-
-
-#### 示例
-
-**内连接(INNER JOIN)**
-
-```sql
-SELECT vend_name, prod_name, prod_price
-FROM vendors INNER JOIN products
-ON vendors.vend_id = products.vend_id;
-```
-
-**自连接**
-
-```sql
-SELECT c1.cust_id, c1.cust_name, c1.cust_contact
-FROM customers c1, customers c2
-WHERE c1.cust_name = c2.cust_name
-AND c2.cust_contact = 'Jim Jones';
-```
-
-**自然连接(NATURAL JOIN)**
-
-```sql
-SELECT *
-FROM Products
-NATURAL JOIN Customers;
-```
-
-**左连接(LEFT JOIN)**
-
-```sql
-SELECT customers.cust_id, orders.order_num
-FROM customers LEFT JOIN orders
-ON customers.cust_id = orders.cust_id;
-```
-
-**右连接(RIGHT JOIN)**
-
-```sql
-SELECT customers.cust_id, orders.order_num
-FROM customers RIGHT JOIN orders
-ON customers.cust_id = orders.cust_id;
-```
-
-### 组合(UNION)
-
-#### 要点
-
-- `UNION` 运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自 `UNION` 中参与查询的提取行。
-- `UNION` 基本规则
- - 所有查询的列数和列顺序必须相同。
- - 每个查询中涉及表的列的数据类型必须相同或兼容。
- - 通常返回的列名取自第一个查询。
-- 默认会去除相同行,如果需要保留相同行,使用 `UNION ALL`。
-- 只能包含一个 `ORDER BY` 子句,并且必须位于语句的最后。
-- 应用场景
- - 在一个查询中从不同的表返回结构数据。
- - 对一个表执行多个查询,按一个查询返回数据。
-
-#### 示例
-
-**组合查询**
-
-```sql
-SELECT cust_name, cust_contact, cust_email
-FROM customers
-WHERE cust_state IN ('IL', 'IN', 'MI')
-UNION
-SELECT cust_name, cust_contact, cust_email
-FROM customers
-WHERE cust_name = 'Fun4All';
-```
-
-### JOIN vs UNION
-
-#### 要点
-
-- JOIN vs UNION
- - `JOIN` 中连接表的列可能不同,但在 `UNION` 中,所有查询的列数和列顺序必须相同。
- - `UNION` 将查询之后的行放在一起(垂直放置),但 `JOIN` 将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。
-
-## 9. 数据定义
-
-> DDL 的主要功能是定义数据库对象(如:数据库、数据表、视图、索引等)。
-
-### 数据库(DATABASE)
-
-#### 示例
-
-**创建数据库**
-
-```sql
-CREATE DATABASE test;
-```
-
-**删除数据库**
-
-```sql
-DROP DATABASE test;
-```
-
-**选择数据库**
-
-```sql
-use test;
-```
-
-### 数据表(TABLE)
-
-#### 示例
-
-##### 创建数据表
-
-**普通创建**
-
-```sql
-CREATE TABLE user (
- id int(10) unsigned NOT NULL COMMENT 'Id',
- username varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户名',
- password varchar(64) NOT NULL DEFAULT 'default' COMMENT '密码',
- email varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱'
-) COMMENT='用户表';
-```
-
-**根据已有的表创建新表**
-
-```sql
-CREATE TABLE vip_user AS
-SELECT * FROM user;
-```
-
-##### 撤销数据表
-
-```sql
-DROP TABLE user;
-```
-
-##### 修改数据表
-
-**添加列**
-
-```sql
-ALTER TABLE user
-ADD age int(3);
-```
-
-**删除列**
-
-```sql
-ALTER TABLE user
-DROP COLUMN age;
-```
-
-**修改列**
-
-```sql
-ALTER TABLE `user`
-MODIFY COLUMN age tinyint;
-```
-
-**添加主键**
-
-```sql
-ALTER TABLE user
-ADD PRIMARY KEY (id);
-```
-
-**删除主键**
-
-```sql
-ALTER TABLE user
-DROP PRIMARY KEY;
-```
-
-### 视图(VIEW)
-
-#### 要点
-
-- 定义
- - 视图是基于 SQL 语句的结果集的可视化的表。
- - 视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。对视图的操作和对普通表的操作一样。
-- 作用
- - 简化复杂的 SQL 操作,比如复杂的联结;
- - 只使用实际表的一部分数据;
- - 通过只给用户访问视图的权限,保证数据的安全性;
- - 更改数据格式和表示。
-
-#### 示例
-
-**创建视图**
-
-```sql
-CREATE VIEW top_10_user_view AS
-SELECT id, username
-FROM user
-WHERE id < 10;
-```
-
-**撤销视图**
-
-```sql
-DROP VIEW top_10_user_view;
-```
-
-### 索引(INDEX)
-
-#### 要点
-
-- 作用
- - 通过索引可以更加快速高效地查询数据。
- - 用户无法看到索引,它们只能被用来加速查询。
-- 注意
- - 更新一个包含索引的表需要比更新一个没有索引的表花费更多的时间,这是由于索引本身也需要更新。因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引。
-- 唯一索引
- - 唯一索引表明此索引的每一个索引值只对应唯一的数据记录。
-
-#### 示例
-
-**创建普通索引**
-
-```sql
-CREATE INDEX user_index
-ON user (id);
-```
-
-**撤销索引**
-
-```sql
-ALTER TABLE user
-DROP INDEX user_index;
-```
-
-**创建唯一索引**
-
-```sql
-CREATE UNIQUE INDEX user_index
-ON user (id);
-```
-
-## 10. 约束
-
-### 要点
-
-- SQL 约束用于规定表中的数据规则。
-- 如果存在违反约束的数据行为,行为会被约束终止。
-- 约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。
-- 约束类型
- - `NOT NULL` - 指示某列不能存储 NULL 值。
- - `UNIQUE` - 保证某列的每行必须有唯一的值。
- - `PRIMARY KEY` - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
- - `FOREIGN KEY` - 保证一个表中的数据匹配另一个表中的值的参照完整性。
- - `CHECK` - 保证列中的值符合指定的条件。
- - `DEFAULT` - 规定没有给列赋值时的默认值。
-
-### 示例
-
-**创建表时使用约束条件**
-
-```sql
-CREATE TABLE Users (
- Id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id',
- Username VARCHAR(64) NOT NULL UNIQUE DEFAULT 'default' COMMENT '用户名',
- Password VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '密码',
- Email VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址',
- Enabled TINYINT(4) DEFAULT NULL COMMENT '是否有效',
- PRIMARY KEY (Id)
-) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-```
-
-## 11. 事务处理
-
-### 要点
-
-- 不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。
-- **MySQL 默认是隐式提交**,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 `START TRANSACTION` 语句时,会关闭隐式提交;当 `COMMIT` 或 `ROLLBACK` 语句执行后,事务会自动关闭,重新恢复隐式提交。
-- 通过 `set autocommit=0` 可以取消自动提交,直到 `set autocommit=1` 才会提交;autocommit 标记是针对每个连接而不是针对服务器的。
-- 指令
- - `START TRANSACTION`:指令用于标记事务的起始点。
- - `SAVEPOINT`:指令用于创建保留点。
- - `ROLLBACK TO`:指令用于回滚到指定的保留点;如果没有设置保留点,则回退到 `START TRANSACTION` 语句处。
- - `COMMIT`:提交事务。
-
-### 示例
-
-**事务处理示例**
-
-```sql
--- 开始事务
-START TRANSACTION;
-
--- 插入操作 A
-INSERT INTO `user`
-VALUES (1, 'root1', 'root1', 'xxxx@163.com');
-
--- 创建保留点 updateA
-SAVEPOINT updateA;
-
--- 插入操作 B
-INSERT INTO `user`
-VALUES (2, 'root2', 'root2', 'xxxx@163.com');
-
--- 回滚到保留点 updateA
-ROLLBACK TO updateA;
-
--- 提交事务,只有操作 A 生效
-COMMIT;
-```
-
-## 12. 权限控制
-
-### 要点
-
-- GRANT 和 REVOKE 可在几个层次上控制访问权限:
- - 整个服务器,使用 GRANT ALL 和 REVOKE ALL;
- - 整个数据库,使用 ON database.\*;
- - 特定的表,使用 ON database.table;
- - 特定的列;
- - 特定的存储过程。
-- 新创建的账户没有任何权限。
-- 账户用 username@host 的形式定义,username@% 使用的是默认主机名。
-- MySQL 的账户信息保存在 mysql 这个数据库中。
- ```sql
- USE mysql;
- SELECT user FROM user;
- ```
-
-### 示例
-
-**创建账户**
-
-```sql
-CREATE USER myuser IDENTIFIED BY 'mypassword';
-```
-
-**修改账户名**
-
-```sql
-UPDATE user SET user='newuser' WHERE user='myuser';
-FLUSH PRIVILEGES;
-```
-
-**删除账户**
-
-```sql
-DROP USER myuser;
-```
-
-**查看权限**
-
-```sql
-SHOW GRANTS FOR myuser;
-```
-
-**授予权限**
-
-```sql
-GRANT SELECT, INSERT ON *.* TO myuser;
-```
-
-**删除权限**
-
-```sql
-REVOKE SELECT, INSERT ON *.* FROM myuser;
-```
-
-**更改密码**
-
-```sql
-SET PASSWORD FOR myuser = 'mypass';
-```
-
-## 13. 存储过程
-
-### 要点
-
-- 存储过程可以看成是对一系列 SQL 操作的批处理;
-- 使用存储过程的好处
- - 代码封装,保证了一定的安全性;
- - 代码复用;
- - 由于是预先编译,因此具有很高的性能。
-- 创建存储过程
- - 命令行中创建存储过程需要自定义分隔符,因为命令行是以 `;` 为结束符,而存储过程中也包含了分号,因此会错误把这部分分号当成是结束符,造成语法错误。
- - 包含 in、out 和 inout 三种参数。
- - 给变量赋值都需要用 select into 语句。
- - 每次只能给一个变量赋值,不支持集合的操作。
-
-### 示例
-
-**创建存储过程**
-
-```sql
-DROP PROCEDURE IF EXISTS `proc_adder`;
-DELIMITER ;;
-CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_adder`(IN a int, IN b int, OUT sum int)
-BEGIN
- DECLARE c int;
- if a is null then set a = 0;
- end if;
-
- if b is null then set b = 0;
- end if;
-
- set sum = a + b;
-END
-;;
-DELIMITER ;
-```
-
-**使用存储过程**
-
-```sql
-set @b=5;
-call proc_adder(2,@b,@s);
-select @s as sum;
-```
-
-## 14. 游标
-
-### 要点
-
-- 游标(cursor)是一个存储在 DBMS 服务器上的数据库查询,它不是一条 SELECT 语句,而是被该语句检索出来的结果集。
-- 在存储过程中使用游标可以对一个结果集进行移动遍历。
-- 游标主要用于交互式应用,其中用户需要对数据集中的任意行进行浏览和修改。
-- 使用游标的四个步骤:
- - 声明游标,这个过程没有实际检索出数据;
- - 打开游标;
- - 取出数据;
- - 关闭游标;
-
-### 示例
-
-**游标示例**
-
-```sql
-DELIMITER $
-CREATE PROCEDURE getTotal()
-BEGIN
- DECLARE total INT;
- -- 创建接收游标数据的变量
- DECLARE sid INT;
- DECLARE sname VARCHAR(10);
- -- 创建总数变量
- DECLARE sage INT;
- -- 创建结束标志变量
- DECLARE done INT DEFAULT false;
- -- 创建游标
- DECLARE cur CURSOR FOR SELECT id,name,age from cursor_table where age>30;
- -- 指定游标循环结束时的返回值
- DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
- SET total = 0;
- OPEN cur;
- FETCH cur INTO sid, sname, sage;
- WHILE(NOT done)
- DO
- SET total = total + 1;
- FETCH cur INTO sid, sname, sage;
- END WHILE;
-
- CLOSE cur;
- SELECT total;
-END $
-DELIMITER ;
-
--- 调用存储过程
-call getTotal();
-```
-
-## 15. 触发器
-
-### 示例
-
-#### 创建触发器
-
-> 提示:为了理解触发器的要点,有必要先了解一下创建触发器的指令。
-
-`CREATE TRIGGER` 指令用于创建触发器。
-
-语法:
-
-```sql
-CREATE TRIGGER trigger_name
-trigger_time
-trigger_event
-ON table_name
-FOR EACH ROW
-BEGIN
- trigger_statements
-END;
-```
-
-说明:
-
-- trigger_name:触发器名
-- trigger_time: 触发器的触发时机。取值为 `BEFORE` 或 `AFTER`。
-- trigger_event: 触发器的监听事件。取值为 `INSERT`、`UPDATE` 或 `DELETE`。
-- table_name: 触发器的监听目标。指定在哪张表上建立触发器。
-- FOR EACH ROW: 行级监视,Mysql 固定写法,其他 DBMS 不同。
-- trigger_statements: 触发器执行动作。是一条或多条 SQL 语句的列表,列表内的每条语句都必须用分号 `;` 来结尾。
-
-示例:
-
-```sql
-DELIMITER $
-CREATE TRIGGER `trigger_insert_user`
-AFTER INSERT ON `user`
-FOR EACH ROW
-BEGIN
- INSERT INTO `user_history`(user_id, operate_type, operate_time)
- VALUES (NEW.id, 'add a user', now());
-END $
-DELIMITER ;
-```
-
-#### 查看触发器
-
-```sql
-SHOW TRIGGERS;
-```
-
-#### 删除触发器
-
-```sql
-DROP TRIGGER IF EXISTS trigger_insert_user;
-```
-
-### 要点
-
-- 触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。
-- 可以使用触发器来进行审计跟踪,把修改记录到另外一张表中。
-- MySQL 不允许在触发器中使用 CALL 语句 ,也就是不能调用存储过程。
-- `BEGIN` 和 `END`
- - 当触发器的触发条件满足时,将会执行 BEGIN 和 END 之间的触发器执行动作。
- > 注意:在 MySQL 中,分号 `;` 是语句结束的标识符,遇到分号表示该段语句已经结束,MySQL 可以开始执行了。因此,解释器遇到触发器执行动作中的分号后就开始执行,然后会报错,因为没有找到和 BEGIN 匹配的 END。
- >
- > 这时就会用到 `DELIMITER` 命令(DELIMITER 是定界符,分隔符的意思)。它是一条命令,不需要语句结束标识,语法为:`DELIMITER new_delemiter`。`new_delemiter` 可以设为 1 个或多个长度的符号,默认的是分号 `;`,我们可以把它修改为其他符号,如 `$`:`DELIMITER $` 。在这之后的语句,以分号结束,解释器不会有什么反应,只有遇到了 `$`,才认为是语句结束。注意,使用完之后,我们还应该记得把它给修改回来。
-- `NEW` 和 `OLD`
- - MySQL 中定义了 `NEW` 和 `OLD` 关键字,用来表示触发器的所在表中,触发了触发器的那一行数据。
- - 在 `INSERT` 型触发器中,`NEW` 用来表示将要(`BEFORE`)或已经(`AFTER`)插入的新数据;
- - 在 `UPDATE` 型触发器中,`OLD` 用来表示将要或已经被修改的原数据,`NEW` 用来表示将要或已经修改为的新数据;
- - 在 `DELETE` 型触发器中,`OLD` 用来表示将要或已经被删除的原数据;
- - 使用方法: `NEW.columnName` (columnName 为相应数据表某一列名)
-
-## 16. 知识点小结
-
-
-
-
-
-## 17. 参考资料
-
-- BenForta. SQL 必知必会 [M]. 人民邮电出版社, 2013.
-- [『浅入深出』MySQL 中事务的实现](https://draveness.me/mysql-transaction)
-- [MySQL 的学习--触发器](https://www.cnblogs.com/CraryPrimitiveMan/p/4206942.html)
-- [维基百科词条 - SQL](https://zh.wikipedia.org/wiki/SQL)
-- [https://www.sitesbay.com/sql/index](https://www.sitesbay.com/sql/index)
-- [SQL Subqueries](https://www.w3resource.com/sql/subqueries/understanding-sql-subqueries.php)
-- [Quick breakdown of the types of joins](https://stackoverflow.com/questions/6294778/mysql-quick-breakdown-of-the-types-of-joins)
-- [SQL UNION](https://www.w3resource.com/sql/sql-union.php)
-- [SQL database security](https://www.w3resource.com/sql/database-security/create-users.php)
-- [Mysql 中的存储过程](https://www.cnblogs.com/chenpi/p/5136483.html)
diff --git "a/database/sql/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\345\216\237\347\220\206.md" "b/database/sql/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\345\216\237\347\220\206.md"
deleted file mode 100644
index ac1f35a451..0000000000
--- "a/database/sql/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\345\216\237\347\220\206.md"
+++ /dev/null
@@ -1,885 +0,0 @@
----
-title: 数据库系统概论
-date: 2018/06/25
-categories:
-- database
-tags:
-- database
----
-
-# 数据库系统概论
-
-> 本文所述内容主要针对的是关系型数据库,nosql 数据库并不适用。
-
-
-
-- [1. 索引](#1-索引)
- - [1.1. 索引的优点和缺点](#11-索引的优点和缺点)
- - [1.2. 索引类型](#12-索引类型)
- - [1.3. 索引数据结构](#13-索引数据结构)
- - [1.4. 索引原则](#14-索引原则)
-- [2. 事务](#2-事务)
- - [2.1. ACID](#21-acid)
- - [2.2. 并发一致性问题](#22-并发一致性问题)
- - [2.3. 事务隔离级别](#23-事务隔离级别)
- - [2.4. 死锁](#24-死锁)
-- [3. 并发控制](#3-并发控制)
- - [3.1. 锁粒度](#31-锁粒度)
- - [3.2. 数据库锁的类型](#32-数据库锁的类型)
- - [3.3. 数据库锁的协议](#33-数据库锁的协议)
-- [4. 多版本并发控制](#4-多版本并发控制)
- - [4.1. 版本号](#41-版本号)
- - [4.2. Undo 日志](#42-undo-日志)
- - [4.3. 实现过程](#43-实现过程)
- - [4.4. 快照读与当前读](#44-快照读与当前读)
-- [5. 分库分表](#5-分库分表)
- - [5.1. 水平拆分](#51-水平拆分)
- - [5.2. 垂直拆分](#52-垂直拆分)
- - [5.3. Sharding 策略](#53-sharding-策略)
- - [5.4. 分库分表的问题及解决方案](#54-分库分表的问题及解决方案)
- - [5.5. 常用的分库分表中间件](#55-常用的分库分表中间件)
-- [6. sql 优化](#6-sql-优化)
- - [6.1. 使用执行计划进行分析](#61-使用执行计划进行分析)
- - [6.2. 优化数据访问](#62-优化数据访问)
- - [6.3. 重构查询方式](#63-重构查询方式)
-- [7. 关系数据库设计理论](#7-关系数据库设计理论)
- - [7.1. 函数依赖](#71-函数依赖)
- - [7.2. 异常](#72-异常)
- - [7.3. 范式](#73-范式)
-- [8. ER 图](#8-er-图)
- - [8.1. 实体的三种联系](#81-实体的三种联系)
- - [8.2. 表示出现多次的关系](#82-表示出现多次的关系)
- - [8.3. 联系的多向性](#83-联系的多向性)
- - [8.4. 表示子类](#84-表示子类)
-- [9. 资料](#9-资料)
-
-
-
-## 1. 索引
-
-索引能够轻易将查询性能提升几个数量级。
-
-- 数据量小的表,使用全表扫描比建立索引更高效。
-- 数据量大的表,使用索引更高效。
-- 数据量特大的表,建立和维护索引的代价将会随之增长,可以使用分区技术。
-
-### 1.1. 索引的优点和缺点
-
-优点:
-
-- 索引大大减少了服务器需要扫描的数据量。
-- 索引可以帮助服务器避免排序和临时表。
-- 索引可以将随机 I/O 变为顺序 I/O。
-
-缺点:
-
-- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
-- 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立组合索引那么需要的空间就会更大。
-- 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
-
-### 1.2. 索引类型
-
-主流的关系型数据库一般都支持以下索引类型:
-
-- 普通索引:最基本的索引,没有任何限制。
-- 唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
-- 主键索引:一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。
-- 组合索引:多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
-
-### 1.3. 索引数据结构
-
-**主流数据库的索引一般使用的数据结构为:B-Tree 或 B+Tree。**
-
-#### B-Tree
-
-B-Tree 不同于 Binary Tree(二叉树,最多有两个子树),它是平衡搜索树。
-
-一棵 M 阶的 B-Tree 满足以下条件:
-
-- 每个结点至多有 M 个孩子;
-- 除根结点和叶结点外,其它每个结点至少有 M/2 个孩子;
-- 根结点至少有两个孩子(除非该树仅包含一个结点);
-- 所有叶结点在同一层,叶结点不包含任何关键字信息;
-- 有 K 个关键字的非叶结点恰好包含 K+1 个孩子;
-
-对于任意结点,其内部的关键字 Key 是升序排列的。每个节点中都包含了 data。
-
-
-
-
-
-对于每个结点,主要包含一个关键字数组 Key[],一个指针数组(指向儿子)Son[]。
-
-在 B-Tree 内,查找的流程是:
-
-1. 使用顺序查找(数组长度较短时)或折半查找方法查找 Key[]数组,若找到关键字 K,则返回该结点的地址及 K 在 Key[]中的位置;
-2. 否则,可确定 K 在某个 Key[i]和 Key[i+1]之间,则从 Son[i]所指的子结点继续查找,直到在某结点中查找成功;
-3. 或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。
-
-#### B+Tree
-
-B+Tree 是 B-Tree 的变种:
-
-- 每个节点的指针上限为 2d 而不是 2d+1(d 为节点的出度)。
-- 非叶子节点不存储 data,只存储 key;叶子节点不存储指针。
-
-
-
-
-
-由于并不是所有节点都具有相同的域,因此 B+Tree 中叶节点和内节点一般大小不同。这点与 B-Tree 不同,虽然 B-Tree 中不同节点存放的 key 和指针可能数量不一致,但是每个节点的域和上限是一致的,所以在实现中 B-Tree 往往对每个节点申请同等大小的空间。
-
-##### 带有顺序访问指针的 B+Tree
-
-一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 的基础上进行了优化,增加了顺序访问指针。
-
-
-
-
-
-在 B+Tree 的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的 B+Tree。
-
-这个优化的目的是为了提高区间访问的性能,例如上图中如果要查询 key 为从 18 到 49 的所有数据记录,当找到 18 后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。
-
-#### Hash
-
-Hash 索引只有精确匹配索引所有列的查询才有效。
-
-对于每一行数据,对所有的索引列计算一个 hashcode。哈希索引将所有的 hashcode 存储在索引中,同时在 Hash 表中保存指向每个数据行的指针。
-
-哈希索引的优点:
-
-- 因为索引数据结构紧凑,所以查询速度非常快。
-
-哈希索引的缺点:
-
-- 哈希索引数据不是按照索引值顺序存储的,所以无法用于排序。
-- 哈希索引不支持部分索引匹配查找。如,在数据列 (A,B) 上建立哈希索引,如果查询只有数据列 A,无法使用该索引。
-- 哈希索引只支持等值比较查询,不支持任何范围查询,如 WHERE price > 100。
-- 哈希索引有可能出现哈希冲突,出现哈希冲突时,必须遍历链表中所有的行指针,逐行比较,直到找到符合条件的行。
-
-### 1.4. 索引原则
-
-#### 独立的列
-
-如果查询中的列不是独立的列,则数据库不会使用索引。
-
-“独立的列” 是指索引列不能是表达式的一部分,也不能是函数的参数。
-
-:x: 错误示例:
-
-```sql
-SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
-SELECT ... WHERE TO_DAYS(CURRENT_DAT) - TO_DAYS(date_col) <= 10;
-```
-
-#### 前缀索引和索引选择性
-
-有时候需要索引很长的字符列,这会让索引变得大且慢。
-
-解决方法是:可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率。但这样也会降低索引的选择性。
-
-索引的选择性是指:不重复的索引值和数据表记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。
-
-对于 BLOB/TEXT/VARCHAR 这种文本类型的列,必须使用前缀索引,因为数据库往往不允许索引这些列的完整长度。
-
-要选择足够长的前缀以保证较高的选择性,同时又不能太长(节约空间)。
-
-#### 多列索引
-
-不要为每个列创建独立的索引。
-
-#### 选择合适的索引列顺序
-
-经验法则:将选择性高的列或基数大的列优先排在多列索引最前列。
-
-但有时,也需要考虑 WHERE 子句中的排序、分组和范围条件等因素,这些因素也会对查询性能造成较大影响。
-
-#### 聚簇索引
-
-聚簇索引不是一种单独的索引类型,而是一种数据存储方式。
-
-聚簇表示数据行和相邻的键值紧凑地存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
-
-#### 覆盖索引
-
-索引包含所有需要查询的字段的值。
-
-具有以下优点:
-
-- 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。
-- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
-- 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
-
-#### 使用索引扫描来做排序
-
-索引最好既满足排序,又用于查找行。这样,就可以使用索引来对结果排序。
-
-#### = 和 in 可以乱序
-
-比如 a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql 的查询优化器会帮你优化成索引可以识别的形式。
-
-#### 尽量的扩展索引,不要新建索引
-
-比如表中已经有 a 的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
-
-## 2. 事务
-
-
-
-
-
-事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。
-
-### 2.1. ACID
-
-#### 原子性(Automicity)
-
-事务被视为不可分割的最小单元,事务中的所有操作要么全部提交成功,要么全部失败回滚。
-
-回滚可以用日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
-
-#### 一致性(Consistency)
-
-数据库在事务执行前后都保持一致性状态。
-
-在一致性状态下,所有事务对一个数据的读取结果都是相同的。
-
-#### 隔离性(Isolation)
-
-一个事务所做的修改在最终提交以前,对其它事务是不可见的。
-
-#### 持久性(Durability)
-
-一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
-
-可以通过数据库备份和恢复来实现,在系统发生奔溃时,使用备份的数据库进行数据恢复。
-
-#### ACID 小结
-
-事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系:
-
-- 只有满足一致性,事务的执行结果才是正确的。
-- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时要只要能满足原子性,就一定能满足一致性。
-- 在并发的情况下,多个事务并发执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
-- 事务满足持久化是为了能应对数据库奔溃的情况。
-
-
-
-
-
-### 2.2. 并发一致性问题
-
-在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。
-
-#### 丢失修改
-
-T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。
-
-
-
-
-
-#### 脏数据
-
-T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
-
-
-
-
-
-#### 不可重复读
-
-T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
-
-
-
-
-
-#### 幻影读
-
-T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
-
-
-
-
-
-#### 并发一致性解决方案
-
-产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。
-
-并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。
-
-### 2.3. 事务隔离级别
-
-- 未提交读(READ UNCOMMITTED) - 事务中的修改,即使没有提交,对其它事务也是可见的。
-- 提交读(READ COMMITTED) - 一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
-- 可重复读(REPEATABLE READ) - 保证在同一个事务中多次读取同样数据的结果是一样的。
-- 可串行化(SERIALIXABLE) - 强制事务串行执行。
-
-| 隔离级别 | 脏读 | 不可重复读 | 幻影读 |
-| :------: | :--: | :--------: | :----: |
-| 未提交读 | YES | YES | YES |
-| 提交读 | NO | YES | YES |
-| 可重复读 | NO | NO | YES |
-| 可串行化 | NO | NO | NO |
-
-### 2.4. 死锁
-
-死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定一个资源时,也会产生死锁。
-
-## 3. 并发控制
-
-无论何时,只要有多个查询需要在同一时刻修改数据,就会产生并发控制的问题。
-
-### 3.1. 锁粒度
-
-应该尽量只锁定需要修改的那部分数据,而不是所有的资源。在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。
-
-但是加锁需要消耗资源,锁的各种操作,包括获取锁、释放锁、以及检查锁状态等,都会增加系统开销。因此锁粒度越小,系统开销就越大。
-
-所谓锁策略,就是在锁的开销和并发程度之间寻求平衡,这种平衡自然也会影响到性能。
-
-很多数据库都提供了表级锁和行级锁。
-
-- **表级锁(table lock)** - 锁定整张表。用户对表进行写操作前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他用户才能获得读锁,读锁之间不会相互阻塞。
-- **行级锁(row lock)** - 仅对指定的行记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。
-
-### 3.2. 数据库锁的类型
-
-#### 读写锁
-
-- 排它锁(Exclusive),简写为 X 锁,又称写锁。
-- 共享锁(Shared),简写为 S 锁,又称读锁。
-
-有以下两个规定:
-
-- 一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。
-- 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。
-
-锁的兼容关系如下:
-
-| - | X | S |
-| :-: | :-: | :-: |
-| X | NO | NO |
-| S | NO | YES |
-
-#### 意向锁
-
-使用意向锁(Intention Locks)可以更容易地支持多粒度封锁。
-
-在存在行级锁和表级锁的情况下,事务 T 想要对表 A 加 X 锁,就需要先检测是否有其它事务对表 A 或者表 A 中的任意一行加了锁,那么就需要对表 A 的每一行都检测一次,这是非常耗时的。
-
-意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。有以下两个规定:
-
-- 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁;
-- 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。
-
-通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。
-
-各种锁的兼容关系如下:
-
-| - | X | IX | S | IS |
-| :-: | :-: | :-: | :-: | :-: |
-| X | NO | NO | NO | NO |
-| IX | NO | YES | NO | YES |
-| S | NO | NO | YES | YES |
-| IS | NO | YES | YES | YES |
-
-解释如下:
-
-- 任意 IS/IX 锁之间都是兼容的,因为它们只是表示想要对表加锁,而不是真正加锁;
-- S 锁只与 S 锁和 IS 锁兼容,也就是说事务 T 想要对数据行加 S 锁,其它事务可以已经获得对表或者表中的行的 S 锁。
-
-### 3.3. 数据库锁的协议
-
-#### 三级锁协议
-
-**一级锁协议**
-
-事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。
-
-可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖。
-
-| T1 | T2 |
-| :-----------: | :-----------: |
-| lock-x(A) | |
-| read A=20 | |
-| | lock-x(A) |
-| | wait |
-| write A=19 | . |
-| commit | . |
-| unlock-x(A) | . |
-| | obtain |
-| | read A=19 |
-| | write A=21 |
-| | commit |
-| | unlock-x(A) |
-
-**二级锁协议**
-
-在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。
-
-可以解决读脏数据问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。
-
-| T1 | T2 |
-| :-----------: | :-----------: |
-| lock-x(A) | |
-| read A=20 | |
-| write A=19 | |
-| | lock-s(A) |
-| | wait |
-| rollback | . |
-| A=20 | . |
-| unlock-x(A) | . |
-| | obtain |
-| | read A=20 |
-| | commit |
-| | unlock-s(A) |
-
-**三级锁协议**
-
-在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。
-
-可以解决不可重复读的问题,因为读 A 时,其它事务不能对 A 加 X 锁,从而避免了在读的期间数据发生改变。
-
-| T1 | T2 |
-| :-----------: | :-----------: |
-| lock-s(A) | |
-| read A=20 | |
-| | lock-x(A) |
-| | wait |
-| read A=20 | . |
-| commit | . |
-| unlock-s(A) | . |
-| | obtain |
-| | read A=20 |
-| | write A=19 |
-| | commit |
-| | unlock-X(A) |
-
-#### 两段锁协议
-
-加锁和解锁分为两个阶段进行。
-
-可串行化调度是指:通过并发控制,使得并发执行的事务结果与某个串行执行的事务结果相同。
-
-事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议,它是可串行化调度。
-
-```html
-lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B)
-```
-
-但不是必要条件,例如以下操作不满足两段锁协议,但是它还是可串行化调度。
-
-```html
-lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C)
-```
-
-## 4. 多版本并发控制
-
-多版本并发控制(Multi-Version Concurrency Control, MVCC)是实现隔离级别的一种具体方式。
-
-Mysql、Oracle、PostgreSQL 等数据库都实现了 MVCC,但各自的实现机制不尽相同。
-
-MVCC 用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC;可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。
-
-MVCC 可以视为行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。
-
-MVCC 的实现,是通过保存数据在某个时间的快照来实现的。
-
-### 4.1. 版本号
-
-- 系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。
-- 事务版本号:事务开始时的系统版本号。
-
-MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号:
-
-- 创建版本号:指示创建一个数据行的快照时的系统版本号;
-- 删除版本号:如果该快照的删除版本号大于当前事务版本号表示该快照有效,否则表示该快照已经被删除了。
-
-### 4.2. Undo 日志
-
-MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。
-
-### 4.3. 实现过程
-
-以下实现过程针对可重复读隔离级别。
-
-#### SELECT
-
-当开始新一个事务时,该事务的版本号肯定会大于当前所有数据行快照的创建版本号,理解这一点很关键。
-
-多个事务必须读取到同一个数据行的快照,并且这个快照是距离现在最近的一个有效快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。
-
-把没有对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于 T 的版本号,因为如果大于或者等于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。
-
-除了上面的要求,T 所要读取的数据行快照的删除版本号必须大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。
-
-#### INSERT
-
-将当前系统版本号作为数据行快照的创建版本号。
-
-
-
-
-
-#### DELETE
-
-将当前系统版本号作为数据行快照的删除版本号。
-
-
-
-
-
-#### UPDATE
-
-将当前系统版本号作为更新后的数据行快照的创建版本号,同时将当前系统版本号作为更新前的数据行快照的删除版本号。可以理解为先执行 DELETE 后执行 INSERT。
-
-
-
-
-
-### 4.4. 快照读与当前读
-
-#### 快照读
-
-使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。
-
-```sql
-select * from table ...;
-```
-
-#### 当前读
-
-读取的是最新的数据,需要加锁。以下第一个语句需要加 S 锁,其它都需要加 X 锁。
-
-```sql
-select * from table where ? lock in share mode;
-select * from table where ? for update;
-insert;
-update;
-delete;
-```
-
-## 5. 分库分表
-
-分库分表的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题。
-
-当然,现实中更多是这两种情况混杂在一起,这时候需要根据实际情况做出选择,也可能会综合使用垂直与水平切分,从而将原有数据库切分成类似矩阵一样可以无限扩充的数据库(server)阵列。
-
-### 5.1. 水平拆分
-
-
-
-
-
-对于海量数据的数据库,如果表并不多,但每张表的数据非常多,这时候适合水平切分,即把表的数据按某种规则(比如按 ID 散列)切分到多个数据库(server)上。
-
-水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。
-
-### 5.2. 垂直拆分
-
-
-
-
-
-垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。
-
-如果是因为表多而数据多,这时候适合使用垂直切分,即把关系紧密(比如同一模块)的表切分出来放在一个 server 上。
-
-### 5.3. Sharding 策略
-
-- 哈希取模:hash(key) % NUM_DB
-- 范围:可以是 ID 范围也可以是时间范围
-- 映射表:使用单独的一个数据库来存储映射关系
-
-### 5.4. 分库分表的问题及解决方案
-
-#### 事务问题
-
-方案一:使用分布式事务
-
-- 优点:交由数据库管理,简单有效
-- 缺点:性能代价高,特别是 shard 越来越多时
-
-方案二:由应用程序和数据库共同控制
-
-- 原理:将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务,并通过应用程序来总控各个小事务。
-- 优点:性能上有优势
-- 缺点:需要应用程序在事务控制上做灵活设计。如果使用了 spring 的事务管理,改动起来会面临一定的困难。
-
-#### 跨节点 Join 的问题
-
-只要是进行切分,跨节点 Join 的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的 id,根据这些 id 发起第二次请求得到关联数据。
-
-#### 跨节点的 count,order by,group by 以及聚合函数问题
-
-这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。
-
-解决方案:与解决跨节点 join 问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和 join 不同的是每个节点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。
-
-#### ID 唯一性
-
-一旦数据库被切分到多个物理节点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的 ID 无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得 ID,以便进行 SQL 路由。
-
-一些常见的主键生成策略:
-
-- 使用全局唯一 ID:GUID。
-- 为每个分片指定一个 ID 范围。
-- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)。
-
-#### 数据迁移,容量规划,扩容等问题
-
-来自淘宝综合业务平台团队,它利用对 2 的倍数取余具有向前兼容的特性(如对 4 取余得 1 的数对 2 取余也是 1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了 Sharding 扩容的难度。
-
-#### 分库数量
-
-分库数量首先和单库能处理的记录数有关,一般来说,Mysql 单库超过 5000 万条记录,Oracle 单库超过 1 亿条记录,DB 压力就很大(当然处理能力和字段数量/访问模式/记录长度有进一步关系)。
-
-#### 跨分片的排序分页
-
-- 如果是在前台应用提供分页,则限定用户只能看前面 n 页,这个限制在业务上也是合理的,一般看后面的分页意义不大(如果一定要看,可以要求用户缩小范围重新查询)。
-- 如果是后台批处理任务要求分批获取数据,则可以加大 page size,比如每次获取 5000 条记录,有效减少分页数(当然离线访问一般走备库,避免冲击主库)。
-- 分库设计时,一般还有配套大数据平台汇总所有分库的记录,有些分页查询可以考虑走大数据平台。
-
-### 5.5. 常用的分库分表中间件
-
-#### 简单易用的组件:
-
-- [当当 sharding-jdbc](https://github.com/dangdangdotcom/sharding-jdbc)
-- [蘑菇街 TSharding](https://github.com/baihui212/tsharding)
-
-#### 强悍重量级的中间件:
-
-- [sharding ](https://github.com/go-pg/sharding)
-- [TDDL Smart Client 的方式(淘宝)](https://github.com/alibaba/tb_tddl)
-- [Atlas(Qihoo 360)](https://github.com/Qihoo360/Atlas)
-- [alibaba.cobar(是阿里巴巴(B2B)部门开发)](https://github.com/alibaba/cobar)
-- [MyCAT(基于阿里开源的 Cobar 产品而研发)](http://www.mycat.org.cn/)
-- [Oceanus(58 同城数据库中间件)](https://github.com/58code/Oceanus)
-- [OneProxy(支付宝首席架构师楼方鑫开发)](http://www.cnblogs.com/youge-OneSQL/articles/4208583.html)
-- [vitess(谷歌开发的数据库中间件)](https://github.com/youtube/vitess)
-
-## 6. sql 优化
-
-### 6.1. 使用执行计划进行分析
-
-执行计划 Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explain 结果来优化查询语句。
-
-比较重要的字段有:
-
-- select_type : 查询类型,有简单查询、联合查询、子查询等
-- key : 使用的索引
-- rows : 扫描的行数
-
-更多内容请参考:[MySQL 性能优化神器 Explain 使用分析](https://segmentfault.com/a/1190000008131735)
-
-### 6.2. 优化数据访问
-
-#### 减少请求的数据量
-
-(一)只返回必要的列
-
-最好不要使用 SELECT \* 语句。
-
-(二)只返回必要的行
-
-使用 WHERE 语句进行查询过滤,有时候也需要使用 LIMIT 语句来限制返回的数据。
-
-(三)缓存重复查询的数据
-
-使用缓存可以避免在数据库中进行查询,特别要查询的数据经常被重复查询,缓存可以带来的查询性能提升将会是非常明显的。
-
-#### 减少服务器端扫描的行数
-
-最有效的方式是使用索引来覆盖查询。
-
-### 6.3. 重构查询方式
-
-#### 切分大查询
-
-一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。
-
-```
-DELEFT FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH);
-```
-
-```
-rows_affected = 0
-do {
- rows_affected = do_query(
- "DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
-} while rows_affected > 0
-```
-
-#### 分解大连接查询
-
-将一个大连接查询(JOIN)分解成对每一个表进行一次单表查询,然后将结果在应用程序中进行关联,这样做的好处有:
-
-- 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
-- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
-- 减少锁竞争;
-- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可扩展。
-- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。
-
-```
-SELECT * FROM tag
-JOIN tag_post ON tag_post.tag_id=tag.id
-JOIN post ON tag_post.post_id=post.id
-WHERE tag.tag='mysql';
-SELECT * FROM tag WHERE tag='mysql';
-SELECT * FROM tag_post WHERE tag_id=1234;
-SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
-```
-
-## 7. 关系数据库设计理论
-
-### 7.1. 函数依赖
-
-记 A->B 表示 A 函数决定 B,也可以说 B 函数依赖于 A。
-
-如果 {A1,A2,... ,An} 是关系的一个或多个属性的集合,该集合函数决定了关系的其它所有属性并且是最小的,那么该集合就称为键码。
-
-对于 A->B,如果能找到 A 的真子集 A',使得 A'-> B,那么 A->B 就是部分函数依赖,否则就是完全函数依赖;
-
-对于 A->B,B->C,则 A->C 是一个传递依赖。
-
-### 7.2. 异常
-
-以下的学生课程关系的函数依赖为 Sno, Cname -> Sname, Sdept, Mname, Grade,键码为 {Sno, Cname}。也就是说,确定学生和课程之后,就能确定其它信息。
-
-| Sno | Sname | Sdept | Mname | Cname | Grade |
-| :-: | :----: | :----: | :----: | :----: | :---: |
-| 1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 |
-| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 |
-| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 |
-| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 |
-
-不符合范式的关系,会产生很多异常,主要有以下四种异常:
-
-- 冗余数据:例如 学生-2 出现了两次。
-- 修改异常:修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。
-- 删除异常:删除一个信息,那么也会丢失其它信息。例如如果删除了 课程-1,需要删除第一行和第三行,那么 学生-1 的信息就会丢失。
-- 插入异常,例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。
-
-### 7.3. 范式
-
-范式理论是为了解决以上提到四种异常。
-
-高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。
-
-
-
-
-
-#### 第一范式 (1NF)
-
-属性不可分;
-
-#### 第二范式 (2NF)
-
-每个非主属性完全函数依赖于键码。
-
-可以通过分解来满足。
-
- **分解前**
-
-| Sno | Sname | Sdept | Mname | Cname | Grade |
-| :-: | :----: | :----: | :----: | :----: | :---: |
-| 1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 |
-| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 |
-| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 |
-| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 |
-
-以上学生课程关系中,{Sno, Cname} 为键码,有如下函数依赖:
-
-- Sno -> Sname, Sdept
-- Sdept -> Mname
-- Sno, Cname-> Grade
-
-Grade 完全函数依赖于键码,它没有任何冗余数据,每个学生的每门课都有特定的成绩。
-
-Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。
-
- **分解后**
-
-关系-1
-
-| Sno | Sname | Sdept | Mname |
-| :-: | :----: | :----: | :----: |
-| 1 | 学生-1 | 学院-1 | 院长-1 |
-| 2 | 学生-2 | 学院-2 | 院长-2 |
-| 3 | 学生-3 | 学院-2 | 院长-2 |
-
-有以下函数依赖:
-
-- Sno -> Sname, Sdept, Mname
-- Sdept -> Mname
-
-关系-2
-
-| Sno | Cname | Grade |
-| :-: | :----: | :---: |
-| 1 | 课程-1 | 90 |
-| 2 | 课程-2 | 80 |
-| 2 | 课程-1 | 100 |
-| 3 | 课程-2 | 95 |
-
-有以下函数依赖:
-
-- Sno, Cname -> Grade
-
-#### 第三范式 (3NF)
-
-非主属性不传递依赖于键码。
-
-上面的 关系-1 中存在以下传递依赖:Sno -> Sdept -> Mname,可以进行以下分解:
-
-关系-11
-
-| Sno | Sname | Sdept |
-| :-: | :----: | :----: |
-| 1 | 学生-1 | 学院-1 |
-| 2 | 学生-2 | 学院-2 |
-| 3 | 学生-3 | 学院-2 |
-
-关系-12
-
-| Sdept | Mname |
-| :----: | :----: |
-| 学院-1 | 院长-1 |
-| 学院-2 | 院长-2 |
-
-## 8. ER 图
-
-Entity-Relationship,有三个组成部分:实体、属性、联系。
-
-用来进行关系型数据库系统的概念设计。
-
-### 8.1. 实体的三种联系
-
-包含一对一,一对多,多对多三种。
-
-如果 A 到 B 是一对多关系,那么画个带箭头的线段指向 B;如果是一对一,画两个带箭头的线段;如果是多对多,画两个不带箭头的线段。下图的 Course 和 Student 是一对多的关系。
-
-### 8.2. 表示出现多次的关系
-
-一个实体在联系出现几次,就要用几条线连接。下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。
-
-### 8.3. 联系的多向性
-
-虽然老师可以开设多门课,并且可以教授多名学生,但是对于特定的学生和课程,只有一个老师教授,这就构成了一个三元联系。
-
-一般只使用二元联系,可以把多元关系转换为二元关系。
-
-### 8.4. 表示子类
-
-用一个三角形和两条线来连接类和子类,与子类有关的属性和联系都连到子类上,而与父类和子类都有关的连到父类上。
-
-## 9. 资料
-
-- [数据库系统原理](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/数据库系统原理.md)
-- [分库分表需要考虑的问题及方案](https://www.jianshu.com/p/32b3e91aa22c)
-- [数据库分库分表(sharding)系列(二) 全局主键生成策略](https://blog.csdn.net/bluishglc/article/details/7710738)
-- [一种支持自由规划无须数据迁移和修改路由代码的 Sharding 扩容方案](https://blog.csdn.net/bluishglc/article/details/7970268)
diff --git "a/database/sql/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md" "b/database/sql/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md"
deleted file mode 100644
index f42776b038..0000000000
--- "a/database/sql/\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md"
+++ /dev/null
@@ -1,402 +0,0 @@
-# 数据库面试题
-
-
-
-- [1. 存储引擎](#1-存储引擎)
- - [1.1. mysql 有哪些存储引擎?有什么区别?](#11-mysql-有哪些存储引擎有什么区别)
-- [2. 索引](#2-索引)
- - [2.1. 数据库索引有哪些数据结构?](#21-数据库索引有哪些数据结构)
- - [2.2. B-Tree 和 B+Tree 有什么区别?](#22-b-tree-和-btree-有什么区别)
- - [2.3. 索引原则有哪些?](#23-索引原则有哪些)
-- [3. 事务](#3-事务)
- - [3.1. 数据库事务隔离级别?事务隔离级别分别解决什么问题?](#31-数据库事务隔离级别事务隔离级别分别解决什么问题)
- - [3.2. 如何解决分布式事务?若出现网络问题或宕机问题,如何解决?](#32-如何解决分布式事务若出现网络问题或宕机问题如何解决)
-- [4. 锁](#4-锁)
- - [4.1. 数据库锁有哪些类型?如何实现?](#41-数据库锁有哪些类型如何实现)
-- [5. 分库分表](#5-分库分表)
- - [5.1. 为什么要分库分表?](#51-为什么要分库分表)
- - [5.2. 分库分表的常见问题以及解决方案?](#52-分库分表的常见问题以及解决方案)
- - [5.3. 如何设计可以动态扩容缩容的分库分表方案?](#53-如何设计可以动态扩容缩容的分库分表方案)
- - [5.4. 有哪些分库分表中间件?各自有什么优缺点?底层实现原理?](#54-有哪些分库分表中间件各自有什么优缺点底层实现原理)
-- [6. 数据库优化](#6-数据库优化)
- - [6.1. 什么是执行计划?](#61-什么是执行计划)
-- [7. 数据库架构设计](#7-数据库架构设计)
- - [7.1. 高并发系统数据层面如何设计?](#71-高并发系统数据层面如何设计)
-
-
-
-## 1. 存储引擎
-
-### 1.1. mysql 有哪些存储引擎?有什么区别?
-
-- **InnoDB** - Mysql 的默认事务型存储引擎。性能不错且支持自动崩溃恢复。
-- **MyISAM** - Mysql 5.1 版本前的默认存储引擎。特性丰富但不支持事务,也没有崩溃恢复功能。
-- **CSV** - 可以将 CSV 文件作为 Mysql 的表来处理,但这种表不支持索引。
-- **Memory** - 适合快速访问数据,且数据不会被修改,重启丢失也没有关系。
-- **NDB** - 用于 Mysql 集群场景。
-
-## 2. 索引
-
-### 2.1. 数据库索引有哪些数据结构?
-
-- B-Tree
-- B+Tree
-- Hash
-
-#### 2.1.1. B-Tree
-
-一棵 M 阶的 B-Tree 满足以下条件:
-
-- 每个结点至多有 M 个孩子;
-- 除根结点和叶结点外,其它每个结点至少有 M/2 个孩子;
-- 根结点至少有两个孩子(除非该树仅包含一个结点);
-- 所有叶结点在同一层,叶结点不包含任何关键字信息;
-- 有 K 个关键字的非叶结点恰好包含 K+1 个孩子;
-
-对于任意结点,其内部的关键字 Key 是升序排列的。每个节点中都包含了 data。
-
-
-
-
-
-对于每个结点,主要包含一个关键字数组 Key[],一个指针数组(指向儿子)Son[]。
-
-在 B-Tree 内,查找的流程是:
-
-1. 使用顺序查找(数组长度较短时)或折半查找方法查找 Key[]数组,若找到关键字 K,则返回该结点的地址及 K 在 Key[]中的位置;
-2. 否则,可确定 K 在某个 Key[i]和 Key[i+1]之间,则从 Son[i]所指的子结点继续查找,直到在某结点中查找成功;
-3. 或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。
-
-#### 2.1.2. B+Tree
-
-B+Tree 是 B-Tree 的变种:
-
-- 每个节点的指针上限为 2d 而不是 2d+1(d 为节点的出度)。
-- 非叶子节点不存储 data,只存储 key;叶子节点不存储指针。
-
-
-
-
-
-由于并不是所有节点都具有相同的域,因此 B+Tree 中叶节点和内节点一般大小不同。这点与 B-Tree 不同,虽然 B-Tree 中不同节点存放的 key 和指针可能数量不一致,但是每个节点的域和上限是一致的,所以在实现中 B-Tree 往往对每个节点申请同等大小的空间。
-
-##### 带有顺序访问指针的 B+Tree
-
-一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 的基础上进行了优化,增加了顺序访问指针。
-
-
-
-
-
-在 B+Tree 的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的 B+Tree。
-
-这个优化的目的是为了提高区间访问的性能,例如上图中如果要查询 key 为从 18 到 49 的所有数据记录,当找到 18 后,只需顺着节点和指针顺序遍历就可以一次性访问到所有数据节点,极大提到了区间查询效率。
-
-#### 2.1.3. Hash
-
-Hash 索引只有精确匹配索引所有列的查询才有效。
-
-对于每一行数据,对所有的索引列计算一个 hashcode。哈希索引将所有的 hashcode 存储在索引中,同时在 Hash 表中保存指向每个数据行的指针。
-
-哈希索引的优点:
-
-- 因为索引数据结构紧凑,所以查询速度非常快。
-
-哈希索引的缺点:
-
-- 哈希索引数据不是按照索引值顺序存储的,所以无法用于排序。
-- 哈希索引不支持部分索引匹配查找。如,在数据列 (A,B) 上建立哈希索引,如果查询只有数据列 A,无法使用该索引。
-- 哈希索引只支持等值比较查询,不支持任何范围查询,如 WHERE price > 100。
-- 哈希索引有可能出现哈希冲突,出现哈希冲突时,必须遍历链表中所有的行指针,逐行比较,直到找到符合条件的行。
-
-### 2.2. B-Tree 和 B+Tree 有什么区别?
-
-- B+Tree 更适合外部存储(一般指磁盘存储),由于内节点(非叶子节点)不存储 data,所以一个节点可以存储更多的内节点,每个节点能索引的范围更大更精确。也就是说使用 B+Tree 单次磁盘 IO 的信息量相比较 B-Tree 更大,IO 效率更高。
-- mysql 是关系型数据库,经常会按照区间来访问某个索引列,B+Tree 的叶子节点间按顺序建立了链指针,加强了区间访问性,所以 B+Tree 对索引列上的区间范围查询很友好。而 B-Tree 每个节点的 key 和 data 在一起,无法进行区间查找。
-
-### 2.3. 索引原则有哪些?
-
-#### 2.3.1. 独立的列
-
-如果查询中的列不是独立的列,则数据库不会使用索引。
-
-“独立的列” 是指索引列不能是表达式的一部分,也不能是函数的参数。
-
-:x: 错误示例:
-
-```sql
-SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
-SELECT ... WHERE TO_DAYS(CURRENT_DAT) - TO_DAYS(date_col) <= 10;
-```
-
-#### 2.3.2. 前缀索引和索引选择性
-
-有时候需要索引很长的字符列,这会让索引变得大且慢。
-
-解决方法是:可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率。但这样也会降低索引的选择性。
-
-索引的选择性是指:不重复的索引值和数据表记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。
-
-对于 BLOB/TEXT/VARCHAR 这种文本类型的列,必须使用前缀索引,因为数据库往往不允许索引这些列的完整长度。
-
-要选择足够长的前缀以保证较高的选择性,同时又不能太长(节约空间)。
-
-#### 2.3.3. 多列索引
-
-不要为每个列创建独立的索引。
-
-#### 2.3.4. 选择合适的索引列顺序
-
-经验法则:将选择性高的列或基数大的列优先排在多列索引最前列。
-
-但有时,也需要考虑 WHERE 子句中的排序、分组和范围条件等因素,这些因素也会对查询性能造成较大影响。
-
-#### 2.3.5. 聚簇索引
-
-聚簇索引不是一种单独的索引类型,而是一种数据存储方式。
-
-聚簇表示数据行和相邻的键值紧凑地存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
-
-#### 2.3.6. 覆盖索引
-
-索引包含所有需要查询的字段的值。
-
-具有以下优点:
-
-- 因为索引条目通常远小于数据行的大小,所以若只读取索引,能大大减少数据访问量。
-- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
-- 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
-
-#### 2.3.7. 使用索引扫描来做排序
-
-索引最好既满足排序,又用于查找行。这样,就可以使用索引来对结果排序。
-
-#### 2.3.8. = 和 in 可以乱序
-
-比如 a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql 的查询优化器会帮你优化成索引可以识别的形式。
-
-#### 2.3.9. 尽量的扩展索引,不要新建索引
-
-比如表中已经有 a 的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
-
-## 3. 事务
-
-### 3.1. 数据库事务隔离级别?事务隔离级别分别解决什么问题?
-
-- 未提交读(READ UNCOMMITTED) - 事务中的修改,即使没有提交,对其它事务也是可见的。
-- 提交读(READ COMMITTED) - 一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
-- 可重复读(REPEATABLE READ) - 保证在同一个事务中多次读取同样数据的结果是一样的。
-- 可串行化(SERIALIXABLE) - 强制事务串行执行。
-
-| 隔离级别 | 脏读 | 不可重复读 | 幻影读 |
-| :------: | :--: | :--------: | :----: |
-| 未提交读 | YES | YES | YES |
-| 提交读 | NO | YES | YES |
-| 可重复读 | NO | NO | YES |
-| 可串行化 | NO | NO | NO |
-
-### 3.2. 如何解决分布式事务?若出现网络问题或宕机问题,如何解决?
-
-## 4. 锁
-
-### 4.1. 数据库锁有哪些类型?如何实现?
-
-#### 4.1.1. 锁粒度
-
-- **表级锁(table lock)** - 锁定整张表。用户对表进行写操作前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他用户才能获得读锁,读锁之间不会相互阻塞。
-- **行级锁(row lock)** - 仅对指定的行记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。
-
-InnoDB 行锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB 才使用行级锁;否则,InnoDB 将使用表锁!
-
-索引分为主键索引和非主键索引两种,如果一条 sql 语句操作了主键索引,MySQL 就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL 会先锁定该非主键索引,再锁定相关的主键索引。在 UPDATE、DELETE 操作时,MySQL 不仅锁定 WHERE 条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的 next-key locking。
-
-当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。发生死锁后,InnoDB 一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。
-
-#### 4.1.2. 读写锁
-
-- 排它锁(Exclusive),简写为 X 锁,又称写锁。
-- 共享锁(Shared),简写为 S 锁,又称读锁。
-
-有以下两个规定:
-
-- 一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。
-- 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。
-
-锁的兼容关系如下:
-
-| - | X | S |
-| :-: | :-: | :-: |
-| X | NO | NO |
-| S | NO | YES |
-
-使用:
-
-- 排他锁:`SELECT ... FOR UPDATE;`
-- 共享锁:`SELECT ... LOCK IN SHARE MODE;`
-
-innodb 下的记录锁(也叫行锁),间隙锁,next-key 锁统统属于排他锁。
-
-在 InnoDB 中,行锁是通过给索引上的索引项加锁来实现的。如果没有索引,InnoDB 将会通过隐藏的聚簇索引来对记录加锁。另外,根据针对 sql 语句检索条件的不同,加锁又有以下三种情形需要我们掌握。
-
-1. Record lock:对索引项加锁。若没有索引项则使用表锁。
-2. Gap lock:对索引项之间的间隙加锁。
-3. Next-key lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。当利用范围条件而不是相等条件获取排他锁时,innoDB 会给符合条件的所有数据加锁。对于在条件范围内但是不存在的记录,叫做间隙。innoDB 也会对这个间隙进行加锁。另外,使用相等的检索条件时,若指定了本身不存在的记录作为检索条件的值的话,则此值对应的索引项也会加锁。
-
-#### 4.1.3. 意向锁
-
-使用意向锁(Intention Locks)可以更容易地支持多粒度封锁。
-
-在存在行级锁和表级锁的情况下,事务 T 想要对表 A 加 X 锁,就需要先检测是否有其它事务对表 A 或者表 A 中的任意一行加了锁,那么就需要对表 A 的每一行都检测一次,这是非常耗时的。
-
-意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。有以下两个规定:
-
-- 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁;
-- 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。
-
-通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。
-
-各种锁的兼容关系如下:
-
-| - | X | IX | S | IS |
-| :-: | :-: | :-: | :-: | :-: |
-| X | NO | NO | NO | NO |
-| IX | NO | YES | NO | YES |
-| S | NO | NO | YES | YES |
-| IS | NO | YES | YES | YES |
-
-解释如下:
-
-- 任意 IS/IX 锁之间都是兼容的,因为它们只是表示想要对表加锁,而不是真正加锁;
-- S 锁只与 S 锁和 IS 锁兼容,也就是说事务 T 想要对数据行加 S 锁,其它事务可以已经获得对表或者表中的行的 S 锁。
-
-意向锁是 InnoDB 自动加的,不需要用户干预。
-
-## 5. 分库分表
-
-### 5.1. 为什么要分库分表?
-
-分库分表的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题。
-
-### 5.2. 分库分表的常见问题以及解决方案?
-
-#### 5.2.1. 事务问题
-
-方案一:使用分布式事务
-
-- 优点:交由数据库管理,简单有效
-- 缺点:性能代价高,特别是 shard 越来越多时
-
-方案二:由应用程序和数据库共同控制
-
-- 原理:将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务,并通过应用程序来总控各个小事务。
-- 优点:性能上有优势
-- 缺点:需要应用程序在事务控制上做灵活设计。如果使用了 spring 的事务管理,改动起来会面临一定的困难。
-
-#### 5.2.2. 跨节点 Join 的问题
-
-只要是进行切分,跨节点 Join 的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的 id,根据这些 id 发起第二次请求得到关联数据。
-
-#### 5.2.3. 跨节点的 count,order by,group by 以及聚合函数问题
-
-这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。
-
-解决方案:与解决跨节点 join 问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和 join 不同的是每个节点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。
-
-#### 5.2.4. ID 唯一性
-
-一旦数据库被切分到多个物理节点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的 ID 无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得 ID,以便进行 SQL 路由。
-
-一些常见的主键生成策略:
-
-- 使用全局唯一 ID:GUID。
-- 为每个分片指定一个 ID 范围。
-- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)。
-
-#### 5.2.5. 数据迁移,容量规划,扩容等问题
-
-来自淘宝综合业务平台团队,它利用对 2 的倍数取余具有向前兼容的特性(如对 4 取余得 1 的数对 2 取余也是 1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了 Sharding 扩容的难度。
-
-#### 5.2.6. 分库数量
-
-分库数量首先和单库能处理的记录数有关,一般来说,Mysql 单库超过 5000 万条记录,Oracle 单库超过 1 亿条记录,DB 压力就很大(当然处理能力和字段数量/访问模式/记录长度有进一步关系)。
-
-#### 5.2.7. 跨分片的排序分页
-
-- 如果是在前台应用提供分页,则限定用户只能看前面 n 页,这个限制在业务上也是合理的,一般看后面的分页意义不大(如果一定要看,可以要求用户缩小范围重新查询)。
-- 如果是后台批处理任务要求分批获取数据,则可以加大 page size,比如每次获取 5000 条记录,有效减少分页数(当然离线访问一般走备库,避免冲击主库)。
-- 分库设计时,一般还有配套大数据平台汇总所有分库的记录,有些分页查询可以考虑走大数据平台。
-
-### 5.3. 如何设计可以动态扩容缩容的分库分表方案?
-
-### 5.4. 有哪些分库分表中间件?各自有什么优缺点?底层实现原理?
-
-#### 简单易用的组件:
-
-- [当当 sharding-jdbc](https://github.com/dangdangdotcom/sharding-jdbc)
-- [蘑菇街 TSharding](https://github.com/baihui212/tsharding)
-
-#### 强悍重量级的中间件:
-
-- [sharding ](https://github.com/go-pg/sharding)
-- [TDDL Smart Client 的方式(淘宝)](https://github.com/alibaba/tb_tddl)
-- [Atlas(Qihoo 360)](https://github.com/Qihoo360/Atlas)
-- [alibaba.cobar(是阿里巴巴(B2B)部门开发)](https://github.com/alibaba/cobar)
-- [MyCAT(基于阿里开源的 Cobar 产品而研发)](http://www.mycat.org.cn/)
-- [Oceanus(58 同城数据库中间件)](https://github.com/58code/Oceanus)
-- [OneProxy(支付宝首席架构师楼方鑫开发)](http://www.cnblogs.com/youge-OneSQL/articles/4208583.html)
-- [vitess(谷歌开发的数据库中间件)](https://github.com/youtube/vitess)
-
-## 6. 数据库优化
-
-### 6.1. 什么是执行计划?
-
-## 7. 数据库架构设计
-
-### 7.1. 高并发系统数据层面如何设计?
-
-#### 读写分离的原理
-
-主服务器用来处理写操作以及实时性要求比较高的读操作,而从服务器用来处理读操作。
-
-读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。
-
-MySQL 读写分离能提高性能的原因在于:
-
-- 主从服务器负责各自的读和写,极大程度缓解了锁的争用;
-- 从服务器可以配置 MyISAM 引擎,提升查询性能以及节约系统开销;
-- 增加冗余,提高可用性。
-
-
-
-
-
-##### Mysql 的复制原理
-
-Mysql 支持两种复制:基于行的复制和基于语句的复制。
-
-这两种方式都是在主库上记录二进制日志,然后在从库重放日志的方式来实现异步的数据复制。这意味着:复制过程存在时延,这段时间内,主从数据可能不一致。
-
-主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。
-
-- **binlog 线程** :负责将主服务器上的数据更改写入二进制文件(binlog)中。
-- **I/O 线程** :负责从主服务器上读取二进制日志文件,并写入从服务器的中继日志中。
-- **SQL 线程** :负责读取中继日志并重放其中的 SQL 语句。
-
-
-
-
-
-#### 垂直切分
-
-按照业务线或功能模块拆分为不同数据库。
-
-更进一步是服务化改造,将强耦合的系统拆分为多个服务。
-
-#### 水平切分
-
-- 哈希取模:hash(key) % NUM_DB
-- 范围:可以是 ID 范围也可以是时间范围
-- 映射表:使用单独的一个数据库来存储映射关系
-
-----
-
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000000..279e115691
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env sh
+
+# ------------------------------------------------------------------------------
+# gh-pages 部署脚本
+# @author Dunwu
+# @since 2020/2/10
+# ------------------------------------------------------------------------------
+
+# 装载其它库
+ROOT_DIR=$(
+ cd $(dirname $0)
+ pwd
+)
+
+# 确保脚本抛出遇到的错误
+set -e
+
+# 生成静态文件
+npm install
+npm run build
+
+# 进入生成的文件夹
+cd ${ROOT_DIR}/.temp
+
+# 如果是发布到自定义域名
+# echo 'www.example.com' > CNAME
+
+if [[ ${GITHUB_TOKEN} && ${GITEE_TOKEN} ]]; then
+ msg='自动部署'
+ GITHUB_URL=https://dunwu:${GITHUB_TOKEN}@github.com/dunwu/blog.git
+ GITEE_URL=https://turnon:${GITEE_TOKEN}@gitee.com/turnon/blog.git
+ git config --global user.name "dunwu"
+ git config --global user.email "forbreak@163.com"
+else
+ msg='手动部署'
+ GITHUB_URL=git@github.com:dunwu/blog.git
+ GITEE_URL=git@gitee.com:turnon/blog.git
+fi
+git init
+git add -A
+git commit -m "${msg}"
+# 推送到github gh-pages分支
+git push -f "${GITHUB_URL}" master:gh-pages
+#由于仓库容量超出gitee限制,停止push
+#git push -f "${GITEE_URL}" master:gh-pages
+
+rm -rf ${ROOT_DIR}/.temp
diff --git a/design/MicroService/kong.md b/design/MicroService/kong.md
deleted file mode 100644
index cf7085dc9a..0000000000
--- a/design/MicroService/kong.md
+++ /dev/null
@@ -1,175 +0,0 @@
-# Kong
-
-> [Kong](https://github.com/Kong/kong) 是一个云原生、快速、可扩展和分布式的微服务抽象层(也称为 API 网关,API 中间件)。
->
-> 关键词:`nginx`,`api网关`,`微服务`
-
-
-
-- [简介](#简介)
- - [为什么选择 Kong](#为什么选择-kong)
-- [Quickstart](#quickstart)
- - [安装配置](#安装配置)
- - [使用 Kong](#使用-kong)
- - [配置服务](#配置服务)
-- [参考资料](#参考资料)
-
-
-
-## 简介
-
-### 为什么选择 Kong
-
-
-
-## Quickstart
-
-### 安装配置
-
-> 本文仅以 Centos7 为例。
-
-Kong 支持在多种环境下安装。
-
-官方安装说明:https://konghq.com/install/
-
-
-
-以下为 Centos7 安装步骤:
-
-(1)下载 rpm 安装包到本地
-
-(2)安装 Kong
-
-```
-$ sudo yum install epel-release
-$ sudo yum install kong-community-edition-0.14.1.*.noarch.rpm --nogpgcheck
-```
-
-(3)准备数据库
-
-Kong 需要存储数据,支持两种数据库:[PostgreSQL 9.5+](http://www.postgresql.org/) 和 [Cassandra 3.x.x](http://cassandra.apache.org/)
-
-本人选择了 PostgreSQL,安装方法可以参考 —— [PostgreSQL 安装](https://github.com/dunwu/database/blob/master/docs/postgresql.md#安装)
-
-安装 PostgreSQL 后,配置一个数据库和数据库用户:
-
-```sql
-CREATE USER kong;
-CREATE DATABASE kong OWNER kong;
-```
-
-(4)执行 Kong 迁移
-
-执行以下命令:
-
-```
-$ kong migrations up [-c /path/to/kong.conf]
-```
-
-注意:永远不应同时运行迁移;一个 Kong 节点应该只执行一次迁移。
-
-(5)启动 Kong
-
-```
-$ kong start [-c /path/to/kong.conf]
-```
-
-(6)测试启动成功
-
-```
-$ curl -i http://localhost:8001/
-```
-
-至此,安装配置完成。
-
-### 使用 Kong
-
-- 启动(必须确保执行过 `kong migrations up`) - `kong start [-c /path/to/kong.conf]`
- - `-c /path/to/kong.conf` 参数用来指定用户的配置
-- 停止 - `kong stop`
-- 重启 - `kong reload`
-
-### 配置服务
-
-(1)添加第一个服务
-
-```sh
-$ curl -i -X POST \
- --url http://localhost:8001/services/ \
- --data 'name=example-service' \
- --data 'url=http://mockbin.org'
-```
-
-应答类似下面形式:
-
-```http
-HTTP/1.1 201 Created
-Content-Type: application/json
-Connection: keep-alive
-
-{
- "host":"mockbin.org",
- "created_at":1519130509,
- "connect_timeout":60000,
- "id":"92956672-f5ea-4e9a-b096-667bf55bc40c",
- "protocol":"http",
- "name":"example-service",
- "read_timeout":60000,
- "port":80,
- "path":null,
- "updated_at":1519130509,
- "retries":5,
- "write_timeout":60000
-}
-```
-
-(2)为服务添加路由
-
-```sh
-$ curl -i -X POST \
- --url http://localhost:8001/services/example-service/routes \
- --data 'hosts[]=example.com'
-```
-
-应答类似下面形式:
-
-```http
-HTTP/1.1 201 Created
-Content-Type: application/json
-Connection: keep-alive
-
-{
- "created_at":1519131139,
- "strip_path":true,
- "hosts":[
- "example.com"
- ],
- "preserve_host":false,
- "regex_priority":0,
- "updated_at":1519131139,
- "paths":null,
- "service":{
- "id":"79d7ee6e-9fc7-4b95-aa3b-61d2e17e7516"
- },
- "methods":null,
- "protocols":[
- "http",
- "https"
- ],
- "id":"f9ce2ed7-c06e-4e16-bd5d-3a82daef3f9d"
-}
-```
-
-此时,Kong 已经关注这个服务,并准备代理请求。
-
-(3)通过 Kong 转发请求
-
-```sh
-$ curl -i -X GET \
- --url http://localhost:8000/ \
- --header 'Host: example.com'
-```
-
-## 参考资料
-
-https://www.itcodemonkey.com/article/5980.html
diff --git a/design/README.md b/design/README.md
deleted file mode 100644
index e5a39e9411..0000000000
--- a/design/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# 系统设计
-
-## 资源
-
-https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md
diff --git "a/design/design-patterns/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/design/design-patterns/\350\256\276\350\256\241\346\250\241\345\274\217.md"
deleted file mode 100644
index 765cf83ce7..0000000000
--- "a/design/design-patterns/\350\256\276\350\256\241\346\250\241\345\274\217.md"
+++ /dev/null
@@ -1,62 +0,0 @@
-# 设计模式
-
-> 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
->
-> 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
-
-## 创建型模式
-
-> 这些设计模式都是关于类实例化的。这种模式可以进一步分为类创建模式和对象创建模式。虽然类创建模式在实例化过程中有效地使用继承,但是对象创建模式有效地使用委托来完成工作。
-
-- 简单工厂模式 (Simple Factory Pattern)
-- 单例模式 (Singleton Pattern)
-- 工厂方法模式 (Factory Method Pattern)
-- 抽象工厂模式 (Abstract Factory Pattern)
-- 建造者模式 (Builder Pattern)
-- 原型模式 (Prototype Pattern)
-
-## 结构型模式
-
-> 这些设计模式都是关于类和对象的组合。结构类创建模式使用继承来组成接口。结构对象模式定义了组合对象以获得新功能的方法。
-
-- 适配器模式 (Adapter Pattern)
-- 桥接模式 (Bridge Pattern)
-- 装饰者模式 (Decorator Pattern)
-- 组合模式 (Composite Pattern)
-- 外观模式 (Facade Pattern)
-- 享元模式 (Flyweight Pattern)
-- 代理模式 (Proxy Pattern)
-
-## 行为型模式
-
-> 这些设计模式都是关于 Class 的对象通信。行为模式是那些最特别关注对象之间通信的模式。
-
-- 模板方法模式 (Template Method Pattern)
-- 命令模式 (Command Pattern)
-- 迭代器模式 (Iterator Pattern)
-- 观察者模式 (Observer Pattern)
-- 解释器模式 (Interpreter Pattern)
-- 中介者模式 (Mediator Pattern)
-- 职责链模式 (Chain of Responsibility Pattern)
-- 备忘录模式 (Memento Pattern)
-- 策略模式 (Strategy Pattern)
-- 访问者模式 (Visitor Pattern)
-- 状态模式 (State Pattern)
-
-## 资源
-
-### 经典书籍
-
-- [O'Reilly:Head First 设计模式(中文版)](https://item.jd.com/10100236.html)
-- [大话设计模式](https://item.jd.com/10079261.html)
-- [图解设计模式](https://item.jd.com/12019365.html)
-
-### 教程
-
-- https://sourcemaking.com/design_patterns
-- https://github.com/iluwatar/java-design-patterns
-- https://github.com/kamranahmedse/design-patterns-for-humans
-
-### 更多资源
-
-- https://github.com/DovAmir/awesome-design-patterns
diff --git a/design/refactor/README.md b/design/refactor/README.md
deleted file mode 100644
index 291d97041d..0000000000
--- a/design/refactor/README.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# 重构教程
-
-## 前言
-
-- [对于重构的思考](think-in-refactor.md)
-
-## 代码臃肿(Bloated)
-
-> **这组坏味道意味着:代码中的类、函数、字段没有经过合理的组织,只是简单的堆砌起来。这一类型的问题通常在代码的初期并不明显,但是随着代码规模的增长而逐渐积累(特别是当没有人努力去根除它们时)。**
-
-- [过长函数](chapter01/过长函数.md)
-- [过大的类](chapter01/过大的类.md)
-- [基本类型偏执](chapter01/基本类型偏执.md)
-- [过长参数列](chapter01/过长参数列.md)
-- [数据泥团](chapter01/数据泥团.md)
-
-## 滥用面向对象(Object-Orientation Abusers)
-
-> **这组坏味道意味着:代码部分或完全地违背了面向对象编程原则。**
-
-- [switch 声明](chapter02/switch声明.md)
-- [临时字段](chapter02/临时字段.md)
-- [被拒绝的馈赠](chapter02/被拒绝的馈赠.md)
-- [异曲同工的类](chapter02/异曲同工的类.md)
-
-## 变革的障碍(Change Preventers)
-
-> **这组坏味道意味着:当你需要改变一处代码时,却发现不得不改变其他的地方。这使得程序开发变得复杂、代价高昂。**
-
-- [发散式变化](chapter03/发散式变化.md)
-- [霰弹式修改](chapter03/霰弹式修改.md)
-- [平行继承体系](chapter03/平行继承体系.md)
-
-## 非必要的(Dispensables)
-
-> **这组坏味道意味着:这样的代码可有可无,它的存在反而影响整体代码的整洁和可读性。**
-
-- [过多的注释](chapter04/过多的注释.md)
-- [重复代码](chapter04/重复代码.md)
-- [冗余类](chapter04/冗余类.md)
-- [纯稚的数据类](chapter04/纯稚的数据类.md)
-- [夸夸其谈未来性](chapter04/夸夸其谈未来性.md)
-
-## 耦合(Couplers)
-
-> **这组坏味道意味着:不同类之间过度耦合。**
-
-- [依恋情结](chapter05/依恋情结.md)
-- [狎昵关系](chapter05/狎昵关系.md)
-- [过度耦合的消息链](chapter05/过度耦合的消息链.md)
-- [中间人](chapter05/中间人.md)
-- [不完美的库类](chapter05/不完美的库类.md)
diff --git "a/design/refactor/chapter01/\345\237\272\346\234\254\347\261\273\345\236\213\345\201\217\346\211\247.md" "b/design/refactor/chapter01/\345\237\272\346\234\254\347\261\273\345\236\213\345\201\217\346\211\247.md"
deleted file mode 100644
index 7ea645dc0a..0000000000
--- "a/design/refactor/chapter01/\345\237\272\346\234\254\347\261\273\345\236\213\345\201\217\346\211\247.md"
+++ /dev/null
@@ -1,136 +0,0 @@
-## 坏味道——基本类型偏执(Primitive Obsession)
-
-### 特征
-
-> - 使用基本类型而不是小对象来实现简单任务(例如货币、范围、电话号码字符串等)。
-> - 使用常量编码信息(例如一个用于引用管理员权限的常量`USER_ADMIN_ROLE = 1` )。
-> - 使用字符串常量作为字段名在数组中使用。
->
-
-
-
-### 问题原因
-
-类似其他大部分坏味道,基本类型偏执诞生于类初建的时候。一开始,可能只是不多的字段,随着表示的特性越来越多,基本数据类型字段也越来越多。
-
-基本类型常常被用于表示模型的类型。你有一组数字或字符串用来表示某个实体。
-
-还有一个场景:在模拟场景,大量的字符串常量被用于数组的索引。
-
-### 解决方法
-
-
-
-大多数编程语言都支持基本数据类型和结构类型(类、结构体等)。结构类型允许程序员将基本数据类型组织起来,以代表某一事物的模型。
-
-基本数据类型可以看成是机构类型的积木块。当基本数据类型数量成规模后,将它们有组织地结合起来,可以更方便的管理这些数据。
-
-- 如果你有大量的基本数据类型字段,就有可能将其中部分存在逻辑联系的字段组织起来,形成一个类。更进一步的是,将与这些数据有关联的方法也一并移入类中。为了实现这个目标,可以尝试 `以类取代类型码(Replace Type Code with Class)` 。
-- 如果基本数据类型字段的值是用于方法的参数,可以使用 `引入参数对象(Introduce Parameter Object)` 或 `保持对象完整(Preserve Whole Object)` 。
-- 如果想要替换的数据值是类型码,而它并不影响行为,则可以运用 `以类取代类型码(Replace Type Code with Class)` 将它替换掉。如果你有与类型码相关的条件表达式,可运用 `以子类取代类型码(Replace Type Code with Subclass)` 或 `以状态/策略模式取代类型码(Replace Type Code with State/Strategy)` 加以处理。
-- 如果你发现自己正从数组中挑选数据,可运用 `以对象取代数组(Replace Array with Object)` 。
-
-### 收益
-
-- 多亏了使用对象替代基本数据类型,使得代码变得更加灵活。
-- 代码变得更加易读和更加有组织。特殊数据可以集中进行操作,而不像之前那样分散。不用再猜测这些陌生的常量的意义以及它们为什么在数组中。
-- 更容易发现重复代码。
-
-
-
-## 重构方法说明
-### 以类取代类型码(Replace Type Code with Class)
-
-**问题**
-
-类之中有一个数值类型码,但它并不影响类的行为。
-
-
-
-**解决**
-
-以一个新的类替换该数值类型码。
-
-
-
-### 引入参数对象(Introduce Parameter Object)
-
-**问题**
-
-某些参数总是很自然地同时出现。
-
-
-
-**解决**
-
-以一个对象来取代这些参数。
-
-
-### 保持对象完整(Preserve Whole Object)
-**问题**
-
-你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。
-
-```java
-int low = daysTempRange.getLow();
-int high = daysTempRange.getHigh();
-boolean withinPlan = plan.withinRange(low, high);
-```
-
-**解决**
-
-改为传递整个对象。
-
-```java
-boolean withinPlan = plan.withinRange(daysTempRange);
-```
-
-### 以子类取代类型码(Replace Type Code with Subclass)
-
-**问题**
-
-你有一个不可变的类型码,它会影响类的行为。
-
-
-
-**解决**
-
-以子类取代这个类型码。
-
-
-
-### 以状态/策略模式取代类型码(Replace Type Code with State/Strategy)
-
-**问题**
-
-你有一个类型码,它会影响类的行为,但你无法通过继承消除它。
-
-
-
-**解决**
-
-以状态对象取代类型码。
-
-
-
-### 以对象取代数组(Replace Array with Object)
-
-**问题**
-
-你有一个数组,其中的元素各自代表不同的东西。
-
-```java
-String[] row = new String[3];
-row[0] = "Liverpool";
-row[1] = "15";
-```
-
-**解决**
-
-以对象替换数组。对于数组中的每个元素,以一个字段来表示。
-
-```java
-Performance row = new Performance();
-row.setName("Liverpool");
-row.setWins("15");
-```
diff --git "a/design/refactor/chapter01/\346\225\260\346\215\256\346\263\245\345\233\242.md" "b/design/refactor/chapter01/\346\225\260\346\215\256\346\263\245\345\233\242.md"
deleted file mode 100644
index 78917117ee..0000000000
--- "a/design/refactor/chapter01/\346\225\260\346\215\256\346\263\245\345\233\242.md"
+++ /dev/null
@@ -1,80 +0,0 @@
-## 坏味道——数据泥团(Data Clumps)
-
-### 特征
-
-> 有时,代码的不同部分包含相同的变量组(例如用于连接到数据库的参数)。这些绑在一起出现的数据应该拥有自己的对象。
-
-
-
-### 问题原因
-
-通常,数据泥团的出现时因为糟糕的编程结构或“复制-粘贴式编程”。
-
-有一个判断是否是数据泥团的好办法:删掉众多数据中的一项。这么做,其他数据有没有因而失去意义?如果它们不再有意义,这就是个明确的信号:你应该为它们产生一个新的对象。
-
-### 解决方法
-
-- 首先找出这些数据以字段形式出现的地方,运用 `提炼类(Extract Class)` 将它们提炼到一个独立对象中。
-- 如果数据泥团在函数的参数列中出现,运用 `引入参数对象(Introduce Parameter Object)` 将它们组织成一个类。
-- 如果数据泥团的部分数据出现在其他函数中,考虑运用 `保持对象完整(Preserve Whole Object)` 将整个数据对象传入到函数中。
-- 检视一下使用这些字段的代码,也许,将它们移入一个数据类是个不错的主意。
-
-### 收益
-
-- 提高代码易读性和组织性。对于特殊数据的操作,可以集中进行处理,而不像以前那样分散。
-- 减少代码量。
-
-
-
-### 何时忽略
-
-- 有时为了对象中的部分数据而将整个对象作为参数传递给函数,可能会产生让两个类之间不收欢迎的依赖关系,这中情况下可以不传递整个对象。
-
-## 重构方法说明
-
-### 提炼类(Extract Class)
-
-**问题**
-
-某个类做了不止一件事。
-
-
-
-**解决**
-
-建立一个新类,将相关的字段和函数从旧类搬移到新类。
-
-
-
-### 引入参数对象(Introduce Parameter Object)
-
-**问题**
-
-某些参数总是很自然地同时出现。
-
-
-
-**解决**
-
-以一个对象来取代这些参数。
-
-
-
-### 保持对象完整(Preserve Whole Object)
-**问题**
-
-你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。
-
-```java
-int low = daysTempRange.getLow();
-int high = daysTempRange.getHigh();
-boolean withinPlan = plan.withinRange(low, high);
-```
-
-**解决**
-
-改为传递整个对象。
-
-```java
-boolean withinPlan = plan.withinRange(daysTempRange);
-```
diff --git "a/design/refactor/chapter01/\350\277\207\345\244\247\347\232\204\347\261\273.md" "b/design/refactor/chapter01/\350\277\207\345\244\247\347\232\204\347\261\273.md"
deleted file mode 100644
index d31b2eaa07..0000000000
--- "a/design/refactor/chapter01/\350\277\207\345\244\247\347\232\204\347\261\273.md"
+++ /dev/null
@@ -1,89 +0,0 @@
-## 坏味道——过大的类(Large Class)
-
-### 特征
-
-> 一个类含有过多字段、函数、代码行。
-
-
-
-### 问题原因
-
-类通常一开始很小,但是随着程序的增长而逐渐膨胀。
-
-类似于过长函数,程序员通常觉得在一个现存类中添加新特性比创建一个新的类要容易。
-
-### 解决方法
-
-设计模式中有一条重要原则:职责单一原则。一个类应该只赋予它一个职责。如果它所承担的职责太多,就该考虑为它减减负。
-
-
-
-- 如果过大类中的部分行为可以提炼到一个独立的组件中,可以使用 `提炼类(Extract Class)`。
-- 如果过大类中的部分行为可以用不同方式实现或使用于特殊场景,可以使用 `提炼子类(Extract Subclass)`。
-- 如果有必要为客户端提供一组操作和行为,可以使用 `提炼接口(Extract Interface)`。
-- 如果你的过大类是个GUI类,可能需要把数据和行为移到一个独立的领域对象去。你可能需要两边各保留一些重复数据,并保持两边同步。 `复制被监视数据(Duplicate Observed Data)` 可以告诉你怎么做。
-
-### 收益
-
-- 重构过大的类可以使程序员不必记住一个类中大量的属性。
-- 在大多数情况下,分割过大的类可以避免代码和功能的重复。
-
-
-
-## 重构方法说明
-
-### 提炼类(Extract Class)
-
-**问题**
-
-某个类做了不止一件事。
-
-
-
-**解决**
-
-建立一个新类,将相关的字段和函数从旧类搬移到新类。
-
-
-
-### 提炼子类(Extract Subclass)
-
-**问题**
-
-一个类中有些特性仅用于特定场景。
-
-
-
-**解决**
-
-创建一个子类,并将用于特殊场景的特性置入其中。
-
-
-
-### 提炼接口(Extract Interface)
-
-**问题**
-
-多个客户端使用一个类部分相同的函数。另一个场景是两个类中的部分函数相同。
-
-
-
-**解决**
-
-移动相同的部分函数到接口中。
-
-
-
-### 复制被监视数据(Duplicate Observed Data)
-
-**问题**
-
-如果存储在类中的数据是负责GUI的。
-
-
-
-**解决**
-
-一个比较好的方法是将负责GUI的数据放入一个独立的类,以确保GUI数据与域类之间的连接和同步。
-
-
diff --git "a/design/refactor/chapter01/\350\277\207\351\225\277\345\207\275\346\225\260.md" "b/design/refactor/chapter01/\350\277\207\351\225\277\345\207\275\346\225\260.md"
deleted file mode 100644
index a97e88b707..0000000000
--- "a/design/refactor/chapter01/\350\277\207\351\225\277\345\207\275\346\225\260.md"
+++ /dev/null
@@ -1,212 +0,0 @@
-## 坏味道——过长函数(Long Method)
-
-### 特征
-
-> 一个函数含有太多行代码。一般来说,任何函数超过 10 行时,你就可以考虑是不是过长了。
-> 函数中的代码行数原则上不要超过 100 行。
-
-
-
-### 问题的原因
-
-通常情况下,创建一个新函数的难度要大于添加功能到一个已存在的函数。大部分人都觉得:“我就添加这么两行代码,为此新建一个函数实在是小题大做了。”于是,张三加两行,李四加两行,王五加两行。。。函数日益庞大,最终烂的像一锅浆糊,再也没人能完全看懂了。于是大家就更不敢轻易动这个函数了,只能恶性循环的往其中添加代码。所以,如果你看到一个超过200行的函数,通常都是多个程序员东拼西凑出来的。
-
-### 解决函数
-
-一个很好的技巧是:**寻找注释**。添加注释,一般有这么几个原因:代码逻辑较为晦涩或复杂;这段代码功能相对独立;特殊处理。
-如果代码前方有一行注释,就是在提醒你:可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。如果函数有一个描述恰当的名字,就不需要去看内部代码究竟是如何实现的。就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数中。
-
-
-
-- 为了给一个函数瘦身,可以使用 `提炼函数(Extract Method)`。
-- 如果局部变量和参数干扰提炼函数,可以使用 `以查询取代临时变量(Replace Temp with Query)`,`引入参数对象(Introduce Parameter Object)` 或 `保持对象完整(Preserve Whole Object)` 。
-- 如果前面两条没有帮助,可以通过 `以函数对象取代函数(Replace Method with Method Object)` 尝试移动整个函数到一个独立的对象中。
-- 条件表达式和循环常常也是提炼的信号。对于条件表达式,可以使用 `分解条件表达式(Decompose Conditional)` 。至于循环,应该使用 `提炼函数(Extract Method)` 将循环和其内的代码提炼到独立函数中。
-
-### 收益
-
-- 在所有类型的面向对象代码中,函数比较短小精悍的类往往生命周期较长。一个函数越长,就越不容易理解和维护。
-- 此外,过长函数中往往含有难以发现的重复代码。
-
-
-
-### 性能
-
-是否像许多人说的那样,增加函数的数量会影响性能?在几乎绝大多数情况下,这种影响是可以忽略不计,所以不用担心。
-此外,现在有了清晰和易读的代码,在需要的时候,你将更容易找到真正有效的函数来重组代码和提高性能。
-
-## 重构方法说明
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
-
-### 以查询取代临时变量(Replace Temp with Query)
-
-**问题**
-
-将表达式的结果放在局部变量中,然后在代码中使用。
-
-```java
-double calculateTotal() {
- double basePrice = quantity * itemPrice;
- if (basePrice > 1000) {
- return basePrice * 0.95;
- }
- else {
- return basePrice * 0.98;
- }
-}
-```
-
-**解决**
-
-将整个表达式移动到一个独立的函数中并返回结果。使用查询函数来替代使用变量。如果需要,可以在其他函数中合并新函数。
-
-```java
-double calculateTotal() {
- double basePrice = quantity * itemPrice;
- if (basePrice > 1000) {
- return basePrice * 0.95;
- }
- else {
- return basePrice * 0.98;
- }
-}
-```
-
-### 引入参数对象(Introduce Parameter Object)
-
-**问题**
-
-某些参数总是很自然地同时出现。
-
-
-
-**解决**
-
-以一个对象来取代这些参数。
-
-
-
-### 保持对象完整(Preserve Whole Object)
-**问题**
-
-你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。
-
-```java
-int low = daysTempRange.getLow();
-int high = daysTempRange.getHigh();
-boolean withinPlan = plan.withinRange(low, high);
-```
-
-**解决**
-
-改为传递整个对象。
-
-```java
-boolean withinPlan = plan.withinRange(daysTempRange);
-```
-
-### 以函数对象取代函数(Replace Method with Method Object)
-
-**问题**
-
-你有一个过长函数,它的局部变量交织在一起,以致于你无法应用提炼函数(Extract Method) 。
-
-```java
-class Order {
- //...
- public double price() {
- double primaryBasePrice;
- double secondaryBasePrice;
- double tertiaryBasePrice;
- // long computation.
- //...
- }
-}
-```
-
-**解决**
-
-将函数移到一个独立的类中,使得局部变量成了这个类的字段。然后,你可以将函数分割成这个类中的多个函数。
-
-```java
-class Order {
- //...
- public double price() {
- return new PriceCalculator(this).compute();
- }
-}
-
-class PriceCalculator {
- private double primaryBasePrice;
- private double secondaryBasePrice;
- private double tertiaryBasePrice;
-
- public PriceCalculator(Order order) {
- // copy relevant information from order object.
- //...
- }
-
- public double compute() {
- // long computation.
- //...
- }
-}
-```
-
-### 分解条件表达式(Decompose Conditional)
-
-**问题**
-
-你有复杂的条件表达式。
-
-```java
-if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
- charge = quantity * winterRate + winterServiceCharge;
-}
-else {
- charge = quantity * summerRate;
-}
-```
-
-**解决**
-
-根据条件分支将整个条件表达式分解成几个函数。
-
-```java
-if (notSummer(date)) {
- charge = winterCharge(quantity);
-}
-else {
- charge = summerCharge(quantity);
-}
-```
diff --git "a/design/refactor/chapter01/\350\277\207\351\225\277\345\217\202\346\225\260\345\210\227.md" "b/design/refactor/chapter01/\350\277\207\351\225\277\345\217\202\346\225\260\345\210\227.md"
deleted file mode 100644
index 4ceedb1015..0000000000
--- "a/design/refactor/chapter01/\350\277\207\351\225\277\345\217\202\346\225\260\345\210\227.md"
+++ /dev/null
@@ -1,88 +0,0 @@
-## 坏味道——过长参数列(Long Parameter List)
-
-### 特征
-
-> 一个函数有超过3、4个入参。
-
-
-
-### 问题原因
-
-过长参数列可能是将多个算法并到一个函数中时发生的。函数中的入参可以用来控制最终选用哪个算法去执行。
-
-过长参数列也可能是解耦类之间依赖关系时的副产品。例如,用于创建函数中所需的特定对象的代码已从函数移动到调用函数的代码处,但创建的对象是作为参数传递到函数中。因此,原始类不再知道对象之间的关系,并且依赖性也已经减少。但是如果创建的这些对象,每一个都将需要它自己的参数,这意味着过长参数列。
-
-太长的参数列难以理解,太多参数会造成前后不一致、不易使用,而且一旦需要更多数据,就不得不修改它。
-
-### 解决方案
-
-
-
-- 如果向已有的对象发出一条请求就可以取代一个参数,那么你应该使用 `以函数取代参数(Replace Parameter with Methods)` 。在这里,,“已有的对象”可能是函数所属类里的一个字段,也可能是另一个参数。
-- 你还可以运用 `保持对象完整(Preserve Whole Object)` 将来自同一对象的一堆数据收集起来,并以该对象替换它们。
-- 如果某些数据缺乏合理的对象归属,可使用 `引入参数对象(Introduce Parameter Object)` 为它们制造出一个“参数对象”。
-
-### 收益
-
-- 更易读,更简短的代码。
-- 重构可能会暴露出之前未注意到的重复代码。
-
-### 何时忽略
-
-- 这里有一个重要的例外:有时候你明显不想造成"被调用对象"与"较大对象"间的某种依赖关系。这时候将数据从对象中拆解出来单独作为参数,也很合情理。但是请注意其所引发的代价。如果参数列太长或变化太频繁,就需要重新考虑自己的依赖结构了。
-
-## 重构方法说明
-
-### 以函数取代参数(Replace Parameter with Methods)
-**问题**
-
-对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。
-
-```java
-int basePrice = quantity * itemPrice;
-double seasonDiscount = this.getSeasonalDiscount();
-double fees = this.getFees();
-double finalPrice = discountedPrice(basePrice, seasonDiscount, fees);
-```
-
-**解决**
-
-让参数接受者去除该项参数,并直接调用前一个函数。
-
-```java
-int basePrice = quantity * itemPrice;
-double finalPrice = discountedPrice(basePrice);
-```
-
-### 保持对象完整(Preserve Whole Object)
-**问题**
-
-你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。
-
-```java
-int low = daysTempRange.getLow();
-int high = daysTempRange.getHigh();
-boolean withinPlan = plan.withinRange(low, high);
-```
-
-**解决**
-
-改为传递整个对象。
-
-```java
-boolean withinPlan = plan.withinRange(daysTempRange);
-```
-
-### 引入参数对象(Introduce Parameter Object)
-
-**问题**
-
-某些参数总是很自然地同时出现。
-
-
-
-**解决**
-
-以一个对象来取代这些参数。
-
-
diff --git "a/design/refactor/chapter02/switch\345\243\260\346\230\216.md" "b/design/refactor/chapter02/switch\345\243\260\346\230\216.md"
deleted file mode 100644
index dc000d6e89..0000000000
--- "a/design/refactor/chapter02/switch\345\243\260\346\230\216.md"
+++ /dev/null
@@ -1,227 +0,0 @@
-## 坏味道——Switch声明(Switch Statements)
-
-### 特征
-
-> 你有一个复杂的 `switch` 语句或 `if` 序列语句。
-
-
-
-### 问题原因
-
-面向对象程序的一个最明显特征就是:少用 `switch` 和 `case` 语句。从本质上说,`switch` 语句的问题在于重复(`if` 序列也同样如此)。你常会发现 `switch` 语句散布于不同地点。如果要为它添加一个新的 `case` 子句,就必须找到所有 `switch` 语句并修改它们。面向对象中的多态概念可为此带来优雅的解决办法。
-
-大多数时候,一看到 `switch` 语句,就应该考虑以多态来替换它。
-
-### 解决方法
-
-- 问题是多态该出现在哪?switch语句常常根据类型码进行选择,你要的是“与该类型码相关的函数或类”,所以应该运用 `提炼函数(Extract Method)` 将 `switch` 语句提炼到一个独立函数中,再以 `搬移函数(Move Method)` 将它搬移到需要多态性的那个类里。
-- 如果你的 `switch` 是基于类型码来识别分支,这时可以运用 `以子类取代类型码(Replace Type Code with Subclass)` 或 `以状态/策略模式取代类型码(Replace Type Code with State/Strategy)` 。
-- 一旦完成这样的继承结构后,就可以运用 `以多态取代条件表达式(Replace Conditional with Polymorphism)` 了。
-- 如果条件分支并不多并且它们使用不同参数调用相同的函数,多态就没必要了。在这种情况下,你可以运用 `以明确函数取代参数(Replace Parameter with Explicit Methods)` 。
-- 如果你的选择条件之一是null,可以运用 `引入 Null 对象(Introduce Null Object)` 。
-
-### 收益
-
-- 提升代码组织性。
-
-
-
-### 何时忽略
-
-- 如果一个 `switch` 操作只是执行简单的行为,就没有重构的必要了。
-- `switch` 常被工厂设计模式族(`工厂方法模式(Factory Method)`和`抽象工厂模式(Abstract Factory) `)所使用,这种情况下也没必要重构。
-
-## 重构方法说明
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 以子类取代类型码(Replace Type Code with Subclass)
-
-**问题**
-
-你有一个不可变的类型码,它会影响类的行为。
-
-
-
-**解决**
-
-以子类取代这个类型码。
-
-
-
-### 以状态/策略模式取代类型码(Replace Type Code with State/Strategy)
-
-**问题**
-
-你有一个类型码,它会影响类的行为,但你无法通过继承消除它。
-
-
-
-**解决**
-
-以状态对象取代类型码。
-
-
-
-### 以多态取代条件表达式(Replace Conditional with Polymorphism)
-
-**问题**
-
-你手上有个条件表达式,它根据对象类型的不同而选择不同的行为。
-
-```java
-class Bird {
- //...
- double getSpeed() {
- switch (type) {
- case EUROPEAN:
- return getBaseSpeed();
- case AFRICAN:
- return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
- case NORWEGIAN_BLUE:
- return (isNailed) ? 0 : getBaseSpeed(voltage);
- }
- throw new RuntimeException("Should be unreachable");
- }
-}
-```
-
-**解决**
-
-将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。
-
-```java
-abstract class Bird {
- //...
- abstract double getSpeed();
-}
-
-class European extends Bird {
- double getSpeed() {
- return getBaseSpeed();
- }
-}
-class African extends Bird {
- double getSpeed() {
- return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
- }
-}
-class NorwegianBlue extends Bird {
- double getSpeed() {
- return (isNailed) ? 0 : getBaseSpeed(voltage);
- }
-}
-
-// Somewhere in client code
-speed = bird.getSpeed();
-```
-
-### 以明确函数取代参数(Replace Parameter with Explicit Methods)
-
-**问题**
-
-你有一个函数,其中完全取决于参数值而采取不同的行为。
-
-```java
-void setValue(String name, int value) {
- if (name.equals("height")) {
- height = value;
- return;
- }
- if (name.equals("width")) {
- width = value;
- return;
- }
- Assert.shouldNeverReachHere();
-}
-```
-
-**解决**
-
-针对该参数的每一个可能值,建立一个独立函数。
-
-```java
-void setHeight(int arg) {
- height = arg;
-}
-void setWidth(int arg) {
- width = arg;
-}
-```
-
-### 引入 Null 对象(Introduce Null Object)
-
-**问题**
-
-你需要再三检查某对象是否为null。
-
-```java
-if (customer == null) {
- plan = BillingPlan.basic();
-}
-else {
- plan = customer.getPlan();
-}
-```
-
-**解决**
-
-将null值替换为null对象。
-
-```java
-class NullCustomer extends Customer {
- Plan getPlan() {
- return new NullPlan();
- }
- // Some other NULL functionality.
-}
-
-// Replace null values with Null-object.
-customer = (order.customer != null) ? order.customer : new NullCustomer();
-
-// Use Null-object as if it's normal subclass.
-plan = customer.getPlan();
-```
diff --git "a/design/refactor/chapter02/\344\270\264\346\227\266\345\255\227\346\256\265.md" "b/design/refactor/chapter02/\344\270\264\346\227\266\345\255\227\346\256\265.md"
deleted file mode 100644
index e1a9f57fe3..0000000000
--- "a/design/refactor/chapter02/\344\270\264\346\227\266\345\255\227\346\256\265.md"
+++ /dev/null
@@ -1,124 +0,0 @@
-## 坏味道——临时字段(Temporary Field)
-
-### 特征
-
-> 临时字段的值只在特定环境下有意义,离开这个环境,它们就什么也不是了。
-
-
-
-### 问题原因
-
-有时你会看到这样的对象:其内某个实例变量仅为某种特定情况而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初设置目的,会让你发疯。
-通常,临时字段是在某一算法需要大量输入时而创建。因此,为了避免函数有过多参数,程序员决定在类中创建这些数据的临时字段。这些临时字段仅仅在算法中使用,其他时候却毫无用处。
-这种代码不好理解。你期望查看对象字段的数据,但是出于某种原因,它们总是为空。
-
-### 解决方法
-
-- 可以通过 `提炼类(Extract Class)` 将临时字段和操作它们的所有代码提炼到一个单独的类中。此外,你可以运用 `以函数对象取代函数(Replace Method with Method Object)` 来实现同样的目的。
-- `引入 Null 对象(Introduce Null Object)` 在“变量不合法”的情况下创建一个null对象,从而避免写出条件表达式。
-
-
-
-### 收益
-
-- 更好的代码清晰度和组织性。
-
-
-
-## 重构方法说明
-
-### 提炼类(Extract Class)
-
-**问题**
-
-某个类做了不止一件事。
-
-
-
-**解决**
-
-建立一个新类,将相关的字段和函数从旧类搬移到新类。
-
-
-
-### 以函数对象取代函数(Replace Method with Method Object)
-
-**问题**
-
-你有一个过长函数,它的局部变量交织在一起,以致于你无法应用提炼函数(Extract Method) 。
-
-```java
-class Order {
- //...
- public double price() {
- double primaryBasePrice;
- double secondaryBasePrice;
- double tertiaryBasePrice;
- // long computation.
- //...
- }
-}
-```
-
-**解决**
-
-将函数移到一个独立的类中,使得局部变量成了这个类的字段。然后,你可以将函数分割成这个类中的多个函数。
-
-```java
-class Order {
- //...
- public double price() {
- return new PriceCalculator(this).compute();
- }
-}
-
-class PriceCalculator {
- private double primaryBasePrice;
- private double secondaryBasePrice;
- private double tertiaryBasePrice;
-
- public PriceCalculator(Order order) {
- // copy relevant information from order object.
- //...
- }
-
- public double compute() {
- // long computation.
- //...
- }
-}
-```
-
-### 引入 Null 对象(Introduce Null Object)
-
-**问题**
-
-你需要再三检查某对象是否为null。
-
-```java
-if (customer == null) {
- plan = BillingPlan.basic();
-}
-else {
- plan = customer.getPlan();
-}
-```
-
-**解决**
-
-将null值替换为null对象。
-
-```java
-class NullCustomer extends Customer {
- Plan getPlan() {
- return new NullPlan();
- }
- // Some other NULL functionality.
-}
-
-// Replace null values with Null-object.
-customer = (order.customer != null) ? order.customer : new NullCustomer();
-
-// Use Null-object as if it's normal subclass.
-plan = customer.getPlan();
-```
diff --git "a/design/refactor/chapter02/\345\274\202\346\233\262\345\220\214\345\267\245\347\232\204\347\261\273.md" "b/design/refactor/chapter02/\345\274\202\346\233\262\345\220\214\345\267\245\347\232\204\347\261\273.md"
deleted file mode 100644
index 4c4aabfbec..0000000000
--- "a/design/refactor/chapter02/\345\274\202\346\233\262\345\220\214\345\267\245\347\232\204\347\261\273.md"
+++ /dev/null
@@ -1,112 +0,0 @@
-## 坏味道——异曲同工的类(Alternative Classes with Different Interfaces)
-
-### 特征
-
-两个类中有着不同的函数,却在做着同一件事。
-
-
-
-### 问题原因
-
-这种情况往往是因为:创建这个类的程序员并不知道已经有实现这个功能的类存在了。
-
-### 解决方法
-
-- 如果两个函数做同一件事,却有着不同的签名,请运用 `函数改名(Rename Method)` 根据它们的用途重新命名。
-- 运用 `搬移函数(Move Method)` 、 `添加参数(Add Parameter)` 和 `令函数携带参数(Parameterize Method)` 来使得方法的名称和实现一致。
-- 如果两个类仅有部分功能是重复的,尝试运用 `提炼超类(Extract Superclass)` 。这种情况下,已存在的类就成了超类。
-- 当最终选择并运用某种方法来重构后,也许你就能删除其中一个类了。
-
-### 收益
-
-- 消除了不必要的重复代码,为代码瘦身了。
-- 代码更易读(不再需要猜测为什么要有两个功能相同的类)。
-
-
-
-### 何时忽略
-
-- 有时合并类是不可能的,或者是如此困难以至于没有意义。例如:两个功能相似的类存在于不同的lib库中。
-
-## 重构方法说明
-### 函数改名(Rename Method)
-
-**问题**
-
-函数的名称未能恰当的揭示函数的用途。
-
-```java
-class Person {
- public String getsnm();
-}
-```
-
-**解决**
-
-修改函数名。
-
-```java
-class Person {
- public String getSecondName();
-}
-```
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 添加参数(Add Parameter)
-
-**问题**
-某个函数需要从调用端得到更多信息。
-
-```java
-class Customer {
- public Contact getContact();
-}
-```
-
-**解决**
-为此函数添加一个对象函数,让改对象带进函数所需信息。
-
-```
-class Customer {
- public Contact getContact(Date date);
-}
-```
-
-### 令函数携带参数(Parameterize Method)
-**问题**
-
-若干函数做了类似的工作,但在函数本体中却包含了不同的值。
-
-
-**解决**
-
-建立单一函数,以参数表达哪些不同的值。
-
-
-
-### 提炼超类(Extract Superclass)
-
-**问题**
-
-两个类有相似特性。
-
-
-
-**解决**
-
-为这两个类建立一个超类,将相同特性移至超类。
-
-
diff --git "a/design/refactor/chapter02/\350\242\253\346\213\222\347\273\235\347\232\204\351\246\210\350\265\240.md" "b/design/refactor/chapter02/\350\242\253\346\213\222\347\273\235\347\232\204\351\246\210\350\265\240.md"
deleted file mode 100644
index 037ac3d5dd..0000000000
--- "a/design/refactor/chapter02/\350\242\253\346\213\222\347\273\235\347\232\204\351\246\210\350\265\240.md"
+++ /dev/null
@@ -1,56 +0,0 @@
-## 坏味道——被拒绝的馈赠(Refused Bequest)
-
-### 特征
-
-子类仅仅使用父类中的部分方法和属性。其他来自父类的馈赠成为了累赘。
-
-
-
-### 问题原因
-
-有些人仅仅是想重用超类中的部分代码而创建了子类。但实际上超类和子类完全不同。
-
-### 解决方法
-
-- 如果继承没有意义并且子类和父类之间确实没有共同点,可以运用 `以委托取代继承(Replace Inheritance with Delegation)` 消除继承。
-- 如果继承是适当的,则去除子类中不需要的字段和方法。运用 `提炼超类(Extract Superclass)` 将所有超类中对于子类有用的字段和函数提取出来,置入一个新的超类中,然后让两个类都继承自它。
-
-
-
-### 收益
-
-- 提高代码的清晰度和组织性。
-
-
-
-## 重构方法说明
-
-### 以委托取代继承(Replace Inheritance with Delegation)
-
-**问题**
-
-某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。
-
-
-
-**解决**
-
-1. 在子类中新建一个字段用以保存超类;
-2. 调整子类函数,令它改而委托超类;
-3. 然后去掉两者之间的继承关系。
-
-
-
-### 提炼超类(Extract Superclass)
-
-**问题**
-
-两个类有相似特性。
-
-
-
-**解决**
-
-为这两个类建立一个超类,将相同特性移至超类。
-
-
diff --git "a/design/refactor/chapter03/\345\217\221\346\225\243\345\274\217\345\217\230\345\214\226.md" "b/design/refactor/chapter03/\345\217\221\346\225\243\345\274\217\345\217\230\345\214\226.md"
deleted file mode 100644
index 9ff3dd9d64..0000000000
--- "a/design/refactor/chapter03/\345\217\221\346\225\243\345\274\217\345\217\230\345\214\226.md"
+++ /dev/null
@@ -1,38 +0,0 @@
-## 坏味道——发散式变化(Divergent Change)
-
-> `发散式变化(Divergent Change)` 类似于 `霰弹式修改(Shotgun Surgery)` ,但实际上完全不同。`发散式变化(Divergent Change)` 是指一个类受多种变化的影响。`霰弹式修改(Shotgun Surgery)` 是指多种变化引发多个类相应的修改。
-
-### 特征
-
-你发现你想要修改一个函数,却必须要同时修改许多不相关的函数。例如,当你想要添加一个新的产品类型时,你需要同步修改对产品进行查找、显示、排序的函数。
-
-
-
-### 问题原因
-
-通常,这种发散式修改是由于编程结构不合理或者“复制-粘贴式编程”。
-
-### 解决办法
-
-- 运用 `提炼类(Extract Class)` 拆分类的行为。
-
-### 收益
-
-- 提高代码组织结构
-- 减少重复代码
-
-## 重构方法说明
-
-### 提炼类(Extract Class)
-
-**问题**
-
-某个类做了不止一件事。
-
-
-
-**解决**
-
-建立一个新类,将相关的字段和函数从旧类搬移到新类。
-
-
diff --git "a/design/refactor/chapter03/\345\271\263\350\241\214\347\273\247\346\211\277\344\275\223\347\263\273.md" "b/design/refactor/chapter03/\345\271\263\350\241\214\347\273\247\346\211\277\344\275\223\347\263\273.md"
deleted file mode 100644
index caff14e1b1..0000000000
--- "a/design/refactor/chapter03/\345\271\263\350\241\214\347\273\247\346\211\277\344\275\223\347\263\273.md"
+++ /dev/null
@@ -1,56 +0,0 @@
-## 坏味道——平行继承体系(Parallel Inheritance Hierarchies)
-
-> `平行继承体系(Parallel Inheritance Hierarchies)` 其实是 `霰弹式修改(Shotgun Surgery)` 的特殊情况。
-
-### 特征
-
-每当你为某个类添加一个子类,必须同时为另一个类相应添加一个子类。这种情况的典型特征是:某个继承体系的类名前缀或类名后缀完全相同。
-
-
-
-### 问题原因
-
-起初的继承体系很小,随着不断添加新类,继承体系越来越大,也越来越难修改。
-
-### 解决方法
-
-- 一般策略是:让一个继承体系的实例引用另一个继承体系的实例。如果再接再厉运用 `搬移函数(Move Method)` 和 `搬移字段(Move Field)`,就可以消除引用端的继承体系。
-
-### 收益
-
-- 更好的代码组织
-- 减少重复代码
-
-### 何时忽略
-
-- 有时具有并行类层次结构只是一种为了避免程序体系结构更混乱的方法。如果你发现尝试消除平行继承体系导致代码更加丑陋,那么你应该回滚你的修改。
-
-## 重构方法说明
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 搬移字段(Move Field)
-
-**问题**
-
-在你的程序中,某个字段被其所驻类之外的另一个类更多地用到。
-
-
-
-**解决**
-
-在目标类新建一个字段,修改源字段的所有用户,令他们改用新字段。
-
-
diff --git "a/design/refactor/chapter03/\351\234\260\345\274\271\345\274\217\344\277\256\346\224\271.md" "b/design/refactor/chapter03/\351\234\260\345\274\271\345\274\217\344\277\256\346\224\271.md"
deleted file mode 100644
index a1c046948b..0000000000
--- "a/design/refactor/chapter03/\351\234\260\345\274\271\345\274\217\344\277\256\346\224\271.md"
+++ /dev/null
@@ -1,72 +0,0 @@
-## 坏味道——霰弹式修改(Shotgun Surgery)
-
-> `霰弹式修改(Shotgun Surgery)` 类似于 `发散式变化(Divergent Change)` ,但实际上完全不同。`发散式变化(Divergent Change)` 是指一个类受多种变化的影响。`霰弹式修改(Shotgun Surgery)` 是指多种变化引发多个类相应的修改。
-
-### 特征
-
-任何修改都需要在许多不同类上做小幅度修改。
-
-
-
-### 问题原因
-
-一个单一的职责被拆分成大量的类。
-
-### 解决方法
-
-- 运用`搬移函数(Move Method)` 和 `搬移字段(Move Field)` 来搬移不同类中相同的行为到一个独立类中。如果没有适合存放搬移函数或字段的类,就创建一个新类。
-- 通常,可以运用 `将类内联化(Inline Class)` 将一些列相关行为放进同一个类。
-
-
-
-### 收益
-
-- 更好的代码组织
-- 减少重复代码
-- 更易维护
-
-
-
-## 重构方法说明
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 搬移字段(Move Field)
-
-**问题**
-
-在你的程序中,某个字段被其所驻类之外的另一个类更多地用到。
-
-
-
-**解决**
-
-在目标类新建一个字段,修改源字段的所有用户,令他们改用新字段。
-
-
-
-### 将类内联化(Inline Class)
-
-**问题**
-
-某个类没有做太多事情。
-
-
-
-**解决**
-
-将这个类的所有特性搬移到另一个类中,然后移除原类。
-
-
diff --git "a/design/refactor/chapter04/\345\206\227\344\275\231\347\261\273.md" "b/design/refactor/chapter04/\345\206\227\344\275\231\347\261\273.md"
deleted file mode 100644
index 9a8ab47659..0000000000
--- "a/design/refactor/chapter04/\345\206\227\344\275\231\347\261\273.md"
+++ /dev/null
@@ -1,62 +0,0 @@
-## 坏味道——冗余类(Lazy Class)
-
-### 特征
-
-理解和维护总是费时费力的。如果一个类不值得你花费精力,它就应该被删除。
-
-
-
-### 问题原因
-
-也许一个类的初始设计是一个功能完全的类,然而随着代码的变迁,变得没什么用了。
-又或者类起初的设计是为了支持未来的功能扩展,然而却一直未派上用场。
-
-### 解决方法
-
-- 没什么用的类可以运用 `将类内联化(Inline Class)` 来干掉。
-
-
-
-- 如果子类用处不大,试试 `折叠继承体系(Collapse Hierarchy)` 。
-
-### 收益
-
-- 减少代码量
-- 易于维护
-
-### 何时忽略
-
-- 有时,创建冗余类是为了描述未来开发的意图。在这种情况下,尝试在代码中保持清晰和简单之间的平衡。
-
-
-
-
-## 重构方法说明
-
-### 将类内联化(Inline Class)
-
-**问题**
-
-某个类没有做太多事情。
-
-
-
-**解决**
-
-将这个类的所有特性搬移到另一个类中,然后移除原类。
-
-
-
-### 折叠继承体系(Collapse Hierarchy)
-
-**问题**
-
-超类和子类之间无太大区别。
-
-
-
-**解决**
-
-将它们合为一体。
-
-
diff --git "a/design/refactor/chapter04/\345\244\270\345\244\270\345\205\266\350\260\210\346\234\252\346\235\245\346\200\247.md" "b/design/refactor/chapter04/\345\244\270\345\244\270\345\205\266\350\260\210\346\234\252\346\235\245\346\200\247.md"
deleted file mode 100644
index 6d7178d9b7..0000000000
--- "a/design/refactor/chapter04/\345\244\270\345\244\270\345\205\266\350\260\210\346\234\252\346\235\245\346\200\247.md"
+++ /dev/null
@@ -1,107 +0,0 @@
-## 坏味道——夸夸其谈未来性(Speculative Generality)
-
-### 特征
-
-存在未被使用的类、函数、字段或参数。
-
-
-
-### 问题原因
-
-有时,代码仅仅为了支持未来的特性而产生,然而却一直未实现。结果,代码变得难以理解和维护。
-
-### 解决方法
-
-- 如果你的某个抽象类其实没有太大作用,请运用 `折叠继承体系(Collapse Hierarch)` 。
-
-
-
-- 不必要的委托可运用 `将类内联化(Inline Class)` 消除。
-- 无用的函数可运用 `内联函数(Inline Method)` 消除。
-- 函数中有无用的参数应该运用 `移除参数(Remove Parameter)` 消除。
-- 无用字段可以直接删除。
-
-### 收益
-
-- 减少代码量。
-- 更易维护。
-
-### 何时忽略
-
-- 如果你在一个框架上工作,创建框架本身没有使用的功能是非常合理的,只要框架的用户需要这个功能。
-- 删除元素之前,请确保它们不在单元测试中使用。如果测试需要从类中获取某些内部信息或执行特殊的测试相关操作,就会发生这种情况。
-
-## 重构方法说明
-
-### 折叠继承体系(Collapse Hierarchy)
-
-**问题**
-
-超类和子类之间无太大区别。
-
-
-
-**解决**
-
-将它们合为一体。
-
-
-
-### 将类内联化(Inline Class)
-
-**问题**
-
-某个类没有做太多事情。
-
-
-
-**解决**
-
-将这个类的所有特性搬移到另一个类中,然后移除原类。
-
-
-
-### 内联函数(Inline Method)
-
-**问题**
-
-一个函数的本体比函数名更清楚易懂。
-
-```java
-class PizzaDelivery {
- //...
- int getRating() {
- return moreThanFiveLateDeliveries() ? 2 : 1;
- }
- boolean moreThanFiveLateDeliveries() {
- return numberOfLateDeliveries > 5;
- }
-}
-```
-
-**解决**
-
-在函数调用点插入函数本体,然后移除该函数。
-
-```java
-class PizzaDelivery {
- //...
- int getRating() {
- return numberOfLateDeliveries > 5 ? 2 : 1;
- }
-}
-```
-
-### 移除参数(Remove Parameter)
-
-**问题**
-
-函数本体不再需要某个参数。
-
-
-
-**解决**
-
-将该参数去除。
-
-
diff --git "a/design/refactor/chapter04/\347\272\257\347\250\232\347\232\204\346\225\260\346\215\256\347\261\273.md" "b/design/refactor/chapter04/\347\272\257\347\250\232\347\232\204\346\225\260\346\215\256\347\261\273.md"
deleted file mode 100644
index 65c975c366..0000000000
--- "a/design/refactor/chapter04/\347\272\257\347\250\232\347\232\204\346\225\260\346\215\256\347\261\273.md"
+++ /dev/null
@@ -1,144 +0,0 @@
-## 坏味道——纯稚的数据类(Data Class)
-
-### 特征
-
-`纯稚的数据类(Data Class)` 指的是只包含字段和访问它们的getter和setter函数的类。这些仅仅是供其他类使用的数据容器。这些类不包含任何附加功能,并且不能对自己拥有的数据进行独立操作。
-
-
-
-### 问题原因
-
-当一个新创建的类只包含几个公共字段(甚至可能几个getters / setters)是很正常的。但是对象的真正力量在于它们可以包含作用于数据的行为类型或操作。
-
-### 解决方法
-
-- 如果一个类有公共字段,你应该运用 `封装字段(Encapsulated Field)` 来隐藏字段的直接访问方式。
-- 如果这些类含容器类的字段,你应该检查它们是不是得到了恰当的封装;如果没有,就运用 `封装集合(Encapsulated Collection)` 把它们封装起来。
-- 找出这些getter/setter函数被其他类运用的地点。尝试以 `搬移函数(Move Method)` 把那些调用行为搬移到 `纯稚的数据类(Data Class)` 来。如果无法搬移这个函数,就运用 `提炼函数(Extract Method)` 产生一个可搬移的函数。
-
-
-
-- 在类已经充满了深思熟虑的函数之后,你可能想要摆脱旧的数据访问方法,以提供适应面较广的类数据访问接口。为此,可以运用 `移除设置函数(Remove Setting Method)` 和 `隐藏函数(Hide Method)` 。
-
-### 收益
-
-- 提高代码的可读性和组织性。特定数据的操作现在被集中在一个地方,而不是在分散在代码各处。
-- 帮助你发现客户端代码的重复处。
-
-## 重构方法说明
-
-### 封装字段(Encapsulated Field)
-
-**问题**
-
-你的类中存在public字段。
-
-```
-class Person {
- public String name;
-}
-```
-
-**解决**
-
-将它声明为private,并提供相应的访问函数。
-
-```
-class Person {
- private String name;
-
- public String getName() {
- return name;
- }
- public void setName(String arg) {
- name = arg;
- }
-}
-```
-
-### 封装集合(Encapsulated Collection)
-
-**问题**
-
-有个函数返回一个集合。
-
-
-
-**解决**
-
-让该函数返回该集合的一个只读副本,并在这个类中提供添加、移除集合元素的函数。
-
-
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
-
-### 移除设置函数(Remove Setting Method)
-
-**问题**
-
-类中的某个字段应该在对象创建时被设值,然后就不再改变。
-
-
-
-**解决**
-
-去掉该字段的所有设值函数。
-
-
-
-### 隐藏函数(Hide Method)
-
-**问题**
-
-有一个函数,从来没有被其他任何类用到。
-
-
-
-**解决**
-
-将这个函数修改为private。
-
-
diff --git "a/design/refactor/chapter04/\350\277\207\345\244\232\347\232\204\346\263\250\351\207\212.md" "b/design/refactor/chapter04/\350\277\207\345\244\232\347\232\204\346\263\250\351\207\212.md"
deleted file mode 100644
index d7ff75bd79..0000000000
--- "a/design/refactor/chapter04/\350\277\207\345\244\232\347\232\204\346\263\250\351\207\212.md"
+++ /dev/null
@@ -1,152 +0,0 @@
-## 坏味道——过多的注释(Comments)
-
-### 特征
-
-注释本身并不是坏事。但是常常有这样的情况:一段代码中出现长长的注释,而它之所以存在,是因为代码很糟糕。
-
-
-
-### 问题原因
-
-注释的作者意识到自己的代码不直观或不明显,所以想使用注释来说明自己的意图。这种情况下,注释就像是烂代码的除臭剂。
-
-> 最好的注释是为函数或类起一个恰当的名字。
-
-如果你觉得一个代码片段没有注释就无法理解,请先尝试重构,试着让所有注释都变得多余。
-
-### 解决方法
-
-- 如果一个注释是为了解释一个复杂的表达式,可以运用 `提炼变量(Extract Variable)` 将表达式切分为易理解的子表达式。
-- 如果你需要通过注释来解释一段代码做了什么,请试试 `提炼函数(Extract Method)` 。
-- 如果函数已经被提炼,但仍需要注释函数做了什么,试试运用 `函数改名(Rename Method)` 来为函数起一个可以自解释的名字。
-- 如果需要对系统某状态进行断言,请运用 `引入断言(Introduce Assertion)` 。
-
-### 收益
-
-- 代码变得更直观和明显。
-
-### 何时忽略
-
-注释有时候很有用:
-
-- 当解释为什么某事物要以特殊方式实现时。
-- 当解释某种复杂算法时。
-- 当你实在不知可以做些什么时。
-
-## 重构方法说明
-
-### 提炼变量(Extract Variable)
-
-**问题**
-
-你有个难以理解的表达式。
-
-```java
-void renderBanner() {
- if ((platform.toUpperCase().indexOf("MAC") > -1) &&
- (browser.toUpperCase().indexOf("IE") > -1) &&
- wasInitialized() && resize > 0 )
- {
- // do something
- }
-}
-```
-
-**解决**
-
-将表达式的结果或它的子表达式的结果用不言自明的变量来替代。
-
-```java
-void renderBanner() {
- final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
- final boolean isIE = browser.toUpperCase().indexOf("IE") > -1;
- final boolean wasResized = resize > 0;
-
- if (isMacOs && isIE && wasInitialized() && wasResized) {
- // do something
- }
-}
-```
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
-
-### 函数改名(Rename Method)
-
-**问题**
-
-函数的名称未能恰当的揭示函数的用途。
-
-```java
-class Person {
- public String getsnm();
-}
-```
-
-**解决**
-
-修改函数名。
-
-```java
-class Person {
- public String getSecondName();
-}
-```
-
-### 引入断言(Introduce Assertion)
-
-**问题**
-
-某一段代码需要对程序状态做出某种假设。
-
-```java
-double getExpenseLimit() {
- // should have either expense limit or a primary project
- return (expenseLimit != NULL_EXPENSE) ?
- expenseLimit:
- primaryProject.getMemberExpenseLimit();
-}
-```
-
-**解决**
-
-以断言明确表现这种假设。
-
-```java
-double getExpenseLimit() {
- Assert.isTrue(expenseLimit != NULL_EXPENSE || primaryProject != null);
-
- return (expenseLimit != NULL_EXPENSE) ?
- expenseLimit:
- primaryProject.getMemberExpenseLimit();
-}
-```
-*注:请不要滥用断言。不要使用它来检查”应该为真“的条件,只能使用它来检查“一定必须为真”的条件。实际上,断言更多是用于自我检测代码的一种手段。在产品真正交付时,往往都会消除所有断言。*
diff --git "a/design/refactor/chapter04/\351\207\215\345\244\215\344\273\243\347\240\201.md" "b/design/refactor/chapter04/\351\207\215\345\244\215\344\273\243\347\240\201.md"
deleted file mode 100644
index 7eeba7b7fc..0000000000
--- "a/design/refactor/chapter04/\351\207\215\345\244\215\344\273\243\347\240\201.md"
+++ /dev/null
@@ -1,271 +0,0 @@
-## 坏味道——重复代码(Duplicate Code)
-
-重复代码堪称为代码坏味道之首。消除重复代码总是有利无害的。
-
-### 特征
-
-两个代码片段看上去几乎一样。
-
-
-
-### 问题原因
-
-重复代码通常发生在多个程序员同时在同一程序的不同部分上工作时。由于他们正在处理不同的任务,他们可能不知道他们的同事已经写了类似的代码。
-
-还有一种更隐晦的重复,特定部分的代码看上去不同但实际在做同一件事。这种重复代码往往难以找到和消除。
-
-有时重复是有目的性的。当急于满足deadline,并且现有代码对于要交付的任务是“几乎正确的”时,新手程序员可能无法抵抗复制和粘贴相关代码的诱惑。在某些情况下,程序员只是太懒惰。
-
-### 解决方法
-
-- 同一个类的两个函数含有相同的表达式,这时可以采用 `提炼函数(Extract Method)` 提炼出重复的代码,然后让这两个地点都调用被提炼出来的那段代码。
-
-
-
-- 如果两个互为兄弟的子类含有重复代码:
- - 首先对两个类都运用 `提炼函数(Extract Method)` ,然后对被提炼出来的函数运用 `函数上移(Pull Up Method)` ,将它推入超类。
- - 如果重复代码在构造函数中,运用 `构造函数本体上移(Pull Up Constructor Body)` 。
- - 如果重复代码只是相似但不是完全相同,运用 `塑造模板函数(Form Template Method)` 获得一个 **模板方法模式(Template Method)** 。
- - 如果有些函数以不同的算法做相同的事,你可以选择其中较清晰地一个,并运用 `替换算法(Substitute Algorithm)` 将其他函数的算法替换掉。
-- 如果两个毫不相关的类中有重复代码:
- - 请尝试运用 `提炼超类(Extract Superclass)` ,以便为维护所有先前功能的这些类创建一个超类。
- - 如果创建超类十分困难,可以在一个类中运用 `提炼类(Extract Class)` ,并在另一个类中使用这个新的组件。
-- 如果存在大量的条件表达式,并且它们执行完全相同的代码(仅仅是它们的条件不同),可以运用 `合并条件表达式(Consolidate Conditional Expression)` 将这些操作合并为单个条件,并运用 `提炼函数(Extract Method)` 将该条件放入一个名字容易理解的独立函数中。
-- 如果条件表达式的所有分支都有部分相同的代码片段:可以运用 `合并重复的条件片段(Consolidate Duplicate Conditional Fragments)` 将它们都存在的代码片段置于条件表达式外部。
-
-### 收益
-
-- 合并重复代码会简化代码的结构,并减少代码量。
-- 代码更简化、更易维护。
-
-
-
-## 重构方法说明
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
-
-### 函数上移(Pull Up Method)
-
-**问题**
-
-有些函数,在各个子类中产生完全相同的结果。
-
-
-
-**解决**
-
-将该函数移至超类。
-
-
-
-### 构造函数本体上移(Pull Up Constructor Body)
-
-**问题**
-
-你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。
-
-```java
-class Manager extends Employee {
- public Manager(String name, String id, int grade) {
- this.name = name;
- this.id = id;
- this.grade = grade;
- }
- //...
-}
-```
-
-**解决**
-
-在超类中新建一个构造函数,并在子类构造函数中调用它。
-
-```java
-class Manager extends Employee {
- public Manager(String name, String id, int grade) {
- super(name, id);
- this.grade = grade;
- }
- //...
-}
-```
-
-### 塑造模板函数(Form Template Method)
-
-**问题**
-
-你有一些子类,其中相应的某些函数以相同的顺序执行类似的操作,但各个操作的细节上有所不同。
-
-
-
-**解决**
-
-将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。
-
-
-
-*注:这里只提到具体做法,建议了解一下模板方法设计模式。*
-
-### 替换算法(Substitute Algorithm)
-
-**问题**
-
-你想要把某个算法替换为另一个更清晰的算法。
-
-```java
-String foundPerson(String[] people){
- for (int i = 0; i < people.length; i++) {
- if (people[i].equals("Don")){
- return "Don";
- }
- if (people[i].equals("John")){
- return "John";
- }
- if (people[i].equals("Kent")){
- return "Kent";
- }
- }
- return "";
-}
-```
-
-**解决**
-
-将函数本体替换为另一个算法。
-
-```java
-String foundPerson(String[] people){
- List candidates =
- Arrays.asList(new String[] {"Don", "John", "Kent"});
- for (int i=0; i < people.length; i++) {
- if (candidates.contains(people[i])) {
- return people[i];
- }
- }
- return "";
-}
-```
-
-### 提炼超类(Extract Superclass)
-
-**问题**
-
-两个类有相似特性。
-
-
-
-**解决**
-
-为这两个类建立一个超类,将相同特性移至超类。
-
-
-
-### 提炼类(Extract Class)
-
-**问题**
-
-某个类做了不止一件事。
-
-
-
-
-**解决**
-
-建立一个新类,将相关的字段和函数从旧类搬移到新类。
-
-
-
-### 合并条件表达式(Consolidate Conditional Expression)
-
-**问题**
-
-你有一系列条件分支,都得到相同结果。
-
-```java
-double disabilityAmount() {
- if (seniority < 2) {
- return 0;
- }
- if (monthsDisabled > 12) {
- return 0;
- }
- if (isPartTime) {
- return 0;
- }
- // compute the disability amount
- //...
-}
-```
-
-**解决**
-
-将这些条件分支合并为一个条件,并将这个条件提炼为一个独立函数。
-
-```java
-double disabilityAmount() {
- if (isNotEligableForDisability()) {
- return 0;
- }
- // compute the disability amount
- //...
-}
-```
-
-### 合并重复的条件片段(Consolidate Duplicate Conditional Fragments)
-
-**问题**
-
-在条件表达式的每个分支上有着相同的一段代码。
-
-```java
-if (isSpecialDeal()) {
- total = price * 0.95;
- send();
-}
-else {
- total = price * 0.98;
- send();
-}
-```
-
-**解决**
-
-将这段重复代码搬移到条件表达式之外。
-
-```java
-if (isSpecialDeal()) {
- total = price * 0.95;
-}
-else {
- total = price * 0.98;
-}
-send();
-```
diff --git "a/design/refactor/chapter05/\344\270\215\345\256\214\347\276\216\347\232\204\345\272\223\347\261\273.md" "b/design/refactor/chapter05/\344\270\215\345\256\214\347\276\216\347\232\204\345\272\223\347\261\273.md"
deleted file mode 100644
index 797f613198..0000000000
--- "a/design/refactor/chapter05/\344\270\215\345\256\214\347\276\216\347\232\204\345\272\223\347\261\273.md"
+++ /dev/null
@@ -1,72 +0,0 @@
-## 坏味道——不完美的库类(Incomplete Library Class)
-
-### 特征
-
-当一个类库已经不能满足实际需要时,你就不得不改变这个库(如果这个库是只读的,那就没辙了)。
-
-### 问题原因
-
-许多编程技术都建立在库类的基础上。库类的作者没用未卜先知的能力,不能因此责怪他们。麻烦的是库往往构造的不够好,而且往往不可能让我们修改其中的类以满足我们的需要。
-
-### 解决方法
-
-- 如果你只想修改类库的一两个函数,可以运用 `引入外加函数(Introduce Foreign Method)`;
-- 如果想要添加一大堆额外行为,就得运用 `引入本地扩展(Introduce Local Extension)` 。
-
-### 收益
-
-- 减少代码重复(你不用一言不合就自己动手实现一个库的全部功能,代价太高)
-
-### 何时忽略
-
-- 如果扩展库会带来额外的工作量。
-
-## 重构方法说明
-
-### 引入外加函数(Introduce Foreign Method)
-
-**问题**
-
-你需要为提供服务的类增加一个函数,但你无法修改这个类。
-
-```java
-class Report {
- //...
- void sendReport() {
- Date nextDay = new Date(previousEnd.getYear(),
- previousEnd.getMonth(), previousEnd.getDate() + 1);
- //...
- }
-}
-```
-
-**解决**
-
-在客户类中建立一个函数,并一个第一个参数形式传入一个服务类实例。
-
-```java
-class Report {
- //...
- void sendReport() {
- Date newStart = nextDay(previousEnd);
- //...
- }
- private static Date nextDay(Date arg) {
- return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
- }
-}
-```
-
-### 引入本地扩展(Introduce Local Extension)
-
-**问题**
-
-你需要为服务类提供一些额外函数,但你无法修改这个类。
-
-
-
-**解决**
-
-建立一个新类,使它包含这些额外函数,让这个扩展品成为源类的子类或包装类。
-
-
diff --git "a/design/refactor/chapter05/\344\270\255\351\227\264\344\272\272.md" "b/design/refactor/chapter05/\344\270\255\351\227\264\344\272\272.md"
deleted file mode 100644
index 0d4c300397..0000000000
--- "a/design/refactor/chapter05/\344\270\255\351\227\264\344\272\272.md"
+++ /dev/null
@@ -1,44 +0,0 @@
-## 坏味道——中间人(Middle Man)
-
-### 特征
-
-如果一个类的作用仅仅是指向另一个类的委托,为什么要存在呢?
-
-
-
-### 问题原因
-
-对象的基本特征之一就是封装:对外部世界隐藏其内部细节。封装往往伴随委托。但是人们可能过度运用委托。比如,你也许会看到一个类的大部分有用工作都委托给了其他类,类本身成了一个空壳,除了委托之外不做任何事情。
-
-### 解决方法
-
-应该运用 `移除中间人(Remove Middle Man)`,直接和真正负责的对象打交道。
-
-### 收益
-
-- 减少笨重的代码。
-
-
-
-### 何时忽略
-
-如果是以下情况,不要删除已创建的中间人:
-
-- 添加中间人是为了避免类之间依赖关系。
-- 一些设计模式有目的地创建中间人(例如代理模式和装饰器模式)。
-
-## 重构方法说明
-
-### 移除中间人(Remove Middle Man)
-
-**问题**
-
-某个类做了过多的简单委托动作。
-
-
-
-**解决**
-
-让客户直接调用委托类。
-
-
diff --git "a/design/refactor/chapter05/\344\276\235\346\201\213\346\203\205\347\273\223.md" "b/design/refactor/chapter05/\344\276\235\346\201\213\346\203\205\347\273\223.md"
deleted file mode 100644
index 4fec882e83..0000000000
--- "a/design/refactor/chapter05/\344\276\235\346\201\213\346\203\205\347\273\223.md"
+++ /dev/null
@@ -1,81 +0,0 @@
-## 坏味道——依恋情结(Feature Envy)
-
-### 特征
-
-一个函数访问其它对象的数据比访问自己的数据更多。
-
-
-
-### 问题原因
-
-这种气味可能发生在字段移动到数据类之后。如果是这种情况,你可能想将数据类的操作移动到这个类中。
-
-### 解决方法
-
-As a basic rule, if things change at the same time, you should keep them in the same place. Usually data and functions that use this data are changed together (although exceptions are possible).
-
-有一个基本原则:同时会发生改变的事情应该被放在同一个地方。通常,数据和使用这些数据的函数是一起改变的。
-
-
-
-- 如果一个函数明显应该被移到另一个地方,可运用 `搬移函数(Move Method)` 。
-- 如果仅仅是函数的部分代码访问另一个对象的数据,运用 `提炼函数(Extract Method)` 将这部分代码移到独立的函数中。
-- 如果一个方法使用来自其他几个类的函数,首先确定哪个类包含大多数使用的数据。然后,将该方法与其他数据一起放在此类中。或者,使用 `提炼函数(Extract Method)` 将方法拆分为几个部分,可以放置在不同类中的不同位置。
-
-### 收益
-
-- 减少重复代码(如果数据处理的代码放在中心位置)。
-- 更好的代码组织性(处理数据的函数靠近实际数据)。
-
-
-
-### 何时忽略
-
-- 有时,行为被有意地与保存数据的类分开。这通常的优点是能够动态地改变行为(见策略设计模式,访问者设计模式和其他模式)。
-
-## 重构方法说明
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
diff --git "a/design/refactor/chapter05/\347\213\216\346\230\265\345\205\263\347\263\273.md" "b/design/refactor/chapter05/\347\213\216\346\230\265\345\205\263\347\263\273.md"
deleted file mode 100644
index 4066dad4f0..0000000000
--- "a/design/refactor/chapter05/\347\213\216\346\230\265\345\205\263\347\263\273.md"
+++ /dev/null
@@ -1,116 +0,0 @@
-## 坏味道——狎昵关系(Inappropriate Intimacy)
-
-### 特征
-
-一个类大量使用另一个类的内部字段和方法。
-
-
-
-### 问题原因
-
-类和类之间应该尽量少的感知彼此(减少耦合)。这样的类更容易维护和复用。
-
-### 解决方法
-
-- 最简单的解决方法是运用 `搬移函数(Move Method)` 和 `搬移字段(Move Field)` 来让类之间斩断羁绊。
-
-
-
-- 你也可以看看是否能运用 `将双向关联改为单向关联(Change Bidirectional Association to Unidirectional)` 让其中一个类对另一个说分手。
-
-- 如果这两个类实在是情比金坚,难分难舍,可以运用 `提炼类(Extract Class)` 把二者共同点提炼到一个新类中,让它们产生爱的结晶。或者,可以尝试运用 `隐藏委托关系(Hide Delegate)` 让另一个类来为它们牵线搭桥。
-- 继承往往造成类之间过分紧密,因为子类对超类的了解总是超过后者的主观愿望,如果你觉得该让这个子类自己闯荡,请运用 `以委托取代继承(Replace Inheritance with Delegation)` 来让超类和子类分家。
-
-### 收益
-
-- 提高代码组织性。
-- 提高代码复用性。
-
-
-
-## 重构方法说明
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
-
-### 搬移字段(Move Field)
-
-**问题**
-
-在你的程序中,某个字段被其所驻类之外的另一个类更多地用到。
-
-
-
-**解决**
-
-在目标类新建一个字段,修改源字段的所有用户,令他们改用新字段。
-
-
-
-### 将双向关联改为单向关联(Change Bidirectional Association to Unidirectional)
-
-**问题**
-
-两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。
-
-
-
-**解决**
-
-去除不必要的关联。
-
-
-
-### 提炼类(Extract Class)
-
-**问题**
-
-某个类做了不止一件事。
-
-
-
-
-**解决**
-
-建立一个新类,将相关的字段和函数从旧类搬移到新类。
-
-
-
-### 隐藏委托关系(Hide Delegate)
-
-**问题**
-
-客户通过一个委托类来调用另一个对象。
-
-
-
-**解决**
-
-在服务类上建立客户所需的所有函数,用以隐藏委托关系。
-
-
-
-### 以委托取代继承(Replace Inheritance with Delegation)
-
-**问题**
-
-某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。
-
-
-
-**解决**
-
-在子类中新建一个字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。
-
-
diff --git "a/design/refactor/chapter05/\350\277\207\345\272\246\350\200\246\345\220\210\347\232\204\346\266\210\346\201\257\351\223\276.md" "b/design/refactor/chapter05/\350\277\207\345\272\246\350\200\246\345\220\210\347\232\204\346\266\210\346\201\257\351\223\276.md"
deleted file mode 100644
index b2ac32d9f1..0000000000
--- "a/design/refactor/chapter05/\350\277\207\345\272\246\350\200\246\345\220\210\347\232\204\346\266\210\346\201\257\351\223\276.md"
+++ /dev/null
@@ -1,92 +0,0 @@
-## 坏味道——过度耦合的消息链(Message Chains)
-
-### 特征
-
-消息链的形式类似于:`obj.getA().getB().getC()`。
-
-
-
-### 问题原因
-
-如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象……这就是消息链。实际代码中你看到的可能是一长串getThis()或一长串临时变量。采取这种方式,意味客户代码将与查找过程中的导航紧密耦合。一旦对象间关系发生任何变化,客户端就不得不做出相应的修改。
-
-### 解决方法
-
-- 可以运用 `隐藏委托关系(Hide Delegate)` 删除一个消息链。
-
-
-
-- 有时更好的选择是:先观察消息链最终得到的对象是用来干什么的。看看能否以 `提炼函数(Extract Method)`把使用该对象的代码提炼到一个独立函数中,再运用 `搬移函数(Move Method)` 把这个函数推入消息链。
-
-### 收益
-
-- 能减少链中类之间的依赖。
-- 能减少代码量。
-
-
-
-### 何时忽略
-
-- 过于侵略性的委托可能会使程序员难以理解功能是如何触发的。
-
-## 重构方法说明
-
-### 隐藏委托关系(Hide Delegate)
-
-**问题**
-
-客户通过一个委托类来调用另一个对象。
-
-
-
-**解决**
-
-在服务类上建立客户所需的所有函数,用以隐藏委托关系。
-
-
-
-### 提炼函数(Extract Method)
-
-**问题**
-
-你有一段代码可以组织在一起。
-
-```java
-void printOwing() {
- printBanner();
-
- //print details
- System.out.println("name: " + name);
- System.out.println("amount: " + getOutstanding());
-}
-```
-
-**解决**
-
-移动这段代码到一个新的函数中,使用函数的调用来替代老代码。
-
-```java
-void printOwing() {
- printBanner();
- printDetails(getOutstanding());
-}
-
-void printDetails(double outstanding) {
- System.out.println("name: " + name);
- System.out.println("amount: " + outstanding);
-}
-```
-
-### 搬移函数(Move Method)
-
-**问题**
-
-你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。
-
-
-
-**解决**
-
-在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。
-
-
diff --git a/design/refactor/think-in-refactor.md b/design/refactor/think-in-refactor.md
deleted file mode 100644
index 8bfb76c3b6..0000000000
--- a/design/refactor/think-in-refactor.md
+++ /dev/null
@@ -1,125 +0,0 @@
-# 对于重构的思考
-
-第一次读《重构:改善既有代码的设计》时,我曾整理过一个简单的笔记。最近,因为参与一个重构项目,再一次温习了《重构:改善既有代码的设计》。过程中,萌发了认真总结、整理重构方法的冲动,于是有了这系列文字。
-
-代码的坏味道还有几篇没有完稿,后面我会陆续补充。。。
-
-## 症与药
-
-### 对代码的坏味道的思考
-
-“有病要早治,不要放弃治疗”。多么朴素的道理 ,人人都懂。
-
-病,就是不健康。
-
-人有病,可以通过打针、吃药、做手术来进行治疗。
-
-**如果把代码的坏味道(代码质量问题)比作病症,那么重构就是治疗代码的坏味道的药。**
-
-个人认为,在重构这件事上,也可以应用治病的道理:
-
-- **防病于未然。**
- —— 春秋战国时期的一代名医扁鹊,曾经有个很著名的医学主张:**防病于未然**。 我觉得这个道理应用于软件代码的重构亦然。编程前要有合理的设计、编程时要有良好的编程风格,尽量减少问题。从这个层面上说,了解代码的坏味道,不仅仅是为了发现问题、解决问题。更重要的作用是:指导我们在编程过程中有意识的去规避这些问题。
-
-- **小病不医,易得大病。**
- —— 刘备说过:“勿以善小而不为,勿以恶小而为之”。发现问题就及时修改,代码质量自然容易进入良性循环;反之,亦然。要重视积累的力量,别总以为代码出现点小问题,那都不是事儿。
-
-- **对症下药。**
- —— 程序出现了问题,要分析出问题的根本,有针对性的制定合理的重构方案。大家都知道吃错药的后果,同样的,**瞎改还不如不改**。
-
-- **忌猛药**
- —— 医病用猛药容易产生副作用。换一句俗语:步子大了容易扯着蛋。重构如果大刀阔斧的干,那你就要有随时可能扑街的心理准备。推倒重来不是重构,而是重写。重构应该是循序渐进,步步为营的过程。当你发现重写代码比重构代码更简单,往往说明你早就该重构了。
-
-### 重构的原则
-
-前面把代码质量问题比作病症,而把重构比作药。
-这里,我们再进一步讨论一下重构的原则。我将采用5W2H分析法来说明。
-
-#### 何谓重构(What)
-
-`重构(Refactoring)` 的常见定义是:不改变软件系统外部行为的前提下,改善它的内部结构。
-
-个人觉得这个定义有点生涩。不妨理解为:重构是给代码治病的行为。而代码有病是指代码的质量(可靠性、安全性、可复用性、可维护性)和性能有问题。
-
-**重构的目的是为了提高代码的质量和性能**。
-
-*注:功能不全或者不正确,那是残疾代码。就像治病治不了残疾,重构也解决不了功能问题。*
-
-#### 为何重构(Why)
-
-翻翻书,上网搜一下,谈到重构的理由大体相同:
-
-- 重构改进软件设计
-- 重构使软件更容易理解
-- 重构帮助找到bug
-- 重构提高编程速度
-
-总之就是,**重构可以提高代码质量**。
-
-#### 何时重构(When)
-
-关于何时重构,我先引用一下 [重构并非难在如何做,而是难在何时开始做 ](http://mt.sohu.com/20160812/n463980993.shtml) 一文的观点。
-
-> 对于一个高速发展的公司来说,停止业务开发,专门来做重构项目,**从来就不是一个可接受的选项**,“边开飞机边换引擎”才是这种公司想要的。
-
-我们不妨来衡量一下重构的成本和收益。
-
-- **重构的成本**
-
- 重构是有成本的,费时费力(时间、人力)不说,还有可能会使本来正常运行的程序出错。所以,很多人都抱着“不求有功,但求无过”的心理得过且过。
-
- 还有一种成本:重构使用较新且较为复杂的技术,学习曲线不平滑,团队成员技术切换困难,短期内开发效率可能不升反降。
-
- 但是,如果一直放任代码腐朽下去,技术债务会越来越沉重。当代码最终快要跑不动时,架构师们往往还是不得不使用激进的手段来治疗代码的顽疾。但是,这个过程通常都是非常痛苦的,而且有着很高的失败风险。
-
-- **重构的收益**
-
- 重构的收益是提高代码的质量和性能,并提高未来的开发效率。但是,应当看到,重构往往并不能在短期内带来实际的效益,或者很难直观看出效益。而对于一个企业来说,没有什么比效益更重要。换句话说,没有实际效益的事,通常也没有价值。很多领导,尤其是非技术方向的领导,并不关心你应用了什么新技术,让代码变得多么优雅等等。
-
-- **重构的合适时机**
-
- 从以上来看,重构实在是个吃力不讨好的事情。
-
- 于是,很多人屈服于万恶的KPI和要命的deadline,一边吐槽着以前的代码是垃圾,一边自己也在造垃圾。
-
- 但是,**重构本应该是个渐进式的过程,不是只有伤筋动骨的改造才叫重构。**如果非要等到代码已经烂到病入膏肓,再使用激进方式来重构,那必然是困难重重,风险极高。
-
- 《重构》书中提到的重构时机应该在添加功能、修复功能、审查代码时,不建议专门抽出时间专门做重构项目。
-
- 我认为,其思想就是指:**重构应该是在开发过程中实时的、渐进的演化过程。**
-
-- **重构的不恰当时机**
-
- 但是,这里我也要强调一下:**不是所有软件开发过程都一定要重构。**
-
- 较能**凸显重构价值的场景**是:代码规模较大、生命周期还较长、承担了较多责任、有一个较大(且较不稳定,人员流动频繁)团队在其上工作的单一代码库。
-
- 与之相反,有一些场景的重构价值就很小:
-
- - 代码库生命周期快要走到尾声,开发逐渐减少,以维护为主。
- - 代码库当前版本马上要发布了,这时重构无疑是给自己找麻烦。
- - 重构代价过于沉重:重构后功能的正确性、稳定性难以保障;技术过于超前,团队成员技术迁移难度太大。
-
-#### 如何重构(How)
-
-重构行为在我看来,也是可以分层级的。由高到低,越高层级难度越大:
-
-- 服务、数据库
- 现代软件往往业务复杂、庞大。使用微服务、数据迁移来拆分业务,降低业务复杂度成为了主流。但是,这些技术的测试、部署复杂,技术难度很高。
-
-- 组件、模块、框架
- 组件、模块、框架的重构,主要是针对代码的设计问题。解决的是代码的整体结构问题。需要对框架、设计模式、分布式、并发等等有足够的了解。
-
-- 类、接口、函数、字段
- 《重构》一书提到了**“代码的坏味道”**以及相关的重构方法。这些都是对类、接口、函数、字段级别代码的重构手段。由于这一级别的重构方法较为简单,所以可操作性较强。具体细节可以阅读《代码的坏味道》篇章。
-
-前两种层级的重构已经涉及到架构层面,影响较大,难度较高,如果功力不够不要轻易变动。由于这两个层级涉及领域较广,这里不做论述。
-
-***此处为分割线。下面是代码的坏味道系列。。。***
-
-## 代码的坏味道
-
-《重构:改善既有代码的设计》中介绍了22种代码的坏味道以及重构手法。这些坏味道可以进一步归类。我总觉得将事物分类有助于理解和记忆。所以本系列将坏味道按照特性分类,然后逐一讲解。
-
-
-
diff --git a/design/uml/UML.md b/design/uml/UML.md
deleted file mode 100644
index e8b3129649..0000000000
--- a/design/uml/UML.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# UML
-
-> 统一建模语言(英语 - Unified Modeling Language,缩写 UML)是非专利的第三代建模和规约语言。UML 是一种开放的方法,用于说明、可视化、构建和编写一个正在开发的、面向对象的、软件密集系统的制品的开放方法。UML 展现了一系列最佳工程实践,这些最佳实践在对大规模,复杂系统进行建模方面,特别是在软件架构层次已经被验证有效。
-
-
-
-
-
-- [简介](#简介)
- - [UML 图类型](#uml-图类型)
- - [UML 概念](#uml-概念)
-- [UML 工具](#uml-工具)
- - [亿图](#亿图)
- - [Visio](#visio)
- - [StarUML](#staruml)
- - [Astah](#astah)
- - [ArgoUML](#argouml)
- - [ProcessOn](#processon)
- - [drawio](#drawio)
-- [参考资料](#参考资料)
-
-
-
-## 简介
-
-### UML 图类型
-
-UML 图类型如下图所示:
-
-
-
-#### [结构式建模图](UML结构建模图.md)
-
-> 结构式建模图(Structure diagrams)强调的是系统式的建模。
-
-- [类图](UML结构建模图.md#类图)
-- [对象图](UML结构建模图.md#对象图)
-- [包图](UML结构建模图.md#包图)
-- [组件图](UML结构建模图.md#组件图)
-- [部署图](UML结构建模图.md#部署图)
-- [复合结构图](UML结构建模图.md#复合结构图)
-
-#### [行为式建模图](UML行为建模图.md)
-
-> 行为式建模图(Behavior diagrams)强调系统模型中触发的事。
-
-- [活动图](UML行为建模图.md#活动图)
-- [状态图](UML行为建模图.md#状态图)
-- [用例图](UML行为建模图.md#用例图)
-- [通信图](UML行为建模图.md#通信图)
-- [交互概述图](UML行为建模图.md#交互概述图)
-- [时序图](UML行为建模图.md#时序图)
-- [时间图](UML行为建模图.md#时间图)
-
-### UML 概念
-
-UML 从来源中使用相当多的概念。我们将之定义于统一建模语言术语汇表。下面仅列代表性的概念。
-
-- 对于结构而言 - 执行者,属性,类,元件,接口,对象,包。
-- 对于行为而言 - 活动(UML),事件(UML),消息(UML),方法(UML),操作(UML),状态(UML),用例(UML)。
-- 对于关系而言 - 聚合,关联,组合,相依,广义化(or 继承)。
-- 其他概念
- - 构造型—这规范符号应用到的模型
- - 多重性—多重性标记法与资料库建模基数对应,例如:`1, 0..1, 1..*`
-
-## UML 工具
-
-UML 工具非常多,到底哪种工具好,真的是仁者见仁智者见智。这里列举一些我接触过的 UML 工具:
-
-### 亿图
-
-> 国内开发的、收费的绘图工具。图形模板、素材非常全面,样式也很精美,可以导出为 word、pdf、图片。
->
-> [亿图官网](http://www.edrawsoft.cn/)
-
-
-
-### Visio
-
-> Office 的绘图工具,特点是简单、清晰。
->
-> [Visio 官网](https://products.office.com/zh-cn/visio/flowchart-software)
-
-
-
-### StarUML
-
-> 样式精美,功能全面的 UML 工具。
->
-> [StarUML 官网](http://staruml.io/)
-
-
-
-### Astah
-
-> 样式不错,功能全面的绘图工具。
->
-> [Astah 官网](http://astah.net/)
-
-
-
-### ArgoUML
-
-> UML 工具。
->
-> [ArgoUML 官网](https://argouml.en.softonic.com/?ex=CAT-759.2)
-
-
-
-### ProcessOn
-
-> 在线绘图工具,特点是简洁、清晰。
->
-> [ProcessOn 官网](https://www.processon.com/)
-
-### drawio
-
-> 开源的在线绘图工具,特点是简洁、清晰。
->
-> [drawio 官网](https://www.draw.io/)
-
-
-
-## 参考资料
-
-- [Wiki-UML](https://zh.wikipedia.org/wiki/统一建模语言)
-- [Sparx UML 教程](https://sparxsystems.cn/resources/uml2_tutorial/index.html)
-- [OMG UML](https://www.omg.org/spec/UML)
-- [UML Tutorial](https://www.tutorialspoint.com/uml/index.htm)
-- [W3Cschool UML 教程](https://www.w3cschool.cn/uml_tutorial/)
-- [UML 学习入门就这一篇文章](https://blog.csdn.net/soft_zzti/article/details/79811923)
diff --git "a/design/uml/UML\347\273\223\346\236\204\345\273\272\346\250\241\345\233\276.md" "b/design/uml/UML\347\273\223\346\236\204\345\273\272\346\250\241\345\233\276.md"
deleted file mode 100644
index ecf16e1a3e..0000000000
--- "a/design/uml/UML\347\273\223\346\236\204\345\273\272\346\250\241\345\233\276.md"
+++ /dev/null
@@ -1,297 +0,0 @@
----
-title: UML 结构建模图
-date: 2018/07/30
-categories:
- - uml
-tags:
- - uml
----
-
-# UML 结构建模图
-
-> 结构图定义了一个模型的静态架构。它们通常被用来对那些构成模型的‘要素'建模,诸如:类,对象,接口和物理组件。另外,它们也被用来对元素间关联和依赖关系进行建模。
->
-> 关键词:`部署图`, `组件图`, `包图`, `类图`, `复合结构图`, `对象图`
-
-
-
-- [部署图](#部署图)
-- [组件图](#组件图)
-- [包图](#包图)
-- [类图](#类图)
-- [复合结构图](#复合结构图)
-- [对象图](#对象图)
-- [参考资料](#参考资料)
-
-
-
-## 部署图
-
-> **部署图(Deployment Diagram)用于对系统的物理结构建模**。部署图将显示系统中的软件组件和硬件组件之间的关系以及处理工作的物理分布。
-
-
-
-### 节点
-
-节点既可以是硬件元素,也可以是软件元素。它显示为一个立方体,如下图所示。
-
-
-
-### 节点实例
-
-图可以显示节点实例,实例与节点的区分是:实例的名称带下划线,冒号放在它的基本节点类型之前。实例在冒号之前可以有名称,也可以没有名称。下图显示了一个具名的计算机实例。
-
-
-
-### 节点构造型
-
-为节点提供了许多标准的构造型,分别命名为 «cdrom», «cd-rom», «computer», «disk array», «pc», «pc client», «pc server», «secure», «server», «storage», «unix server», «user pc»。 并在节点符号的右上角显示适当的图标。
-
-
-
-### 工件
-
-工件是[软件开发](http://www.sparxsystems.cn/platforms/software_development.html)过程中的产品。包括过程模型(如:用例模型,设计模型等),源文件,执行文件,设计文档,测试报告,构造型,用户手册等等。
-
-工件表示为带有工件名称的矩形,并显示«artifact»关键字和文档符号。
-
-
-
-### 关联
-
-在部署图的上下文联系中,关联代表节点间的联系通道。下图显示了一个网络系统的部署图,描述了网络协议为构造型和关联终端的多重性,
-
-
-
-### 作为容器的节点
-
-节点可以包含其他元素,如组件和工件。下图显示了一个嵌入式系统某个部分的部署图。描写了一个被主板节点包含的可执行工件。
-
-
-
-## 组件图
-
-> **组件图(Component Diagram)描绘了组成一个软件系统的模块和嵌入控件**。组件图比类图具有更高层次的抽象-通常运行时一个组件被一个或多个类(或对象)实现。它们象积木那样使得组件能最终构成系统的绝大部分。
-
-
-
-上图演示了一些组件和它们的内部关系。装配连接器(Assembly connectors)“连接”由"Product"和"Customer"的提供接口到由 "Order"指定的需求接口。 一个依赖关系映射了客户相关的帐户信息到“Order”需要的 "Payment"需求接口。
-
-实际上,组件图同包图很相似,它们都有明确的界限,把元素分组到逻辑结构中。他们之间的不同是:组件图提供了语义更丰富的分组机制,在组件图中,所有的模型元素都是私有的,而包图只显示公有的成员。
-
-### 表现组件
-
-组件可表示为带关键字 «component»的矩形类元;也可用右上角有组件图标的矩形表示。
-
-
-
-### 装配连接器
-
-装配连接器在组件 “Component1”的需求接口和另一个组件 “Component2”的提供接口之间建立桥梁; 这个桥梁使得一个组件能提供另一个组件所需要的服务。
-
-
-
-### 带端口组件
-
-使用端口的组件图允许在它的环境指定一个服务和行为,同时这个服务和行为也是组件需要的。当端口进行双向操作的时候,它可以指定输入和输出。下图详述了用于在线服务的带端口组件,它有两个提供接口 “order entry”和 “tracking”,也有 “payment” 需求接口。
-
-
-
-## 包图
-
-> **包图(Package Diagram)用来表现包和它所包含元素的组织**。当用来代表类元素时,包图提供了命名空间的可视化。包图最常用的用途是用来组织用例图和类图,尽管它不局限于这些 UML 元素。
-
-下面是一个包图的例子。
-
-
-
-包中的元素共享相同的命名空间,因此,一个指定命名空间的元素必须有唯一的名称。
-
-包可以用来代表物理或逻辑关系。选择把类包括在指定的包里,有助于在同一个包里赋予这些类相同继承层次。通常认为把通过复合相关联的类,以及与它们相协作的类放在同一个包里。
-
-
-
-在 UML2.5 中,包用文件夹来表示,包中的元素共享同一个命名空间,并且必须是可识别的,因此要有唯一的名称或类型。包必须显示包名,在附属方框部分有选择的显示包内的元素。
-
-- **包的合并** - 包之间的合并连接符«merge»定义了源包元素与目标包同名元素之间的泛化关系。源包元素的定义被扩展来包含目标包元素定义。当源包元素与目标包内没有同名元素时,目标包元素的定义不受影响。
-- **包的导入** - 导入连接符 «import»表明目标包的元素,在该例中是一个类 ,在源包中被引用要用非限定修饰名。源包的命名空间获得目标类的接口,目标包的命名空间则不受影响。
-- **嵌套连接符** - 源包和目标包间的嵌套连接符说明目标包完全包含源包。
-
-## 类图
-
-> **类图(Class Diagram)展示了面向对象系统的构造模块**。描绘了模型或部分模型的静态视图,显示它包含的属性和行为,而不是详细描述操作的功能或完善方法。类图最常用来表达多个类和接口之间的关系。泛化(Generalizations),聚合(aggregations)和关联(associations)分别是类之间继承,复合或应用,及连接的表现。
-
-下面的图显示了类之间的聚合关系。弱聚合(浅色箭头)表现在类 "Account" 使用 "AddressBook",但是不必要包含它的一个实例。强聚合(图中的黑色箭头)表示了目标类包含源类,例如,"Contact" 和 "ContactGroup"值被包含在 "AddressBook"中。
-
-
-
-### 类(Classes)
-
-类是定义对象所具有的属性和行为的元素。行为用类能理解的合适消息和适合每条消息的操作来描述。 类中也可能定义约束,标记值,构造型。
-
-### 类的标柱(Class Notation)
-
-类用矩形表示。除类的名称外,还可以选择性地显示属性和操作。 分栏分别用来显示类的名称,属性和操作。
-
-在下面图中,类的类名显示在最上面的分栏,它下面的分栏显示详细属性,如:"center" 属性显示初始化的值。最后面的分栏显示操作,如: setWidth,setLength 和 setPosition 以及他们的参数。 属性和操作名前的标注表示了该属性或操作的可见性: 如果使用 "+"号,这个属性或操作是公共的 ; "-" 号则代表这个属性或操作是私有的。 "#"号是这个属性或操作被定义为保护的," ~" 号代表包的可见性。
-
-
-
-### 接口(Interfaces)
-
-接口是实施者同意满足的行为规范,是一种约定。实现一个接口,类必需支持其要求的行为,使系统按照同样的方式,即公共的接口,处理不相关的元素。
-
-
-
-接口有相似于类的外形风格,含有指定的操作,如下图所示。如果没有明确的详细操作,也可以画成一个圆环。当画成圆环的时候,到这个环形标柱的实现连接没有目标箭头。
-
-
-
-### 表(Tables)
-
-表尽管不是基本 UML 的一部分,仍然是“图型”能完成的实例用。在右上角画一个表的小图标来表示。表属性用“图型” «column»表示。 绝大多数表单有一个主键,是由一个或几个字段组成的一个唯一的字码组合加主键操作来访问表格,主键操作“图型”为«PK»。 一些表有一个或多个外键,使用一个或多个字段加一个外键操作,映射到相关表的主键上去,外键操作“图型”为«FK»。
-
-
-
-### 关联(Associations)
-
-关联表明两个模型元素之间有关系,通常用在一个类中被实现为一个实例变量。连接符可以包含两端的命名的角色,基数性,方向和约束。关联是元素之间普通的关系。如果多于两个元素,也可以使用菱形的关联关系。当从类图生成代码时,关联末端的对象将变成目标类中实例变量。见下图示例 "playsFor" 将变成"Player"类中的实例变量。
-
-
-
-### 泛化(Generalizations)
-
-泛化被用来说明继承关系。连接从特定类元到一般类元。泛化的含义是源类继承了目标类的特性。下图的图显示了一个父类泛化一个子类, 类“Circle”的一个实例将会有属性 “ x_position”,“ y_position” , “radius” 和 方法 “display()”。 注意:类 "Shape" 是抽象的,类名显示为斜体。
-
-
-
-下图显示了与上图相同信息的视图。
-
-
-
-### 聚合(Aggregations)
-
-聚合通常被用来描述由更小的组件所构成的元素。聚合关系表示为白色菱形箭头指向目标类或父类。
-
-聚合的更强形式 -组合聚合(强聚合) - 显示为黑色菱形箭头,用来组合每次最大化的包含组件。如果一个组合聚合的父类被删除,通常与他相关的所有部分都会被删除,但是,如果一个部件从组合中去掉,将不用删除整个组合。组合是可迁,非对称的关系和递归的。
-
-下面的图示:显示了弱聚合和强聚合的不同。“ address book” 由许多 “contacts” 和 “contact groups”组成。 “contact group” 是一个“contacts”的虚分组; “contact”可以被包含在不止一个 “ contact group”。 如果你删除一个“ address book”,所有的 “contacts” 和 “contact groups” 也将会被删除;如果你删除“ contact group”, 没有 “contacts”会被删除。
-
-
-
-### 关联类(Association Classes)
-
-关联类是一个允许关联连接有属性和操作的构造。下面的示例:显示了远不止简单连接两个类的连接,如给“employee”分配项目。“ employee”在项目中所起的作用是一个复杂的实体,既有自身的也有不属于“employee” 或 “project” 类的细节。 例如,“ employee”可以同时为几个项目工作,有不同的职务头衔和对应的安全权限。
-
-
-
-### 依赖(Dependencies)
-
-依赖被用来描述模型元素间广泛的依赖关系。通常在设计过程早期显示两个元素之间存在某种关系,因为是初期而不能确定具体是什么关系,在设计过程末期,该继承关系会被归入已有构造型 (构造型 可以是实例化 «instantiate»,跟踪 «trace»,导入 «import», 和其它的关系),或被替换成一个更明确类型的连接符。
-
-### 跟踪(Traces)
-
-跟踪关系是一种特殊化的依赖关系。连接模型元素或跨模型但是具有相同概念的模型元素集。跟踪被经常用来追踪需求和模型的变化。由于变化是双向的,这种依赖关系的顺序通常被忽略。这种关系的属性可以被指定为单向映射,但跟踪是双向的,非正式的和很少可计算的。
-
-### 实现(Realizations)
-
-是源对象执行或实现目标,实现被用来表达模型的可跟踪性和完整性-业务模型或需求被一个或多个用例实现,用例则被类实现,类被组件实现,等等。这种实现贯穿于系统设计的映射需求和类等,直至抽象建模水平级。从而确保整个系统的一张宏图,它也反映系统的所有微小组成,以及约束和定义它的细节。实现关系用带虚线的实箭头表示。
-
-
-
-### 嵌套(Nestings)
-
-嵌套连接符用来表示源元素嵌套在目标元素中。下图显示“ inner class”的定义,尽管在 EA 中,更多地按照着他们在项目层次视图中的位置来显示这种关系。
-
-
-
-## 复合结构图
-
-> **复合结构图显示类的内部结构,包括它与系统其他部分的交互点。也显示各部分的配置与关系,这些部分一起执行类元的行为。**
-
-类元素已经在类图部分被详细地阐述,这部分用来说明类表现复合元素的方式,如:暴露接口,包含端口和部件。
-
-
-
-### 部件
-
-部件是代表一组(一个或多个)实例的元素,这组实例的拥有者是一类元实例,例如:如果一个图的实例有一组图形元素,则这些图形元素可以被表示为部件,并可以对他们之间的某种关系建模。注意:一个部件可以在它的父类被删除之前从父类中被去掉,这样部件就不会被同时删除了。
-部件在类或组件内部显示为不加修饰的方框。
-
-
-
-### 端口
-
-端口是类型化的元素,代表一个包含类元实例的外部可视的部分。端口定义了类元和它的环境之间的交互。端口显示在包含它的部件,类或组合结构的边缘上。端口指定了类元提供的服务,以及类元要求环境提供的服务。
-端口显示为所属类元边界指定的方框。
-
-
-
-### 接口
-
-接口与类相似,但是有一些限制,所有的接口操作都是公共和抽象的,不提供任何默认的实现。所有的接口属性都必须是常量。然而,当一个类从一个单独的超级类继承而来,它可以实现多个接口。
-当一个接口在图中单列出来,它既可以显示为类元素的方框,带 «interface» 关键字和表明它是抽象的斜体名称,也可以显示为圆环。
-
-
-
-注意:圆环标注不显示接口操作。当接口显示为类所有的接口,它们会被当作暴露接口引用。暴露接口可以定义为是提供的,还是需求的。提供接口确认包含它的类元提供指定接口元素定义的操作,可通过类和接口间实现的连接来定义。需求接口说明该类元能与其他类元进行通信,这些类元提供了指定接口元素所定义的操作。需求接口可通过在类和接口间建立依赖连接来定义。
-提供接口显示为“带棒球体”,依附在类元边缘。需求接口显示为“带棒杯体”,也是依附在类元边缘。
-
-
-
-### 委托
-
-委托连接器用来定义组件外部端口和接口的内部工作方式。委托连接器表示为带有 «delegate» 关键字的箭头。它连接组件的外部约定,表现为它的端口,到组件部件行为的内部实现。
-
-
-
-### 协作
-
-协作定义了一系列共同协作的角色,它们集体展示一个指定的设计功能。协作图应仅仅显示完成指定任务或功能的角色与属性。隔离主要角色是用来简化结构和澄清行为,也用于重用。一个协作通常实现一个模式。
-协作元素显示为椭圆。
-
-
-
-### 角色绑定
-
-角色绑定连接器是一条从连接协作到所要完成该任务类元的连线。它显示为虚线,并在类元端显示作用名。
-
-
-
-### 表现
-
-表现连接器用于连接协作到类元来表示此类元中使用了该协作。显示为带关键字 «represents»的虚线箭头。
-
-
-
-发生
-发生连接器用于连接协作到类元来表示此协作表现了(同原文)该类元;显示为带关键字«occurrence»的虚线箭头。
-
-
-
-## 对象图
-
-> **对象图(Object Diagram)可以认为是类图的特殊情形,是类图元素子集,被用来及时强调在某些点,类的实例间的关系**。这对理解类图很有帮助。他们在构造上与类图显示没有不同,但是反映出多样性和作用。
-
-### 类和对象元素
-
-下面的图显示了类元素和对象元素外观上的不同。注意:类元素包括三个部分,分别是名字栏,属性栏和操作栏;对象元素默认为没有分栏。名称显示也有不同:对象名称有下划线,并可能显示该对象实例化所用类元的名称。
-
-
-
-### 运行状态
-
-类元元素可以有任意数量的属性和操作。在对象实例中不会被显示出来。但可能定义对象的运行状态,显示特殊实例的属性设置值。
-
-
-
-### 类和对象图示例
-
-下图是一个对象图,其中插入了类定义图。它例示如何用对象图来测试类图中任务多重性的方法。“car” 类对 “wheel” 类有“1 对多” 的多重性,但是如果已经选择用“1 对 4” 来替代,那样就不会在对象图显示“3 个轮子”的汽车。
-
-
-
-## 参考资料
-
-- https://sparxsystems.cn/resources/uml2_tutorial/index.html
-- http://www.cnblogs.com/ywqu/category/223486.html
diff --git "a/design/uml/UML\350\241\214\344\270\272\345\273\272\346\250\241\345\233\276.md" "b/design/uml/UML\350\241\214\344\270\272\345\273\272\346\250\241\345\233\276.md"
deleted file mode 100644
index 6efe7029b3..0000000000
--- "a/design/uml/UML\350\241\214\344\270\272\345\273\272\346\250\241\345\233\276.md"
+++ /dev/null
@@ -1,441 +0,0 @@
-# 行为建模图
-
-> 行为图用来记录在一个模型内部,随时间的变化,模型执行的交互变化和瞬间的状态;并跟踪系统在真实环境下如何表现,以及观察系统对一个操作或事件的反应,以及它的结果。
->
-> 关键词:`活动图`, `状态机图`, `用例图`, `通信图`, `交互概述图`, `时序图`, `时间图`
-
-
-
-- [活动图](#活动图)
-- [状态机图](#状态机图)
-- [用例图](#用例图)
-- [通信图](#通信图)
-- [交互概述图](#交互概述图)
-- [时序图](#时序图)
-- [时间图](#时间图)
-- [参考资料](#参考资料)
-
-
-
-## 活动图
-
-> UML 中,活动图用来展示活动的顺序。**显示了从起始点到终点的工作流,描述了活动图中存在于事件进程的判断路径**。活动图可以用来详细阐述某些活动执行中发生并行处理的情况。活动图对业务建模也比较有用,用来详细描述发生在业务活动中的过程。
-> 一个活动图的示例如下所示。
-
-
-
-下面描述组成活动图的元素。
-
-### 活动
-
-活动是行为参数化顺序的规范。活动被表示为圆角矩形,内含全部的动作,工作流和其他组成活动的元素。
-
-
-
-### 动作
-
-一个动作代表活动中的一个步骤。动作用圆角矩形表示。
-
-
-
-### 动作约束
-
-动作可以附带约束,下图显示了一个带前置条件和后置条件的动作。
-
-
-
-### 控制流
-
-控制流显示一个动作到下一个动作的流。表示为带箭头实线
-
-
-
-### 初始节点
-
-一个开始或起始点用大黑圆点表示,如下图。
-
-
-
-### 结束节点
-
-结束节点有两种类型:活动结束节点和流结束节点。活动结束节点表示为中心带黑点的圆环。
-
-
-
-流结束节点表示为内部为叉号的圆环。
-
-
-
-这两种不同类型节点的区别为:流结束节点表明单独的控制流的终点。活动结束终点是活动图内所有控制流的结束。
-
-### 对象和对象流
-
-对象流是对象和数据转递的通道。对象显示为矩形。
-
-
-
-对象流显示为带箭头的连接器,表明方向和通过的对象。
-
-
-
-一个对象流在它的至少一个终端有一个对象。在上图中,可以采用带输入输出引脚的速记标柱表示。
-
-
-
-数据存储显示为带 «datastore» 关键字的对象。
-
-
-
-### 判断节点和合并节点
-
-判断节点和合并节点是相同标注:菱形。它们可以被命名。从判断节点出来的控制流有监护条件,当监护条件满足时,可以对流控制。下图显示了判断节点和合并节点的使用。
-
-
-
-### 分叉和结合节点
-
-分叉和结合节点有同样的标柱:垂直或水平条(方向取决于工作流从左到右,还是从上到下)。它们说明了控制的并发线程的起始和终点,下图显示他们的使用示例。
-
-
-
-结合节点与合并节点不同之处在于:结合节点同步两个输入量,产生一个单独的输出量。来自结合节点的输出量要接收到所有的输入量后才能执行。合并节点直接将控制流传递通过。如果两个或更多的输入量到达合并节点。则它的输出流指定的动作会被执行两次或更多次。
-
-### 扩展域
-
-扩展域是会执行多次的结构活动域。输入输出扩展节点表示为一组“3 厢” ,代表多个选择项。关键词 "iterative", "parallel" 或 "stream"显示在区域的左上角
-
-
-
-### 异常处理器
-
-异常处理器在活动图中可以建模。
-
-
-
-### 可中断活动区
-
-可中断活动区环绕一组可以中断的动作。在下面非常简单的例子中: 当控制被传递到结束订单 "Close Order" 动作,定单处理"Process Order" 动作会执行直到完成,除非"Cancel Request"取消请求中断被接受,这会将控制传递给"Cancel Order"动作。
-
-
-
-### 分割
-
-一个活动分割显示为垂直或水平泳道。在下图中,分割被用来在活动图中分隔动作,有在 "accounting department"中执行的,有在 "customer"中执行的。
-
-
-
-## 状态机图
-
-> **状态机图(state-machine-diagram)对一个单独对象的行为建模,指明对象在它的整个生命周期里,响应不同事件时,执行相关事件的顺序。**
-
-如下示例, 下列的状态机图显示了门在它的整个生命周期里如何运作。
-
-
-
-门可以处于以下的三种状态之一: "Opened"打开状态, "Closed"关闭状态,或者"Locked"锁定状态。 它分别响应事件:“Open”开门, “Close”关门, “Lock”锁门 和 “Unlock”解锁。 注意:不是所有的事件,在所有的状态下都是有效的。如:一个门打开的时候是不可能锁定的,除非你关上门。并且,状态转移可能有附加监护条件:假设门是开的,如果“doorWay->isEmpty”(门是空的)被满足,那么它只能响应关门事件。状态机图使用的语法和约定将在下面的部分进行讨论。
-
-### 状态
-
-状态被表示为圆角矩形,状态名写在里面。
-
-
-
-### 起始和结束状态
-
-初始状态表示为实心黑圆环,可以标注名称。结束状态表示为中心带黑点圆环,也可以被标注名称。
-
-
-
-### 转移
-
-一个状态到下一个状态的转移表示为带箭头实线。转移可以有一个“Trigger”触发器,一个“Guard”监护条件和一个“effect”效果。如下所示:
-
-
-
-"Trigger"触发器是转移的起因,它可以是某个条件下的一个信号,一个事件,一个变化或一个时间通路。"Guard"监护是一个条件,而且必须为真,以便于让触发器引起转移。效果"Effect"是直接作用到对象上的一个动作,该对象具有做为转移结果的状态机。
-
-### 状态活动
-
-在上面的状态转移示例中,一个效果与该转移相关联。如果目标状态有多个转移到达,并且每一个转移都有相同的效果与它相关联,那最好将该效果与目标状态相关联,而不与转移相关联。你可以通过为这个状态定义初始动作来实现。下图显示了一个带入口动作和出口动作的状态。
-
-
-
-可以定义发生在事件上的动作或一直发生的动作。每一种类型的动作是可以定义任意数量的。
-
-### 自转移
-
-一个状态可能有一个返回到自身的转移,如下图。效果与转移关联是十分有帮助。
-
-
-
-### 复合状态
-
-一个状态机图可以有子状态机图,如下图所示:
-
-
-
-可选择不同方式显示相同信息,如下图所示:
-
-
-
-上面版本的标注说明"Check PIN"的子状态机图显示在单独的图中。
-
-### 入口点
-
-有时,你不想在正常的初始状态进入子状态机。例如下面的子状态机,它通常从"初始化"状态开始,但是如果因为某些原因,它不必执行初始化,可能靠转移到指定的入口点来从 "Ready" 状态开始。
-
-
-
-下图显示了状态机的上一层。
-
-
-
-### 出口点
-
-有与入口点相类似的方式,它可能也指定可选择的出口点。下图给出了主处理状态执行后,所执行状态的去向将取决于该状态转移时所使用的路径。
-
-
-
-### 选择伪状态
-
-选择伪状态显示为菱形,有一个转移输入,两个或多个输出。下图显示不管到达哪一个状态,经过选择伪状态后的去向,取决于在伪状态中执行时所选择的消息格式。
-
-
-
-### 连接伪状态
-
-连接伪状态用来将多个状态转移链接在一起。一个单独的连接伪状态可以有一个或多个输入和一个或多个输出,监护可能应用于每一个转移,连接是没有语义的。连接可以把一个输入转移分成多个输出转移来实现一个静态分支。与之对照的是选择伪状态实现一个动态条件分支。
-
-
-
-### 终止伪状态
-
-进入终止伪状态是指状态机生命线已经终止。终止伪状态表示为叉号。
-
-
-
-### 历史状态
-
-历史状态用来当状态机中断时,恢复状态机之前状态。下面例图说明了历史状态的使用。这个例子是关于洗衣机的状态机。
-
-在这个状态机中,当洗衣机运行时,它会按照"Washing" 到 Rinsing"再到"Spinning"来进行。如果电源被切断 ,洗衣机会停止运行并进入"Power Off" 状态。当电源恢复,运行状态在"History State"符号处进入,表示它会从上次离开的地方恢复。
-
-### 并发区
-
-一个状态可以被分成几个不同的区,包含同时存在和执行的子状态。下面的例子显示状态 "Applying Brakes", "front brake"和"rear brakes" 将同时独立运作。注意使用了分叉和结合伪状态而不是选择和合并伪状态。这些符号用来同步并发的线程。
-
-
-
-## 用例图
-
-> **用例图用来记录系统的需求,它提供系统与用户及其他参与者的一种通信手段。**
-
-### 执行者
-
-用例图显示了系统和系统外实体之间的交互。这些实体被引用为执行者。执行者代表角色,可以包括:用户,外部硬件和其他系统。执行者往往被画成简笔画小人。也可以用带«actor»关键字的类矩形表示。
-
-
-
-在下图中,执行者可以详细的泛化其他执行者:
-
-
-
-### 用例
-
-用例是有意义的单独工作单元。它向系统外部的人或事提供一个易于观察的高层次行为视图。 用例的标注符号是一个椭圆。
-
-
-
-使用用例的符号是带可选择箭头的连接线,箭头显示控制的方向。下图说明执行者 "Customer"使用 "Withdraw"用例。
-
-
-
-用途连接器(uses connector)可以有选择性的在每一个端点有多重性值,如下图,显示客户一次可能只执行一次取款交易。但是银行可以同时执行许多取款交易。
-
-
-
-### 用例定义
-
-一个典型的用例包括:
-
-- **名称和描述** - 用例通常用一个动词词组定义,而且有一个简短的文字说明。
-- **需求** - 需求定义了一个用例必须提供给终端用户的正式功能性需求。它们符合构造方法建立的功能性规范。一个需求是用例将执行一个动作或提供多个值给系统的约定或承诺。
-- **约束** - 一个约束是一个用例运行的条件或限制。它包括:前置条件,后置条件和不变化条件 。前置条件指明了用例在发生之前需要符合的条件。后置条件用来说明在用例执行之后一些条件必须为"真"。不变化条件说明用例整个执行过程中该条件始终为"真"。
-- **情形** - 情形是用例的实例在执行过程中,事件发生流程的形式描述。它定义了系统和外部执行者之间的事件指定顺序。通常用文本方式来表示,并对应时序图中的文字描述。
-- **情形图**
-- **附加信息**
-
-### 包含用例
-
-用例可能包含其他用例的功能来作为它正常处理的一部分。通常它假设,任何被包含的用例在基本程序运行时每一次都会被调用。下面例子:用例“卡的确认” 在运行时,被用例“取钱”当作一个子部分。
-
-
-
-用例可以被一个或多个用例包含。通过提炼通用的行为,将它变成可以多次重复使用的用例。有助于降低功能重复级别。
-
-### 扩展用例
-
-一个用例可以被用来扩展另一个用例的行为,通常使用在特别情况下。例如:假设在修改一个特别类型的客户订单之前,用户必须得到某种更高级别的许可,然后“获得许可”用例将有选择的扩展常规的“修改订单”用例。
-
-
-
-**扩展点** - 扩展用例的加入点被定义为扩展点。
-
-
-
-**系统边界** - 它用来显示用例在系统内部,执行者在系统的外部。
-
-
-
-## 通信图
-
-> 通信图,以前称之为协作图,是一种交互图,**所显示消息与时序图相似,但是它更侧重于对象间的联系**。
-
-在通信图中,对象之间显示关联连接器。消息附加到这些关联上,显示短箭头指向消息流的方向。消息的顺序通过编号码显示。
-
-下面的两个图用通信图和时序图分别显示相同的信息。尽管我们可能从通信图的编号码得到消息顺序,但它不是立即可见的。通信图十分清楚的显示了邻近对象间全部完整的消息传递。
-
-
-
-
-
-## 交互概述图
-
-> **一个交互概览图是活动图的一种形式,它的节点代表交互图。交互图包含时序图,通信图,交互概览图和时间图。 大多数交互概览图标注与活动图一样。例如:起始,结束,判断,合并,分叉和结合节点是完全相同。并且,交互概览图介绍了两种新的元素:交互发生和交互元素。**
-
-### 交互发生
-
-交互发生引用现有的交互图。显示为一个引用框,左上角显示 "ref" 。被引用的图名显示在框的中央。
-
-
-
-### 交互元素
-
-交互元素与交互发生相似之处在于都是在一个矩形框中显示一个现有的交互图。不同之处在内部显示参考图的内容不同。
-
-
-
-### 将它们放在一起
-
-所有的活动图控件,都可以相同地被使用于交互概览图,如:分叉,结合,合并等等。它把控制逻辑放入较低一级的图中。下面的例子就说明了一个典型的销售过程。子过程是从交互发生抽象而来。
-
-
-
-## 时序图
-
-> **时序图是交互图的一种形式,它显示对象沿生命线发展,对象之间随时间的交互表示为从源生命线指向目标生命线的消息。时序图能很好地显示那些对象与其它那些对象通信,什么消息触发了这些通信,时序图不能很好显示复杂过程的逻辑。**
-
-### 生命线
-
-一条生命线在时序图中代表一个独立的参与者。表示为包含对象名的矩形,如果它的名字是"self",则说明该生命线代表控制带时序图的类元。
-
-
-
-有时,时序图会包含一个顶端是执行者的生命线。这情况说明掌握这个时序图的是用例。健壮图中的边界,控制和实体元素也可以有生命线。
-
-
-
-### 消息
-
-消息显示为箭头。消息可以完成传输,也可能丢失和找回,它可以是同步的,也可以是异步的,即可以是调用,也可以是信号。在下图中,第一条消息是同步消息(标为实箭头)完成传输,并隐含一条返回消息。第二条消息是异步消息 (标为实线箭头),第三条是异步返回消息(标为虚线)。
-
-
-
-### 执行发生
-
-向下延伸的细条状矩形表示执行事件或控制焦点的激活。在上图中有三个执行事件。第一个是源对象发送两条消息和收到两条回复。第二个是目标对象收到一条同步消息并返回一条回复。第三个是目标对象收到一条异步消息并返回一条回复。
-
-### 内部通信
-
-内部消息表现为一个操作的递归调用,或一个方法调用属于同一个对象的其他方法。显示为生命线上执行事件的嵌套控制焦点。
-
-
-
-### 迷路消息和拾取消息
-
-迷路消息是那些发送了却没有到达指定接收者,或者到达的接收者不再当前图中。拾取消息是收到来自那些未知的发送者,或者来自没有显示在当前图的发送者的消息。它们都表明是去往或来自一个终点元素。
-
-
-
-### 生命线开始与结束
-
-生命线可以在时序图时间刻度范围内创建和销毁,在下面的例子中,生命线被停止符号(叉号)终止。在前面的例子中,生命线顶端的符号(Child)显示在比创建它的对象符号(parent)沿页面要低的位置上。下图显示创建和终止对象。
-
-
-
-### 时间和期限约束
-
-消息默认显示为水平线。因为生命线显示为沿屏幕向下的时间通道,所以当给实时系统建模,或是有时间约束的业务过程建模,考虑执行动作所需时间长度是很重要的。因此可以给消息设置一个期限约束,这样的消息显示为下斜线。
-
-
-
-### 复合片段
-
-如前面所说,时序图不适合表达复杂的过程逻辑。在一种情况下,有许多机制允许把一定程度的过程逻辑加入到图中,并把它们放到复合片段的标题下。复合片段是一个或多个处理顺序被包含在一个框架中,并在指定名称的环境下执行。片段可以是:
-
-- 选择性片段 (显示 “alt”) 为 if…then…else 结构建模。
-- 选项片段 (显示 “opt”) 为 "switch"(开关) 结构建模。
-- 中断片段对被处理事件的可选择顺序建模,而不是该图的其他部分。
-- 并行片段(显示 “par”) 为并发处理建模。
-- 弱顺序片段 (显示 “seq”) 包含了一组消息,这组消息必须在后继片段开始之前被处理。但不会把片段内消息的先后顺序强加到不共享同一条生命线的消息上。
-- 严格顺序片段 (显示 “strict”) 包含了一系列需要按照给定顺序处理的消息。
-- 非片段 (显示 “neg”) 包含了一系列不可用的消息。
-- 关键片段 具有关键部分。
-- 忽略片段 声明一个没有意义的消息,如果它出现在当前上下文中。
-- 考虑片段与忽略片段相反,不包含在考虑片段内的消息都应该被忽略。
-- 断言片段 (显示 “assert”)标明任何没有显示为声明操作数的顺序都是无效的。
-- 循环片段 包含一系列被重复的消息。
-
-下图显示的是循环片段:
-
-
-
-这也是一个类似于复合片段的交互发生。 交互发生被其他图参考,显示为左上角带"ref",将被参考图名显示在方框的中间。
-
-### 门
-
-门是连接片段内消息和片段外消息的连接点。 在 EA 中,门显示为片段框架上的小正方形。作用为时序图与页面外的连接器。 用来表示进来的消息源,或者出去消息的终点。下面两个图显示它们在实践中的使用。注意:" top level diagram"中的门用消息箭头指向参考片段,在这里没有必要把它画成方块。
-
-
-
-
-
-### 部分分解
-
-一个对象可以引出多条生命线,使得对象内部和对象之间的消息显示在同一图上。
-
-
-
-### 状态常量/延续
-
-状态常量是生命线的约束,运行时始终为"真"。显示为两侧半圆的矩形,如下图:
-
-
-
-延续虽与状态常量有同样的标注,但是被用于复合片段,并可以延伸跨越多条生命线。
-
-## 时间图
-
-> **UML 时间图被用来显示随时间变化,一个或多个元素的值或状态的更改。也显示时控事件之间的交互和管理它们的时间和期限约束。**
-
-### 状态生命线
-
-状态生命线显示随时间变化,一个单项状态的改变。不论时间单位如何选择,X 轴显示经过的时间,Y 轴被标为给出状态的列表。状态生命线如下所示:
-
-
-
-### 值生命线
-
-值生命线显示随时间变化,一个单项的值的变化。X 轴显示经过的时间,时间单位为任意,和状态生命线一样。平行线之间显示值,每次值变化,平行线交叉。如下图所示。
-
-
-
-### 将它们放在一起
-
-状态和值的生命线能叠加组合。它们必须有相同的 X 轴。 消息可以从一个生命线传递到另一个。每一个状态和值的变换能有一个定义的事件,一个时间限制是指一个事件何时必须发生,和一个期限限制说明状态或值多长时间必须有效。一旦这些已经被应用,其时间图可能显示如下。
-
-
-
-## 参考资料
-
-- https://sparxsystems.cn/resources/uml2_tutorial/index.html
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/TODO\346\250\241\346\235\277.xlsx" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/TODO\346\250\241\346\235\277.xlsx"
deleted file mode 100644
index 152af373d1..0000000000
Binary files "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/TODO\346\250\241\346\235\277.xlsx" and /dev/null differ
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\346\200\235\347\273\264\345\257\274\345\233\276\347\262\276\351\200\211\351\205\215\350\211\262\346\250\241\346\235\277.xmind" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\346\200\235\347\273\264\345\257\274\345\233\276\347\262\276\351\200\211\351\205\215\350\211\262\346\250\241\346\235\277.xmind"
deleted file mode 100644
index aad65fc1fa..0000000000
Binary files "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\346\200\235\347\273\264\345\257\274\345\233\276\347\262\276\351\200\211\351\205\215\350\211\262\346\250\241\346\235\277.xmind" and /dev/null differ
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\346\246\202\350\246\201\350\256\276\350\256\241\346\250\241\346\235\277.docx" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\346\246\202\350\246\201\350\256\276\350\256\241\346\250\241\346\235\277.docx"
deleted file mode 100644
index eeecb956b7..0000000000
Binary files "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\346\246\202\350\246\201\350\256\276\350\256\241\346\250\241\346\235\277.docx" and /dev/null differ
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\256\272\346\226\207\346\250\241\346\235\277.docx" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\256\272\346\226\207\346\250\241\346\235\277.docx"
deleted file mode 100644
index 04759a9e7c..0000000000
Binary files "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\256\272\346\226\207\346\250\241\346\235\277.docx" and /dev/null differ
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\257\246\347\273\206\350\256\276\350\256\241\346\250\241\346\235\277.docx" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\257\246\347\273\206\350\256\276\350\256\241\346\250\241\346\235\277.docx"
deleted file mode 100644
index 2bbfcce8fe..0000000000
Binary files "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\257\246\347\273\206\350\256\276\350\256\241\346\250\241\346\235\277.docx" and /dev/null differ
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\257\246\347\273\206\350\256\276\350\256\241\350\257\264\346\230\216\344\271\246\346\250\241\346\235\277.docx" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\257\246\347\273\206\350\256\276\350\256\241\350\257\264\346\230\216\344\271\246\346\250\241\346\235\277.docx"
deleted file mode 100644
index 1bdefc48ba..0000000000
Binary files "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\350\257\246\347\273\206\350\256\276\350\256\241\350\257\264\346\230\216\344\271\246\346\250\241\346\235\277.docx" and /dev/null differ
diff --git "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\351\202\256\344\273\266\346\250\241\346\235\277.md" "b/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\351\202\256\344\273\266\346\250\241\346\235\277.md"
deleted file mode 100644
index 9ca66ff84c..0000000000
--- "a/design/\346\210\221\347\232\204\345\274\200\345\217\221\346\226\207\346\241\243\346\250\241\346\235\277/\351\202\256\344\273\266\346\250\241\346\235\277.md"
+++ /dev/null
@@ -1,40 +0,0 @@
-# 邮件模板
-
-> 工作邮件模板
->
-> 中文字体推荐使用**微软雅黑**。
->
-> 英文字体推荐使用**Arial**、**Times New Roman** 。
-
-## 英文模板
-
-```
-Hi, Dear XXX
-
-... ...
-Best Regards
-=============================================
-XXX
-Position:XXXX
-Department:XXXX
-Phone:XXXX
-Email:XXXX@XXXX
-=============================================
-```
-
-## 中文模板
-
-```
-XXX,您好!
-
-。。。 。。。
-此致
- 敬礼!
-=============================================
-XXX
-岗位:XXXX
-部门:XXXX
-分机:XXXX
-邮箱:XXXX@XXXX
-=============================================
-```
diff --git a/frontend/README.md b/frontend/README.md
deleted file mode 100644
index a13e61a1e8..0000000000
--- a/frontend/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# 前端技术
-
-## Base
-
-> 前端 web 技术的基石: `html` + `css` + `js`
->
-> - HTML 定义了网页的内容。
-> - CSS 定义了网页的样式。
-> - JavaScript 定义了网页的行为。
-
-- [Html](base/html.md)
-- [Css](base/css.md)
-- [Javascript](base/js.md)
-
-## Nodejs, Npm, Yarn
-
-> Nodejs 和包管理器
->
-> 流行: `npm`, `yarn`
-
-- [Nodejs](nodejs/nodejs.md) - 关键词: `nodejs`, `REPL`, `require`, `exports`
-- [Npm](nodejs/npm.md) - 关键词: `nodejs`, `包管理`, `npm`, `cnpm`, `package.json`, `node_modules`
-- [Yarn](nodejs/yarn.md) - 关键词: `nodejs`, `包管理`, `yarn`, `yarn.lock`
-
-## ES6, TypeScript, Babel
-
-> 下一代 Javascript 语言
-
-- [ES6](es6/es6.md) - 关键词: `ES6`, `ECMAScript`, `arrow`, `this`, `let`, `const`, `class`, `extends`, `super`, `arrow` ...
-- [Babel](es6/babel.md) - 关键词: `babel-cli`, `.babelrc`, `preset`, `polyfill`
-- [TypeScript](es6/typescript.md) - 关键词: `typescript`, `tsc`
-
-## Bundlers
-
-> 捆绑资源管理器
->
-> 流行: `Webpack`
-
-- [Webpack 入门](webpack/webpack.md)
-- [Webpack 概念](webpack/concept.md)
-- [Webpack 资源管理](webpack/asset-management.md)
-- [Webpack 代码分离](webpack/code-splitting.md)
-- [Webpack 开发工具](webpack/development.md)
-
-## QA
-
-> 静态检查工具、代码格式化工具
->
-> 流行: `JSLint`, `JSHint`, `ESLint`, `Prettier`, `Standard`, `TSlint`
-
-- [JavaScript QA 工具总结](qa/qa-summary.md)
-- [eslint](qa/eslint.md)
-
-## MVC - React, Vue, Angular
-
-- [Angular,React,Vue 比较](mvc/Angular,React,Vue比较.md)
-
-### React 技术栈
-
-- [React](mvc/react/react.md)
-
-### Vue 技术栈
-
-### Angular 技术栈
-
-- [Angular](mvc/angular/angular.md)
diff --git a/frontend/base/README.md b/frontend/base/README.md
deleted file mode 100644
index d31de06635..0000000000
--- a/frontend/base/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Base - Html, Css, Javascript
-
-> 前端 web 技术的基石:html + css + js
->
-> - HTML 定义了网页的内容。
-> - CSS 定义了网页的样式。
-> - JavaScript 定义了网页的行为。
-
-## :book: 本章内容
-
-建议学习路线:html -> css -> js
-
-- [Html](html.md)
-- [Css](css.md)
-- [Javascript](js.md)
-
-## :books: 拓展阅读
-
-- [**Css 教程**](https://github.com/dunwu/css-tutorial)
-- [**JavaScript 教程**](https://github.com/dunwu/js-tutorial)
-
-## :door: 传送门
-
-:point_right: [**前端技术指南**](https://github.com/dunwu/frontend-tutorial)
diff --git a/frontend/base/css.md b/frontend/base/css.md
deleted file mode 100644
index 8d069759fd..0000000000
--- a/frontend/base/css.md
+++ /dev/null
@@ -1,1053 +0,0 @@
-# CSS 快速入门
-
-> 层叠样式表(英语:Cascading Style Sheets,简写 CSS),又称串樣式列表、级联样式表、串接样式表、階層式樣式表,一种用来为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体、间距和颜色等)的计算机语言。
->
-> 关键词: css
-
-
-
-- [简介](#简介)
-- [框模型](#框模型)
- - [圆角边框](#圆角边框)
- - [边框阴影](#边框阴影)
- - [边框图片](#边框图片)
-- [样式](#样式)
- - [背景](#背景)
- - [文本](#文本)
- - [字体](#字体)
-- [布局](#布局)
- - [创建多列](#创建多列)
- - [规定列之间的间隔](#规定列之间的间隔)
- - [列规则](#列规则)
- - [列属性](#列属性)
-- [转换](#转换)
- - [2D 转换](#2d-转换)
- - [3D 转换](#3d-转换)
-- [过渡](#过渡)
- - [它如何工作?](#它如何工作)
- - [多项改变](#多项改变)
- - [过渡属性](#过渡属性)
-- [动画](#动画)
- - [@keyframes 规则](#keyframes-规则)
- - [什么是 CSS3 中的动画?](#什么是-css3-中的动画)
- - [动画属性](#动画属性)
-- [引申和引用](#引申和引用)
-
-
-
-CSS3 是最新的 CSS 标准。
-
-CSS3 完全向后兼容,因此您不必改变现有的设计。浏览器通常支持 CSS2。
-
-## 简介
-
-CSS3 被划分为模块。
-
-其中最重要的 CSS3 模块包括:
-
-- 选择器
-- 框模型
-- 样式
-- 布局
-- 2D/3D 转换
-- 动画
-- 用户界面
-
-## 框模型
-
-通过 CSS3,您能够创建圆角边框,向矩形添加阴影,使用图片来绘制边框 - 并且不需使用设计软件,比如 PhotoShop。
-
-在本章中,您将学到以下边框属性:
-
-- border-radius
-- box-shadow
-- border-image
-
-### 圆角边框
-
-在 CSS2 中添加圆角矩形需要技巧。我们必须为每个圆角使用不同的图片。
-
-在 CSS3 中,创建圆角是非常容易的。
-
-在 CSS3 中,`border-radius` 属性用于创建圆角:
-
-这个矩形有圆角哦!
-
-**实例**
-
-向 div 元素添加圆角:
-
-```css
-div {
- border: 2px solid;
- border-radius: 25px;
- -moz-border-radius: 25px; /* Old Firefox */
-}
-```
-
-### 边框阴影
-
-在 CSS3 中,`box-shadow` 用于向方框添加阴影:
-
-**实例**
-
-向 div 元素添加方框阴影:
-
-```css
-div {
- box-shadow: 10px 10px 5px #888888;
-}
-```
-
-### 边框图片
-
-通过 CSS3 的 border-image 属性,您可以使用图片来创建边框:
-
-border-image 属性允许您规定用于边框的图片!
-
-用于创建上面的边框的原始图片:
-
-**实例**
-
-使用图片创建围绕 div 元素的边框:
-
-```
-div
-{
-border-image:url(border.png) 30 30 round;
--moz-border-image:url(border.png) 30 30 round; /* 老的 Firefox */
--webkit-border-image:url(border.png) 30 30 round; /* Safari 和 Chrome */
--o-border-image:url(border.png) 30 30 round; /* Opera */
-}
-```
-
-## 样式
-
-### 背景
-
-CSS3 包含多个新的背景属性,它们提供了对背景更强大的控制。
-
-在本章,您将学到以下背景属性:
-
-- background-size
-- background-origin
-
-您也将学到如何使用多重背景图片。
-
-#### background-size 属性
-
-`background-size` 属性规定背景图片的尺寸。
-
-在 CSS3 之前,背景图片的尺寸是由图片的实际尺寸决定的。在 CSS3 中,可以规定背景图片的尺寸,这就允许我们在不同的环境中重复使用背景图片。
-
-您能够以像素或百分比规定尺寸。如果以百分比规定尺寸,那么尺寸相对于父元素的宽度和高度。
-
-**实例:调整背景图片的大小**
-
-```css
-div {
- background: url(bg_flower.gif);
- -moz-background-size: 63px 100px; /* 老版本的 Firefox */
- background-size: 63px 100px;
- background-repeat: no-repeat;
-}
-```
-
-**实例:对背景图片进行拉伸,使其完成填充内容区域**
-
-```css
-div {
- background: url(bg_flower.gif);
- -moz-background-size: 40% 100%; /* 老版本的 Firefox */
- background-size: 40% 100%;
- background-repeat: no-repeat;
-}
-```
-
-#### background-origin 属性
-
-`background-origin` 属性规定背景图片的定位区域。
-
-背景图片可以放置于 content-box、padding-box 或 border-box 区域。
-
-
-
-**实例:在 content-box 中定位背景图片**
-
-```css
-div {
- background: url(bg_flower.gif);
- background-repeat: no-repeat;
- background-size: 100% 100%;
- -webkit-background-origin: content-box; /* Safari */
- background-origin: content-box;
-}
-```
-
-#### 多重背景图片
-
-CSS3 允许您为元素使用多个背景图像。
-
-**实例:为 body 元素设置两幅背景图片**
-
-```css
-body {
- background-image: url(bg_flower.gif), url(bg_flower_2.gif);
-}
-```
-
-### 文本
-
-CSS3 包含多个新的文本特性。
-
-在本章中,您将学到如下文本属性:
-
-- text-shadow
-- word-wrap
-
-#### 文本阴影
-
-在 CSS3 中,text-shadow 可向文本应用阴影。
-
-您能够规定水平阴影、垂直阴影、模糊距离,以及阴影的颜色:
-
-**实例:向标题添加阴影**
-
-```css
-h1 {
- text-shadow: 5px 5px 5px #ff0000;
-}
-```
-
-#### 自动换行
-
-单词太长的话就可能无法超出某个区域:
-
-This paragraph contains a very long word: thisisaveryveryveryveryveryverylongword. The long word will break and wrap to the next line.
-
-在 CSS3 中,word-wrap 属性允许您允许文本强制文本进行换行 - 即使这意味着会对单词进行拆分:
-
-This paragraph contains a very long word: thisisaveryveryveryveryveryverylongword. The long word will break and wrap to the next line.
-
-下面是 CSS 代码:
-
-**实例:允许对长单词进行拆分,并换行到下一行**
-
-```css
-p {
- word-wrap: break-word;
-}
-```
-
-### 字体
-
-#### @font-face 规则
-
-在 CSS3 之前,web 设计师必须使用已在用户计算机上安装好的字体。
-
-通过 CSS3,web 设计师可以使用他们喜欢的任意字体。
-
-当您您找到或购买到希望使用的字体时,可将该字体文件存放到 web 服务器上,它会在需要时被自动下载到用户的计算机上。
-
-您“自己的”的字体是在 CSS3 @font-face 规则中定义的。
-
-#### 使用您需要的字体
-
-在新的 `@font-face` 规则中,您必须首先定义字体的名称(比如 myFirstFont),然后指向该字体文件。
-
-如需为 HTML 元素使用字体,请通过 `font-family` 属性来引用字体的名称 (myFirstFont):
-
-实例
-
-```css
-
-```
-
-#### 使用粗体字体
-
-您必须为粗体文本添加另一个包含描述符的 `@font-face`:
-
-实例
-
-```css
-@font-face {
- font-family: myFirstFont;
- src: url("../../assets/fonts/sansation-light.ttf"); /* IE9+ */
-}
-
-@font-face {
- font-family: myFirstFont;
- src: url("../../assets/fonts/sansation-bold.ttf"); /* IE9+ */
- font-weight: bold;
-}
-```
-
-文件 "Sansation_Bold.ttf" 是另一个字体文件,它包含了 Sansation 字体的粗体字符。
-
-只要 font-family 为 "myFirstFont" 的文本需要显示为粗体,浏览器就会使用该字体。
-
-通过这种方式,我们可以为相同的字体设置许多 @font-face 规则。
-
-#### 字体描述符
-
-下面的表格列出了能够在 @font-face 规则中定义的所有字体描述符:
-
-| 描述符 | 值 | 描述 |
-| ------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
-| font-family | _name_ | 必需。规定字体的名称。 |
-| src | _URL_ | 必需。定义字体文件的 URL。 |
-| font-stretch | normalcondensedultra-condensedextra-condensedsemi-condensedexpandedsemi-expandedextra-expandedultra-expanded | 可选。定义如何拉伸字体。默认是 "normal"。 |
-| font-style | ormalitalicoblique | 可选。定义字体的样式。默认是 "normal"。 |
-| font-weight | normalbold100200300400500600700800900 | 可选。定义字体的粗细。默认是 "normal"。 |
-| unicode-range | _unicode-range_ | 可选。定义字体支持的 UNICODE 字符范围。默认是 "U+0-10FFFF"。 |
-
-## 布局
-
-通过 CSS3,您能够创建多个列来对文本进行布局 - 就像报纸那样!
-
-在本章中,您将学习如下多列属性:
-
-- column-count
-- column-gap
-- column-rule
-
-### 创建多列
-
-`column-count` 属性规定元素应该被分隔的列数:
-
-**实例:把 div 元素中的文本分隔为三列**
-
-```css
-div {
- -moz-column-count: 3; /* Firefox */
- -webkit-column-count: 3; /* Safari and Chrome */
- column-count: 3;
-}
-```
-
-### 规定列之间的间隔
-
-`column-gap` 属性规定列之间的间隔:
-
-**实例:规定列之间 40 像素的间隔**
-
-```css
-div {
- -moz-column-gap: 30px; /* Firefox */
- -webkit-column-gap: 30px; /* Safari and Chrome */
- column-gap: 30px;
-}
-```
-
-### 列规则
-
-`column-rule` 属性设置列之间的宽度、样式和颜色规则。
-
-实例
-
-规定列之间的宽度、样式和颜色规则:
-
-```css
-div {
- -moz-column-rule: 3px outset #ff0000; /* Firefox */
- -webkit-column-rule: 3px outset #ff0000; /* Safari and Chrome */
- column-rule: 3px outset #ff0000;
-}
-```
-
-### 列属性
-
-| 属性 | 描述 |
-| ----------------- | -------------------------------------------------- |
-| column-count | 规定元素应该被分隔的列数。 |
-| column-fill | 规定如何填充列。 |
-| column-gap | 规定列之间的间隔。 |
-| column-rule | 设置所有 column-rule-\* 属性的简写属性。 |
-| column-rule-color | 规定列之间规则的颜色。 |
-| column-rule-style | 规定列之间规则的样式。 |
-| column-rule-width | 规定列之间规则的宽度。 |
-| column-span | 规定元素应该横跨的列数。 |
-| column-width | 规定列的宽度。 |
-| columns | 规定设置 column-width 和 column-count 的简写属性。 |
-
-## 转换
-
-通过 CSS3 转换,我们能够对元素进行移动、缩放、转动、拉长或拉伸。
-
-转换是使元素改变形状、尺寸和位置的一种效果。
-
-您可以使用 2D 或 3D 转换来转换您的元素。
-
-### 2D 转换
-
-在本章中,您将学到如下 2D 转换方法:
-
-- translate()
-- rotate()
-- scale()
-- skew()
-- matrix()
-
-**实例**
-
-```css
-div {
- transform: rotate(30deg);
- -ms-transform: rotate(30deg); /* IE 9 */
- -webkit-transform: rotate(30deg); /* Safari and Chrome */
- -o-transform: rotate(30deg); /* Opera */
- -moz-transform: rotate(30deg); /* Firefox */
-}
-```
-
-#### translate() 方法
-
-通过 `translate()` 方法,元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数:
-
-**实例**
-
-```css
-div {
- transform: translate(50px, 100px);
- -ms-transform: translate(50px, 100px); /* IE 9 */
- -webkit-transform: translate(50px, 100px); /* Safari and Chrome */
- -o-transform: translate(50px, 100px); /* Opera */
- -moz-transform: translate(50px, 100px); /* Firefox */
-}
-```
-
-值 translate(50px,100px) 把元素从左侧移动 50 像素,从顶端移动 100 像素。
-
-#### rotate() 方法
-
-通过 `rotate()` 方法,元素顺时针旋转给定的角度。允许负值,元素将逆时针旋转。
-
-**实例**
-
-```css
-div {
- transform: rotate(30deg);
- -ms-transform: rotate(30deg); /* IE 9 */
- -webkit-transform: rotate(30deg); /* Safari and Chrome */
- -o-transform: rotate(30deg); /* Opera */
- -moz-transform: rotate(30deg); /* Firefox */
-}
-```
-
-值 rotate(30deg) 把元素顺时针旋转 30 度。
-
-#### scale() 方法
-
-通过 `scale()` 方法,元素的尺寸会增加或减少,根据给定的宽度(X 轴)和高度(Y 轴)参数:
-
-**实例**
-
-```css
-div {
- transform: scale(2, 4);
- -ms-transform: scale(2, 4); /* IE 9 */
- -webkit-transform: scale(2, 4); /* Safari 和 Chrome */
- -o-transform: scale(2, 4); /* Opera */
- -moz-transform: scale(2, 4); /* Firefox */
-}
-```
-
-值 scale(2,4) 把宽度转换为原始尺寸的 2 倍,把高度转换为原始高度的 4 倍。
-
-#### skew() 方法
-
-通过 `skew()` 方法,元素翻转给定的角度,根据给定的水平线(X 轴)和垂直线(Y 轴)参数:
-
-**实例**
-
-```css
-div {
- transform: skew(30deg, 20deg);
- -ms-transform: skew(30deg, 20deg); /* IE 9 */
- -webkit-transform: skew(30deg, 20deg); /* Safari and Chrome */
- -o-transform: skew(30deg, 20deg); /* Opera */
- -moz-transform: skew(30deg, 20deg); /* Firefox */
-}
-```
-
-值 skew(30deg,20deg) 围绕 X 轴把元素翻转 30 度,围绕 Y 轴翻转 20 度。
-
-#### matrix() 方法
-
-`matrix()` 方法把所有 2D 转换方法组合在一起。
-
-`matrix()` 方法需要六个参数,包含数学函数,允许您:旋转、缩放、移动以及倾斜元素。
-
-**实例**
-
-如何使用 matrix 方法将 div 元素旋转 30 度:
-
-```css
-div {
- transform: matrix(0.866, 0.5, -0.5, 0.866, 0, 0);
- -ms-transform: matrix(0.866, 0.5, -0.5, 0.866, 0, 0); /* IE 9 */
- -moz-transform: matrix(0.866, 0.5, -0.5, 0.866, 0, 0); /* Firefox */
- -webkit-transform: matrix(
- 0.866,
- 0.5,
- -0.5,
- 0.866,
- 0,
- 0
- ); /* Safari and Chrome */
- -o-transform: matrix(0.866, 0.5, -0.5, 0.866, 0, 0); /* Opera */
-}
-```
-
-#### 新的转换属性
-
-下面的表格列出了所有的转换属性:
-
-| 属性 | 描述 |
-| ---------------- | ---------------------------- |
-| transform | 向元素应用 2D 或 3D 转换。 |
-| transform-origin | 允许你改变被转换元素的位置。 |
-
-#### 2D Transform 方法
-
-| 函数 | 描述 |
-| ------------------------------- | ---------------------------------------- |
-| matrix(_n_,_n_,_n_,_n_,_n_,_n_) | 定义 2D 转换,使用六个值的矩阵。 |
-| translate(_x_,_y_) | 定义 2D 转换,沿着 X 和 Y 轴移动元素。 |
-| translateX(_n_) | 定义 2D 转换,沿着 X 轴移动元素。 |
-| translateY(_n_) | 定义 2D 转换,沿着 Y 轴移动元素。 |
-| scale(_x_,_y_) | 定义 2D 缩放转换,改变元素的宽度和高度。 |
-| scaleX(_n_) | 定义 2D 缩放转换,改变元素的宽度。 |
-| scaleY(_n_) | 定义 2D 缩放转换,改变元素的高度。 |
-| rotate(_angle_) | 定义 2D 旋转,在参数中规定角度。 |
-| skew(_x-angle_,_y-angle_) | 定义 2D 倾斜转换,沿着 X 和 Y 轴。 |
-| skewX(_angle_) | 定义 2D 倾斜转换,沿着 X 轴。 |
-| skewY(_angle_) | 定义 2D 倾斜转换,沿着 Y 轴。 |
-
-### 3D 转换
-
-CSS3 允许您使用 3D 转换来对元素进行格式化。
-
-在本章中,您将学到其中的一些 3D 转换方法:
-
-- rotateX()
-- rotateY()
-
-点击下面的元素,来查看 2D 转换与 3D 转换之间的不同之处:
-
-#### rotateX() 方法
-
-通过 `rotateX()` 方法,元素围绕其 X 轴以给定的度数进行旋转。
-
-**实例**
-
-```css
-div {
- transform: rotateX(120deg);
- -webkit-transform: rotateX(120deg); /* Safari 和 Chrome */
- -moz-transform: rotateX(120deg); /* Firefox */
-}
-```
-
-#### rotateY() 旋转
-
-通过 rotateY() 方法,元素围绕其 Y 轴以给定的度数进行旋转。
-
-实例
-
-```css
-div {
- transform: rotateY(130deg);
- -webkit-transform: rotateY(130deg); /* Safari 和 Chrome */
- -moz-transform: rotateY(130deg); /* Firefox */
-}
-```
-
-#### 转换属性
-
-下面的表格列出了所有的转换属性:
-
-| 属性 | 描述 |
-| ------------------- | ------------------------------------ |
-| transform | 向元素应用 2D 或 3D 转换。 |
-| transform-origin | 允许你改变被转换元素的位置。 |
-| transform-style | 规定被嵌套元素如何在 3D 空间中显示。 |
-| perspective | 规定 3D 元素的透视效果。 |
-| perspective-origin | 规定 3D 元素的底部位置。 |
-| backface-visibility | 定义元素在不面对屏幕时是否可见。 |
-
-## 过渡
-
-通过 CSS3,我们可以在不使用 Flash 动画或 JavaScript 的情况下,当元素从一种样式变换为另一种样式时为元素添加效果。
-
-### 它如何工作?
-
-CSS3 过渡是元素从一种样式逐渐改变为另一种的效果。
-
-要实现这一点,必须规定两项内容:
-
-- 规定您希望把效果添加到哪个 CSS 属性上
-- 规定效果的时长
-
-**实例**
-
-应用于宽度属性的过渡效果,时长为 2 秒:
-
-```css
-div {
- transition: width 2s;
- -moz-transition: width 2s; /* Firefox 4 */
- -webkit-transition: width 2s; /* Safari 和 Chrome */
- -o-transition: width 2s; /* Opera */
-}
-```
-
-注释:如果时长未规定,则不会有过渡效果,因为默认值是 0。
-
-效果开始于指定的 CSS 属性改变值时。CSS 属性改变的典型时间是鼠标指针位于元素上时:
-
-**实例**
-
-规定当鼠标指针悬浮于 `` 元素上时:
-
-```css
-div:hover {
- width: 300px;
-}
-```
-
-注释:当指针移出元素时,它会逐渐变回原来的样式。
-
-### 多项改变
-
-如需向多个样式添加过渡效果,请添加多个属性,由逗号隔开:
-
-**实例**
-
-向宽度、高度和转换添加过渡效果:
-
-```css
-div {
- transition: width 2s, height 2s, transform 2s;
- -moz-transition: width 2s, height 2s, -moz-transform 2s;
- -webkit-transition: width 2s, height 2s, -webkit-transform 2s;
- -o-transition: width 2s, height 2s, -o-transform 2s;
-}
-```
-
-### 过渡属性
-
-下面的表格列出了所有的转换属性:
-
-| 属性 | 描述 |
-| -------------------------- | -------------------------------------------- |
-| transition | 简写属性,用于在一个属性中设置四个过渡属性。 |
-| transition-property | 规定应用过渡的 CSS 属性的名称。 |
-| transition-duration | 定义过渡效果花费的时间。默认是 0。 |
-| transition-timing-function | 规定过渡效果的时间曲线。默认是 "ease"。 |
-| transition-delay | 规定过渡效果何时开始。默认是 0。 |
-
-下面的两个例子设置所有过渡属性:
-
-**实例:在一个例子中使用所有过渡属性**
-
-```css
-div {
- transition-property: width;
- transition-duration: 1s;
- transition-timing-function: linear;
- transition-delay: 2s;
- /* Firefox 4 */
- -moz-transition-property: width;
- -moz-transition-duration: 1s;
- -moz-transition-timing-function: linear;
- -moz-transition-delay: 2s;
- /* Safari 和 Chrome */
- -webkit-transition-property: width;
- -webkit-transition-duration: 1s;
- -webkit-transition-timing-function: linear;
- -webkit-transition-delay: 2s;
- /* Opera */
- -o-transition-property: width;
- -o-transition-duration: 1s;
- -o-transition-timing-function: linear;
- -o-transition-delay: 2s;
-}
-```
-
-**实例:与上面的例子相同的过渡效果,但是使用了简写的 transition 属性**
-
-```css
-div {
- transition: width 1s linear 2s;
- /* Firefox 4 */
- -moz-transition: width 1s linear 2s;
- /* Safari and Chrome */
- -webkit-transition: width 1s linear 2s;
- /* Opera */
- -o-transition: width 1s linear 2s;
-}
-```
-
-## 动画
-
-通过 CSS3,我们能够创建动画,这可以在许多网页中取代动画图片、Flash 动画以及 JavaScript。
-
-### @keyframes 规则
-
-如需在 CSS3 中创建动画,您需要学习 `@keyframes` 规则。
-
-`@keyframes` 规则用于创建动画。在 `@keyframes` 中规定某项 CSS 样式,就能创建由当前样式逐渐改为新样式的动画效果。
-
-**实例**
-
-```css
-@keyframes myfirst {
- from {
- background: red;
- }
- to {
- background: yellow;
- }
-}
-
-/* Firefox */
-@-moz-keyframes myfirst {
- from {
- background: red;
- }
- to {
- background: yellow;
- }
-}
-
-/* Safari 和 Chrome */
-@-webkit-keyframes myfirst {
- from {
- background: red;
- }
- to {
- background: yellow;
- }
-}
-
-/* Opera */
-@-o-keyframes myfirst {
- from {
- background: red;
- }
- to {
- background: yellow;
- }
-}
-```
-
-当您在 @keyframes 中创建动画时,请把它捆绑到某个选择器,否则不会产生动画效果。
-
-通过规定至少以下两项 CSS3 动画属性,即可将动画绑定到选择器:
-
-- 规定动画的名称
-- 规定动画的时长
-
-**实例:把 "myfirst" 动画捆绑到 div 元素,时长:5 秒**
-
-```css
-div {
- animation: myfirst 5s;
- -moz-animation: myfirst 5s; /* Firefox */
- -webkit-animation: myfirst 5s; /* Safari 和 Chrome */
- -o-animation: myfirst 5s; /* Opera */
-}
-```
-
-> 注释:您必须定义动画的名称和时长。如果忽略时长,则动画不会允许,因为默认值是 0。
-
-### 什么是 CSS3 中的动画?
-
-动画是使元素从一种样式逐渐变化为另一种样式的效果。
-
-您可以改变任意多的样式任意多的次数。
-
-请用百分比来规定变化发生的时间,或用关键词 "from" 和 "to",等同于 0% 和 100%。
-
-0% 是动画的开始,100% 是动画的完成。
-
-为了得到最佳的浏览器支持,您应该始终定义 0% 和 100% 选择器。
-
-**实例:当动画为 25% 及 50% 时改变背景色,然后当动画 100% 完成时再次改变**
-
-```css
-@keyframes myfirst {
- 0% {
- background: red;
- }
- 25% {
- background: yellow;
- }
- 50% {
- background: blue;
- }
- 100% {
- background: green;
- }
-}
-
-/* Firefox */
-@-moz-keyframes myfirst {
- 0% {
- background: red;
- }
- 25% {
- background: yellow;
- }
- 50% {
- background: blue;
- }
- 100% {
- background: green;
- }
-}
-
-/* Safari 和 Chrome */
-@-webkit-keyframes myfirst {
- 0% {
- background: red;
- }
- 25% {
- background: yellow;
- }
- 50% {
- background: blue;
- }
- 100% {
- background: green;
- }
-}
-
-/* Opera */
-@-o-keyframes myfirst {
- 0% {
- background: red;
- }
- 25% {
- background: yellow;
- }
- 50% {
- background: blue;
- }
- 100% {
- background: green;
- }
-}
-```
-
-**实例:改变背景色和位置**
-
-```css
-@keyframes myfirst {
- 0% {
- background: red;
- left: 0px;
- top: 0px;
- }
- 25% {
- background: yellow;
- left: 200px;
- top: 0px;
- }
- 50% {
- background: blue;
- left: 200px;
- top: 200px;
- }
- 75% {
- background: green;
- left: 0px;
- top: 200px;
- }
- 100% {
- background: red;
- left: 0px;
- top: 0px;
- }
-}
-
-/* Firefox */
-@-moz-keyframes myfirst {
- 0% {
- background: red;
- left: 0px;
- top: 0px;
- }
- 25% {
- background: yellow;
- left: 200px;
- top: 0px;
- }
- 50% {
- background: blue;
- left: 200px;
- top: 200px;
- }
- 75% {
- background: green;
- left: 0px;
- top: 200px;
- }
- 100% {
- background: red;
- left: 0px;
- top: 0px;
- }
-}
-
-/* Safari 和 Chrome */
-@-webkit-keyframes myfirst {
- 0% {
- background: red;
- left: 0px;
- top: 0px;
- }
- 25% {
- background: yellow;
- left: 200px;
- top: 0px;
- }
- 50% {
- background: blue;
- left: 200px;
- top: 200px;
- }
- 75% {
- background: green;
- left: 0px;
- top: 200px;
- }
- 100% {
- background: red;
- left: 0px;
- top: 0px;
- }
-}
-
-/* Opera */
-@-o-keyframes myfirst {
- 0% {
- background: red;
- left: 0px;
- top: 0px;
- }
- 25% {
- background: yellow;
- left: 200px;
- top: 0px;
- }
- 50% {
- background: blue;
- left: 200px;
- top: 200px;
- }
- 75% {
- background: green;
- left: 0px;
- top: 200px;
- }
- 100% {
- background: red;
- left: 0px;
- top: 0px;
- }
-}
-```
-
-### 动画属性
-
-下面的表格列出了 `@keyframes` 规则和所有动画属性:
-
-| 属性 | 描述 |
-| ------------------------- | -------------------------------------------------------- |
-| @keyframes | 规定动画。 |
-| animation | 所有动画属性的简写属性,除了 animation-play-state 属性。 |
-| animation-name | 规定 @keyframes 动画的名称。 |
-| animation-duration | 规定动画完成一个周期所花费的秒或毫秒。默认是 0。 |
-| animation-timing-function | 规定动画的速度曲线。默认是 "ease"。 |
-| animation-delay | 规定动画何时开始。默认是 0。 |
-| animation-iteration-count | 规定动画被播放的次数。默认是 1。 |
-| animation-direction | 规定动画是否在下一周期逆向地播放。默认是 "normal"。 |
-| animation-play-state | 规定动画是否正在运行或暂停。默认是 "running"。 |
-| animation-fill-mode | 规定对象动画时间之外的状态。 |
-
-下面的两个例子设置了所有动画属性:
-
-**实例:运行名为 myfirst 的动画,其中设置了所有动画属性**
-
-```css
-div {
- animation-name: myfirst;
- animation-duration: 5s;
- animation-timing-function: linear;
- animation-delay: 2s;
- animation-iteration-count: infinite;
- animation-direction: alternate;
- animation-play-state: running;
- /* Firefox: */
- -moz-animation-name: myfirst;
- -moz-animation-duration: 5s;
- -moz-animation-timing-function: linear;
- -moz-animation-delay: 2s;
- -moz-animation-iteration-count: infinite;
- -moz-animation-direction: alternate;
- -moz-animation-play-state: running;
- /* Safari 和 Chrome: */
- -webkit-animation-name: myfirst;
- -webkit-animation-duration: 5s;
- -webkit-animation-timing-function: linear;
- -webkit-animation-delay: 2s;
- -webkit-animation-iteration-count: infinite;
- -webkit-animation-direction: alternate;
- -webkit-animation-play-state: running;
- /* Opera: */
- -o-animation-name: myfirst;
- -o-animation-duration: 5s;
- -o-animation-timing-function: linear;
- -o-animation-delay: 2s;
- -o-animation-iteration-count: infinite;
- -o-animation-direction: alternate;
- -o-animation-play-state: running;
-}
-```
-
-**实例:与上面的动画相同,但是使用了简写的动画 animation 属性**
-
-```css
-div {
- animation: myfirst 5s linear 2s infinite alternate;
- /* Firefox: */
- -moz-animation: myfirst 5s linear 2s infinite alternate;
- /* Safari 和 Chrome: */
- -webkit-animation: myfirst 5s linear 2s infinite alternate;
- /* Opera: */
- -o-animation: myfirst 5s linear 2s infinite alternate;
-}
-```
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Html](html.md)
-> - [Css](css.md)
-> - [Javascript](javascript.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- [W3school css 教程](http://www.w3school.com.cn/css/index.asp)
diff --git a/frontend/base/html.md b/frontend/base/html.md
deleted file mode 100644
index 9eb3872fcf..0000000000
--- a/frontend/base/html.md
+++ /dev/null
@@ -1,672 +0,0 @@
-# Html 快速入门
-
-> 超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页的标准标记语言。
->
-> 关键词: `标签`, `元素`, `属性`
-
-
-
-
-
-- [简介](#简介)
- - [什么是 HTML?](#什么是-html)
- - [HTML 文档 = 网页](#html-文档--网页)
- - [HTML 结构](#html-结构)
- - [HTML 标签](#html-标签)
- - [HTML 元素](#html-元素)
- - [HTML 属性](#html-属性)
-- [Quickstart](#quickstart)
- - [基础](#基础)
- - [样式](#样式)
- - [链接、锚点、图片](#链接锚点图片)
- - [列表](#列表)
- - [表单](#表单)
- - [表格](#表格)
- - [区块](#区块)
- - [框架](#框架)
-- [Html 基础](#html-基础)
- - [标题](#标题)
- - [段落](#段落)
- - [链接](#链接)
- - [图像](#图像)
- - [水平线](#水平线)
- - [换行](#换行)
- - [注释](#注释)
-- [FAQ](#faq)
- - [中文编码](#中文编码)
- - [html 和 htm](#html-和-htm)
- - [HTML 忽略空格和换行](#html-忽略空格和换行)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-### 什么是 HTML?
-
-HTML 是用来描述网页的一种语言。
-
-- HTML 指的是超文本标记语言: Hypertext Markup Language
-- HTML 不是一种编程语言,而是一种**标记**语言
-- 标记语言是一套**标记标签** (markup tag)
-- HTML 使用标记标签来**描述**网页
-- HTML 文档包含了 HTML**标签**及**文本**内容
-- HTML 文档也叫做 **web 页面**
-
-### HTML 文档 = 网页
-
-- HTML 文档**描述网页**
-- HTML 文档**包含 HTML 标签**和纯文本
-- HTML 文档也被称为**网页**
-
-Web 浏览器的作用是读取 HTML 文档,并以网页的形式显示出它们。浏览器不会显示 HTML 标签,而是使用标签来解释页面的内容。
-
-### HTML 结构
-
-#### 文档类型声明
-
-`` 声明为 HTML5 文档。
-
-`` 声明有助于浏览器中正确显示网页。
-
-网络上有很多不同的文件,如果能够正确声明 HTML 的版本,浏览器就能正确显示网页内容。
-
-doctype 声明是不区分大小写的,以下方式均可:
-
-```html
-
-
-
-
-```
-
-**通用声明**
-
-- **HTML5**
-
-```html
-
-```
-
-- **HTML 4.01**
-
-```html
-
-```
-
-- **XHTML 1.0**
-
-```html
-
-```
-
-#### html 元素
-
-`` 元素是 HTML 页面的根元素。
-
-#### head 元素
-
-`` 元素包含了文档的元(meta)数据。
-
-#### body 元素
-
-`` 元素包含了可见的页面内容。
-
-```html
-
-
-
-
页面标题
-
-
-
这是一个标题
-
这是一个段落。
-
-
-```
-
-### HTML 标签
-
-HTML 标记标签通常被称为 HTML 标签 (HTML tag)。
-
-- HTML 标签是由**尖括号**包围的关键词,比如 ``
-- HTML 标签通常是**成对出现**的,比如 `
` 和 ` `
-- 标签对中的第一个标签是**开始标签**,第二个标签是**结束标签**
-- 开始和结束标签也被称为**开放标签**和**闭合标签**
-
-```
-<标签>内容标签>
-```
-
-### HTML 元素
-
-"HTML 标签" 和 "HTML 元素" 通常都是描述同样的意思.
-
-但是严格来讲, 一个 HTML 元素包含了开始标签与结束标签,如下实例:
-
-HTML 元素:
-
-```html
-
这是一个段落。
-```
-
-### HTML 属性
-
-- HTML 元素可以设置**属性**
-
-- 属性可以在元素中添加**附加信息**
-
-- 属性一般描述于**开始标签**
-
-- 属性总是以名称/值对的形式出现,**比如:name="value"**。
-
-- 属性和属性值对大小写不敏感。
-
- 不过,万维网联盟在其 HTML 4 推荐标准中推荐小写的属性/属性值。
-
- 而新版本的 (X)HTML 要求使用小写属性。
-
-#### HTML 属性常用引用属性值
-
-属性值应该始终被包括在引号内。
-
-双引号是最常用的,不过使用单引号也没有问题。
-
-**提示:** 在某些个别的情况下,比如属性值本身就含有双引号,那么您必须使用单引号,例如:`name='John "ShotGun" Nelson'`
-
-#### 常用属性
-
-下面列出了适用于大多数 HTML 元素的属性:
-
-| 属性 | 描述 |
-| ----- | --------------------------------------------------------------- |
-| class | 为 html 元素定义一个或多个类名(classname)(类名从样式文件引入) |
-| id | 定义元素的唯一 id |
-| style | 规定元素的行内样式(inline style) |
-| title | 描述了元素的额外信息 (作为工具条使用) |
-
-## Quickstart
-
-### 基础
-
-```html
-
-
-
-
这是文章标题
-
-
-
-
这是标题 1
-
这是标题 2
-
这是标题 3
-
这是标题 4
-
这是标题 5
-
这是标题 6
-
这是段落
-
-
-
-
-```
-
-### 样式
-
-```html
-
粗体文本
-
斜体文本
-
下划线文本
-
定义着重文字
-
定义加重语气
-
定义插入字
-
定义删除字
-
上标
-
下标
-
-
-
预格式文本
-
一段电脑代码
-
定义项目
-
键盘输入
-
计算机样本
-
变量
-
-
-
- Written by Jon Doe .
- Visit us at:
- Example.com
- Box 564, Disneyland
- USA
-
-
-
-
该段落文字从右到左显示
-
-
-
- For 50 years, WWF has been protecting the future of nature. The world's leading conservation organization, WWF works
- in 100 countries and is supported by 1.2 million members in the United States and close to 5 million globally.
-
-
-
-WWF's goal is to:
Build a future where people live in harmony with nature.
-
-
-
The Scream by Edward Munch. Painted in 1893.
-
定义项目
-```
-
-### 链接、锚点、图片
-
-```html
-
This is a Link
-
-
Send e-mail A named anchor:
-
Useful Tips Section
-
Jump to the Useful Tips Section
-```
-
-### 列表
-
-```html
-
-
-
-
-
-First item
-Next item
-
-
-
-
-First term
-Definition
-Next term
-Definition
-
-```
-
-### 表单
-
-```html
-
-```
-
-### 表格
-
-```html
-
- Monthly Savings
-
-
-
-
-
-
- Month
- Savings
-
-
-
-
- Sum
- $180
-
-
-
-
- January
- $100
-
-
- February
- $80
-
-
-
-```
-
-### 区块
-
-```html
-
-
这是一个在 div 元素中的标题。
-
这是一个在 div 元素中的文本。
-
-
-
some text. some other text.
-```
-
-### 框架
-
-```html
-
-```
-
-## Html 基础
-
-### 标题
-
-> **标题很重要**
->
-> 请确保标题标签只用于标题。不要仅仅是为了产生粗体或大号的文本而使用标题。
->
-> 搜索引擎使用标题为您的网页的结构和内容编制索引。
->
-> 因为用户可以通过标题来快速浏览您的网页,所以用标题来呈现文档结构是很重要的。
-
-HTML 标题(Heading)是通过 `
` - `` 标签来定义的。
-
-`` 定义最大的标题。`` 定义最小的标题。
-
-默认情况下,HTML 会自动地在块级元素前后添加一个额外的空行,比如段落、标题元素前后。
-
-```html
-这是标题 1
-这是标题 2
-这是标题 3
-这是标题 4
-这是标题 5
-这是标题 6
-```
-
-### 段落
-
-> **可以把 HTML 文档分割为若干段落。**
-
-HTML 段落是通过 ` ` 来定义的。
-
-浏览器会自动地在段落的前后添加空行。(`
` 是块级元素)
-
-```html
-
这是一个段落。
-
这是另外一个段落。
-```
-
-使用空的段落标记 `
` 去插入一个空行是个坏习惯。用 `
` 标签代替它!
-
-### 链接
-
-> **HTML 使用超级链接与网络上的另一个文档相连。**
->
-> **几乎可以在所有的网页中找到链接。点击链接可以从一张页面跳转到另一张页面。**
-
-HTML 使用标签 `
` 来设置超文本链接。
-
-超链接可以是一个字,一个词,或者一组词,也可以是一幅图像,您可以点击这些内容来跳转到新的文档或者当前文档中的某个部分。
-
-当您把鼠标指针移动到网页中的某个链接上时,箭头会变为一只小手。
-
-我们通过使用 ` ` 标签在 HTML 中创建链接。有两种使用 ` ` 标签的方式:
-
-1. 通过使用 `href` 属性 - 创建指向另一个文档的链接
-2. 通过使用 `name` 属性 - 创建文档内的书签
-
-```html
- 这是一个链接
-```
-
-#### herf 属性
-
-`href` 属性规定链接的目标。
-
-链接的 HTML 代码很简单。它类似这样:
-
-```html
-
Link text
-```
-
-开始标签和结束标签之间的文字被作为超级链接来显示。
-
-**示例**
-
-```
-
Visit
-```
-
-提示:"链接文本" 不必一定是文本。图片或其他 HTML 元素都可以成为链接。
-
-#### name 属性
-
-`name` 属性规定锚(anchor)的名称。
-
-您可以使用 name 属性创建 HTML 页面中的书签。书签不会以任何特殊方式显示,它对读者是不可见的。
-
-当使用命名锚(named anchors)时,我们可以创建直接跳至该命名锚(比如页面中某个小节)的链接,这样使用者就无需不停地滚动页面来寻找他们需要的信息了。
-
-**命名锚的语法:**
-
-```
-
锚(显示在页面上的文本)
-```
-
-提示:锚的名称可以是任何你喜欢的名字。
-
-提示:您可以使用 id 属性来替代 name 属性,命名锚同样有效。
-
-**示例**
-
-首先,我们在 HTML 文档中对锚进行命名(创建一个书签):
-
-```html
-
基本的注意事项 - 有用的提示
-```
-
-然后,我们在同一个文档中创建指向该锚的链接:
-
-```html
-
有用的提示
-```
-
-您也可以在其他页面中创建指向该锚的链接:
-
-```html
-
有用的提示
-```
-
-在上面的代码中,我们将 # 符号和锚名称添加到 URL 的末端,就可以直接链接到 tips 这个命名锚了。
-
-#### target 属性
-
-使用 `target` 属性,你可以定义被链接的文档在何处显示。
-
-下面的这行会在新窗口打开文档:
-
-```html
-
Visit
-```
-
-### 图像
-
-HTML 图像是通过标签 `
` 来定义的。
-
-`
` 是空标签,意思是说,它只包含属性,并且没有闭合标签。
-
-```html
-
-```
-
-#### src 属性
-
-要在页面上显示图像,你需要使用 `src` 属性。src 指 "source"。源属性的值是图像的 URL 地址。
-
-定义图像的语法是:
-
-```
-
-```
-
-URL 指存储图像的位置。如果名为 "boat.gif" 的图像位于 www.example.com 的 images 目录中,那么其 URL 为 http://www.example.com/images/boat.gif。
-
-浏览器将图像显示在文档中图像标签出现的地方。如果你将图像标签置于两个段落之间,那么浏览器会首先显示第一个段落,然后显示图片,最后显示第二段。
-
-#### alt 属性
-
-alt 属性用来为图像定义一串预备的可替换的文本。替换文本属性的值是用户定义的。
-
-```html
-
-```
-
-在浏览器无法载入图像时,替换文本属性告诉读者她们失去的信息。此时,浏览器将显示这个替代性的文本而不是图像。为页面上的图像都加上替换文本属性是个好习惯,这样有助于更好的显示信息,并且对于那些使用纯文本浏览器的人来说是非常有用的。
-
-### 水平线
-
-`
` 标签在 HTML 页面中创建水平线。
-
-水平线可用于分隔内容。
-
-```html
-
这是一个段落。
-
-
这是一个段落。
-
-
这是一个段落。
-```
-
-### 换行
-
-如果您希望在不产生一个新段落的情况下进行换行(新行),请使用 `
` 标签:
-
-```html
-
这个 段落 演示了分行的效果
-```
-
-`
` 元素是一个空的 HTML 元素。由于关闭标签没有任何意义,因此它没有结束标签。
-
-选择 `
` 还是 `
` ?
-
-您也许发现 `
` 与 `
` 很相似。
-
-在 XHTML、XML 以及未来的 HTML 版本中,不允许使用没有结束标签(闭合标签)的 HTML 元素。
-
-即使 `
` 在所有浏览器中的显示都没有问题,使用 `
` 也是**更长远的保障**。
-
-#### HTML 忽略空格和换行
-
-对于 HTML,您无法通过在 HTML 代码中添加额外的空格或换行来改变输出的效果。
-
-当显示页面时,浏览器会移除**源代码中**多余的空格和空行。所有连续的空格或空行都会被算作一个空格。需要注意的是,HTML 代码中的所有连续的空行(换行)也被显示为一个空格。
-
-```html
-
-
-
春晓
-
- 春眠不觉晓,
- 处处闻啼鸟。
- 夜来风雨声,
- 花落知多少。
-
-
注意,浏览器忽略了源代码中的排版(省略了多余的空格和换行)。
-
-
-```
-
-说明:HTML 的输出结果并不会按照源代码中那样去排版内容。
-
-### 注释
-
-可以将注释插入 HTML 代码中,这样可以提高其可读性,使代码更易被人理解。浏览器会忽略注释,也不会显示它们。
-
-```html
-
-```
-
-#### 条件注释
-
-您也许会在 HTML 中偶尔发现条件注释:
-
-```
-
-```
-
-条件注释定义只有 Internet Explorer 执行的 HTML 标签。
-
-## FAQ
-
-### 中文编码
-
-目前在大部分浏览器中,直接输出中文会出现中文乱码的情况,这时候我们就需要在头部将字符声明为 **UTF-8**。
-
-添加 `
` 元素。
-
-```html
-
-
-
-
-
页面标题
-
-
-
我的第一个标题
-
我的第一个段落。
-
-
-```
-
-### html 和 htm
-
-`.html` 和 `.htm` 都是 html 文档的后缀名,二者没有区别。
-
-### HTML 忽略空格和换行
-
-对于 HTML,您无法通过在 HTML 代码中添加额外的空格或换行来改变输出的效果。
-
-当显示页面时,浏览器会移除**源代码中**多余的空格和空行。所有连续的空格或空行都会被算作一个空格。需要注意的是,HTML 代码中的所有连续的空行(换行)也被显示为一个空格。
-
-```html
-
-
-
春晓
-
- 春眠不觉晓,
- 处处闻啼鸟。
- 夜来风雨声,
- 花落知多少。
-
-
注意,浏览器忽略了源代码中的排版(省略了多余的空格和换行)。
-
-
-```
-
-说明:HTML 的输出结果并不会按照源代码中那样去排版内容。
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Html](html.md)
-> - [Css](css.md)
-> - [Javascript](javascript.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- [mozilla html 教程](https://developer.mozilla.org/en-US/Learn/HTML)
-- [W3school html 教程](http://www.w3school.com.cn/html/index.asp)
diff --git a/frontend/base/js.md b/frontend/base/js.md
deleted file mode 100644
index 4f99f3e8d3..0000000000
--- a/frontend/base/js.md
+++ /dev/null
@@ -1,204 +0,0 @@
-# Javascript 教程
-
-
-
-- [简介](#简介)
-- [用法](#用法)
-- [输出](#输出)
-- [变量](#变量)
-- [数据类型](#数据类型)
-- [操作符](#操作符)
-- [语句](#语句)
-- [控制语句](#控制语句)
- - [if...else if....else 语句](#ifelse-ifelse-语句)
- - [switch 语句](#switch-语句)
- - [for 循环](#for-循环)
- - [while 循环](#while-循环)
- - [break 和 continue](#break-和-continue)
-- [函数](#函数)
-- [注释](#注释)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-JavaScript 是一种脚本,一门编程语言,它可以在网页上实现复杂的功能,网页展现给你的不再是简单的静态信息,实时的内容更新,交互式的地图,2D/3D 动画,滚动播放的视频,等等。
-
-## 用法
-
-- HTML 中的脚本必须位于 `` 标签之间。
-- 脚本可被放置在 HTML 页面的 `` 和 `` 部分中。
-
-(1)如需在 HTML 页面中插入 JavaScript,请使用 `
-```
-
-(2)如需使用外部文件,请在 `
-
-
-```
-
-## 输出
-
-JavaScript 没有任何打印或者输出的函数。
-
-JavaScript 可以通过不同的方式来输出数据:
-
-- 使用 window.alert() 弹出警告框。
-- 使用 document.write() 方法将内容写到 HTML 文档中。
-- 使用 innerHTML 写入到 HTML 元素。
-- 使用 console.log() 写入到浏览器的控制台。
-
-## 变量
-
-JavaScript 使用关键字 `var` 来定义变量, 使用等号 `=` 来为变量赋值。
-
-```js
-var x, length;
-x = 5;
-length = 6;
-```
-
-变量命名要求:
-
-- 变量必须以字母开头
-- 变量也能以 \$ 和 \_ 符号开头(不过我们不推荐这么做)
-- 变量名称对大小写敏感(y 和 Y 是不同的变量)
-
-## 数据类型
-
-JavaScript 有多种数据类型:字符串(String)、数字(Number)、布尔(Boolean)、数组(Array)、对象(Object)、空(Null)、未定义(Undefined)。
-
-```js
-var length = 16; // Number 通过数字字面量赋值
-var points = x * 10; // Number 通过表达式字面量赋值
-var lastName = "Johnson"; // String 通过字符串字面量赋值
-var cars = ["Saab", "Volvo", "BMW"]; // Array 通过数组字面量赋值
-var person = { firstName: "John", lastName: "Doe" }; // Object 通过对象字面量赋值
-```
-
-## 操作符
-
-JavaScript 支持的操作符:
-
-- 赋值,算术和位运算符
-- 条件,比较及逻辑运算符
-
-## 语句
-
-语句是用分号 `;` 分隔。
-
-```js
-x = 5 + 6;
-y = x * 10;
-```
-
-## 控制语句
-
-分支:
-
-### if...else if....else 语句
-
-```js
-if (condition1) {
- // 当条件 1 为 true 时执行的代码
-} else if (condition2) {
- // 当条件 2 为 true 时执行的代码
-} else {
- // 当条件 1 和 条件 2 都不为 true 时执行的代码
-}
-```
-
-### switch 语句
-
-```js
-switch (n) {
- case 1:
- // 执行代码块 1
- break;
- case 2:
- // 执行代码块 2
- break;
- default:
- // n 与 case 1 和 case 2 不同时执行的代码
- break;
-}
-```
-
-### for 循环
-
-```js
-for (语句 1; 语句 2; 语句 3) {
- // 被执行的代码块
-}
-```
-
-### while 循环
-
-```js
-while (条件) {
- // 需要执行的代码
-}
-```
-
-### break 和 continue
-
-- break 语句可用于跳出循环。
-- continue 语句中断循环中的迭代,如果出现了指定的条件,然后继续循环中的下一个迭代。
-
-## 函数
-
-函数就是包裹在花括号中的代码块,前面使用了关键词 function:
-
-```js
-function myFunction(a, b) {
- return a * b; // 返回 a 乘于 b 的结果
-}
-```
-
-## 注释
-
-- 单行注释以 `//` 开头。
-- 多行注释以 `/*` 开始,以 `*/` 结尾。
-
-JavaScript 不会执行注释。
-
-可以添加注释来对 JavaScript 进行解释,或者提高代码的可读性。
-
-```js
-/*
-多行注释
-*/
-
-// 单行注释
-```
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Html](html.md)
-> - [Css](css.md)
-> - [Javascript](javascript.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- 书籍
- - [You Don't Know JS](https://github.com/getify/You-Dont-Know-JS)
-- 教程
- - [W3Cschool JavaScript 教程](https://www.w3cschool.cn/javascript/)
- - [MDN JavaScript 教程](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript)
-- 规范
- - [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) - JavaScript 编程规范
- - [JavaScript Standard Style](https://github.com/standard/standard/blob/master/docs/README-zhcn.md) - 自带 linter & 代码自动修正
-- 更多资源
- - [awesome-javascript](https://github.com/sorrycc/awesome-javascript)
diff --git a/frontend/es6/ES6.md b/frontend/es6/ES6.md
deleted file mode 100644
index 18c8a064a3..0000000000
--- a/frontend/es6/ES6.md
+++ /dev/null
@@ -1,921 +0,0 @@
-# ES6
-
-> 关键词: `ES6`, `ECMAScript`, `arrow`, `this`, `let`, `const`, `class`, `extends`, `super`, `arrow` ...
-
-
-
-- [简介](#简介)
-- [最常用的 ES6 特性](#最常用的-es6-特性)
- - [arrow, this](#arrow-this)
- - [let, const](#let-const)
- - [class, extends, super](#class-extends-super)
- - [template strings](#template-strings)
- - [Destructuring](#destructuring)
- - [default, rest, spread](#default-rest-spread)
- - [增强的对象字面量](#增强的对象字面量)
- - [Iterator,for..Of](#iteratorforof)
- - [Generators](#generators)
- - [Unicode](#unicode)
- - [Module](#module)
- - [Module Loader](#module-loader)
- - [Map + Set + WeakMap + WeakSet](#map--set--weakmap--weakset)
- - [Proxy](#proxy)
- - [Symbols](#symbols)
- - [可以被继承的内建对象](#可以被继承的内建对象)
- - [Math + Number + String + Object APIs](#math--number--string--object-apis)
- - [二进制和八进制字面量](#二进制和八进制字面量)
- - [Promises](#promises)
- - [Reflect API](#reflect-api)
- - [Tail Calls(尾调用)](#tail-calls尾调用)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-ECMAScript 6(以下简称 ES6)是 JavaScript 语言的下一代标准。
-
-因为当前版本的 ES6 是在 2015 年发布的,所以又称 ECMAScript 2015。
-
-也就是说,ES6 就是 ES2015。
-
-注意:并非所有的浏览器都支持 ES6。所以,如果要让浏览器能够理解 ES6 的语义,需要使用转码器,比如最流行的 [Babel](https://babeljs.io/)。
-
-## 最常用的 ES6 特性
-
-### arrow, this
-
-这个恐怕是 ES6 最最常用的一个新特性了,用它来写 `function` 比原来的写法要简洁清晰很多。
-
-箭头函数用 `=>` 来代表一个函数,语法上类似于 C#, Java8 和 CoffeeScript 中的相关特性。它同时支持表达式(Expression bodies)和语句(Statement bodies)的写法。值得注意的是,与一般的函数不同,箭头函数与包裹它的代码共享相同的 `this` 对象,如果箭头函数在其它函数的内部,它也将共享该函数的 `arguments` 变量。
-
-```js
-function(i){ return i + 1; } //ES5
-(i) => i + 1 //ES6
-```
-
-简直是简单的不像话对吧...
-
-如果函数比较复杂,则需要用 `{}` 把代码包起来:
-
-```js
-function(x, y) {
- x++;
- y--;
- return x + y;
-}
-(x, y) => {x++; y--; return x+y}
-```
-
-除了看上去更简洁以外,arrow function 还有一项超级无敌的功能!
-
-长期以来,JavaScript 语言的 `this` 对象一直是一个令人头痛的问题,在对象方法中使用 `this`,必须非常小心。例如:
-
-```js
-class Animal {
- constructor() {
- this.type = "animal";
- }
- says(say) {
- setTimeout(function() {
- console.log(this.type + " says " + say);
- }, 1000);
- }
-}
-
-var animal = new Animal();
-animal.says("hi"); //undefined says hi
-```
-
-运行上面的代码会报错,这是因为`setTimeout`中的`this`指向的是全局对象。所以为了让它能够正确的运行,传统的解决方法有两种:
-
-(1)第一种是将 this 传给 self,再用 self 来指代 this
-
-```js
- says(say){
- var self = this;
- setTimeout(function(){
- console.log(self.type + ' says ' + say)
- }, 1000)
-```
-
-(2)第二种方法是用`bind(this)`,即
-
-```js
-says(say){
- setTimeout(function(){
- console.log(self.type + ' says ' + say)
- }.bind(this), 1000)
-```
-
-但现在我们有了箭头函数,就不需要这么麻烦了:
-
-```js
-class Animal {
- constructor() {
- this.type = "animal";
- }
- says(say) {
- setTimeout(() => {
- console.log(this.type + " says " + say);
- }, 1000);
- }
-}
-var animal = new Animal();
-animal.says("hi"); //animal says hi
-```
-
-当我们使用箭头函数时,函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
-
-并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数根本没有自己的 this,它的 this 是继承外面的,因此内部的 this 就是外层代码块的 this。
-
-### let, const
-
-这两个关键字具有块级作用域。
-
-- `let` 用于声明变量;
-- `const` 用于声明常量。`const` 仅允许被赋值一次,通过静态限制(Static restrictions )的方式阻止变量在赋值前被使用。
-
-#### let
-
-ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
-
-(1)内层变量覆盖外层变量
-
-`var` vs. `let` 示例 1:
-
-```js
-// ES5 示例
-var name = "zach";
-
-while (true) {
- var name = "obama";
- console.log(name); //obama
- break;
-}
-
-console.log(name); //obama
-
---------------------------------------
-
-// ES6 示例
-let name = "zach";
-
-while (true) {
- let name = "obama";
- console.log(name); //obama
- break;
-}
-
-console.log(name); //zach
-```
-
-> 说明:使用 `var` 两次输出都是 obama。而 `let` 则实际上为 JavaScript 新增了块级作用域。用它所声明的变量,只在 `let` 命令所在的代码块内有效。
-
-(2)用来计数的循环变量泄露为全局变量
-
-`var` vs. `let` 示例 2:
-
-```js
-// ES5
-var a = [];
-for (var i = 0; i < 10; i++) {
- a[i] = function() {
- console.log(i);
- };
-}
-a[6](); // 10
-
-// ES6
-let a = [];
-for (let i = 0; i < 10; i++) {
- a[i] = function() {
- console.log(i);
- };
-}
-a[6](); // 6
-```
-
-说明:上面代码中,变量 i 是 `var` 声明的,在全局范围内都有效。所以每一次循环,新的 i 值都会覆盖旧值,导致最后输出的是最后一轮的 i 的值。而使用 `let` 则不会出现这个问题。
-
-#### const
-
-`const` 用于声明常量,一旦声明,常量的值就不能改变。
-
-```js
-const PI = Math.PI;
-PI = 23; //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only
-```
-
-当我们尝试去改变用 const 声明的常量时,浏览器就会报错。
-
-`const` 有一个很好的应用场景,就是当我们引用第三方库的时声明的变量,用 `const` 来声明可以避免未来不小心重命名而导致出现 bug:
-
-```js
-const monent = require("moment");
-```
-
-### class, extends, super
-
-这三个特性涉及了 ES5 中最令人头疼的的几个部分:原型、构造函数,继承。
-有了 ES6 我们不再烦恼!
-
-ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念。新的 class 写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂。
-
-```js
-class Animal {
- constructor() {
- this.type = "animal";
- }
- says(say) {
- console.log(this.type + " says " + say);
- }
-}
-
-let animal = new Animal();
-animal.says("hello"); //animal says hello
-
-class Cat extends Animal {
- constructor() {
- super();
- this.type = "cat";
- }
-}
-
-let cat = new Cat();
-cat.says("hello"); //cat says hello
-```
-
-上面代码首先用 `class` 定义了一个类,可以看到里面有一个 `constructor` 方法,这就是构造方法,而 `this` 关键字则代表实例对象。简单地说,`constructor` 内定义的方法和属性是实例对象自己的,而 `constructor` 外定义的方法和属性则是所有实力对象可以共享的。
-
-ES2015 的类只是一个语法糖,通过 class 关键字让语法更接近传统的面向对象模式,本质上还是基于原型的。使用单一便捷的声明格式,使得类使用起来更方便,也更具互操作性。类支持基于原型的继承,调用父类的构造函数,生成实例,静态方法和构造函数。
-
-`Class` 之间可以通过 `extends` 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。上面定义了一个 Cat 类,该类通过 `extends` 关键字,继承了 Animal 类的所有属性和方法。
-
-`super` 关键字,它指代父类的实例(即父类的 this 对象)。子类必须在 `constructor` 方法中调用 `super` 方法,否则新建实例时会报错。这是因为子类没有自己的 `this` 对象,而是继承父类的 `this` 对象,然后对其进行加工。如果不调用 `super` 方法,子类就得不到 `this` 对象。
-
-ES6 的继承机制,实质是先创造父类的实例对象 `this`(所以必须先调用 `super` 方法),然后再用子类的构造函数修改 `this`。
-
-P.S 如果你写 react 的话,就会发现以上三个东西在最新版 React 中出现得很多。创建的每个 component 都是一个继承`React.Component`的类。[详见 react 文档](https://facebook.github.io/react/docs/reusable-components.html)
-
-### template strings
-
-模版字符串提供了构建字符串的语法糖,类似于 Perl,Python 等语言中的字符串插值。可以构建一个自定义标签,避免注入攻击或者从字符串内容中构建更加高级的数据结构。
-
-当我们要插入大段的 html 内容到文档中时,传统的写法非常麻烦:
-
-```js
-// ES5
-$("#result").append(
- "There are
" +
- basket.count +
- " " +
- "items in your basket, " +
- "
" +
- basket.onSale +
- " are on sale!"
-);
-```
-
-我们要用一堆的'+'号来连接文本与变量,而使用 ES6 的新特性模板字符串 `` 后,我们可以直接这么来写:
-
-```js
-// ES6
-$("#result").append(`
- There are
${basket.count} items
- in your basket,
${basket.onSale}
- are on sale!
-`);
-```
-
-用反引号 `\`` 来标识起始,用 ${}来引用变量,而且所有的空格和缩进都会被保留在输出之中。
-
-```js
-// 创建基本的模板字符串
-`This is a pretty little template string.``In ES5 this is // 多行字符串
- not legal.`;
-
-// 插入变量
-var name = "Bob",
- time = "today";
-`Hello ${name}, how are you ${time}?`;
-
-// 不用转义
-String.raw`In ES5 "\n" is a line-feed.`;
-
-// 创建一个HTTP请求头的模版字符串,通过替换内容来构建请求
-GET`http://foo.org/bar?a=${a}&b=${b}
- Content-Type: application/json
- X-Credentials: ${credentials}
- { "foo": ${foo},
- "bar": ${bar} }`(myOnReadyStateChangeHandler);
-```
-
-### Destructuring
-
-ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
-
-看下面的例子:
-
-```js
-var cat = "ken";
-var dog = "lili";
-var zoo = { cat: cat, dog: dog };
-console.log(zoo); //Object {cat: "ken", dog: "lili"}
-```
-
-用 ES6 完全可以像下面这么写:
-
-```js
-let cat = "ken";
-let dog = "lili";
-let zoo = { cat, dog };
-console.log(zoo); //Object {cat: "ken", dog: "lili"}
-```
-
-反过来可以这么写:
-
-```js
-let dog = { type: "animal", many: 2 };
-let { type, many } = dog;
-console.log(type, many); //animal 2
-```
-
-解构允许使用模式匹配的方式进行绑定,并支持匹配 数组和对象。解构具有一定的容错机制,就像查找普通对象`foo['foo']`这样,当没有找到时会返回`undefined`(而不会直接报错)。
-
-> 注:当上层结构都不存在时,解构是会报错的,如`const [{id: id}] = []`,解构数组为空,导致整个 obj 为`undefined`,此时再去找`obj.id`就会报错。
-
-```js
-// 列表(数组)匹配
-var [a, , b] = [1, 2, 3];
-
-// 对象匹配
-var {
- op: a,
- lhs: { op: b },
- rhs: c
-} = getASTNode();
-
-// 对象匹配的简写
-// 绑定当前作用域的 `op`, `lhs` 和 `rhs`
-var { op, lhs, rhs } = getASTNode();
-
-// 可以用在函数的参数中
-function g({ name: x }) {
- console.log(x);
-}
-g({ name: 5 });
-
-// 解构容错机制
-var [a] = [];
-a === undefined;
-
-// 带默认值的解构容错
-var [a = 1] = [];
-a === 1;
-
-// 解构 + 默认参数
-function r({ x, y, w = 10, h = 10 }) {
- return x + y + w + h;
-}
-r({ x: 1, y: 2 }) === 23;
-```
-
-### default, rest, spread
-
-(1)默认参数
-
-默认参数(default)的功能是在函数被调用时对参数做自动估值(若没被赋值,则自动赋值)
-
-大家可以看下面的例子,调用`animal()`方法时忘了传参数,传统的做法就是加上这一句`type = type || 'cat'`来指定默认值。
-
-```js
-// ES5
-function animal(type) {
- type = type || "cat";
- console.log(type);
-}
-animal();
-
-// ES6
-function animal(type = "cat") {
- console.log(type);
-}
-animal();
-```
-
-(2)不定参数
-
-不定参数(rest)用在参数末尾,将最末尾的参数转换为数组。不定参数(rest)让我们不再需要`arguments`,更直接地解决了一些常见的问题。
-
-最后一个 rest 语法也很简单,直接看例子:
-
-```js
-function animals(...types) {
- console.log(types);
-}
-animals("cat", "dog", "fish"); //["cat", "dog", "fish"]
-```
-
-而如果不用 ES6 的话,我们则得使用 ES5 的`arguments`。
-
-(3)扩展运算符
-
-扩展运算符(spread)则可以将数组转换为连续的函数参数,
-
-```js
-function f(x, y, z) {
- return x + y + z;
-}
-// 将数组中的每个元素展开为函数参数
-f(...[1, 2, 3]) == 6;
-```
-
-### 增强的对象字面量
-
-经扩展后的对象字面量,允许在结构中设置原型,简化了`foo: foo`这样的赋值,定义方法和调用父级。这样使得对象字面量(Object Literals)更接近类的声明,并且使得基于对象的设计更加方便。
-
-```js
-var obj = {
- // 设置 prototype
- __proto__: theProtoObj,
- // 计算属性不会重复设置__proto__,或者将直接触发错误。
- ["__proto__"]: somethingElse,
- // ‘handler: handler’ 简写
- handler,
- // 方法
- toString() {
- // 调用父级方法
- return "d " + super.toString();
- },
- // 设置动态的属性名
- ["prop_" + (() => 42)()]: 42
-};
-```
-
-> `__proto__` 需要原生支持, 并且在 之前的 ECMAScript 版本中已被弃用。虽然现在大多数引擎支持, 但是 [仍有些引擎](https://kangax.github.io/compat-table/es6/#__proto___in_object_literals)是不支持的。另外,值得注意的是,如同[附录 B](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.prototype.__proto__)所示,只有 [web 浏览器](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-additional-ecmascript-features-for-web-browsers) 仍然需要支持该属性。在 node 中已经被支持。
-
-### Iterator,for..Of
-
-ES6 中的迭代器(Iterators)对象允许像 CLR(Common Language Runtime)的 IEnumerable 接口或者 Java 的 Iterable 一样创建自定义迭代器,可以将`for..in`这种遍历模式更加一般化为`for..of`的形式。它是支持惰性模式的,不需要真正实现一个数组(只需要实现 Iterator 接口),就像 LINQ 语言那样。
-
-```js
-// 实现斐波那契数列的迭代器
-let fibonacci = {
- [Symbol.iterator]() {
- let pre = 0,
- cur = 1;
- return {
- next() {
- [pre, cur] = [cur, pre + cur];
- return { done: false, value: cur };
- }
- };
- }
-};
-
-for (var n of fibonacci) {
- // 循环将在n > 1000 时结束
- if (n > 1000) break;
- console.log(n);
-}
-```
-
-迭代器还可以基于”鸭子类型”来实现(使用[TypeScript](http://typescriptlang.org/) 这种基于类型的语法来说明):
-
-```js
-interface IteratorResult {
- done: boolean;
- value: any;
-}
-interface Iterator {
- next(): IteratorResult;
-}
-interface Iterable {
- [Symbol.iterator](): Iterator
-}
-```
-
-> 通过 polyfill 支持
->
-> 为了使用迭代器你必须引入 Babel 的 [polyfill](https://www.babeljs.cn/docs/usage/polyfill).
-
-### Generators
-
-Generator 通过使用`function*`和`yield`关键字简化了迭代器的编写。通过`function*`声明的函数会返回一个 Generators 实例。Generator 可以看做是迭代器的子类,包含了额外的`next`和`throw`方法。这些特性可以让得到的结果值再传回 Generator,因此`yield`是一个具有返回值(或抛出一个值)的表达式。
-
-注意:Generator 也可以用于使用‘await’这样的异步编程中,详见 ES7 `await` [协议](https://github.com/lukehoban/ecmascript-asyncawait).
-
-```js
-var fibonacci = {
- [Symbol.iterator]: function*() {
- var pre = 0,
- cur = 1;
- for (;;) {
- var temp = pre;
- pre = cur;
- cur += temp;
- yield cur;
- }
- }
-};
-
-for (var n of fibonacci) {
- // truncate the sequence at 1000
- if (n > 1000) break;
- console.log(n);
-}
-```
-
-Generator 接口 (使用[TypeScript](http://typescriptlang.org/) 这种基于类型的语法来说明):
-
-```js
-interface Generator extends Iterator {
- next(value?: any): IteratorResult;
- throw(exception: any);
-}
-```
-
-> 通过 polyfill 支持
->
-> 要使用 Generator,你需要在项目中包含 Babel 的 [polyfill](https://www.babeljs.cn/docs/usage/polyfill).
-
-### Unicode
-
-ES6 加强了对 Unicode 编码 的支持,包括新的 unicode 表示法,正则表达式的`u`模式来匹配码点(code points),也提供新的 API 去处理 21 位的码点(code points)。这些新特性允许我们使用 JavaScript 构建国际化的应用。
-
-```js
-// 和ES5.1相同
-"𠮷".length == 2;
-
-// 正则表达式新的u模式
-"𠮷".match(/./u)[0].length == 2;
-
-// 新的unicode表示法
-("\u{20BB7}" == "𠮷") == "\uD842\uDFB7";
-
-// 新的字符串方法
-"𠮷".codePointAt(0) == 0x20bb7;
-
-// for of迭代码点
-for (var c of "𠮷") {
- console.log(c);
-}
-```
-
-### Module
-
-ES6 从语言层面对模块进行了支持。编写方式借鉴了流行的 JavaScript 模块加载器(AMD, CommonJS)。由宿主环境的默认加载器定义模块运行时的行为,采取隐式异步模式——在模块可以被获取和加载前不会有代码执行。
-
-```js
-// lib/math.js
-export function sum(x, y) {
- return x + y;
-}
-export var pi = 3.141593;
-// app.js
-import * as math from "lib/math";
-console.log("2π = " + math.sum(math.pi, math.pi));
-// otherApp.js
-import { sum, pi } from "lib/math";
-console.log("2π = " + sum(pi, pi));
-```
-
-还有的功能包括:`export default` and `export *`:
-
-```js
-// lib/mathplusplus.js
-export * from "lib/math";
-export var e = 2.71828182846;
-export default function(x) {
- return Math.exp(x);
-}
-// app.js
-import exp, { pi, e } from "lib/mathplusplus";
-console.log("e^π = " + exp(pi));
-```
-
-> 模块的格式:
->
-> Babel 可以将 ES2015 的模块转换为一下几种格式:Common.js,AMD,System,以及 UMD。你甚至可以创建你自己的方式。详见[模块文档](https://www.babeljs.cn/docs/plugins/).
-
-### Module Loader
-
-> 非 ES2015 部分
->
-> 这并不是 ES2015 的一部分:这部分 ECMAScript 2015 规范是由实现定义(implementation-defined)的。最终的标准将在 WHATWG 的[Loader 规范](https://whatwg.github.io/loader/)中确定,目前这项工作正在进行中,下面的内容来自于之前的 ES2015 草稿。
-
-模块加载器支持以下功能:
-
-- 动态加载(Dynamic loading)
-- 状态一致性(State isolation)
-- 全局空间一致性(Global namespace isolation)
-- 编译钩子(Compilation hooks)
-- 嵌套虚拟化(Nested virtualization)
-
-你可以对默认的加载器进行配置,构建出新的加载器,可以被加载于独立或受限的执行环境。
-
-```js
-// 动态加载 – ‘System’ 是默认的加载器
-System.import("lib/math").then(function(m) {
- alert("2π = " + m.sum(m.pi, m.pi));
-});
-
-// 创建执行沙箱 – new Loaders
-var loader = new Loader({
- global: fixup(window) // replace ‘console.log’
-});
-loader.eval('console.log("hello world!");');
-
-// 直接操作模块的缓存
-System.get("jquery");
-System.set("jquery", Module({ $: $ })); // WARNING: not yet finalized
-```
-
-> 需要额外的 polyfill
->
-> 由于 Babel 默认使用 common.js 的模块,你需要一个 polyfill 来使用加载器 API。 [点击获取](https://github.com/ModuleLoader/es6-module-loader).
-
-> 使用模块加载器
->
-> 为了使用此功能,你需要告诉 Babel 使用`system`模块格式化工具。在此查看 [System.js](https://github.com/systemjs/systemjs)
-
-### Map + Set + WeakMap + WeakSet
-
-为常见算法的实现提供了更有效的数据结构。WeakMaps 提供了对对象的弱引用(不会被垃圾回收计数)。
-
-```js
-// Sets
-var s = new Set();
-s.add("hello")
- .add("goodbye")
- .add("hello");
-s.size === 2;
-s.has("hello") === true;
-
-// Maps
-var m = new Map();
-m.set("hello", 42);
-m.set(s, 34);
-m.get(s) == 34;
-
-// Weak Maps
-var wm = new WeakMap();
-wm.set(s, { extra: 42 });
-wm.size === undefined;
-
-// Weak Sets
-var ws = new WeakSet();
-ws.add({ data: 42 });
-// 由于传入的对象没有其它引用,故将不会被set保存。
-```
-
-> 需要 polyfill 支持
->
-> 为了在所有环境下使用 Maps,Sets,WeakMaps 和 WeakSets,你需要使用 Babel 的 [polyfill](https://www.babeljs.cn/docs/usage/polyfill).
-
-### Proxy
-
-Proxy(代理对象) 允许创建一个可以全范围控制宿主对象行为的对象,可用于拦截,对象的虚拟化,日志记录/性能分析等。
-
-```js
-// 代理普通对象
-var target = {};
-var handler = {
- get: function(receiver, name) {
- return `Hello, ${name}!`;
- }
-};
-
-var p = new Proxy(target, handler);
-p.world === "Hello, world!";
-// 代理函数对象
-var target = function() {
- return "I am the target";
-};
-var handler = {
- apply: function(receiver, ...args) {
- return "I am the proxy";
- }
-};
-
-var p = new Proxy(target, handler);
-p() === "I am the proxy";
-```
-
-下面是完全在运行态的元操作(meta-operations)中可能出现的 trap:
-
-```js
-var handler =
-{
- // target.prop
- get: ...,
- // target.prop = value
- set: ...,
- // 'prop' in target
- has: ...,
- // delete target.prop
- deleteProperty: ...,
- // target(...args)
- apply: ...,
- // new target(...args)
- construct: ...,
- // Object.getOwnPropertyDescriptor(target, 'prop')
- getOwnPropertyDescriptor: ...,
- // Object.defineProperty(target, 'prop', descriptor)
- defineProperty: ...,
- // Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
- // target.__proto__, object.isPrototypeOf(target), object instanceof target
- getPrototypeOf: ...,
- // Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
- setPrototypeOf: ...,
- // for (let i in target) {}
- enumerate: ...,
- // Object.keys(target)
- ownKeys: ...,
- // Object.preventExtensions(target)
- preventExtensions: ...,
- // Object.isExtensible(target)
- isExtensible :...
-}
-```
-
-> 不支持的特性
->
-> 由于 ES5 的局限性,Proxies 无法被转换或者通过 polyfill 兼容,查看[不同 JavaScript 引擎](https://kangax.github.io/compat-table/es6/#test-Proxy).
-
-### Symbols
-
-Symbol 对对象的状态进行访问控制。Symbol 允许对象的属性不仅可以通过`string(ES5)`命名,还可以通过`symbol`命名。`symbol`是一种基本数据类型。可选的`name`参数用于调试——但并不是它本身的一部分。Symbol 是唯一的,但不是私有的,因为它们使用诸如`Object.getOwnPropertySymbols`这样的方法来使用。
-
-```js
-(function() {
-
- // 模块内的 symbol
- var key = Symbol("key");
-
- function MyClass(privateData) {
- this[key] = privateData;
- }
-
- MyClass.prototype = {
- doStuff: function() {
- ... this[key] ...
- }
- };
-
- // Bable只能有限支持,完全支持需要原生实现
- typeof key === "symbol"
-})();
-
-var c = new MyClass("hello")
-c["key"] === undefined
-```
-
-> 通过 polyfill 部分实现:
->
-> 通过 Babel 的[polyfill](https://www.babeljs.cn/docs/usage/polyfill).部分实现。由于语言的限制,部分功能不能转换或通过 polyfill 兼容。您可以查看 code.js 的 [注意事项](https://github.com/zloirock/core-js#caveats-when-using-symbol-polyfill) 获取更多信息.
-
-### 可以被继承的内建对象
-
-在 ES2015 中,可以创建内建对象如`Array`,`Date`以及`DOMElement`的子类。
-
-```js
-// 创建Array的子类
-class MyArray extends Array {
- constructor(...args) {
- super(...args);
- }
-}
-
-var arr = new MyArray();
-arr[1] = 12;
-arr.length == 2;
-```
-
-> 部分支持
->
-> 部分支持:由于 ES5 引擎的限制,可以创建 HTMLElement 的子类,但不能创建诸如 Array,Date 和 Error 等对象的子类。
-
-### Math + Number + String + Object APIs
-
-新增很多功能,如核心的 Math 库,数组转换和用于对象复制的 Object.assign()。
-
-```js
-Number.EPSILON;
-Number.isInteger(Infinity); // false
-Number.isNaN("NaN"); // false
-
-Math.acosh(3); // 1.762747174039086
-Math.hypot(3, 4); // 5
-Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 2
-
-"abcde".includes("cd"); // true
-"abc".repeat(3); // "abcabcabc"
-
-Array.from(document.querySelectorAll("*")); // Returns a real Array
-Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
- [(0, 0, 0)].fill(7, 1) // [0,7,7]
- [(1, 2, 3)].findIndex(x => x == 2) // 1
- [("a", "b", "c")].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
- [("a", "b", "c")].keys() // iterator 0, 1, 2
- [("a", "b", "c")].values(); // iterator "a", "b", "c"
-
-Object.assign(Point, { origin: new Point(0, 0) });
-```
-
-> 通过 polyfill 有限的支持
->
-> 上述许多 API 都通过 polyfill 进行了支持,但是部分特性由于多种原因没有被实现(如,String.prototype.normalize 需要编写大量额外的代码来实现),你可以在 [这里](https://github.com/addyosmani/es6-tools#polyfills)找到更多的 polyfill。
-
-### 二进制和八进制字面量
-
-新增两种数字字面量:二进制`b`和八进制`o`。
-
-```js
-0b111110111 === 503; // true
-0o767 === 503; // true
-```
-
-> 仅支持字面模式
->
-> Babel 仅可以转换 0o767,并不能转换 Number("0o767")。
-
-### Promises
-
-Promises 是一种异步编程的方式。Promises 在将来可能会得到支持。目前很多的 JavaScript 库都使用了 Promises。
-
-```js
-function timeout(duration = 0) {
- return new Promise((resolve, reject) => {
- setTimeout(resolve, duration);
- });
-}
-
-var p = timeout(1000)
- .then(() => {
- return timeout(2000);
- })
- .then(() => {
- throw new Error("hmm");
- })
- .catch(err => {
- return Promise.all([timeout(100), timeout(200)]);
- });
-```
-
-> 通过 polyfill
->
-> 要使用 Promises,你需要引入 Babel 的 [polyfill](https://www.babeljs.cn/docs/usage/polyfill).
-
-### Reflect API
-
-完整的 Reflect API 暴露在对象的运行级元操作上。它可以用来有效地还原 Proxy API,并允许调用相应的 proxy traps,尤其是在执行 proxies 时非常有用。
-
-```js
-var O = { a: 1 };
-Object.defineProperty(O, "b", { value: 2 });
-O[Symbol("c")] = 3;
-
-Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
-
-function C(a, b) {
- this.c = a + b;
-}
-var instance = Reflect.construct(C, [20, 22]);
-instance.c; // 42
-```
-
-> 通过 polyfill
->
-> 要使用 Reflect API,你需要引入 Babel 的 [polyfill](https://www.babeljs.cn/docs/usage/polyfill).
-
-### Tail Calls(尾调用)
-
-尾递归调用可以保证调用栈不会无限增长,使得在无界输入的情况下,递归算法是安全的。
-
-```js
-function factorial(n, acc = 1) {
- "use strict";
- if (n <= 1) return acc;
- return factorial(n - 1, n * acc);
-}
-
-// 在绝大多数JS引擎中运行这段代码会导致栈溢出
-// 但是在ES2015中,即便输入很随意也可以安全运行
-factorial(100000)
-```
-
-## 引申和引用
-
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方**
- - [ECMAScript 2015](https://www.ecma-international.org/ecma-262/6.0/index.html)
-- **教程**
- - [ECMAScript 6 入门](https://github.com/ruanyf/es6tutorial)
- - [ES6-Learning](https://github.com/ericdouglas/ES6-Learning)
-- **文章**
- - [30 分钟掌握 ES6/ES2015 核心内容](http://www.jianshu.com/p/ebfeb687eb70)
- - [ECMAScript 6 in WebStorm: Transpiling](https://blog.jetbrains.com/webstorm/2015/05/ecmascript-6-in-webstorm-transpiling/)
- - [ECMAScript 6 特性](https://github.com/lukehoban/es6features)
diff --git a/frontend/es6/babel.md b/frontend/es6/babel.md
deleted file mode 100644
index f05ec4a5ef..0000000000
--- a/frontend/es6/babel.md
+++ /dev/null
@@ -1,592 +0,0 @@
-# Babel 教程
-
-> `Babel` 是一个通用的多用途 JavaScript 编译器。
->
-> ⚠ **注意:**
->
-> Babel 可以与很多构建工具(如 `Browserify`、`Grunt`、`Gulp` 等)进行集成。由于本教程选择 `Webpack` ,所以只讲解与 `Webpack` 的集成。想了解如何与其它工具集成,请参考:[**官方文档 - installation**](https://babeljs.io/docs/setup/#installation)
->
-> 关键词: `babel-cli`, `.babelrc`, `preset`, `polyfill`
-
-
-
-- [1. 简介](#1-简介)
- - [1.1. Babel 能做什么?](#11-babel-能做什么)
- - [1.2. Babel 不能做什么?](#12-babel-不能做什么)
-- [2. 安装 Babel](#2-安装-babel)
- - [2.1. `babel-cli`](#21-babel-cli)
- - [2.2. `babel-node`](#22-babel-node)
- - [2.3. `babel-register`](#23-babel-register)
- - [2.4. `babel-core`](#24-babel-core)
- - [2.5. 与 webpack 集成](#25-与-webpack-集成)
-- [3. 配置 Babel](#3-配置-babel)
- - [3.1. `.babelrc`](#31-babelrc)
- - [3.2. 在其它工具中配置](#32-在其它工具中配置)
-- [4. 执行 Babel 生成的代码](#4-执行-babel-生成的代码)
- - [4.1. `babel-polyfill`](#41-babel-polyfill)
- - [4.2. `babel-runtime`](#42-babel-runtime)
-- [5. 引申和引用](#5-引申和引用)
-
-
-
-## 1. 简介
-
-### 1.1. Babel 能做什么?
-
-- Babel 通过语法转换来支持最新版本的 JavaScript (ES6),而不用等待浏览器的支持。
-- Babel 可以转换 React 的 JSX 语法和删除类型注释。
-- Babel 是由插件构建的。因此,你可以根据自己的需要订制。
-- 支持 source map ,所以您可以轻松调试您编译的代码。
-
-### 1.2. Babel 不能做什么?
-
-- Babel 只转换语法(如箭头函数),不支持新的全局变量。但是,您可以使用 [`babel-polyfill`](http://babeljs.io/docs/usage/polyfill/) 来辅助支持。
-
-## 2. 安装 Babel
-
-### 2.1. `babel-cli`
-
-`babel-cli` 是 Babel 的命令行工具。
-
-**安装**
-
-```bash
-# 本地安装
-$ npm install --save-dev babel-cli
-# 全局安装
-$ npm install --global babel-cli
-```
-
-**用法**
-
-```bash
-# 将编译后的结果直接输出至终端
-$ babel example.js
-
-# 将结果写入到指定的文件
-$ babel example.js --out-file compiled.js
-$ babel example.js -o compiled.js
-
-# 将一个目录整个编译成一个新的目录
-$ babel src --out-dir lib
-$ babel src -d lib
-```
-
-#### 与 `package.json` 集成
-
-> :pushpin: **提示:**
->
-> 建议使用本地安装方式安装 `babel-cli` 。
->
-> 原因在与:
->
-> 1. 在同一台机器上的不同项目或许会依赖不同版本的 Babel 并允许你有选择的更新。
-> 2. 这意味着你对工作环境没有隐式依赖,这让你的项目有很好的可移植性并且易于安装。
-
-本地安装 `babel-cli` ,直接使用 `babel` 命令将无法识别。你可以选在在 `package.json` 文件的 `scripts` 属性中定义命令。`npm` 会自动找到本地安装的库。
-
-```json
-{
- "scripts": {
- "build": "babel src -d lib"
- },
- "devDependencies": {
- "babel-cli": "^6.0.0"
- }
-}
-```
-
-现在可以在终端里执行命令:
-
-```bash
-$ npm run build
-```
-
-> :flashlight: **示例 DEMO01:** ([**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter04/babel/demo01))
->
-> 说明:
->
-> 示例的上一级目录 [`codes/chapter04/babel`](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter04/babel) 已经配好了配置文件。
->
-> 在 `codes/chapter04/babel` 路径下执行命令:
->
-> ```bash
-> $ npm install
-> $ npm run demo01
-> ```
->
-> 会生成一个 `dist/demo01` 目录,其中就是被转码后的文件。
-
-### 2.2. `babel-node`
-
-`babel-cli` 工具自带一个 `babel-node` 命令,提供一个支持 ES6 的 REPL 环境。它支持 Node 的 REPL 环境的所有功能,而且可以直接运行 ES6 代码。
-
-它不用单独安装,而是随 `babel-cli` 一起安装。然后,执行 `babel-node` 就进入 PEPL 环境。
-
-然后用 `babel-node` 来替代 `node` 运行所有的代码:
-
-```bash
-$ babel-node
-> (x => x * 2)(1)
-2
-```
-
-`babel-node` 命令可以直接运行 ES6 脚本:
-
-```bash
-$ babel-node example.js
-```
-
-如果用 npm 的话只需要这样做:
-
-```json
-{
- "scripts": {
- "script-name": "babel-node script.js"
- },
- "devDependencies": {
- "babel-cli": "^6.0.0"
- }
-}
-```
-
-然后,执行命令:
-
-```bash
-$ npm run babel-node
-```
-
-### 2.3. `babel-register`
-
-下一个常用的运行 Babel 的方法是通过 `babel-register`。这种方法只需要引入文件就可以运行 Babel,或许能更好地融入你的项目设置。
-
-> :warning: **注意:**
->
-> 这种方法并不适合正式产品环境使用。 直接部署用此方式编译的代码不是好的做法。 在部署之前预先编译会更好。
->
-> 不过用在构建脚本或是其他本地运行的脚本中是非常合适的。
-
-**安装**
-
-```bash
-$ npm install --save-dev babel-register
-```
-
-**使用**
-
-1. 创建 `index.js` 文件:
-
-```js
-console.log('Hello world!');
-```
-
-这是,使用 `node index.js` 来运行它是不会使用 Babel 来编译的。所以我们需要设置 `babel-register`。.
-
-2. 创建 `register.js` 文件:
-
-```js
-require('babel-register');
-require('./index.js');
-```
-
-这样做可以把 Babel *注册*到 Node 的模块系统中并开始编译其中 `require` 的所有文件。
-
-3. 执行命令
-
- 现在我们可以使用 `register.js` 来代替 `node index.js` 来运行了。
-
-```bash
-$ node register.js
-```
-
-需要注意的是:你不能在你要编译的文件内同时注册 Babel,因为 node 会在 Babel 编译它之前就将它执行了。
-
-> :flashlight: **示例 DEMO02:** ([**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter04/babel/demo02))
->
-> 说明:
->
-> 示例的上一级目录 [`codes/chapter04/babel`](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter04/babel) 已经配好了配置文件。
->
-> 在 `codes/chapter04/babel` 路径下执行命令:
->
-> ```bash
-> $ npm install
-> $ npm run demo02
-> ```
->
-> 控制台会打印如下内容:
->
-> ```
-> > node demo02/register.js
->
-> Hello world!
-> ```
-
-### 2.4. `babel-core`
-
-如果你需要以编程的方式来使用 Babel,可以使用 `babel-core` 这个包。
-
-**安装**
-
-```bash
-$ npm install babel-core
-```
-
-**使用**
-
-在代码中引入 `babel-core`
-
-```js
-var babel = require('babel-core');
-```
-
-**编译 API**
-
-```js
-# 如果是字符串形式的 JavaScript 代码,可以使用 transform 编译
-babel.transform("code();", options);
-// => { code, map, ast }
-
-# 如果是文件的话,异步编译使用 transformFile
-babel.transformFile("filename.js", options, function(err, result) {
- result; // => { code, map, ast }
-});
-# 如果是文件的话,同步编译使用 transformFileSync
-babel.transformFileSync("filename.js", options);
-// => { code, map, ast }
-
-# 要是已经有一个 Babel AST(抽象语法树)了就可以直接从 AST 进行转换
-babel.transformFromAst(ast, code, options);
-// => { code, map, ast }
-```
-
-### 2.5. 与 webpack 集成
-
-> :pushpin: **提示:**
->
-> 本教程由于选择的编译工具为 `webpack` ,所以这里只介绍与 `webpack` 的集成。
->
-> 实际上,Babel 还可以与其它许多工具集成,更多内容参考:[官方文档 - setup](https://babeljs.io/docs/setup/#installation)
-
-**安装**
-
-```bash
-$ npm install --save-dev babel-loader babel-core
-```
-
-**配置 **
-
-在 Chapter03 的 [Webpack 资源管理](https://github.com/dunwu/frontend-tutorial/tree/master/docs/chapter03/webpack/asset-management.md) 一文中,介绍过使用 babel-loader 来处理 React 语法。
-
-在 **webpack.config.js** 配置如下:
-
-```js
-// 关于模块配置
-module: {
-
- // 模块规则(配置 loader、解析器等选项)
- rules: [
- {
- // 语义解释器,将 js/jsx 文件中的 es2015/react 语法自动转为浏览器可识别的 Javascript 语法
- test: /\.jsx?$/,
- include: path.resolve(__dirname, "app"),
- loader: "babel-loader",
- },
- ]
-},
-```
-
-> :flashlight: **示例**
->
-> **chapter03-jigsaw:** ([**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/jigsaw))
->
-> **chapter04-jigsaw:** ([**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter04/jigsaw))
->
-> 说明:
->
-> `chapter04-jigsaw` 和 `chapter03-jigsaw` 相比,多了一个 `.babelrc` 文件。它其实是将 `chapter03-jigsaw` 中的 `webpack.common.js` 文件里的 babel-loader 的配置移入了 `.babelrc` 文件。
->
-> 这两个代码目录的执行结果完全相同。
->
-> 执行方法:
->
-> ```bash
-> $ npm install
-> # 开发环境 - 本地启动一个访问地址为 localhost:9000 的 web app
-> $ npm run dev
-> # 开发环境 - 生成一个 dist 目录,其中打包了所有资源文件,在浏览器打开 index.html,可以看到和开发环境差不多的展示。
-> $ npm run prod
-> # 清除输出目录 dist
-> $ npm run clean
-> ```
-
-## 3. 配置 Babel
-
-> :pushpin: 提示:
->
-> 由于 `Babel` 是一个非常灵活的通用编译器,因此默认情况下它反而什么都不做。
->
-> 你需要通过配置文件,明确地告诉 Babel 应该要做什么。
-
-### 3.1. `.babelrc`
-
-`.babelrc` 文件是 `Babel` 的默认配置文件。
-
-`.babelrc` **文件的内容形式就是序列化的 JSON。**
-
-该文件用来设置转码规则(presets)和插件(plugins),基本格式如下:
-
-```json
-{
- "presets": [],
- "plugins": []
-}
-```
-
-#### 转码规则(preset)
-
-转码规则可以告诉 Babel 去处理什么语法。
-
-常见的转码规则有:
-
-- `babel-preset-es2015`
-
- 这是 ES2015(最新版本的 JavaScript 标准,也叫做 ES6)的转码规则。使用它后,Babel 可以将 ES6 语法转码为普通 JavaScript(即 ES5) 语法。
-
-- `babel-preset-react`
-
- 这是 React 的转码规则。使用它后,Babel 可以将 React 语法转码为普通 JavaScript 语法。
-
-- `babel-preset-stage-x`
-
- 这是 ES7 不同阶段语法提案的转码规则。使用它后,Babel 可以将 ES7 不同阶段语法转码为普通 JavaScript 语法。
-
- > :pushpin: 提示:
- >
- > JavaScript 还有一些提案,正在积极通过 TC39(ECMAScript 标准背后的技术委员会)的流程成为标准的一部分。
- >
- > 这个流程分为 5(0-4)个阶段。 随着提案得到越多的关注就越有可能被标准采纳,于是他们就继续通过各个阶段,最终在阶段 4 被标准正式采纳。以下是 4 个不同阶段的(打包的)预设:
- >
- > - `babel-preset-stage-0`
- > - `babel-preset-stage-1`
- > - `babel-preset-stage-2`
- > - `babel-preset-stage-3`
- >
- > stage-4 预设是不存在的因为它就是上面的 `es2015` 预设。
- >
- > 以上每种预设都依赖于紧随的后期阶段预设。例如,`babel-preset-stage-1` 依赖 `babel-preset-stage-2`,后者又依赖`babel-preset-stage-3`。
-
-**安装**
-
-```bash
-# es2015(即ES6)语法转码规则
-$ npm install --save-dev babel-preset-es2015
-
-# react 语法转码规则
-$ npm install --save-dev babel-preset-react
-
-# stage 是指 ES7 不同阶段的语法转码规则,选装一个即可
-$ npm install --save-dev babel-preset-stage-0
-$ npm install --save-dev babel-preset-stage-1
-$ npm install --save-dev babel-preset-stage-2
-$ npm install --save-dev babel-preset-stage-3
-```
-
-**使用**
-
-安装完后,需要在配置文件 `.babelrc` 中引入项目中实际需要的预设转码规则,让 Babel 得以知道规则。
-
-形式如下:
-
-```json
-{
- "presets": ["es2015", "react", "stage-0"],
- "plugins": []
-}
-```
-
-#### 插件(plugins)
-
-插件是 Babel 的核心。
-
-Babel 插件大致分为三类:
-
-- 转码插件
-
- 有很多种插件:将 ES6 / ES2015 转换为 ES5,转换为 ES3,minification,JSX,flow,实验功能等等。
-
-* 语法插件
-
- 这些只是使转码插件能够解析某些功能(转码插件已经包含语法插件,因此这两个功能你都不需要):`babel-plugin-syntax-x`
-
-* 助手
-
- 这些主要用于各种插件内部使用:`babel-helper-x`。
-
-更多插件请在 [npm](https://www.npmjs.com/search?q=babel-plugin) 搜索(真的好多!)
-
-更详细介绍请参考:[官方文档 - 插件](http://babeljs.io/docs/plugins)
-
-### 3.2. 在其它工具中配置
-
-> :pushpin: **提示:**
->
-> 除了在 `.babelrc` 文件中定义 Babel 配置。实际上,还可以在其他工具中对其进行配置。
-
-#### 在 `package.json` 中配置
-
-可以在 `package.json` 文件的 `babel` 属性中配置 Babel 规则。
-
-配置方法与 `.babelrc` 文件完全相同。
-
-形式如下:
-
-```json
-"babel": {
- "presets": [
- "es2015"
- ]
- "plugins": []
-},
-```
-
-#### 在 `webpack.config.js` 中配置
-
-可以在 `webpack.config.js` 文件配置 `babel-loader` 时,直接在 `options` 属性中配置 Babel 规则。
-
-形式如下:
-
-```js
-{
- test: /\.jsx?$/,
- include: path.resolve(__dirname, "app"),
- exclude: /node_modules/,
- loader: "babel-loader",
- options: {
- presets: [
- [
- "es2015",
- {
- "modules": false
- }
- ],
- "react"
- ],
- plugins: [
- "syntax-dynamic-import", // 动态导入插件
- "react-hot-loader/babel" // 开启 React 代码的模块热替换(HMR)
- ]
- },
-}
-```
-
-## 4. 执行 Babel 生成的代码
-
-即便你已经用 Babel 编译了你的代码,但这还不算完。
-
-### 4.1. `babel-polyfill`
-
-Babel 几乎可以编译所有新潮的 JavaScript 语法,但对于 APIs 来说却并非如此。
-
-比方说,下列含有箭头函数的需要编译的代码:
-
-```js
-function addAll() {
- return Array.from(arguments).reduce((a, b) => a + b);
-}
-```
-
-最终会变成这样:
-
-```js
-function addAll() {
- return Array.from(arguments).reduce(function(a, b) {
- return a + b;
- });
-}
-```
-
-然而,它依然无法随处可用。因为并非所有的 JavaScript 环境都支持 `Array.from`。
-
-为了解决这个问题,我们使用一种叫做 [Polyfill(代码填充,也可译作兼容性补丁)](https://remysharp.com/2010/10/08/what-is-a-polyfill) 的技术。 简单地说,polyfill 即是在当前运行环境中用来复制(意指模拟性的复制,而不是拷贝)尚不存在的原生 api 的代码。 能让你提前使用还不可用的 APIs,`Array.from` 就是一个例子。
-
-Babel 提供了 `babel-polyfill` 来支持 polyfill 。
-
-**安装**
-
-```bash
-$ npm install --save babel-polyfill
-```
-
-**使用**
-
-然后,只需要在文件顶部导入 `babel-polyfill` 就可以了:
-
-```js
-import 'babel-polyfill';
-```
-
-### 4.2. `babel-runtime`
-
-babel-runtime 与 polyfill 类似,不同之处在于它不修改全局范围,并且与 `babel-plugin-transform-runtime`(通常在库/插件代码中)一起使用。
-
-为了实现 ECMAScript 规范的细节,Babel 会使用“助手”方法来保持生成代码的整洁。
-
-由于这些助手方法可能会特别长并且会被添加到每一个文件的顶部,因此你可以把它们统一移动到一个单一的“运行时(runtime)”中去。
-
-通过安装 `babel-plugin-transform-runtime` 和 `babel-runtime` 来开始。
-
-```bash
-$ npm install --save-dev babel-plugin-transform-runtime
-$ npm install --save babel-runtime
-```
-
-然后更新 `.babelrc`:
-
-```json
-{
- "presets": ["es2015", "react", "stage-0"],
- "plugins": ["transform-runtime", "transform-es2015-classes"]
-}
-```
-
-现在,Babel 会把这样的代码:
-
-```js
-class Foo {
- method() {}
-}
-```
-
-编译成:
-
-```js
-import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
-import _createClass from 'babel-runtime/helpers/createClass';
-
-let Foo = (function() {
- function Foo() {
- _classCallCheck(this, Foo);
- }
-
- _createClass(Foo, [
- {
- key: 'method',
- value: function method() {}
- }
- ]);
-
- return Foo;
-})();
-```
-
-这样就不需要把 `_classCallCheck` 和 `_createClass` 这两个助手方法放进每一个需要的文件里去了。
-
-## 5. 引申和引用
-
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方**
- - [Babel 官网](https://babeljs.io/)
- - [Babel 中文网](https://www.babeljs.cn/)
- - [Babel Github](https://github.com/babel/babel)
diff --git a/frontend/es6/typescript.md b/frontend/es6/typescript.md
deleted file mode 100644
index 74b0b90c9f..0000000000
--- a/frontend/es6/typescript.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# TypeScript
-
-> **TypeScript 是 JavaScript 类型的超集,它可以编译成纯 JavaScript。**
->
-> TypeScript 可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。
->
-> 关键词: `typescript`, `tsc`
-
-## 入门
-
-(1)安装 TypeScript
-
-```sh
-npm install -g typescript
-```
-
-(2)编写 TypeScript 代码
-
-创建 greeter.ts 文件,内容如下:
-
-```js
-class Student {
- fullName: string;
- constructor(public firstName, public middleInitial, public lastName) {
- this.fullName = firstName + " " + middleInitial + " " + lastName;
- }
-}
-
-interface Person {
- firstName: string;
- lastName: string;
-}
-
-function greeter(person : Person) {
- return "Hello, " + person.firstName + " " + person.lastName;
-}
-
-let user = new Student("Jane", "M.", "User");
-
-document.body.innerHTML = greeter(user);
-```
-
-(3)使用 tsc 编译代码
-
-```
-tsc greeter.ts
-```
-
-输出结果为一个 greeter.js 文件,它包含了和输入文件中相同的 JavsScript 代码。
-
-(4)在 html 中引用编译后的 js 文件
-
-新建 greeter.html 文件,内容如下:
-
-```html
-
-
-
TypeScript Greeter
-
-
-
-
-```
-
-在浏览器里打开 greeter.html 运行这个应用。
-
-## 引申和引用
-
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方**
- - [TypeScript 官网](http://www.typescriptlang.org/)
- - [TypeScript 中文网](https://www.tslang.cn/index.html)
- - [TypeScript Github](https://github.com/Microsoft/TypeScript/)
- - [TypeScript 样例](https://github.com/Microsoft/TypeScriptSamples/)
-- **教程**
- - [TypeScript 入门教程](https://github.com/xcatliu/typescript-tutorial)
diff --git "a/frontend/mvc/Angular,React,Vue\346\257\224\350\276\203.md" "b/frontend/mvc/Angular,React,Vue\346\257\224\350\276\203.md"
deleted file mode 100644
index 1c9048f377..0000000000
--- "a/frontend/mvc/Angular,React,Vue\346\257\224\350\276\203.md"
+++ /dev/null
@@ -1,144 +0,0 @@
-# ngular、React、Vue
-
-
-
-
-
-- [框架成熟度](#框架成熟度)
-- [React,Angular 和 Vue 的比较](#reactangular-和-vue-的比较)
- - [Typescript,ES6 与 ES5](#typescriptes6-与-es5)
- - [JSX 还是 HTML](#jsx-还是-html)
- - [框架和库](#框架和库)
- - [状态管理和数据绑定](#状态管理和数据绑定)
- - [其他的编程概念](#其他的编程概念)
- - [灵活性与精简到微服务](#灵活性与精简到微服务)
- - [体积和性能](#体积和性能)
- - [测试](#测试)
- - [通用与原生 app](#通用与原生-app)
- - [学习曲线](#学习曲线)
-- [总结](#总结)
- - [如何选择 React、Vue、Angular?](#如何选择-reactvueangular)
-- [引申和引用](#引申和引用)
-
-
-
-## 框架成熟度
-
-活跃度:
-
-Github Star 数:React > Vue > Angular
-贡献者数:React > Angular > Vue
-
-
-
-## React,Angular 和 Vue 的比较
-
-React 和 Vue 都擅长处理组件:小型的无状态的函数接收输入和返回元素作为输出。
-
-### Typescript,ES6 与 ES5
-
-- React 专注于使用 Javascript ES6;
-- Vue 使用 Javascript ES5 或 ES6;
-- Angular 依赖于 TypeScript。
-
-> 注:与整个 JavaScript 语言相比,TypeScript 的用户群仍然很小。TypeScript 存在消失的可能性。
-
-### JSX 还是 HTML
-
-JSX 是一个类似 HTML 语法的可选预处理器,并随后在 JavaScript 中进行编译。JSX 对于开发来说是一个很大的优势,因为代码写在同一个地方,可以在代码完成和编译时更好地检查工作成果。当你在 JSX 中输入错误时,React 将不会编译,并打印输出错误的行号。
-
-JSX 意味着 React 中的所有内容都是 Javascript -- 用于 JSX 模板和逻辑。
-
-- React 使用 JSX;
-- Angular 2 继续把 'JS' 放到 HTML 中;Angular 模板使用特殊的 Angular 语法来增强 HTML;
-- Vue 具有“单个文件组件”。这似乎是对于关注分离的权衡 - 模板,脚本和样式在一个文件中,但在三个不同的有序部分中。
-
-注:把 html 放到 js 是好的选择,因为 js 比 html 更强大。
-
-### 框架和库
-
-- Angular 是一个框架而不是一个库,因为它提供了关于如何构建应用程序的强有力的约束,并且还提供了更多开箱即用的功能。
-- React 和 Vue 是很灵活的。他们的库可以和各种包搭配。
-
-### 状态管理和数据绑定
-
-React 经常与 Redux 在一起使用。Redux 以三个基本原则来自述:
-
-- 单一数据源(Single source of truth)
-- State 是只读的(State is read-only)
-- 使用纯函数执行修改(Changes are made with pure functions)
-
-整个应用程序的状态存储在单个 store 的状态树中。这有助于调试应用程序,一些功能更容易实现。状态是只读的,只能通过 action 来改变,以避免竞争条件(这也有助于调试)。
-
-React 和 Angular 之间的巨大差异是 单向与双向绑定。
-
-Vue 支持单向绑定和双向绑定(默认为单向绑定)。
-
-### 其他的编程概念
-
-Angular 包含依赖注入(dependency injection),即一个对象将依赖项(服务)提供给另一个对象(客户端)的模式。这导致更多的灵活性和更干净的代码。
-
-模型 - 视图 - 控制器模式(MVC)将项目分为三个部分:模型,视图和控制器。Angular(MVC 模式的框架)有开箱即用的 MVC 特性。React 只有 V —— 你需要自己解决 M 和 C。
-
-### 灵活性与精简到微服务
-
-React 和 Vue 通过只选择真正需要的东西,你可以更好地控制应用程序的大小。它们提供了更灵活的方式去把一个老应用的一部分从单页应用(SPA)转移到微服务。Angular 最适合单页应用(SPA),因为它可能太臃肿而不能用于微服务。
-
-React 可以让你将应用程序的一小部分替换成更好用的 JS 库,而不是期待你的框架能够创新。小巧,可组合的单一用途工具的理念永远不会过时。
-
-总结一下:Vue 有着很好的性能和高深的内存分配技巧。如果比较快慢的话,这些框架都非常接近(比如 Inferno)。请记住,性能基准只能作为考虑的附注,而不是作为判断标准。
-
-### 体积和性能
-
-Angular 框架非常臃肿。gzip 文件大小为 143k,而 Vue 为 23K,React 为 43k。
-
-为了提高性能,React 和 Vue 都使用了虚拟 DOM(Virtual DOM)。
-
-### 测试
-
-- Facebook 使用 Jest 来测试其 React 代码。
-- Angular 2 中使用 Jasmine 作为测试框架。
-- Vue 缺乏测试指导
-
-### 通用与原生 app
-
-React 和 Angular 都支持原生开发。Angular 拥有用于原生应用的 NativeScript(由 Telerik 支持)和用于混合开发的 Ionic 框架。借助 React,你可以试试 react-native-renderer 来构建跨平台的 iOS 和 Android 应用程序,或者用 react-native 开发原生 app。许多 app(包括 Facebook;查看更多的展示)都是用 react-native 构建的。
-
-### 学习曲线
-
-Angular 的学习曲线确实很陡。
-对于 React,你可能需要针对第三方库进行大量重大决策。
-Vue 学习起来很容易。
-
-总结:如果你是一个没有经验的 Javascript 开发人员 - 或者如果你在过去十年中主要使用 jQuery,那么你应该考虑使用 Vue。转向 React 时,思维方式的转换更为明显。Vue 看起来更像是简单的 Javascript,同时也引入了一些新的概念:组件,事件驱动模型和单向数据流。这同样是很小的部分。
-
-在调试方面,React 和 Vue 的黑魔法更少是一个加分项。找出 bug 更容易,因为需要看的地方少了,堆栈跟踪的时候,自己的代码和那些库之间有更明显的区别。
-
-## 总结
-
-### 如何选择 React、Vue、Angular?
-
-- 我应该选什么?如果你在 Google 工作:Angular
-- 如果你喜欢 TypeScript:Angular(或 React)
-- 如果你喜欢面向对象编程(OOP): Angular
-- 如果你需要指导手册,架构和帮助:Angular
-- 如果你在 Facebook 工作:React
-- 如果你喜欢灵活性:React
-- 如果你喜欢大型的技术生态系统:React
-- 如果你喜欢在几十个软件包中进行选择:React
-- 如果你喜欢 JS 和“一切都是 Javascript 的方法”:React
-- 如果你喜欢真正干净的代码:Vue
-- 如果你想要最平缓的学习曲线:Vue
-- 如果你想要最轻量级的框架:Vue
-- 如果你想在一个文件中分离关注点:Vue
-- 如果你一个人工作,或者有一个小团队:Vue(或 React)
-- 如果你的应用程序往往变得非常大:Angular(或 React)
-- 如果你想用 react-native 构建一个应用程序:React
-- 如果你想在圈子中有很多的开发者:Angular 或 React
-- 如果你与设计师合作,并需要干净的 HTML 文件:Angular or Vue
-- 如果你喜欢 Vue 但是害怕有限的技术生态系统:React
-- 如果你不能决定,先学习 React,然后 Vue,然后 Angular。
-
-## 引申和引用
-
-- [[译] 2017 年比较 Angular、React、Vue 三剑客](https://juejin.im/post/5a0d5df1f265da43062a542f)
diff --git a/frontend/mvc/angular/angular.md b/frontend/mvc/angular/angular.md
deleted file mode 100644
index eccc8ea607..0000000000
--- a/frontend/mvc/angular/angular.md
+++ /dev/null
@@ -1,416 +0,0 @@
-# Angular 教程
-
-> **Angular 是一个用 HTML 和 TypeScript 构建客户端应用的平台与框架。**
-
-
-
-- [入门](#入门)
-- [架构](#架构)
-- [模块](#模块)
- - [@NgModule 元数据](#ngmodule-元数据)
- - [NgModule 和组件](#ngmodule-和组件)
- - [ES2015 模块](#es2015-模块)
-- [组件](#组件)
- - [组件的元数据](#组件的元数据)
- - [模板与视图](#模板与视图)
- - [模板语法](#模板语法)
- - [数据绑定](#数据绑定)
- - [管道](#管道)
- - [指令](#指令)
-- [服务与依赖注入](#服务与依赖注入)
- - [依赖注入](#依赖注入)
- - [提供服务](#提供服务)
-- [路由](#路由)
-- [引申和引用](#引申和引用)
-
-
-
-## 入门
-
-> 先决条件:Node.js
-
-(1)执行 npm install -g @angular/cli 安装 Angular CLI
-
-(2)创建工作空间和初始应用
-
-Angular 工作空间就是你开发应用的上下文环境。 每个工作空间包含一些供一个或多个项目使用的文件。 每个项目都是一组由应用、库或端到端(e2e)测试构成的文件。
-
-执行命令 `ng new my-app`,根据提示选择需要的特性,Angular CLI 会安装必要的 Angular npm 包及其它依赖。
-
-(3)启动开发服务器
-
-执行下面的命令,ng serve 命令会自动打开浏览器,并访问 http://localhost:4200/
-
-```
-cd my-app
-ng serve --open
-```
-
-
-
-(4)添加组件
-
-组件是 Angular 应用中的基本构造块。 它们在屏幕上显示数据、监听用户输入,并根据这些输入采取行动。
-
-修改 ./src/app/app.component.ts
-
-```js
-@Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
-})
-export class AppComponent {
- title = 'My First Angular App!';
-}
-```
-
-修改 ./src/app/app.component.css
-
-```css
-h1 {
- color: #369;
- font-family: Arial, Helvetica, sans-serif;
- font-size: 250%;
-}
-```
-
-效果:
-
-
-
-## 架构
-
-**Angular 是一个用 HTML 和 TypeScript 构建客户端应用的平台与框架。**
-
-Angular 的基本构造块是 **NgModule**,它为**组件**提供了编译的上下文环境。 NgModule 会把相关的代码收集到一些功能集中。Angular 应用就是由一组 NgModule 定义出的。 应用至少会有一个用于引导应用的**根模块**,通常还会有很多**特性模块**。
-
-- 组件定义**视图**。视图是一组可见的屏幕元素,Angular 可以根据你的程序逻辑和数据来选择和修改它们。 每个应用都至少有一个根组件。
-- 组件使用**服务**。服务会提供那些与视图不直接相关的功能。服务提供商可以作为**依赖**被**注入**到组件中, 这能让你的代码更加模块化、可复用,而且高效。
-
-
-
-组件和模板共同定义了 Angular 的视图。
-
-- 组件类上的装饰器为其添加了元数据,其中包括指向相关模板的指针。
-- 组件模板中的指令和绑定标记会根据程序数据和程序逻辑修改这些视图。
-
-依赖注入器会为组件提供一些服务,比如路由器服务就能让你定义如何在视图之间导航。
-
-## 模块
-
-Angular 应用是模块化的,它拥有自己的模块化系统,称作 **NgModule**。
-
-一个 NgModule 就是一个容器,用于存放一些内聚的代码块,这些代码块专注于某个应用领域、某个工作流或一组紧密相关的功能。 它可以包含一些组件、服务或其它代码文件,其作用域由包含它们的 NgModule 定义。 它还可以导入一些由其它模块中导出的功能,并导出一些指定的功能供其它 NgModule 使用。
-
-每个 Angular 应用都有一个根模块,通常命名为 AppModule,并位于一个名叫 app.module.ts 的文件中。根模块提供了用来启动应用的**引导**机制。 一个应用通常会包含很多功能模块。
-
-把你的代码组织成一些清晰的功能模块,可以帮助管理复杂应用的开发工作并实现可复用性设计。 另外,这项技术还能让你获得惰性加载(也就是按需加载模块)的优点,以尽可能减小启动时需要加载的代码体积。
-
-### @NgModule 元数据
-
-NgModule 是一个带有 `@NgModule()` 装饰器的类。@NgModule() 装饰器是一个函数,它接受一个元数据对象,该对象的属性用来描述这个模块。其中最重要的属性如下。
-
-- declarations(可声明对象表) —— 那些属于本 NgModule 的组件、指令、管道。
-- exports(导出表) —— 那些能在其它模块的组件模板中使用的可声明对象的子集。
-- imports(导入表) —— 那些导出了本模块中的组件模板所需的类的其它模块。
-- providers —— 本模块向全局服务中贡献的那些服务的创建器。 这些服务能被本应用中的任何部分使用。(你也可以在组件级别指定服务提供商,这通常是首选方式。)
-- bootstrap —— 应用的主视图,称为根组件。它是应用中所有其它视图的宿主。只有根模块才应该设置这个 bootstrap 属性。
-
-一个简单的根 NgModule 定义:
-
-```js
-import { NgModule } from '@angular/core';
-import { BrowserModule } from '@angular/platform-browser';
-@NgModule({
- imports: [BrowserModule],
- providers: [Logger],
- declarations: [AppComponent],
- exports: [AppComponent],
- bootstrap: [AppComponent]
-})
-export class AppModule {}
-```
-
-### NgModule 和组件
-
-NgModule 为其中的组件提供了一个**编译上下文环境**。根模块总会有一个根组件,并在引导期间创建它。 但是,任何模块都能包含任意数量的其它组件,这些组件可以通过路由器加载,也可以通过模板创建。那些属于这个 NgModule 的组件会共享同一个编译上下文环境。
-
-
-
-组件及其模板共同定义**视图**。组件还可以包含**视图层级结构**,它能让你定义任意复杂的屏幕区域,可以将其作为一个整体进行创建、修改和销毁。 一个视图层次结构中可以混合使用由不同 NgModule 中的组件定义的视图。 这种情况很常见,特别是对一些 UI 库来说。
-
-
-
-当你创建一个组件时,它直接与一个叫做**宿主视图**的视图关联起来。 宿主视图可以是视图层次结构的根,该视图层次结构可以包含一些**内嵌视图**,这些内嵌视图又是其它组件的宿主视图。 这些组件可以位于相同的 NgModule 中,也可以从其它 NgModule 中导入。 树中的视图可以嵌套到任意深度。
-
-### ES2015 模块
-
-NgModule 系统与 ES201)用来管理对象的模块系统不同,而且也没有直接关联。 这两种模块系统不同但互补。你可以使用它们来共同编写你的应用。
-
-JavaScript 中,每个文件是一个模块,文件中定义的所有对象都从属于那个模块。 通过 export 关键字,模块可以把它的某些对象声明为公共的。 其它 JavaScript 模块可以使用 import 语句来访问这些公共对象。
-
-```js
-// 导入模块
-import { NgModule } from '@angular/core';
-import { AppComponent } from './app.component';
-
-// 导出模块
-export class AppModule {}
-```
-
-## 组件
-
-组件控制屏幕上被称为**视图**的一小片区域。
-
-每个 Angular 应用都至少有一个组件,也就是**根组件**,它会把组件树和页面中的 DOM 连接起来。 每个组件都会定义一个类,其中包含应用的数据和逻辑,并与一个 HTML **模板**相关联,该模板定义了一个供目标环境下显示的视图。
-
-### 组件的元数据
-
-@Component() 装饰器表明紧随它的那个类是一个组件,并提供模板和该组件专属的元数据。
-
-> @Component() 装饰器表明紧随它的那个类是一个组件,并提供模板和该组件专属的元数据。
-
-组件的元数据告诉 Angular 到哪里获取它需要的主要构造块,以创建和展示这个组件及其视图。 具体来说,它把一个模板(无论是直接内联在代码中还是引用的外部文件)和该组件关联起来。 该组件及其**模板**,共同描述了一个**视图**。
-
-除了包含或指向模板之外,@Component 的元数据还会配置要如何在 HTML 中引用该组件,以及该组件需要哪些服务等等。
-
-最常用的 @Component 配置选项:
-
-- `selector` —— 是一个 CSS 选择器,它会告诉 Angular,一旦在模板 HTML 中找到了这个选择器对应的标签,就创建并插入该组件的一个实例。 比如,如果应用的 HTML 中包含 `
`,Angular 就会在这些标签中插入一个 HeroListComponent 实例的视图。
-- `templateUrl` —— 该组件的 HTML 模板文件相对于这个组件文件的地址。 另外,你还可以用 template 属性的值来提供内联的 HTML 模板。 这个模板定义了该组件的宿主视图。
-- `providers` —— 当前组件所需的服务提供商的一个数组。在这个例子中,它告诉 Angular 该如何提供一个 HeroService 实例,以获取要显示的英雄列表。
-
-### 模板与视图
-
-你要通过组件的配套模板来定义其视图。模板就是一种 HTML,它会告诉 Angular 如何渲染该组件。
-
-视图通常会分层次进行组织,让你能以 UI 分区或页面为单位进行修改、显示或隐藏。与组件直接关联的模板会定义该组件的**宿主视图**。该组件还可以定义一个**带层次结构的视图**,它包含一些**内嵌的视图**作为其它组件的宿主。
-
-带层次结构的视图可以包含同一模块(NgModule)中组件的视图,也可以(而且经常会)包含其它模块中定义的组件的视图。
-
-### 模板语法
-
-模板很像标准的 HTML,但是它还包含 Angular 的[模板语法](https://angular.cn/guide/template-syntax),这些模板语法可以根据你的应用逻辑、应用状态和 DOM 数据来修改这些 HTML。 你的模板可以使用**数据绑定**来协调应用和 DOM 中的数据,使用**管道**在显示出来之前对其进行转换,使用**指令**来把程序逻辑应用到要显示的内容上。
-
-模板示例:
-
-```html
-
Hero List
-
-
Pick a hero from the list
-
-
-
-```
-
-> 说明:上例中,除了 HTML 元素外,还包括一些 Angular 的模板语法元素:
->
-> - `*ngFor` 指令告诉 Angular 在一个列表上进行迭代。
-> - `{ {hero.name} }`、`(click)` 和 `[hero]` 把程序数据绑定到及绑定回 DOM,以响应用户的输入。更多内容参见稍后的数据绑定部分。
-> - 模板中的 `
` 标签是一个代表新组件 HeroDetailComponent 的元素。 HeroDetailComponent(代码略)定义了 HeroListComponent 的英雄详情子视图。 注意观察像这样的自定义组件是如何与原生 HTML 元素无缝的混合在一起的。
-
-### 数据绑定
-
-模板会把 HTML 和 Angular 的标记(markup)组合起来,这些标记可以在 HTML 元素显示出来之前修改它们。模板中的**指令**会提供程序逻辑,而**绑定标记**会把你应用中的数据和 DOM 连接在一起。 有两种类型的数据绑定:
-
-- **事件绑定**让你的应用可以通过更新应用的数据来响应目标环境下的用户输入。
-- **属性绑定**让你将从应用数据中计算出来的值插入到 HTML 中。
-
-在视图显示出来之前,Angular 会先根据你的应用数据和逻辑来运行模板中的指令并解析绑定表达式,以修改 HTML 元素和 DOM。
-
-Angular 支持**双向数据绑定**,这意味着 DOM 中发生的变化(比如用户的选择)同样可以反映回你的程序数据中。
-
-下图显示了数据绑定标记的四种形式。每种形式都有一个方向 —— 从组件到 DOM、从 DOM 到组件或双向。
-
-
-
-在双向绑定中,数据属性值通过属性绑定从组件流到输入框。用户的修改通过事件绑定流回组件,把属性值设置为最新的值。
-
-### 管道
-
-Angular 的管道可以让你在模板中声明显示值的转换逻辑。 带有 `@Pipe` 装饰器的类中会定义一个转换函数,用来把输入值转换成供视图显示用的输出值。
-
-Angular 自带了很多管道,完整的列表参见 [Pipes API 列表](https://angular.cn/api?type=pipe)。你也可以自己定义一些新管道。
-
-要在 HTML 模板中指定值的转换方式,请使用 [管道操作符 (|)](https://angular.cn/guide/template-syntax#pipe)。
-
-```html
-{ {interpolated_value | pipe_name} }
-```
-
-你可以把管道串联起来,把一个管道函数的输出送给另一个管道函数进行转换。 管道还能接收一些参数,来控制它该如何进行转换。比如,你可以把要使用的日期格式传给 `date` 管道:
-
-```html
-
-
-Today is { {today | date} }
-
-
-
-The date is { {today | date:'fullDate'} }
-
-
-
-The time is { {today | date:'shortTime'} }
-```
-
-### 指令
-
-Angular 的模板是**动态的**。当 Angular 渲染它们的时候,会根据**指令**给出的指示对 DOM 进行转换。 指令就是一个带有 `@Directive()` 装饰器的类。
-
-组件从技术角度上说就是一个指令,但是由于组件对 Angular 应用来说非常独特、非常重要,因此 Angular 专门定义了 `@Component()` 装饰器,它使用一些面向模板的特性扩展了 `@Directive()` 装饰器。
-
-除组件外,还有两种指令:**结构型指令**和**属性型指令**。 Angular 本身定义了一系列这两种类型的指令,你也可以使用 `@Directive()` 装饰器来定义自己的指令。
-
-像组件一样,指令的元数据把它所装饰的指令类和一个 `selector` 关联起来,`selector` 用来把该指令插入到 HTML 中。 在模板中,指令通常作为属性出现在元素标签上,可能仅仅作为名字出现,也可能作为赋值目标或绑定目标出现。
-
-#### 结构型指令
-
-**结构型指令**通过添加、移除或替换 DOM 元素来修改布局。 这个范例模板使用了两个内置的结构型指令来为要渲染的视图添加程序逻辑:
-
-```html
-
-
-```
-
-- `*ngFor` 是一个迭代器,它要求 Angular 为 heroes 列表中的每个 `` 渲染出一个 ` `。
-- `*ngIf` 是个条件语句,只有当选中的英雄存在时,它才会包含 HeroDetail 组件。
-
-#### 属性型指令
-
-*属性型指令*会修改现有元素的外观或行为。 在模板中,它们看起来就像普通的 HTML 属性一样,因此得名“属性型指令”。
-
-`ngModel` 指令就是属性型指令的一个例子,它实现了双向数据绑定。 `ngModel` 修改现有元素(一般是 ` `)的行为:设置其显示属性值,并响应 change 事件。
-
-```html
-
-```
-
-## 服务与依赖注入
-
-**服务**是一个广义的概念,它包括应用所需的任何值、函数或特性。狭义的服务是一个明确定义了用途的类。它应该做一些具体的事,并做好。
-
-Angular 把组件和服务区分开,以提高模块性和复用性。 通过把组件中和视图有关的功能与其他类型的处理分离开,你可以让组件类更加精简、高效。
-
-理想情况下,组件的工作只管用户体验,而不用顾及其它。 它应该提供用于数据绑定的属性和方法,以便作为视图(由模板渲染)和应用逻辑(通常包含一些**模型**的概念)的中介者。
-
-组件应该把诸如从服务器获取数据、验证用户输入或直接往控制台中写日志等工作委托给各种服务。通过把各种处理任务定义到可注入的服务类中,你可以让它被任何组件使用。 通过在不同的环境中注入同一种服务的不同提供商,你还可以让你的应用更具适应性。
-
-Angular 不会**强制**你遵循这些原则。它只会通过**依赖注入**让你能更容易地将应用逻辑分解为服务,并让这些服务可用于各个组件中。
-
-服务范例一:
-
-```js
-export class Logger {
- log(msg: any) {
- console.log(msg);
- }
- error(msg: any) {
- console.error(msg);
- }
- warn(msg: any) {
- console.warn(msg);
- }
-}
-```
-
-服务也可以依赖其它服务。服务范例二:
-
-```js
-export class HeroService {
- private heroes: Hero[] = [];
-
- constructor(
- private backend: BackendService,
- private logger: Logger) { }
-
- getHeroes() {
- this.backend.getAll(Hero).then( (heroes: Hero[]) => {
- this.logger.log(`Fetched ${heroes.length} heroes.`);
- this.heroes.push(...heroes); // fill cache
- });
- return this.heroes;
- }
-}
-```
-
-### 依赖注入
-
-对于与特定视图无关并希望跨组件共享的数据或逻辑,可以创建**服务**类。服务类的定义通常紧跟在 `@Injectable()` 装饰器之后。该装饰器提供的元数据可以让你的服务作为**依赖**被**注入**到客户组件中。
-
-- **注入器**是主要的机制。你不用自己创建 Angular 注入器。Angular 会在启动过程中为你创建全应用级注入器以及所需的其它注入器。你不用自己创建注入器。
-- 该注入器会创建依赖、维护一个**容器**来管理这些依赖,并尽可能复用它们。
-- **提供商**是一个对象,用来告诉注入器应该如何获取或创建依赖。
-
-你的应用中所需的任何依赖,都必须使用该应用的注入器来注册一个提供商,以便注入器可以使用这个提供商来创建新实例。对于服务,该提供商通常就是服务类本身。
-
-> 依赖不一定是服务 —— 它还可能是函数或值。
-
-当 Angular 创建组件类的新实例时,它会通过查看该组件类的构造函数,来决定该组件依赖哪些服务或其它依赖项。 比如 `HeroListComponent` 的构造函数中需要 `HeroService`:
-
-```js
-constructor(private service: HeroService) { }
-```
-
-当 Angular 发现某个组件依赖某个服务时,它会首先检查是否该注入器中已经有了那个服务的任何现有实例。如果所请求的服务尚不存在,注入器就会使用以前注册的服务提供商来制作一个,并把它加入注入器中,然后把该服务返回给 Angular。
-
-当所有请求的服务已解析并返回时,Angular 可以用这些服务实例为参数,调用该组件的构造函数。
-
-`HeroService` 的注入过程如下所示:
-
-
-
-### 提供服务
-
-对于要用到的任何服务,你必须至少注册一个**提供商**。服务可以在自己的元数据中把自己注册为提供商,这样可以让自己随处可用。或者,你也可以为特定的模块或组件注册提供商。要注册提供商,就要在服务的 `@Injectable()` 装饰器中提供它的元数据,或者在`@NgModule()` 或 `@Component()` 的元数据中。
-
-- 默认情况下,Angular CLI 的 [`ng generate service`](https://angular.cn/cli/generate) 命令会在 `@Injectable()` 装饰器中提供元数据来把它注册到根注入器中。本教程就用这种方法注册了 HeroService 的提供商:
-
-```js
-@Injectable({
- providedIn: 'root',
-})
-```
-
-当你在根一级提供服务时,Angular 会为 HeroService 创建一个单一的共享实例,并且把它注入到任何想要它的类中。这种在 `@Injectable` 元数据中注册提供商的方式还让 Angular 能够通过移除那些从未被用过的服务来优化大小。
-
-- 当你使用[特定的 NgModule](https://angular.cn/guide/architecture-modules) 注册提供商时,该服务的同一个实例将会对该 NgModule 中的所有组件可用。要想在这一层注册,请用 `@NgModule()` 装饰器中的 `providers` 属性:
-
-```
-content_copy@NgModule({ providers: [ BackendService, Logger ], ... })
-```
-
-- 当你在组件级注册提供商时,你会为该组件的每一个新实例提供该服务的一个新实例。 要在组件级注册,就要在 `@Component()` 元数据的 `providers` 属性中注册服务提供商。
-
-src/app/hero-list.component.ts (component providers)`content_copy@Component({ selector: 'app-hero-list', templateUrl: './hero-list.component.html', providers: [ HeroService ] })`
-
-要了解更多细节,请参见[依赖注入](https://angular.cn/guide/dependency-injection)一节。
-
-## 路由
-
-Angular 的 Router 模块提供了一个服务,它可以让你定义在应用的各个不同状态和视图层次结构之间导航时要使用的路径。 它的工作模型基于人们熟知的浏览器导航约定:
-
-- 在地址栏输入 URL,浏览器就会导航到相应的页面。
-- 在页面中点击链接,浏览器就会导航到一个新页面。
-- 点击浏览器的前进和后退按钮,浏览器就会在你的浏览历史中向前或向后导航。
-
-不过路由器会把类似 URL 的路径映射到视图而不是页面。 当用户执行一个动作时(比如点击链接),本应该在浏览器中加载一个新页面,但是路由器拦截了浏览器的这个行为,并显示或隐藏一个视图层次结构。
-
-如果路由器认为当前的应用状态需要某些特定的功能,而定义此功能的模块尚未加载,路由器就会按需**惰性加载**此模块。
-
-路由器会根据你应用中的导航规则和数据状态来拦截 URL。 当用户点击按钮、选择下拉框或收到其它任何来源的输入时,你可以导航到一个新视图。 路由器会在浏览器的历史日志中记录这个动作,所以前进和后退按钮也能正常工作。
-
-要定义导航规则,你就要把**导航路径**和你的组件关联起来。 路径(path)使用类似 URL 的语法来和程序数据整合在一起,就像模板语法会把你的视图和程序数据整合起来一样。 然后你就可以用程序逻辑来决定要显示或隐藏哪些视图,以根据你制定的访问规则对用户的输入做出响应。
-
-## 引申和引用
-
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方**
- - [Angular 官网](https://angular.io)
- - [Angular 中文网](https://angular.cn/)
- - [Angular Github](https://github.com/angular/angular)
- - [AngularJS Github](https://github.com/angular/angular.js)
diff --git a/frontend/mvc/react/react.md b/frontend/mvc/react/react.md
deleted file mode 100644
index 21860df8f2..0000000000
--- a/frontend/mvc/react/react.md
+++ /dev/null
@@ -1,2554 +0,0 @@
----
-id: react-quickstart
-title: React 快速入门
----
-
-# React 快速入门
-
-
-
-- [安装](#安装)
-- [Introducing JSX](#introducing-jsx)
- - [JSX 中嵌入表达式](#jsx-中嵌入表达式)
- - [JSX 也是一个表达式](#jsx-也是一个表达式)
- - [用 JSX 指定属性值](#用-jsx-指定属性值)
- - [用 JSX 指定子元素](#用-jsx-指定子元素)
- - [JSX 防止注入攻击](#jsx-防止注入攻击)
- - [JSX 表示对象](#jsx-表示对象)
-- [渲染元素](#渲染元素)
- - [渲染一个元素到 DOM](#渲染一个元素到-dom)
- - [更新已渲染的元素](#更新已渲染的元素)
- - [React 只更新必需要更新的部分](#react-只更新必需要更新的部分)
-- [组件(Components) 和 属性(Props)](#组件components-和-属性props)
- - [函数式组件和类组件](#函数式组件和类组件)
- - [渲染一个组件](#渲染一个组件)
- - [构成组件](#构成组件)
- - [提取组件](#提取组件)
- - [Props 是只读的](#props-是只读的)
-- [把函数式组件转化为类组件](#把函数式组件转化为类组件)
-- [在类组件中添加本地状态(state)](#在类组件中添加本地状态state)
-- [在类中添加生命周期方法](#在类中添加生命周期方法)
-- [正确地使用 State(状态)](#正确地使用-state状态)
- - [不要直接修改 state(状态)](#不要直接修改-state状态)
- - [state(状态) 更新可能是异步的](#state状态-更新可能是异步的)
- - [state(状态)更新会被合并](#state状态更新会被合并)
-- [数据向下流动](#数据向下流动)
- - [元素变量](#元素变量)
- - [使用逻辑 && 操作符的内联 if 用法](#使用逻辑--操作符的内联-if-用法)
- - [使用条件操作符的内联 If-Else](#使用条件操作符的内联-if-else)
- - [防止组件渲染](#防止组件渲染)
- - [多组件渲染](#多组件渲染)
- - [基本列表组件](#基本列表组件)
-- [键(Keys)](#键keys)
- - [使用 keys 提取组件](#使用-keys-提取组件)
- - [keys 在同辈元素中必须是唯一的](#keys-在同辈元素中必须是唯一的)
- - [在 JSX 中嵌入 map()](#在-jsx-中嵌入-map)
-- [受控组件(Controlled Components)](#受控组件controlled-components)
-- [textare 标签](#textare-标签)
-- [select 标签](#select-标签)
-- [处理多个输入元素](#处理多个输入元素)
-- [受控组件的替代方案](#受控组件的替代方案)
-- [添加第二个输入](#添加第二个输入)
-- [编写转换函数](#编写转换函数)
-- [状态提升(Lifting State Up)](#状态提升lifting-state-up)
-- [经验总结](#经验总结)
-- [包含](#包含)
-- [特例](#特例)
-- [如何看待?](#如何看待)
-
-
-
-## 安装
-
-1. 直接下载使用
-
-React 可以直接下载使用,下载包中也提供了很多学习的实例。
-
-你可以在官网 [http://facebook.github.io/react/](http://facebook.github.io/react/) 下载最新版。
-
-2. 通过 npm 使用 React
-
-```sh
-$ npm install -S react react-dom
-```
-
-3. 通过 yarn 使用 React
-
-```sh
-$ yarn add react react-dom
-```
-
-4. 使用各种快速构建工具
-
- 目前最流行的构建工具应该是 create-react-app,它使得用户可以通过命令就能快速构建 React 开发环境。
-
- create-react-app 自动创建的项目是基于 Webpack + ES6 。
-
-```sh
-$ npm install -g create-react-app
-$ create-react-app my-app
-$ cd my-app/
-$ npm start
-```
-
-## Introducing JSX
-
-考虑一下这个变量的声明:
-
-```
-const element = Hello, world! ;
-
-```
-
-这种有趣的标签语法既不是字符串也不是 HTML。
-
-这就是 JSX ,他是 JavaScrip 的一种扩展语法。我们推荐在 React 中使用这种语法来描述 UI 信息。JSX 可能会让你想起某种模板语言,但是它具有 JavaScrip 的全部能力。
-
-JSX 可以生成 React "元素"。我们将在[下一章](http://www.css88.com/react/docs/rendering-elements.html)探索如何把它渲染到 DOM 上。下面你可以找到 JSX 的基础知识,以帮助您开始使用。
-
-### JSX 中嵌入表达式
-
-你可以用 花括号 把任意的 [JavaScript 表达式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions) 嵌入到 JSX 中。
-
-例如,`2 + 2`, `user.firstName`, 和 `formatName(user)`,这些都是可用的表达式。
-
-```
-function formatName(user) {
- return user.firstName + ' ' + user.lastName;
-}
-
-const user = {
- firstName: 'Harper',
- lastName: 'Perez'
-};
-
-const element = (
-
- Hello, {formatName(user)}!
-
-);
-
-ReactDOM.render(
- element,
- document.getElementById('root')
-);
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/PGEjdG?editors=0010)。
-
-为便于阅读,我们将 JSX 分割成多行。我们推荐使用括号将 JSX 包裹起来,虽然这不是必须的,但这样做可以避免[分号自动插入](http://stackoverflow.com/q/2846283)的陷阱。
-
-### JSX 也是一个表达式
-
-编译之后,JSX 表达式就变成了常规的 JavaScript 对象。
-
-这意味着你可以在 `if` 语句或者是 `for` 循环中使用 JSX,用它给变量赋值,当做参数接收,或者作为函数的返回值。
-
-```
-function getGreeting(user) {
- if (user) {
- return Hello, {formatName(user)}! ;
- }
- return Hello, Stranger. ;
-}
-
-```
-
-### 用 JSX 指定属性值
-
-您可以使用双引号来指定字符串字面量作为属性值:
-
-```
-const element =
;
-
-```
-
-您也可以用花括号嵌入一个 JavaScript 表达式作为属性值:
-
-```
-const element = ;
-
-```
-
-在属性中嵌入 JavaScript 表达式时,不要使用引号来包裹大括号。否则,JSX 将该属性视为字符串字面量而不是表达式。对于字符串值你应该使用引号,对于表达式你应该使用大括号,但两者不能同时用于同一属性。
-
-### 用 JSX 指定子元素
-
-如果是空标签,您应该像 XML 一样,使用 `/>`立即闭合它:
-
-```
-const element = ;
-
-```
-
-JSX 标签可能包含子元素:
-
-```
-const element = (
-
-
Hello!
- Good to see you here.
-
-);
-
-```
-
-> **警告:**
->
-> 比起 HTML , JSX 更接近于 JavaScript , 所以 React DOM 使用驼峰(`camelCase`)属性命名约定, 而不是 HTML 属性名称。
->
-> 例如,`class` 在 JSX 中变为[`className`](https://developer.mozilla.org/en-US/docs/Web/API/Element/className),`tabindex` 变为 [`tabIndex`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex)。
-
-### JSX 防止注入攻击
-
-在 JSX 中嵌入用户输入是安全的:
-
-```
-const title = response.potentiallyMaliciousInput;
-// This is safe:
-const element = {title} ;
-
-```
-
-默认情况下, 在渲染之前, React DOM 会格式化([escapes](http://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-on-html)) JSX 中的所有值. 从而保证用户无法注入任何应用之外的代码. 在被渲染之前,所有的数据都被转义成为了字符串处理。 以避免 [XSS(跨站脚本)](https://en.wikipedia.org/wiki/Cross-site_scripting) 攻击。
-
-### JSX 表示对象
-
-Babel 将 JSX 编译成 `React.createElement()` 调用。
-
-下面的两个例子是是完全相同的:
-
-```
-const element = (
-
- Hello, world!
-
-);
-
-```
-
-```
-const element = React.createElement(
- 'h1',
- {className: 'greeting'},
- 'Hello, world!'
-);
-
-```
-
-`React.createElement()` 会执行一些检查来帮助你编写没有 bug 的代码,但基本上它会创建一个如下所示的对象:
-
-```
-// 注意: 这是简化的结构
-const element = {
- type: 'h1',
- props: {
- className: 'greeting',
- children: 'Hello, world'
- }
-};
-
-```
-
-这些对象被称作“React 元素”。你可以把他们想象成为你想在屏幕上显示内容的一种描述。React 会读取这些对象,用他们来构建 DOM,并且保持它们的不断更新。
-
-我们将在下一节中来探索如何将 React 元素渲染到 DOM 上。
-
-> **提示:**
->
-> 我们建议你去搜一下你用的编辑器的 "Babel" 语法方案, 以便 ES6 和 JSX 代码都能够被正确高亮的显示。
-
-## 渲染元素
-
-元素(Elements)是 React 应用中最小的建造部件(或者说构建块,building blocks)。
-
-一个元素用于描述你在将在屏幕上看到的内容:
-
-```
-const element = Hello, world ;
-
-```
-
-不同于浏览器的 DOM 元素, React 元素是普通的对象,非常容易创建。React DOM 会负责更新 DOM ,以匹配 React 元素(愚人码头注:DOM 元素与 React 元素保持一致)。
-
-> **注意:**
->
-> 有人可能会将元素与更广为人知的 "组件(Components)" 概念相混淆。我们将在[下一节](http://www.css88.com/react/docs/components-and-props.html)介绍组件。元素是构成组件的"材料", 所以我们建议你看完本节再进入下一节。
-
-### 渲染一个元素到 DOM
-
-我们假设你的 HTML 文件中的什么地方有这么一个``:
-
-```
-
-
-```
-
-我们称这个是一个 "根" DOM 节点,因为该节点内的所有内容都由 React DOM 管理。
-
-单纯用 React 构建的应用程序通常只有一个单独的 根 DOM 节点。但如果你要把 React 整合进现有的 app 中 ,那你可能会有多个相互独立的根 DOM 节点。
-
-要渲染一个 React 元素到一个 根 DOM 节点,吧它们传递给 `ReactDOM.render()` 方法:
-
-```
-const element = Hello, world ;
-ReactDOM.render(
- element,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/rrpgNB?editors=1010)。
-
-上面代码会在页面上显示 "Hello, world" 。
-
-### 更新已渲染的元素
-
-React 元素是 [不可突变(immutable)](https://en.wikipedia.org/wiki/Immutable_object) 的. 一旦你创建了一个元素, 就不能再修改其子元素或任何属性。一个元素就像电影里的一帧: 它表示在某一特定时间点的 UI 。
-
-就我们所知, 更新 UI 的唯一方法是创建一个新的元素, 并将其传入`ReactDOM.render()`方法.
-
-思考以下时钟例子:
-
-```
-function tick() {
- const element = (
-
-
Hello, world!
- It is {new Date().toLocaleTimeString()}.
-
- );
- ReactDOM.render(
- element,
- document.getElementById('root')
- );
-}
-
-setInterval(tick, 1000);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/gwoJZk?editors=0010)。
-
-以上代码每隔 1 秒, 就会通过 [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval) 回调 `ReactDOM.render()` 方法来重新渲染元素。
-
-> **注意:**
->
-> 实际上,大多数 React 应用只会调用 `ReactDOM.render()` 一次。在接下来的章节中,我们将学习如何将这些代码封装到[有状态的组件中](http://www.css88.com/react/docs/state-and-lifecycle.html)。
->
-> 我们建议您不要跳过任何一节,因为每一节之间都是彼此有联系的。
-
-### React 只更新必需要更新的部分
-
-React DOM 会将元素及其子元素与之前版本逐一对比, 并只对有必要更新的 DOM 进行更新, 以达到 DOM 所需的状态。
-
-你可以用浏览器工具对 [上一个例子](http://codepen.io/gaearon/pen/gwoJZk?editors=0010) 进行检查来验证这一点:
-
-
-
-即使我们我们每隔 1 秒都重建了整个元素, 但实际上 React DOM 只更新了修改过的文本节点.
-
-在我们的经验中, 关注每个时间点 UI 的表现, 而不是关注随着时间不断更新 UI 的状态, 可以减少很多奇怪的 bug 。
-
-## 组件(Components) 和 属性(Props)
-
-组件使你可以将 UI 划分为一个一个独立,可复用的小部件,并可以对每个部件进行单独的设计。
-
-从定义上来说, 组件就像 JavaScript 的函数。组件可以接收任意输入(称为"props"), 并返回 React 元素,用以描述屏幕显示内容。
-
-> 愚人码头注:Props , 即属性(Property), 在代码中写作 props , 故可用 props 指代 properties .
-
-### 函数式组件和类组件
-
-最简单的定义组件的方法是写一个 JavaScript 函数:
-
-```
-function Welcome(props) {
- return Hello, {props.name} ;
-}
-
-```
-
-这个函数是一个合法的 React 组件,因为它接收一个 `props` 参数, 并返回一个 React 元素。 我们把此类组件称为"函数式(Functional)"组件, 因为从字面上看来它就是一个 JavaScript 函数。
-
-你也可以用一个 [ES6 的 class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) 来定义一个组件:
-
-```
-class Welcome extends React.Component {
- render() {
- return Hello, {this.props.name} ;
- }
-}
-
-```
-
-上面两个组件从 React 的角度来看是等效的。
-
-类组件有一些额外的特性,我们将在[下一节](http://www.css88.com/react/docs/state-and-lifecycle.html)讨论。在此之前, 我们先用函数式组件,因为它们更加简洁。
-
-### 渲染一个组件
-
-在前面, 我们只遇到代表 DOM 标签的 React 元素:
-
-```
-const element =
;
-
-```
-
-然而,元素也可以代表用户定义的组件:
-
-```
-const element = ;
-
-```
-
-当 React 遇到一个代表用户定义组件的元素时,它将 JSX 属性以一个单独对象的形式传递给相应的组件。 我们将其称为 "props" 对象。
-
-比如, 以下代码在页面上渲染 "Hello, Sara" :
-
-```
-function Welcome(props) {
- return Hello, {props.name} ;
-}
-
-const element = ;
-ReactDOM.render(
- element,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/YGYmEG?editors=0010)。
-
-我们简单扼要重述一下上面这个例子:
-
-1. 我们调用了 `ReactDOM.render()` 方法并向其中传入了 `` 元素。
-2. React 调用 `Welcome` 组件,并向其中传入了 `{name: 'Sara'}` 作为 props 对象。
-3. `Welcome` 组件返回 `Hello, Sara`。
-4. React DOM 迅速更新 DOM ,使其显示为 `Hello, Sara`。
-
-> **警告:**
->
-> 组件名称总是以大写字母开始。
->
-> 举例来说, `代表一个 DOM 标签,而` 则代表一个组件,并且需要在作用域中有一个 `Welcome` 组件。
-
-### 构成组件
-
-组件可以在它们的输出中引用其它组件。这使得我们可以使用同样的组件来抽象到任意层级。一个按钮,一个表单,一个对话框,一个屏幕:在 React 应用中,所有这些都通常描述为组件。
-
-例如,我们可以创建一个 `App` 组件,并在其内部多次渲染 `Welcome`:
-
-```
-function Welcome(props) {
- return Hello, {props.name} ;
-}
-
-function App() {
- return (
-
-
-
-
-
- );
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/KgQKPr?editors=0010)。
-
-通常,新的 React apps 都有一个单独的顶层 `App` 组件。然而,如果你在已有的应用中整合 React,你可以需要由下至上地, 从类似于 `Button` 这样的小组件开始, 逐渐整合到视图层的顶层。
-
-> **警告:**
->
-> 组件必须返回一个单独的根元素。这就是为什么我们添加一个 `来包含所有` 元素的原因。
-
-### 提取组件
-
-不要害怕把一个组件分为多个更小的组件。
-
-举个例子,思考下名 `Comment` 组件:
-
-```
-function Comment(props) {
- return (
-
-
-
-
- {props.author.name}
-
-
-
- {props.text}
-
-
- {formatDate(props.date)}
-
-
- );
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/VKQwEo?editors=0010)。
-
-它接受 `author`(一个对象),`text`(一个字符串)和 `date`(一个日期)作为 props,并用于在某社交网站中描述一条评论。
-
-这个组件修改起来很麻烦,因为它是被嵌套的,而且很难复用其中的某个部分。让我们从其中提取一些组件。
-
-首先,提取头像 `Avatar`:
-
-```
-function Avatar(props) {
- return (
-
- );
-}
-
-```
-
-`Avatar` 组件不用关心它在 `Comment` 中是如何渲染的。这是为什么我们它的 prop 一个更通用的属性名: `user`, 而不是 `author` 的原因。
-
-我们建议从组件本身的角度来命名 props 而不是它被使用的上下文环境。
-
-我们可以稍微简化一下 `Comment` 组件:
-
-```
-function Comment(props) {
- return (
-
-
-
-
- {props.author.name}
-
-
-
- {props.text}
-
-
- {formatDate(props.date)}
-
-
- );
-}
-
-```
-
-接下来,我们提取用户信息 `UserInfo` 组件, 用于将 `Avatar` 显示在用户名旁边:
-
-```
-function UserInfo(props) {
- return (
-
-
-
- {props.user.name}
-
-
- );
-}
-
-```
-
-这使我们可以进一步简化 `Comment` 组件:
-
-```
-function Comment(props) {
- return (
-
-
-
- {props.text}
-
-
- {formatDate(props.date)}
-
-
- );
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/rrJNJY?editors=0010)。
-
-提取组件可能看起来是一个繁琐的工作,但是在大型的 Apps 中可以回报给我们的是大量的可复用组件。一个好的经验准则是如果你 UI 的一部分需要用多次 (`Button`,`Panel`,`Avatar`),或者本身足够复杂(`App`,`FeedStory`,`Comment`),最好的做法是使其成为可复用组件。
-
-### Props 是只读的
-
-无论你用[函数或类](http://www.css88.com/react/docs/components-and-props.html#functional-and-class-components)的方法来声明组件, 它都无法修改其自身 props. 思考下列 `sum` (求和)函数:
-
-```
-function sum(a, b) {
- return a + b;
-}
-
-```
-
-这种函数称为 [“纯函数”](https://en.wikipedia.org/wiki/Pure_function) ,因为它们不会试图改变它们的输入,并且对于同样的输入,始终可以得到相同的结果。
-
-反之, 以下是非纯函数, 因为它改变了自身的输入值:
-
-```
-function withdraw(account, amount) {
- account.total -= amount;
-}
-
-```
-
-虽然 React 很灵活,但是它有一条严格的规则:
-
-**所有 React 组件都必须是纯函数,并禁止修改其自身 props 。**
-
-当然, 应用 UI 总是动态的,并且随时有可以改变。 所以在[下一节](http://www.css88.com/react/docs/state-and-lifecycle.html), 我们会介绍一个新的概念`state`(状态) 。`state` 允许 React 组件在不违反上述规则的情况下, 根据用户操作, 网络响应, 或者其他随便什么东西, 来动态地改变其输出。
-
-# 状态(State) 和生命周期
-
-思考[前面章节](http://www.css88.com/react/docs/rendering-elements.html#updating-the-rendered-element)中提到过的时钟例子.
-
-目前为止我们只学了一种更新 UI 的方式。
-
-我们通过调 `ReactDOM.render()` 方法来更新渲染的输出:
-
-```
-function tick() {
- const element = (
-
-
Hello, world!
- It is {new Date().toLocaleTimeString()}.
-
- );
- ReactDOM.render(
- element,
- document.getElementById('root')
- );
-}
-
-setInterval(tick, 1000);
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/gwoJZk?editors=0010)。
-
-在本节中,我们将学习如何使 `Clock` 组件变得真正可复用 和 封装的更好。它将设置自己的计时器,并在每秒更新自身。
-
-我们可以从封装时钟开始:
-
-```
-function Clock(props) {
- return (
-
-
Hello, world!
- It is {props.date.toLocaleTimeString()}.
-
- );
-}
-
-function tick() {
- ReactDOM.render(
- ,
- document.getElementById('root')
- );
-}
-
-setInterval(tick, 1000);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/dpdoYR?editors=0010)。
-
-然而,它没有满足一个关键的要求:`Clock` 设置定时器并每秒更新 UI ,事实上应该是 `Clock`自身实现的一部分。
-
-理想情况下,我们应该只引用一个 `Clock` , 然后让它自动计时并更新:
-
-```
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-要实现这点,我们需要添加 `state` 到 `Clock` 组件。
-
-`state` 和 `props` 类似,但是它是私有的,并且由组件本身完全控制。
-
-我们[之前提到过](http://www.css88.com/react/docs/components-and-props.html#functional-and-class-components), 用类定义的组件有一些额外的特性。 这个"类专有的特性", 指的就是局部状态。
-
-## 把函数式组件转化为类组件
-
-你可以遵从以下 5 步, 把一个类似 `Clock` 这样的函数式组件转化为类组件:
-
-1. 创建一个继承自 `React.Component` 类的 [ES6 class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) 同名类。
-2. 添加一个名为 `render()` 的空方法。
-3. 把原函数中的所有内容移至 `render()` 中。
-4. 在 `render()` 方法中使用 `this.props` 替代 `props`。
-5. 删除保留的空函数声明。
-
-```
-class Clock extends React.Component {
- render() {
- return (
-
-
Hello, world!
- It is {this.props.date.toLocaleTimeString()}.
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/zKRGpo?editors=0010)。
-
-`Clock` 现在被定为类组件,而不是函数式组件。
-
-类允许我们在其中添加本地状态(state)和生命周期钩子。
-
-## 在类组件中添加本地状态(state)
-
-我们现在通过以下 3 步, 把`date`从属性(`props`) 改为 状态(`state`):
-
-We will move the `date` from props to state in three steps:
-
-1. 替换 `render()` 方法中的 `this.props.date` 为 `this.state.date`:
-
-```
-class Clock extends React.Component {
- render() {
- return (
-
-
Hello, world!
- It is {this.state.date.toLocaleTimeString()}.
-
- );
- }
-}
-
-```
-
-2. 添加一个 [类构造函数(class constructor)](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes#Constructor) 初始化 `this.state`:
-
-```
-class Clock extends React.Component {
- constructor(props) {
- super(props);
- this.state = {date: new Date()};
- }
-
- render() {
- return (
-
-
Hello, world!
- It is {this.state.date.toLocaleTimeString()}.
-
- );
- }
-}
-
-```
-
-注意我们如何将 `props` 传递给基础构造函数:
-
-```
- constructor(props) {
- super(props);
- this.state = {date: new Date()};
- }
-
-```
-
-类组件应始终使用 `props` 调用基础构造函数。
-
-3. 移除 `` 元素中的 `date` 属性:
-
-```
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-我们稍后再把 计时器代码 添加到组件内部。
-
-现有的结果是这样:
-
-```
-class Clock extends React.Component {
- constructor(props) {
- super(props);
- this.state = {date: new Date()};
- }
-
- render() {
- return (
-
-
Hello, world!
- It is {this.state.date.toLocaleTimeString()}.
-
- );
- }
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/KgQpJd?editors=0010)。
-
-接下来,我们将使 `Clock` 设置自己的计时器,并每秒更新一次。
-
-## 在类中添加生命周期方法
-
-在一个具有许多组件的应用程序中,在组件被销毁时释放所占用的资源是非常重要的。
-
-当 `Clock` 第一次渲染到 DOM 时,我们要[设置一个定时器](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval) 。 这在 React 中称为 "挂载(mounting)" 。
-
-当 `Clock` 产生的 DOM 被销毁时,我们也想[清除该计时器](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval)。 这在 React 中称为 "卸载(unmounting)" 。
-
-当组件挂载和卸载时,我们可以在组件类上声明特殊的方法来运行一些代码:
-
-```
-class Clock extends React.Component {
- constructor(props) {
- super(props);
- this.state = {date: new Date()};
- }
-
- componentDidMount() {
-
- }
-
- componentWillUnmount() {
-
- }
-
- render() {
- return (
-
-
Hello, world!
- It is {this.state.date.toLocaleTimeString()}.
-
- );
- }
-}
-
-```
-
-这些方法称为 "生命周期钩子"。
-
-`componentDidMount()` 钩子在组件输出被渲染到 DOM 之后运行。这是设置时钟的不错的位置:
-
-```
- componentDidMount() {
- this.timerID = setInterval(
- () => this.tick(),
- 1000
- );
- }
-
-```
-
-注意我们把计时器 ID 直接存在 `this` 中。
-
-`this.props` 由 React 本身设定, 而 `this.state` 具有特殊的含义,但如果需要存储一些不用于视觉输出的内容,则可以手动向类中添加额外的字段。
-
-如果在 `render()` 方法中没有被引用, 它不应该出现在 state 中。
-
-我们在`componentWillUnmount()`生命周期钩子中取消这个计时器:
-
-```
- componentWillUnmount() {
- clearInterval(this.timerID);
- }
-
-```
-
-最后,我们将会实现每秒运行的 `tick()` 方法。
-
-它将使用 `this.setState()` 来来周期性地更新组件本地状态:
-
-```
-class Clock extends React.Component {
- constructor(props) {
- super(props);
- this.state = {date: new Date()};
- }
-
- componentDidMount() {
- this.timerID = setInterval(
- () => this.tick(),
- 1000
- );
- }
-
- componentWillUnmount() {
- clearInterval(this.timerID);
- }
-
- tick() {
- this.setState({
- date: new Date()
- });
- }
-
- render() {
- return (
-
-
Hello, world!
- It is {this.state.date.toLocaleTimeString()}.
-
- );
- }
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/amqdNA?editors=0010)。
-
-现在这个时钟每秒都会走了。
-
-我们来快速回顾一下该过程,以及调用方法的顺序:
-
-1. 当 `` 被传入 `ReactDOM.render()` 时, React 会调用 `Clock`组件的构造函数。 因为`Clock` 要显示的是当前时间,所以它将使用包含当前时间的对象来初始化 `this.state`。我们稍后会更新此状态。
-2. 然后 React 调用了 `Clock` 组件的 `render()` 方法。 React 从该方法返回内容中得到要显示在屏幕上的内容。然后,React 然后更新 DOM 以匹配 `Clock` 的渲染输出。
-3. 当 `Clock` 输出被插入到 DOM 中时,React 调用 `componentDidMount()` 生命周期钩子。在该方法中,`Clock` 组件请求浏览器设置一个定时器来一次调用 `tick()`。
-4. 浏览器会每隔一秒调用一次 `tick()`方法。在该方法中, `Clock` 组件通过 `setState()` 方法并传递一个包含当前时间的对象来安排一个 UI 的更新。通过 `setState()`, React 得知了组件 `state`(状态)的变化, 随即再次调用 `render()` 方法,获取了当前应该显示的内容。 这次,`render()` 方法中的 `this.state.date` 的值已经发生了改变, 从而,其输出的内容也随之改变。React 于是据此对 DOM 进行更新。
-5. 如果通过其他操作将 `Clock` 组件从 DOM 中移除了, React 会调用`componentWillUnmount()` 生命周期钩子, 所以计时器也会被停止。
-
-## 正确地使用 State(状态)
-
-关于 `setState()` 有三件事是你应该知道的。
-
-### 不要直接修改 state(状态)
-
-例如,这样将不会重新渲染一个组件:
-
-```
-// 错误
-this.state.comment = 'Hello';
-
-```
-
-用 `setState()` 代替:
-
-```
-// 正确
-this.setState({comment: 'Hello'});
-
-```
-
-唯一可以分配 `this.state` 的地方是构造函数。
-
-### state(状态) 更新可能是异步的
-
-React 为了优化性能,有可能会将多个 `setState()` 调用合并为一次更新。
-
-因为 `this.props` 和 `this.state` 可能是异步更新的,你不能依赖他们的值计算下一个 state(状态)。
-
-例如, 以下代码可能导致 `counter`(计数器)更新失败:
-
-```
-// 错误
-this.setState({
- counter: this.state.counter + this.props.increment,
-});
-
-```
-
-要解决这个问题,应该使用第 2 种 `setState()` 的格式,它接收一个函数,而不是一个对象。该函数接收前一个状态值作为第 1 个参数, 并将更新后的值作为第 21 个参数:
-
-要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。这个函数将接收前一个状态作为第一个参数,应用更新时的 props 作为第二个参数:
-
-```
-// 正确
-this.setState((prevState, props) => ({
- counter: prevState.counter + props.increment
-}));
-
-```
-
-我们在上面使用了一个[箭头函数](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions),但是也可以使用一个常规的函数:
-
-```
-// 正确
-this.setState(function(prevState, props) {
- return {
- counter: prevState.counter + props.increment
- };
-});
-
-```
-
-### state(状态)更新会被合并
-
-当你调用 `setState()`, React 将合并你提供的对象到当前的状态中。
-
-例如,你的状态可能包含几个独立的变量:
-
-```
- constructor(props) {
- super(props);
- this.state = {
- posts: [],
- comments: []
- };
- }
-
-```
-
-然后通过调用独立的 `setState()` 调用分别更新它们:
-
-```
- componentDidMount() {
- fetchPosts().then(response => {
- this.setState({
- posts: response.posts
- });
- });
-
- fetchComments().then(response => {
- this.setState({
- comments: response.comments
- });
- });
- }
-
-```
-
-合并是浅合并,所以 `this.setState({comments})` 不会改变 `this.state.posts` 的值,但会完全替换`this.state.comments` 的值。
-
-## 数据向下流动
-
-无论作为父组件还是子组件,它都无法获悉一个组件是否有状态,同时也不需要关心另一个组件是定义为函数组件还是类组件。
-
-这就是 state(状态) 经常被称为 本地状态 或 封装状态的原因。 它不能被拥有并设置它的组件 以外的任何组件访问。
-
-一个组件可以选择将 state(状态) 向下传递,作为其子组件的 props(属性):
-
-```
-It is {this.state.date.toLocaleTimeString()}.
-
-```
-
-同样适用于用户定义组件:
-
-```
-
-
-```
-
-`FormattedDate` 组件通过 props(属性) 接收了 `date` 的值,但它仍然不能获知该值是来自于`Clock`的 state(状态) ,还是 `Clock` 的 props(属性),或者是直接手动创建的:
-
-```
-function FormattedDate(props) {
- return It is {props.date.toLocaleTimeString()}. ;
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/zKRqNB?editors=0010)。
-
-这通常称为一个“从上到下”,或者“单向”的数据流。任何 state(状态) 始终由某个特定组件所有,并且从该 state(状态) 导出的任何数据 或 UI 只能影响树中 “下方” 的组件。
-
-如果把组件树想像为 props(属性) 的瀑布,所有组件的 state(状态) 就如同一个额外的水源汇入主流,且只能随着主流的方向向下流动。
-
-要证明所有组件都是完全独立的, 我们可以创建一个 `App` 组件,并在其中渲染 3 个``:
-
-```
-function App() {
- return (
-
-
-
-
-
- );
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/vXdGmd?editors=0010)。
-
-每个 `Clock` 都设置它自己的计时器并独立更新。
-
-在 React 应用中,一个组件是否是有状态或者无状态的,被认为是组件的一个实现细节,随着时间推移可能发生改变。你可以在有状态的组件中使用无状态组件,反之亦然。
-
-通过 React 元素处理事件跟在 DOM 元素上处理事件非常相似。但是有一些语法上的区别:
-
-- React 事件使用驼峰命名,而不是全部小写。
-- 通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。
-
-例如,HTML:
-
-```
-
- Activate Lasers
-
-
-```
-
-在 React 中略有不同:
-
-```
-
- Activate Lasers
-
-
-```
-
-另一个区别是,在 React 中你不能通过返回 `false`(愚人码头注:即 `return false;` 语句) 来阻止默认行为。必须明确调用 `preventDefault` 。例如,对于纯 HTML ,要阻止链接打开一个新页面的默认行为,可以这样写:
-
-```
-
- Click me
-
-
-```
-
-在 React 中, 应该这么写:
-
-```
-function ActionLink() {
- function handleClick(e) {
- e.preventDefault();
- console.log('The link was clicked.');
- }
-
- return (
-
- Click me
-
- );
-}
-
-```
-
-这里, e 是一个合成的事件。 React 根据 [W3C 规范](https://www.w3.org/TR/DOM-Level-3-Events/) 定义了这个合成事件,所以你不需要担心跨浏览器的兼容性问题。查看 [`SyntheticEvent`](http://www.css88.com/react/docs/events.html) 参考指南了解更多。
-
-当使用 React 时,你一般不需要调用 `addEventListener` 在 DOM 元素被创建后添加事件监听器。相反,只要当元素被初始渲染的时候提供一个监听器就可以了。
-
-当使用一个 [ES6 类](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes) 定义一个组件时,通常的一个事件处理程序是类上的一个方法。例如,`Toggle` 组件渲染一个按钮,让用户在 “ON” 和 "OFF" 状态之间切换:
-
-```
-class Toggle extends React.Component {
- constructor(props) {
- super(props);
- this.state = {isToggleOn: true};
-
- // 这个绑定是必要的,使`this`在回调中起作用
- this.handleClick = this.handleClick.bind(this);
- }
-
- handleClick() {
- this.setState(prevState => ({
- isToggleOn: !prevState.isToggleOn
- }));
- }
-
- render() {
- return (
-
- {this.state.isToggleOn ? 'ON' : 'OFF'}
-
- );
- }
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/xEmzGg?editors=0010)。
-
-在 JSX 回调中你必须注意 `this` 的指向。 在 JavaScript 中,类方法默认没有 [绑定](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind) 的。如果你忘记绑定 `this.handleClick` 并将其传递给`onClick`,那么在直接调用该函数时,`this` 会是`undefined` 。
-
-这不是 React 特有的行为;这是 [JavaScript 中的函数如何工作](https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/)的一部分。 一般情况下,如果你引用一个后面没跟 `()` 的方法,例如 `onClick={this.handleClick}` ,那你就应该 绑定(bind) 该方法。
-
-如果调用 `bind` 令你烦恼,有两种方法可以解决这个问题。 如果您使用实验性的 [属性初始化语法](https://babeljs.io/docs/plugins/transform-class-properties/) ,那么你可以使用属性初始值设置来正确地 绑定(bind) 回调:
-
-```
-class LoggingButton extends React.Component {
- // 这个语法确保 `this` 绑定在 handleClick 中。
- // 警告:这是 *实验性的* 语法。
- handleClick = () => {
- console.log('this is:', this);
- }
-
- render() {
- return (
-
- Click me
-
- );
- }
-}
-
-```
-
-这个语法在 [创建 React App](https://github.com/facebookincubator/create-react-app) 中是默认开启的。
-
-如果你没有使用属性初始化语法,可以在回调中使用一个 [箭头函数](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions):
-
-```
-class LoggingButton extends React.Component {
- handleClick() {
- console.log('this is:', this);
- }
-
- render() {
- // 这个语法确保 `this` 被绑定在 handleClick 中
- return (
- this.handleClick(e)}>
- Click me
-
- );
- }
-}
-
-```
-
-这个语法的问题是,每次 `LoggingButton` 渲染时都创建一个不同的回调。在多数情况下,没什么问题。然而,如果这个回调被作为 prop(属性) 传递给下级组件,这些组件可能需要额外的重复渲染。我们通常建议在构造函数中进行绑定,以避免这类性能问题。
-
-# 条件渲染
-
-在 React 中,你可以创建不同的组件封装你所需要的行为。然后,只渲染它们之中的一些,取决于你的应用的状态。
-
-React 中的条件渲染就和在 JavaScript 中的条件语句一样。使用 JavaScript 操作符如 [`if`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else) 或者[条件操作符](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)来创建渲染当前状态的元素,并且让 React 更新匹配的 UI 。
-
-思考以下两个组件:
-
-```
-function UserGreeting(props) {
- return Welcome back! ;
-}
-
-function GuestGreeting(props) {
- return Please sign up. ;
-}
-
-```
-
-我们需要创建一个 `Greeting` 组件, 用来根据用户是否登录, 判断并显示上述两个组件之一:
-
-```
-function Greeting(props) {
- const isLoggedIn = props.isLoggedIn;
- if (isLoggedIn) {
- return ;
- }
- return ;
-}
-
-ReactDOM.render(
- // 修改为 isLoggedIn={true} 试试:
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/ZpVxNq?editors=0011)。
-
-这个例子根据 `isLoggedIn` prop 渲染了不同的问候语 。
-
-### 元素变量
-
-你可以用变量来存储元素。这可以帮助您有条件地渲染组件的一部分,而输出的其余部分不会更改。
-
-思考以下两个新组件,分别用于显示登出和登入按钮:
-
-```
-function LoginButton(props) {
- return (
-
- Login
-
- );
-}
-
-function LogoutButton(props) {
- return (
-
- Logout
-
- );
-}
-
-```
-
-在接下来的例子中,我们将会创建一个[有状态组件](http://www.css88.com/react/docs/state-and-lifecycle.html#adding-local-state-to-a-class),叫做 `LoginControl` 。
-
-它将渲染 `或者` ,取决于当前状态。同时渲染前面提到的`` 组件:
-
-```
-class LoginControl extends React.Component {
- constructor(props) {
- super(props);
- this.handleLoginClick = this.handleLoginClick.bind(this);
- this.handleLogoutClick = this.handleLogoutClick.bind(this);
- this.state = {isLoggedIn: false};
- }
-
- handleLoginClick() {
- this.setState({isLoggedIn: true});
- }
-
- handleLogoutClick() {
- this.setState({isLoggedIn: false});
- }
-
- render() {
- const isLoggedIn = this.state.isLoggedIn;
-
- let button = null;
- if (isLoggedIn) {
- button = ;
- } else {
- button = ;
- }
-
- return (
-
-
- {button}
-
- );
- }
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/QKzAgB?editors=0010)。
-
-虽然声明一个变量并使用一个 `if` 语句是一个有条件地渲染组件的好方法,有时你可能想要使用一个更简短的语法。在 JSX 中有几种内联条件的方法,如下所述。
-
-### 使用逻辑 && 操作符的内联 if 用法
-
-您可以 [在 JSX 中嵌入任何表达式](http://www.css88.com/react/docs/introducing-jsx.html#embedding-expressions-in-jsx) ,方法是将其包裹在花括号中。这也包括 JavaScript 逻辑`&&` 运算符。 它有助于有条件地包含一个元素:
-
-```
-function Mailbox(props) {
- const unreadMessages = props.unreadMessages;
- return (
-
-
Hello!
- {unreadMessages.length > 0 &&
-
- You have {unreadMessages.length} unread messages.
-
- }
-
- );
-}
-
-const messages = ['React', 'Re: React', 'Re:Re: React'];
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/ozJddz?editors=0010)。
-
-它可以正常运行,因为在 JavaScript 中, `true && expression` 总是会评估为 `expression` ,而`false && expression` 总是执行为 `false` 。
-
-因此,如果条件为 `true` ,则 `&&` 后面的元素将显示在输出中。 如果是 `false`,React 将会忽略并跳过它。
-
-### 使用条件操作符的内联 If-Else
-
-另一个用于条件渲染元素的内联方法是使用 JavaScript 的条件操作符 [`condition ? true : false`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator) 。
-
-在下面这个例子中,我们使用它来进行条件渲染一个小的文本块:
-
-```
-render() {
- const isLoggedIn = this.state.isLoggedIn;
- return (
-
- The user is {isLoggedIn ? 'currently' : 'not'} logged in.
-
- );
-}
-
-```
-
-它也可以用于更大的表达式,虽然不太明显发生了什么:
-
-```
-render() {
- const isLoggedIn = this.state.isLoggedIn;
- return (
-
- {isLoggedIn ? (
-
- ) : (
-
- )}
-
- );
-}
-
-```
-
-就像 JavaScript 一样,你可以根据你和你的团队认为更易于阅读的方式选择合适的风格。还要记住,无论何时何地,当条件变得太复杂时,可能是[提取组件](http://www.css88.com/react/docs/components-and-props.html#extracting-components)的好时机。
-
-### 防止组件渲染
-
-在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回`null` 而不是其渲染输出。
-
-在下面的例子中,根据名为`warn`的 prop 值,呈现 `` 。如果 prop 值为 `false`,则该组件不渲染:
-
-```
-function WarningBanner(props) {
- if (!props.warn) {
- return null;
- }
-
- return (
-
- Warning!
-
- );
-}
-
-class Page extends React.Component {
- constructor(props) {
- super(props);
- this.state = {showWarning: true}
- this.handleToggleClick = this.handleToggleClick.bind(this);
- }
-
- handleToggleClick() {
- this.setState(prevState => ({
- showWarning: !prevState.showWarning
- }));
- }
-
- render() {
- return (
-
-
-
- {this.state.showWarning ? 'Hide' : 'Show'}
-
-
- );
- }
-}
-
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/Xjoqwm?editors=0010)。
-
-从组件的 `render` 方法返回 `null` 不会影响组件生命周期方法的触发。 例如,`componentWillUpdate` 和 `componentDidUpdate` 仍将被调用。
-
-# 列表(Lists) 和 键(Keys)
-
-首先,让我们回顾一下在 JavaScript 中如何转换列表。
-
-给定下面的代码,我们使用 [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 函数使 `numbers` 数组中的元素值翻倍。我们将 `map()` 返回的新数组分配给变量 `doubled`,并且打印这个它:
-
-```
-const numbers = [1, 2, 3, 4, 5];
-const doubled = numbers.map((number) => number * 2);
-console.log(doubled);
-
-```
-
-这段代码在控制台中打印为 `[2, 4, 6, 8, 10]`。
-
-在 React 中,转换数组为 [元素列表](http://www.css88.com/react/docs/rendering-elements.html) 的方式,和上述方法基本相同。
-
-### 多组件渲染
-
-可以创建元素集合,并用一对大括号 `{}` [在 JSX 中直接将其引用](http://www.css88.com/react/docs/introducing-jsx.html#embedding-expressions-in-jsx)即可。
-
-下面,我们用 JavaScript 的 [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 函数将 `numbers` 数组循环处理。对于每一项,我们返回一个 `` 元素。最终,我们将结果元素数组分配给 `listItems`:
-
-```
-const numbers = [1, 2, 3, 4, 5];
-const listItems = numbers.map((number) =>
- {number}
-);
-
-```
-
-把整个 `listItems` 数组包含到一个 `` 元素,并[渲染到 DOM](http://www.css88.com/react/docs/rendering-elements.html#rendering-an-element-into-the-dom):
-
-```
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/GjPyQr?editors=0011)。
-
-这段代码显示从 1 到 5 的数字列表。
-
-### 基本列表组件
-
-通常情况下,我们会在一个[组件](http://www.css88.com/react/docs/components-and-props.html)中渲染列表。
-
-我们可以重构前面的例子到一个组件,它接受一个 `numbers` 数组,并输出一个元素的无序列表。
-
-```
-function NumberList(props) {
- const numbers = props.numbers;
- const listItems = numbers.map((number) =>
- {number}
- );
- return (
-
- );
-}
-
-const numbers = [1, 2, 3, 4, 5];
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-当运行上述代码的时候,将会收到一个警告:a key should be provided for list items(应该为列表元素提供一个键)(愚人码头注 :CodeOpen 中没有报警告,是因为其示例中使用的是 min 版本的 React,换成非 min 版本的就可以看到)。当创建元素列表时,“key” 是一个你需要包含的特殊字符串属性。我们将在下一节讨论它的重要性。
-
-我们在 `numbers.map()` 中赋值一个 `key` 给我们的列表元素,解决丢失 key 的问题。
-
-```
-function NumberList(props) {
- const numbers = props.numbers;
- const listItems = numbers.map((number) =>
-
- {number}
-
- );
- return (
-
- );
-}
-
-const numbers = [1, 2, 3, 4, 5];
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/jrXYRR?editors=0011)。
-
-## 键(Keys)
-
-键(Keys) 帮助 React 标识哪个项被修改、添加或者移除了。数组中的每一个元素都应该有一个唯一不变的键(Keys)来标识:
-
-```
-const numbers = [1, 2, 3, 4, 5];
-const listItems = numbers.map((number) =>
-
- {number}
-
-);
-
-```
-
-挑选 key 最好的方式是使用一个在它的同辈元素中不重复的标识字符串。多数情况你可以使用数据中的 IDs 作为 keys:
-
-```
-const todoItems = todos.map((todo) =>
-
- {todo.text}
-
-);
-
-```
-
-当要渲染的列表项中没有稳定的 IDs 时,你可以使用数据项的索引值作为 key 的最后选择:
-
-```
-const todoItems = todos.map((todo, index) =>
- // Only do this if items have no stable IDs
-
- {todo.text}
-
-);
-
-```
-
-如果列表项可能被重新排序时,我们不建议使用索引作为 keys,因为这导致一定的性能问题,会很慢。如果感兴趣,你可以阅读一下[深入的介绍关于为什么 keys 是必须的](http://www.css88.com/react/docs/reconciliation.html#recursing-on-children)。
-
-### 使用 keys 提取组件
-
-keys 只在数组的上下文中存在意义。
-
-例如,如果你[提取](http://www.css88.com/react/docs/components-and-props.html#extracting-components) 一个 `ListItem` 组件,应该把 key 放置在数组处理的 `` 元素中,不能放在 `ListItem` 组件自身中的 `` 根元素上。
-
-**例子:错误的 key 用法**
-
-```
-function ListItem(props) {
- const value = props.value;
- return (
- // 错误!不需要在这里指定 key:
-
- {value}
-
- );
-}
-
-function NumberList(props) {
- const numbers = props.numbers;
- const listItems = numbers.map((number) =>
- // 错误!key 应该在这里指定:
-
- );
- return (
-
- );
-}
-
-const numbers = [1, 2, 3, 4, 5];
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-**错误!key 应该在这里指定:**
-
-```
-function ListItem(props) {
- // 正确!这里不需要指定 key :
- return {props.value} ;
-}
-
-function NumberList(props) {
- const numbers = props.numbers;
- const listItems = numbers.map((number) =>
- // 正确!key 应该在这里被指定
-
- );
- return (
-
- );
-}
-
-const numbers = [1, 2, 3, 4, 5];
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/rthor/pen/QKzJKG?editors=0010)。
-
-一个好的经验准则是元素中调用 `map()` 需要 keys 。
-
-### keys 在同辈元素中必须是唯一的
-
-在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys :
-
-```
-function Blog(props) {
- const sidebar = (
-
- {props.posts.map((post) =>
-
- {post.title}
-
- )}
-
- );
- const content = props.posts.map((post) =>
-
-
{post.title}
-
{post.content}
-
- );
- return (
-
- {sidebar}
-
- {content}
-
- );
-}
-
-const posts = [
- {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
- {id: 2, title: 'Installation', content: 'You can install React from npm.'}
-];
-ReactDOM.render(
- ,
- document.getElementById('root')
-);
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/NRZYGN?editors=0010)。
-
-键是 React 的一个内部映射,但其不会传递给组件的内部。如果你需要在组件中使用相同的值,可以明确使用一个不同名字的 prop 传入。
-
-```
-const content = posts.map((post) =>
-
-);
-
-```
-
-上面的例子中, `Post` 组件可以读取 `props.id`,但是不能读取 `props.key` 。
-
-### 在 JSX 中嵌入 map()
-
-在上面的例子中,我们单独声明了一个 `listItems` 变量,并在 JSX 中引用了该变量:
-
-```
-function NumberList(props) {
- const numbers = props.numbers;
- const listItems = numbers.map((number) =>
-
- );
- return (
-
- );
-}
-
-```
-
-JSX 允许在大括号中[嵌入任何表达式](http://www.css88.com/react/docs/introducing-jsx.html#embedding-expressions-in-jsx),因此可以 内联 `map()` 结果:
-
-```
-function NumberList(props) {
- const numbers = props.numbers;
- return (
-
- {numbers.map((number) =>
-
- )}
-
- );
-}
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/BLvYrB?editors=0010)。
-
-有时这可以产生清晰的代码,但是这个风格也可能被滥用。就像在 JavaScript 中,是否有必要提取一个变量以提高程序的可读性,这取决于你。但是记住,如果 `map()` 体中有太多嵌套,可能是[提取组件](http://www.css88.com/react/docs/components-and-props.html#extracting-components)的好时机。
-
-# 表单(Forms)
-
-HTML 表单元素与 React 中的其他 DOM 元素有所不同,因为表单元素自然地保留了一些内部状态。例如,这个纯 HTML 表单接受一个单独的 name:
-
-```
-
-
- Name:
-
-
-
-
-
-```
-
-该表单和 HTML 表单的默认行为一致,当用户提交此表单时浏览器会打开一个新页面。如果你希望 React 中保持这个行为,也可以工作。但是多数情况下,用一个处理表单提交并访问用户输入到表单中的数据的 JavaScript 函数也很方便。实现这一点的标准方法是使用一种称为“受控组件(controlled components)”的技术。
-
-## 受控组件(Controlled Components)
-
-在 HTML 中,表单元素如 `,` 和 `` 表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React 中,可变状态一般保存在组件的 state(状态) 属性中,并且只能通过 [`setState()`](http://www.css88.com/react/docs/react-component.html#setstate) 更新。
-
-我们可以通过使 React 的 state 成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制在用户输入之后的行为。这种形式,其值由 React 控制的输入表单元素称为“受控组件”。
-
-例如,如果我们想使上一个例子在提交时记录名称,我们可以将表单写为受控组件:
-
-```
-class NameForm extends React.Component {
- constructor(props) {
- super(props);
- this.state = {value: ''};
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- }
-
- handleChange(event) {
- this.setState({value: event.target.value});
- }
-
- handleSubmit(event) {
- alert('A name was submitted: ' + this.state.value);
- event.preventDefault();
- }
-
- render() {
- return (
-
-
- Name:
-
-
-
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/VmmPgp?editors=0010)。
-
-设置表单元素的 value 属性之后,其显示值将由 this.state.value 决定,以满足 React 状态的同一数据理念。每次键盘敲击之后会执行 handleChange 方法以更新 React 状态,显示值也将随着用户的输入改变。
-
-由于 `value` 属性设置在我们的表单元素上,显示的值总是 `this.state.value`,以满足 state 状态的同一数据理念。由于 `handleChange` 在每次敲击键盘时运行,以更新 React state(状态),显示的值将更新为用户的输入。
-
-对于受控组件来说,每一次 state(状态) 变化都会伴有相关联的处理函数。这使得可以直接修改或验证用户的输入。比如,如果我们希望强制 name 的输入都是大写字母,可以这样来写`handleChange` 方法:
-
-```
-handleChange(event) {
- this.setState({value: event.target.value.toUpperCase()});
-}
-
-```
-
-## textare 标签
-
-在 HTML 中,`` 元素通过它的子节点定义了它的文本值:
-
-```
-
- Hello there, this is some text in a text area
-
-
-```
-
-在 React 中,`` 的赋值使用 `value` 属性替代。这样一来,表单中 `` 的书写方式接近于单行文本输入框 :
-
-```
-class EssayForm extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: 'Please write an essay about your favorite DOM element.'
- };
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- }
-
- handleChange(event) {
- this.setState({value: event.target.value});
- }
-
- handleSubmit(event) {
- alert('An essay was submitted: ' + this.state.value);
- event.preventDefault();
- }
-
- render() {
- return (
-
-
- Name:
-
-
-
-
- );
- }
-}
-
-```
-
-注意,`this.state.value` 在构造函数中初始化,所以这些文本一开始就出现在文本域中。
-
-## select 标签
-
-在 HTML 中,`` 创建了一个下拉列表。例如,这段 HTML 创建一个下拉的口味(flavors)列表:
-
-```
-
- Grapefruit
- Lime
- Coconut
- Mango
-
-
-```
-
-注意,Coconut 选项是初始化选中的,因为它的 `selected` 属性。React 中,并不使用这个`selected` 属性,而是在根 `select` 标签中使用了一个 `value` 属性。这使得受控组件使用更方便,因为你只需要更新一处即可。例如:
-
-```
-class FlavorForm extends React.Component {
- constructor(props) {
- super(props);
- this.state = {value: 'coconut'};
-
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
- }
-
- handleChange(event) {
- this.setState({value: event.target.value});
- }
-
- handleSubmit(event) {
- alert('Your favorite flavor is: ' + this.state.value);
- event.preventDefault();
- }
-
- render() {
- return (
-
-
- Pick your favorite La Croix flavor:
-
- Grapefruit
- Lime
- Coconut
- Mango
-
-
-
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/JbbEzX?editors=0010)。
-
-总的来说,这使 `,` 和 `` 都以类似的方式工作 —— 它们都接受一个 `value` 属性可以用来实现一个受控组件。
-
-## 处理多个输入元素
-
-当您需要处理多个受控的 `input` 元素时,您可以为每个元素添加一个 `name` 属性,并且让处理函数根据 `event.target.name` 的值来选择要做什么。
-
-例如:
-
-```
-class Reservation extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isGoing: true,
- numberOfGuests: 2
- };
-
- this.handleInputChange = this.handleInputChange.bind(this);
- }
-
- handleInputChange(event) {
- const target = event.target;
- const value = target.type === 'checkbox' ? target.checked : target.value;
- const name = target.name;
-
- this.setState({
- [name]: value
- });
- }
-
- render() {
- return (
-
-
- Is going:
-
-
-
-
- Number of guests:
-
-
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](https://codepen.io/gaearon/pen/wgedvV?editors=0010)。
-
-注意我们如何使用 ES6[计算的属性名称](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names)语法来更新与给定输入名称相对应的 state(状态) 键:
-
-```
-this.setState({
- [name]: value
-});
-
-```
-
-这段代码等价于 ES5 代码:
-
-```
-var partialState = {};
-partialState[name] = value;
-this.setState(partialState);
-
-```
-
-此外,由于 `setState()` 自动[将部分状态合并到当前状态](http://www.css88.com/react/docs/state-and-lifecycle.html#state-updates-are-merged),所以我们只需要调用更改的部分即可。
-
-## 受控组件的替代方案
-
-有时使用受控组件有些乏味,因为你需要为每一个可更改的数据提供事件处理器,并通过 React 组件管理所有输入状态。当你将已经存在的代码转换为 React 时,或将 React 应用程序与非 React 库集成时,这可能变得特别烦人。在这些情况下,您可能需要使用[不受控的组件](http://www.css88.com/react/docs/uncontrolled-components.html),用于实现输入表单的替代技术。
-
-# 状态提升(Lifting State Up)
-
-通常情况下,同一个数据的变化需要几个不同的组件来反映。我们建议提升共享的状态到它们最近的祖先组件中。我们看下这是如何运作的。
-
-在本节,我们将会创建一个温度计算器,用来计算水在一个给定温度下是否会沸腾。
-
-我们通过一个称为 `BoilingVerdict` 的组件开始。它接受 `celsius`(摄氏温度)作为 prop ,并打印是否足以使水沸腾:
-
-```
-function BoilingVerdict(props) {
- if (props.celsius >= 100) {
- return The water would boil.
;
- }
- return The water would not boil.
;
-}
-
-```
-
-接下来,我们将会创建一个 `Calculator` 组件。它渲染一个 `` 让你输入温度,并在`this.state.temperature` 中保存它的值。
-
-另外,它会根据当前输入的温度来渲染 `BoilingVerdict` 。
-
-```
-class Calculator extends React.Component {
- constructor(props) {
- super(props);
- this.handleChange = this.handleChange.bind(this);
- this.state = {temperature: ''};
- }
-
- handleChange(e) {
- this.setState({temperature: e.target.value});
- }
-
- render() {
- const temperature = this.state.temperature;
- return (
-
- Enter temperature in Celsius:
-
-
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/valscion/pen/VpZJRZ?editors=0010)。
-
-## 添加第二个输入
-
-我们新的需求是,除了一个摄氏温度输入之外,我们再提供了一个华氏温度输入,并且两者保持自动同步。
-
-我们可以从 `Calculator` 中提取一个 `TemperatureInput` 组件开始。我们将添加一个新的 `scale`属性,值可能是 `"c"` 或者 `"f"` :
-
-```
-const scaleNames = {
- c: 'Celsius',
- f: 'Fahrenheit'
-};
-
-class TemperatureInput extends React.Component {
- constructor(props) {
- super(props);
- this.handleChange = this.handleChange.bind(this);
- this.state = {temperature: ''};
- }
-
- handleChange(e) {
- this.setState({temperature: e.target.value});
- }
-
- render() {
- const temperature = this.state.temperature;
- const scale = this.props.scale;
- return (
-
- Enter temperature in {scaleNames[scale]}:
-
-
- );
- }
-}
-
-```
-
-现在我们可以修改 `Calculator` 来渲染两个独立的温度输入:
-
-```
-class Calculator extends React.Component {
- render() {
- return (
-
-
-
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/valscion/pen/GWKbao?editors=0010)。
-
-我们现在有两个 (input)输入框 了,但是当你输入其中一个温度时,另一个输入并没有更新。这是跟我们的需要不符的:我们希望它们保持同步。
-
-我们也不能在 `Calculator` 中显示 `BoilingVerdict` 。 `Calculator` 不知道当前的温度,因为它是在 `TemperatureInput` 中隐藏的。
-
-## 编写转换函数
-
-首先,我们编写两个函数来在摄氏温度和华氏温度之间转换:
-
-```
-function toCelsius(fahrenheit) {
- return (fahrenheit - 32) * 5 / 9;
-}
-
-function toFahrenheit(celsius) {
- return (celsius * 9 / 5) + 32;
-}
-
-```
-
-这两个函数用来转化数字。接下来再编写一个函数用来接收一个字符串 `temperature` 和一个 转化器函数 作为参数,并返回一个字符串。这个函数用来在两个输入之间进行相互转换。
-
-对于无效的 `temperature` 值,它返回一个空字符串,输出结果保留 3 位小数:
-
-```
-function tryConvert(temperature, convert) {
- const input = parseFloat(temperature);
- if (Number.isNaN(input)) {
- return '';
- }
- const output = convert(input);
- const rounded = Math.round(output * 1000) / 1000;
- return rounded.toString();
-}
-
-```
-
-例如, `tryConvert('abc', toCelsius)` 将返回一个空字符串,而 `tryConvert('10.22', toFahrenheit)` 返回 `'50.396'` 。
-
-## 状态提升(Lifting State Up)
-
-目前,两个 `TemperatureInput` 组件都将其值保持在本地状态中:
-
-```
-class TemperatureInput extends React.Component {
- constructor(props) {
- super(props);
- this.handleChange = this.handleChange.bind(this);
- this.state = {temperature: ''};
- }
-
- handleChange(e) {
- this.setState({temperature: e.target.value});
- }
-
- render() {
- const temperature = this.state.temperature;
-
-```
-
-但是,我们希望这两个输入是相互同步的。当我们更新摄氏温度输入时,华氏温度输入应反映转换后的温度,反之亦然。
-
-在 React 中,共享 state(状态) 是通过将其移动到需要它的组件的最接近的共同祖先组件来实现的。 这被称为“状态提升(Lifting State Up)”。我们将从 `TemperatureInput` 中移除相关状态本地状态,并将其移动到 `Calculator` 中。
-
-如果 `Calculator` 拥有共享状态,那么它将成为两个输入当前温度的“单一数据来源”。它可以指示他们具有彼此一致的值。由于两个 `TemperatureInput` 组件的 props 都来自同一个父级`Calculator`组件,两个输入将始终保持同步。
-
-让我们一步一步看看这是如何工作的。
-
-首先,我们将在 `TemperatureInput` 组件中用 `this.props.temperature` 替换`this.state.temperature` 。 现在,我们假装 `this.props.temperature` 已经存在,虽然我们将来需要从 `Calculator` 传递过来:
-
-```
- render() {
- // 之前是: const temperature = this.state.temperature;
- const temperature = this.props.temperature;
-
-```
-
-我们知道 [props(属性) 是只读的](http://www.css88.com/react/docs/components-and-props.html#props-are-read-only)。 当 `temperature` 是 本地 state(状态)时, `TemperatureInput`可以调用 `this.setState()` 来更改它。 然而,现在 `temperature` 来自父级作为 prop(属性) ,`TemperatureInput` 就无法控制它。
-
-在 React 中,通常通过使组件“受控”的方式来解决。就像 DOM ``一样接受一个 `value`和一个 `onChange` prop(属性) ,所以可以定制 `TemperatureInput` 接受来自其父级 `Calculator` 的`temperature` 和 `onTemperatureChange` 。
-
-现在,当 `TemperatureInput` 想要更新其温度时,它就会调用`this.props.onTemperatureChange`:
-
-```
- handleChange(e) {
- // 之前是: this.setState({temperature: e.target.value});
- this.props.onTemperatureChange(e.target.value);
-
-```
-
-请注意,自定义组件中的 `temperature` 或 `onTemperatureChange` prop(属性) 名称没有特殊的含义。我们可以命名为任何其他名称,像命名他们为 `value` 和 `onChange`,是一个常见的惯例。
-
-`onTemperatureChange` prop(属性) 和 `temperature` prop(属性) 一起由父级的 `Calculator` 组件提供。它将通过修改自己的本地 state(状态) 来处理变更,从而通过新值重新渲染两个输入。我们将很快看到新的 `Calculator` 实现。
-
-在修改 `Calculator` 之前,让我们回顾一下对 `TemperatureInput` 组件的更改。我们已经从中删除了本地 state(状态) ,不是读取`this.state.temperature` ,我们现在读取`this.props.temperature` 。当我们想要更改时, 不是调用 `this.setState()` ,而是调用`this.props.onTemperatureChange()`, 这将由 `Calculator` 提供:
-
-```
-class TemperatureInput extends React.Component {
- constructor(props) {
- super(props);
- this.handleChange = this.handleChange.bind(this);
- }
-
- handleChange(e) {
- this.props.onTemperatureChange(e.target.value);
- }
-
- render() {
- const temperature = this.props.temperature;
- const scale = this.props.scale;
- return (
-
- Enter temperature in {scaleNames[scale]}:
-
-
- );
- }
-}
-
-```
-
-现在我们来看一下 `Calculator` 组件。
-
-我们将当前输入的 `temperature` 和 `scale` 存储在本地 state(状态) 中。这是我们从输入 “提升” 的 state(状态) ,它将作为两个输入的 “单一数据来源” 。为了渲染两个输入,我们需要知道的所有数据的最小表示。
-
-例如,如果我们在摄氏度输入框中输入 37 ,则 `Calculator` 组件的状态将是:
-
-```
-{
- temperature: '37',
- scale: 'c'
-}
-
-```
-
-如果我们稍后将华氏温度字段编辑为 212 ,则 `Calculator` 组件的状态将是:
-
-```
-{
- temperature: '212',
- scale: 'f'
-}
-
-```
-
-我们可以存储两个输入框的值,但事实证明是不必要的。存储最近更改的输入框的值,以及它所表示的度量衡就够了。然后,我们可以基于当前的 `temperature`(温度) 和 `scale`(度量衡) 来推断其他输入的值。
-
-输入框保持同步,因为它们的值是从相同的 state(状态) 计算出来的:
-
-```
-class Calculator extends React.Component {
- constructor(props) {
- super(props);
- this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
- this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
- this.state = {temperature: '', scale: 'c'};
- }
-
- handleCelsiusChange(temperature) {
- this.setState({scale: 'c', temperature});
- }
-
- handleFahrenheitChange(temperature) {
- this.setState({scale: 'f', temperature});
- }
-
- render() {
- const scale = this.state.scale;
- const temperature = this.state.temperature;
- const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
- const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
-
- return (
-
-
-
-
-
- );
- }
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/valscion/pen/jBNjja?editors=0010)。
-
-现在,无论你编辑哪个输入框,`Calculator` 中的 `this.state.temperature` 和 `this.state.scale`都会更新。其中一个输入框获取值,所以任何用户输入都被保留,并且另一个输入总是基于它重新计算值。
-
-让我们回顾一下编辑输入时会发生什么:
-
-- React 调用在 DOM `` 上的 `onChange` 指定的函数。在我们的例子中,这是`TemperatureInput` 组件中的 `handleChange` 方法。
-- `TemperatureInput` 组件中的 `handleChange` 方法使用 新的期望值 调用`this.props.onTemperatureChange()`。`TemperatureInput` 组件中的 props(属性) ,包括`onTemperatureChange`,由其父组件 `Calculator` 提供。
-- 当它预先呈现时, `Calculator` 指定了摄氏 `TemperatureInput` 的 `onTemperatureChange` 是`Calculator` 的 `handleCelsiusChange` 方法,并且华氏 `TemperatureInput` 的`onTemperatureChange` 是 `Calculator` 的 `handleFahrenheitChange` 方法。因此,会根据我们编辑的输入框,分别调用这两个 `Calculator` 方法。
-- 在这些方法中, `Calculator` 组件要求 React 通过使用 新的输入值 和 刚刚编辑的输入框的当前度量衡 来调用 `this.setState()` 来重新渲染自身。
-- React 调用 `Calculator` 组件的 `render` 方法来了解 UI 外观应该是什么样子。基于当前温度和激活的度量衡来重新计算两个输入框的值。这里进行温度转换。
-- React 使用 `Calculator` 指定的新 props(属性) 调用各个 `TemperatureInput` 组件的 `render`方法。 它了解 UI 外观应该是什么样子。
-- React DOM 更新 DOM 以匹配期望的输入值。我们刚刚编辑的输入框接收当前值,另一个输入框更新为转换后的温度。
-
-每个更新都会执行相同的步骤,以便输入保持同步。
-
-## 经验总结
-
-在一个 React 应用中,对于任何可变的数据都应该循序“单一数据源”原则。通常情况下,state 首先被添加到需要它进行渲染的组件。然后,如果其它的组件也需要它,你可以提升状态到它们最近的祖先组件。你应该依赖 [从上到下的数据流向](http://www.css88.com/react/docs/state-and-lifecycle.html#the-data-flows-down) ,而不是试图在不同的组件中同步状态。
-
-提升状态相对于双向绑定方法需要写更多的“模板”代码,但是有一个好处,它可以更方便的找到和隔离 bugs。由于任何 state(状态) 都 “存活” 在若干的组件中,而且可以分别对其独立修改,所以发生错误的可能大大减少。另外,你可以实现任何定制的逻辑来拒绝或者转换用户输入。
-
-如果某个东西可以从 props(属性) 或者 state(状态) 得到,那么它可能不应该在 state(状态) 中。例如,我们只保存最后编辑的 `temperature` 和它的 `scale`,而不是保存 `celsiusValue` 和`fahrenheitValue` 。另一个输入框的值总是在 `render()` 方法中计算得来的。这使我们对其进行清除和四舍五入到其他字段同时不会丢失用户输入的精度。
-
-当你看到 UI 中的错误,你可以使用 [React 开发者工具](https://github.com/facebook/react-devtools)来检查 props ,并向上遍历树,直到找到负责更新状态的组件。这使你可以跟踪到 bug 的源头:
-
-# 组合和继承对比(Composition vs Inheritance)
-
-React 拥有一个强大的组合模型,我们建议使用组合而不是继承以实现代码的重用。
-
-在本节中,我们将考虑几个问题,即 React 新手经常会使用继承,并展示我们如何通过组合来解决它们。
-
-## 包含
-
-一些组件在设计前无法获知自己要使用什么子组件,尤其在 `Sidebar` 和 `Dialog` 等通用 “容器” 中比较常见。
-
-我们建议这种组件使用特别的 `children` prop 来直接传递 子元素到他们的输出中:
-
-```
-function FancyBorder(props) {
- return (
-
- {props.children}
-
- );
-}
-
-```
-
-这允许其他组件通过嵌套 JSX 传递任意子组件给他们:
-
-```
-function WelcomeDialog() {
- return (
-
-
- Welcome
-
-
- Thank you for visiting our spacecraft!
-
-
- );
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/ozqNOV?editors=0010)。
-
-在 `` JSX 标签中的任何内容被传递到 `FancyBorder` 组件中,作为一个 `children`prop(属性)。由于 `FancyBorder` 渲染 `{props.children}` 到一个 `` 中,传递的元素会呈现在最终的输出中。
-
-然而这并不常见,有时候,在一个组件中你可能需要多个 “占位符” 。在这种情况下,你可以使用自定义的 prop(属性),而不是使用 `children` :
-
-```
-function SplitPane(props) {
- return (
-
-
- {props.left}
-
-
- {props.right}
-
-
- );
-}
-
-function App() {
- return (
-
- }
- right={
-
- } />
- );
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/gwZOJp?editors=0010)。
-
-如 `和` 等 React 元素本质上也是对象,所以可以将其像其他数据一样作为 props(属性) 传递使用。
-
-## 特例
-
-有时候,我们考虑组件作为其它组件的“特殊情况”。例如,我们可能说一个 `WelcomeDialog` 是`Dialog` 的一个特殊用例。
-
-在 React 中,也可以使用组合来实现,一个偏“特殊”的组件渲染出一个偏“通用”的组件,通过 props(属性) 配置它:
-
-```
-function Dialog(props) {
- return (
-
-
- {props.title}
-
-
- {props.message}
-
-
- );
-}
-
-function WelcomeDialog() {
- return (
-
- );
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/kkEaOZ?editors=0010)。
-
-对于用类定义的组件组合也同样适用:
-
-```
-function Dialog(props) {
- return (
-
-
- {props.title}
-
-
- {props.message}
-
- {props.children}
-
- );
-}
-
-class SignUpDialog extends React.Component {
- constructor(props) {
- super(props);
- this.handleChange = this.handleChange.bind(this);
- this.handleSignUp = this.handleSignUp.bind(this);
- this.state = {login: ''};
- }
-
- render() {
- return (
-
-
-
- Sign Me Up!
-
-
- );
- }
-
- handleChange(e) {
- this.setState({login: e.target.value});
- }
-
- handleSignUp() {
- alert(`Welcome aboard, ${this.state.login}!`);
- }
-}
-
-```
-
-[在 CodePen 中尝试](http://codepen.io/gaearon/pen/gwZbYa?editors=0010)。
-
-## 如何看待?
-
-在 Facebook ,我们在千万的组件中使用 React,我们还没有发现任何用例,值得我们建议你用继承层次结构来创建组件。
-
-使用 props(属性) 和 组合已经足够灵活来明确、安全的定制一个组件的外观和行为。切记,组件可以接受任意的 props(属性) ,包括原始值、React 元素,或者函数。
-
-如果要在组件之间重用非 UI 功能,我们建议将其提取到单独的 JavaScript 模块中。组件可以导入它并使用该函数,对象或类,而不扩展它。
diff --git a/frontend/mvc/vue/vue-cli.md b/frontend/mvc/vue/vue-cli.md
deleted file mode 100644
index 9a25fc6297..0000000000
--- a/frontend/mvc/vue/vue-cli.md
+++ /dev/null
@@ -1,413 +0,0 @@
-# Vue CLI
-
-> :hammer_and_wrench: Vue.js 开发的标准工具
->
-> - version: 3.x
-
-## 简介
-
-Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:
-
-- 通过 `@vue/cli` 搭建交互式的项目脚手架。
-
-- 通过 `@vue/cli` + `@vue/cli-service-global` 快速开始零配置原型开发。
-
-- 一个运行时依赖 (
-
- ```
- @vue/cli-service
- ```
-
- ),该依赖:
-
- - 可升级;
- - 基于 webpack 构建,并带有合理的默认配置;
- - 可以通过项目内的配置文件进行配置;
- - 可以通过插件进行扩展。
-
-- 一个丰富的官方插件集合,集成了前端生态中最好的工具。
-
-- 一套完全图形化的创建和管理 Vue.js 项目的用户界面。
-
-## 入门
-
-安装:
-
-```bash
-npm install -g @vue/cli
-# OR
-yarn global add @vue/cli
-```
-
-创建一个项目:
-
-```bash
-vue create my-project
-# OR
-vue ui
-```
-
-确认安装成功:
-
-```bash
-vue --version
-```
-
-## 基础
-
-
-
-### vue serve
-
-你可以使用 `vue serve` 和 `vue build` 命令对单个 `*.vue` 文件进行快速原型开发,不过这需要先额外安装一个全局的扩展:
-
-```bash
-npm install -g @vue/cli-service-global
-```
-
-`vue serve` 的缺点就是它需要安装全局依赖,这使得它在不同机器上的一致性不能得到保证。因此这只适用于快速原型开发。
-
-```text
-Usage: serve [options] [entry]
-
-在开发环境模式下零配置为 .js 或 .vue 文件启动一个服务器
-
-
-Options:
-
- -o, --open 打开浏览器
- -c, --copy 将本地 URL 复制到剪切板
- -h, --help 输出用法信息
-```
-
-你所需要的仅仅是一个 `App.vue` 文件:
-
-```vue
-
- Hello!
-
-```
-
-然后在这个 `App.vue` 文件所在的目录下运行:
-
-```bash
-vue serve
-```
-
-`vue serve` 使用了和 `vue create` 创建的项目相同的默认设置 (webpack、Babel、PostCSS 和 ESLint)。它会在当前目录自动推导入口文件——入口可以是 `main.js`、`index.js`、`App.vue` 或 `app.vue` 中的一个。你也可以显式地指定入口文件:
-
-```bash
-vue serve MyComponent.vue
-```
-
-如果需要,你还可以提供一个 `index.html`、`package.json`、安装并使用本地依赖、甚至通过相应的配置文件配置 Babel、PostCSS 和 ESLint。
-
-### vue build
-
-```text
-Usage: build [options] [entry]
-
-在生产环境模式下零配置构建一个 .js 或 .vue 文件
-
-
-Options:
-
- -t, --target 构建目标 (app | lib | wc | wc-async, 默认值:app)
- -n, --name 库的名字或 Web Components 组件的名字 (默认值:入口文件名)
- -d, --dest 输出目录 (默认值:dist)
- -h, --help 输出用法信息
-```
-
-你也可以使用 `vue build` 将目标文件构建成一个生产环境的包并用来部署:
-
-```bash
-vue build MyComponent.vue
-```
-
-`vue build` 也提供了将组件构建成为一个库或一个 Web Components 组件的能力。查阅[构建目标](https://cli.vuejs.org/zh/guide/build-targets.html)了解更多。
-
-### vue create
-
-运行以下命令来创建一个新项目:
-
-```bash
-vue create hello-world
-```
-
-你会被提示选取一个 preset。你可以选默认的包含了基本的 Babel + ESLint 设置的 preset,也可以选“手动选择特性”来选取需要的特性。
-
-`vue create` 命令有一些可选项,你可以通过运行以下命令进行探索:
-
-```bash
-vue create --help
-用法:create [options]
-
-创建一个由 `vue-cli-service` 提供支持的新项目
-
-
-选项:
-
- -p, --preset 忽略提示符并使用已保存的或远程的预设选项
- -d, --default 忽略提示符并使用默认预设选项
- -i, --inlinePreset 忽略提示符并使用内联的 JSON 字符串预设选项
- -m, --packageManager 在安装依赖时使用指定的 npm 客户端
- -r, --registry 在安装依赖时使用指定的 npm registry
- -g, --git [message] 强制 / 跳过 git 初始化,并可选的指定初始化提交信息
- -n, --no-git 跳过 git 初始化
- -f, --force 覆写目标目录可能存在的配置
- -c, --clone 使用 git clone 获取远程预设选项
- -x, --proxy 使用指定的代理创建项目
- -b, --bare 创建项目时省略默认组件中的新手指导信息
- -h, --help 输出使用帮助信息
-```
-
-### vue ui
-
-可以通过 `vue ui` 命令以图形化界面创建和管理项目:
-
-```bash
-vue ui
-```
-
-上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。
-
-
-
-## 插件
-
-Vue CLI 使用了一套基于插件的架构。
-
-插件可以修改 webpack 的内部配置,也可以向 `vue-cli-service` 注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的。
-
-### 在现有的项目中安装插件
-
-每个 CLI 插件都会包含一个 (用来创建文件的) 生成器和一个 (用来调整 webpack 核心配置和注入命令的) 运行时插件。当你使用 `vue create` 来创建一个新项目的时候,有些插件会根据你选择的特性被预安装好。如果你想在一个已经被创建好的项目中安装一个插件,可以使用 `vue add` 命令:
-
-```bash
-vue add @vue/eslint
-```
-
-### 项目本地的插件
-
-如果你需要在项目里直接访问插件 API 而不需要创建一个完整的插件,你可以在 `package.json` 文件中使用 `vuePlugins.service` 选项:
-
-```json
-{
- "vuePlugins": {
- "service": ["my-commands.js"]
- }
-}
-```
-
-每个文件都需要暴露一个函数,接受插件 API 作为第一个参数。关于插件 API 的更多信息可以查阅[插件开发指南](https://cli.vuejs.org/zh/dev-guide/plugin-dev.html)。
-
-你也可以通过 `vuePlugins.ui` 选项添加像 UI 插件一样工作的文件:
-
-```json
-{
- "vuePlugins": {
- "ui": ["my-ui.js"]
- }
-}
-```
-
-更多信息请阅读 [UI 插件 API](https://cli.vuejs.org/zh/dev-guide/ui-api.html)。
-
-### Preset
-
-一个 Vue CLI preset 是一个包含创建新项目所需预定义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。
-
-在 `vue create` 过程中保存的 preset 会被放在你的 home 目录下的一个配置文件中 (`~/.vuerc`)。你可以通过直接编辑这个文件来调整、添加、删除保存好的 preset。
-
-这里有一个 preset 的示例:
-
-```json
-{
- "useConfigFiles": true,
- "router": true,
- "vuex": true,
- "cssPreprocessor": "sass",
- "plugins": {
- "@vue/cli-plugin-babel": {},
- "@vue/cli-plugin-eslint": {
- "config": "airbnb",
- "lintOn": ["save", "commit"]
- }
- }
-}
-```
-
-#### Preset 插件的版本管理
-
-你可以显式地指定用到的插件的版本:
-
-```json
-{
- "plugins": {
- "@vue/cli-plugin-eslint": {
- "version": "^3.0.0",
- // ... 该插件的其它选项
- }
- }
-}
-```
-
-注意对于官方插件来说这不是必须的——当被忽略时,CLI 会自动使用 registry 中最新的版本。不过**我们推荐为 preset 列出的所有第三方插件提供显式的版本范围**。
-
-#### 允许插件的命令提示
-
-每个插件在项目创建的过程中都可以注入它自己的命令提示,不过当你使用了一个 preset,这些命令提示就会被跳过,因为 Vue CLI 假设所有的插件选项都已经在 preset 中声明过了。
-
-在有些情况下你可能希望 preset 只声明需要的插件,同时让用户通过插件注入的命令提示来保留一些灵活性。
-
-对于这种场景你可以在插件选项中指定 `"prompts": true` 来允许注入命令提示:
-
-```json
-{
- "plugins": {
- "@vue/cli-plugin-eslint": {
- // 让用户选取他们自己的 ESLint config
- "prompts": true
- }
- }
-}
-```
-
-#### 远程 Preset
-
-你可以通过发布 git repo 将一个 preset 分享给其他开发者。这个 repo 应该包含以下文件:
-
-- `preset.json`: 包含 preset 数据的主要文件(必需)。
-- `generator.js`: 一个可以注入或是修改项目中文件的 [Generator](https://cli.vuejs.org/zh/dev-guide/plugin-dev.html#generator)。
-- `prompts.js` 一个可以通过命令行对话为 generator 收集选项的 [prompts 文件](https://cli.vuejs.org/zh/dev-guide/plugin-dev.html#第三方插件的对话)。
-
-发布 repo 后,你就可以在创建项目的时候通过 `--preset` 选项使用这个远程的 preset 了:
-
-```bash
-# 从 GitHub repo 使用 preset
-vue create --preset username/repo my-project
-```
-
-GitLab 和 BitBucket 也是支持的。如果要从私有 repo 获取,请确保使用 `--clone` 选项:
-
-```bash
-vue create --preset gitlab:username/repo --clone my-project
-vue create --preset bitbucket:username/repo --clone my-project
-```
-
-#### 加载文件系统中的 Preset
-
-当开发一个远程 preset 的时候,你必须不厌其烦的向远程 repo 发出 push 进行反复测试。为了简化这个流程,你也可以直接在本地测试 preset。如果 `--preset` 选项的值是一个相对或绝对文件路径,或是以 `.json` 结尾,则 Vue CLI 会加载本地的 preset:
-
-```bash
-# ./my-preset 应当是一个包含 preset.json 的文件夹
-vue create --preset ./my-preset my-project
-
-# 或者,直接使用当前工作目录下的 json 文件:
-vue create --preset my-preset.json my-project
-```
-
-### CLI 服务
-
-在一个 Vue CLI 项目中,`@vue/cli-service` 安装了一个名为 `vue-cli-service` 的命令。你可以在 npm scripts 中以 `vue-cli-service`、或者从终端中以 `./node_modules/.bin/vue-cli-service` 访问这个命令。
-
-这是你使用默认 preset 的项目的 `package.json`:
-
-```json
-{
- "scripts": {
- "serve": "vue-cli-service serve",
- "build": "vue-cli-service build"
- }
-}
-```
-
-你可以通过 npm 或 Yarn 调用这些 script:
-
-```bash
-npm run serve
-# OR
-yarn serve
-```
-
-
-
-## 开发
-
-### 浏览器兼容性
-
-#### browserslist
-
-你会发现有 `package.json` 文件里的 `browserslist` 字段 (或一个单独的 `.browserslistrc` 文件),指定了项目的目标浏览器的范围。这个值会被 [@babel/preset-env](https://new.babeljs.io/docs/en/next/babel-preset-env.html) 和 [Autoprefixer](https://github.com/postcss/autoprefixer) 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。
-
-#### Polyfill
-
-一个默认的 Vue CLI 项目会使用 [@vue/babel-preset-app](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app),它通过 `@babel/preset-env` 和 `browserslist` 配置来决定项目需要的 polyfill。
-
-默认情况下,它会把 [`useBuiltIns: 'usage'`](https://new.babeljs.io/docs/en/next/babel-preset-env.html#usebuiltins-usage) 传递给 `@babel/preset-env`,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着**如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。**
-
-如果有依赖需要 polyfill,你有几种选择:
-
-1. **如果该依赖基于一个目标环境不支持的 ES 版本撰写:** 将其添加到 `vue.config.js` 中的 [`transpileDependencies`](https://cli.vuejs.org/zh/config/#transpiledependencies) 选项。这会为该依赖同时开启语法语法转换和根据使用情况检测 polyfill。
-
-2. **如果该依赖交付了 ES5 代码并显式地列出了需要的 polyfill:** 你可以使用 `@vue/babel-preset-app` 的 [polyfills](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app#polyfills) 选项预包含所需要的 polyfill。**注意 es6.promise 将被默认包含,因为现在的库依赖 Promise 是非常普遍的。**
-
- ```js
- // babel.config.js
- module.exports = {
- presets: [
- ['@vue/app', {
- polyfills: [
- 'es6.promise',
- 'es6.symbol'
- ]
- }]
- ]
- }
- ```
-
- > 提示
- >
- > 我们推荐以这种方式添加 polyfill 而不是在源代码中直接导入它们,因为如果这里列出的 polyfill 在 `browserslist` 的目标中不需要,则它会被自动排除。
-
-3. **如果该依赖交付 ES5 代码,但使用了 ES6+ 特性且没有显式地列出需要的 polyfill (例如 Vuetify):请使用 useBuiltIns: 'entry' 然后在入口文件添加 import '@babel/polyfill'。这会根据 browserslist 目标导入所有** polyfill,这样你就不用再担心依赖的 polyfill 问题了,但是因为包含了一些没有用到的 polyfill 所以最终的包大小可能会增加。
-
-### HTML 和静态资源
-
-#### HTML
-
-`public/index.html` 文件是一个会被 [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) 处理的模板。在构建过程中,资源链接会被自动注入。另外,Vue CLI 也会自动注入 resource hint (`preload/prefetch`、manifest 和图标链接 (当用到 PWA 插件时) 以及构建过程中处理的 JavaScript 和 CSS 文件的资源链接。
-
-#### 插值
-
-因为 index 文件被用作模板,所以你可以使用 [lodash template](https://lodash.com/docs/4.17.10#template) 语法插入内容:
-
-- `<%= VALUE %>` 用来做不转义插值;
-- `<%- VALUE %>` 用来做 HTML 转义插值;
-- `<% expression %>` 用来描述 JavaScript 流程控制。
-
-除了[被 `html-webpack-plugin` 暴露的默认值](https://github.com/jantimon/html-webpack-plugin#writing-your-own-templates)之外,所有[客户端环境变量](https://cli.vuejs.org/zh/guide/mode-and-env.html#using-env-variables-in-client-side-code)也可以直接使用。例如,`BASE_URL` 的用法:
-
-```html
-
-```
-
-更多内容可以查阅:
-
-- [baseUrl](https://cli.vuejs.org/zh/config/#baseurl)
-
-### Preload
-
-CSS
-
-Webpack
-
-环境变量和模式
-
-构建目标
-
-部署
-
-## 配置
-
-配置细节参考:https://cli.vuejs.org/zh/config/
\ No newline at end of file
diff --git a/frontend/mvc/vue/vue.md b/frontend/mvc/vue/vue.md
deleted file mode 100644
index b74449f910..0000000000
--- a/frontend/mvc/vue/vue.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Vue 教程
-
-
-
-- [简介](#简介)
- - [vue 生命周期](#vue-生命周期)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-Vue (读音 /vjuː/,类似于 **view**) 是一套用于构建用户界面的**渐进式框架**。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
-
-Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:
-
-html
-
-```html
-{{ message }}
-```
-
-Javascript
-
-```javascript
-var app = new Vue({
- el: '#app',
- data: {
- message: 'Hello Vue!'
- }
-});
-```
-
-### vue 生命周期
-
-
-
-## 引申和引用
-
-- 官方
- - [vue Github](https://github.com/vuejs/vue)
- - [vue 官方文档](https://cn.vuejs.org/index.html)
- - [vue-router Github](https://github.com/vuejs/vue-router) - vue 官方路由框架
- - [vuex Github](https://github.com/vuejs/vuex) - vue 官方集中式状态管理框架
- - [vue-cli Github](https://github.com/vuejs/vue-cli) - vue 官方开发工具
- - [vue-devtools Github](https://github.com/vuejs/vue-devtools) - vue 官方 debug 工具
-- 更多资源
- - [Awesome Vue](https://github.com/vuejs/awesome-vue)
diff --git a/frontend/nodejs/nodejs.md b/frontend/nodejs/nodejs.md
deleted file mode 100644
index 207e97e1e9..0000000000
--- a/frontend/nodejs/nodejs.md
+++ /dev/null
@@ -1,977 +0,0 @@
-# Node.js 入门
-
-> Node.js 是一个能够在服务器端运行 JavaScript 源代码的跨平台运行环境。
->
-> 关键词: `nodejs`, `REPL`, `require`, `exports`
-
-
-
-- [安装配置](#安装配置)
- - [Windows / IOS](#windows--ios)
- - [Linux / Ubuntu / Debian](#linux--ubuntu--debian)
- - [第一个应用](#第一个应用)
-- [交互式解释器(REPL)](#交互式解释器repl)
- - [简单的表达式运算](#简单的表达式运算)
- - [使用变量](#使用变量)
- - [多行表达式](#多行表达式)
-- [Node 的代码组织](#node-的代码组织)
- - [模块路径解析规则](#模块路径解析规则)
- - [包(package)](#包package)
- - [index.js](#indexjs)
- - [package.json](#packagejson)
- - [命令行程序](#命令行程序)
- - [工程目录](#工程目录)
-- [Node 的 IO 操作](#node-的-io-操作)
- - [Buffer](#buffer)
- - [Stream](#stream)
- - [文件系统](#文件系统)
-- [Node.js 工具](#nodejs-工具)
- - [nvm](#nvm)
-- [引申和引用](#引申和引用)
-
-
-
-## 安装配置
-
-### Windows / IOS
-
-可以在官方下载安装文件:[https://nodejs.org/en/download/](https://nodejs.org/en/download/)。
-
-
-
-### Linux / Ubuntu / Debian
-
-命令格式如下:
-
-```sh
-curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
-sudo apt-get install -y nodejs
-```
-
-详细内容参考官方文档:[通过包管理器安装 Node.js](https://nodejs.org/en/download/package-manager/)
-
-### 第一个应用
-
-创建 helloworld.js 文件,内容如下:
-
-```js
-function helloworld() {
- console.log("Hello World!");
-}
-helloworld();
-```
-
-执行命令 `node helloworld.js` 运行脚本。
-
-## 交互式解释器(REPL)
-
-Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。
-
-Node 自带了交互式解释器,可以执行以下任务:
-
-- **读取** - 读取用户输入,解析输入了 Javascript 数据结构并存储在内存中。
-- **执行** - 执行输入的数据结构
-- **打印** - 输出结果
-- **循环** - 循环操作以上步骤直到用户两次按下 **ctrl-c** 按钮退出。
-
-Node 的交互式解释器可以很好的调试 Javascript 代码。
-
-我们可以输入 `node` 命令来启动 Node 的终端:
-
-### 简单的表达式运算
-
-```node
-$ node
-> 1 + 4
-5
-> 1 + ( 2 * 3 ) - 4
-2.5
-```
-
-### 使用变量
-
-你可以将数据存储在变量中,并在你需要的时候使用它。
-
-变量声明需要使用 `var` 关键字,如果没有使用 `var` 关键字变量会直接打印出来。
-
-使用 `var` 关键字的变量可以使用 `console.log()` 来输出变量。
-
-```node
-$ node
-> x = 10
-10
-> var y = 10
-undefined
-> x + y
-20
-> console.log("Hello World")
-Hello World
-undefined
-> console.log("www.runoob.com")
-www.runoob.com
-undefined
-```
-
-### 多行表达式
-
-Node REPL 支持输入多行表达式,这就有点类似 JavaScript。接下来让我们来执行一个 do-while 循环:
-
-```node
-$ node
-> var x = 0
-undefined
-> do {
-... x++;
-... console.log("x: " + x);
-... } while ( x < 5 );
-x: 1
-x: 2
-x: 3
-x: 4
-x: 5
-undefined
->
-```
-
-**...** 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式。
-
-不会打乱对象原有的继承关系。
-
-## Node 的代码组织
-
-有经验的 C 程序员在编写一个新程序时首先从 make 文件写起。同样的,使用 Node.js 编写程序前,为了有个良好的开端,首先需要准备好代码的目录结构和部署方式,就如同修房子要先搭脚手架。本章将介绍与之相关的各种知识。
-
-### 模块路径解析规则
-
-我们已经知道,`require` 函数支持斜杠(`/`)或盘符(`C:`)开头的绝对路径,也支持`./`开头的相对路径。但这两种路径在模块之间建立了强耦合关系,一旦某个模块文件的存放位置需要变更,使用该模块的其它模块的代码也需要跟着调整,变得牵一发动全身。因此,`require`函数支持第三种形式的路径,写法类似于`foo/bar`,并依次按照以下规则解析路径,直到找到模块位置。
-
-#### 内置模块
-
-如果传递给 `require` 函数的是 Node.js 内置模块名称,不做路径解析,直接返回内部模块的导出对象,例如`require("fs")`。
-
-#### node_modules 目录
-
-Node.js 定义了一个特殊的 `node_modules` 目录用于存放模块。
-
-例如某个模块的绝对路径是 `/home/user/hello.js`,在该模块中使用 `require('foo/bar')` 方式加载模块时,则 Node.js 会依次尝试使用以下路径。
-
-```
-/home/user/node_modules/foo/bar
-/home/node_modules/foo/bar
-/node_modules/foo/bar
-```
-
-#### NODE_PATH 环境变量
-
-与 `PATH` 环境变量类似,Node.js 允许通过 `NODE_PATH` 环境变量来指定额外的模块搜索路径。
-
-`NODE_PATH` 环境变量中包含一到多个目录路径,路径之间在 Linux 下使用`:`分隔,在 Windows 下使用`;`分隔。例如定义了以下 `NODE_PATH` 环境变量:
-
-```
-NODE_PATH=/home/user/lib:/home/lib
-```
-
-当使用 `require('foo/bar')` 的方式加载模块时,则 Node.js 依次尝试以下路径。
-
-```
-/home/user/lib/foo/bar
-/home/lib/foo/bar
-```
-
-### 包(package)
-
-我们已经知道了 JS 模块的基本单位是单个 JS 文件,但复杂些的模块往往由多个子模块组成。为了便于管理和使用,我们可以把由多个子模块组成的大模块称做`包`,并把所有子模块放在同一个目录里。
-
-在组成一个包的所有子模块中,需要有一个入口模块,入口模块的导出对象被作为包的导出对象。例如有以下目录结构。
-
-```
-- /home/user/lib/
- - cat/
- head.js
- body.js
- main.js
-```
-
-其中`cat`目录定义了一个包,其中包含了 3 个子模块。`main.js`作为入口模块,其内容如下:
-
-```js
-var head = require("./head");
-var body = require("./body");
-
-exports.create = function(name) {
- return {
- name: name,
- head: head.create(),
- body: body.create()
- };
-};
-```
-
-在其它模块里使用包的时候,需要加载包的入口模块。接着上例,使用`require('/home/user/lib/cat/main')`能达到目的,但是入口模块名称出现在路径里看上去不是个好主意。因此我们需要做点额外的工作,让包使用起来更像是单个模块。
-
-### index.js
-
-当模块的文件名是`index.js`,加载模块时可以使用模块所在目录的路径代替模块文件路径,因此接着上例,以下两条语句等价。
-
-```js
-var cat = require("/home/user/lib/cat");
-var cat = require("/home/user/lib/cat/index");
-```
-
-这样处理后,就只需要把包目录路径传递给`require`函数,感觉上整个目录被当作单个模块使用,更有整体感。
-
-### package.json
-
-如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个`package.json`文件,并在其中指定入口模块的路径。上例中的`cat`模块可以重构如下。
-
-```
-- /home/user/lib/
- - cat/
- + doc/
- - lib/
- head.js
- body.js
- main.js
- + tests/
- package.json
-```
-
-其中`package.json`内容如下。
-
-```json
-{
- "name": "cat",
- "main": "./lib/main.js"
-}
-```
-
-如此一来,就同样可以使用`require('/home/user/lib/cat')`的方式加载模块。Node.js 会根据包目录下的`package.json`找到入口模块所在位置。
-
-### 命令行程序
-
-使用 Node.js 编写的东西,要么是一个包,要么是一个命令行程序,而前者最终也会用于开发后者。因此我们在部署代码时需要一些技巧,让用户觉得自己是在使用一个命令行程序。
-
-例如我们用 Node.js 写了个程序,可以把命令行参数原样打印出来。该程序很简单,在主模块内实现了所有功能。并且写好后,我们把该程序部署在`/home/user/bin/node-echo.js`这个位置。为了在任何目录下都能运行该程序,我们需要使用以下终端命令。
-
-```
-$ node /home/user/bin/node-echo.js Hello World
-Hello World
-```
-
-这种使用方式看起来不怎么像是一个命令行程序,下边的才是我们期望的方式。
-
-```
-$ node-echo Hello World
-```
-
-#### Linux
-
-在 Linux 系统下,我们可以把 JS 文件当作 shell 脚本来运行,从而达到上述目的,具体步骤如下:
-
-(1)在 shell 脚本中,可以通过`#!`注释来指定当前脚本使用的解析器。所以我们首先在`node-echo.js`文件顶部增加以下一行注释,表明当前脚本使用 Node.js 解析。
-
-```
-#! /usr/bin/env node
-```
-
-Node.js 会忽略掉位于 JS 模块首行的`#!`注释,不必担心这行注释是非法语句。
-
-(2)然后,我们使用以下命令赋予`node-echo.js`文件执行权限。
-
-```
-$ chmod +x /home/user/bin/node-echo.js
-```
-
-(3)最后,我们在 PATH 环境变量中指定的某个目录下,例如在`/usr/local/bin`下边创建一个软链文件,文件名与我们希望使用的终端命令同名,命令如下:
-
-```
-$ sudo ln -s /home/user/bin/node-echo.js /usr/local/bin/node-echo
-```
-
-这样处理后,我们就可以在任何目录下使用`node-echo`命令了。
-
-#### Windows
-
-在 Windows 系统下的做法完全不同,我们得靠`.cmd`文件来解决问题。假设`node-echo.js`存放在`C:\Users\user\bin`目录,并且该目录已经添加到 PATH 环境变量里了。接下来需要在该目录下新建一个名为`node-echo.cmd`的文件,文件内容如下:
-
-```
-@node "C:\User\user\bin\node-echo.js" %*
-```
-
-这样处理后,我们就可以在任何目录下使用`node-echo`命令了。
-
-### 工程目录
-
-了解了以上知识后,现在我们可以来完整地规划一个工程目录了。以编写一个命令行程序为例,一般我们会同时提供命令行模式和 API 模式两种使用方式,并且我们会借助三方包来编写代码。除了代码外,一个完整的程序也应该有自己的文档和测试用例。因此,一个标准的工程目录都看起来像下边这样。
-
-```
-- /home/user/workspace/node-echo/ ## 工程目录
- - bin/ ## 存放命令行相关代码
- node-echo
- + docs/ ## 存放文档
- - libs/ ## 存放API相关代码
- echo.js
- - node_modules/ ## 存放三方包
- + argv/
- + tests/ ## 存放测试用例
- package.json ## 元数据文件
- README.md ## 说明文件
-```
-
-其中部分文件内容如下:
-
-```js
-/* bin/node-echo */
-var argv = require('argv'),
- echo = require('../lib/echo');
-console.log(echo(argv.join(' ')));
-
-/* lib/echo.js */
-module.exports = function (message) {
- return message;
-};
-
-/* package.json */
-{
- "name": "node-echo",
- "main": "./lib/echo.js"
-}
-```
-
-以上例子中分类存放了不同类型的文件,并通过`node_moudles`目录直接使用三方包名加载模块。此外,定义了`package.json`之后,`node-echo`目录也可被当作一个包来使用。
-
-## Node 的 IO 操作
-
-### Buffer
-
-JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
-
-但在处理像 TCP 流或文件流时,必须使用到二进制数据。因此在 Node.js 中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
-
-在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理 I/O 操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。
-
-#### 创建 Buffer
-
-Node Buffer 类可以通过多种方式来创建。
-
-1. 创建指定长度的的 Buffer 实例:
-
-```js
-var buf = new Buffer(10);
-```
-
-2. 通过给定的数组创建 Buffer 实例:
-
-```js
-var buf = new Buffer([10, 20, 30, 40, 50]);
-```
-
-3. 通过一个字符串来创建 Buffer 实例:
-
-```js
-var buf = new Buffer("How are you?", "utf-8");
-```
-
-`utf-8` 是默认的编码方式,此外它同样支持以下编码:`ascii`, `utf8`, `utf16le`, `ucs2`, `base64` 和 `hex`。
-
-#### 写入缓冲区
-
-**语法**
-
-```js
-buf.write(string[, offset[, length]][, encoding])
-```
-
-**参数**
-
-- **string** - 写入缓冲区的字符串。
-- **offset** - 缓冲区开始写入的索引值,默认为 0 。
-- **length** - 写入的字节数,默认为 buffer.length
-- **encoding** - 使用的编码。默认为 'utf8' 。
-
-**返回值**
-
-返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。
-
-**示例**
-
-```js
-var buf1 = new Buffer(10);
-console.log("buf1 写入字节数: " + buf1.write("0123456789"));
-
-var buf2 = new Buffer(5);
-console.log("buf2 写入字节数: " + buf2.write("0123456789"));
-```
-
-执行以上代码,输出结果为:
-
-```
-10
-5
-```
-
-#### 从缓冲区读取数据
-
-**语法**
-
-```js
-buf.toString([encoding[, start[, end]]])
-```
-
-**参数**
-
-- **encoding** - 使用的编码。默认为 'utf8' 。
-- **start** - 指定开始读取的索引位置,默认为 0。
-- **end** - 结束位置,默认为缓冲区的末尾。
-
-**返回值**
-
-解码缓冲区数据并使用指定的编码返回字符串。
-
-**实例**
-
-```js
-buf = new Buffer(26);
-for (var i = 0; i < 26; i++) {
- buf[i] = i + 97;
-}
-
-console.log(buf.toString("ascii")); // 输出: abcdefghijklmnopqrstuvwxyz
-console.log(buf.toString("ascii", 0, 5)); // 输出: abcde
-console.log(buf.toString("utf8", 0, 5)); // 输出: abcde
-console.log(buf.toString(undefined, 0, 5)); // 使用 'utf8' 编码, 并输出: abcde
-```
-
-#### 将 Buffer 转换为 JSON 对象
-
-**语法**
-
-```js
-buf.toJSON();
-```
-
-**返回**值
-
-返回 JSON 对象。
-
-**实例**
-
-```js
-var buf = new Buffer("goodbye");
-var json = buf.toJSON(buf);
-console.log(json);
-```
-
-执行以上代码,输出结果为:
-
-```js
-{ type: 'Buffer', data: [ 103, 111, 111, 100, 98, 121, 101 ] }
-```
-
-#### 缓冲区合并
-
-**语法**
-
-```js
-Buffer.concat(list[, totalLength])
-```
-
-**参数**
-
-- **list** - 用于合并的 Buffer 对象数组列表。
-- **totalLength** - 指定合并后 Buffer 对象的总长度。
-
-**返回值**
-
-返回一个多个成员合并的新 Buffer 对象。
-
-**实例**
-
-```js
-var buffer1 = new Buffer("Nothing is ");
-var buffer2 = new Buffer("impossible");
-var buffer3 = Buffer.concat([buffer1, buffer2]);
-console.log("buffer3 内容: " + buffer3.toString());
-```
-
-执行以上代码,输出结果为:
-
-```
-buffer3 内容: Nothing is impossible
-```
-
-#### 缓冲区比较
-
-**语法**
-
-```js
-buf.compare(otherBuffer);
-```
-
-**参数**
-
-参数描述如下:
-
-- **otherBuffer** 与 **buf** 对象比较的另外一个 Buffer 对象。
-
-**返回值**
-
-返回一个数字,表示 **buf** 在 **otherBuffer** 之前,之后或相同。
-
-**实例**
-
-```js
-var buffer1 = new Buffer("ABC");
-var buffer2 = new Buffer("ABCD");
-var result = buffer1.compare(buffer2);
-
-if (result < 0) {
- console.log(buffer1 + " 在 " + buffer2 + "之前");
-} else if (result == 0) {
- console.log(buffer1 + " 与 " + buffer2 + "相同");
-} else {
- console.log(buffer1 + " 在 " + buffer2 + "之后");
-}
-```
-
-执行以上代码,输出结果为:
-
-```
-ABC在ABCD之前
-```
-
-#### 拷贝缓冲区
-
-**语法**
-
-```js
-buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
-```
-
-**参数**
-
-- **targetBuffer** - 要拷贝的 Buffer 对象。
-- **targetStart** - 数字, 可选, 默认: 0
-- **sourceStart** - 数字, 可选, 默认: 0
-- **sourceEnd** - 数字, 可选, 默认: buffer.length
-
-**返回值**
-
-没有返回值。
-
-**实例**
-
-```js
-var buffer1 = new Buffer("ABC");
-var buffer2 = new Buffer(3);
-buffer1.copy(buffer2);
-console.log("buffer2 内容: " + buffer2.toString());
-```
-
-执行以上代码,输出结果为:
-
-```
-buffer2 内容: ABC
-```
-
-#### 剪切缓冲区
-
-**语法**
-
-```js
-buf.slice([start[, end]])
-```
-
-**参数**
-
-- **start** - 数字, 可选, 默认: 0
-- **end** - 数字, 可选, 默认: buffer.length
-
-**返回值**
-
-返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。
-
-实例
-
-```js
-var buffer1 = new Buffer("goodbye");
-var buffer2 = buffer1.slice(0, 2);
-console.log("buffer2 内容: " + buffer2.toString());
-```
-
-执行以上代码,输出结果为:
-
-```
-buffer2 内容: go
-```
-
-#### 缓冲区长度
-
-**语法**
-
-```js
-buf.length;
-```
-
-**返回值**
-
-返回 Buffer 对象所占据的内存长度。
-
-**实例**
-
-```js
-var buffer = new Buffer("made in China");
-console.log("buffer length: " + buffer.length);
-```
-
-执行以上代码,输出结果为:
-
-```
-buffer length: 13
-```
-
-### Stream
-
-Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对 http 服务器发起请求的 request 对象就是一个 Stream,还有 stdout(标准输出)。
-
-Node.js,Stream 有四种流类型:
-
-- **Readable** - 可读操作。
-- **Writable** - 可写操作。
-- **Duplex** - 可读可写操作.
-- **Transform** - 操作被写入数据,然后读出结果。
-
-所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
-
-- **data** - 当有数据可读时触发。
-- **end** - 没有更多的数据可读时触发。
-- **error** - 在接收和写入过程中发生错误时触发。
-- **finish** - 所有数据已被写入到底层系统时触发。
-
-本教程会为大家介绍常用的流操作。
-
-#### 读取流
-
-获取读取流
-
-```js
-var readerStream = fs.createReadStream(pathname);
-
-readerStream.on("data", function(chunk) {
- doSomething(chunk);
-});
-
-readerStream.on("end", function() {
- cleanUp();
-});
-```
-
-完整示例代码见:codes/chapter01/node/stream/streamDemo01.js
-
-#### 写入流
-
-获取写入流
-
-```js
-var writerStream = fs.createWriteStream("output.txt");
-writerStream.write(data, "UTF8");
-writerStream.end();
-```
-
-完整示例代码见:codes/chapter01/node/stream/streamDemo02.js
-
-#### 管道流
-
-管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
-
-```js
-var fs = require("fs");
-var readerStream = fs.createReadStream("input.txt");
-var writerStream = fs.createWriteStream("output.txt");
-readerStream.pipe(writerStream);
-```
-
-完整示例代码见:codes/chapter01/node/stream/streamDemo03.js
-
-#### 链式流
-
-链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。
-
-```js
-var fs = require("fs");
-var zlib = require("zlib");
-fs.createReadStream("input.txt")
- .pipe(zlib.createGzip())
- .pipe(fs.createWriteStream("input.txt.gz"));
-```
-
-完整示例代码见:codes/chapter01/node/stream/streamDemo04.js
-
-### 文件系统
-
-Node.js 提供一组类似 UNIX(POSIX)标准的文件操作 API。 Node 导入文件系统模块(fs)语法如下所示:
-
-```js
-var fs = require("fs");
-```
-
-#### 异步和同步
-
-Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
-
-异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
-
-建议大家是用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo01.js
-
-#### 打开文件
-
-**语法**
-
-```js
-fs.open(path, flags[, mode], callback)
-```
-
-**参数**
-
-- **path** - 文件的路径。
-- **flags** - 文件打开的行为。具体值详见下文。
-- **mode** - 设置文件模式(权限),文件创建默认权限为 0666(可读,可写)。
-- **callback** - 回调函数,带有两个参数如:callback(err, fd)。
-
-flags 参数可以是以下值:
-
-| Flag | 描述 |
-| ---- | ------------------------------------------------------ |
-| r | 以读取模式打开文件。如果文件不存在抛出异常。 |
-| r+ | 以读写模式打开文件。如果文件不存在抛出异常。 |
-| rs | 以同步的方式读取文件。 |
-| rs+ | 以同步的方式读取和写入文件。 |
-| w | 以写入模式打开文件,如果文件不存在则创建。 |
-| wx | 类似 'w',但是如果文件路径存在,则文件写入失败。 |
-| w+ | 以读写模式打开文件,如果文件不存在则创建。 |
-| wx+ | 类似 'w+', 但是如果文件路径存在,则文件读写失败。 |
-| a | 以追加模式打开文件,如果文件不存在则创建。 |
-| ax | 类似 'a', 但是如果文件路径存在,则文件追加失败。 |
-| a+ | 以读取追加模式打开文件,如果文件不存在则创建。 |
-| ax+ | 类似 'a+', 但是如果文件路径存在,则文件读取追加失败。 |
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo02.js
-
-#### 获取文件信息
-
-**语法**
-
-```js
-fs.stat(path, callback);
-```
-
-**参数**
-
-- **path** - 文件路径。
-- **callback** - 回调函数,带有两个参数如:(err, stats), **stats** 是 fs.Stats 对象。
-
-fs.stat(path)执行后,会将 stats 类的实例返回给其回调函数。可以通过 stats 类中的提供方法判断文件的相关属性。
-
-stats 类中的方法有:
-
-| 方法 | 描述 |
-| ------------------------- | --------------------------------------------------------------------------------- |
-| stats.isFile() | 如果是文件返回 true,否则返回 false。 |
-| stats.isDirectory() | 如果是目录返回 true,否则返回 false。 |
-| stats.isBlockDevice() | 如果是块设备返回 true,否则返回 false。 |
-| stats.isCharacterDevice() | 如果是字符设备返回 true,否则返回 false。 |
-| stats.isSymbolicLink() | 如果是软链接返回 true,否则返回 false。 |
-| stats.isFIFO() | 如果是 FIFO,返回 true,否则返回 false。FIFO 是 UNIX 中的一种特殊类型的命令管道。 |
-| stats.isSocket() | 如果是 Socket 返回 true,否则返回 false。 |
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo03.js
-
-#### 写入文件
-
-**语法**
-
-```js
-fs.writeFile(file, data[, options], callback)
-```
-
-如果文件存在,该方法写入的内容会覆盖旧的文件内容。
-
-**参数**
-
-- **file** - 文件名或文件描述符。
-- **data** - 要写入文件的数据,可以是 String(字符串) 或 Buffer(流) 对象。
-- **options** - 该参数是一个对象,包含 {encoding, mode, flag}。默认编码为 utf8, 模式为 0666 , flag 为 'w'
-- **callback** - 回调函数,回调函数只包含错误信息参数(err),在写入失败时返回。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo04.js
-
-#### 读取文件
-
-**语法**
-
-```js
-fs.read(fd, buffer, offset, length, position, callback);
-```
-
-该方法使用了文件描述符来读取文件。
-
-**参数**
-
-- **fd** - 通过 fs.open() 方法返回的文件描述符。
-- **buffer** - 数据写入的缓冲区。
-- **offset** - 缓冲区写入的写入偏移量。
-- **length** - 要从文件中读取的字节数。
-- **position** - 文件读取的起始位置,如果 position 的值为 null,则会从当前文件指针的位置读取。
-- **callback** - 回调函数,有三个参数 err, bytesRead, buffer,err 为错误信息, bytesRead 表示读取的字节数,buffer 为缓冲区对象。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo05.js
-
-#### 关闭文件
-
-**语法**
-
-```js
-fs.close(fd, callback);
-```
-
-该方法使用了文件描述符来读取文件。
-
-**参数**
-
-- **fd** - 通过 fs.open() 方法返回的文件描述符。
-- **callback** - 回调函数,没有参数。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo06.js
-
-#### 截取文件
-
-**语法**
-
-```js
-fs.ftruncate(fd, len, callback);
-```
-
-该方法使用了文件描述符来读取文件。
-
-**参数**
-
-- **fd** - 通过 fs.open() 方法返回的文件描述符。
-- **len** - 文件内容截取的长度。
-- **callback** - 回调函数,没有参数。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo07.js
-
-#### 删除文件
-
-**语法**
-
-```js
-fs.unlink(path, callback);
-```
-
-**参数**
-
-- **path** - 文件路径。
-- **callback** - 回调函数,没有参数。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo08.js
-
-#### 创建目录
-
-**语法**
-
-```js
-fs.mkdir(path[, mode], callback)
-```
-
-**参数**
-
-- **path** - 文件路径。
-- **mode** - 设置目录权限,默认为 0777。
-- **callback** - 回调函数,没有参数。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo09.js
-
-#### 读取目录
-
-**语法**
-
-```js
-fs.readdir(path, callback);
-```
-
-**参数**
-
-- **path** - 文件路径。
-- **callback** - 回调函数,回调函数带有两个参数 err, files,err 为错误信息,files 为 目录下的文件数组列表。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo10.js
-
-#### 删除目录
-
-**语法**
-
-```js
-fs.rmdir(path, callback);
-```
-
-**参数**
-
-- **path** - 文件路径。
-- **callback** - 回调函数,没有参数。
-
-**完整代码示例**:codes/chapter01/node/fs/fsDemo11.js
-
-## Node.js 工具
-
-### nvm
-
-[nvm](https://github.com/creationix/nvm) 是 Node 版本管理器。
-
-安装
-
-```sh
-# 两条命令效果相同
-wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
-curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
-```
-
-设置环境变量
-
-```sh
-export NVM_DIR="${XDG_CONFIG_HOME/:-$HOME/.}nvm"
-[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
-```
-
-使用
-
-```sh
-nvm install 6.14.4 # 安装 Node.js 版本
-nvm use node6.14.4 # 指定 Node.js 版本(必须已安装)
-```
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Node.js](nodejs.md)
-> - [Npm](npm.md)
-> - [Yarn](yarn.md)
->
-> :package: 本文归档在 [前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方**
- - [Node.js 官网](https://nodejs.org/zh-cn/)
- - [Node.js Github](https://github.com/nodejs/node)
-- **教程**
- - [Node.js 包教不包会](https://github.com/alsotang/node-lessons)
- - [一起学 Node.js](https://github.com/nswbmw/N-blog)
- - [七天学会 NodeJS](https://github.com/nqdeng/7-days-nodejs)
-- **规范**
- - [Node.JS 最佳实践](https://github.com/i0natan/nodebestpractices)
-- **工具**
- - [nvm](https://github.com/creationix/nvm) - Node 版本管理器
-- **更多资源**
- - [awesome-nodejs](https://github.com/sindresorhus/awesome-nodejs)
diff --git a/frontend/nodejs/npm.md b/frontend/nodejs/npm.md
deleted file mode 100644
index a2931df605..0000000000
--- a/frontend/nodejs/npm.md
+++ /dev/null
@@ -1,581 +0,0 @@
-# Npm 入门
-
-> **Npm 是随同 Nodejs 一起安装的 js 包管理工具。**
->
-> 关键词: `nodejs`, `包管理`, `npm`, `cnpm`, `package.json`, `node_modules`
-
-
-
-- [简介](#简介)
-- [安装](#安装)
-- [Npm 工作流](#npm-工作流)
-- [Npm 常用命令](#npm-常用命令)
- - [初始化新项目](#初始化新项目)
- - [安装模块](#安装模块)
- - [卸载模块](#卸载模块)
- - [更新模块](#更新模块)
- - [管理配置文件](#管理配置文件)
- - [发布包](#发布包)
- - [执行脚本](#执行脚本)
- - [查看安装信息](#查看安装信息)
-- [配置文件](#配置文件)
- - [package 版本](#package-版本)
- - [版本号](#版本号)
-- [npm 扩展](#npm-扩展)
- - [cnpm](#cnpm)
- - [nrm](#nrm)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-**Npm 是随同 Nodejs 一起安装的 js 包管理工具。**
-
-常见的使用场景有以下几种:
-
-- 允许用户从 Npm 服务器下载别人编写的第三方包到本地使用。
-- 允许用户从 Npm 服务器下载并安装别人编写的命令行程序到本地使用。
-- 允许用户将自己编写的包或命令行程序上传到 Npm 服务器供别人使用。
-
-如果一个项目中存在 `package.json` 文件,那么用户可以直接使用 `npm install` 命令自动安装和维护当前项目所需的所有模块。在 `package.json` 文件中,开发者可以指定每个依赖项的版本范围,这样既可以保证模块自动更新,又不会因为所需模块功能大幅变化导致项目出现问题。开发者也可以选择将模块固定在某个版本之上。
-
-## 安装
-
-由于新版的 nodejs 已经集成了 npm,所以之前 npm 也一并安装好了。同样可以通过输入 `npm -v` 来测试是否成功安装。命令如下,出现版本提示表示安装成功:
-
-```sh
-$ npm -v
-2.3.0
-```
-
-可以通过命令方式升级 npm
-
-- Linux - `sudo npm install npm -g`
-- Window - `npm install npm -g`
-
-## Npm 工作流
-
-Npm 工作流:
-
-1. 创建一个新项目
-2. 增加/更新/删除依赖
-3. 安装/重装你的依赖
-4. 引入版本控制系统(例如 git)
-5. 持续集成
-
-## Npm 常用命令
-
-**每个命令都会更新 `package.json` 文件。**
-
-- Npm 提供了很多命令,例如`install`和`publish`,使用`npm help`可查看所有命令。
-- 使用`npm help`可查看某条命令的详细帮助,例如`npm help install`。
-- 在`package.json`所在目录下使用`npm install . -g`可先在本地安装当前命令行程序,可用于发布前的本地测试。
-- 使用`npm update`可以把当前目录下`node_modules`子目录里边的对应模块更新至最新版本。
-- 使用`npm update -g`可以把全局安装的对应命令行程序更新至最新版。
-- 使用`npm cache clear`可以清空 Npm 本地缓存,用于对付使用相同版本号发布新版本代码的人。
-- 使用`npm unpublish @`可以撤销发布自己发布过的某个版本代码。
-
-Npm 提供了很多命令,例如 install 和 publish,使用 npm help 可查看所有命令。
-
-### 初始化新项目
-
-> `npm init` 用于初始化项目,它会创建一个名为 `package.json` 的配置文件。
-
-**命令格式**
-
-```
-npm init [-f|--force|-y|--yes]
-```
-
-**说明**
-
-执行命令后,npm 会问你一系列问题,然后在执行命令的目录下创建一个`package.json`文件。
-
-如果使用 `-f` / `--force` 或 `-y` / `--yes` ,npm 会使用默认值为你创建 `package.json` 文件,不再询问任何问题。
-
-创建模块,package.json 文件是必不可少的。我们可以使用 Npm 生成 `package.json` 文件,生成的文件包含了基本的结果。
-
-```sh
-$ npm init
-This utility will walk you through creating a `package.json` file.
-It only covers the most common items, and tries to guess sensible defaults.
-
-See `npm help json` for definitive documentation on these fields
-and exactly what they do.
-
-Use `npm install --save` afterwards to install a package and
-save it as a dependency in the `package.json` file.
-
-Press ^C at any time to quit.
-name: (node_modules) runoob # 模块名
-version: (1.0.0)
-description: Node.js 测试模块(www.runoob.com) # 描述
-entry point: (index.js)
-test command: make test
-git repository: https://github.com/runoob/runoob.git # Github 地址
-keywords:
-author:
-license: (ISC)
-About to write to ……/node_modules/package.json: # 生成地址
-
-{
- "name": "runoob",
- "version": "1.0.0",
- "description": "Node.js 测试模块(www.runoob.com)",
- ……
-}
-
-
-Is this ok? (yes) yes
-```
-
-以上的信息,你需要根据你自己的情况输入。在最后输入 "yes" 后会生成 `package.json` 文件。
-
-接下来我们可以使用以下命令在 npm 资源库中注册用户(使用邮箱注册):
-
-```sh
-$ npm adduser
-Username: mcmohd
-Password:
-Email: (this IS public) mcmohd@gmail.com
-```
-
-### 安装模块
-
-> `npm install` 用于安装模块。
-
-**命令格式**
-
-```sh
-npm install (with no args, in package dir)
-npm install # 安装位于文件系统上的包。
-npm install # 获取 url,然后安装它。为了区分此选项和其他选项,参数必须以“http://”或“https://”开头。
-npm install # 安装位于文件系统上某文件夹中的包
-npm install [<@scope>/] # 安装指定的包的最新版本。
-npm install [<@scope>/]@ # 安装被 tag 引用的包的版本。如果 tag 不存在于该包的注册表数据中,则失败。
-npm install [<@scope>/]@ # 安装指定的包的版本。如果版本尚未发布到注册表,则失败。
-npm install [<@scope>/]@ # 安装与指定版本范围相匹配的包版本。
-```
-
-`npm install [<@scope>/] [-S|--save|-D|--save-dev|-O|--save-optional]` 参数说明:
-
-`npm install` 有 3 个可选参数,用于保存或更新主 `package.json` 中的包版本:
-
-- `-S, --save` - 包将被添加到 `dependencies`。
-- `-D, --save-dev` - 包将被添加到 `devDependencies`。
-- `-O, --save-optional` - 包将被添加到 `optionalDependencies`。
-
-当使用上述任何选项将依赖保存到 `package.json` 时,有两个额外的可选标志 -
-
-- `-E, --save-exact` - 会在 `package.json` 文件指定安装模块的确切版本。
-- `-B, --save-bundle` - 包也将被添加到`bundleDependencies`。
-
-#### 全局安装与本地安装
-
-npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g 而已,比如
-
-```sh
-npm install express # 本地安装
-npm install express -g # 全局安装
-```
-
-##### 本地安装
-
-1. 将安装包放在 `node_modules` 下(运行 npm 命令时所在的目录),如果没有 `node_modules` 目录,会在当前执行 npm 命令的目录下新建 `node_modules` 目录。
-2. 可以通过 require() 来引入本地安装的包。
-
-示例:我们使用 npm 命令安装常用的 Node.js 的 web 框架模块 **express**:
-
-```
-$ npm install express
-```
-
-安装好之后,express 包就放在了工程目录下的 `node_modules` 目录中,因此在代码中只需要通过 `require('express')` 的方式就好,无需指定第三方包路径。
-
-```
-var express = require('express');
-```
-
-##### 全局安装
-
-1. 将安装包放在 /usr/local 下或者你 node 的安装目录。
-2. 可以直接在命令行里使用。
-
-如果你希望具备两者功能,则需要在两个地方安装它或使用 **npm link**。
-
-接下来我们使用全局方式安装 express
-
-```sh
-$ npm install express -g
-```
-
-安装过程输出如下内容,第一行输出了模块的版本号及安装位置。
-
-```sh
-express@4.13.3 node_modules/express
-├── escape-html@1.0.2
-├── range-parser@1.0.2
-├── merge-descriptors@1.0.0
-├── array-flatten@1.1.1
-├── cookie@0.1.3
-├── utils-merge@1.0.0
-├── parseurl@1.3.0
-├── cookie-signature@1.0.6
-├── methods@1.1.1
-├── fresh@0.3.0
-├── vary@1.0.1
-├── path-to-regexp@0.1.7
-├── content-type@1.0.1
-├── etag@1.7.0
-├── serve-static@1.10.0
-├── content-disposition@0.5.0
-├── depd@1.0.1
-├── qs@4.0.0
-├── finalhandler@0.4.0 (unpipe@1.0.0)
-├── on-finished@2.3.0 (ee-first@1.1.1)
-├── proxy-addr@1.0.8 (forwarded@0.1.0, ipaddr.js@1.0.1)
-├── debug@2.2.0 (ms@0.7.1)
-├── type-is@1.6.8 (media-typer@0.3.0, mime-types@2.1.6)
-├── accepts@1.2.12 (negotiator@0.5.3, mime-types@2.1.6)
-└── send@0.13.0 (destroy@1.0.3, statuses@1.2.1, ms@0.7.1, mime@1.3.4, http-errors@1.3.1)
-```
-
-### 卸载模块
-
-> `npm uninstall` 用于卸载包。
-
-**命令格式**
-
-```
-npm uninstall [<@scope>/][@]... [-S|--save|-D|--save-dev|-O|--save-optional]
-
-aliases: remove, rm, r, un, unlink
-```
-
-**说明**
-
-在全局模式下(即,在命令中附加`-g`或`--global`),它将当前包上下文作为全局包卸载。
-
-`npm uninstall` 有 3 个可选参数,用于保存或更新主 package.json 中的包版本:
-
-- `-S, --save` - 包将被添加到 `dependencies`。
-- `-D, --save-dev` - 包将被添加到 `devDependencies`。
-- `-O, --save-optional` - 包将被添加到 `optionalDependencies`。
-
-例:
-
-```sh
-npm uninstall sax
-npm uninstall sax --save
-npm uninstall @myorg/privatepackage --save
-npm uninstall node-tap --save-dev
-npm uninstall dtrace-provider --save-optional
-```
-
-### 更新模块
-
-> `npm update` 用于更新本地安装的模块。
-
-**命令格式**
-
-```sh
-npm update [-g] [...]
-
-aliases: up, upgrade
-```
-
-> 注:从 **npm@2.6.1** 开始,`npm update` 仅更新顶级包。旧版本的 npm 会递归检查所有的依赖。如果要达到旧版本的行为,请使用`npm --depth 9999 update`。
-
-### 管理配置文件
-
-> `npm config` 命令用于管理配置文件。
-
-**命令格式**
-
-```sh
-npm config set [-g|--global] # 设置一个配置参数
-npm config get # 获取一个配置参数
-npm config delete # 删除一个配置参数
-npm config list # 打印配置参数列表
-npm config edit # 直接编辑配置文件
-npm get # npm config get 的简写。
-npm set [-g|--global] # npm config set [-g|--global] 的简写
-```
-
-### 发布包
-
-> `npm publish` 用于发布一个包。
-
-**命令格式**
-
-```sh
-npm publish [|] [--tag ] [--access ]
-
-Publishes '.' if no argument supplied
-Sets tag 'latest' if no --tag specified
-```
-
-> 说明:将包发布到注册表,以便可以按名称安装。如果没有本地 `.gitignore` 或 `.npmignore` 文件,则包括软件包目录中的所有文件。如果这两个过滤文件都存在时,某个文件被 `.gitignore` 忽略,而不被 `.npmignore` 忽略,则它将被包括。
-
-### 执行脚本
-
-> `npm run` 用于执行脚本。
-
-如果在 `package.json` 文件中的 `scripts` 字段定义了命令,就可以使用 `npm run` 来执行脚本命令。
-
-示例:
-
-假设 `package.json` 文件中的 `scripts` 字段如下定义:
-
-```json
-"scripts": {
- "test": "mocha",
- "lint": "eslint lib bin hot scripts",
- "prepublish": "npm run test && npm run lint",
- "start": "node index.js"
-}
-```
-
-- `npm run test` - 相当于执行 `mocha` 命令。它会开始执行测试框架 Mocha 。
-- `npm run lint` - 相当于执行 `eslint lib bin hot scripts` 命令。它会开始执行 eslint 检查。
-- `npm run prepublish` - 相当于执行 `npm run test` 和 `npm run lint` 两条命令。现在你了解如何复合命令了吧。
-- `npm start` - 相当于执行 `node index.js` 。Node.js 启动一个服务的入口脚本。
-
-### 查看安装信息
-
-你可以使用以下命令来查看所有全局安装的模块:
-
-```sh
-$ npm list -g
-
-├─┬ cnpm@4.3.2
-│ ├── auto-correct@1.0.0
-│ ├── bagpipe@0.3.5
-│ ├── colors@1.1.2
-│ ├─┬ commander@2.9.0
-│ │ └── graceful-readlink@1.0.1
-│ ├─┬ cross-spawn@0.2.9
-│ │ └── lru-cache@2.7.3
-……
-```
-
-如果要查看某个模块的版本号,可以使用命令如下:
-
-```sh
-$ npm list grunt
-
-projectName@projectVersion /path/to/project/folder
-└── grunt@0.4.1
-```
-
-## 配置文件
-
-使用 npm 来管理的 javascript 项目一般都有一个`package.json`文件。它定义了这个项目所依赖的各种包,以及项目的配置信息(比如名称、版本、依赖等元数据)。
-
-`package.json` 中的内容就是 json 形式。
-
-重要字段:
-
-- `name` - 包名。
-- `version` - 包的版本号。
-- `description` - 包的描述。
-- `homepage` - 包的官网 url 。
-- `author` - 包的作者姓名。
-- `contributors` - 包的其他贡献者姓名。
-- `dependencies` - 指定项目运行所依赖的模块。
-- `devDependencies` - 指定项目开发所依赖的模块。
-- `repository` - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。
-- `main` - main 字段是一个模块 ID,它是一个指向你程序的主要项目。就是说,如果你包的名字叫 express,然后用户安装它,然后 require("express")。
-- `keywords` - 关键字
-- `bin` - 用来指定各个内部命令对应的可执行文件的位置。
-- `scripts` - 指定了运行脚本命令的 npm 命令行缩写。
-
-**示例:一个完整的 package.json**
-
-```json
-{
- "name": "reactnotes",
- "version": "1.0.0",
- "description": "react 教程",
- "main": "./index.js",
- "dependencies": {
- "react": "^15.4.1",
- "react-dom": "^15.4.1"
- },
- "devDependencies": {
- "webpack-dev-server": "^1.16.2"
- },
- "scripts": {
- "start": "node index.js"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/atlantis1024/ReactNotes.git"
- },
- "author": "victor",
- "license": "Apache-2.0",
- "bugs": {
- "url": "https://github.com/atlantis1024/ReactNotes/issues"
- },
- "homepage": "https://github.com/atlantis1024/ReactNotes#readme"
-}
-```
-
-### package 版本
-
-上文介绍 package.json 文件中的 `dependencies` 和 `devDependencies` 字段,这二者都是 json 数组。它们的每个 json 子对象,key 表示包名,value 表示版本。
-
-npm 允许的版本声明方式十分多样。下面将为你介绍一二。
-
-**说明**
-
-- `version` - 安装一个确定的版本,遵循“大版本.次要版本.小版本”的格式规定。如:1.0.0。
-- `~version` - 以 `~1.0.0` 来举例,表示安装 1.0.x 的最新版本(不低于 1.0.0)。但是大版本号和次要版本号不能变。
-- `^version` - 以 `^1.0.0` 来举例,表示安装 1.x.x 的最新版本(不低于 1.0.0),但是大版本号不能变。
-- `1.2.x` - 表示安装 1.2.x。
-- `>、>=、<、<=` - 可以像数组比较一样,使用比较符来限定版本范围。
-- `version1 - version2` - 相当于 `>=version1 <=version2`.
-- `range1 || range2` - 版本满足 range1 或 range2 两个限定条件中任意一个即可。
-- `tag` - 一个指定 tag 对应的版本。
-- `*` 或 `""` (空字符串):任意版本。
-- `latest` - 最新版本。
-- `http://...` 或 `file://...` - 你可以指定 http 或本地文件路径下的包作为版本。
-- `git...` - 参考下面的“直接将 Git Url 作为依赖包版本”
-- `user/repo` - 参考下面的“直接将 Git Url 作为依赖包版本”
-
-例:下面的版本声明都是有效的
-
-```json
-{
- "dependencies": {
- "foo": "1.0.0 - 2.9999.9999",
- "bar": ">=1.0.2 <2.1.2",
- "baz": ">1.0.2 <=2.3.4",
- "boo": "2.0.1",
- "qux": "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0",
- "asd": "http://asdf.com/asdf.tar.gz",
- "til": "~1.2",
- "elf": "~1.2.3",
- "two": "2.x",
- "thr": "3.3.x",
- "lat": "latest",
- "dyl": "file:../dyl"
- }
-}
-```
-
-**直接将 Git Url 作为依赖包版本**
-
-Git Url 形式可以如下:
-
-```sh
-git://github.com/user/project.git#commit-ish
-git+ssh://user@hostname:project.git#commit-ish
-git+ssh://user@hostname/project.git#commit-ish
-git+http://user@hostname/project/blah.git#commit-ish
-git+https://user@hostname/project/blah.git#commit-ish
-```
-
-### 版本号
-
-使用 Npm 下载和发布代码时都会接触到版本号。Npm 使用语义版本号来管理代码,这里简单介绍一下。
-
-语义版本号分为 X.Y.Z 三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。
-
-- 如果只是修复 bug,需要更新 Z 位。
-- 如果是新增了功能,但是向下兼容,需要更新 Y 位。
-- 如果有大变动,向下不兼容,需要更新 X 位。
-
-版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如"argv": "0.0.x"表示依赖于 0.0.x 系列的最新版 argv。
-
-Npm 支持的所有版本号范围指定方式可以查看[官方文档](https://npmjs.org/doc/files/package.json.html#dependencies)。
-
-## npm 扩展
-
-### cnpm
-
-大家都知道国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用[淘宝 NPM 镜像](https://npm.taobao.org/)。
-
-[淘宝 NPM 镜像](https://npm.taobao.org/)是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10 分钟 一次以保证尽量与官方服务同步。
-
-你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
-
-```
-$ npm install -g cnpm --registry=https://registry.npm.taobao.org
-```
-
-这样就可以使用 cnpm 命令来安装模块了:
-
-```
-$ cnpm install [name]
-```
-
-更多信息可以查阅:[http://npm.taobao.org/](http://npm.taobao.org/)。
-
-### nrm
-
-[Nrm](https://github.com/Pana/nrm) 是 NPM 注册服务器管理工具,可以快速切换不同的注册表:npm,cnpm,nj,taobao,或者是你自己的私服。
-
-#### 安装 nrm
-
-```
-npm install -g nrm
-```
-
-查看可用服务器
-
-```sh
-# 查看可用服务器
-$ nrm ls
-
-* npm ----- https://registry.npmjs.org/
- cnpm ---- http://r.cnpmjs.org/
- taobao -- https://registry.npm.taobao.org/
- nj ------ https://registry.nodejitsu.com/
- rednpm -- http://registry.mirror.cqupt.edu.cn
- skimdb -- https://skimdb.npmjs.com/registry
-
-# 切换服务器
-$ nrm use tabao
-```
-
-#### nrm 命令语义
-
-```sh
-Usage: nrm [options] [command]
-
- Commands:
-
- ls List all the registries
- use Change registry to registry
- add [home] Add one custom registry
- del Delete one custom registry
- home [browser] Open the homepage of registry with optional browser
- test [registry] Show the response time for one or all registries
- help Print this help
-
- Options:
-
- -h, --help output usage information
- -V, --version output the version number
-```
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Node.js](nodejs.md)
-> - [Npm](npm.md)
-> - [Yarn](yarn.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- [Npm 官网](https://www.npmjs.com/)
-- [Npm 中文网](https://www.npmjs.com.cn/)
-- [Npm Github](https://github.com/npm/cli)
-- [淘宝 NPM 镜像](https://npm.taobao.org/) - 代替官方版本,加速下载
-- [awesome-npm](https://github.com/sindresorhus/awesome-npm) - npm 资源
-- [sinopia](https://github.com/rlidwka/sinopia) - 零配置搭建 npm 私服
-- [nrm](https://github.com/Pana/nrm) - npm 服务器地址管理工具
-- [NPM 使用介绍](http://www.runoob.com/nodejs/nodejs-npm.html)
diff --git a/frontend/nodejs/yarn.md b/frontend/nodejs/yarn.md
deleted file mode 100644
index 56fec22e93..0000000000
--- a/frontend/nodejs/yarn.md
+++ /dev/null
@@ -1,231 +0,0 @@
-# Yarn 入门
-
-> Yarn 是快速、可靠、安全的 js 包管理器。
->
-> 关键词: `nodejs`, `包管理`, `yarn`, `yarn.lock`
-
-
-
-- [简介](#简介)
-- [安装](#安装)
-- [Yarn 工作流](#yarn-工作流)
-- [Yarn 常用命令](#yarn-常用命令)
- - [初始化新项目](#初始化新项目)
- - [添加依赖包](#添加依赖包)
- - [更新依赖包](#更新依赖包)
- - [删除依赖包](#删除依赖包)
- - [安装依赖项](#安装依赖项)
-- [创建一个新项目](#创建一个新项目)
-- [配置文件](#配置文件)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-**Yarn 是快速、可靠、安全的 js 包管理器。**
-
-- **快速** - Yarn 会缓存它下载的每个包,所以无需重复下载。它还能并行化操作以最大化资源利用率,安装速度之快前所未有。
-- **安全** - Yarn 会在每个安装包被执行前校验其完整性。
-- **可靠** - Yarn 使用格式详尽而又简洁的 lockfile 文件和确定性算法来安装依赖,能够保证在一个系统上的运行的安装过程也会以同样的方式运行在其他系统上。
-
-## 安装
-
-先决条件:已安装 Nodejs。
-
-执行命令:`npm i -g yarn`
-
-虽然还有其他安装方式,但并不推荐。
-
-## Yarn 工作流
-
-Yarn 工作流:
-
-1. 创建一个新项目
-2. 增加/更新/删除依赖
-3. 安装/重装你的依赖
-4. 引入版本控制系统(例如 git)
-5. 持续集成
-
-## Yarn 常用命令
-
-**每个命令都会更新 `package.json` 和 `yarn.lock` 文件。**
-
-### 初始化新项目
-
-```sh
-yarn init
-```
-
-### 添加依赖包
-
-在使用一个包之前,你需要执行以下命令将其加入依赖项列表:
-
-```sh
-yarn add [package]
-```
-
-`[package]`会被加入到`package.json`文件中的依赖列表,同时`yarn.lock`也会被更新。
-
-```diff
- {
- "name": "my-package",
- "dependencies": {
-+ "package-1": "^1.0.0"
- }
- }
-```
-
-你可以用以下参数添加其它[类型](https://yarnpkg.com/zh-Hans/docs/dependency-types)的依赖:
-
-- `yarn add --dev` 添加到 `devDependencies`
-- `yarn add --peer` 添加到 `peerDependencies`
-- `yarn add --optional` 添加到 `optionalDependencies`
-
-通过指定[依赖版本](https://yarnpkg.com/zh-Hans/docs/dependency-versions)和[标签](https://yarnpkg.com/zh-Hans/docs/cli/tag),你可以安装一个特定版本的包:
-
-```sh
-yarn add [package]@[version]
-yarn add [package]@[tag]
-```
-
-`[version]` 或 `[tag]` 会被添加到 `package.json`,并在安装依赖时被解析。
-
-例如:
-
-```sh
-yarn add package-1@1.2.3
-yarn add package-2@^1.0.0
-yarn add package-3@beta
-{
- "dependencies": {
- "package-1": "1.2.3",
- "package-2": "^1.0.0",
- "package-3": "beta"
- }
-}
-```
-
-**将依赖项添加到不同依赖项类别**
-
-分别添加到 `devDependencies`、`peerDependencies` 和 `optionalDependencies`:
-
-```sh
-yarn add [package] --dev
-yarn add [package] --peer
-yarn add [package] --optional
-```
-
-### 更新依赖包
-
-```sh
-yarn upgrade [package]
-yarn upgrade [package]@[version]
-yarn upgrade [package]@[tag]
-```
-
-这会更新`package.json`和`yarn.lock` 文件。
-
-```diff
- {
- "name": "my-package",
- "dependencies": {
-- "package-1": "^1.0.0"
-+ "package-1": "^2.0.0"
- }
- }
-```
-
-### 删除依赖包
-
-```
-yarn remove [package]
-```
-
-这会更新`package.json`和`yarn.lock` 文件。
-
-### 安装依赖项
-
-`yarn install` 是用于安装一个项目的所有依赖。 Yarn 会从 `package.json` 中读取依赖,并将依赖信息存储到 `yarn.lock` 中。
-
-如果你正在开发一个包,通常你会在以下情况之后进行依赖安装:
-
-- 你刚检出需要这些依赖项的项目代码。
-- 项目的另一个开发者添加了新的依赖,你需要用到。
-
-有很多参数可以控制依赖安装的过程,包括:
-
-- 安装所有依赖 - yarn 或 yarn install
-- 安装一个包的单一版本 - yarn install --flat
-- 强制重新下载所有包 - yarn install --force
-- 只安装生产环境依赖 - yarn install --production
-
-> 参考:`yarn install` 的 [完整参数列表](https://yarnpkg.com/zh-Hans/docs/cli/install)。
-
-## 创建一个新项目
-
-不论是已经有了现成的代码仓库(目录),还是正着手启动一个全新项目,你都可以使用同样的方法引入 Yarn。
-
-在命令行终端里,跳转到准备引入 Yarn 的目录(通常是一个项目的根目录),执行以下命令:
-
-```sh
-yarn init
-```
-
-这将打开一个用于创建 Yarn 项目的交互式表单,其中包含以下问题:
-
-```
-name (your-project):
-version (1.0.0):
-description:
-entry point (index.js):
-git repository:
-author:
-license (MIT):
-```
-
-你既可以回答这些问题,也可以直接敲回车键(enter/return)使用默认配置或者留空。
-
-## 配置文件
-
-为了别人能使用你的包,以下文件必须被提交进版本控制系统:
-
-- `package.json` - 包含包的所有依赖信息;
-- `yarn.lock` - 记录每一个依赖项的确切版本信息;
-- 包实现功能的实际项目代码。
-
-> 请参阅[Yarn Example Package](https://github.com/yarnpkg/example-yarn-package)项目,查看一个可用的 Yarn 包所需的最少文件配置。
-
-现在应该创建了一个和下面文件内容类似的 `package.json`:
-
-```json
-{
- "name": "my-new-project",
- "version": "1.0.0",
- "description": "My New Project description.",
- "main": "index.js",
- "repository": {
- "url": "https://example.com/your-username/my-new-project",
- "type": "git"
- },
- "author": "Your Name ",
- "license": "MIT"
-}
-```
-
-执行`yarn init`之后,除了以上文件被创建之外,没有任何副作用。你可以随意编辑此文件。
-
-`package.json`文件里存储了项目的有关信息。 包括项目名称、维护者信息、代码托管地址,以及最重要的:项目依赖。
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Node.js](nodejs.md)
-> - [Npm](npm.md)
-> - [Yarn](yarn.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- [Yarn Github](https://github.com/yarnpkg/yarn)
-- [Yarn 官方文档](https://yarnpkg.com/zh-Hans/)
diff --git a/frontend/qa/eslint.md b/frontend/qa/eslint.md
deleted file mode 100644
index cb1407e1c4..0000000000
--- a/frontend/qa/eslint.md
+++ /dev/null
@@ -1,1593 +0,0 @@
-# ESLint
-
-ESLint 是一种用于识别和报告 ECMAScript / JavaScript 代码中的模式的工具。在许多方面,它类似于 JSLint 和 JSHint,但有一些例外:
-
-- ESLint 使用 Espree 进行 JavaScript 解析。
-- ESLint 使用 AST 来评估代码中的模式。
-- ESLint 是完全可插拔的,每个规则都是一个插件,您可以在运行时添加更多。
-
-
-
-- [安装](#安装)
-- [ESLint 配置](#eslint-配置)
- - [指定解析器选项](#指定解析器选项)
- - [指定解析器](#指定解析器)
- - [指定环境](#指定环境)
- - [指定全局变量](#指定全局变量)
- - [配置插件](#配置插件)
- - [配置规则](#配置规则)
- - [使用行注释禁用规则](#使用行注释禁用规则)
- - [添加分享配置](#添加分享配置)
- - [使用配置文件](#使用配置文件)
- - [配置文件文件格式](#配置文件文件格式)
- - [配置的层级和继承](#配置的层级和继承)
- - [扩展配置文件](#扩展配置文件)
- - [基于 glob 模式的配置](#基于-glob-模式的配置)
- - [在配置文件中注释](#在配置文件中注释)
- - [指定需要检查的文件扩展名](#指定需要检查的文件扩展名)
- - [忽略文件和目录](#忽略文件和目录)
-- [实战](#实战)
- - [ESLint 集成 Airbnb](#eslint-集成-airbnb)
-- [ESLint 命令](#eslint-命令)
- - [选项](#选项)
- - [.eslintignore 文件](#eslintignore-文件)
-- [引申和引用](#引申和引用)
-
-
-
-## 安装
-
-(1)本地安装
-
-如果要将 ESLint 作为项目构建系统的一部分,我们建议在本地进行安装。你可以使用 npm:
-
-```sh
-$ npm install eslint --save-dev
-```
-
-然后您应该设置一个配置文件:
-
-```sh
-$ ./node_modules/.bin/eslint --init
-```
-
-之后,您可以在项目的根目录中运行 ESLint,如下所示:
-
-```sh
-$ ./node_modules/.bin/eslint yourfile.js
-```
-
-您使用的任何插件或可共享配置也必须在本地安装以与本地安装的 ESLint 配合使用。
-
-(2)全局安装
-
-如果要使 ESLint 可用于跨所有项目运行的工具,我们建议在全局安装 ESLint。你可以使用 npm:
-
-```sh
-$ npm install -g eslint
-```
-
-然后您应该设置一个配置文件:
-
-```sh
-$ eslint --init
-```
-
-之后,您可以在任何文件或目录下运行 ESLint,如下所示:
-
-```sh
-$ eslint yourfile.js
-```
-
-您使用的任何插件或可共享配置也必须全局安装,才能使用全局安装的 ESLint。
-
-> 注意:eslint --init 旨在根据每个项目设置和配置 ESLint,并将 ESLint 及其插件的本地安装运行在其运行的目录中。如果您希望使用 ESLint 的全局安装,则配置中使用的任何插件也必须全局安装
-
-## ESLint 配置
-
-运行 `eslint --init` 后,您的目录中将有一个 `.eslintrc` 文件。在其中,您将看到一些如下配置的规则:
-
-```json
-{
- "rules": {
- "semi": ["error", "always"],
- "quotes": ["error", "double"]
- }
-}
-```
-
-`semi` 和 `quotes` 是 ESLint 中规则的名称。第一个值是规则的错误级别,可以是以下值之一:
-
-- `off` or `0` - 关闭规则
-- `warn` or `1` - 将规则作为警告(不影响退出代码)
-- `error` or `2` - 将规则作为错误(退出代码将为 1)
-
-这三个错误级别允许您细分控制 ESLint 如何应用规则(有关更多配置选项和详细信息,请参阅[配置文档](https://eslint.org/docs/user-guide/configuring))。
-
-您的 `.eslintrc` 配置文件也将包括以下行:
-
-```json
-"extends": "eslint:recommended"
-```
-
-由于这一行,[规则页面](https://eslint.org/docs/rules/)上的所有打钩的规则都将被打开。或者,您可以通过在 [npmjs.com](https://www.npmjs.com/search?q=eslint-config) 上搜索 “eslint-config” 来使用其他人创建的配置。 ESLint 不会删除您的代码,除非您从共享配置中扩展或在配置中明确地打开规则。
-
-ESlint 被设计为完全可配置的,这意味着你可以关闭每一个规则而只运行基本语法验证,或混合和匹配 ESLint 默认绑定的规则和你的自定义规则,以让 ESLint 更适合你的项目。有两种主要的方式来配置 ESLint:
-
-1. **Configuration Comments** - 使用 JavaScript 注释把配置信息直接嵌入到一个代码源文件中。
-2. **Configuration Files** - 使用 JavaScript、JSON 或者 YAML 文件为整个目录和它的子目录指定配置信息。可以配置一个独立的 [.eslintrc.\*](https://cn.eslint.org/docs/user-guide/configuring#configuration-file-formats) 文件,或者直接在 [`package.json`](https://docs.npmjs.com/files/package.json) 文件里的 `eslintConfig` 字段指定配置,ESLint 会查找和自动读取它们,再者,你可以在[命令行](https://cn.eslint.org/docs/user-guide/command-line-interface)运行时指定一个任意的配置文件。
-
-有很多信息可以配置:
-
-- **Environments** - 指定脚本的运行环境。每种环境都会有一组特定的预定义全局变量。
-- **Globals** - 脚本在执行期间访问的额外的全局变量。
-- **Rules** - 启用的规则及其各自的错误级别。
-
-所有这些选项让你可以细粒度地控制 ESLint 如何对待你的代码。
-
-### 指定解析器选项
-
-ESLint 允许你指定你想要支持的 JavaScript 语言选项。默认情况下,ESLint 支持 ECMAScript 5 语法。你可以覆盖该设置,以启用对 ECMAScript 其它版本和 JSX 的支持。
-
-请注意,对 JSX 语法的支持不用于对 React 的支持。React 使用了一些特定的 ESLint 无法识别的 JSX 语法。如果你正在使用 React 并且想要 React 语义支持,我们推荐你使用 [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react)。
-
-同样的,支持 ES6 语法并不意味着同时支持新的 ES6 全局变量或类型(比如 `Set` 等新类型)。使用 `{ "parserOptions": { "ecmaVersion": 6 } }` 来启用 ES6 语法支持;要额外支持新的 ES6 全局变量,使用 `{ "env":{ "es6": true } }`(这个设置会同时自动启用 ES6 语法支持)。
-
-解析器选项可以在 `.eslintrc.*` 文件使用 `parserOptions` 属性设置。可用的选项有:
-
-- `ecmaVersion` - 默认设置为 5, 你可以使用 3、5、6、7 或 8 来指定你想要使用的 ECMAScript 版本。你也可以用使用年份命名的版本号指定为 2015(同 6),2016(同 7),或 2017(同 8)
-- `sourceType` - 设置为 `script` (默认) 或 `module`(如果你的代码是 ECMAScript 模块)。
-- `ecmaFeatures` - 这是个对象,表示你想使用的额外的语言特性:`globalReturn` - 允许在全局作用域下使用 `return` 语句`impliedStrict` - 启用全局 [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) (如果 `ecmaVersion` 是 5 或更高)`jsx` - 启用 [JSX](http://facebook.github.io/jsx/)`experimentalObjectRestSpread` - 启用实验性的 [object rest/spread properties](https://github.com/sebmarkbage/ecmascript-rest-spread) 支持。(**重要:**这是一个实验性的功能,在未来可能会有明显改变。 建议你写的规则 **不要** 依赖该功能,除非当它发生改变时你愿意承担维护成本。)
-
-`.eslintrc.json` 文件示例:
-
-```
-{
- "parserOptions": {
- "ecmaVersion": 6,
- "sourceType": "module",
- "ecmaFeatures": {
- "jsx": true
- }
- },
- "rules": {
- "semi": 2
- }
-}
-
-```
-
-设置解析器选项能帮助 ESLint 确定什么是解析错误,所有语言选项默认都是 `false`。
-
-### 指定解析器
-
-ESLint 默认使用[Espree](https://github.com/eslint/espree)作为其解析器,你可以在配置文件中指定一个不同的解析器,只要该解析器符合下列要求:
-
-1. 它必须是本地安装的一个 npm 模块。
-2. 它必须有兼容 Esprima 的接口(它必须输出一个 `parse()` 方法)
-3. 它必须产出兼容 Esprima 的 AST 和 token 对象。
-
-注意,即使满足这些兼容性要求,也不能保证一个外部解析器可以与 ESLint 正常配合工作,ESLint 也不会修复与其它解析器不兼容的相关 bug。
-
-为了表明使用该 npm 模块作为你的解析器,你需要在你的 `.eslintrc` 文件里指定 `parser` 选项。例如,下面的配置指定了 Esprima 作为解析器:
-
-```
-{
- "parser": "esprima",
- "rules": {
- "semi": "error"
- }
-}
-
-```
-
-以下解析器与 ESLint 兼容:
-
-- [Esprima](https://npmjs.com/package/esprima)
-- [Babel-ESLint](https://npmjs.com/package/babel-eslint) - 一个对[Babel](http://babeljs.io/)解析器的包装,使其能够与 ESLint 兼容。
-- [typescript-eslint-parser(实验)](https://npmjs.com/package/typescript-eslint-parser) - 一个把 TypeScript 转换为 ESTree 兼容格式的解析器,这样它就可以在 ESLint 中使用了。这样做的目的是通过 ESLint 来解析 TypeScript 文件(尽管不一定必须通过所有的 ESLint 规则)。
-
-注意,在使用自定义解析器时,为了让 ESLint 在处理非 ECMAScript 5 特性时正常工作,配置属性 `parserOptions` 仍然是必须的。解析器会被传入 `parserOptions`,但是不一定会使用它们来决定功能特性的开关。
-
-### 指定环境
-
-一个“环境”定义了一组预定义的全局变量。可用的环境包括:
-
-- `browser` - 浏览器环境中的全局变量。
-- `node` - Node.js 全局变量和 Node.js 作用域。
-- `commonjs` - CommonJS 全局变量和 CommonJS 作用域 (一般用于 Browserify/WebPack 打包的只在浏览器中运行的代码)。
-- `shared-node-browser` - Node 和 Browser 通用全局变量。
-- `es6` - 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 `ecmaVersion` 解析器选项为 6)。
-- `worker` - Web Workers 全局变量。
-- `amd` - 将 `require()` 和 `define()` 定义为像 [amd](https://github.com/amdjs/amdjs-api/wiki/AMD) 一样的全局变量。
-- `mocha` - 添加所有的 Mocha 测试全局变量。
-- `jasmine` - 添加所有的 Jasmine 版本 1.3 和 2.0 的测试全局变量。
-- `jest` - Jest 全局变量。
-- `phantomjs` - PhantomJS 全局变量。
-- `protractor` - Protractor 全局变量。
-- `qunit` - QUnit 全局变量。
-- `jquery` - jQuery 全局变量。
-- `prototypejs` - Prototype.js 全局变量。
-- `shelljs` - ShellJS 全局变量。
-- `meteor` - Meteor 全局变量。
-- `mongo` - MongoDB 全局变量。
-- `applescript` - AppleScript 全局变量。
-- `nashorn` - Java 8 Nashorn 全局变量。
-- `serviceworker` - Service Worker 全局变量。
-- `atomtest` - Atom 测试全局变量。
-- `embertest` - Ember 测试全局变量。
-- `webextensions` - WebExtensions 全局变量。
-- `greasemonkey` - GreaseMonkey 全局变量。
-
-这些环境并不是互斥的,所以你可以同时定义多个。
-
-可以在源文件里、在配置文件中或使用 [命令行](https://cn.eslint.org/docs/user-guide/command-line-interface) 的 `--env` 选项来指定环境。
-
-要在你的 JavaScript 文件中使用注释来指定环境,格式如下:
-
-```
-/* eslint-env node, mocha */
-
-```
-
-该设置启用了 Node.js 和 Mocha 环境。
-
-要在配置文件里指定环境,使用 `env` 关键字指定你想启用的环境,并设置它们为 `true`。例如,以下示例启用了 browser 和 Node.js 的环境:
-
-```
-{
- "env": {
- "browser": true,
- "node": true
- }
-}
-
-```
-
-或在 `package.json` 文件中:
-
-```
-{
- "name": "mypackage",
- "version": "0.0.1",
- "eslintConfig": {
- "env": {
- "browser": true,
- "node": true
- }
- }
-}
-
-```
-
-在 YAML 文件中:
-
-```
----
- env:
- browser: true
- node: true
-
-```
-
-如果你想在一个特定的插件中使用一种环境,确保提前在 `plugins` 数组里指定了插件名,然后在 env 配置中不带前缀的插件名后跟一个 `/` ,紧随着环境名。例如:
-
-```
-{
- "plugins": ["example"],
- "env": {
- "example/custom": true
- }
-}
-
-```
-
-或在 `package.json` 文件中
-
-```
-{
- "name": "mypackage",
- "version": "0.0.1",
- "eslintConfig": {
- "plugins": ["example"],
- "env": {
- "example/custom": true
- }
- }
-}
-
-```
-
-在 YAML 文件中:
-
-```
----
- plugins:
- - example
- env:
- example/custom: true
-
-```
-
-### 指定全局变量
-
-当访问当前源文件内未定义的变量时,[no-undef](https://cn.eslint.org/docs/rules/no-undef) 规则将发出警告。如果你想在一个源文件里使用全局变量,推荐你在 ESLint 中定义这些全局变量,这样 ESLint 就不会发出警告了。你可以使用注释或在配置文件中定义全局变量。
-
-要在你的 JavaScript 文件中,用注释指定全局变量,格式如下:
-
-```
-/* global var1, var2 */
-
-```
-
-这里定义了两个全局变量:`var1` 和 `var2`。如果你想指定这些变量不应被重写(只读),你可以将它们设置为 `false`:
-
-```
-/* global var1:false, var2:false */
-
-```
-
-在配置文件里配置全局变量时,使用 `globals` 指出你要使用的全局变量。将变量设置为 `true` 将允许变量被重写,或 `false` 将不允许被重写。比如:
-
-```
-{
- "globals": {
- "var1": true,
- "var2": false
- }
-}
-
-```
-
-在 YAML 中:
-
-```
----
- globals:
- var1: true
- var2: false
-
-```
-
-在这些例子中 `var1` 允许被重写,`var2` 不允许被重写。
-
-**注意:** 要启用[no-global-assign](https://cn.eslint.org/docs/rules/no-global-assign)规则来禁止对只读的全局变量进行修改。
-
-### 配置插件
-
-ESLint 支持使用第三方插件。在使用插件之前,你必须使用 npm 安装它。
-
-在配置文件里配置插件时,可以使用 `plugins` 关键字来存放插件名字的列表。插件名称可以省略 `eslint-plugin-` 前缀。
-
-```
-{
- "plugins": [
- "plugin1",
- "eslint-plugin-plugin2"
- ]
-}
-
-```
-
-在 YAML 中:
-
-```
----
- plugins:
- - plugin1
- - eslint-plugin-plugin2
-
-```
-
-**注意:**全局安装的 ESLint 只能使用全局安装的插件。本地安装的 ESLint 不仅可以使用本地安装的插件,也可以使用全局安装的插件。
-
-### 配置规则
-
-ESLint 附带有大量的规则。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,你必须将规则 ID 设置为下列值之一:
-
-- `off` 或 `0` - 关闭规则
-- `warn` 或 `1` - 开启规则,使用警告级别的错误:`warn` (不会导致程序退出)
-- `error` 或 `2` - 开启规则,使用错误级别的错误:`error` (当被触发的时候,程序会退出)
-
-为了在文件注释里配置规则,使用以下格式的注释:
-
-```
-/* eslint eqeqeq: "off", curly: "error" */
-
-```
-
-在这个例子里,[`eqeqeq`](https://cn.eslint.org/docs/rules/eqeqeq) 规则被关闭,[`curly`](https://cn.eslint.org/docs/rules/curly) 规则被打开,定义为错误级别。你也可以使用对应的数字定义规则严重程度:
-
-```
-/* eslint eqeqeq: 0, curly: 2 */
-
-```
-
-这个例子和上个例子是一样的,只不过它是用的数字而不是字符串。`eqeqeq` 规则是关闭的,`curly` 规则被设置为错误级别。
-
-如果一个规则有额外的选项,你可以使用数组字面量指定它们,比如:
-
-```
-/* eslint quotes: ["error", "double"], curly: 2 */
-
-```
-
-这条注释为规则 [`quotes`](https://cn.eslint.org/docs/rules/quotes) 指定了 “double”选项。数组的第一项总是规则的严重程度(数字或字符串)。
-
-还可以使用 `rules` 连同错误级别和任何你想使用的选项,在配置文件中进行规则配置。例如:
-
-```
-{
- "rules": {
- "eqeqeq": "off",
- "curly": "error",
- "quotes": ["error", "double"]
- }
-}
-
-```
-
-在 YAML 中:
-
-```
----
-rules:
- eqeqeq: off
- curly: error
- quotes:
- - error
- - double
-
-```
-
-配置定义在插件中的一个规则的时候,你必须使用 `插件名/规则ID` 的形式。比如:
-
-```
-{
- "plugins": [
- "plugin1"
- ],
- "rules": {
- "eqeqeq": "off",
- "curly": "error",
- "quotes": ["error", "double"],
- "plugin1/rule1": "error"
- }
-}
-
-```
-
-在 YAML 中:
-
-```
----
-plugins:
- - plugin1
-rules:
- eqeqeq: 0
- curly: error
- quotes:
- - error
- - "double"
- plugin1/rule1: error
-
-```
-
-在这些配置文件中,规则 `plugin1/rule1` 表示来自插件 `plugin1` 的 `rule1` 规则。你也可以使用这种格式的注释配置,比如:
-
-```
-/* eslint "plugin1/rule1": "error" */
-
-```
-
-**注意:**当指定来自插件的规则时,确保删除 `eslint-plugin-` 前缀。ESLint 在内部只使用没有前缀的名称去定位规则。
-
-### 使用行注释禁用规则
-
-可以在你的文件中使用以下格式的块注释来临时禁止规则出现警告:
-
-```
-/* eslint-disable */
-
-alert('foo');
-
-/* eslint-enable */
-
-```
-
-你也可以对指定的规则启用或禁用警告:
-
-```
-/* eslint-disable no-alert, no-console */
-
-alert('foo');
-console.log('bar');
-
-/* eslint-enable no-alert, no-console */
-
-
-```
-
-如果在整个文件范围内禁止规则出现警告,将 `/* eslint-disable */` 块注释放在文件顶部:
-
-```
-/* eslint-disable */
-
-alert('foo');
-
-```
-
-你也可以对整个文件启用或禁用警告:
-
-```
-/* eslint-disable no-alert */
-
-// Disables no-alert for the rest of the file
-alert('foo');
-
-```
-
-可以在你的文件中使用以下格式的行注释在某一特定的行上禁用所有规则:
-
-```
-alert('foo'); // eslint-disable-line
-
-// eslint-disable-next-line
-alert('foo');
-
-```
-
-在某一特定的行上禁用某个指定的规则:
-
-```
-alert('foo'); // eslint-disable-line no-alert
-
-// eslint-disable-next-line no-alert
-alert('foo');
-
-```
-
-在某个特定的行上禁用多个规则:
-
-```
-alert('foo'); // eslint-disable-line no-alert, quotes, semi
-
-// eslint-disable-next-line no-alert, quotes, semi
-alert('foo');
-
-```
-
-上面的所有方法同样适用于插件规则。例如,禁止 `eslint-plugin-example` 的 `rule-name` 规则,把插件名(`example`)和规则名(`rule-name`)结合为 `example/rule-name`:
-
-```
-foo(); // eslint-disable-line example/rule-name
-
-```
-
-**注意:**为文件的某部分禁用警告的注释,告诉 ESLint 不要对禁用的代码报告规则的冲突。ESLint 仍解析整个文件,然而,禁用的代码仍需要是有效的 JavaScript 语法。
-
-### 添加分享配置
-
-ESLint 支持在配置文件添加共享设置。你可以添加 `settings` 对象到配置文件,它将提供给每一个将被执行的规则。如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置。
-
-在 JSON 中:
-
-```
-{
- "settings": {
- "sharedData": "Hello"
- }
-}
-
-```
-
-在 YAML 中:
-
-```
----
- settings:
- sharedData: "Hello"
-
-```
-
-### 使用配置文件
-
-有两种方式可以使用配置文件。第一种是将文件保存到你喜欢的地方,然后将它的位置使用 `-c` 选项传递命令行,比如:
-
-```
-eslint -c myconfig.json myfiletotest.js
-
-```
-
-第二种方式是通过 `.eslintrc.*` 和 `package.json`。ESLint 将自动在要检测的文件目录里寻找它们,紧接着是父级目录,一直到文件系统的根目录。当你想对一个项目的不同部分的使用不同配置,或当你希望别人能够直接使用 ESLint,而无需记住要在配置文件中传递什么,这种方式就很有用。
-
-每种情况,配置文件都会覆盖默认设置。
-
-### 配置文件文件格式
-
-ESLint 支持几种格式的配置文件:
-
-- **JavaScript** - 使用 `.eslintrc.js` 然后输出一个配置对象。
-- **YAML** - 使用 `.eslintrc.yaml` 或 `.eslintrc.yml` 去定义配置的结构。
-- **JSON** - 使用 `.eslintrc.json` 去定义配置的结构,ESLint 的 JSON 文件允许 JavaScript 风格的注释。
-- **(不推荐)** - 使用 `.eslintrc`,可以使 JSON 也可以是 YAML。
-- **package.json** - 在 `package.json` 里创建一个 `eslintConfig`属性,在那里定义你的配置。
-
-如果同一个目录下有多个配置文件,ESLint 只会使用一个。优先级顺序如下:
-
-1. `.eslintrc.js`
-2. `.eslintrc.yaml`
-3. `.eslintrc.yml`
-4. `.eslintrc.json`
-5. `.eslintrc`
-6. `package.json`
-
-### 配置的层级和继承
-
-当使用 `.eslintrc.*` 和 `package.json`文件的配置时,你可以利用层叠配置。例如,假如你有以下结构:
-
-```
-your-project
-├── .eslintrc
-├── lib
-│ └── source.js
-└─┬ tests
- ├── .eslintrc
- └── test.js
-
-```
-
-层叠配置使用离要检测的文件最近的 `.eslintrc`文件作为最高优先级,然后才是父目录里的配置文件,等等。当你在这个项目中允许 ESLint 时,`lib/`下面的所有文件将使用项目根目录里的 `.eslintrc` 文件作为它的配置文件。当 ESLint 遍历到 `test/` 目录,`your-project/.eslintrc` 之外,它还会用到 `your-project/tests/.eslintrc`。所以 `your-project/tests/test.js` 是基于它的目录层次结构中的两个`.eslintrc` 文件的组合,并且离的最近的一个优先。通过这种方式,你可以有项目级 ESLint 设置,也有覆盖特定目录的 ESLint 设置。
-
-同样的,如果在根目录的 `package.json` 文件中有一个 `eslintConfig` 字段,其中的配置将使用于所有子目录,但是当 `tests` 目录下的 `.eslintrc` 文件中的规则与之发生冲突时,就会覆盖它。
-
-```
-your-project
-├── package.json
-├── lib
-│ └── source.js
-└─┬ tests
- ├── .eslintrc
- └── test.js
-
-```
-
-如果同一目录下 `.eslintrc` 和 `package.json` 同时存在,`.eslintrc` 优先级高会被使用,`package.json` 文件将不会被使用。
-
-**注意:**如果在你的主目录下有一个自定义的配置文件 (`~/.eslintrc`) ,如果没有其它配置文件时它才会被使用。因为个人配置将适用于用户目录下的所有目录和文件,包括第三方的代码,当 ESLint 运行时可能会导致问题。
-
-默认情况下,ESLint 会在所有父级目录里寻找配置文件,一直到根目录。如果你想要你所有项目都遵循一个特定的约定时,这将会很有用,但有时候会导致意想不到的结果。为了将 ESLint 限制到一个特定的项目,在你项目根目录下的 `package.json` 文件或者 `.eslintrc.*` 文件里的 `eslintConfig` 字段下设置 `root": true`。ESLint 一旦发现配置文件中有 `root": true`,它就会停止在父级目录中寻找。
-
-```
-{
- "root": true
-}
-
-```
-
-在 YAML 中:
-
-```
----
- root: true
-
-```
-
-例如,`projectA` 的 `lib/` 目录下的 `.eslintrc` 文件中设置了 `root": true`。这种情况下,当检测 `main.js` 时,`lib/` 下的配置将会被使用,`projectA/` 下的 `.eslintrc` 将不会被使用。
-
-```
-home
-└── user
- ├── .eslintrc <- Always skipped if other configs present
- └── projectA
- ├── .eslintrc <- Not used
- └── lib
- ├── .eslintrc <- { "root": true }
- └── main.js
-
-```
-
-完整的配置层次结构,从最高优先级最低的优先级,如下:
-
-1. 行内配置
- 1. `/*eslint-disable*/` 和 `/*eslint-enable*/`
- 2. `/*global*/`
- 3. `/*eslint*/`
- 4. `/*eslint-env*/`
-2. 命令行选项:
- 1. `--global`
- 2. `--rule`
- 3. `--env`
- 4. `-c`、`--config`
-3. 项目级配置:
- 1. 与要检测的文件在同一目录下的 `.eslintrc.*` 或 `package.json` 文件
- 2. 继续在父级目录寻找 `.eslintrc` 或 `package.json`文件,直到根目录(包括根目录)或直到发现一个有`root": true`的配置。
- 3. 如果不是(1)到(3)中的任何一种情况,退回到 `~/.eslintrc` 中自定义的默认配置。
-
-### 扩展配置文件
-
-一个配置文件可以被基础配置中的已启用的规则继承。
-
-`extends` 属性值可以是:
-
-- 在配置中指定的一个字符串
-- 字符串数组:每个配置继承它前面的配置
-
-ESLint 递归地进行扩展配置,所以一个基础的配置也可以有一个 `extends` 属性。
-
-`rules` 属性可以做下面的任何事情以扩展(或覆盖)规则:
-
-- 启用额外的规则
-- 改变继承的规则级别而不改变它的选项:
- - 基础配置:`eqeqeq": ["error", "allow-null"]`
- - 派生的配置:`eqeqeq": "warn`
- - 最后生成的配置:`eqeqeq": ["warn", "allow-null"]`
-- 覆盖基础配置中的规则的选项
- - 基础配置:`quotes": ["error", "single", "avoid-escape"]`
- - 派生的配置:`quotes": ["error", "single"]`
- - 最后生成的配置:`quotes": ["error", "single"]`
-
-#### 使用 eslint:recommended
-
-值为 `eslint:recommended` 的 `extends` 属性启用一系列核心规则,这些规则报告一些常见问题,在 [规则页面](https://cn.eslint.org/docs/rules/) 中被标记为 。这个推荐的子集只能在 ESLint 主要版本进行更新。
-
-如果你的配置集成了推荐的规则:在你升级到 ESLint 新的主版本之后,在你使用[命令行](https://cn.eslint.org/docs/user-guide/command-line-interface#fix)的 `--fix` 选项之前,检查一下报告的问题,这样你就知道一个新的可修复的推荐的规则将更改代码。
-
-`eslint --init` 命令可以创建一个配置,这样你就可以继承推荐的规则。
-
-JavaScript 格式的一个配置文件的例子:
-
-```
-module.exports = {
- "extends": "eslint:recommended",
- "rules": {
- // enable additional rules
- "indent": ["error", 4],
- "linebreak-style": ["error", "unix"],
- "quotes": ["error", "double"],
- "semi": ["error", "always"],
-
- // override default options for rules from base configurations
- "comma-dangle": ["error", "always"],
- "no-cond-assign": ["error", "always"],
-
- // disable rules from base configurations
- "no-console": "off",
- }
-}
-```
-
-#### 使用可共享的配置包
-
-[可共享的配置](https://cn.eslint.org/docs/developer-guide/shareable-configs) 是一个 npm 包,它输出一个配置对象。要确保这个包安装在 ESLint 能请求到的目录下。
-
-`extends` 属性值可以省略包名的前缀 `eslint-config-`。
-
-`eslint --init` 命令可以创建一个配置,这样你就可以扩展一个流行的风格指南(比如,`eslint-config-standard`)。
-
-YAML 格式的一个配置文件的例子:
-
-```
-extends: standard
-rules:
- comma-dangle:
- - error
- - always
- no-empty: warn
-
-```
-
-#### 使用插件中的配置
-
-[插件](https://cn.eslint.org/docs/developer-guide/working-with-plugins) 是一个 npm 包,通常输出规则。一些插件也可以输出一个或多个命名的 [配置](https://cn.eslint.org/docs/developer-guide/working-with-plugins#configs-in-plugins)。要确保这个包安装在 ESLint 能请求到的目录下。
-
-`plugins` [属性值](https://cn.eslint.org/docs/user-guide/configuring#configuring-plugins) 可以省略包名的前缀 `eslint-plugin-`。
-
-`extends` 属性值可以由以下组成:
-
-- `plugin:`
-- 包名 (省略了前缀,比如,`react`)
-- `/`
-- 配置名称 (比如 `recommended`)
-
-JSON 格式的一个配置文件的例子:
-
-```
-{
- "plugins": [
- "react"
- ],
- "extends": [
- "eslint:recommended",
- "plugin:react/recommended"
- ],
- "rules": {
- "no-set-state": "off"
- }
-}
-
-```
-
-#### 使用一个配置文件
-
-`extends` 属性值可以是基本[配置文件](https://cn.eslint.org/docs/user-guide/configuring#using-configuration-files)的绝对路径或相对路径。
-
-ESLint 解析基本配置文件的相对路径相对你你使用的配置文件,**除非**那个文件在你的主目录或非 ESLint 安装目录的父级目录。在这些情况下,ESLint 解析基本配合文件的相对路径相对于被检测的 **项目**目录(尤其是当前工作目录)。
-
-JSON 格式的一个配置文件的例子:
-
-```
-{
- "extends": [
- "./node_modules/coding-standard/eslintDefaults.js",
- "./node_modules/coding-standard/.eslintrc-es6",
- "./node_modules/coding-standard/.eslintrc-jsx"
- ],
- "rules": {
- "eqeqeq": "warn"
- }
-}
-
-```
-
-#### 使用 eslint:all
-
-`extends` 属性值可以是 `eslint:all`,启用当前安装的 ESLint 中所有的核心规则。这些规则可以在 ESLint 的任何版本进行更改。
-
-**重要:**这些配置 **不推荐在产品中使用**,因为它随着 ESLint 版本进行更改。使用的话,请自己承担风险。
-
-如果你配置 ESLint 升级时自动地启用新规则,当源码没有任何改变时,ESLint 可以报告新问题,因此任何 ESLint 的新的小版本好像有破坏性的更改。
-
-当你决定在一个项目上使用的规则和选项,尤其是如果你很少覆盖选项或禁用规则,你可能启用所有核心规则作为一种快捷方式使用。规则的默认选项并不是 ESLint 推荐的(例如,`quotes` 规则的默认选项并不意味着双引号要比单引号好)。
-
-如果你的配置扩展了所有的核心规则:在你升级到一个新的大或小的 ESLint 版本,在你使用[命令行](https://cn.eslint.org/docs/user-guide/command-line-interface#fix)的 `--fix` 选项之前,检查一下报告的问题,这样你就知道一个新的可修复的规则将更改代码。
-
-JavaScript 格式的一个配置文件的例子:
-
-```
-module.exports = {
- "extends": "eslint:all",
- "rules": {
- // override default options
- "comma-dangle": ["error", "always"],
- "indent": ["error", 2],
- "no-cond-assign": ["error", "always"],
-
- // disable now, but enable in the future
- "one-var": "off", // ["error", "never"]
-
- // disable
- "init-declarations": "off",
- "no-console": "off",
- "no-inline-comments": "off",
- }
-}
-
-```
-
-### 基于 glob 模式的配置
-
-有时,你可能需要更精细的配置,比如,如果同一个目录下的文件需要有不同的配置。因此,你可以在配置中使用 `overrides` 键,它只适用于匹配特定的 glob 模式的文件,使用你在命令行上传递的格式 (e.g., `app/**/*.test.js`)。
-
-#### 怎么工作
-
-- Glob 模式覆盖只能在配置文件 (`.eslintrc.*` 或 `package.json`) 中进行配置。
-- 模式应用于相对于配置文件的目录的文件路径。 比如,如果你的配置文件的路径为 `/Users/john/workspace/any-project/.eslintrc.js` 而你要检测的路径为 `/Users/john/workspace/any-project/lib/util.js`,那么你在 `.eslintrc.js` 中提供的模式是相对于 `lib/util.js` 来执行的.
-- 在相同的配置文件中,Glob 模式覆盖比其他常规配置具有更高的优先级。 同一个配置中的多个覆盖将按顺序被应用。也就是说,配置文件中的最后一个覆盖会有最高优先级。
-- 一个 glob 特定的配置几乎与 ESLint 的其他配置相同。覆盖块可以包含常规配置中的除了 `extends`、`overrides` 和 `root` 之外的其他任何有效配置选项,
-- 可以在单个覆盖块中提供多个 glob 模式。一个文件必须匹配至少一个配置中提供的模式。
-- 覆盖块也可以指定从匹配中排除的模式。如果一个文件匹配了任何一个排除模式,该配置将不再被应用。
-
-#### 相对 glob 模式
-
-```
-project-root
-├── app
-│ ├── lib
-│ │ ├── foo.js
-│ │ ├── fooSpec.js
-│ ├── components
-│ │ ├── bar.js
-│ │ ├── barSpec.js
-│ ├── .eslintrc.json
-├── server
-│ ├── server.js
-│ ├── serverSpec.js
-├── .eslintrc.json
-
-```
-
-`app/.eslintrc.json` 文件中的配置定义了 glob 模式 `**/*Spec.js`。该模式是相对 `app/.eslintrc.json` 的基本目录的。因此,该模式将匹配 `app/lib/fooSpec.js` 和 `app/components/barSpec.js` 但 **不匹配** `server/serverSpec.js`。如果你在项目根目录下的 `.eslintrc.json` 文件中定义了同样的模式,它将匹配这三个 `*Spec` 文件。
-
-#### 配置示例
-
-在你的 `.eslintrc.json` 文件中:
-
-```
-{
- "rules": {
- "quotes": [ 2, "double" ]
- },
-
- "overrides": [
- {
- "files": [ "bin/*.js", "lib/*.js" ],
- "excludedFiles": "*.test.js",
- "rules": {
- "quotes": [ 2, "single" ]
- }
- }
- ]
-}
-
-```
-
-### 在配置文件中注释
-
-JSON 和 YAML 配置文件格式都支持注释 ( `package.json` 文件不应该包括注释)。你可以在其他类型的文件中使用 JavaScript 风格的注释或使用 YAML 风格的注释,ESLint 会忽略它们。这允许你的配置更加人性化。例如:
-
-```
-{
- "env": {
- "browser": true
- },
- "rules": {
- // Override our default settings just for this directory
- "eqeqeq": "warn",
- "strict": "off"
- }
-}
-
-```
-
-### 指定需要检查的文件扩展名
-
-目前,告诉 ESLint 哪个文件扩展名要检测的唯一方法是使用 [`--ext`](https://cn.eslint.org/docs/user-guide/command-line-interface#ext) 命令行选项指定一个逗号分隔的扩展名列表。注意,该标记只在与目录一起使用时有效,如果使用文件名或 glob 模式,它将会被忽略。
-
-### 忽略文件和目录
-
-你可以通过在项目根目录创建一个 `.eslintignore` 文件告诉 ESLint 去忽略特定的文件和目录。`.eslintignore` 文件是一个纯文本文件,其中的每一行都是一个 glob 模式表明哪些路径应该忽略检测。例如,以下将忽略所有的 JavaScript 文件:
-
-```
-**/*.js
-
-```
-
-当 ESLint 运行时,在确定哪些文件要检测之前,它会在当前工作目录中查找一个 `.eslintignore` 文件。如果发现了这个文件,当遍历目录时,将会应用这些偏好设置。一次只有一个 `.eslintignore` 文件会被使用,所以,不是当前工作目录下的 `.eslintignore` 文件将不会被用到。
-
-Globs 匹配使用 [node-ignore](https://github.com/kaelzhang/node-ignore),所以大量可用的特性有:
-
-- 以 `#` 开头的行被当作注释,不影响忽略模式。
-- 路径是相对于 `.eslintignore` 的位置或当前工作目录。这也会影响通过 `--ignore-pattern`传递的路径。
-- 忽略模式同 `.gitignore` [规范](http://git-scm.com/docs/gitignore)
-- 以 `!` 开头的行是否定模式,它将会重新包含一个之前被忽略的模式。
-
-除了 `.eslintignore` 文件中的模式,ESLint 总是忽略 `/node_modules/*` 和 `/bower_components/*` 中的文件。
-
-例如:把下面 `.eslintignore` 文件放到当前工作目录里,将忽略 `node_modules`,`bower_components` 以及 `build/` 目录下除了 `build/index.js` 的所有文件。
-
-```
-## /node_modules/* and /bower_components/* ignored by default
-
-## Ignore built files except build/index.js
-build/*
-!build/index.js
-
-```
-
-#### 使用备用文件
-
-如果相比于当前工作目录下 `.eslintignore` 文件,你更想使用一个不同的文件,你可以在命令行使用 `--ignore-path` 选项指定它。例如,你可以使用 `.jshintignore` 文件,因为它有相同的格式:
-
-```
-eslint --ignore-path .jshintignore file.js
-
-```
-
-你也可以使用你的 `.gitignore` 文件:
-
-```
-eslint --ignore-path .gitignore file.js
-```
-
-任何文件只要满足标准忽略文件格式都可以用。记住,指定 `--ignore-path` 意味着任何现有的 `.eslintignore` 文件将不被使用。请注意,`.eslintignore` 中的匹配规则比 `.gitignore` 中的更严格。
-
-#### 在 package.json 中使用 eslintConfig
-
-```
-{
- "name": "mypackage",
- "version": "0.0.1",
- "eslintConfig": {
- "env": {
- "browser": true,
- "node": true
- }
- },
- "eslintIgnore": ["hello.js", "world.js"]
-}
-```
-
-#### 忽略文件告警
-
-当您将目录传递给 ESLint 时,文件和目录将被忽略。如果将特定文件传递给 ESLint,则会看到一条警告,指示该文件已被跳过。例如,假设你有一个 `.eslintignore` 文件,如下所示:
-
-```
-foo.js
-
-```
-
-然后,您执行:
-
-```
-eslint foo.js
-
-```
-
-您将会看到以下告警:
-
-```
-foo.js
- 0:0 warning File ignored because of your .eslintignore file. Use --no-ignore to override.
-
-✖ 1 problem (0 errors, 1 warning)
-
-```
-
-发生此消息是因为 ESLint 不确定是否要检查该文件。如消息所示,您可以使用 `--no-ignore` 省略使用忽略规则。
-
-## 实战
-
-React 项目中,通常会使用 React、JSX、ES6 等特性,所以 ESLint 推荐的检查规则可能还不能满足我们对代码检查的需要。
-
-### ESLint 集成 Airbnb
-
-业界比较流行的 [Airbnb JavaScript Style](https://github.com/airbnb/javascript) 可以说是一个不错的选择。
-
-**那么如何让 ESLint 支持 Airbnb 呢?**
-
-下面将一一道来。
-
-#### eslint-config-airbnb
-
-[eslint-config-airbnb](https://www.npmjs.com/package/eslint-config-airbnb) 该包将 Airbnb 的规则作为 ESLint 可扩展的共享配置。
-
-**安装方法一**
-
-执行 `eslint --init` 命令,依次选择 **Use a popular style guide** -> **Airbnb**
-
-ESLint 会自动下载 Airbnb 扩展所需的所有插件。
-
-**安装方法二**
-
-ESLint 默认的下载地址是 https://registry.npmjs.org/ ,由于是外网,可能会很慢。
-
-如果你使用淘宝的 cnpm,也可以自己下载 Airbnb 扩展所需的插件:
-
-- [eslint-config-airbnb](https://www.npmjs.com/package/eslint-config-airbnb)
-- [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import)
-- [eslint-plugin-jsx-a11y](https://github.com/evcohen/eslint-plugin-jsx-a11y)
-- [eslint-plugin-react](https://github.com/yannickcr/eslint-plugin-react)
-
-执行安装命令:
-
-```sh
-$ cnpm i -D eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-config-airbnb
-```
-
-#### babel-eslint
-
-由于 React 项目往往会使用 ES6 特性。ES6 若想识别 ES6 特性,需要相应的解析器,[babel-eslint](https://github.com/babel/babel-eslint) 是比较流行的选择。
-
-执行安装命令:
-
-```sh
-$ cnpm i -D babel-eslint
-```
-
-#### 配置
-
-ESLint 支持几种格式的配置文件,个人推荐使用 `.eslintrc.js` ,这是官方优先级最高的配置文件。
-
-配置文件中配置 Airbnb 有几个重点项:
-
-```json
-{
- // 指定校验的环境
- 'env': {
- 'browser': true,
- 'node': true,
- 'es6': true,
- 'commonjs': true
- },
- // 指定扩展标准
- 'extends': ['airbnb'],
- // 指定解析器
- 'parser': 'babel-eslint',
- // 指定插件
- 'plugins': [
- 'import',
- 'jsx-a11y',
- 'react'
- ],
- 'rules': {
- ...
- }
-}
-```
-
-Airbnb 的检查规则是比较严格的,如果你认为有些检查项没有必要,也可以自行在 `rules` 中修改。
-
-## ESLint 命令
-
-为了在 Node.js 上运行 ESLint,你必须先安装 npm。如果还没有安装 npm ,按照这里的说明进行安装:[https://www.npmjs.com/](https://www.npmjs.com/)。
-
-一旦安装了 npm,运行下面的命令
-
-```
-npm i -g eslint
-```
-
-这句命令从 npm 仓库安装了 ESLint CLI。使用以下格式运行 ESLint:
-
-```
-eslint [options] [file|dir|glob]*
-```
-
-比如:
-
-```
-eslint file1.js file2.js
-```
-
-或者:
-
-```
-eslint lib/**
-```
-
-请注意,传递一个 glob 模式作为参数时,它将由你的 shell 进行扩展。扩展的结果取决于你的 shell 及其配置。如果你想使用 node 的 `glob` 语法,你需要给参数加上引号(在 windows 系统运行时,如果你需要,也可以使用双引号 ),像下面这样:
-
-```
-eslint "lib/**"
-```
-
-### 选项
-
-命令行工具有几个选项,你可以通过运行 `eslint -h` 查看所有选项。
-
-```sh
-eslint [options] file.js [file.js] [dir]
-
-Basic configuration:
- -c, --config path::String Use configuration from this file or shareable config
- --no-eslintrc Disable use of configuration from .eslintrc
- --env [String] Specify environments
- --ext [String] Specify JavaScript file extensions - default: .js
- --global [String] Define global variables
- --parser String Specify the parser to be used
- --parser-options Object Specify parser options
-
-Caching:
- --cache Only check changed files - default: false
- --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache
- --cache-location path::String Path to the cache file or directory
-
-Specifying rules and plugins:
- --rulesdir [path::String] Use additional rules from this directory
- --plugin [String] Specify plugins
- --rule Object Specify rules
-
-Ignoring files:
- --ignore-path path::String Specify path of ignore file
- --no-ignore Disable use of ignore files and patterns
- --ignore-pattern [String] Pattern of files to ignore (in addition to those in .eslintignore)
-
-Using stdin:
- --stdin Lint code provided on - default: false
- --stdin-filename String Specify filename to process STDIN as
-
-Handling warnings:
- --quiet Report errors only - default: false
- --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1
-
-Output:
- -o, --output-file path::String Specify file to write report to
- -f, --format String Use a specific output format - default: stylish
- --color, --no-color Force enabling/disabling of color
-
-Miscellaneous:
- --init Run config initialization wizard - default: false
- --fix Automatically fix problems
- --debug Output debugging information
- -h, --help Show help
- -v, --version Output the version number
- --no-inline-config Prevent comments from changing config or rules
- --print-config path::String Print the configuration for the given file
-```
-
-这些选项可以通过重复该选项或使用逗号分隔的列表进行指定(除了 `--ignore-pattern` 不允许第二种风格)。
-
-示例:
-
-```sh
-eslint --ext .jsx --ext .js lib/
-eslint --ext .jsx,.js lib/
-```
-
-#### 基本配置
-
-##### `-c`, `--config`
-
-该选项允许你为 ESLint (查看 [Configuring ESLint](https://cn.eslint.org/docs/user-guide/configuring) 了解更多)指定一个额外的配置文件。
-
-例如:
-
-```
-eslint -c ~/my-eslint.json file.js
-```
-
-这个例子使用了 `~/my-eslint.json` 作为配置文件。
-
-它还接受 [sharable config](https://cn.eslint.org/docs/developer-guide/shareable-configs) 的一个模块的 ID。
-
-例如:
-
-```
-eslint -c myconfig file.js
-```
-
-这个例子直接使用可共享的配置 `eslint-config-myconfig`。
-
-##### `--no-eslintrc`
-
-禁用 `.eslintrc` 和 `package.json` 文件中的配置。
-
-示例:
-
-```
-eslint --no-eslintrc file.js
-```
-
-##### `--env`
-
-这个选项用来指定环境。关于每种环境中定义的全局变量的详细信息请查看 [configuration](https://cn.eslint.org/docs/user-guide/configuring) 文档。该选项只能启用环境,不能禁用在其它配置文件中设置的环境。要指定多个环境的话,使用逗号分隔它们,或多次使用这个选项。
-
-例如:
-
-```
-eslint --env browser,node file.js
-eslint --env browser --env node file.js
-```
-
-##### `--ext`
-
-这个选项允许你指定 ESLint 在指定的目录下查找 JavaScript 文件时要使用的文件扩展名。默认情况下,它使用 `.js` 作为唯一性文件扩展名。
-
-示例:
-
-```
-## Use only .js2 extension
-eslint . --ext .js2
-
-## Use both .js and .js2
-eslint . --ext .js --ext .js2
-
-## Also use both .js and .js2
-eslint . --ext .js,.js2
-```
-
-**注意:**如果你使用了 glob 模式,则 `--ext` 被忽略
-
-例如,`eslint lib/* --ext .js` 将匹配 `lib/` 下的所有文件,忽略扩展名。
-
-##### `--global`
-
-这个选项定义了全局变量,这样它们就不会被 `no-undef` 规则标记为未定义了。任何指定的全局变量默认是只读的,在变量名字后加上 `:true` 后会使它变为可写。要指定多个变量,使用逗号分隔它们,或多次使用这个选项。
-
-示例:
-
-```
-eslint --global require,exports:true file.js
-eslint --global require --global exports:true
-```
-
-##### `--parser`
-
-该选项允许你为 ESLint 指定一个解析器。默认情况下,使用 `espree`。
-
-##### `--parser-options`
-
-该选项允许你指定 ESLint 要使用的解析器选项。注意,可用的解析器选项取决于你所选用的解析器。
-
-示例:
-
-```
-echo '3 ** 4' | eslint --stdin --parser-options=ecmaVersion:6 ## will fail with a parsing error
-echo '3 ** 4' | eslint --stdin --parser-options=ecmaVersion:7 ## succeeds, yay!
-```
-
-#### 缓存
-
-##### `--cache`
-
-存储处理过的文件的信息以便只对有改变的文件进行操作。缓存默认被存储在 `.eslintcache`。启用这个选项可以显著改善 ESLint 的运行时间,确保只对有改变的文件进行检测。
-
-**注意:**如果你运行 ESLint `--cache`,然后又运行 ESLint 不带 `--cache`,`.eslintcache` 文件将被删除。这是必要的,因为检测的结果可能会改变,使 `.eslintcache` 无效。如果你想控制缓存文件何时被删除,那么使用 `--cache-location` 来指定一个缓存文件的位置。
-
-##### `--cache-file`
-
-缓存文件的路径。如果没有指定,则使用 `.eslintcache`。这个文件会在 `eslint` 命令行被执行的文件目录中被创建。 **已弃用:** 请使用 `--cache-location`。
-
-##### `--cache-location`
-
-缓存文件的路径。可以是一个文件或者一个目录。如果没有指定,则使用 `.eslintcache` 。这个文件会在 `eslint` 命令行被执行的文件目录中被创建。
-
-如果指定一个目录,缓存文件将在指定的文件夹下被创建。文件名将基于当前工作目录(CWD) 的 hash 值,比如:`.cache_hashOfCWD`。
-
-**重要提示:**如果不存在缓存文件的目录,请确保在尾部添加 `/`(\*nix 系统)或 `\`(windows 系统)。否则该路径将被假定为是一个文件。
-
-示例:
-
-```
-eslint "src/**/*.js" --cache --cache-location "/Users/user/.eslintcache/"
-```
-
-#### 指定规则和插件
-
-##### `--rulesdir`
-
-这个选项允许你指定另一个加载规则文件的目录。这允许你在运行时动态加载新规则。当你有自定义规则,而且这些规则不适合绑定到 ESLint 时,这会很有用。
-
-示例:
-
-```
-eslint --rulesdir my-rules/ file.js
-```
-
-为了使你自定义的规则目录下的规则正常工作,必须遵照同绑定的规则一样的格式。你也可以通过包含多个 `--rulesdir` 选项来为自定义规则指定多个位置。
-
-```
-eslint --rulesdir my-rules/ --rulesdir my-other-rules/ file.js
-```
-
-注意,与核心规则和插件规则一样,你仍需要在配置文件或通过 `--rule` 命令行选项启用这些规则,以便在检测过程中实际运行这些规则。使用 `--rulesdir` 指定一个规则目录不会自动启用那些目录下的规则。
-
-##### `--plugin`
-
-这个选项指定一个要加载的插件。你可以省略插件名的前缀 `eslint-plugin-`。在你使用插件直接,你必须使用 npm 安装它。
-
-示例:
-
-```
-eslint --plugin jquery file.js
-eslint --plugin eslint-plugin-mocha file.js
-```
-
-##### `--rule`
-
-这个选项指定要使用的规则。这些规则将会与配制文件中指定的规则合并。(你可以使用 `--no-eslintrc` 改变这种行为。)要定义多个规则,使用逗号分隔它们,或多次使用这个选项。[levn](https://github.com/gkz/levn#levn--) 格式被用来指定规则。
-
-如果这个规则定义在插件内,你必须在规则 ID 前使用插件名和 `/`,即 `插件名/规则ID`。
-
-示例:
-
-```
-eslint --rule 'quotes: [2, double]'
-eslint --rule 'guard-for-in: 2' --rule 'brace-style: [2, 1tbs]'
-eslint --rule 'jquery/dollar-sign: 2'
-```
-
-#### 忽略文件
-
-##### `--ignore-path`
-
-这个选项允许你指定一个文件作为 `.eslintignore`。默认情况下,ESLint 在当前工作目录下查找 `.eslintignore`。你可以通过提供另一个文件的路径改变这种行为。
-
-示例:
-
-```
-eslint --ignore-path tmp/.eslintignore file.js
-eslint --ignore-path .gitignore file.js
-```
-
-##### `--no-ignore`
-
-禁止排除 `.eslintignore`、`--ignore-path` 和 `--ignore-pattern` 文件中指定的文件。
-
-示例:
-
-```
-eslint --no-ignore file.js
-```
-
-##### `--ignore-pattern`
-
-该选项允许你指定要忽略的文件模式(除了那些在 `.eslintignore` 的)。你可以重复该选项已提供多个模式。语法同 `.eslintignore` 文件中的相同。你应该将你的模式用引号括起来,以避免命令行解析器的解析。
-
-示例:
-
-```
-eslint --ignore-pattern '/lib/' --ignore-pattern '/src/vendor/*' .
-```
-
-#### 使用 stdin
-
-##### `--stdin`
-
-这个选项告诉 ESLint 从 STDIN 而不是从文件中读取和检测源码。你可以使用该选项向 ESLint 来输入代码。
-
-示例:
-
-```
-cat myfile.js | eslint --stdin
-```
-
-##### `--stdin-filename`
-
-这个选项允许你指定一个文件名去处理 STDIN。当你处理从 STDIN 来的文件和有规则依赖于这个文件名时,这会很有用。
-
-示例:
-
-```
-cat myfile.js | eslint --stdin --stdin-filename=myfile.js
-```
-
-#### 处理告警
-
-##### `--quiet`
-
-这个选项允许你禁止报告警告。如果开启这个选项,ESLint 只会报告错误。
-
-示例:
-
-```
-eslint --quiet file.js
-```
-
-##### `--max-warnings`
-
-这个选项允许你指定一个警告的阈值,当你的项目中有太多违反规则的警告时,这个阈值被用来强制 ESLint 以错误状态退出。
-
-通常情况下,如果 ESLint 运行过程中,没有出现错误(只有警告),它将以成功的状态退出。然而,如果指定了 `--max-warnings`,而且警告的总数超过了指定的阈值,ESLint 将以错误的状态退出。通过指定一个 `-1` 的阈值或省略这个选项将会避免这种行为。
-
-示例:
-
-```
-eslint --max-warnings 10 file.js
-```
-
-#### 输出
-
-##### `-o`, `--output-file`
-
-将报告写到一个文件。
-
-示例:
-
-```
-eslint -o ./test/test.html
-```
-
-当指定这个选项时,就会按给定的格式输出到指定的文件名。
-
-##### `-f`, `--format`
-
-这个选项指定了控制台的输出格式。可用的格式是:
-
-- [codeframe](https://cn.eslint.org/docs/user-guide/formatters/#codeframe)
-- [compact](https://cn.eslint.org/docs/user-guide/formatters/#compact)
-- [html](https://cn.eslint.org/docs/user-guide/formatters/#html)
-- [jslint-xml](https://cn.eslint.org/docs/user-guide/formatters/#jslint-xml)
-- [json](https://cn.eslint.org/docs/user-guide/formatters/#json)
-- [junit](https://cn.eslint.org/docs/user-guide/formatters/#junit)
-- [stylish](https://cn.eslint.org/docs/user-guide/formatters/#stylish) (the default)
-- [table](https://cn.eslint.org/docs/user-guide/formatters/#table)
-- [tap](https://cn.eslint.org/docs/user-guide/formatters/#tap)
-- [unix](https://cn.eslint.org/docs/user-guide/formatters/#unix)
-- [visualstudio](https://cn.eslint.org/docs/user-guide/formatters/#visualstudio)
-
-例如:
-
-```
-eslint -f compact file.js
-```
-
-你也可以在命令行中通过指定一个自定义的格式的文件路径来使用自定义的格式。
-
-示例:
-
-```
-eslint -f ./customformat.js file.js
-```
-
-当指定之后,给定的格式就输出到控制台。如果你想将输出保存到一个文件,你可以在命令行上这样操作:
-
-```
-eslint -f compact file.js > results.txt
-```
-
-这会将输出保存到 `results.txt` 文件。
-
-##### `--color`, `--no-color`
-
-在管道输出中禁用颜色。
-
-示例:
-
-```
-eslint --color file.js | cat
-eslint --no-color file.js
-```
-
-#### 杂项
-
-##### `--init`
-
-这个选项将会配置初始化向导。它被用来帮助新用户快速地创建 `.eslintrc` 文件,用户通过回答一些问题,选择一个流行的风格指南,或检查你的源文件,自动生成一个合适的配置。
-
-生成的配置文件将被创建在当前目录。
-
-##### `--fix`
-
-该选项指示 ESLint 试图修复尽可能多的问题。修复只针对实际文件本身,而且剩下的未修复的问题才会输出。不是所有的问题都能使用这个选项进行修复,该选项在以下情形中不起作用:
-
-1. 当代码传递给 ESLint 时,这个选项抛出一个错误。
-2. 这个选项对使用处理器的代码不起作用。
-
-##### `--debug`
-
-这个选项将调试信息输出到控制台。当你看到一个问题并且很难定位它时,这些调试信息会很有用。ESLint 团队可能会通过询问这些调试信息帮助你解决 bug。
-
-##### `-h`, `--help`
-
-这个选项会输出帮助菜单,显示所有可用的选项。当有这个选项时,忽略其他所有选项。
-
-##### `-v`, `--version`
-
-这个选项在控制台输出当前 ESlint 的版本。当有这个标记时,忽略其他所有标记。
-
-##### `--no-inline-config`
-
-这个选项会阻止像 `/*eslint-disable*/` 或者 `/*global foo*/` 这样的内联注释起作用。这允许你在不修改文件的情况下设置一个 ESLint 配置。所有的内联注释都会被忽略,比如:
-
-- `/*eslint-disable*/`
-- `/*eslint-enable*/`
-- `/*global*/`
-- `/*eslint*/`
-- `/*eslint-env*/`
-- `// eslint-disable-line`
-- `// eslint-disable-next-line`
-
-示例:
-
-```
-eslint --no-inline-config file.js
-```
-
-##### `--print-config`
-
-这个选项输出传递的文件使用的配置。当有这个标记时,不进行检测,只有配置相关的选项才是有效的。
-
-示例:
-
-```
-eslint --print-config file.js
-```
-
-### .eslintignore 文件
-
-当 ESLint 作用于一个目录时,ESLint 支持使用 `.eslintignore` 文件来避免检测处理。通过特定的命令行参数指定的文件就可以免除被忽略。`.eslintignore` 文件是个纯文本文件,每一行都包含一种模式。它可以放在目标目录的任何父级目录;它将影响到它所在的当前目录和所有子目录。这里是 `.eslintignore` 文件的一个简单示例:
-
-```
-node_modules/*
-**/vendor/*.js
-```
-
-ESLint 默认忽略的模式分解和目录的更多详细信息可以在 [Configuring ESLint](https://cn.eslint.org/docs/user-guide/configuring#ignoring-files-and-directories) 中找到。
-
-## 引申和引用
-
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-[ESLint](https://github.com/eslint/eslint)
diff --git a/frontend/qa/qa-summary.md b/frontend/qa/qa-summary.md
deleted file mode 100644
index 5464a71bda..0000000000
--- a/frontend/qa/qa-summary.md
+++ /dev/null
@@ -1,93 +0,0 @@
-# JavaScript QA 工具总结
-
-
-
-- [JavaScript QA 工具总结](#javascript-qa-工具总结)
- - [[JSLint](https://github.com/douglascrockford/JSLint)](#jslinthttpsgithubcomdouglascrockfordjslint)
- - [[JSHint](https://github.com/jshint/jshint)](#jshinthttpsgithubcomjshintjshint)
- - [[ESLint](https://github.com/eslint/eslint) :heavy_check_mark:](#eslinthttpsgithubcomeslinteslint-heavy_check_mark)
- - [[Prettier](https://github.com/prettier/prettier) :heavy_check_mark:](#prettierhttpsgithubcomprettierprettier-heavy_check_mark)
- - [[EditorConfig](https://editorconfig.org/) :heavy_check_mark:](#editorconfighttpseditorconfigorg-heavy_check_mark)
- - [[Standard](https://github.com/feross/standard) :heavy_check_mark:](#standardhttpsgithubcomferossstandard-heavy_check_mark)
- - [[TSlint](https://github.com/palantir/tslint)](#tslinthttpsgithubcompalantirtslint)
- - [引申和引用](#引申和引用)
-
-
-
-## [JSLint](https://github.com/douglascrockford/JSLint)
-
-JavaScript 静态检查工具。
-
-- 优点
- - 可以直接使用
-- 缺点
- - 没有配置文件,规则不能修改
- - 不支持自定义规则
- - 没有文档记录规则
- - 很难弄清楚哪个规则引起的错误
-
-## [JSHint](https://github.com/jshint/jshint)
-
-JavaScript 静态检查工具。
-
-- 优点
- - 大多是参数可以配置
- - 支持配置文件,在大项目中容易使用
- - 已经支持需要类库,像 jQuery、QUnit、NodeJS、Mocha 等
- - 支持基本的 ES6
-- 缺点
- - 难于知道哪个规则产生错误
- - 存在两类选项:强制选项和松散选项。使得配置有些混乱
- - 不支持自定义规则
-
-## [ESLint](https://github.com/eslint/eslint) :heavy_check_mark:
-
-JavaScript 静态检查工具。
-
-- 优点
- - 灵活:任何规则都可以开启闭合,以及有些规则有些额外配置
- - 很容易拓展和有需要可用插件
- - 容易理解产出
- - 包含了在其他检查器中不可用的规则,使得 ESLint 在错误检查上更有用
- - 支持 ES6,唯一支持 JSX 的工具
- - 支持自定义报告
-- 缺点
- - 速度慢,但不是主要问题
-
-## [Prettier](https://github.com/prettier/prettier) :heavy_check_mark:
-
-可定制的代码格式化工具。
-
-支持格式:
-
-- JavaScript · TypeScript · Flow · JSX · JSON
-- CSS · SCSS · Less
-- HTML · Vue · Angular
-- GraphQL · Markdown · YAML
-
-Intellij 和 Vscode 都可以安装 Prettier 插件来使用。
-
-[Install](https://prettier.io/docs/en/install.html) · [Options](https://prettier.io/docs/en/options.html) · [CLI](https://prettier.io/docs/en/cli.html) · [API](https://prettier.io/docs/en/api.html)
-
-## [EditorConfig](https://editorconfig.org/) :heavy_check_mark:
-
-可以在编辑器或 IDE 中维护代码格式。
-
-[EditorConfig 应用示例](https://github.com/editorconfig/editorconfig/wiki/Projects-Using-EditorConfig)
-
-## [Standard](https://github.com/feross/standard) :heavy_check_mark:
-
-JavaScript 静态检查工具。
-
-- 优点
-- 无须配置。
-- 自动代码格式化。 只需运行 standard --fix
-- 提前发现风格及程序问题。
-
-## [TSlint](https://github.com/palantir/tslint)
-
-TypeScript 静态检查工具。
-
-## 引申和引用
-
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
diff --git a/frontend/tool/gitbook-quiickstart.md b/frontend/tool/gitbook-quiickstart.md
deleted file mode 100644
index 2d8e1c6542..0000000000
--- a/frontend/tool/gitbook-quiickstart.md
+++ /dev/null
@@ -1,751 +0,0 @@
-> 本文详细讲解了 Gitbook 生成电子书的完整过程,内容包括:安装、命令、配置、文档结构、生成电子书、部署。
->
-> 限于篇幅,本文不介绍任何 Gitbook 定制化页面的内容。
->
-> 想看看 Gitbook 在线电子书效果,请猛戳这里:[gitbook-notes](https://dunwu.gitbooks.io/gitbook-notes)
-
-
-
-- [概述](#概述)
-- [GitBook 安装](#gitbook-安装)
-- [GitBook 命令](#gitbook-命令)
-- [Gitbook 目录结构](#gitbook-目录结构)
-- [Gitbook 配置](#gitbook-配置)
-- [生成电子书](#生成电子书)
-- [Gitbook 部署](#gitbook-部署)
-- [资源](#资源)
-
-
-
-## 概述
-
-GitBook 是使用 GitHub / Git 和 Markdown(或 AsciiDoc)构建漂亮书籍的命令行工具(和 Node.js 库)。
-
-GitBook 可以将您的内容作为网站(可定制和可扩展)或电子书(PDF,ePub 或 Mobi)输出。
-
-[GitBook.com](https://www.gitbook.com/) 是使用 GitBook 格式创建和托管图书的在线平台。它提供托管,协作功能和易于使用的编辑器。
-
-
-
-## GitBook 安装
-
-### 本地安装
-
-#### 环境要求
-
-安装 GitBook 是很简单的。您的系统只需要满足这两个要求:
-
-- NodeJS(推荐使用 v4.0.0 及以上版本)
-- Windows,Linux,Unix 或 Mac OS X
-
-#### 通过 NPM 安装
-
-安装 GitBook 的最好办法是通过 **NPM**。在终端提示符下,只需运行以下命令即可安装 GitBook:
-
-```bash
-$ npm install gitbook-cli -g
-```
-
-`gitbook-cli` 是 GitBook 的一个命令行工具。它将自动安装所需版本的 GitBook 来构建一本书。
-
-执行下面的命令,查看 GitBook 版本,以验证安装成功。
-
-```bash
-$ gitbook -V
-```
-
-#### 安装历史版本
-
-`gitbook-cli` 可以轻松下载并安装其他版本的 GitBook 来测试您的书籍:
-
-```bash
-$ gitbook fetch beta
-```
-
-使用 `gitbook ls-remote` 会列举可以下载的版本。
-
-### 创建一本书
-
-#### 初始化
-
-GitBook 可以设置一个样板书:
-
-```bash
-$ gitbook init
-```
-
-如果您希望将书籍创建到一个新目录中,可以通过运行 `gitbook init ./directory` 这样做。
-
-#### 构建
-
-使用下面的命令,会在项目的目录下生成一个 `_book` 目录,里面的内容为静态站点的资源文件:
-
-```bash
-$ gitbook build
-```
-
-##### Debugging
-
-您可以使用选项 `--log=debug` 和 `--debug` 来获取更好的错误消息(使用堆栈跟踪)。例如:
-
-```bash
-$ gitbook build ./ --log=debug --debug
-```
-
-#### 启动服务
-
-使用下列命令会运行一个 web 服务, 通过 `http://localhost:4000/` 可以预览书籍
-
-```bash
-$ gitbook serve
-```
-
-## GitBook 命令
-
-这里主要介绍一下 GitBook 的命令行工具 `gitbook-cli` 的一些命令, 首先说明两点:
-
-- `gitbook-cli` 和 `gitbook` 是两个软件
-- `gitbook-cli` 会将下载的 gitbook 的不同版本放到 `~/.gitbook`中, 可以通过设置`GITBOOK_DIR`环境变量来指定另外的文件夹
-
-**列出 gitbook 所有的命令**
-
-```bash
-gitbook help
-```
-
-**输出 `gitbook-cli` 的帮助信息**
-
-```bash
-gitbook --help
-```
-
-**生成静态网页**
-
-```bash
-gitbook build
-```
-
-**生成静态网页并运行服务器**
-
-```bash
-gitbook serve
-```
-
-**生成时指定 gitbook 的版本, 本地没有会先下载**
-
-```bash
-gitbook build --gitbook=2.0.1
-```
-
-**列出本地所有的 gitbook 版本**
-
-```bash
-gitbook ls
-```
-
-**列出远程可用的 gitbook 版本**
-
-```bash
-gitbook ls-remote
-```
-
-**安装对应的 gitbook 版本**
-
-```bash
-gitbook fetch 标签/版本号
-```
-
-**更新到 gitbook 的最新版本**
-
-```bash
-gitbook update
-```
-
-**卸载对应的 gitbook 版本**
-
-```bash
-gitbook uninstall 2.0.1
-```
-
-**指定 log 的级别**
-
-```bash
-gitbook build --log=debug
-```
-
-**输出错误信息**
-
-```bash
-gitbook builid --debug
-```
-
-## Gitbook 目录结构
-
-### GitBook 项目结构
-
-GitBook 使用简单的目录结构。在 [SUMMARY](https://toolchain.gitbook.com/pages.html) (即 `SUMMARY.md` 文件)中列出的所有 Markdown / Asciidoc 文件将被转换为 HTML。多语言书籍结构略有不同。
-
-一个基本的 GitBook 电子书结构通常如下:
-
-```
-.
-├── book.json
-├── README.md
-├── SUMMARY.md
-├── chapter-1/
-| ├── README.md
-| └── something.md
-└── chapter-2/
- ├── README.md
- └── something.md
-```
-
-GitBook 特殊文件的功能:
-
-| 文件 | 描述 |
-| ------------- | --------------------------------- |
-| `book.json` | 配置数据 (**optional**) |
-| `README.md` | 电子书的前言或简介 (**required**) |
-| `SUMMARY.md` | 电子书目录 (**optional**) |
-| `GLOSSARY.md` | 词汇/注释术语列表 (**optional**) |
-
-#### 静态文件和图片
-
-静态文件是在 `SUMMARY.md` 中未列出的文件。除非被忽略,否则所有静态文件都将复制到输出路径。
-
-#### 忽略文件和文件夹
-
-GitBook 将读取 `.gitignore`,`.bookignore` 和 `.ignore` 文件,以获取要过滤的文件和文件夹。这些文件中的格式遵循 `.gitignore` 的规则:
-
-```
-# This is a comment
-
-# Ignore the file test.md
-test.md
-
-# Ignore everything in the directory "bin"
-bin/*
-```
-
-#### 项目与子目录集成
-
-对于软件项目,您可以使用子目录(如 `docs/` )来存储项目文档的图书。您可以配置根选项来指示 GitBook 可以找到该图书文件的文件夹:
-
-```
-.
-├── book.json
-└── docs/
- ├── README.md
- └── SUMMARY.md
-```
-
-在 `book.json` 中配置以下内容:
-
-```json
-{
- "root": "./docs"
-}
-```
-
-### Summary
-
-GitBook 使用 `SUMMARY.md` 文件来定义本书的章节和子章节的结构。 `SUMMARY.md` 文件用于生成本书的目录。
-
-`SUMMARY.md` 的格式是一个链接列表。链接的标题将作为章节的标题,链接的目标是该章节文件的路径。
-
-向父章节添加嵌套列表将创建子章节。
-
-**简单示例:**
-
-```
-# Summary
-
-* [Part I](part1/README.md)
- * [Writing is nice](part1/writing.md)
- * [GitBook is nice](part1/gitbook.md)
-* [Part II](part2/README.md)
- * [We love feedback](part2/feedback_please.md)
- * [Better tools for authors](part2/better_tools.md)
-```
-
-每章都有一个专用页面(`part#/README.md`),并分为子章节。
-
-**锚点**
-
-目录中的章节可以使用锚点指向文件的特定部分。
-
-```
-# Summary
-
-### Part I
-
-* [Part I](part1/README.md)
- * [Writing is nice](part1/README.md#writing)
- * [GitBook is nice](part1/README.md#gitbook)
-* [Part II](part2/README.md)
- * [We love feedback](part2/README.md#feedback)
- * [Better tools for authors](part2/README.md#tools)
-```
-
-**部分**
-
-目录可以分为以标题或水平线 `----` 分隔的部分:
-
-```
-# Summary
-
-### Part I
-
-* [Writing is nice](part1/writing.md)
-* [GitBook is nice](part1/gitbook.md)
-
-### Part II
-
-* [We love feedback](part2/feedback_please.md)
-* [Better tools for authors](part2/better_tools.md)
-
-----
-
-* [Last part without title](part3/title.md)
-```
-
-Parts 只是章节组,没有专用页面,但根据主题,它将在导航中显示。
-
-#### 页面
-
-**Markdown 语法**
-
-默认情况下,GitBook 的大多数文件都使用 Markdown 语法。 GitBook 推荐使用这种语法。所使用的语法类似于 [GitHub Flavored Markdown syntax](https://guides.github.com/features/mastering-markdown/) 。
-
-此外,你还可以选择 [AsciiDoc 语法](https://toolchain.gitbook.com/syntax/asciidoc.html)。
-
-**页面内容示例:**
-
-```
-# Title of the chapter
-
-This is a great introduction.
-
-## Section 1
-
-Markdown will dictates _most_ of your **book's structure**
-
-## Section 2
-
-...
-```
-
-**页面前言**
-
-页面可以包含一个可选的前言。它可以用于定义页面的描述。前面的事情必须是文件中的第一件事,必须采取在三虚线之间设置的有效 YAML 的形式。这是一个基本的例子:
-
-```
----
-description: This is a short description of my page
----
-
-# The content of my page
-...
-```
-
-### Glossary
-
-允许您指定要显示为注释的术语及其各自的定义。根据这些术语,GitBook 将自动构建索引并突出显示这些术语。
-
-`GLOSSARY.md` 的格式是 `h2` 标题的列表,以及描述段落:
-
-```
-## Term
-Definition for this term
-
-## Another term
-With it's definition, this can contain bold text
-and all other kinds of inline markup ...
-```
-
-## Gitbook 配置
-
-> GitBook 允许您使用灵活的配置自定义您的电子书。
->
-> 这些选项在 `book.json` 文件中指定。对于不熟悉 JSON 语法的作者,您可以使用 [JSONlint](http://jsonlint.com/) 等工具验证语法。
-
-### 常规设置
-
-| 变量 | 描述 |
-| ------------- | ---------------------------------------------------------------------------------------------------- |
-| `root` | 包含所有图书文件的根文件夹的路径,除了 `book.json` |
-| `structure` | 指定自述文件,摘要,词汇表等的路径,参考 [Structure paragraph](#structure). |
-| `title` | 您的书名,默认值是从 README 中提取出来的。在 GitBook.com 上,这个字段是预填的。 |
-| `description` | 您的书籍的描述,默认值是从 README 中提取出来的。在 GitBook.com 上,这个字段是预填的。 |
-| `author` | 作者名。在 GitBook.com 上,这个字段是预填的。 |
-| `isbn` | 国际标准书号 ISBN |
-| `language` | 本书的语言类型 —— [ISO code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 。默认值是 `en` |
-| `direction` | 文本阅读顺序。可以是 `rtl` (从右向左)或 `ltr` (从左向右),默认值依赖于 `language` 的值。 |
-| `gitbook` | 应该使用的 GitBook 版本。使用 [SemVer](http://semver.org/) 规范,并接受类似于 `“> = 3.0.0”` 的条件。 |
-
-author
-
-作者姓名,在 GitBook.com 上,这个字段是预先填写的。
-
-例:
-
-```json
-"author" : "victor zhang"
-```
-
-description
-
-电子书的描述,默认值是从 README 中提取出来的。在 GitBook.com 上,这个字段是预先填写的。
-
-例:
-
-```json
-"description" : "Gitbook 教程"
-```
-
-direction
-
-文本的方向。可以是 rtl 或 ltr,默认值取决于语言的值。
-
-例:
-
-```json
-"direction" : "ltr"
-```
-
-gitbook
-
-应该使用的 GitBook 版本。使用 SemVer 规范,接受类似于 >=3.0.0 的条件。
-
-例:
-
-```json
-"gitbook" : "3.0.0",
-"gitbook" : ">=3.0.0"
-```
-
-language
-
-Gitbook 使用的语言, 版本 2.6.4 中可选的语言如下:
-
-```
-en, ar, bn, cs, de, en, es, fa, fi, fr, he, it, ja, ko, no, pl, pt, ro, ru, sv, uk, vi, zh-hans, zh-tw
-```
-
-例:
-
-```json
-"language" : "zh-hans",
-```
-
-links
-
-在左侧导航栏添加链接信息
-
-例:
-
-```json
-"links" : {
- "sidebar" : {
- "Home" : "https://github.com/dunwu/gitbook-notes"
- }
-}
-```
-
-root
-
-包含所有图书文件的根文件夹的路径, book.json 文件除外。
-
-例:
-
-```json
-"root" : "./docs",
-```
-
-structure
-
-指定 Readme、Summary、Glossary 和 Languages 对应的文件名。
-
-styles
-
-自定义页面样式, 默认情况下各 generator 对应的 css 文件
-
-例:
-
-```json
-"styles": {
- "website": "styles/website.css",
- "ebook": "styles/ebook.css",
- "pdf": "styles/pdf.css",
- "mobi": "styles/mobi.css",
- "epub": "styles/epub.css"
-}
-```
-
-例如要使 `h1`、`h2` 标签有下边框, 可以在 `website.css` 中设置
-
-```css
-h1,
-h2 {
- border-bottom: 1px solid #efeaea;
-}
-```
-
-title
-
-电子书的书名,默认值是从 README 中提取出来的。在 GitBook.com 上,这个字段是预先填写的。
-
-例:
-
-```json
-"title" : "gitbook-notes",
-```
-
-### plugins
-
-插件及其配置在 `book.json` 中指定。有关详细信息。
-
-自 3.0.0 版本开始,GitBook 可以使用主题。有关详细信息,请参阅 [the theming section](https://toolchain.gitbook.com/themes/) 。
-
-| 变量 | 描述 |
-| --------------- | ---------------- |
-| `plugins` | 要加载的插件列表 |
-| `pluginsConfig` | 插件的配置 |
-
-#### 添加插件
-
-```json
-"plugins": [
- "splitter"
-]
-```
-
-添加新插件之后需要运行 `gitbook install` 来安装新的插件
-
-### 去除自带插件
-
-Gitbook 默认带有 5 个插件:
-
-- highlight
-- search
-- sharing
-- font-settings
-- livereload
-
-```json
-"plugins": [
- "-search"
-]
-```
-
-### structure
-
-除了 `root` 属性之外,您可以指定 Readme,Summary,Glossary 和 Languages 的名称(而不是使用默认名称,如 README.md)。这些文件必须在项目的根目录下(或 `root` 的根目录,如果你在 `book.json` 中配置了 `root` 属性)。不接受的路径,如:`dir / MY_README.md`。
-
-| 变量 | 描述 |
-| --------------------- | ------------------------------------------- |
-| `structure.readme` | Readme 文件名(默认值是 `README.md` ) |
-| `structure.summary` | Summary 文件名(默认值是 `SUMMARY.md` ) |
-| `structure.glossary` | Glossary 文件名(默认值是 `GLOSSARY.md` ) |
-| `structure.languages` | Languages 文件名(默认值是 `LANGS.md` ) |
-
-### pdf
-
-可以使用 `book.json` 中的一组选项来定制 PDF 输出:
-
-| Variable | Description |
-| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
-| `pdf.pageNumbers` | 将页码添加到每个页面的底部(默认为 true) |
-| `pdf.fontSize` | 基本字体大小(默认是 12) |
-| `pdf.fontFamily` | 基本字体样式(默认是 `Arial`) |
-| `pdf.paperSize` | 页面尺寸,选项有: `'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'legal', 'letter'` (默认值是 `a4`) |
-| `pdf.margin.top` | 上边界(默认值是 56) |
-| `pdf.margin.bottom` | 下边界(默认值是 56) |
-| `pdf.margin.right` | 右边界(默认值是 62) |
-| `pdf.margin.left` | 左边界(默认值是 62) |
-
-## 生成电子书
-
-GitBook 可以生成一个网站,但也可以输出内容作为电子书(ePub,Mobi,PDF)。
-
-```
-# Generate a PDF file
-$ gitbook pdf ./ ./mybook.pdf
-
-# Generate an ePub file
-$ gitbook epub ./ ./mybook.epub
-
-# Generate a Mobi file
-$ gitbook mobi ./ ./mybook.mobi
-```
-
-### 安装 ebook-convert
-
-`ebook-convert` 可以用来生成电子书(epub,mobi,pdf)。
-
-#### GNU/Linux
-
-安装 [Calibre application](https://calibre-ebook.com/download).
-
-```sh
-$ sudo aptitude install calibre
-```
-
-在一些 GNU / Linux 发行版中,节点被安装为 nodejs,您需要手动创建一个符号链接:
-
-```sh
-$sudo ln -s /usr/bin/nodejs /usr/bin/node
-```
-
-#### OS X
-
-下载 [Calibre application](https://calibre-ebook.com/download)。将 `calibre.app` 移动到应用程序文件夹后,创建一个符号链接到 `ebook-convert` 工具:
-
-```sh
-$ sudo ln -s ~/Applications/calibre.app/Contents/MacOS/ebook-convert /usr/bin
-```
-
-您可以使用 $PATH 中的任何目录替换 `/usr/bin` 。
-
-### 封面
-
-封面用于所有电子书格式。您可以自己提供一个,也可以使用 [autocover plugin](https://plugins.gitbook.com/plugin/autocover) 生成一个。
-
-要提供封面,请将 `cover.jpg` 文件放在书本的根目录下。添加一个 `cover_small.jpg` 将指定一个较小版本的封面。封面应为 `JPEG` 文件。
-
-好的封面应该遵守以下准则:
-
-- `cover.jpg` 的尺寸为 1800x2360 像素,`cover_small.jpg` 为 200x262
-- 没有边界
-- 清晰可见的书名
-- 任何重要的文字应该在小版本中可见
-
-## Gitbook 部署
-
-### 托管到 gitbook.com
-
-[GitBook.com](https://www.gitbook.com/) 是使用 GitBook 格式创建和托管图书的在线平台。它提供托管,协作功能和易于使用的编辑器。
-
-**创建新书**
-
-如下图所示,根据个人需求,选择一个模板创建你的电子书。
-
-
-
-**设置书的基本信息**
-
-
-
-**clone 到本地**
-
-Gitbook.com 会为每本书创建一个 git 仓库。
-
-如下图所示,拷贝 git 地址,然后 `git clone` 到本地。
-
-
-
-**发布**
-
-在本地按照 Gitbook 规范编辑电子书,然后 `git push` 到 Gitbook 的远程仓库。
-
-默认访问地址是:https://用户名.gitbooks.io/项目名/content/
-
-例如:我的用户名为 dunwu,一个电子书项目名为 test,则访问路径是: `https://dunwu.gitbooks.io/test/content/`
-
-当然,如果你有自己的域名,也可以设置 Domains 选项,来指定访问路径为你的域。
-
-### 托管到 Github
-
-如果你不希望使用 Gitbook 的仓库,而是想直接使用 Github 的仓库,也是可以的。
-
-首先,你需要绑定你的 Github 账号。最简单的方式当然就是登录 Gitbook.com 时使用 Github 账号登录方式了。否则,你也可以在 Account Settings 中的 Github 设置选项中去进行绑定。
-
-
-
-绑定了 Github 账号后,你可以在新建电子书时,选择从一个指定的 Github 仓库导入电子书项目。参考下图:
-
-
-
-只要你指定的 Github 仓库中的文档内容符合 Gitbook 规范,Gitbook 就会自动根据你的每次更新去构建生成电子书网站。
-
-默认访问地址是:
-
-```
-https://Github用户名.gitbooks.io/Github 仓库/content/
-```
-
-例如:我的用户名为 dunwu,Github 仓库名为 gitbook-notes,则访问路径是:
-
-[https://dunwu.gitbooks.io/gitbook-notes/content/](https://dunwu.gitbooks.io/gitbook-notes/content/)
-
-#### 托管到 Github Pages
-
-也许你以前也了解 Github 的一个功能: [GitHub Pages](https://pages.github.com/) 。它允许用户在 GitHub 仓库托管你的个人、组织或项目的静态页面(自动识别 html、css、javascript)。
-
-**建立 xxx.github.io 仓库**
-
-要使用这个特性,首先,你必须建立一个严格遵循以下命名要求的仓库:`Github账号名.github.io`举例,我的 Github 账号为 dunwu,则这个仓库应该叫 `dunwu.github.io`。通常,这个仓库被用来作为个人或组织的博客。
-
-**建立 gh-pages 分支**
-
-完成第 1 步后,在任意一个 Github 仓库中建立一个名为 `gh-pages` 的分支。只要 `gh-pages` 中的内容符合一个静态站点要求,就可以在如下地址中进行访问:`https://Github用户名.gitbooks.io/Github 仓库` 。例如:我的一个 Github 仓库名为 react-notes,则访问路径是:`https://dunwu.github.io/react-notes`
-
-**自动化发布到 gh-pages**
-
-如果每次都手动 git push 到远程 gh-pages 分支,略有点麻烦。
-
-怎么实现自动化发布呢?
-
-有两种方法:
-
-**使用 gh-pages 插件**
-
-如果你了解 Nodejs,那么最简单的发布方式就是使用 `gh-pages` 插件。
-
-先在本地安装插件
-
-```
-$ npm i -D gh-pages
-```
-
-然后,在 package.json 文件中添加脚本命令:
-
-如下:`-d` 命令参数后面是要发布的静态站点内容的目录
-
-```json
-"scripts": {
- "deploy": "gh-pages -d build"
-},
-```
-
-**脚本**
-
-写一个执行 git 命令的脚本就搞定了。
-
-下面的脚本无论是在 bat 或 sh 脚本中都可以执行。
-
-```
-cd build
-git init
-git checkout -b gh-pages
-git add .
-git commit -am "Update"
-git push git@github.com:dunwu/gitbook-notes gh-pages --force"
-```
-
-## 资源
-
-### 官方资源
-
-- [Gitbook Github](https://github.com/GitbookIO/gitbook)
-- [Gitbook 官网](https://www.gitbook.com/)
-- [Gitbook Toolchain 文档](https://toolchain.gitbook.com/)
-- [Gitbook 帮助中心](https://help.gitbook.com/)
-
-### 教程资源
-
-- [gitbook-use](https://github.com/zhangjikai/gitbook-use) by zhangjikai
-
-### 工具
-
-- [Gitbook 编辑器](https://www.gitbook.com/editor)
diff --git a/frontend/webpack/asset-management.md b/frontend/webpack/asset-management.md
deleted file mode 100644
index 0c6f57ca8d..0000000000
--- a/frontend/webpack/asset-management.md
+++ /dev/null
@@ -1,401 +0,0 @@
-# Webpack 资源管理
-
-> :pushpin: **提示:**
->
-> 1. 版本问题
->
-> 本文基于 `webpack 2.x` 版本。`webpack 2.x` 相比 `webpack 1.x` 有重大改变。所以,如果你的项目中已使用了 webpack 1.x ,本教程的示例将不适用,请慎重。
->
-> 如果铁了心要升级 webpack ,请参考 [webpack 官方文档 - 从 v1 迁移到 v2](https://doc.webpack-china.org/guides/migrating/)
->
-> 2) 阅读建议
->
-> 阅读本文前,建议先阅读 [Webpack 概念](https://github.com/dunwu/frontend-tutorial/blob/master/docs/chapter03/webpack/concept.md) 。
-
-## webpack 的优势
-
-**webpack** 最重要的功能就是资源管理。
-
-JavaScript 世界已有好几个有名的资源管理工具,webpack 有什么独到之处呢?
-
-在 **webpack** 出现之前,前端开发人员会使用 **grunt** 和 **gulp** 等工具来处理这些 web 资源,如样式文件(例如 `.css`, `.less`, `.sass`),图片(例如 `.png`, `.jpg`, `.svg`),字体(例如 `.woff`, `.woff2`, `.eot`)和数据(例如 `.json`, `.xml`, `.csv`)等,并将它们从 `/src` 文件夹移动到 `/dist` 或 `/build` 目录中。
-
-而 webpack 从 `entry(入口)` 开始,访问应用程序,并**动态打包(dynamically bundle)**所有依赖项。这是极好的创举,因为现在每个模块都可以明确表述它自身的依赖,这可以避免打包未使用的模块。
-
-## Loader
-
-`Loader(加载器)` 用于对模块的源代码进行转换。
-
-使用加载器一般遵循几步:
-
-1. 安装加载器
-2. 配置 Loader
-3. 引用资源文件
-
-### 安装加载器
-
-根据需要加载的资源文件,选择下载对应的加载器。
-
-```bash
-$ npm install --save-dev css-loader
-```
-
-更多 webpack 可用 Loader 请查看:[webpack loaders](https://doc.webpack-china.org/loaders/)
-
-### 配置 Loader
-
-> :warning: **注意:**
->
-> `webpack 2.x` 版本的 Loader 配置和 `webpack 1.x` 版本差别很大。
-
-Loader 在 `webpack.config.js` 文件的 `module` 属性中配置。
-
-**资源类型对应单一加载器**
-
-```js
-module: {
- rules: [
- {test: /\.css$/, loader: 'css-loader'}
- ]
-},
-```
-
-**资源类型对应多个加载器**
-
-```js
-module: {
- rules: [
- {
- test: /\.css$/,
- use: ["style-loader", "css-loader"]
- }
- ]
-},
-```
-
-**加载器含配置选项**
-
-```js
-module: {
- rules: [
- {
- test: /\.css$/,
- use: [
- { loader: 'style-loader' },
- {
- loader: 'css-loader',
- options: {
- modules: true
- }
- }
- ]
- }
- ]
-},
-```
-
-### 引用资源文件
-
-完成上两步后,就可以在 JavaScript 中使用 `import` ,`require` 关键字引用相应类型资源文件。
-
-```js
-import "./index.css";
-require("./index.css");
-```
-
-## Plugin
-
-`Plugin(插件)` 用于解决 Loader 无法解决的问题,它是 Loader 的辅助。
-
-由于 **plugin** 可以携带参数/选项,你必须在 wepback 配置中,向 `plugins` 属性传入 `new` 实例。
-
-### 安装插件
-
-webpack 自身包含了一些常用插件,你可以通过 webpack 来引用。除此之外的插件,使用前需要安装
-
-```bash
-$ npm install --save-dev html-webpack-plugin OpenBrowserPlugin
-```
-
-更多 webpack Plugins 可以查看: [webpack plugins](https://doc.webpack-china.org/plugins/)
-
-### 配置 Plugin
-
-```js
-const webpack = require("webpack");
-const HtmlWebpackPlugin = require("html-webpack-plugin");
-const OpenBrowserPlugin = require("open-browser-webpack-plugin");
-
-module.exports = {
- // 附加插件列表
- plugins: [
- // 压缩 js 插件
- new webpack.optimize.UglifyJsPlugin({
- compress: {
- warnings: false
- }
- }),
-
- // 用于简化 HTML 文件(index.html)的创建,提供访问 bundle 的服务。
- new HtmlWebpackPlugin({
- title: "frontend-tutorial",
- template: "./index.html"
- }),
-
- // 自动打开浏览器
- new OpenBrowserPlugin({
- url: "http://localhost:8080"
- })
- ]
-};
-```
-
-## 加载资源专题
-
-### 加载 React
-
-很多浏览器并不识别 React 语法,为了让浏览器支持,你需要使用 **babel-loader** 解释器来转义 React 语法为普通的 Javascript 语法。
-
-> :warning: **注意:**
->
-> 官方推荐 babel-loader 和 webpack 的对应版本
->
-> webpack 1.x | babel-loader <= 6.x
->
-> webpack 2.x | babel-loader >= 7.x (推荐)(^6.2.10 也可以运行,但会有不赞成的警告(deprecation warnings))
-
-首先,安装需要使用的库:
-
-```bash
-$ npm install --save-dev babel-loader babel-preset-es2015 babel-preset-react
-```
-
-babel-preset-xxx 表示你希望转义的语法。
-
-webpack.config.js 中的模块配置如下:
-
-```js
-// 关于模块配置
-module: {
-
- // 模块规则(配置 loader、解析器等选项)
- rules: [
- // 这里是匹配条件,每个选项都接收一个正则表达式或字符串
- // test 和 include 具有相同的作用,都是必须匹配选项
- // exclude 是必不匹配选项(优先于 test 和 include)
- // 最佳实践:
- // - 只在 test 和 文件名匹配 中使用正则表达式
- // - 在 include 和 exclude 中使用绝对路径数组
- // - 尽量避免 exclude,更倾向于使用 include
- {
- // 语义解释器,将 js/jsx 文件中的 es2015/react 语法自动转为浏览器可识别的 Javascript 语法
- test: /\.jsx?$/,
- include: path.resolve(__dirname, "app"),
-
- // 应该应用的 loader,它相对上下文解析
- // 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
- // 查看 webpack 1 升级指南。
- loader: "babel-loader",
-
- // loader 的可选项
- options: {
- presets: ["es2015", "react"]
- },
- },
- ]
-},
-```
-
-> :flashlight: **示例 DEMO04:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo04/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo04))
-
-### 加载 CSS
-
-为了从 JavaScript 模块中 `import` 一个 CSS 文件,你只需要在 module 中安装并添加 [style-loader](https://doc.webpack-china.org/loaders/style-loader) 和 [css-loader](https://doc.webpack-china.org/loaders/css-loader) 。
-
-```bash
-$ npm install --save-dev style-loader css-loader
-```
-
-**webpack.config.js**
-
-```js
-module.exports = {
- //...
- module: {
- rules: [
- {
- test: /\.css$/,
- use: ["css-loader", "style-loader"]
- }
- ]
- }
- //...
-};
-```
-
-好了,此时你就可以在代码中通过 `import './style.css'` 的方式引入 CSS 文件。
-
-其余,加载 less,sass 等样式文件也是大同小异,不一一细说。
-
-> :flashlight: **示例 DEMO06:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo06/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo06))
-
-### 加载图片
-
-如何打包、加载图片呢?你可以使用 file-loader 来指定要加载的图片。
-
-```bash
-$ npm install --save-dev file-loader
-```
-
-**webpack.config.js**
-
-```js
-module.exports = {
- //...
- module: {
- rules: [
- {
- test: /\.(png|svg|jpg|gif)$/,
- use: ["file-loader"]
- }
- ]
- }
- //...
-};
-```
-
-然后,你可以通过 `import imgBig from './lion.png'` 的方式引入图片。例:
-
-```jsx
-import React from "react";
-import imgBig from "./lion.png";
-
-class Welcome extends React.PureComponent {
- render() {
- return (
-
-
Hello, {this.props.name}
-
-
- );
- }
-}
-export default Welcome;
-```
-
-#### 压缩图片
-
-这还不算完,日常开发中,经常会遇到有些图片文件过大的问题,这会影响你的 app 的加载速度。webpack 提供了压缩图片的方法帮你解决图片大的问题。
-
-首先,你需要安装 **image-webpack-loader**
-
-```bash
-$ npm i --save-dev image-webpack-loader
-```
-
-接下来,修改 webpack.config.js
-
-```js
-{
- // 图片加载 + 图片压缩
- test: /\.(png|svg|jpg|gif)$/,
- loaders: [
- "file-loader",
- {
- loader: "image-webpack-loader",
- query: {
- progressive: true,
- pngquant: {
- quality: "65-90",
- speed: 4
- }
- }
- }
- ]
-}
-```
-
-> :flashlight: **示例 DEMO07:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo07/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo07))
-
-### 加载字体
-
-那么,像字体这样的其他资源如何处理呢?file-loader 和 url-loader 可以接收并加载任何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文件,包括字体:
-
-**webpack.config.js**
-
-```js
-module.exports = {
- //...
- module: {
- rules: [
- {
- test: /\.(woff|woff2|eot|ttf|svg)$/,
- use: ["file-loader"]
- }
- ]
- }
- //...
-};
-```
-
-一切就绪后,你可以在 css 文件中这样引入字体:
-
-```css
-@font-face {
- font-family: "MyDiyFont";
- src: url("./font/iconfont.eot"); /* IE9*/
- src: url("./font/iconfont.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
- url("./font/iconfont.woff") format("woff"),
- /* chrome、firefox */ url("./font/iconfont.ttf") format("truetype"), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
- url("./font/iconfont.svg#iconfont") format("svg"); /* iOS 4.1- */
-}
-
-h1 {
- font-family: "MyDiyFont";
- font-size: 24px;
-}
-
-p {
- font-family: "MyDiyFont";
- font-size: 18px;
-}
-```
-
-然后,相对路径,会被替换为构建目录中的完整路径/文件名。
-
-> :flashlight: **示例 DEMO08:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo08/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo08))
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Webpack 入门](webpack.md)
-> - [Webpack 概念](concept.md)
-> - [Webpack 资源管理](asset-management.md)
-> - [Webpack 代码分离](code-splitting.md)
-> - [Webpack 开发工具](development.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方资料**
- - [Webpack 官网](https://webpack.js.org/)
- - [Webpack 中文网](https://webpack.docschina.org/)
- - [webpack Github](https://github.com/webpack/webpack)
- - [webpack-dev-server Github](https://github.com/webpack/webpack-dev-server)
- - [webpack-dev-server 官方文档](http://webpack.github.io/docs/webpack-dev-server.html)
-- **入门资料**
- - [webpack-demos](https://github.com/ruanyf/webpack-demos)
- - [webpack-howto](https://github.com/petehunt/webpack-howto/blob/master/README-zh.md)
- - [webpack-handbook](http://zhaoda.net/webpack-handbook/index.html)
-- **教程**
- - [如何学习 Webpack](webpack-howto.md)
- - [Webpack 概念](concept.md)
- - [Webpack 入门](webpack.md)
- - [Webpack 资源管理](asset-management.md)
- - [Webpack 代码分离](code-splitting.md)
- - [Webpack 开发工具](development.md)
-- **文章**
- - [JavaScript 模块化七日谈](http://huangxuan.me/2015/07/09/js-module-7day/)
- - [前端模块化开发那点历史](https://github.com/seajs/seajs/issues/588)
-- **更多资源**
- - [awesome-webpack-cn](https://github.com/webpack-china/awesome-webpack-cn)
diff --git a/frontend/webpack/code-splitting.md b/frontend/webpack/code-splitting.md
deleted file mode 100644
index 3992bf87d2..0000000000
--- a/frontend/webpack/code-splitting.md
+++ /dev/null
@@ -1,247 +0,0 @@
-# Webpack 代码分离
-
-> :pushpin: **提示:**
->
-> 1. 版本问题
->
-> 本文基于 `webpack 2.x` 版本。`webpack 2.x` 相比 `webpack 1.x` 有重大改变。所以,如果你的项目中已使用了 webpack 1.x ,本教程的示例将不适用,请慎重。
->
-> 如果铁了心要升级 webpack ,请参考 [webpack 官方文档 - 从 v1 迁移到 v2](https://doc.webpack-china.org/guides/migrating/)
->
-> 2) 阅读建议
->
-> 阅读本文前,建议先阅读 [Webpack 概念](https://github.com/dunwu/frontend-tutorial/blob/master/docs/chapter03/webpack/concept.md) 。
-
-代码分离是 webpack 中最引人注目的特性之一。
-
-你可以把你的代码分离到不同的 bundle 中,然后你就可以去按需加载这些文件。
-
-总的来说, `webpack` 分离可以分为两类:
-
-- 资源分离
-- 代码分离
-
-## 资源分离(Resource Splitting)
-
-对第三方库(vendor) 和 CSS 进行代码分离,这些方式有助于实现缓存和并行加载。
-
-### 分离 CSS(CSS Splitting)
-
-你可能也想将你的样式代码分离到单独的 bundle 中,以此使其独立于应用程序逻辑。这加强了样式的可缓存性,并且使得浏览器能够并行加载应用程序代码中的样式文件,避免 FOUC 问题 ([无样式内容造成的闪烁](https://en.wikipedia.org/wiki/Flash_of_unstyled_content))。
-
-安装 [`ExtractTextWebpackPlugin`](https://doc.webpack-china.org/plugins/extract-text-webpack-plugin) 如下
-
-```bash
-$ npm install --save-dev extract-text-webpack-plugin
-```
-
-webpack.config.js 中需要按下面进行配置:
-
-```js
-const ExtractTextPlugin = require("extract-text-webpack-plugin");
-
-module.exports = {
- // 关于模块配置
- module: {
- // 模块规则(配置 loader、解析器等选项)
- rules: [
- {
- // css 加载
- test: /\.css$/,
- use: ExtractTextPlugin.extract({
- use: "css-loader"
- })
- }
- ]
- },
-
- // 附加插件列表
- plugins: [
- // 将样式文件独立打包
- new ExtractTextPlugin("styles.css")
- ]
-};
-```
-
-> :flashlight: **示例 DEMO09:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo09/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo09))
-
-### 分离第三方库(Vendor Code Splitting)
-
-一个典型的应用程序,由于框架/功能性需求,会依赖于许多第三方库的代码。不同于应用程序代码,这些第三方库代码不会频繁修改。
-
-如果我们将这些库(library)中的代码,保留在与应用程序代码相独立的 bundle 中,我们就可以利用浏览器缓存机制,把这些文件长时间地缓存在用户机器上。
-
-为了完成这个目标,不管应用程序代码如何变化,vendor 文件名中的 `hash` 部分必须保持不变。学习如何使用 `CommonsChunkPlugin` [分离 vendor/library](https://doc.webpack-china.org/guides/code-splitting-libraries) 代码。
-
-webpack.config.js
-
-```js
-const webpack = require("webpack");
-
-module.exports = {
- // 这里应用程序开始执行
- // webpack 开始打包
- // 本例中 entry 为多入口
- entry: {
- main: "./app/index",
- vendor: "react"
- },
-
- // webpack 如何输出结果的相关选项
- output: {
- // 所有输出文件的目标路径
- // 必须是绝对路径(使用 Node.js 的 path 模块)
- path: path.resolve(__dirname, "dist"),
-
- // 「入口分块(entry chunk)」的文件名模板(出口分块?)
- // filename: "bundle.min.js",
- // filename: "[name].js", // 用于多个入口点(entry point)(出口点?)
- // filename: "[chunkhash].js", // 用于长效缓存
- filename: "[name].[chunkhash:8].js" // 用于长效缓存
- },
-
- // 附加插件列表
- plugins: [
- new webpack.optimize.CommonsChunkPlugin({
- name: "vendor" // 指定公共 bundle 的名字。
- })
- ]
-};
-```
-
-在上面的配置中,
-
-1. 在 `entry` 属性中,将 react 指定为一个独立的入口 **vendor**;
-2. 然后,在 `output` 属性中,将 **filename** 指定为 [name].[chunkhash:8].js,这表示输出文件的文件名样式。
-3. 最后在 `plugins` 列表中引入 **CommonsChunkPlugin** 插件,用来指定 bundle 。
-
-执行 webpack 命令后,webpack 会生成 2 个 bundle 文件,形式如:
-
-```
-main.bef8f974.js
-vendor.2f1bd4c8.js
-```
-
-> :flashlight: **示例 DEMO10:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo10/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo10))
-
-## 代码按需分离(On Demand Code Splitting)
-
-虽然前面几类资源分离,需要用户预先在配置中指定分离模块,但也可以在应用程序代码中创建动态分离模块。
-
-这可以用于更细粒度的代码块,例如,根据我们的应用程序路由,或根据用户行为预测。这可以使用户按照实际需要加载非必要资源。
-
-前一节,我们了解了 webpack 可以将资源拆分为 bundle。接下来,我们要学习如何异步加载。例如,这允许首先提供最低限度的引导 bundle,并在稍后再异步地加载其他功能。
-
-webpack 支持两种相似的技术实现此目的:使用 `import()` (推荐,ECMAScript 提案) 和 `require.ensure()` (遗留,webpack 特定)。本文只介绍官方推荐的 `import()` 方式。
-
-[ES2015 loader 规范](https://whatwg.github.io/loader/)定义了 `import()` 作为一种在运行时(runtime)动态载入 ES2015 模块的方法。
-
-webpack 把 `import()` 作为一个分离点(split-point),并把引入的模块作为一个单独的 chunk。 `import()` 将模块名字作为参数并返回一个 `Promoise` 对象,即 `import(name) -> Promise`
-
-### 配合 Babel 使用
-
-如果你想要在 [Babel](http://babeljs.io/) 中使用 `import`,但是由于 import() 还是属于 Stage 3 的特性,所以你需要安装/添加 [`syntax-dynamic-import`](http://babeljs.io/docs/plugins/syntax-dynamic-import/) 插件来避免 parser 报错。在草案正式成为规范后,就不再需要这个插件了。
-
-例:
-
-我们来定义一个 Clock 组件,动态引入 moment 库。
-
-首先,安装 moment 库。
-
-```bash
-$ npm install --save-dev moment
-```
-
-JavaScript 代码:
-
-```js
-class Clock extends React.Component {
- constructor(props) {
- super(props);
- this.state = { date: new Date().toLocaleDateString() };
- this.click = this.click.bind(this);
- }
-
- click() {
- // 动态引入import()
- import("moment")
- .then(moment => moment().format("MMMM Do YYYY, h:mm:ss a"))
- .then(str => this.setState({ date: str }))
- .catch(err => console.log("Failed to load moment", err));
- }
-
- render() {
- return (
-
-
It is {this.state.date}.
-
Click here to changing the time.
-
- );
- }
-}
-```
-
-webpack.config.js
-
-```js
-// 关于模块配置
-module: {
-
- // 模块规则(配置 loader、解析器等选项)
- rules: [
- {
- // 语义解释器,将 js/jsx 文件中的 es2015/react 语法自动转为浏览器可识别的 Javascript 语法
- test: /\.jsx?$/,
- include: path.resolve(__dirname, "app"),
-
- // 应该应用的 loader,它相对上下文解析
- // 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
- // 查看 webpack 1 升级指南。
- loader: "babel-loader",
-
- // loader 的可选项
- options: {
- presets: ["es2015", "react"],
- plugins: ['syntax-dynamic-import']
- },
- },
- ]
-},
-```
-
-> :flashlight: **示例 DEMO11:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo11/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo11))
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Webpack 入门](webpack.md)
-> - [Webpack 概念](concept.md)
-> - [Webpack 资源管理](asset-management.md)
-> - [Webpack 代码分离](code-splitting.md)
-> - [Webpack 开发工具](development.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方资料**
- - [Webpack 官网](https://webpack.js.org/)
- - [Webpack 中文网](https://webpack.docschina.org/)
- - [webpack Github](https://github.com/webpack/webpack)
- - [webpack-dev-server Github](https://github.com/webpack/webpack-dev-server)
- - [webpack-dev-server 官方文档](http://webpack.github.io/docs/webpack-dev-server.html)
-- **入门资料**
- - [webpack-demos](https://github.com/ruanyf/webpack-demos)
- - [webpack-howto](https://github.com/petehunt/webpack-howto/blob/master/README-zh.md)
- - [webpack-handbook](http://zhaoda.net/webpack-handbook/index.html)
-- **教程**
- - [如何学习 Webpack](webpack-howto.md)
- - [Webpack 概念](concept.md)
- - [Webpack 入门](webpack.md)
- - [Webpack 资源管理](asset-management.md)
- - [Webpack 代码分离](code-splitting.md)
- - [Webpack 开发工具](development.md)
-- **文章**
- - [JavaScript 模块化七日谈](http://huangxuan.me/2015/07/09/js-module-7day/)
- - [前端模块化开发那点历史](https://github.com/seajs/seajs/issues/588)
-- **更多资源**
- - [awesome-webpack-cn](https://github.com/webpack-china/awesome-webpack-cn)
diff --git a/frontend/webpack/concept.md b/frontend/webpack/concept.md
deleted file mode 100644
index 7bab4941e5..0000000000
--- a/frontend/webpack/concept.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# Webpack 概念
-
-*webpack* 是一个现代的 JavaScript 应用程序的*模块打包器(module bundler)*。当 webpack 处理应用程序时,它会递归地构建一个*依赖关系图表(dependency graph)*,其中包含应用程序需要的每个模块,然后将所有这些模块打包成少量的 *bundle* - 通常只有一个,由浏览器加载。
-
-学习 webpack,需要先了解几个核心概念,下面会一一道来。
-
-
-
-## 模块化(module)
-
-在[模块化编程](https://en.wikipedia.org/wiki/Modular_programming)中,开发者将程序分解相对独立的代码块,并称之为*模块*。
-
-每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的*模块*提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。
-
-Node.js 从最一开始就支持模块化编程。然而,在 web,*模块化*的支持正缓慢到来。在 web 存在多种支持 JavaScript 模块化的工具,这些工具各有优势和限制。webpack 基于从这些系统获得的经验教训,并将*模块*的概念应用于项目中的任何文件。
-
-### 什么是 webpack 模块
-
-对比 [Node.js 模块](https://nodejs.org/api/modules.html),webpack *模块*能够以各种方式表达它们的依赖关系,几个例子如下:
-
-- [ES2015 `import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) 语句
-- [CommonJS](http://www.commonjs.org/specs/modules/1.0/) `require()` 语句
-- [AMD](https://github.com/amdjs/amdjs-api/blob/master/AMD.md) `define` 和 `require` 语句
-- css/sass/less 文件中的 [`@import` 语句](https://developer.mozilla.org/en-US/docs/Web/CSS/@import)。
-- 样式(`url(...)`)或 HTML 文件(``)中的图片链接(image url)
-
-> webpack 1 需要特定的 loader 来转换 ES 2015 `import`,然而 webpack 2 天然支持。
-
-### 支持的模块类型
-
-webpack 通过 *loader* 可以支持各种语言和预处理器编写模块。*loader* 描述了 webpack **如何**处理 非 JavaScript(non-JavaScript) *模块*,并且在*bundle*中引入这些*依赖*。 webpack 社区已经为各种流行语言和语言处理器构建了 *loader*,包括:
-
-- [CoffeeScript](http://coffeescript.org/)
-- [TypeScript](https://www.typescriptlang.org/)
-- [ESNext (Babel)](https://babeljs.io/)
-- [Sass](http://sass-lang.com/)
-- [Less](http://lesscss.org/)
-- [Stylus](http://stylus-lang.com/)
-
-总的来说,webpack 提供了可定制的、强大和丰富的 API,允许**任何技术栈**使用 webpack,保持了在你的开发、测试和生成流程中**无侵入性(non-opinionated)**。
-
-## 配置文件 - webpack.config.js
-
-> webpack 是高度可配置的,如何模块化打包、加载都可以基于配置文件定制。
->
-> webpack 的默认配置文件是 `webpack.config.js`。
-
-因为 webpack 配置是标准的 Node.js CommonJS 模块,你可以使用**如下特性**:
-
-- 通过 `require(...)` 导入其他文件
-- 通过 `require(...)` 使用 npm 的工具函数
-- 使用 JavaScript 控制流表达式,例如 `?:` 操作符
-- 对常用值使用常量或变量
-- 编写并执行函数来生成部分配置
-
-## 依赖图表(Dependency Graph)
-
-任何时候,一个文件依赖于另一个文件,webpack 就把此视为文件之间有*依赖关系*。这使得 webpack 可以接收非代码资源(non-code asset)(例如图像或 web 字体),并且可以把它们作为*依赖*提供给你的应用程序。
-
-webpack 从命令行或配置文件中定义的一个模块列表开始,处理你的应用程序。 从这些*入口起点*开始,webpack 递归地构建一个*依赖图表*,这个依赖图表包含着应用程序所需的每个模块,然后将所有这些模块打包为少量的 *bundle*- 通常只有一个 - 可由浏览器加载。
-
-> 对于 *HTTP/1.1* 客户端,由 webpack 打包你的应用程序会尤其强大,因为在浏览器发起一个新请求时,它能够减少应用程序必须等待的时间。对于 *HTTP/2*,你还可以使用代码拆分(Code Splitting)以及通过 webpack 打包来实现[最佳优化](https://medium.com/webpack/webpack-http-2-7083ec3f3ce6#.7y5d3hz59)。
-
-## 入口(entry)
-
-webpack 将创建所有应用程序的**依赖关系图表(dependency graph)**。图表的起点被称之为*入口起点(entry point)*。*入口起点*告诉 webpack *从哪里开始*,并遵循着依赖关系图表知道*要打包什么*。可以将您应用程序的*入口起点*认为是**根上下文(contextual root)**或 **app 第一个启动文件**。
-
-在 webpack 中,我们使用 [webpack 配置对象(webpack configuration object)](https://doc.webpack-china.org/configuration) 中的 `entry` 属性来定义*入口*。
-
-例:
-
-```js
-module.exports = {
- entry: "./path/to/my/entry/file.js"
-};
-```
-
-## 输出(output)
-
-将所有的资源(assets)归拢在一起后,还需要告诉 webpack **在哪里**打包应用程序。webpack 的 `output` 属性描述了**如何处理归拢在一起的代码**(bundled code)。
-
-例:
-
-```js
-const path = require("path");
-
-module.exports = {
- entry: "./path/to/my/entry/file.js",
- output: {
- path: path.resolve(__dirname, "dist"),
- filename: "my-first-webpack.bundle.js"
- }
-};
-```
-
-## 加载(loader)
-
-webpack 的目标是,让 **webpack** 聚焦于项目中的所有资源(asset),而浏览器不需要关注考虑这些(这并不意味着资源(asset)都必须打包在一起)。webpack 把[每个文件(.css, .html, .scss, .jpg, etc.) 都作为模块](https://doc.webpack-china.org/concepts/modules)处理。然而 webpack **只理解 JavaScript**。
-
-**webpack loader 会将这些文件转换为模块,而转换后的文件会被添加到依赖图表中。**
-
-在更高层面,webpack 的配置有两个目标。
-
-1. 识别出(identify)应该被对应的 loader 进行转换(transform)的那些文件
-
-2. 由于进行过文件转换,所以能够将被转换的文件添加到依赖图表(并且最终添加到 bundle 中)(`use` 属性)
-
-例:
-
-```js
-const path = require("path");
-
-const config = {
- entry: "./path/to/my/entry/file.js",
- output: {
- path: path.resolve(__dirname, "dist"),
- filename: "my-first-webpack.bundle.js"
- },
- module: {
- rules: [{ test: /\.(js|jsx)$/, use: "babel-loader" }]
- }
-};
-
-module.exports = config;
-```
-
-## 插件(plugins)
-
-由于 loader 仅在每个文件的基础上执行转换,而 `插件(plugins)` 最常用于(但不限于)在打包模块的“compilation”和“chunk”生命周期执行操作和自定义功能[(查看更多)](https://doc.webpack-china.org/concepts/plugins)。webpack 的插件系统[极其强大和可定制化](https://doc.webpack-china.org/api/plugins)。
-
-想要使用一个插件,你只需要 `require()` 它,然后把它添加到 `plugins` 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,你需要使用 `new` 创建实例来调用它。
-
-例:
-
-```js
-const HtmlWebpackPlugin = require("html-webpack-plugin"); //installed via npm
-const webpack = require("webpack"); //to access built-in plugins
-const path = require("path");
-
-const config = {
- entry: "./path/to/my/entry/file.js",
- output: {
- path: path.resolve(__dirname, "dist"),
- filename: "my-first-webpack.bundle.js"
- },
- module: {
- rules: [{ test: /\.(js|jsx)$/, use: "babel-loader" }]
- },
- plugins: [
- new webpack.optimize.UglifyJsPlugin(),
- new HtmlWebpackPlugin({ template: "./src/index.html" })
- ]
-};
-
-module.exports = config;
-```
-
-## 热替换(Hot Module Replacement)
-
-模块热替换功能会在应用程序运行过程中替换、添加或删除[模块](https://doc.webpack-china.org/concepts/modules/),而无需重新加载页面。这使得你可以在独立模块变更后,无需刷新整个页面,就可以更新这些模块,极大地加速了开发时间。
-
-### 这一切是如何运行的?
-
-### 站在 App 的角度
-
-1. app 代码要求 HMR runtime 检查更新。
-2. HMR runtime (异步)下载更新,然后通知 app 代码更新可用。
-3. app 代码要求 HMR runtime 应用更新。
-4. HMR runtime (异步)应用更新。
-
-你可以设置 HMR,使此进程自动触发更新,或者你可以选择要求在用户交互后进行更新。
-
-#### 站在编译器(webpack) 的角度
-
-除了普通资源,编译器(compiler)需要发出 "update",以允许更新之前的版本到新的版本。"update" 由两部分组成:
-
-1. 待更新 manifest (JSON)
-2. 一个或多个待更新 chunk (JavaScript)
-
-manifest 包括新的编译 hash 和所有的待更新 chunk 目录。
-
-每个待更新 chunk 包括用于与所有被更新模块相对应 chunk 的代码(或一个 flag 用于表明模块要被移除)。
-
-编译器确保模块 ID 和 chunk ID 在这些构建之间保持一致。通常将这些 ID 存储在内存中(例如,当使用 [webpack-dev-server](https://doc.webpack-china.org/configuration/dev-server/) 时),但是也可能将它们存储在一个 JSON 文件中。
-
-#### 站在模块的角度
-
-HMR 是可选功能,只会影响包含 HMR 代码的模块。举个例子,通过 [`style-loader`](https://github.com/webpack/style-loader) 为 style 样式追加补丁。 为了运行追加补丁,`style-loader` 实现了 HMR 接口;当它通过 HMR 接收到更新,它会使用新的样式替换旧的样式。
-
-类似的,当在一个模块中实现了 HMR 接口,你可以描述出当模块被更新后发生了什么。然而在多数情况下,不需要强制在每个模块中写入 HMR 代码。如果一个模块没有 HMR 处理函数,更新就会冒泡。这意味着一个简单的处理函数能够对整个模块树(complete module tree)进行处理。如果在这个模块树中,一个单独的模块被更新,那么整个模块树都会被重新加载(只会重新加载,不会迁移)。
-
-#### 站在 HMR Runtime 的角度 (Technical)
-
-对于模块系统的 runtime,附加的代码被发送到 `parents` 和 `children` 跟踪模块。
-
-在管理方面,runtime 支持两个方法 `check` 和 `apply`。
-
-`check` 发送 HTTP 请求来更新 manifest。如果请求失败,说明没有可用更新。如果请求成功,待更新 chunk 会和当前加载过的 chunk 进行比较。对每个加载过的 chunk,会下载相对应的待更新 chunk。当所有待更新 chunk 完成下载,就会准备切换到 `ready` 状态。
-
-`apply` 方法将所有被更新模块标记为无效。对于每个无效模块,都需要在模块中有一个更新处理函数,或者在它的父级模块们中有更新处理函数。否则,无效标记冒泡,并将父级也标记为无效。每个冒泡继续直到到达应用程序入口起点,或者到达带有更新处理函数的模块(以最先到达为准)。如果它从入口起点开始冒泡,则此过程失败。
-
-之后,所有无效模块都被(通过 dispose 处理函数)处理和解除加载。然后更新当前 hash,并且调用所有 "accept" 处理函数。runtime 切换回`闲置`状态,一切照常继续。
-
-### 产生的文件 (Technical)
-
-左侧表示初始编译器通过。右侧表示更新了模块 4 和 9 。
-
-
-
-### 它能够用于?
-
-你可以在开发过程中将 HMR 作为 LiveReload 的替代。[webpack-dev-server](https://doc.webpack-china.org/configuration/dev-server/) 支持热模式,在试图重新加载整个页面之前,热模式会尝试使用 HMR 来更新。查看如何实现[在 React 项目中使用 HMR](https://doc.webpack-china.org/guides/hmr-react) 为例。
-
-一些 loader 已经生成可热更新的模块。例如,`style-loader` 能够置换出页面的样式表。对于这样的模块,你不需要做任何特殊处理。
-
-webpack 的强大之处在于它的可定制化,取决于特定项目需求,这里有*许多*配置 HMR 的方式。
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Webpack 入门](webpack.md)
-> - [Webpack 概念](concept.md)
-> - [Webpack 资源管理](asset-management.md)
-> - [Webpack 代码分离](code-splitting.md)
-> - [Webpack 开发工具](development.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方资料**
- - [Webpack 官网](https://webpack.js.org/)
- - [Webpack 中文网](https://webpack.docschina.org/)
- - [webpack Github](https://github.com/webpack/webpack)
- - [webpack-dev-server Github](https://github.com/webpack/webpack-dev-server)
- - [webpack-dev-server 官方文档](http://webpack.github.io/docs/webpack-dev-server.html)
-- **入门资料**
- - [webpack-demos](https://github.com/ruanyf/webpack-demos)
- - [webpack-howto](https://github.com/petehunt/webpack-howto/blob/master/README-zh.md)
- - [webpack-handbook](http://zhaoda.net/webpack-handbook/index.html)
-- **教程**
- - [如何学习 Webpack](webpack-howto.md)
- - [Webpack 概念](concept.md)
- - [Webpack 入门](webpack.md)
- - [Webpack 资源管理](asset-management.md)
- - [Webpack 代码分离](code-splitting.md)
- - [Webpack 开发工具](development.md)
-- **文章**
- - [JavaScript 模块化七日谈](http://huangxuan.me/2015/07/09/js-module-7day/)
- - [前端模块化开发那点历史](https://github.com/seajs/seajs/issues/588)
-- **更多资源**
- - [awesome-webpack-cn](https://github.com/webpack-china/awesome-webpack-cn)
diff --git a/frontend/webpack/development.md b/frontend/webpack/development.md
deleted file mode 100644
index 0dd7222aca..0000000000
--- a/frontend/webpack/development.md
+++ /dev/null
@@ -1,298 +0,0 @@
-# Webpack 开发工具
-
-> :warning: **注意:**
-> 永远不要在生产环境中使用这些工具,永远不要。
-
-## devtool
-
-当 JavaScript 异常抛出时,你常会想知道这个错误发生在哪个文件的哪一行。然而因为 webpack 将文件输出为一个或多个 bundle,所以 追踪这一错误会很不方便。
-
-**Source maps** 试图解决这一问题。它有很多选择,各有优劣:
-
-| devtool | build | rebuild | production | quality |
-| ---------------------------- | ----- | ------- | ---------- | ----------------------------- |
-| eval | +++ | +++ | no | generated code |
-| cheap-eval-source-map | + | ++ | no | transformed code (lines only) |
-| cheap-source-map | + | o | yes | transformed code (lines only) |
-| cheap-module-eval-source-map | o | ++ | no | original source (lines only) |
-| cheap-module-source-map | o | - | yes | original source (lines only) |
-| eval-source-map | -- | + | no | original source |
-| source-map | -- | -- | yes | original source |
-| nosources-source-map | -- | -- | yes | without source content |
-
-> `+` 表示较快,`-` 表示较慢,`o` 表示时间相同
-
-对于开发环境,通常希望更快速的 Source Map,需要添加到 bundle 中以增加体积为代价,但是对于生产环境,则希望更精准的 Source Map,需要从 bundle 中分离并独立存在。
-
-个人建议:开发环境使用 `cheap-module-eval-source-map` ;开发环境使用 `cheap-module-source-map` 。
-
-使用方式非常简单,在 `webpack.config.js` 中配置如下:
-
-```js
-module.exports = {
- // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
- // devtool: "eval", // 没有模块映射,而是命名模块。以牺牲细节达到最快。
- // devtool: "source-map", // 牺牲了构建速度的 `source-map' 是最详细的
- // devtool: "inline-source-map", // 嵌入到源文件中
- // devtool: "eval-source-map", // 将 SourceMap 嵌入到每个模块中
- // devtool: "hidden-source-map", // SourceMap 不在源文件中引用
- // devtool: "cheap-source-map", // 没有模块映射(module mappings)的 SourceMap 低级变体(cheap-variant)
- // devtool: "cheap-module-source-map", // 有模块映射(module mappings)的 SourceMap 低级变体
- devtool: "cheap-module-eval-source-map"
-};
-```
-
-## webpack-dev-server
-
-**webpack-dev-server** 可以提供了一个服务器和实时重载(live reloading) 功能。
-
-在开始前,确定你有一个 `index.html` 文件指向你的 bundle。假设 `output.filename` 是 `bunlde.js`。
-
-```html
-
-
-
-
-
-```
-
-首先从 npm 安装 `webpack-dev-server`:
-
-```bash
-$ npm install --save-dev webpack-dev-server
-```
-
-安装完成之后,你应该可以使用 `webpack-dev-server` 了,方式如下:
-
-```bash
-$ webpack-dev-server --open
-```
-
-上述命令应该自动在浏览器中打开 `http://localhost:8080`。
-
-> :pushpin: **提示:**
->
-> 本教程中的 [**示例代码**](https://github.com/dunwu/frontend-tutorial/tree/master/codes) 除了 `demo00` ,都可以使用 webpack-dev-server 命令启动服务。
-
-在你的文件中做一点更改并且保存。你应该可以在控制台中看到正在编译。编译完成之后,页面应该会刷新。如果控制台中什么都没发生,你可能需要调整下 [`watchOptions`](https://doc.webpack-china.org/configuration/dev-server#devserver-watchoptions-)。
-
-默认情况下 webpack 会使用**inline mode**(内联模式)。这种模式在你的 bundle 中注入客户端(用来 live reloading 和展示构建错误)。Inline 模式下,你会在你的 DevTools 控制台中看到构建错误。
-
-**webpack-dev-server** 可以做很多事情,比如转发请求到你的后端服务器。
-
-**webpack-dev-server** 支持很多 cli 参数,来手动配置服务的选项。
-
-但是,个人建议,一种更好的做法是在 `webpack.config.js` 文件中通过配置 `devServer` 属性来配置 webpack-dev-server 。
-
-更多配置项参考:[官方文档 - 开发中 Server(DevServer)](https://doc.webpack-china.org/configuration/dev-server/)
-
-> :flashlight: **示例 DEMO12:** ([**DEMO**](https://dunwu.github.io/frontend-tutorial/chapter03/webpack2/demo12/dist/index.html) / [**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo12))
->
-> 在本示例中,devServer 配置如下:
->
-> ```
-> devServer: {
-> contentBase: [path.join(__dirname, "dist")],
-> compress: true,
-> port: 9000, // 启动端口号
-> inline: true,
-> }
-> ```
->
-> 执行 webpack-dev-server 后,会启动一个端口为 9000 的本地服务。
-
-### 热模块替换(Hot Module Replacement)
-
-> :pushpin: **提示:**
->
-> 模块热替换功能一般用于开发环境。
-
-现在你有了实时重载功能,你甚至可以更进一步:Hot Module Replacement(热模块替换)。这是一个接口,使得你可以替换模块**而不需要刷新页面**。不用每次修改都重新启动服务,这可以极大地提高开发效率。
-
-那么,如何配置 webpack 来实现热替换呢?
-
-请按以下步骤一步步来:
-
-首先,安装依赖 `react-hot-loader`(确保使用这个包的 `next` 版本)
-
-```bash
-$ npm install --save babel-loader react-hot-loader@next
-```
-
-**配置 entry**
-
-你需要定义几个用于热替换的入口路径
-
-```js
-entry: {
- main: [
- // App 入口
- "./app/index",
-
- // 开启 React 代码的模块热替换(HMR)
- 'react-hot-loader/patch',
-
- // 为 webpack-dev-server 的环境打包代码
- // 然后连接到指定服务器域名与端口
- 'webpack-dev-server/client?http://localhost:9000',
-
- // 为热替换(HMR)打包好代码
- // only- 意味着只有成功更新运行代码才会执行热替换(HMR)
- 'webpack/hot/only-dev-server',
- ],
-},
-```
-
-**配置 output**
-
-`publicPath` 对于热替换(HMR)是必须的,让 webpack 知道在哪里载入热更新的模块(chunk)
-
-```js
-output: {
- ... ...
- // 对于热替换(HMR)是必须的,让 webpack 知道在哪里载入热更新的模块(chunk)
- publicPath: "/"
-},
-```
-
-**配置 module**
-
-需要使用 ES2015 模块来使 HMR 正常工作。为此,在我们的 es2015 preset 设置中,将 `module` 选项设置为 false。
-
-并且,在此要引入 `react-hot-loader/babel` 开启 React 代码的模块热替换(HMR)
-
-```js
-module: {
-
- rules: [
- {
- // 语义解释器,将 js/jsx 文件中的 es2015/react 语法自动转为浏览器可识别的 Javascript 语法
- test: /\.jsx?$/,
- include: path.resolve(__dirname, "app"),
- exclude: /node_modules/,
-
- // 应该应用的 loader,它相对上下文解析
- // 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
- // 查看 webpack 1 升级指南。
- loader: "babel-loader",
-
- // loader 的可选项
- options: {
- presets: [
- // webpack 现在已经支持原生的 import 语句了, 并且将其运用在 tree-shaking 特性上
- [
- "es2015",
- {
- "modules": false
- }
- ],
-
- "react" // 转译 React 组件为 JavaScript 代码
- ],
- plugins: [
- "react-hot-loader/babel" // 开启 React 代码的模块热替换(HMR)
- ]
- },
- },
- ]
- },
-```
-
-**配置 devServer**
-
-此处,也需要引入 `publicPath` ,且和上文 `output` 的 `publicPath` 值保持一致。
-
-`hot` 属性需要置为 true,表示开启服务器的模块热替换。
-
-```js
-devServer: {
- contentBase: [path.join(__dirname, "dist")],
- compress: true,
- port: 9000, // 启动端口号
- hot: true, // 启用 webpack 的模块热替换特性
- inline: true,
- publicPath: "/", // 和上文 output 的“publicPath”值保持一致
-}
-```
-
-**配置 plugins**
-
-最后,需要开启 webpack 自带的 `HotModuleReplacementPlugin` 和 `NamedModulesPlugin` 插件,启动热替换功能。
-
-```js
-plugins: [
- // 开启全局的模块热替换(HMR)
- new webpack.HotModuleReplacementPlugin(),
-
- // 当模块热替换(HMR)时在浏览器控制台输出对用户更友好的模块名字信息
- new webpack.NamedModulesPlugin(),
-],
-```
-
-> :flashlight: **示例 DEMO13:** ([**SOURCE**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo13))
->
-> 在示例中,启动服务后,打开浏览器,访问 http://localhost:9000/ 。
->
-> 按快捷键 `F12` 打开浏览器调试窗口,可以看到类似提示信息
->
-> ```
-> [HMR] Waiting for update signal from WDS...
-> [HMR] Waiting for update signal from WDS...
-> [WDS] Hot Module Replacement enabled.
-> ```
->
-> 这表示热替换功能已启动。
->
-> 修改 `app/index.jsx` 文件,来看看热替换的效果:
->
-> 修改前:
->
-> ```js
-> ReactDOM.render(
-> ,
-> document.getElementById("root")
-> );
-> ```
->
-> 修改后:
->
-> ```js
-> ReactDOM.render( , document.getElementById("root"));
-> ```
->
-> 此时,应该看到页面内容会替换为你修改的内容。
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Webpack 入门](webpack.md)
-> - [Webpack 概念](concept.md)
-> - [Webpack 资源管理](asset-management.md)
-> - [Webpack 代码分离](code-splitting.md)
-> - [Webpack 开发工具](development.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方资料**
- - [Webpack 官网](https://webpack.js.org/)
- - [Webpack 中文网](https://webpack.docschina.org/)
- - [webpack Github](https://github.com/webpack/webpack)
- - [webpack-dev-server Github](https://github.com/webpack/webpack-dev-server)
- - [webpack-dev-server 官方文档](http://webpack.github.io/docs/webpack-dev-server.html)
-- **入门资料**
- - [webpack-demos](https://github.com/ruanyf/webpack-demos)
- - [webpack-howto](https://github.com/petehunt/webpack-howto/blob/master/README-zh.md)
- - [webpack-handbook](http://zhaoda.net/webpack-handbook/index.html)
-- **教程**
- - [如何学习 Webpack](webpack-howto.md)
- - [Webpack 概念](concept.md)
- - [Webpack 入门](webpack.md)
- - [Webpack 资源管理](asset-management.md)
- - [Webpack 代码分离](code-splitting.md)
- - [Webpack 开发工具](development.md)
-- **文章**
- - [JavaScript 模块化七日谈](http://huangxuan.me/2015/07/09/js-module-7day/)
- - [前端模块化开发那点历史](https://github.com/seajs/seajs/issues/588)
-- **更多资源**
- - [awesome-webpack-cn](https://github.com/webpack-china/awesome-webpack-cn)
diff --git a/frontend/webpack/webpack.md b/frontend/webpack/webpack.md
deleted file mode 100644
index aeb9f4da29..0000000000
--- a/frontend/webpack/webpack.md
+++ /dev/null
@@ -1,222 +0,0 @@
-# Webpack 入门
-
-> [Webpack](https://webpack.github.io/) 是一个模组打包工具(module bundler)。其主要目的是将 JavaScript 文件捆绑在浏览器中,但它也能够转换,捆绑或打包任何资源文件。
->
-> Webpack 可以按需加载应用程序的组件。使得 Javascript 应用可以高度复用。
->
-> 当前版本:4.x
->
-> 关键词: webpack, loader
-
-
-
-- [安装](#安装)
- - [本地安装](#本地安装)
- - [全局安装](#全局安装)
-- [创建一个 bundle 文件](#创建一个-bundle-文件)
-- [webpack.config.js](#webpackconfigjs)
- - [单入口(Entry)](#单入口entry)
- - [多入口(Entry)](#多入口entry)
-- [引申和引用](#引申和引用)
-
-
-
-## 安装
-
-### 本地安装
-
-```bash
-$ npm install --save-dev webpack
-$ npm install --save-dev webpack@
-```
-
-如果你在项目中使用了 `npm` ,`npm` 首先会在本地模块中寻找 `webpack`。这是一个实用的小技巧。
-
-```json
-"scripts": {
- "start": "webpack --config mywebpack.config.js"
-}
-```
-
-上面是 `npm` 的标准配置,也是我们推荐的实践。
-
-> 当你在本地安装 webpack 后,你能够从 `node_modules/.bin/webpack` 访问它的 bin 版本。
-
-### 全局安装
-
-```bash
-$ npm install --global webpack
-```
-
-`webpack` 命令现在可以全局执行了。
-
-## 创建一个 bundle 文件
-
-创建一个 `app/index.js` 文件。
-
-```js
-document.write("Hello World ");
-```
-
-创建一个 `index.html` 文件。
-
-```html
-
-
-
-
-
-```
-
-执行命令
-
-```bash
-$ webpack ./app/index.js ./dist/bundle.js
-```
-
-这条命令的第一个参数为输入文件,第二个参数为输出文件。
-
-会在目录下生成一个 `dist/bundle.js` 文件,它已打包所需的所有代码的输出文件。
-
-在浏览器中打开 `index.html` 文件。
-
-> :flashlight: **示例:** ([**DEMO00**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo00))
-
-## webpack.config.js
-
-`webpack.config.js` 为 `webpack` 默认的配置文件,当执行 `webpack` 命令时,`webpack` 会在当前目录下自动搜索 `webpack.config.js` 文件。
-
-### 单入口(Entry)
-
-基于 ([**DEMO00**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo00)) 的代码,新建一个 `webpack.config.js` 文件,内容如下:
-
-```js
-const path = require("path");
-
-module.exports = {
- // 这里应用程序开始执行
- // webpack 开始打包
- entry: "./app/index.js",
-
- // webpack 如何输出结果的相关选项
- output: {
- // 所有输出文件的目标路径
- // 必须是绝对路径(使用 Node.js 的 path 模块)
- path: path.resolve(__dirname, "dist"),
-
- // 「入口分块(entry chunk)」的文件名模板(出口分块?)
- filename: "bundle.js"
- }
-};
-```
-
-执行命令
-
-```bash
-$ webpack
-```
-
-在浏览器中打开 `index.html` 文件。
-
-> :flashlight: **示例:** ([**DEMO01**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo01))
-
-### 多入口(Entry)
-
-如果有多个入口文件怎么办?很简单,我们来看一个示例:
-
-新建 app/about.js 文件
-
-```js
-document.write("ABOUT ");
-```
-
-新建 app/home.js 文件
-
-```js
-document.write("HOME ");
-```
-
-新建 index.html 文件
-
-```html
-
-
-
-
-
-
-```
-
-新建 webpack.config.js 文件
-
-```js
-const path = require("path");
-
-module.exports = {
- // 这里应用程序开始执行
- // webpack 开始打包
- // 本例中 entry 为多入口
- entry: {
- home: "./app/home.js",
- about: "./app/about.js"
- },
-
- // webpack 如何输出结果的相关选项
- output: {
- // 所有输出文件的目标路径
- // 必须是绝对路径(使用 Node.js 的 path 模块)
- path: path.resolve(__dirname, "dist"),
-
- // 「入口分块(entry chunk)」的文件名模板(出口分块?)
- // filename: "bundle.js", // 用于多个入口点(entry point)(出口点?)
- filename: "[name].js" // 用于多个入口点(entry point)(出口点?)
- // filename: "[chunkhash].js", // 用于长效缓存
- // filename: "[name].[chunkhash].js", // 用于长效缓存
- }
-};
-```
-
-执行命令
-
-```bash
-$ webpack
-```
-
-在浏览器中打开 `index.html` 文件。
-
-> :flashlight: **示例:** ([**DEMO02**](https://github.com/dunwu/frontend-tutorial/tree/master/codes/chapter03/webpack2/demo02))
-
-## 引申和引用
-
-> :books: 拓展阅读
->
-> - [Webpack 入门](webpack.md)
-> - [Webpack 概念](concept.md)
-> - [Webpack 资源管理](asset-management.md)
-> - [Webpack 代码分离](code-splitting.md)
-> - [Webpack 开发工具](development.md)
->
-> :package: 本文归档在 [我的前端技术教程系列:frontend-tutorial](https://github.com/dunwu/frontend-tutorial)
-
-- **官方资料**
- - [Webpack 官网](https://webpack.js.org/)
- - [Webpack 中文网](https://webpack.docschina.org/)
- - [webpack Github](https://github.com/webpack/webpack)
- - [webpack-dev-server Github](https://github.com/webpack/webpack-dev-server)
- - [webpack-dev-server 官方文档](http://webpack.github.io/docs/webpack-dev-server.html)
-- **入门资料**
- - [webpack-demos](https://github.com/ruanyf/webpack-demos)
- - [webpack-howto](https://github.com/petehunt/webpack-howto/blob/master/README-zh.md)
- - [webpack-handbook](http://zhaoda.net/webpack-handbook/index.html)
-- **教程**
- - [如何学习 Webpack](webpack-howto.md)
- - [Webpack 概念](concept.md)
- - [Webpack 入门](webpack.md)
- - [Webpack 资源管理](asset-management.md)
- - [Webpack 代码分离](code-splitting.md)
- - [Webpack 开发工具](development.md)
-- **文章**
- - [JavaScript 模块化七日谈](http://huangxuan.me/2015/07/09/js-module-7day/)
- - [前端模块化开发那点历史](https://github.com/seajs/seajs/issues/588)
-- **更多资源**
- - [awesome-webpack-cn](https://github.com/webpack-china/awesome-webpack-cn)
diff --git "a/minds/\344\272\272\346\226\207/\345\206\233\344\272\213/README.md" "b/minds/\344\272\272\346\226\207/\345\206\233\344\272\213/README.md"
deleted file mode 100644
index 6babbd4c48..0000000000
--- "a/minds/\344\272\272\346\226\207/\345\206\233\344\272\213/README.md"
+++ /dev/null
@@ -1,7 +0,0 @@
-# 军事
-
-> 军事是与战争、军队、军人等有关事务的总称。
-
-## 关键词
-
-战争、战役、战斗、战士、战略、战术、武器、兵种、进攻、防御、指挥、兵法、军衔
diff --git "a/minds/\344\272\272\346\226\207/\345\206\233\344\272\213/\345\206\233\344\272\213.xmind" "b/minds/\344\272\272\346\226\207/\345\206\233\344\272\213/\345\206\233\344\272\213.xmind"
deleted file mode 100644
index 81033304ad..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\345\206\233\344\272\213/\345\206\233\344\272\213.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\345\216\206\345\217\262/\344\270\226\347\225\214\345\217\262.xmind" "b/minds/\344\272\272\346\226\207/\345\216\206\345\217\262/\344\270\226\347\225\214\345\217\262.xmind"
deleted file mode 100644
index 32cf3abf17..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\345\216\206\345\217\262/\344\270\226\347\225\214\345\217\262.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\345\216\206\345\217\262/\344\270\255\345\233\275\345\217\244\344\273\243\345\217\262.xmind" "b/minds/\344\272\272\346\226\207/\345\216\206\345\217\262/\344\270\255\345\233\275\345\217\244\344\273\243\345\217\262.xmind"
deleted file mode 100644
index 55e240a454..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\345\216\206\345\217\262/\344\270\255\345\233\275\345\217\244\344\273\243\345\217\262.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\345\275\261\350\247\206/\345\275\261\350\247\206.xmind" "b/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\345\275\261\350\247\206/\345\275\261\350\247\206.xmind"
deleted file mode 100644
index cfa0a5b0f4..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\345\275\261\350\247\206/\345\275\261\350\247\206.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\346\226\207\345\255\246/\346\226\207\345\255\246.xmind" "b/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\346\226\207\345\255\246/\346\226\207\345\255\246.xmind"
deleted file mode 100644
index c51a6ea3cf..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\346\226\207\345\255\246/\346\226\207\345\255\246.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\346\270\270\346\210\217/\345\212\250\344\275\234\345\206\222\351\231\251\346\270\270\346\210\217.xmind" "b/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\346\270\270\346\210\217/\345\212\250\344\275\234\345\206\222\351\231\251\346\270\270\346\210\217.xmind"
deleted file mode 100644
index 690a14a4f9..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\346\270\270\346\210\217/\345\212\250\344\275\234\345\206\222\351\231\251\346\270\270\346\210\217.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\351\237\263\344\271\220/\345\215\216\350\257\255\346\255\214\346\233\262.xmind" "b/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\351\237\263\344\271\220/\345\215\216\350\257\255\346\255\214\346\233\262.xmind"
deleted file mode 100644
index daf2d887a3..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\351\237\263\344\271\220/\345\215\216\350\257\255\346\255\214\346\233\262.xmind" and /dev/null differ
diff --git "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\351\237\263\344\271\220/\351\237\263\344\271\220.xmind" "b/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\351\237\263\344\271\220/\351\237\263\344\271\220.xmind"
deleted file mode 100644
index 736a91d15f..0000000000
Binary files "a/minds/\344\272\272\346\226\207/\350\211\272\346\234\257/\351\237\263\344\271\220/\351\237\263\344\271\220.xmind" and /dev/null differ
diff --git "a/minds/\347\237\245\350\257\206\346\200\273\347\272\262.xmind" "b/minds/\347\237\245\350\257\206\346\200\273\347\272\262.xmind"
deleted file mode 100644
index 012200dfdf..0000000000
Binary files "a/minds/\347\237\245\350\257\206\346\200\273\347\272\262.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/apollo.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/apollo.xmind"
deleted file mode 100644
index 2efa8ea7b8..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/apollo.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/docker.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/docker.xmind"
deleted file mode 100644
index e57d9582bd..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/docker.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/elk.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/elk.xmind"
deleted file mode 100644
index 01cdf3b124..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/elk.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/git.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/git.xmind"
deleted file mode 100644
index 147612d998..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/git.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/jenkins.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/jenkins.xmind"
deleted file mode 100644
index db5e356b63..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/jenkins.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/rundeck.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/rundeck.xmind"
deleted file mode 100644
index 9bb836a269..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/rundeck.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/Tool/\345\270\270\347\224\250\345\267\245\345\205\267.xmind" "b/minds/\347\247\221\346\212\200/CS/Tool/\345\270\270\347\224\250\345\267\245\345\205\267.xmind"
deleted file mode 100644
index c02e6d78d4..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/Tool/\345\270\270\347\224\250\345\267\245\345\205\267.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\345\211\215\347\253\257\346\212\200\346\234\257/html.xmind" "b/minds/\347\247\221\346\212\200/CS/\345\211\215\347\253\257\346\212\200\346\234\257/html.xmind"
deleted file mode 100644
index 037164f3d5..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\345\211\215\347\253\257\346\212\200\346\234\257/html.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\346\223\215\344\275\234\347\263\273\347\273\237/linux.xmind" "b/minds/\347\247\221\346\212\200/CS/\346\223\215\344\275\234\347\263\273\347\273\237/linux.xmind"
deleted file mode 100644
index 291a618dde..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\346\223\215\344\275\234\347\263\273\347\273\237/linux.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/database.xmind" "b/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/database.xmind"
deleted file mode 100644
index 4e418c10f6..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/database.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/redis.xmind" "b/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/redis.xmind"
deleted file mode 100644
index ede2c1bcfb..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/redis.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/sql.xmind" "b/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/sql.xmind"
deleted file mode 100644
index 11810c2c19..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\346\225\260\346\215\256\345\272\223/sql.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\345\256\271\345\231\250.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\345\256\271\345\231\250.xmind"
deleted file mode 100644
index 25507f6d74..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\345\256\271\345\231\250.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\345\271\266\345\217\221.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\345\271\266\345\217\221.xmind"
deleted file mode 100644
index 696bf68444..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\345\271\266\345\217\221.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\350\231\232\346\213\237\346\234\272.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\350\231\232\346\213\237\346\234\272.xmind"
deleted file mode 100644
index 671a6c0e0a..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/Java\350\231\232\346\213\237\346\234\272.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/dubbo.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/dubbo.xmind"
deleted file mode 100644
index 88ebcb79b0..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/dubbo.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/java-stack.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/java-stack.xmind"
deleted file mode 100644
index 2ddfafc84c..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/java-stack.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javacore.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javacore.xmind"
deleted file mode 100644
index 28d327d7f3..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javacore.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javaee.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javaee.xmind"
deleted file mode 100644
index d0b1ea3757..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javaee.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javalib.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javalib.xmind"
deleted file mode 100644
index 634625a55a..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javalib.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javase.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javase.xmind"
deleted file mode 100644
index 381bee4264..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javase.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javatool.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javatool.xmind"
deleted file mode 100644
index 99580d25a3..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/javatool.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/maven.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/maven.xmind"
deleted file mode 100644
index 5e711a423c..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/maven.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/shiro.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/shiro.xmind"
deleted file mode 100644
index 09a888dc68..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/shiro.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/spring-boot.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/spring-boot.xmind"
deleted file mode 100644
index 74ae4ffaf2..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/spring-boot.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/spring.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/spring.xmind"
deleted file mode 100644
index 5678656914..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/spring.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/\345\210\206\345\270\203\345\274\217.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/\345\210\206\345\270\203\345\274\217.xmind"
deleted file mode 100644
index 0cd690b849..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/\345\210\206\345\270\203\345\274\217.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/\346\236\266\346\236\204.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/\346\236\266\346\236\204.xmind"
deleted file mode 100644
index ddf122bd2e..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/Java/\346\236\266\346\236\204.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/\345\246\202\344\275\225\345\255\246\344\271\240\344\270\200\351\227\250\347\274\226\347\250\213\350\257\255\350\250\200.xmind" "b/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/\345\246\202\344\275\225\345\255\246\344\271\240\344\270\200\351\227\250\347\274\226\347\250\213\350\257\255\350\250\200.xmind"
deleted file mode 100644
index 0e500ad15c..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\347\274\226\347\250\213\350\257\255\350\250\200/\345\246\202\344\275\225\345\255\246\344\271\240\344\270\200\351\227\250\347\274\226\347\250\213\350\257\255\350\250\200.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/http.xmind" "b/minds/\347\247\221\346\212\200/CS/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/http.xmind"
deleted file mode 100644
index bfac3014d5..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/http.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/network.xmind" "b/minds/\347\247\221\346\212\200/CS/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/network.xmind"
deleted file mode 100644
index eb63df3b06..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/network.xmind" and /dev/null differ
diff --git "a/minds/\347\247\221\346\212\200/CS/\350\275\257\344\273\266\345\267\245\347\250\213/\351\241\271\347\233\256\347\256\241\347\220\206.xmind" "b/minds/\347\247\221\346\212\200/CS/\350\275\257\344\273\266\345\267\245\347\250\213/\351\241\271\347\233\256\347\256\241\347\220\206.xmind"
deleted file mode 100644
index b6cf6c27f7..0000000000
Binary files "a/minds/\347\247\221\346\212\200/CS/\350\275\257\344\273\266\345\267\245\347\250\213/\351\241\271\347\233\256\347\256\241\347\220\206.xmind" and /dev/null differ
diff --git a/network/DNS.md b/network/DNS.md
deleted file mode 100644
index 0d5ea2abac..0000000000
--- a/network/DNS.md
+++ /dev/null
@@ -1,133 +0,0 @@
-# DNS
-
-> 域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS 使用 TCP 和 UDP 端口 53。当前,对于每一级域名长度的限制是 63 个字符,域名总长度则不能超过 253 个字符。
->
-> 关键词:DNS, 域名解析
-
-
-
-- [简介](#简介)
- - [什么是 DNS?](#什么是-dns)
- - [什么是域名?](#什么是域名)
- - [DNS 的分层](#dns-的分层)
- - [DNS 服务类型](#dns-服务类型)
- - [记录类型](#记录类型)
-- [域名解析](#域名解析)
-- [Linux 上的域名相关命令](#linux-上的域名相关命令)
- - [hostname](#hostname)
- - [nslookup](#nslookup)
-- [引申和引用](#引申和引用)
-
-
-
-## 简介
-
-### 什么是 DNS?
-
-DNS 是一个应用层协议。
-
-域名系统 (DNS) 的作用是将人类可读的域名 (如,www.example.com) 转换为机器可读的 IP 地址 (如,192.0.2.44)。
-
-### 什么是域名?
-
-域名是由一串用点分隔符 `.` 组成的互联网上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的方位。域名可以说是一个 IP 地址的代称,目的是为了便于记忆后者。例如,wikipedia.org 是一个域名,和 IP 地址 208.80.152.2 相对应。人们可以直接访问 wikipedia.org 来代替 IP 地址,然后域名系统(DNS)就会将它转化成便于机器识别的 IP 地址。这样,人们只需要记忆 wikipedia.org 这一串带有特殊含义的字符,而不需要记忆没有含义的数字。
-
-
-
-### DNS 的分层
-
-**域名系统是分层次的。**
-
-在域名系统的层次结构中,各种域名都隶属于域名系统根域的下级。域名的第一级是顶级域,它包括通用顶级域,例如 `.com`、`.net` 和 `.org`;以及国家和地区顶级域,例如 `.us`、`.cn` 和 `.tk`。顶级域名下一层是二级域名,一级一级地往下。这些域名向人们提供注册服务,人们可以用它创建公开的互联网资源或运行网站。顶级域名的管理服务由对应的域名注册管理机构(域名注册局)负责,注册服务通常由域名注册商负责。
-
-### DNS 服务类型
-
-- **授权型 DNS** - 一种授权型 DNS 服务提供一种更新机制,供开发人员用于管理其公用 DNS 名称。然后,它响应 DNS 查询,将域名转换为 IP 地址,以便计算机可以相互通信。授权型 DNS 对域有最终授权且负责提供递归型 DNS 服务器对 IP 地址信息的响应。Amazon Route 53 是一种授权型 DNS 系统。
-- **递归型 DNS** - 客户端通常不会对授权型 DNS 服务直接进行查询。而是通常连接到称为解析程序的其他类型 DNS 服务,或递归型 DNS 服务。递归型 DNS 服务就像是旅馆的门童:尽管没有任何自身的 DNS 记录,但是可充当代表您获得 DNS 信息的中间程序。如果递归型 DNS 拥有已缓存或存储一段时间的 DNS 参考,那么它会通过提供源或 IP 信息来响应 DNS 查询。如果没有,则它会将查询传递到一个或多个授权型 DNS 服务器以查找信息。
-
-### 记录类型
-
-DNS 中,常见的资源记录类型有:
-
-- **NS 记录(域名服务)** ─ 指定解析域名或子域名的 DNS 服务器。
-- **MX 记录(邮件交换)** ─ 指定接收信息的邮件服务器。
-- **A 记录(地址)** ─ 指定域名对应的 IPv4 地址记录。
-- **AAAA 记录(地址)** ─ 指定域名对应的 IPv6 地址记录。
-- **CNAME(规范)** ─ 一个域名映射到另一个域名或 `CNAME` 记录( example.com 指向 [www.example.com](http://www.example.com/) )或映射到一个 `A`记录。
-- **PTR 记录(反向记录)** ─ PTR 记录用于定义与 IP 地址相关联的名称。 PTR 记录是 A 或 AAAA 记录的逆。 PTR 记录是唯一的,因为它们以 .arpa 根开始并被委派给 IP 地址的所有者。
-
-> 详细可以参考:[维基百科 - 域名服务器记录类型列表](https://zh.wikipedia.org/wiki/域名服务器记录类型列表)
-
-## 域名解析
-
-主机名到 IP 地址的映射有两种方式:
-
-- **静态映射** - 在本机上配置域名和 IP 的映射,旨在本机上使用。Windows 和 Linux 的 hosts 文件中的内容就属于静态映射。
-- **动态映射** - 建立一套域名解析系统(DNS),只在专门的 DNS 服务器上配置主机到 IP 地址的映射,网络上需要使用主机名通信的设备,首先需要到 DNS 服务器查询主机所对应的 IP 地址。
-
-通过域名去查询域名服务器,得到 IP 地址的过程叫做域名解析。在解析域名时,一般先静态域名解析,再动态解析域名。可以将一些常用的域名放入静态域名解析表中,这样可以大大提高域名解析效率。
-
-
-
-上图展示了一个动态域名解析的流程,步骤如下:
-
-1. 用户打开 Web 浏览器,在地址栏中输入 www.example.com,然后按 Enter 键。
-2. www.example.com 的请求被路由到 DNS 解析程序,这一般由用户的 Internet 服务提供商 (ISP) 进行管理,例如有线 Internet 服务提供商、DSL 宽带提供商或公司网络。
-3. ISP 的 DNS 解析程序将 www.example.com 的请求转发到 DNS 根名称服务器。
-4. ISP 的 DNS 解析程序再次转发 www.example.com 的请求,这次转发到 .com 域的一个 TLD 名称服务器。.com 域的名称服务器使用与 example.com 域相关的四个 Amazon Route 53 名称服务器的名称来响应该请求。
-5. ISP 的 DNS 解析程序选择一个 Amazon Route 53 名称服务器,并将 www.example.com 的请求转发到该名称服务器。
-6. Amazon Route 53 名称服务器在 example.com 托管区域中查找 www.example.com 记录,获得相关值,例如,Web 服务器的 IP 地址 (192.0.2.44),并将 IP 地址返回至 DNS 解析程序。
-7. ISP 的 DNS 解析程序最终获得用户需要的 IP 地址。解析程序将此值返回至 Web 浏览器。DNS 解析程序还会将 example.com 的 IP 地址缓存 (存储) 您指定的时长,以便它能够在下次有人浏览 example.com 时更快地作出响应。有关更多信息,请参阅存活期 (TTL)。
-8. Web 浏览器将 www.example.com 的请求发送到从 DNS 解析程序中获得的 IP 地址。这是您的内容所处位置,例如,在 Amazon EC2 实例中或配置为网站终端节点的 Amazon S3 存储桶中运行的 Web 服务器。
-9. 192.0.2.44 上的 Web 服务器或其他资源将 www.example.com 的 Web 页面返回到 Web 浏览器,且 Web 浏览器会显示该页面。
-
-> 注意:只有配置了域名服务器,才能执行域名解析。
->
-> 例如,在 Linux 中执行 `vim /etc/resolv.conf` 命令,在其中添加下面的内容来配置域名服务器地址:
->
-> ```
-> nameserver 218.2.135.1
-> ```
-
-## Linux 上的域名相关命令
-
-### hostname
-
-> hostname 命令用于查看和设置系统的主机名称。环境变量 HOSTNAME 也保存了当前的主机名。在使用 hostname 命令设置主机名后,系统并不会永久保存新的主机名,重新启动机器之后还是原来的主机名。如果需要永久修改主机名,需要同时修改 `/etc/hosts` 和 `/etc/sysconfig/network` 的相关内容。
->
-> 参考:http://man.linuxde.net/hostname
-
-示例:
-
-```sh
-$ hostname
-AY1307311912260196fcZ
-```
-
-### nslookup
-
-> nslookup 命令是常用域名查询工具,就是查 DNS 信息用的命令。
->
-> 参考:http://man.linuxde.net/nslookup
-
-示例:
-
-```sh
-[root@localhost ~]# nslookup www.jsdig.com
-Server: 202.96.104.15
-Address: 202.96.104.15#53
-
-Non-authoritative answer:
-www.jsdig.com canonical name = host.1.jsdig.com.
-Name: host.1.jsdig.com
-Address: 100.42.212.8
-```
-
-## 引申和引用
-
-- [维基百科 - 域名](https://zh.wikipedia.org/wiki/域名)
-- [维基百科 - 域名系统](https://zh.wikipedia.org/wiki/域名系统)
-- [维基百科 - 域名服务器记录类型列表](https://zh.wikipedia.org/wiki/域名服务器记录类型列表)
-- [什么是 DNS?](https://aws.amazon.com/cn/route53/what-is-dns/)
-- [RFC 1034](https://tools.ietf.org/html/rfc1034)
-- [RFC 1035](https://tools.ietf.org/html/rfc1035)
diff --git a/network/HTTP.md b/network/HTTP.md
deleted file mode 100644
index b9d3335433..0000000000
--- a/network/HTTP.md
+++ /dev/null
@@ -1,212 +0,0 @@
-# HTTP
-
-
-
-- [HTTP 是什么?](#http-是什么)
-- [实例](#实例)
-- [工作原理](#工作原理)
-- [特点](#特点)
-- [客户端请求消息](#客户端请求消息)
-- [服务器响应消息](#服务器响应消息)
-- [HTTP 状态码](#http-状态码)
-- [引申和引用](#引申和引用)
-
-
-
-## HTTP 是什么?
-
-HTTP(HyperText Transfer Protocol,超文本传输协议)是 WWW (World Wide Web)实现数据通信的基石。
-
-HTTP 是由 **IETF**(Internet Engineering Task Force,互联网工程工作小组) 和 **W3C**(World Wide Web Consortium,万维网协会) 共同合作制订的,它们发布了一系列的**RFC**(Request For Comments),其中最著名的是 RFC 2616,它定义了**HTTP /1.1**。
-
-它是一种应用层协议(OSI 七层模型的最顶层),它基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。
-
-## 实例
-
-如果你学习过计算机网络,熟悉 OSI 模型,那么你可以跳过这个实例了。
-
-而不了解 OSI 模型的朋友,不妨通过一个实例来对 HTTP 报文有一个感性的认识。
-
-以下是使用 wireshark 抓取的一个实际访问百度首页的 HTTP GET 报文:
-
-
-
-可以清楚的看到它的层级结构如下图,经过了层层的包装。
-
-
-
-## 工作原理
-
-HTTP 工作于 **Client/Server** 模型上。
-
-客户端和服务器之间的通信采用 **request/response** 机制。
-
-客户端是终端(可以是浏览器、爬虫程序等),服务器是网站的 Web 服务器。
-
-一次 HTTP 操作称为一个事务,其工作过程大致可分为四步:
-
-1. **建立连接** - 首先,客户端和服务器需要建立一个到服务器指定端口(默认端口号为 **80**)的 TCP 连接(注:虽然 HTTP 采用 TCP 连接是最流行的方式,但是 RFC 并没有指定一定要采用这种网络传输方式。)。
-2. **发送请求信息** - 客户端向服务器发送请求。请求方式的格式为,统一资源标识符、协议版本号,后边是 MIME 信息包括请求修饰符
-3. **发送响应信息** - 服务器监听指定接口是否收到请求,一旦发现请求,处理后,返回响应结果给客户端。其格式为一个状态行包括信息的协议版本号、一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。
-4. **关闭连接** - 客户端根据响应,显示结果给用户,最后关闭连接。
-
-## 特点
-
-- **无连接的** - 无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
-- **无状态的** - HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
-- **媒体独立的** - 这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过 HTTP 发送。客户端以及服务器指定使用适合的 MIME-type 内容类型。
-- **C/S 模型的** - 基于 Client/Server 模型工作。
-
-# HTTP 消息结构
-
-HTTP 是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。
-
-一个 HTTP"客户端"是一个应用程序(Web 浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个 HTTP 的请求的目的。
-
-一个 HTTP"服务器"同样也是一个应用程序(通常是一个 Web 服务,如 Apache Web 服务器或 IIS 服务器等),通过接收客户端的请求并向客户端发送 HTTP 响应数据。
-
-HTTP 使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。
-
-一旦建立连接后,数据消息就通过类似 Internet 邮件所使用的格式[RFC5322]和多用途 Internet 邮件扩展(MIME)[RFC2045]来传送。
-
-
-
-## 客户端请求消息
-
-客户端发送一个 HTTP 请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。
-
-
-
-## 服务器响应消息
-
-HTTP 响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
-
-
-
-# HTTP 请求
-
-根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
-
-**HTTP1.0**定义了三种请求方法: **GET**, **POST** 和 **HEAD**方法。
-
-**HTTP1.1**新增了五种请求方法:**OPTIONS**, **PUT**, **DELETE**, **TRACE** 和 **CONNECT**方法。
-
-| 方法 | 描述 |
-| ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
-| GET | 请求指定的页面信息,并返回实体主体。 |
-| HEAD | 类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
-| POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
-| PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
-| DELETE | 请求服务器删除指定的页面。 |
-| CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
-| OPTIONS | 允许客户端查看服务器的性能。 |
-| TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
-
-**HTTP** **请求消息头**
-
-| **请求消息头** | **说明** |
-| --------------- | -------------------------------------------- |
-| Accept | 浏览器支持的格式 |
-| Accept-Encoding | 支持的编码格式,如(UTF-8,GBK) |
-| Accept-Language | 支持的语言类型 |
-| User-Agent | 浏览器信息 |
-| Cookie | 记录的是用户当前的状态 |
-| Referer | 指从哪个页面单击链接进入的页面 |
-| HOST | 目的地址对应的主机名 |
-| Connection | 连接类型。如 Keep-Alive 表示长连接,不会断开 |
-| Content-Length | 内容长度 |
-| Content-Type | 内容类型 |
-
-# HTTP 响应
-
-| **响应消息头** | **说明** |
-| ---------------- | ------------------------------------------------------------ |
-| Allow | 服务器支持哪些请求方法(如 GET、POST 等)。 |
-| Content-Encoding | 文档的编码(Encode)方法。只有在解码之后才可以得到 Content-Type 头指定的内容类型。利用 gzip 压缩文档能够显著地减少 HTML 文档的下载时间。Java 的 GZIPOutputStream 可以很方便地进行 gzip 压缩,但只有 Unix 上的 Netscape 和 Windows 上的 IE 4、IE 5 才支持它。因此,Servlet 应该通过查看 Accept-Encoding 头(即 request.getHeader("Accept-Encoding"))检查浏览器是否支持 gzip,为支持 gzip 的浏览器返回经 gzip 压缩的 HTML 页面,为其他浏览器返回普通页面。 |
-| Content-Length | 表示内容长度。只有当浏览器使用持久 HTTP 连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStram,完成后查看其大小,然后把该值放入 Content-Length 头,最后通过`byteArrayStream.writeTo(response.getOutputStream()` 发送内容。 |
-| Content-Type | 表示后面的文档属于什么 MIME 类型。Servlet 默认为 `text/plain`,但通常需要显式地指定为 text/html。由于经常要设置 Content-Type,因此 HttpServletResponse 提供了一个专用的方法 setContentType。 |
-| Date | 当前的 GMT 时间。你可以用 setDateHeader 来设置这个头以避免转换时间格式的麻烦。 |
-| Expires | 应该在什么时候认为文档已经过期,从而不再缓存它? |
-| Last-Modified | 文档的最后改动时间。客户可以通过 If-Modified-Since 请求头提供一个日期,该请求将被视为一个条件 GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个 304(Not Modified)状态。Last-Modified 也可用 setDateHeader 方法来设置。 |
-| Location | 表示客户应当到哪里去提取文档。Location 通常不是直接设置的,而是通过 HttpServletResponse 的 sendRedirect 方法,该方法同时设置状态代码为 302。 |
-| Refresh | 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过 `response.setHeader("Refresh", "5;URL=http://host/path")`让浏览器读取指定的页面。 注意这种功能通常是通过设置 HTML 页面 HEAD 区的 ` `实现,这是因为,自动刷新或重定向对于那些不能使用 CGI 或 Servlet 的 HTML 编写者十分重要。但是,对于 Servlet 来说,直接设置 Refresh 头更加方便。 注意 Refresh 的意义是"N 秒之后刷新本页面或访问指定页面",而不是"每隔 N 秒刷新本页面或访问指定页面"。因此,连续刷新要求每次都发送一个 Refresh 头,而发送 204 状态代码则可以阻止浏览器继续刷新,不管是使用 Refresh 头还是 ` `。 注意 Refresh 头不属于 HTTP 1.1 正式规范的一部分,而是一个扩展,但 Netscape 和 IE 都支持它。 |
-| Server | 服务器名字。Servlet 一般不设置这个值,而是由 Web 服务器自己设置。 |
-| Set-Cookie | 设置和页面关联的 Cookie。Servlet 不应使用`response.setHeader("Set-Cookie", ...)`,而是应使用 HttpServletResponse 提供的专用方法 addCookie。参见下文有关 Cookie 设置的讨论。 |
-| WWW-Authenticate | 客户应该在 Authorization 头中提供什么类型的授权信息?在包含 401(Unauthorized)状态行的应答中这个头是必需的。例如,`response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")`。 注意 Servlet 一般不进行这方面的处理,而是让 Web 服务器的专门机制来控制受密码保护页面的访问(例如.htaccess)。 |
-
-## HTTP 状态码
-
-当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含 HTTP 状态码的信息头(server header)用以响应浏览器的请求。
-
-HTTP 状态码的英文为 HTTP Status Code。
-
-下面是常见的 HTTP 状态码:
-
-- 200 - 请求成功
-- 301 - 资源(网页等)被永久转移到其它 URL
-- 404 - 请求的资源(网页等)不存在
-- 500 - 内部服务器错误
-
-HTTP 状态码分类
-
-HTTP 状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP 状态码共分为 5 种类型:
-
-| **分类** | **分类描述** |
-| -------- | ---------------------------------------------- |
-| 1 | 信息,服务器收到请求,需要请求者继续执行操作 |
-| 2 | 成功,操作被成功接收并处理 |
-| 3 | 重定向,需要进一步的操作以完成请求 |
-| 4 | 客户端错误,请求包含语法错误或无法完成请求 |
-| 5 | 服务器错误,服务器在处理请求的过程中发生了错误 |
-
-**HTTP 状态列表:**
-
-| 状态码 | 状态码英文名称 |
-| ------ | ------------------------------- |
-| 100 | Continue |
-| 101 | Switching Protocols |
-| 200 | OK |
-| 201 | Created |
-| 202 | Accepted |
-| 203 | Non-Authoritative Information |
-| 204 | No Content |
-| 205 | Reset Content |
-| 206 | Partial Content |
-| 300 | Multiple Choices |
-| 301 | Moved Permanently |
-| 302 | Found |
-| 303 | See Other |
-| 304 | Not Modified |
-| 305 | Use Proxy |
-| 306 | Unused |
-| 307 | Temporary Redirect |
-| 400 | Bad Request |
-| 401 | Unauthorized |
-| 402 | Payment Required |
-| 403 | Forbidden |
-| 404 | Not Found |
-| 405 | Method Not Allowed |
-| 406 | Not Acceptable |
-| 407 | Proxy Authentication Required |
-| 408 | Request Time-out |
-| 409 | Conflict |
-| 410 | Gone |
-| 411 | Length Required |
-| 412 | Precondition Failed |
-| 413 | Request Entity Too Large |
-| 414 | Request-URI Too Large |
-| 415 | Unsupported Media Type |
-| 416 | Requested range not satisfiable |
-| 417 | Expectation Failed |
-| 500 | Internal Server Error |
-| 501 | Not Implemented |
-| 502 | Bad Gateway |
-| 503 | Service Unavailable |
-| 504 | Gateway Time-out |
-| 505 | HTTP Version not supported |
-
-## 引申和引用
-
-- http://blog.csdn.net/gueter/article/details/1524447
-- http://www.runoob.com/http/http-intro.html
-- https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
diff --git a/network/ICMP.md b/network/ICMP.md
deleted file mode 100644
index 7ce6fedf46..0000000000
--- a/network/ICMP.md
+++ /dev/null
@@ -1,242 +0,0 @@
-# ICMP
-
-
-
-- [ICMP 简介](#icmp-简介)
-- [ICMP 类型](#icmp-类型)
-- [目的不可达(Destination Unreachable Message)](#目的不可达destination-unreachable-message)
-- [超时(Time Exceeded Message)](#超时time-exceeded-message)
-- [参数错误报文(Parameter Problem Message)](#参数错误报文parameter-problem-message)
-- [源冷却(Source Quench Message)](#源冷却source-quench-message)
-- [重定向(Redirect Message)](#重定向redirect-message)
-- [请求回显或回显应答(Echo or Echo Reply Message)](#请求回显或回显应答echo-or-echo-reply-message)
-- [时间戳或时间戳请求(Timestamp or Timestamp Reply Message)](#时间戳或时间戳请求timestamp-or-timestamp-reply-message)
-- [信息请求或信息响应](#信息请求或信息响应)
-- [总结](#总结)
-- [参考](#参考)
-
-
-
-## ICMP 简介
-
-ICMP 全名为(INTERNET CONTROL MESSAGE PROTOCOL)网络控制消息协议。
-
-ICMP 的协议号为**1**。
-
-ICMP 报文就像是 IP 报文的小弟,总顶着 IP 报文的名头出来混。因为 ICMP 报文是在 IP 报文内部的,如图:
-
-
-
-图:IP 数据报
-
-## ICMP 类型
-
-ICMP 报文主要有两大功能:查询报文和差错报文。
-
-## 目的不可达(Destination Unreachable Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| unused |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Internet Header + 64 bits of Original Data Datagram |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-日常生活中,邮寄包裹会经过多个传递环节,任意一环如果无法传下去,都会返回寄件人,并附上无法邮寄的原因。同理,当路由器收到一个无法传递下去的 IP 报文时,会发送 ICMP**目的不可达报文**(Type 为 3)给 IP 报文的源发送方。报文中的 Code 就表示发送失败的原因。
-
-Code
-
-0 = net unreachable;
-
-1 = host unreachable;
-
-2 = protocol unreachable;
-
-3 = port unreachable;
-
-4 = fragmentation needed and DF set;
-
-5 = source route failed.
-
-## 超时(Time Exceeded Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| unused |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Internet Header + 64 bits of Original Data Datagram |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-网络传输 IP 数据报的过程中,如果 IP 数据包的 TTL 值逐渐递减为 0 时,需要丢弃数据报。这时,路由器需要向源发送方发送 **ICMP 超时报文**(Type 为 11),Code 为 0,表示传输过程中超时了。
-
-一个 IP 数据报可能会因为过大而被分片,然后在目的主机侧把所有的分片重组。如果主机迟迟没有等到所有的分片报文,就会向源发送方发送一个 ICMP 超时报文,Code 为 1,表示分片重组超时了。
-
-## 参数错误报文(Parameter Problem Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Pointer | unused |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Internet Header + 64 bits of Original Data Datagram |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-当路由器或主机处理数据报时,发现因为报文头的参数错误而不得不丢弃报文时,需要向源发送方发送**参数错误报文**(Type 为 12)。当 Code 为 0 时,报文中的 Pointer 表示错误的字节位置。
-
-## 源冷却(Source Quench Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| unused |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Internet Header + 64 bits of Original Data Datagram |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-路由器在处理报文时会有一个缓存队列。如果超过最大缓存队列,将无法处理,从而丢弃报文。并向源发送方发一个 **ICMP 源冷却报文**(Type 为 4),告诉对方:“嘿,我这里客满了,你迟点再来。”
-
-## 重定向(Redirect Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Gateway Internet Address |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Internet Header + 64 bits of Original Data Datagram |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-想像一下,在公司中,有人来你的项目组问你某某某在哪儿。你一想,我们组没有这人啊。你肯定就会说,我们组没有这号人,你去其他组看看。当路由收到 IP 数据报,发现数据报的目的地址在路由表上没有,它就会发 **ICMP 重定向报文**(Type 为 5)给源发送方,提醒它想要发送的地址不在,去其他地方找找吧。
-
-## 请求回显或回显应答(Echo or Echo Reply Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Identifier | Sequence Number |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Data ...
-
-+-+-+-+-+-
-
-**Type(8)**是**请求回显报文(Echo)**;Type(0)是**回显应答报文(Echo Reply)**。
-
-请求回显或回显应答报文属于查询报文。Ping 就是用这种报文进行查询和回应。
-
-## 时间戳或时间戳请求(Timestamp or Timestamp Reply Message)
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Identifier | Sequence Number |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Originate Timestamp |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Receive Timestamp |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Transmit Timestamp |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-时间戳报文是用来记录收发以及传输时间的报文。Originate Timestamp 记录的是发送方发送报文的时刻;**Receive Timestamp**记录的是接收方收到报文的时刻;**Transmit Timestamp**表示回显这最后发送报文的时刻。
-
-## 信息请求或信息响应
-
-0 1 2 3
-
-0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Type | Code | Checksum |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-| Identifier | Sequence Number |
-
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-这种报文是用来找出一个主机所在的网络个数(一个主机可能会在多个网络中)。报文的 IP 消息头的目的地址会填为全 0,表示 this,源地址会填为源 IP 所在的网络 IP。
-
-## 总结
-
-**图:ICMP 知识点思维导图**
-
-
-
-## 参考
-
-- [RFC792](http://www.rfc-editor.org/info/rfc792)
diff --git a/os/apollo/README.md b/os/apollo/README.md
deleted file mode 100644
index 57e2fd31b7..0000000000
--- a/os/apollo/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Apollo
-
-> Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
-
-官方 Github:https://github.com/ctripcorp/apollo
-
-由于官方示例不能直接使用,所以我 Fork 后,略作修改:https://github.com/dunwu/apollo
diff --git a/os/apollo/apollo.xmind b/os/apollo/apollo.xmind
deleted file mode 100644
index 3adf568312..0000000000
Binary files a/os/apollo/apollo.xmind and /dev/null differ
diff --git "a/os/deploy/linux\345\237\272\346\234\254\351\205\215\347\275\256.md" "b/os/deploy/linux\345\237\272\346\234\254\351\205\215\347\275\256.md"
deleted file mode 100644
index fd2ecb142d..0000000000
--- "a/os/deploy/linux\345\237\272\346\234\254\351\205\215\347\275\256.md"
+++ /dev/null
@@ -1,16 +0,0 @@
-
-### 设置 Linux 启动模式
-
-1. 停机(记得不要把 initdefault 配置为 0,因为这样会使 Linux 不能启动)
-2. 单用户模式,就像 Win9X 下的安全模式
-3. 多用户,但是没有 NFS
-4. 完全多用户模式,准则的运行级
-5. 通常不用,在一些特殊情况下可以用它来做一些事情
-6. X11,即进到 X-Window 系统
-7. 重新启动 (记得不要把 initdefault 配置为 6,因为这样会使 Linux 不断地重新启动)
-
-设置方法:
-
-```sh
-$ sed -i 's/id:5:initdefault:/id:3:initdefault:/' /etc/inittab
-```
\ No newline at end of file
diff --git a/os/docker/advanced/docker-design.md b/os/docker/advanced/docker-design.md
deleted file mode 100644
index 88cad43c1f..0000000000
--- a/os/docker/advanced/docker-design.md
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-- [Docker 的设计](#docker-%E7%9A%84%E8%AE%BE%E8%AE%A1)
- - [Docker 架构](#docker-%E6%9E%B6%E6%9E%84)
- - [Docker 守护进程(docker daemon)](#docker-%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%EF%BC%88docker-daemon%EF%BC%89)
- - [Docker 客户端](#docker-%E5%AE%A2%E6%88%B7%E7%AB%AF)
- - [Docker 注册中心](#docker-%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83)
- - [Docker 对象](#docker-%E5%AF%B9%E8%B1%A1)
- - [镜像](#%E9%95%9C%E5%83%8F)
- - [容器](#%E5%AE%B9%E5%99%A8)
- - [服务](#%E6%9C%8D%E5%8A%A1)
- - [底层技术](#%E5%BA%95%E5%B1%82%E6%8A%80%E6%9C%AF)
- - [命名空间](#%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4)
- - [控制组](#%E6%8E%A7%E5%88%B6%E7%BB%84)
- - [联合文件系统](#%E8%81%94%E5%90%88%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F)
- - [容器格式](#%E5%AE%B9%E5%99%A8%E6%A0%BC%E5%BC%8F)
- - [资料](#%E8%B5%84%E6%96%99)
-
-
-
-# Docker 的设计
-
-## Docker 架构
-
-Docker 使用 C/S 体系结构。Docker 守护进程,负责构建、运行和分发 Docker 容器;Docker 客户端与 Docker 守护进程通信。Docker 客户端和守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程使用 REST API,并通过 UNIX 套接字或网络接口进行通信。
-
-
-
-### Docker 守护进程(docker daemon)
-
-Docker 守护进程(`dockerd`)监听 Docker API 请求并管理 Docker 对象(如镜像,容器,网络和卷)。守护进程还可以与其他守护进程通信来管理 Docker 服务。
-
-### Docker 客户端
-
-Docker 客户端(`docker`)是许多 Docker 用户与 Docker 进行交互的主要方式。当你使用诸如 `docker run` 之类的命令时,客户端将这些命令发送到 `dockerd`,`dockerd` 执行这些命令。 `docker` 命令使用 Docker API。 Docker 客户端可以与多个守护进程进行通信。
-
-### Docker 注册中心
-
-Docker 注册中心存储 Docker 镜像。Docker Hub 和 Docker Cloud 是任何人都可以使用的公共注册中心,并且 Docker 默认配置为在 Docker Hub 上查找镜像。你甚至可以运行你自己的私人注册中心。如果您使用 Docker Datacenter(DDC),它包括 Docker Trusted Registry(DTR)。
-
-当您使用 `docker pull` 或 `docker run` 命令时,所需的镜像将从配置的注册中心中提取。当您使用 `docker push` 命令时,您的镜像将被推送到您配置的注册中心。
-
-[Docker 商店](http://store.docker.com/) 允许您购买和销售 Docker 镜像或免费发布。例如,您可以购买包含来自软件供应商的应用程序或服务的 Docker 镜像,并使用该镜像将应用程序部署到您的测试,临时和生产环境中。您可以通过拉取新版本的镜像并重新部署容器来升级应用程序。
-
-### Docker 对象
-
-#### 镜像
-
-镜像是一个只读模板,带有创建 Docker 容器的说明。通常,镜像基于另一个镜像,并具有一些额外的自定义功能。例如,您可以构建基于 ubuntu 镜像的镜像,但会安装 Apache Web 服务器和应用程序,以及使应用程序运行所需的配置细节。
-
-您可能会创建自己的镜像,或者您可能只能使用其他人创建并在注册中心中发布的镜像。为了构建您自己的镜像,您可以使用简单的语法创建 `Dockerfile`,以定义创建镜像并运行所需的步骤。 `Dockerfile` 中的每条指令都会在镜像中创建一个图层。当您更改 `Dockerfile` 并重建镜像时,只重建那些已更改的图层。与其他虚拟化技术相比,这是使镜像轻量,小巧,快速的一部分。
-
-#### 容器
-
-容器是镜像的可运行实例。您可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。您可以将容器连接到一个或多个网络,将存储器连接到它,甚至可以根据其当前状态创建新镜像。
-
-默认情况下,容器与其他容器及其主机相对隔离。您可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。
-
-容器由其镜像以及您在创建或启动时提供给它的任何配置选项来定义。当一个容器被移除时,其未被存储在永久存储器中的状态将消失。
-
-#### 服务
-
-通过服务,您可以跨多个 Docker 守护进程扩展容器,这些守护进程可以作为一个群组与多个管理人员、工作人员一起工作。集群中的每个成员都是 Docker 守护进程,守护进程都使用 Docker API 进行通信。服务允许您定义所需的状态,例如在任何给定时间必须可用的服务的副本数量。默认情况下,该服务在所有工作节点之间进行负载平衡。对于消费者来说,Docker 服务似乎是一个单一的应用程序。Docker 引擎在 Docker 1.12 及更高版本中支持集群模式。
-
-## 底层技术
-
-Docker 使用 Go 编写,利用 Linux 内核的几个特性来提供其功能。
-
-### 命名空间
-
-Docker 使用名为 `namespaces` 的技术来提供独立工作空间(即容器)。当你运行一个容器时,Docker 会为该容器创建一组命名空间。
-
-这些命名空间提供了一个隔离层。容器的每个方面都在单独的命名空间中运行,并且其访问权限限于该命名空间。
-
-Docker 引擎在 Linux 上使用如下的命名空间:
-
-* `pid` 命名空间:进程隔离(PID:进程ID)。
-* `net` 命名空间:管理网络接口(NET:网络)。
-* `ipc` 命名空间:管理对IPC资源的访问(IPC:InterProcess Communication)。
-* `mnt` 命名空间:管理文件系统挂载点(MNT:挂载)。
-* `uts` 命名空间:隔离内核和版本标识符。 (UTS:Unix分时系统)。
-
-### 控制组
-
-Linux 上的 Docker Engine 也依赖于另一种称为控制组(`cgroups`)的技术。 cgroup 将应用程序限制为一组特定的资源。控制组允许 Docker 引擎将可用硬件资源共享给容器,并可选地强制实施限制和约束。例如,您可以限制可用于特定容器的内存。
-
-### 联合文件系统
-
-联合文件系统(UnionFS)是通过创建图层进行操作的文件系统,这使它们非常轻巧和快速。 Docker 引擎使用 UnionFS 为容器提供构建块。Docker 引擎可以使用多种 UnionFS 变体,包括 AUFS,btrfs,vfs 和 DeviceMapper。
-
-### 容器格式
-
-Docker 引擎将命名空间,控制组和 UnionFS 组合成一个名为容器格式的包装器。默认的容器格式是`libcontainer`。将来,Docker 可以通过与诸如 BSD Jails 或 Solaris Zones 等技术集成来支持其他容器格式。
-
-## 资料
-
-* https://docs.docker.com/engine/docker-overview/
diff --git a/os/docker/appendix/docker-cli.md b/os/docker/appendix/docker-cli.md
deleted file mode 100644
index 2eab4623c6..0000000000
--- a/os/docker/appendix/docker-cli.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# Docker 命令
-
-
-
-- [镜像(Images)](#镜像images)
-- [容器(Container)](#容器container)
- - [生命周期](#生命周期)
- - [启动和停止](#启动和停止)
- - [信息](#信息)
- - [导入 / 导出](#导入--导出)
- - [执行命令](#执行命令)
-- [网络(Networks)](#网络networks)
- - [生命周期](#生命周期-1)
- - [信息](#信息-1)
- - [链接](#链接)
-- [仓管中心和仓库(Registry & Repository)](#仓管中心和仓库registry--repository)
-- [Dockerfile](#dockerfile)
-- [卷标(Volumes)](#卷标volumes)
- - [生命周期](#生命周期-2)
- - [信息](#信息-2)
-- [引用和引申](#引用和引申)
-
-
-
-## 镜像(Images)
-
-- [`docker image ls`](https://github.com/yeasy/docker_practice/blob/master/image/list.md) - 查看所有镜像。
-- [`docker image rm`](https://github.com/yeasy/docker_practice/blob/master/image/rm.md) - 删除本地镜像。
-- `docker import` - 从压缩文件中创建镜像。
-- `docker export` - 导出既有容器。
-- `docker build` - 从 Dockerfile 创建镜像。
-- `docker commit` - 为容器创建镜像,如果容器正在运行则会临时暂停。
-- `docker rmi` - 删除镜像。
-- `docker load` - 通过 STDIN 从压缩包加载镜像,包括镜像和标签(images and tags) (0.7 起).
-- `docker save` - 通过 STDOUT 保存镜像到压缩包,包括所有的父层,标签和版本(parent layers, tags & versions) (0.7 起).
-- `docker history` - 查看镜像历史记录。
-- `docker tag` - 给镜像命名打标(tags) (本地或者仓库)。
-
-## 容器(Container)
-
-### 生命周期
-
-- `docker create` - 创建一个容器但是不启动。
-- `docker rename` - 允许重命名容器。
-- `docker run` - 在同一个操作中创建并启动一个容器。
-- `docker rm` - 删除容器。
-- `docker update` - 更新容器的资源限制。
-
-### 启动和停止
-
-- `docker start` - 启动容器。
-- `docker stop` - 停止运行中的容器。
-- `docker restart` - 停止之后再启动容器。
-- `docker pause` - 暂停运行中的容器,将其 "冻结" 在当前状态。
-- `docker unpause` - 结束容器暂停状态。
-- `docker wait` - 阻塞,到运行中的容器停止为止。
-- `docker kill` - 向运行中容器发送 SIGKILL 指令。
-- `docker attach` - 链接到运行中容器。
-
-### 信息
-
-- `docker ps` - 查看运行中的所有容器。
-- `docker logs` - 从容器中获取日志。(你也可以使用自定义日志驱动,不过在 1.10 中,它只支持 json-file 和 journald)
-- `docker inspect` - 查看某个容器的所有信息(包括 IP 地址)。
-- `docker events` - 从容器中获取事件(events)。
-- `docker port` - 查看容器的公开端口。
-- `docker top` - 查看容器中活动进程。
-- `docker stats` - 查看容器的资源使用情况统计信息。
-- `docker diff` - 查看容器的 FS 中有变化文件信息。
-
-### 导入 / 导出
-
-docker cp 在容器和本地文件系统之间复制文件或文件夹。
-docker export 将容器的文件系统切换为压缩包(tarball archive stream)输出到 STDOUT。
-
-### 执行命令
-
-docker exec 在容器中执行命令。
-
-## 网络(Networks)
-
-### 生命周期
-
-- `docker network create`
-- `docker network rm`
-
-### 信息
-
-- `docker network ls`
-- `docker network inspect`
-
-### 链接
-
-- `docker network connect`
-- `docker network disconnect`
-
-## 仓管中心和仓库(Registry & Repository)
-
-- `docker login` - 登入仓管中心。
-- `docker logout` - 登出仓管中心。
-- `docker search` - 从仓管中心检索镜像。
-- `docker pull` - 从仓管中心拉去镜像到本地。
-- `docker push` - 从本地推送镜像到仓管中心。
-
-## Dockerfile
-
-- .dockerignore
-- FROM 为其他指令设置基础镜像(Base Image)。
-- MAINTAINER 为生成的镜像设置作者字段。
-- RUN 在当前镜像的基础上生成一个新层并执行命令。
-- CMD 设置容器默认执行命令。
-- EXPOSE 告知 Docker 容器在运行时所要监听的网络端口。注意:并没有实际上将端口设置为可访问。
-- ENV 设置环境变量。
-- ADD 将文件,文件夹或者远程文件复制到容器中。缓存无效。尽量用 COPY 代替 ADD。
-- COPY 将文件或文件夹复制到容器中。
-- ENTRYPOINT 将一个容器设置为可执行。
-- VOLUME 为外部挂载卷标或其他容器设置挂载点(mount point)。
-- USER 设置执行 RUN / CMD / ENTRYPOINT 命令的用户名。
-- WORKDIR 设置工作目录。
-- ARG 定义编译时(build-time)变量。
-- ONBUILD 添加触发指令,当该镜像被作为其他镜像的基础镜像时该指令会被触发。
-- STOPSIGNAL 设置通过系统向容器发出退出指令。
-- LABEL 将键值对元数据(key/value metadata)应用到你的镜像,容器,或者守护进程。
-
-## 卷标(Volumes)
-
-### 生命周期
-
-- `docker volume create`
-- `docker volume rm`
-
-### 信息
-
-- `docker volume ls`
-- `docker volume inspect`
-
-## 引用和引申
-
-https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn
diff --git a/os/docker/appendix/docker-glossary.md b/os/docker/appendix/docker-glossary.md
deleted file mode 100644
index 1a21c908d2..0000000000
--- a/os/docker/appendix/docker-glossary.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Docker 术语
-
-* **Docker 镜像(Images)**:Docker 镜像是用于创建 Docker 容器的模板。
-* **Docker 容器(Container)**:容器是独立运行的一个或一组应用。
-* **Docker 客户端(Client)**:Docker 客户端通过命令行或者其他工具使用 Docker API (https://docs.docker.com/reference/api/docker_remote_api) 与 Docker 的守护进程通信。
-* **Docker 主机(Host)**:一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
-* **Docker 仓库(Registry)**:Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。:Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。
-* **Docker Machine**:Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。
-* **Docker Machine**:Dockerfile是一个文本文档,其中包含您通常为了构建Docker镜像而手动执行的所有命令。 Docker可以通过从Dockerfile读取指令自动构建映像。
diff --git a/os/docker/appendix/docker-recipe.md b/os/docker/appendix/docker-recipe.md
deleted file mode 100644
index 80af6994af..0000000000
--- a/os/docker/appendix/docker-recipe.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# Docker 技巧
-
-## 命令技巧
-
-### 停止所有的 docker container
-
-_这样才能够删除其中的images_
-
-```
-docker stop $(docker ps -a -q)
-```
-
-### 删除所有的 docker container
-
-```
-docker rm $(docker ps -a -q)
-```
-
-### 删除所有的 docker images
-
-```
-docker rmi $(docker images -q)
-```
\ No newline at end of file
diff --git a/os/docker/appendix/docker-resources.md b/os/docker/appendix/docker-resources.md
deleted file mode 100644
index b63122db17..0000000000
--- a/os/docker/appendix/docker-resources.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Docker 资源
-
-## Docker 官方资源
-
-- Docker 官网:http://www.docker.com
-- Docker Github:https://github.com/moby/moby
-- Docker 官方文档:https://docs.docker.com/
-- Docker windows 入门:https://docs.docker.com/windows/
-- Docker Linux 入门:https://docs.docker.com/linux/
-- Docker mac 入门:https://docs.docker.com/mac/
-- Docker 用户指引:https://docs.docker.com/engine/userguide/
-- Docker 官方博客:http://blog.docker.com/
-- Docker Hub: https://hub.docker.com/
-- Docker 开源: https://www.docker.com/open-source
-
-## 资源整理
-
-- Awesome Docker: https://github.com/veggiemonk/awesome-docker
-
-## Docker 中文资源
-
-- Docker 中文网站:https://www.docker-cn.com/
-- Docker 安装手册:https://docs.docker-cn.com/engine/installation/
-
-## Docker 国内镜像
-
-- 网易加速器:http://hub-mirror.c.163.com
-- 官方中国加速器:https://registry.docker-cn.com
-- ustc 的镜像:https://docker.mirrors.ustc.edu.cn
-- daocloud:https://www.daocloud.io/mirror#accelerator-doc(注册后使用)
diff --git a/os/docker/basics/docker-container.md b/os/docker/basics/docker-container.md
deleted file mode 100644
index c993a7be81..0000000000
--- a/os/docker/basics/docker-container.md
+++ /dev/null
@@ -1,261 +0,0 @@
-# Docker 容器
-
-容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。
-
-
-
-- [启动容器](#启动容器)
- - [新建并启动](#新建并启动)
- - [启动已终止容器](#启动已终止容器)
-- [后台运行](#后台运行)
-- [终止容器](#终止容器)
-- [进入容器](#进入容器)
- - [`attach` 命令](#attach-命令)
- - [`exec` 命令](#exec-命令)
-- [导出和导入容器](#导出和导入容器)
- - [导出容器](#导出容器)
- - [导入容器快照](#导入容器快照)
-- [删除容器](#删除容器)
-- [清理所有处于终止状态的容器](#清理所有处于终止状态的容器)
-
-
-
-## 启动容器
-
-启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(`stopped`)的容器重新启动。
-
-因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。
-
-### 新建并启动
-
-所需要的命令主要为 `docker run`。
-
-例如,下面的命令输出一个 “Hello World”,之后终止容器。
-
-```bash
-$ docker run ubuntu:14.04 /bin/echo 'Hello world'
-Hello world
-```
-
-这跟在本地直接执行 `/bin/echo 'hello world'` 几乎感觉不出任何区别。
-
-下面的命令则启动一个 bash 终端,允许用户进行交互。
-
-```bash
-$ docker run -t -i ubuntu:14.04 /bin/bash
-root@af8bae53bdd3:/#
-```
-
-其中,`-t` 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, `-i` 则让容器的标准输入保持打开。
-
-在交互模式下,用户可以通过所创建的终端来输入命令,例如
-
-```bash
-root@af8bae53bdd3:/# pwd
-/
-root@af8bae53bdd3:/# ls
-bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
-```
-
-当利用 `docker run` 来创建容器时,Docker 在后台运行的标准操作包括:
-
-- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
-- 利用镜像创建并启动一个容器
-- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
-- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
-- 从地址池配置一个 ip 地址给容器
-- 执行用户指定的应用程序
-- 执行完毕后容器被终止
-
-### 启动已终止容器
-
-可以利用 `docker container start` 命令,直接将一个已经终止的容器启动运行。
-
-容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 `ps` 或 `top` 来查看进程信息。
-
-```bash
-root@ba267838cc1b:/# ps
- PID TTY TIME CMD
- 1 ? 00:00:00 bash
- 11 ? 00:00:00 ps
-```
-
-可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。
-
-## 后台运行
-
-更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 `-d` 参数来实现。
-
-下面举两个例子来说明一下。
-
-如果不使用 `-d` 参数运行容器。
-
-```bash
-$ docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
-hello world
-hello world
-hello world
-hello world
-```
-
-容器会把输出的结果 (STDOUT) 打印到宿主机上面
-
-如果使用了 `-d` 参数运行容器。
-
-```bash
-$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
-77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
-```
-
-此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 `docker logs` 查看)。
-
-**注:** 容器是否会长久运行,是和 `docker run` 指定的命令有关,和 `-d` 参数无关。
-
-使用 `-d` 参数启动后会返回一个唯一的 id,也可以通过 `docker container ls` 命令来查看容器信息。
-
-```
-$ docker container ls
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-77b2dc01fe0f ubuntu:18.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
-```
-
-要获取容器的输出信息,可以通过 `docker container logs` 命令。
-
-```bash
-$ docker container logs [container ID or NAMES]
-hello world
-hello world
-hello world
-. . .
-```
-
-## 终止容器
-
-可以使用 `docker container stop` 来终止一个运行中的容器。
-
-此外,当 Docker 容器中指定的应用终结时,容器也自动终止。
-
-例如对于上一章节中只启动了一个终端的容器,用户通过 `exit` 命令或 `Ctrl+d` 来退出终端时,所创建的容器立刻终止。
-
-终止状态的容器可以用 `docker container ls -a` 命令看到。例如
-
-```bash
-docker container ls -a
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton
-98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike
-```
-
-处于终止状态的容器,可以通过 `docker container start` 命令来重新启动。
-
-此外,`docker container restart` 命令会将一个运行态的容器终止,然后再重新启动它。
-
-## 进入容器
-
-在使用 `-d` 参数时,容器启动后会进入后台。
-
-某些时候需要进入容器进行操作,包括使用 `docker attach` 命令或 `docker exec` 命令,推荐大家使用 `docker exec` 命令,原因会在下面说明。
-
-### `attach` 命令
-
-`docker attach` 是 Docker 自带的命令。下面示例如何使用该命令。
-
-```bash
-$ docker run -dit ubuntu
-243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
-
-$ docker container ls
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia
-
-$ docker attach 243c
-root@243c32535da7:/#
-```
-
-_注意:_ 如果从这个 stdin 中 exit,会导致容器的停止。
-
-### `exec` 命令
-
-#### -i -t 参数
-
-`docker exec` 后边可以跟多个参数,这里主要说明 `-i` `-t` 参数。
-
-只用 `-i` 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
-
-当 `-i` `-t` 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。
-
-```bash
-$ docker run -dit ubuntu
-69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6
-
-$ docker container ls
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles
-
-$ docker exec -i 69d1 bash
-ls
-bin
-boot
-dev
-...
-
-$ docker exec -it 69d1 bash
-root@69d137adef7a:/#
-```
-
-如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 `docker exec` 的原因。
-
-更多参数说明请使用 `docker exec --help` 查看。
-
-## 导出和导入容器
-
-### 导出容器
-
-如果要导出本地某个容器,可以使用 `docker export` 命令。
-
-```bash
-$ docker container ls -a
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
-$ docker export 7691a814370e > ubuntu.tar
-```
-
-这样将导出容器快照到本地文件。
-
-### 导入容器快照
-
-可以使用 `docker import` 从容器快照文件中再导入为镜像,例如
-
-```bash
-$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
-$ docker image ls
-REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
-test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
-```
-
-此外,也可以通过指定 URL 或者某个目录来导入,例如
-
-```bash
-$ docker import http://example.com/exampleimage.tgz example/imagerepo
-```
-
-_注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。_
-
-## 删除容器
-
-可以使用 `docker container rm` 来删除一个处于终止状态的容器。例如
-
-```bash
-$ docker container rm trusting_newton
-trusting_newton
-```
-
-如果要删除一个运行中的容器,可以添加 `-f` 参数。Docker 会发送 `SIGKILL` 信号给容器。
-
-## 清理所有处于终止状态的容器
-
-用 `docker container ls -a` 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。
-
-```bash
-$ docker container prune
-```
diff --git a/os/docker/basics/docker-dockerfile.md b/os/docker/basics/docker-dockerfile.md
deleted file mode 100644
index 4c22154035..0000000000
--- a/os/docker/basics/docker-dockerfile.md
+++ /dev/null
@@ -1,649 +0,0 @@
-# Dockerfile
-
-
-
-- [Dockerfile 指令](#dockerfile-指令)
- - [FROM(指定基础镜像)](#from指定基础镜像)
- - [RUN(执行命令)](#run执行命令)
- - [COPY(复制文件)](#copy复制文件)
- - [ADD(更高级的复制文件)](#add更高级的复制文件)
- - [CMD(容器启动命令)](#cmd容器启动命令)
- - [ENTRYPOINT(入口点)](#entrypoint入口点)
- - [ENV(设置环境变量)](#env设置环境变量)
- - [ARG(构建参数)](#arg构建参数)
- - [VOLUME(定义匿名卷)](#volume定义匿名卷)
- - [EXPOSE(暴露端口)](#expose暴露端口)
- - [WORKDIR(指定工作目录)](#workdir指定工作目录)
- - [USER(指定当前用户)](#user指定当前用户)
- - [HEALTHCHECK(健康检查)](#healthcheck健康检查)
- - [ONBUILD(为他人作嫁衣裳)](#onbuild为他人作嫁衣裳)
-- [引用和引申](#引用和引申)
-
-
-
-## Dockerfile 指令
-
-### FROM(指定基础镜像)
-
-> 作用:
->
-> `FROM` 指令用于指定基础镜像。
-
-所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 `nginx` 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 `FROM` 就是指定**基础镜像**,因此一个 `Dockerfile` 中 `FROM` 是必备的指令,并且必须是第一条指令。
-
-在 [Docker Store](https://store.docker.com/) 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 [`nginx`](https://store.docker.com/images/nginx/)、[`redis`](https://store.docker.com/images/redis/)、[`mongo`](https://store.docker.com/images/mongo/)、[`mysql`](https://store.docker.com/images/mysql/)、[`httpd`](https://store.docker.com/images/httpd/)、[`php`](https://store.docker.com/images/php/)、[`tomcat`](https://store.docker.com/images/tomcat/) 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 [`node`](https://store.docker.com/images/node)、[`openjdk`](https://store.docker.com/images/openjdk/)、[`python`](https://store.docker.com/images/python/)、[`ruby`](https://store.docker.com/images/ruby/)、[`golang`](https://store.docker.com/images/golang/) 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
-
-如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 [`ubuntu`](https://store.docker.com/images/ubuntu/)、[`debian`](https://store.docker.com/images/debian/)、[`centos`](https://store.docker.com/images/centos/)、[`fedora`](https://store.docker.com/images/fedora/)、[`alpine`](https://store.docker.com/images/alpine/) 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
-
-除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 `scratch`。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
-
-```dockerfile
-FROM scratch
-...
-```
-
-如果你以 `scratch` 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
-
-不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 [`swarm`](https://hub.docker.com/_/swarm/)、[`coreos/etcd`](https://quay.io/repository/coreos/etcd)。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 `FROM scratch` 会让镜像体积更加小巧。使用 [Go 语言](https://golang.org/) 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
-
-### RUN(执行命令)
-
-`RUN` 指令是用来执行命令行命令的。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种:
-
-- _shell_ 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。
-
-```dockerfile
-RUN echo 'Hello, Docker! ' > /usr/share/nginx/html/index.html
-```
-
-- _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。
-
-既然 `RUN` 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:
-
-```dockerfile
-FROM debian:jessie
-
-RUN apt-get update
-RUN apt-get install -y gcc libc6-dev make
-RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
-RUN mkdir -p /usr/src/redis
-RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
-RUN make -C /usr/src/redis
-RUN make -C /usr/src/redis install
-```
-
-之前说过,Dockerfile 中每一个指令都会建立一层,`RUN` 也不例外。每一个 `RUN` 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,`commit` 这一层的修改,构成新的镜像。
-
-而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。
-
-_Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。_
-
-上面的 `Dockerfile` 正确的写法应该是这样:
-
-```dockerfile
-FROM debian:jessie
-
-RUN buildDeps='gcc libc6-dev make' \
- && apt-get update \
- && apt-get install -y $buildDeps \
- && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
- && mkdir -p /usr/src/redis \
- && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
- && make -C /usr/src/redis \
- && make -C /usr/src/redis install \
- && rm -rf /var/lib/apt/lists/* \
- && rm redis.tar.gz \
- && rm -r /usr/src/redis \
- && apt-get purge -y --auto-remove $buildDeps
-```
-
-首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 `RUN` 对一一对应不同的命令,而是仅仅使用一个 `RUN` 指令,并使用 `&&` 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。
-
-并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 `\` 的命令换行方式,以及行首 `#` 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。
-
-此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 `apt` 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。
-
-很多人初学 Docker 制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。
-
-### COPY(复制文件)
-
-> 作用:
->
-> **`COPY` 指令将从构建上下文目录中 `<源路径>` 的文件/目录复制到新的一层的镜像内的 `<目标路径>` 位置。**
-
-格式:
-
-- `COPY [--chown=:] <源路径>... <目标路径>`
-- `COPY [--chown=:] ["<源路径1>",... "<目标路径>"]`
-
-示例:
-
-```dockerfile
-COPY package.json /usr/src/app/
-```
-
-`<源路径>` 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 [`filepath.Match`](https://golang.org/pkg/path/filepath/#Match) 规则,如:
-
-```dockerfile
-COPY hom* /mydir/
-COPY hom?.txt /mydir/
-```
-
-`<目标路径>` 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 `WORKDIR` 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
-
-此外,还需要注意一点,使用 `COPY` 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
-
-在使用该指令的时候还可以加上 `--chown=:` 选项来改变文件的所属用户及所属组。
-
-```dockerfile
-COPY --chown=55:mygroup files* /mydir/
-COPY --chown=bin files* /mydir/
-COPY --chown=1 files* /mydir/
-COPY --chown=10:11 files* /mydir/
-```
-
-### ADD(更高级的复制文件)
-
-> 作用:
->
-> `ADD` 指令和 `COPY` 的格式和性质基本一致。但是在 `COPY` 基础上增加了一些功能。
->
-> 比如 `<源路径>` 可以是一个 `URL`,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 `<目标路径>`去。下载后的文件权限自动设置为 `600`,如果这并不是想要的权限,那么还需要增加额外的一层 `RUN` 进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 `RUN` 指令进行解压缩。所以不如直接使用 `RUN` 指令,然后使用 `wget` 或者 `curl` 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。
->
-> 如果 `<源路径>` 为一个 `tar` 压缩文件的话,压缩格式为 `gzip`, `bzip2` 以及 `xz` 的情况下,`ADD` 指令将会自动解压缩这个压缩文件到 `<目标路径>` 去。
-
-在某些情况下,这个自动解压缩的功能非常有用,比如官方镜像 `ubuntu` 中:
-
-```dockerfile
-FROM scratch
-ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /
-...
-```
-
-但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 `ADD` 命令了。
-
-在 Docker 官方的 [Dockerfile 最佳实践文档](https://yeasy.gitbooks.io/docker_practice/content/appendix/best_practices.html) 中要求,尽可能的使用 `COPY`,因为 `COPY` 的语义很明确,就是复制文件而已,而 `ADD` 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 `ADD` 的场合,就是所提及的需要自动解压缩的场合。
-
-另外需要注意的是,`ADD` 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
-
-因此在 `COPY` 和 `ADD` 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 `COPY` 指令,仅在需要自动解压缩的场合使用 `ADD`。
-
-在使用该指令的时候还可以加上 `--chown=:` 选项来改变文件的所属用户及所属组。
-
-```dockerfile
-ADD --chown=55:mygroup files* /mydir/
-ADD --chown=bin files* /mydir/
-ADD --chown=1 files* /mydir/
-ADD --chown=10:11 files* /mydir/
-```
-
-### CMD(容器启动命令)
-
-> 作用:
->
-> 之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。`CMD` 指令就是用于指定默认的容器主进程的启动命令的。
-
-`CMD` 指令的格式和 `RUN` 相似,也是两种格式:
-
-- `shell` 格式:`CMD <命令>`
-- `exec` 格式:`CMD ["可执行文件", "参数1", "参数2"...]`
-- 参数列表格式:`CMD ["参数1", "参数2"...]`。在指定了 `ENTRYPOINT` 指令后,用 `CMD` 指定具体的参数。
-
-在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,`ubuntu` 镜像默认的 `CMD` 是 `/bin/bash`,如果我们直接 `docker run -it ubuntu` 的话,会直接进入 `bash`。我们也可以在运行时指定运行别的命令,如 `docker run -it ubuntu cat /etc/os-release`。这就是用 `cat /etc/os-release` 命令替换了默认的 `/bin/bash` 命令了,输出了系统版本信息。
-
-在指令格式上,一般推荐使用 `exec` 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 `"`,而不要使用单引号。
-
-如果使用 `shell` 格式的话,实际的命令会被包装为 `sh -c` 的参数的形式进行执行。比如:
-
-```dockerfile
-CMD echo $HOME
-```
-
-在实际执行中,会将其变更为:
-
-```dockerfile
-CMD [ "sh", "-c", "echo $HOME" ]
-```
-
-这就是为什么我们可以使用环境变量的原因,因为这些环境变量会被 shell 进行解析处理。
-
-提到 `CMD` 就不得不提容器中应用在前台执行和后台执行的问题。这是初学者常出现的一个混淆。
-
-Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 upstart/systemd 去启动后台服务,容器内没有后台服务的概念。
-
-一些初学者将 `CMD` 写为:
-
-```dockerfile
-CMD service nginx start
-```
-
-然后发现容器执行后就立即退出了。甚至在容器内去使用 `systemctl` 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。
-
-对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
-
-而使用 `service nginx start` 命令,则是希望 upstart 来以后台守护进程形式启动 `nginx` 服务。而刚才说了 `CMD service nginx start` 会被理解为 `CMD [ "sh", "-c", "service nginx start"]`,因此主进程实际上是 `sh`。那么当 `service nginx start` 命令结束后,`sh` 也就结束了,`sh` 作为主进程退出了,自然就会令容器退出。
-
-正确的做法是直接执行 `nginx` 可执行文件,并且要求以前台形式运行。比如:
-
-```dockerfile
-CMD ["nginx", "-g", "daemon off;"]
-```
-
-### ENTRYPOINT(入口点)
-
-`ENTRYPOINT` 的格式和 `RUN` 指令格式一样,分为 `exec` 格式和 `shell` 格式。
-
-`ENTRYPOINT` 的目的和 `CMD` 一样,都是在指定容器启动程序及参数。`ENTRYPOINT` 在运行时也可以替代,不过比 `CMD` 要略显繁琐,需要通过 `docker run` 的参数 `--entrypoint` 来指定。
-
-当指定了 `ENTRYPOINT` 后,`CMD` 的含义就发生了改变,不再是直接的运行其命令,而是将 `CMD` 的内容作为参数传给 `ENTRYPOINT` 指令,换句话说实际执行时,将变为:
-
-```bash
- ""
-```
-
-那么有了 `CMD` 后,为什么还要有 `ENTRYPOINT` 呢?这种 ` ""` 有什么好处么?让我们来看几个场景。
-
-#### 场景一:让镜像变成像命令一样使用
-
-假设我们需要一个得知自己当前公网 IP 的镜像,那么可以先用 `CMD` 来实现:
-
-```dockerfile
-FROM ubuntu:18.04
-RUN apt-get update \
- && apt-get install -y curl \
- && rm -rf /var/lib/apt/lists/*
-CMD [ "curl", "-s", "https://ip.cn" ]
-```
-
-假如我们使用 `docker build -t myip .` 来构建镜像的话,如果我们需要查询当前公网 IP,只需要执行:
-
-```bash
-$ docker run myip
-当前 IP:61.148.226.66 来自:北京市 联通
-```
-
-嗯,这么看起来好像可以直接把镜像当做命令使用了,不过命令总有参数,如果我们希望加参数呢?比如从上面的 `CMD` 中可以看到实质的命令是 `curl`,那么如果我们希望显示 HTTP 头信息,就需要加上 `-i` 参数。那么我们可以直接加 `-i` 参数给 `docker run myip` 么?
-
-```bash
-$ docker run myip -i
-docker: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".
-```
-
-我们可以看到可执行文件找不到的报错,`executable file not found`。之前我们说过,跟在镜像名后面的是 `command`,运行时会替换 `CMD` 的默认值。因此这里的 `-i` 替换了原来的 `CMD`,而不是添加在原来的 `curl -s https://ip.cn` 后面。而 `-i` 根本不是命令,所以自然找不到。
-
-那么如果我们希望加入 `-i` 这参数,我们就必须重新完整的输入这个命令:
-
-```bash
-$ docker run myip curl -s https://ip.cn -i
-```
-
-这显然不是很好的解决方案,而使用 `ENTRYPOINT` 就可以解决这个问题。现在我们重新用 `ENTRYPOINT` 来实现这个镜像:
-
-```dockerfile
-FROM ubuntu:18.04
-RUN apt-get update \
- && apt-get install -y curl \
- && rm -rf /var/lib/apt/lists/*
-ENTRYPOINT [ "curl", "-s", "https://ip.cn" ]
-```
-
-这次我们再来尝试直接使用 `docker run myip -i`:
-
-```bash
-$ docker run myip
-当前 IP:61.148.226.66 来自:北京市 联通
-
-$ docker run myip -i
-HTTP/1.1 200 OK
-Server: nginx/1.8.0
-Date: Tue, 22 Nov 2016 05:12:40 GMT
-Content-Type: text/html; charset=UTF-8
-Vary: Accept-Encoding
-X-Powered-By: PHP/5.6.24-1~dotdeb+7.1
-X-Cache: MISS from cache-2
-X-Cache-Lookup: MISS from cache-2:80
-X-Cache: MISS from proxy-2_6
-Transfer-Encoding: chunked
-Via: 1.1 cache-2:80, 1.1 proxy-2_6:8006
-Connection: keep-alive
-
-当前 IP:61.148.226.66 来自:北京市 联通
-```
-
-可以看到,这次成功了。这是因为当存在 `ENTRYPOINT` 后,`CMD` 的内容将会作为参数传给 `ENTRYPOINT`,而这里 `-i` 就是新的 `CMD`,因此会作为参数传给 `curl`,从而达到了我们预期的效果。
-
-#### 场景二:应用运行前的准备工作
-
-启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。
-
-比如 `mysql` 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的 mysql 服务器运行之前解决。
-
-此外,可能希望避免使用 `root` 用户去启动服务,从而提高安全性,而在启动服务前还需要以 `root` 身份执行一些必要的准备工作,最后切换到服务用户身份启动服务。或者除了服务外,其它命令依旧可以使用 `root` 身份执行,方便调试等。
-
-这些准备工作是和容器 `CMD` 无关的,无论 `CMD` 为什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 `ENTRYPOINT` 中去执行,而这个脚本会将接到的参数(也就是 ``)作为命令,在脚本最后执行。比如官方镜像 `redis` 中就是这么做的:
-
-```dockerfile
-FROM alpine:3.4
-...
-RUN addgroup -S redis && adduser -S -G redis redis
-...
-ENTRYPOINT ["docker-entrypoint.sh"]
-
-EXPOSE 6379
-CMD [ "redis-server" ]
-```
-
-可以看到其中为了 redis 服务创建了 redis 用户,并在最后指定了 `ENTRYPOINT` 为 `docker-entrypoint.sh` 脚本。
-
-```bash
-#!/bin/sh
-...
-# allow the container to be started with `--user`
-if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
- chown -R redis .
- exec su-exec redis "$0" "$@"
-fi
-
-exec "$@"
-```
-
-该脚本的内容就是根据 `CMD` 的内容来判断,如果是 `redis-server` 的话,则切换到 `redis` 用户身份启动服务器,否则依旧使用 `root` 身份执行。比如:
-
-```bash
-$ docker run -it redis id
-uid=0(root) gid=0(root) groups=0(root)
-```
-
-### ENV(设置环境变量)
-
-> 作用:`ENV` 指令用于设置环境变量。无论是后面的其它指令,如 `RUN`,还是运行时的应用,都可以直接使用这里定义的环境变量。
-
-格式:
-
-- `ENV `
-- `ENV = =...`
-
-示例 1:
-
-```dockerfile
-ENV VERSION=1.0 DEBUG=on \
- NAME="Happy Feet"
-```
-
-这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是一致的。
-
-示例 2:
-
-定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。比如在官方 `node` 镜像 `Dockerfile` 中,就有类似这样的代码:
-
-```dockerfile
-ENV NODE_VERSION 7.2.0
-
-RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
- && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
- && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
- && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
- && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
- && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
- && ln -s /usr/local/bin/node /usr/local/bin/nodejs
-```
-
-在这里先定义了环境变量 `NODE_VERSION`,其后的 `RUN` 这层里,多次使用 `$NODE_VERSION` 来进行操作定制。可以看到,将来升级镜像构建版本的时候,只需要更新 `7.2.0` 即可,`Dockerfile` 构建维护变得更轻松了。
-
-下列指令可以支持环境变量展开: `ADD`、`COPY`、`ENV`、`EXPOSE`、`LABEL`、`USER`、`WORKDIR`、`VOLUME`、`STOPSIGNAL`、`ONBUILD`。
-
-可以从这个指令列表里感觉到,环境变量可以使用的地方很多,很强大。通过环境变量,我们可以让一份 `Dockerfile` 制作更多的镜像,只需使用不同的环境变量即可。
-
-### ARG(构建参数)
-
-> 作用:
->
-> `Dockerfile` 中的 `ARG` 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 `docker build` 中用 `--build-arg <参数名>=<值>` 来覆盖。
->
-> 构建参数和 `ENV` 的效果一样,都是设置环境变量。所不同的是,`ARG` 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 `ARG` 保存密码之类的信息,因为 `docker history` 还是可以看到所有值的。
-
-格式:`ARG <参数名>[=<默认值>]`
-
-在 1.13 之前的版本,要求 `--build-arg` 中的参数名,必须在 `Dockerfile` 中用 `ARG` 定义过了,换句话说,就是 `--build-arg` 指定的参数,必须在 `Dockerfile` 中使用了。如果对应参数没有被使用,则会报错退出构建。从 1.13 开始,这种严格的限制被放开,不再报错退出,而是显示警告信息,并继续构建。这对于使用 CI 系统,用同样的构建流程构建不同的 `Dockerfile` 的时候比较有帮助,避免构建命令必须根据每个 Dockerfile 的内容修改。
-
-### VOLUME(定义匿名卷)
-
-格式:
-
-- `VOLUME ["<路径1>", "<路径2>"...]`
-- `VOLUME <路径>`
-
-之前我们说过,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,后面的章节我们会进一步介绍 Docker 卷的概念。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 `Dockerfile` 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
-
-```dockerfile
-VOLUME /data
-```
-
-这里的 `/data` 目录就会在运行时自动挂载为匿名卷,任何向 `/data` 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如:
-
-```dockerfile
-docker run -d -v mydata:/data xxxx
-```
-
-在这行命令中,就使用了 `mydata` 这个命名卷挂载到了 `/data` 这个位置,替代了 `Dockerfile` 中定义的匿名卷的挂载配置。
-
-### EXPOSE(暴露端口)
-
-> 作用:
->
-> `EXPOSE` 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 `docker run -P` 时,会自动随机映射 `EXPOSE` 的端口。
->
-> 要将 `EXPOSE` 和在运行时使用 `-p <宿主端口>:<容器端口>` 区分开来。`-p`,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 `EXPOSE` 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
-
-格式:`EXPOSE <端口1> [<端口2>...]`。
-
-### WORKDIR(指定工作目录)
-
-> 作用:
->
-> 使用 `WORKDIR` 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,`WORKDIR` 会帮你建立目录。
-
-格式:`WORKDIR <工作目录路径>`。
-
-示例 1:
-
-之前提到一些初学者常犯的错误是把 `Dockerfile` 等同于 Shell 脚本来书写,这种错误的理解还可能会导致出现下面这样的错误:
-
-```dockerfile
-RUN cd /app
-RUN echo "hello" > world.txt
-```
-
-如果将这个 `Dockerfile` 进行构建镜像运行后,会发现找不到 `/app/world.txt` 文件,或者其内容不是 `hello`。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 `Dockerfile` 中,这两行 `RUN` 命令的执行环境根本不同,是两个完全不同的容器。这就是对 `Dockerfile` 构建分层存储的概念不了解所导致的错误。
-
-之前说过每一个 `RUN` 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 `RUN cd /app` 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。
-
-因此如果需要改变以后各层的工作目录的位置,那么应该使用 `WORKDIR` 指令。
-
-### USER(指定当前用户)
-
-> 作用:
->
-> `USER` 指令和 `WORKDIR` 相似,都是改变环境状态并影响以后的层。`WORKDIR` 是改变工作目录,`USER` 则是改变之后层的执行 `RUN`, `CMD` 以及 `ENTRYPOINT` 这类命令的身份。
->
-> 当然,和 `WORKDIR` 一样,`USER` 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
-
-格式:`USER <用户名>[:<用户组>]`
-
-示例 1:
-
-```dockerfile
-RUN groupadd -r redis && useradd -r -g redis redis
-USER redis
-RUN [ "redis-server" ]
-```
-
-如果以 `root` 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 `su`或者 `sudo`,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 [`gosu`](https://github.com/tianon/gosu)。
-
-```dockerfile
-# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
-RUN groupadd -r redis && useradd -r -g redis redis
-# 下载 gosu
-RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
- && chmod +x /usr/local/bin/gosu \
- && gosu nobody true
-# 设置 CMD,并以另外的用户执行
-CMD [ "exec", "gosu", "redis", "redis-server" ]
-```
-
-### HEALTHCHECK(健康检查)
-
-格式:
-
-- `HEALTHCHECK [选项] CMD <命令>`:设置检查容器健康状况的命令
-- `HEALTHCHECK NONE`:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
-
-`HEALTHCHECK` 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12 引入的新指令。
-
-在没有 `HEALTHCHECK` 指令前,Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。
-
-而自 1.12 之后,Docker 提供了 `HEALTHCHECK` 指令,通过该指令指定一行命令,用这行命令来判断容器主进程的服务状态是否还正常,从而比较真实的反应容器实际状态。
-
-当在一个镜像指定了 `HEALTHCHECK` 指令后,用其启动容器,初始状态会为 `starting`,在 `HEALTHCHECK` 指令检查成功后变为 `healthy`,如果连续一定次数失败,则会变为 `unhealthy`。
-
-`HEALTHCHECK` 支持下列选项:
-
-- `--interval=<间隔>`:两次健康检查的间隔,默认为 30 秒;
-- `--timeout=<时长>`:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
-- `--retries=<次数>`:当连续失败指定次数后,则将容器状态视为 `unhealthy`,默认 3 次。
-
-和 `CMD`, `ENTRYPOINT` 一样,`HEALTHCHECK` 只可以出现一次,如果写了多个,只有最后一个生效。
-
-在 `HEALTHCHECK [选项] CMD` 后面的命令,格式和 `ENTRYPOINT` 一样,分为 `shell` 格式,和 `exec` 格式。命令的返回值决定了该次健康检查的成功与否:`0`:成功;`1`:失败;`2`:保留,不要使用这个值。
-
-假设我们有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否在正常工作,我们可以用 `curl` 来帮助判断,其 `Dockerfile` 的 `HEALTHCHECK` 可以这么写:
-
-```dockerfile
-FROM nginx
-RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
-HEALTHCHECK --interval=5s --timeout=3s \
- CMD curl -fs http://localhost/ || exit 1
-```
-
-这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 `curl -fs http://localhost/ || exit 1` 作为健康检查命令。
-
-使用 `docker build` 来构建这个镜像:
-
-```bash
-$ docker build -t myweb:v1 .
-```
-
-构建好了后,我们启动一个容器:
-
-```bash
-$ docker run -d --name web -p 80:80 myweb:v1
-```
-
-当运行该镜像后,可以通过 `docker container ls` 看到最初的状态为 `(health: starting)`:
-
-```bash
-$ docker container ls
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web
-```
-
-在等待几秒钟后,再次 `docker container ls`,就会看到健康状态变化为了 `(healthy)`:
-
-```bash
-$ docker container ls
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web
-```
-
-如果健康检查连续失败超过了重试次数,状态就会变为 `(unhealthy)`。
-
-为了帮助排障,健康检查命令的输出(包括 `stdout` 以及 `stderr`)都会被存储于健康状态里,可以用 `docker inspect` 来查看。
-
-```bash
-$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
-{
- "FailingStreak": 0,
- "Log": [
- {
- "End": "2016-11-25T14:35:37.940957051Z",
- "ExitCode": 0,
- "Output": "\n\n\nWelcome to nginx! \n\n\n\nWelcome to nginx! \nIf you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.
\n\nFor online documentation and support please refer to\nnginx.org . \nCommercial support is available at\nnginx.com .
\n\nThank you for using nginx.
\n\n\n",
- "Start": "2016-11-25T14:35:37.780192565Z"
- }
- ],
- "Status": "healthy"
-}
-```
-
-### ONBUILD(为他人作嫁衣裳)
-
-格式:`ONBUILD <其它指令>`。
-
-`ONBUILD` 是一个特殊的指令,它后面跟的是其它指令,比如 `RUN`, `COPY` 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
-
-`Dockerfile` 中的其它指令都是为了定制当前镜像而准备的,唯有 `ONBUILD` 是为了帮助别人定制自己而准备的。
-
-假设我们要制作 Node.js 所写的应用的镜像。我们都知道 Node.js 使用 `npm` 进行包管理,所有依赖、配置、启动信息等会放到 `package.json` 文件里。在拿到程序代码后,需要先进行 `npm install` 才可以获得所有需要的依赖。然后就可以通过 `npm start`来启动应用。因此,一般来说会这样写 `Dockerfile`:
-
-```dockerfile
-FROM node:slim
-RUN mkdir /app
-WORKDIR /app
-COPY ./package.json /app
-RUN [ "npm", "install" ]
-COPY . /app/
-CMD [ "npm", "start" ]
-```
-
-把这个 `Dockerfile` 放到 Node.js 项目的根目录,构建好镜像后,就可以直接拿来启动容器运行。但是如果我们还有第二个 Node.js 项目也差不多呢?好吧,那就再把这个 `Dockerfile` 复制到第二个项目里。那如果有第三个项目呢?再复制么?文件的副本越多,版本控制就越困难,让我们继续看这样的场景维护的问题。
-
-如果第一个 Node.js 项目在开发过程中,发现这个 `Dockerfile` 里存在问题,比如敲错字了、或者需要安装额外的包,然后开发人员修复了这个 `Dockerfile`,再次构建,问题解决。� 第一个项目没问题了,但是第二个项目呢?虽然最初 `Dockerfile` 是复制、粘贴自第一个项目的,但是并不会因为第一个项目修复了他们的 `Dockerfile`,而第二个项目的 `Dockerfile` 就会被自动修复。
-
-那么我们可不可以做一个基础镜像,然后各个项目使用这个基础镜像呢?这样基础镜像更新,各个项目不用同步 `Dockerfile`的变化,重新构建后就继承了基础镜像的更新?好吧,可以,让我们看看这样的结果。那么上面的这个 `Dockerfile` 就会变为:
-
-```dockerfile
-FROM node:slim
-RUN mkdir /app
-WORKDIR /app
-CMD [ "npm", "start" ]
-```
-
-这里我们把项目相关的构建指令拿出来,放到子项目里去。假设这个基础镜像的名字为 `my-node` 的话,各个项目内的自己的 `Dockerfile` 就变为:
-
-```dockerfile
-FROM my-node
-COPY ./package.json /app
-RUN [ "npm", "install" ]
-COPY . /app/
-```
-
-基础镜像变化后,各个项目都用这个 `Dockerfile` 重新构建镜像,会继承基础镜像的更新。
-
-那么,问题解决了么?没有。准确说,只解决了一半。如果这个 `Dockerfile` 里面有些东西需要调整呢?比如 `npm install` 都需要加一些参数,那怎么办?这一行 `RUN` 是不可能放入基础镜像的,因为涉及到了当前项目的 `./package.json`,难道又要一个个修改么?所以说,这样制作基础镜像,只解决了原来的 `Dockerfile` 的前 4 条指令的变化问题,而后面三条指令的变化则完全没办法处理。
-
-`ONBUILD` 可以解决这个问题。让我们用 `ONBUILD` 重新写一下基础镜像的 `Dockerfile`:
-
-```dockerfile
-FROM node:slim
-RUN mkdir /app
-WORKDIR /app
-ONBUILD COPY ./package.json /app
-ONBUILD RUN [ "npm", "install" ]
-ONBUILD COPY . /app/
-CMD [ "npm", "start" ]
-```
-
-这次我们回到原始的 `Dockerfile`,但是这次将项目相关的指令加上 `ONBUILD`,这样在构建基础镜像的时候,这三行并不会被执行。然后各个项目的 `Dockerfile` 就变成了简单地:
-
-```dockerfile
-FROM my-node
-```
-
-是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。
-
-## 引用和引申
-
-- [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/)
-- [Dockerfile 最佳实践文档](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
-- [Docker 官方镜像 Dockerfile](https://github.com/docker-library/docs)
-- [Dockerfile 指令详解](https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile/)
diff --git a/os/docker/basics/docker-helloworld.md b/os/docker/basics/docker-helloworld.md
deleted file mode 100644
index 1c034cdbc2..0000000000
--- a/os/docker/basics/docker-helloworld.md
+++ /dev/null
@@ -1,71 +0,0 @@
-# Docker 之 Hello World
-
-## 前提
-
-确保你的环境上已经成功安装 Docker。
-
-## Hello World 实例
-
-1. 使用 `docker version` 命令确保你的环境已成功安装 Docker。
-
-```
-# docker version
-Client:
- Version: 1.13.1
- API version: 1.26
- Package version:
- Go version: go1.8.3
- Git commit: 774336d/1.13.1
- Built: Wed Mar 7 17:06:16 2018
- OS/Arch: linux/amd64
-
-Server:
- Version: 1.13.1
- API version: 1.26 (minimum version 1.12)
- Package version:
- Go version: go1.8.3
- Git commit: 774336d/1.13.1
- Built: Wed Mar 7 17:06:16 2018
- OS/Arch: linux/amd64
- Experimental: false
-```
-
-2. 使用 `docker run` 命令运行 Hello World 镜像。
-
-```
-docker run hello-world
-
-Unable to find image 'hello-world:latest' locally
-latest: Pulling from library/hello-world
-ca4f61b1923c: Pull complete
-Digest: sha256:ca0eeb6fb05351dfc8759c20733c91def84cb8007aa89a5bf606bc8b315b9fc7
-Status: Downloaded newer image for hello-world:latest
-
-Hello from Docker!
-This message shows that your installation appears to be working correctly.
-...
-```
-
-3. 使用 `docker image ls`命令查看镜像
-
-```
-docker image ls
-REPOSITORY TAG IMAGE ID CREATED SIZE
-docker.io/maven latest 76c9ab5df55b 7 days ago 737 MB
-docker.io/python 2.7-slim 5541369755c4 13 days ago 139 MB
-docker.io/hello-world latest f2a91732366c 4 months ago 1.85 kB
-docker.io/java 8-jre e44d62cf8862 14 months ago 311 MB
-docker.io/training/webapp latest 6fae60ef3446 2 years ago 349 MB
-```
-
-4. 使用 `docker container ls --all` 命令查看容器
-
-如果查看正在运行的容器,不需要添加 `--all` 参数。
-
-```
-docker container ls --all
-CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
-a661d957c6fa hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago mystifying_swartz
-3098f24a1064 docker.io/hello-world "/hello" 6 minutes ago Exited (0) 6 minutes ago sad_yonath
-4c98c4f18a39 hello-world "/hello" 8 minutes ago Exited (0) 8 minutes ago admiring_banach
-```
\ No newline at end of file
diff --git a/os/docker/basics/docker-image.md b/os/docker/basics/docker-image.md
deleted file mode 100644
index ac6ca4259b..0000000000
--- a/os/docker/basics/docker-image.md
+++ /dev/null
@@ -1,491 +0,0 @@
-# Docker 镜像
-
-
-
-- [获取镜像](#获取镜像)
- - [运行](#运行)
-- [列出镜像](#列出镜像)
- - [镜像体积](#镜像体积)
- - [虚悬镜像](#虚悬镜像)
- - [中间层镜像](#中间层镜像)
- - [列出部分镜像](#列出部分镜像)
- - [以特定格式显示](#以特定格式显示)
-- [删除本地镜像](#删除本地镜像)
- - [用 ID、镜像名、摘要删除镜像](#用-id镜像名摘要删除镜像)
- - [Untagged 和 Deleted](#untagged-和-deleted)
- - [用 docker image ls 命令来配合](#用-docker-image-ls-命令来配合)
- - [CentOS/RHEL 的用户需要注意的事项](#centosrhel-的用户需要注意的事项)
-- [使用 Dockerfile 定制镜像](#使用-dockerfile-定制镜像)
- - [构建镜像](#构建镜像)
- - [镜像构建上下文(Context)](#镜像构建上下文context)
- - [其它 `docker build` 的用法](#其它-docker-build-的用法)
-
-
-
-## 获取镜像
-
-之前提到过,[Docker Hub](https://hub.docker.com/explore/) 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。
-
-从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为:
-
-```bash
-docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
-```
-
-具体的选项可以通过 `docker pull --help` 命令看到,这里我们说一下镜像名称的格式。
-
-- Docker 镜像仓库地址:地址的格式一般是 `<域名/IP>[:端口号]`。默认地址是 Docker Hub。
-- 仓库名:如之前所说,这里的仓库名是两段式名称,即 `<用户名>/<软件名>`。对于 Docker Hub,如果不给出用户名,则默认为 `library`,也就是官方镜像。
-
-比如:
-
-```bash
-$ docker pull ubuntu:18.04
-18.04: Pulling from library/ubuntu
-bf5d46315322: Pull complete
-9f13e0ac480c: Pull complete
-e8988b5b3097: Pull complete
-40af181810e7: Pull complete
-e6f7c7e5c03e: Pull complete
-Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe
-Status: Downloaded newer image for ubuntu:18.04
-```
-
-上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 `ubuntu:18.04`,因此将会获取官方镜像 `library/ubuntu` 仓库中标签为 `18.04` 的镜像。
-
-从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 `sha256` 的摘要,以确保下载一致性。
-
-在使用上面命令的时候,你可能会发现,你所看到的层 ID 以及 `sha256` 的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的 bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像。
-
-*如果从 Docker Hub 下载镜像非常缓慢,可以参照 镜像加速器 一节配置加速器。*
-
-### 运行
-
-有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 `ubuntu:18.04` 为例,如果我们打算启动里面的 `bash` 并且进行交互式操作的话,可以执行下面的命令。
-
-```bash
-$ docker run -it --rm \
- ubuntu:18.04 \
- bash
-
-root@e7009c6ce357:/# cat /etc/os-release
-NAME="Ubuntu"
-VERSION="18.04.1 LTS (Bionic Beaver)"
-ID=ubuntu
-ID_LIKE=debian
-PRETTY_NAME="Ubuntu 18.04.1 LTS"
-VERSION_ID="18.04"
-HOME_URL="https://www.ubuntu.com/"
-SUPPORT_URL="https://help.ubuntu.com/"
-BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
-PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
-VERSION_CODENAME=bionic
-UBUNTU_CODENAME=bionic
-```
-
-`docker run` 就是运行容器的命令,具体格式我们会在 [容器](https://yeasy.gitbooks.io/docker_practice/content/container) 一节进行详细讲解,我们这里简要的说明一下上面用到的参数。
-
-- `-it`:这是两个参数,一个是 `-i`:交互式操作,一个是 `-t` 终端。我们这里打算进入 `bash` 执行一些命令并查看返回结果,因此我们需要交互式终端。
-- `--rm`:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 `docker rm`。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 `--rm` 可以避免浪费空间。
-- `ubuntu:18.04`:这是指用 `ubuntu:18.04` 镜像为基础来启动容器。
-- `bash`:放在镜像名后的是**命令**,这里我们希望有个交互式 Shell,因此用的是 `bash`。
-
-进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 `cat /etc/os-release`,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 `Ubuntu 18.04.1 LTS` 系统。
-
-最后我们通过 `exit` 退出了这个容器。
-
-## 列出镜像
-
-要想列出已经下载下来的镜像,可以使用 `docker image ls` 命令。
-
-```bash
-$ docker image ls
-REPOSITORY TAG IMAGE ID CREATED SIZE
-redis latest 5f515359c7f8 5 days ago 183 MB
-nginx latest 05a60462f8ba 5 days ago 181 MB
-mongo 3.2 fe9198c04d62 5 days ago 342 MB
- 00285df0df87 5 days ago 342 MB
-ubuntu 18.04 f753707788c5 4 weeks ago 127 MB
-ubuntu latest f753707788c5 4 weeks ago 127 MB
-ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB
-```
-
-列表包含了 `仓库名`、`标签`、`镜像 ID`、`创建时间` 以及 `所占用的空间`。
-
-其中仓库名、标签在之前的基础概念章节已经介绍过了。**镜像 ID** 则是镜像的唯一标识,一个镜像可以对应多个**标签**。因此,在上面的例子中,我们可以看到 `ubuntu:18.04` 和 `ubuntu:latest` 拥有相同的 ID,因为它们对应的是同一个镜像。
-
-### 镜像体积
-
-如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,`ubuntu:18.04` 镜像大小,在这里是 `127 MB`,但是在 [Docker Hub](https://hub.docker.com/r/library/ubuntu/tags/) 显示的却是 `50 MB`。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 `docker image ls` 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小。
-
-另外一个需要注意的问题是,`docker image ls` 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
-
-你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。
-
-```bash
-$ docker system df
-
-TYPE TOTAL ACTIVE SIZE RECLAIMABLE
-Images 24 0 1.992GB 1.992GB (100%)
-Containers 1 0 62.82MB 62.82MB (100%)
-Local Volumes 9 0 652.2MB 652.2MB (100%)
-Build Cache 0B 0B
-```
-
-### 虚悬镜像
-
-上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 ``。:
-
-```bash
- 00285df0df87 5 days ago 342 MB
-```
-
-这个镜像原本是有镜像名和标签的,原来为 `mongo:3.2`,随着官方镜像维护,发布了新版本后,重新 `docker pull mongo:3.2` 时,`mongo:3.2` 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 ``。除了 `docker pull` 可能导致这种情况,`docker build` 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 `` 的镜像。这类无标签镜像也被称为 **虚悬镜像(dangling image)** ,可以用下面的命令专门显示这类镜像:
-
-```bash
-$ docker image ls -f dangling=true
-REPOSITORY TAG IMAGE ID CREATED SIZE
- 00285df0df87 5 days ago 342 MB
-```
-
-一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。
-
-```bash
-$ docker image prune
-```
-
-### 中间层镜像
-
-为了加速镜像构建、重复利用资源,Docker 会利用 **中间层镜像**。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 `docker image ls` 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 `-a` 参数。
-
-```bash
-$ docker image ls -a
-```
-
-这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。实际上,这些镜像也没必要删除,因为之前说过,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。
-
-### 列出部分镜像
-
-不加任何参数的情况下,`docker image ls` 会列出所有顶级镜像,但是有时候我们只希望列出部分镜像。`docker image ls` 有好几个参数可以帮助做到这个事情。
-
-根据仓库名列出镜像
-
-```bash
-$ docker image ls ubuntu
-REPOSITORY TAG IMAGE ID CREATED SIZE
-ubuntu 18.04 f753707788c5 4 weeks ago 127 MB
-ubuntu latest f753707788c5 4 weeks ago 127 MB
-ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB
-```
-
-列出特定的某个镜像,也就是说指定仓库名和标签
-
-```bash
-$ docker image ls ubuntu:18.04
-REPOSITORY TAG IMAGE ID CREATED SIZE
-ubuntu 18.04 f753707788c5 4 weeks ago 127 MB
-```
-
-除此以外,`docker image ls` 还支持强大的过滤器参数 `--filter`,或者简写 `-f`。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在 `mongo:3.2` 之后建立的镜像,可以用下面的命令:
-
-```bash
-$ docker image ls -f since=mongo:3.2
-REPOSITORY TAG IMAGE ID CREATED SIZE
-redis latest 5f515359c7f8 5 days ago 183 MB
-nginx latest 05a60462f8ba 5 days ago 181 MB
-```
-
-想查看某个位置之前的镜像也可以,只需要把 `since` 换成 `before` 即可。
-
-此外,如果镜像构建时,定义了 `LABEL`,还可以通过 `LABEL` 来过滤。
-
-```bash
-$ docker image ls -f label=com.example.version=0.1
-...
-```
-
-### 以特定格式显示
-
-默认情况下,`docker image ls` 会输出一个完整的表格,但是我们并非所有时候都会需要这些内容。比如,刚才删除虚悬镜像的时候,我们需要利用 `docker image ls` 把所有的虚悬镜像的 ID 列出来,然后才可以交给 `docker image rm` 命令作为参数来删除指定的这些镜像,这个时候就用到了 `-q` 参数。
-
-```bash
-$ docker image ls -q
-5f515359c7f8
-05a60462f8ba
-fe9198c04d62
-00285df0df87
-f753707788c5
-f753707788c5
-1e0c3dd64ccd
-```
-
-`--filter` 配合 `-q` 产生出指定范围的 ID 列表,然后送给另一个 `docker` 命令作为参数,从而针对这组实体成批的进行某种操作的做法在 Docker 命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成很强大的功能。因此每次在文档看到过滤器后,可以多注意一下它们的用法。
-
-另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 [Go 的模板语法](https://gohugo.io/templates/go-templates/)。
-
-比如,下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名:
-
-```bash
-$ docker image ls --format "{{.ID}}: {{.Repository}}"
-5f515359c7f8: redis
-05a60462f8ba: nginx
-fe9198c04d62: mongo
-00285df0df87:
-f753707788c5: ubuntu
-f753707788c5: ubuntu
-1e0c3dd64ccd: ubuntu
-```
-
-或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:
-
-```bash
-$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
-IMAGE ID REPOSITORY TAG
-5f515359c7f8 redis latest
-05a60462f8ba nginx latest
-fe9198c04d62 mongo 3.2
-00285df0df87
-f753707788c5 ubuntu 18.04
-f753707788c5 ubuntu latest
-1e0c3dd64ccd ubuntu 14.04
-```
-
-## 删除本地镜像
-
-如果要删除本地的镜像,可以使用 `docker image rm` 命令,其格式为:
-
-```
-$ docker image rm [选项] <镜像1> [<镜像2> ...]
-```
-
-### 用 ID、镜像名、摘要删除镜像
-
-其中,`<镜像>` 可以是 `镜像短 ID`、`镜像长 ID`、`镜像名` 或者 `镜像摘要`。
-
-比如我们有这么一些镜像:
-
-```bash
-$ docker image ls
-REPOSITORY TAG IMAGE ID CREATED SIZE
-centos latest 0584b3d2cf6d 3 weeks ago 196.5 MB
-redis alpine 501ad78535f0 3 weeks ago 21.03 MB
-docker latest cf693ec9b5c7 3 weeks ago 105.1 MB
-nginx latest e43d811ce2f4 5 weeks ago 181.5 MB
-```
-
-我们可以用镜像的完整 ID,也称为 `长 ID`,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 `短 ID` 来删除镜像。`docker image ls` 默认列出的就已经是短 ID 了,一般取前3个字符以上,只要足够区分于别的镜像就可以了。
-
-比如这里,如果我们要删除 `redis:alpine` 镜像,可以执行:
-
-```bash
-$ docker image rm 501
-Untagged: redis:alpine
-Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d
-Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7
-Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
-Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
-Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2fa
-Deleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3
-Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7
-```
-
-我们也可以用`镜像名`,也就是 `<仓库名>:<标签>`,来删除镜像。
-
-```bash
-$ docker image rm centos
-Untagged: centos:latest
-Untagged: centos@sha256:b2f9d1c0ff5f87a4743104d099a3d561002ac500db1b9bfa02a783a46e0d366c
-Deleted: sha256:0584b3d2cf6d235ee310cf14b54667d889887b838d3f3d3033acd70fc3c48b8a
-Deleted: sha256:97ca462ad9eeae25941546209454496e1d66749d53dfa2ee32bf1faabd239d38
-```
-
-当然,更精确的是使用 `镜像摘要` 删除镜像。
-
-```bash
-$ docker image ls --digests
-REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
-node slim sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 6e0c4c8e3913 3 weeks ago 214 MB
-
-$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
-Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
-```
-
-### Untagged 和 Deleted
-
-如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是 `Untagged`,另一类是 `Deleted`。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。
-
-因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 `Untagged` 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 `Delete` 行为就不会发生。所以并非所有的 `docker image rm`都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。
-
-当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 `docker pull` 看到的层数不一样的源。
-
-除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。
-
-### 用 docker image ls 命令来配合
-
-像其它可以承接多个实体的命令一样,可以使用 `docker image ls -q` 来配合使用 `docker image rm`,这样可以成批的删除希望删除的镜像。我们在“镜像列表”章节介绍过很多过滤镜像列表的方式都可以拿过来使用。
-
-比如,我们需要删除所有仓库名为 `redis` 的镜像:
-
-```bash
-$ docker image rm $(docker image ls -q redis)
-```
-
-或者删除所有在 `mongo:3.2` 之前的镜像:
-
-```bash
-$ docker image rm $(docker image ls -q -f before=mongo:3.2)
-```
-
-充分利用你的想象力和 Linux 命令行的强大,你可以完成很多非常赞的功能。
-
-### CentOS/RHEL 的用户需要注意的事项
-
-在 Ubuntu/Debian 上有 `UnionFS` 可以使用,如 `aufs` 或者 `overlay2`,而 CentOS 和 RHEL 的内核中没有相关驱动。因此对于这类系统,一般使用 `devicemapper` 驱动利用 LVM 的一些机制来模拟分层存储。这样的做法除了性能比较差外,稳定性一般也不好,而且配置相对复杂。Docker 安装在 CentOS/RHEL 上后,会默认选择 `devicemapper`,但是为了简化配置,其 `devicemapper`是跑在一个稀疏文件模拟的块设备上,也被称为 `loop-lvm`。这样的选择是因为不需要额外配置就可以运行 Docker,这是自动配置唯一能做到的事情。但是 `loop-lvm` 的做法非常不好,其稳定性、性能更差,无论是日志还是 `docker info` 中都会看到警告信息。官方文档有明确的文章讲解了如何配置块设备给 `devicemapper` 驱动做存储层的做法,这类做法也被称为配置 `direct-lvm`。
-
-除了前面说到的问题外,`devicemapper` + `loop-lvm` 还有一个缺陷,因为它是稀疏文件,所以它会不断增长。用户在使用过程中会注意到 `/var/lib/docker/devicemapper/devicemapper/data` 不断增长,而且无法控制。很多人会希望删除镜像或者可以解决这个问题,结果发现效果并不明显。原因就是这个稀疏文件的空间释放后基本不进行垃圾回收的问题。因此往往会出现即使删除了文件内容,空间却无法回收,随着使用这个稀疏文件一直在不断增长。
-
-所以对于 CentOS/RHEL 的用户来说,在没有办法使用 `UnionFS` 的情况下,一定要配置 `direct-lvm` 给 `devicemapper`,无论是为了性能、稳定性还是空间利用率。
-
-*或许有人注意到了 CentOS 7 中存在被 backports 回来的 overlay 驱动,不过 CentOS 里的这个驱动达不到生产环境使用的稳定程度,所以不推荐使用。*
-
-## 使用 Dockerfile 定制镜像
-
-从刚才的 `docker commit` 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
-
-Dockerfile 是一个文本文件,其内包含了一条条的**指令(Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
-
-还以之前定制 `nginx` 镜像为例,这次我们使用 Dockerfile 来定制。
-
-在一个空白目录中,建立一个文本文件,并命名为 `Dockerfile`:
-
-```bash
-$ mkdir mynginx
-$ cd mynginx
-$ touch Dockerfile
-```
-
-其内容为:
-
-```dockerfile
-FROM nginx
-RUN echo 'Hello, Docker! ' > /usr/share/nginx/html/index.html
-```
-
-这个 Dockerfile 很简单,一共就两行。涉及到了两条指令,`FROM` 和 `RUN`。
-
-### 构建镜像
-
-好了,让我们再回到之前定制的 nginx 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。
-
-在 `Dockerfile` 文件所在目录执行:
-
-```bash
-$ docker build -t nginx:v3 .
-Sending build context to Docker daemon 2.048 kB
-Step 1 : FROM nginx
- ---> e43d811ce2f4
-Step 2 : RUN echo 'Hello, Docker! ' > /usr/share/nginx/html/index.html
- ---> Running in 9cdc27646c7b
- ---> 44aa4490ce2c
-Removing intermediate container 9cdc27646c7b
-Successfully built 44aa4490ce2c
-```
-
-从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 `Step 2` 中,如同我们之前所说的那样,`RUN` 指令启动了一个容器 `9cdc27646c7b`,执行了所要求的命令,并最后提交了这一层 `44aa4490ce2c`,随后删除了所用到的这个容器 `9cdc27646c7b`。
-
-这里我们使用了 `docker build` 命令进行镜像构建。其格式为:
-
-```bash
-docker build [选项] <上下文路径/URL/->
-```
-
-在这里我们指定了最终镜像的名称 `-t nginx:v3`,构建成功后,我们可以像之前运行 `nginx:v2` 那样来运行这个镜像,其结果会和 `nginx:v2` 一样。
-
-### 镜像构建上下文(Context)
-
-如果注意,会看到 `docker build` 命令最后有一个 `.`。`.` 表示当前目录,而 `Dockerfile` 就在当前目录,因此不少初学者以为这个路径是在指定 `Dockerfile` 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定**上下文路径**。那么什么是上下文呢?
-
-首先我们要理解 `docker build` 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 [Docker Remote API](https://docs.docker.com/engine/reference/api/docker_remote_api/),而如 `docker` 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 `docker` 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
-
-当我们进行镜像构建的时候,并非所有定制都会通过 `RUN` 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 `COPY` 指令、`ADD` 指令等。而 `docker build` 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
-
-这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,`docker build` 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
-
-如果在 `Dockerfile` 中这么写:
-
-```Dockerfile
-COPY ./package.json /app/
-```
-
-这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`,也不是复制 `Dockerfile` 所在目录下的 `package.json`,而是复制 **上下文(context)** 目录下的 `package.json`。
-
-因此,`COPY` 这类指令中的源文件的路径都是*相对路径*。这也是初学者经常会问的为什么 `COPY ../package.json /app` 或者 `COPY /opt/xxxx /app` 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
-
-现在就可以理解刚才的命令 `docker build -t nginx:v3 .` 中的这个 `.`,实际上是在指定上下文的目录,`docker build` 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
-
-如果观察 `docker build` 输出,我们其实已经看到了这个发送上下文的过程:
-
-```bash
-$ docker build -t nginx:v3 .
-Sending build context to Docker daemon 2.048 kB
-...
-```
-
-理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 `COPY /opt/xxxx /app` 不工作后,于是干脆将 `Dockerfile` 放到了硬盘根目录去构建,结果发现 `docker build` 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 `docker build`打包整个硬盘,这显然是使用错误。
-
-一般来说,应该会将 `Dockerfile` 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 `.gitignore` 一样的语法写一个 `.dockerignore`,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
-
-那么为什么会有人误以为 `.` 是指定 `Dockerfile` 所在目录呢?这是因为在默认情况下,如果不额外指定 `Dockerfile` 的话,会将上下文目录下的名为 `Dockerfile` 的文件作为 Dockerfile。
-
-这只是默认行为,实际上 `Dockerfile` 的文件名并不要求必须为 `Dockerfile`,而且并不要求必须位于上下文目录中,比如可以用 `-f ../Dockerfile.php` 参数指定某个文件作为 `Dockerfile`。
-
-当然,一般大家习惯性的会使用默认的文件名 `Dockerfile`,以及会将其置于镜像构建上下文目录中。
-
-### 其它 `docker build` 的用法
-
-#### 直接用 Git repo 进行构建
-
-或许你已经注意到了,`docker build` 还支持从 URL 构建,比如可以直接从 Git repo 中构建:
-
-```bash
-$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14
-docker build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14
-Sending build context to Docker daemon 2.048 kB
-Step 1 : FROM gitlab/gitlab-ce:8.14.0-ce.0
-8.14.0-ce.0: Pulling from gitlab/gitlab-ce
-aed15891ba52: Already exists
-773ae8583d14: Already exists
-...
-```
-
-这行命令指定了构建所需的 Git repo,并且指定默认的 `master` 分支,构建目录为 `/8.14/`,然后 Docker 就会自己去 `git clone` 这个项目、切换到指定分支、并进入到指定目录后开始构建。
-
-#### 用给定的 tar 压缩包构建
-
-```bash
-$ docker build http://server/context.tar.gz
-```
-
-如果所给出的 URL 不是个 Git repo,而是个 `tar` 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。
-
-#### 从标准输入中读取 Dockerfile 进行构建
-
-```bash
-docker build - < Dockerfile
-```
-
-或
-
-```bash
-cat Dockerfile | docker build -
-```
-
-如果标准输入传入的是文本文件,则将其视为 `Dockerfile`,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 `COPY` 进镜像之类的事情。
-
-#### 从标准输入中读取上下文压缩包进行构建
-
-```bash
-$ docker build - < context.tar.gz
-```
-
-如果发现标准输入的文件格式是 `gzip`、`bzip2` 以及 `xz` 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。
diff --git a/os/docker/basics/docker-repository.md b/os/docker/basics/docker-repository.md
deleted file mode 100644
index 5c6a7bdfcc..0000000000
--- a/os/docker/basics/docker-repository.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Docker 仓库
-
-仓库(Repository)是集中存放镜像的地方。
-
-一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 dl.dockerpool.com/ubuntu 来说,dl.dockerpool.com 是注册服务器地址,ubuntu 是仓库名。
-
-## Docker Hub
-
-目前 Docker 官方维护了一个公共仓库 [Docker Hub](https://hub.docker.com/),其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。
-
-### 注册
-
-你可以在 [https://cloud.docker.com](https://cloud.docker.com/) 免费注册一个 Docker 账号。
-
-### 登录
-
-可以通过执行 `docker login` 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。
-
-你可以通过 `docker logout` 退出登录。
-
-### 拉取镜像
-
-你可以通过 `docker search` 命令来查找官方仓库中的镜像,并利用 `docker pull` 命令来将它下载到本地。
-
-例如以 `centos` 为关键词进行搜索:
-
-```bash
-$ docker search centos
-NAME DESCRIPTION STARS OFFICIAL AUTOMATED
-centos The official build of CentOS. 465 [OK]
-tianon/centos CentOS 5 and 6, created using rinse instea... 28
-blalor/centos Bare-bones base CentOS 6.5 image 6 [OK]
-saltstack/centos-6-minimal 6 [OK]
-tutum/centos-6.4 DEPRECATED. Use tutum/centos:6.4 instead. ... 5 [OK]
-```
-
-可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度)、是否官方创建、是否自动创建。
-
-官方的镜像说明是官方项目组创建和维护的,automated 资源允许用户验证镜像的来源和内容。
-
-根据是否是官方提供,可将镜像资源分为两类。
-
-一种是类似 `centos` 这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。
-
-还有一种类型,比如 `tianon/centos` 镜像,它是由 Docker 的用户创建并维护的,往往带有用户名称前缀。可以通过前缀 `username/` 来指定使用某个用户提供的镜像,比如 tianon 用户。
-
-另外,在查找的时候通过 `--filter=stars=N` 参数可以指定仅显示收藏数量为 `N` 以上的镜像。
-
-下载官方 `centos` 镜像到本地。
-
-```bash
-$ docker pull centos
-Pulling repository centos
-0b443ba03958: Download complete
-539c0211cd76: Download complete
-511136ea3c5a: Download complete
-7064731afe90: Download complete
-```
-
-### 推送镜像
-
-用户也可以在登录后通过 `docker push` 命令来将自己的镜像推送到 Docker Hub。
-
-以下命令中的 `username` 请替换为你的 Docker 账号用户名。
-
-```bash
-$ docker tag ubuntu:18.04 username/ubuntu:18.04
-
-$ docker image ls
-
-REPOSITORY TAG IMAGE ID CREATED SIZE
-ubuntu 18.04 275d79972a86 6 days ago 94.6MB
-username/ubuntu 18.04 275d79972a86 6 days ago 94.6MB
-
-$ docker push username/ubuntu:18.04
-
-$ docker search username
-
-NAME DESCRIPTION STARS OFFICIAL AUTOMATED
-username/ubuntu
-```
-
-### 自动创建
-
-自动创建(Automated Builds)功能对于需要经常升级镜像内程序来说,十分方便。
-
-有时候,用户创建了镜像,安装了某个软件,如果软件发布新版本则需要手动更新镜像。
-
-而自动创建允许用户通过 Docker Hub 指定跟踪一个目标网站(目前支持 [GitHub](https://github.com/) 或 [BitBucket](https://bitbucket.org/))上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。
-
-要配置自动创建,包括如下的步骤:
-
-- 创建并登录 Docker Hub,以及目标网站;
-- 在目标网站中连接帐户到 Docker Hub;
-- 在 Docker Hub 中 [配置一个自动创建](https://registry.hub.docker.com/builds/add/);
-- 选取一个目标网站中的项目(需要含 `Dockerfile`)和分支;
-- 指定 `Dockerfile` 的位置,并提交创建。
-
-之后,可以在 Docker Hub 的 [自动创建页面](https://registry.hub.docker.com/builds/) 中跟踪每次创建的状态。
-
diff --git a/os/docker/basics/docker-services.md b/os/docker/basics/docker-services.md
deleted file mode 100644
index 036a0fe32e..0000000000
--- a/os/docker/basics/docker-services.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Docker 服务
-
-## 关于服务
-
-在分布式应用程序中,应用程序的不同部分被称为“服务”。例如,如果您想象一个视频共享网站,它可能包含用于将应用程序数据存储在数据库中的服务,用户上传文件后在后台传输的服务,前端应用服务等等。
-
-服务实际上只是“生产环境中的容器”。一个服务只运行一个镜像,但它需要制定镜像的运行方式 - 应该使用哪个端口,容器应该运行多少副本,以便服务具有所需的容量,以及等等。缩放服务会更改运行该软件的容器实例的数量,从而为流程中的服务分配更多计算资源。
-
-幸运的是,使用 Docker 平台定义,运行和扩展服务非常简单 - 只需编写一个 docker-compose.yml 文件即可。
-
-## docker-compose.yml 文件
-
-它是一个YAML文件,它定义了Docker容器在生产中的行为方式。
-
-
-```
-version: "3"
-services:
- web:
- # replace username/repo:tag with your name and image details
- image: username/repo:tag
- deploy:
- replicas: 5
- resources:
- limits:
- cpus: "0.1"
- memory: 50M
- restart_policy:
- condition: on-failure
- ports:
- - "80:80"
- networks:
- - webnet
-networks:
- webnet:
-```
\ No newline at end of file
diff --git a/os/docker/docker-introduction.md b/os/docker/docker-introduction.md
deleted file mode 100644
index 0ba12defd3..0000000000
--- a/os/docker/docker-introduction.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# Docker 简介
-
-> **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。**
-
-## 概述
-
-### Docker 是什么
-
-Docker 是开发,传输和运行应用程序的开放平台。
-
-Docker 使您能够将应用程序与基础架构分开,以便快速交付软件。
-
-借助 Docker,您可以像管理应用程序一样管理基础架构。通过利用 Docker 的方法快速进行运输,测试和部署代码,您可以显著缩短编写代码和在生产环境中运行代码之间的耗时。
-
-### Docker 平台
-
-Docker 提供了被称为容器的松散隔离环境,在环境中可以打包和运行应用程序。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机的内核中运行。这意味着您可以在给定的硬件组合上运行更多容器,而不是使用虚拟机。你甚至可以在实际上是虚拟机的主机中运行 Docker 容器!
-
-Docker 提供工具和平台来管理容器的生命周期:
-
-- 使用容器开发您的应用程序及其支持组件。
-- 容器成为分发和测试你的应用程序的单元。
-- 准备好后,将您的应用程序部署到生产环境中,作为容器或协调服务。无论您的生产环境是本地数据中心,云提供商还是两者的混合,这都是一样的。
-
-### Docker 引擎
-
-Docker 引擎是一个 C/S 架构的应用,它有这些主要的组件:
-
-服务器是一个长期运行的程序,被称为守护进程。
-
-REST API 指定程序可用于与守护进程进行通信并指示其执行操作的接口。
-
-命令行客户端。
-
-
-
-CLI 使用 Docker REST API 通过脚本或直接 CLI 命令来控制 Docker 守护进程或与其进行交互。许多其他 Docker 应用程序使用底层的 API 和 CLI。
-
-守护进程创建并管理 Docker 对象,如镜像,容器,网络和卷。
-
-### 传统虚拟机和 Docker
-
-
-
-## 概念
-
-Docker 包括三个基本概念
-
-- 镜像(Image)
-- 容器(Container)
-- 仓库(Repository)
-
-### 镜像
-
-我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
-
-Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
-
-#### 分层存储
-
-因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
-
-镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
-
-分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
-
-### 容器
-
-镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
-
-容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
-
-前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为**容器存储层**。
-
-容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
-
-按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
-
-数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
-
-### 仓库
-
-镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,[Docker Registry](https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html) 就是这样的服务。
-
-一个 **Docker Registry** 中可以包含多个**仓库**(`Repository`);每个仓库可以包含多个**标签**(`Tag`);每个标签对应一个镜像。
-
-通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 `latest` 作为默认标签。
-
-以 [Ubuntu 镜像](https://store.docker.com/images/ubuntu) 为例,`ubuntu` 是仓库的名字,其内包含有不同的版本标签,如,`16.04`, `18.04`。我们可以通过 `ubuntu:14.04`,或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 `ubuntu`,那将视为 `ubuntu:latest`。
-
-仓库名经常以 _两段式路径_ 形式出现,比如 `jwilder/nginx-proxy`,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
-
-## 引用和引申
-
-- https://yeasy.gitbooks.io/docker_practice/content/basic_concept/repository.html
diff --git a/os/docker/docker-quickstart.md b/os/docker/docker-quickstart.md
deleted file mode 100644
index e88355cd81..0000000000
--- a/os/docker/docker-quickstart.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Docker 入门
-
-
-
-- [Hello World 示例](#hello-world-示例)
-
-
-
-## Hello World 示例
-
-(1)拉取镜像
-
-```
-docker image pull library/hello-world
-```
-
-`docker image pull` 是抓取 image 文件的命令。`library/hello-world` 是 image 文件在仓库里面的位置,其中 `library` 是 image 文件所在的组,`hello-world` 是 image 文件的名字。
-
-由于 Docker 官方提供的 image 文件,都放在[`library`](https://hub.docker.com/r/library/)组里面,所以它的是默认组,可以省略。因此,上面的命令可以写成下面这样。
-
-```
-docker image pull hello-world
-```
-
-(2)查看镜像
-
-```sh
-~ docker image ls
-REPOSITORY TAG IMAGE ID CREATED SIZE
-hello-world latest 4ab4c602aa5e 3 months ago 1.84kB
-```
-
-(3)运行镜像
-
-```
-docker container run hello-world
-```
-
-`docker container run` 命令会从 image 文件,生成一个正在运行的容器实例。
-
-注意,`docker container run` 命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的 `docker image pull` 命令并不是必需的步骤。
-
-如果运行成功,你会在屏幕上读到下面的输出。
diff --git a/os/docker/docker.md b/os/docker/docker.md
deleted file mode 100644
index a115151025..0000000000
--- a/os/docker/docker.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Docker 教程
-
-## [简介](docker-introduction.md)
-
-> **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。**
->
-> Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
-
-
-
-## [入门篇](docker-quickstart.md)
-
-## 基础篇
-
-### 安装
-
-Docker 分为 CE 和 EE 两大版本。
-
-- CE 即社区版(免费,支持周期 7 个月)。Docker CE 分为 `stable`, `test`, 和 `nightly` 三个更新频道。每六个月发布一个 stable 版本。
-- EE 即企业版,强调安全,付费使用,支持周期 24 个月。
-
-Docker CE 可以安装在 Linux 、Windows 10 (PC) 和 MAC 上。
-
-> 参考:
->
-> - [官方安装指南](https://docs.docker.com/install/)
-> - [Docker 中文教程安装指南](https://yeasy.gitbooks.io/docker_practice/content/install/)
-
-### [Docker 镜像](basics/docker-image.md)
-
-### [Docker 容器](basics/docker-container.md)
-
-### [Dockerfile](basics/docker-dockerfile.md)
-
-- FROM(指定基础镜像)
-- RUN(执行命令)
-- COPY(复制文件)
-- ADD(更高级的复制文件)
-- CMD(容器启动命令)
-- ENTRYPOINT(入口点)
-- ENV(设置环境变量)
-- ARG(构建参数)
-- VOLUME(定义匿名卷)
-- EXPOSE(暴露端口)
-- WORKDIR(指定工作目录)
-- USER(指定当前用户)
-- HEALTHCHECK(健康检查)
-- ONBUILD(为他人作嫁衣裳)
-
-## 进阶篇
-
-### 设计
-
-## 实战篇
-
-## 常见问题
-
-## 附录
-
-### [命令](appendix/docker-cli.md)
-
-### [资源](appendix/docker-resources.md)
-
-### 术语
-
-### 技巧
-
-### 版本
-
-### 反馈
diff --git a/os/docker/docker.xmind b/os/docker/docker.xmind
deleted file mode 100644
index e57d9582bd..0000000000
Binary files a/os/docker/docker.xmind and /dev/null differ
diff --git a/os/docker/practice/docker-install-mysql.md b/os/docker/practice/docker-install-mysql.md
deleted file mode 100644
index 3cfbdaa14c..0000000000
--- a/os/docker/practice/docker-install-mysql.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Docker 安装 MySQL
-
-> 实测环境:Centos
-
-## 查看可下载镜像
-
-```docker
-# docker search mysql
-INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
-docker.io docker.io/mysql MySQL is a widely used, open-source relati... 5757 [OK]
-docker.io docker.io/mariadb MariaDB is a community-developed fork of M... 1863 [OK]
-docker.io docker.io/mysql/mysql-server Optimized MySQL Server Docker images. Crea... 397 [OK]
-...
-```
-
-## 选择下载官方镜像
-
-比如,我想下载最新版本,则执行如下命令:
-
-```docker
-docker pull mysql
-```
-
-## 使用镜像
-
-```docker
-docker run -p 3306:3306 --name mysql -v /opt/docker_v/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql
-```
-
-## 资源
-
-* https://hub.docker.com/_/mysql/
diff --git a/os/docker/practice/docker-install-nginx.md b/os/docker/practice/docker-install-nginx.md
deleted file mode 100644
index 32c4fe8203..0000000000
--- a/os/docker/practice/docker-install-nginx.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Docker 安装 Nginx
-
-> 实测环境:Centos
-
-## 查看可用镜像
-
-执行 `docker search nginx` 命令查看可用镜像:
-
-```docker
-# docker search nginx
-INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
-docker.io docker.io/nginx Official build of Nginx. 8272 [OK]
-docker.io docker.io/jwilder/nginx-proxy Automated Nginx reverse proxy for docker c... 1300 [OK]
-docker.io docker.io/richarvey/nginx-php-fpm Container running Nginx + PHP-FPM capable ... 540 [OK]
-docker.io docker.io/jrcs/letsencrypt-nginx-proxy-companion LetsEncrypt container to use with nginx as... 336 [OK]
-...
-```
-
-## 选择下载镜像
-
-执行 `docker pull nginx` 命令下载镜像
-
-## 运行镜像
-
-```
-docker run -p 80:80 --name mynginx -d nginx
-```
-
diff --git a/os/git/README.md b/os/git/README.md
deleted file mode 100644
index b4db8a2570..0000000000
--- a/os/git/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# git
-
-## 目录
-
-* [快速指南(quickstart)](git-quickstart.md)
-* 基础篇(basics)
- * [安装(installation)](basics/git-installation.md)
- * [配置(configuration)](basics/git-configuration.md)
-* 进阶篇(advanced)
- * [git-flow 工作流](advanced/git-flow.md)
-* 附录(appendix)
- * [常见问题(faq)](appendix/git-faq.md)
- * [命令(command)](appendix/git-command.md)
- * [资源(resource)](appendix/git-resource.md)
diff --git a/os/git/advanced/git-flow.md b/os/git/advanced/git-flow.md
deleted file mode 100644
index 365aa40f2e..0000000000
--- a/os/git/advanced/git-flow.md
+++ /dev/null
@@ -1,263 +0,0 @@
----
-title: git-flow 工作流
-date: 2017-12-06
-categories:
-- linux
-tags:
-- linux
-- vcs
-- git
----
-
-# git-flow 工作流
-
-## 集中式 vs 分布式
-
-在分布式版本控制工具 [**Git**](https://git-scm.com/) 之前,我一直用的是集中式版本控制工具,具体来说,我用过 **[ClearCase](http://www-03.ibm.com/software/products/en/clearcase)**(简称 cc)、**[Subversion](https://tortoisesvn.net/)**(简称 svn)。
-
-这里,结合我的一点个人经验,谈谈这几个工具的特点。
-
-### 集中式版本控制
-
-**[ClearCase](http://www-03.ibm.com/software/products/en/clearcase)** 是 IBM 的产品。IBM 的产品总给人一种过于重型化的感觉,似乎它们的产品什么都想做,结果反而让人觉得太复杂,不好用(想想曾经被 Notes 邮箱支配的恐惧吧)。**[ClearCase](http://www-03.ibm.com/software/products/en/clearcase)** 一如既往,功能复杂,但是操作很繁琐。
-
-**[Subversion](https://tortoisesvn.net/)** 由于 logo 是乌龟,所以也常被称为小乌龟。它的优点是:GUI 工具做的很细致,易于上手;安全控制和权限控制非常细粒度(目录级别的管控);对于特别大型的项目,每个开发者都拷贝完整的镜像到本地,会占用较大的磁盘空间,还不如将所有数据都存放在大型服务器上。
-
-### 分布式版本控制
-
-分布式版本控制工具当然不止 Git,但是受 Github 的影响,Git 无疑是最火,用户最广的工具。
-
-Git 的优点主要就体现在分布式设计上:
-
-- 支持离线工作。除了同步远程仓库,基本都是在本地完成工作。
-- 安全稳定。集中式版本控制工具,要求所有机器将每次修改都推送到中央服务器,一旦中央服务器宕机,所有人都不能工作。分布式版本控制工具的每台机器克隆远程仓库到本地后,都拥有完整的镜像,一台机器宕机,不影响其他人工作,而且数据容易恢复。
-- Git 自身有着优秀的分支模型,拉分支、打标签都非常便捷。
-
-### 小结
-
-**[ClearCase](http://www-03.ibm.com/software/products/en/clearcase)** 比较难用,谁用谁知道。
-
-**[Subversion](https://tortoisesvn.net/)** 由于自身的优点,还是有不少公司仍在使用的。不能说不使用分布式版本控制就一定是 out 了,很多时候也是基于公司应用场景的考量(大公司往往更看重安全和权限控制)。个人认为 svn 还是挺好用的,除了拉分支确实比较麻烦些,但这也是基于 svn 本身权限控制的考虑。
-
-[**Git**](https://git-scm.com/) 由于其开源免费、分布式特性,深受互联网公司的青睐。
-
-## 版本管理的挑战
-
-Git 是一个非常优秀的版本控制工具,但是在实际版本管理中,仍然会遇到一些问题:
-
-1. 如何开始一个Feature的开发,而不影响别的Feature?
-2. 由于很容易创建新分支,分支多了如何管理,时间久了,如何知道每个分支是干什么的?
-3. 哪些分支已经合并回了主干?
-4. 如何进行Release的管理?开始一个Release的时候如何冻结Feature, 如何在Prepare Release的时候,开发人员可以继续开发新的功能?
-5. 线上代码出Bug了,如何快速修复?而且修复的代码要包含到开发人员的分支以及下一个Release?
-
-大部分开发人员现在使用Git就只是用三个甚至两个分支,一个是Master, 一个是Develop, 还有一个是基于Develop打得各种分支。这个在小项目规模的时候还勉强可以支撑,因为很多人做项目就只有一个Release, 但是人员一多,而且项目周期一长就会出现各种问题。
-
-## git-flow 模型
-
-[Vincent Driessen](http://nvie.com/about/) 同学发表了博客 [A successful Git branching model](http://nvie.com/git-model) ,文章讲解了他是如何让自己的[Git](http://www.oschina.net/p/git)仓库保持整洁,除此之外,他发布了git-flow; 一个可以轻松实现该模型的Git扩展。
-
-这个最佳实践已经成为最为流行的 git 分支模型,一些 GUI 工具甚至已经支持建立这种模型。
-
-下面,来介绍一下 git-flow 模型。
-
-
-
-`Gitflow`工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并`push`分支到要中央仓库中。
-
-### 核心分支
-
-`Gitflow`工作流使用2个分支来记录项目的历史。
-
-`master` 分支对应线上实际的发布版本。
-
-`develop` 分支作为功能的集成分支。
-
-
-
-### 功能分支
-
-每个新功能位于一个自己的分支,这样可以[`push`到中央仓库以备份和协作](https://www.atlassian.com/git/tutorial/remote-repositories#!push)。但功能分支不是从`master`分支上拉出新分支,而是使用`develop`分支作为父分支。当新功能完成时,[合并回`develop`分支](https://www.atlassian.com/git/tutorial/git-branches#!merge)。新功能提交应该从不直接与`master`分支交互。
-
-
-
-注意,从各种含义和目的上来看,功能分支加上`develop`分支就是功能分支工作流的用法。但`Gitflow`工作流没有在这里止步。
-
-### 发布分支
-
-
-
-一旦`develop`分支上有了做一次发布(或者说快到了既定的发布日)的足够功能,就从`develop`分支上`fork`一个发布分支。新建的分支用于开始发布循环,所以从这个时间点开始之后新的功能不能再加到这个分支上 —— 这个分支只应该做`Bug`修复、文档生成和其它面向发布任务。一旦对外发布的工作都完成了,发布分支合并到`master`分支并分配一个版本号打好`Tag`。另外,这些从新建发布分支以来的做的修改要合并回`develop`分支。
-
-使用一个用于发布准备的专门分支,使得一个团队可以在完善当前的发布版本的同时,另一个团队可以继续开发下个版本的功能。
-这也打造定义良好的开发阶段(比如,可以很轻松地说,『这周我们要做准备发布版本4.0』,并且在仓库的目录结构中可以实际看到)。
-
-常用的分支约定:
-
-`用于新建发布分支的分支: develop用于合并的分支: master分支命名: release-* 或 release/*`
-
-### 维护分支
-
-
-
-维护分支或说是热修复(`hotfix`)分支用于生成快速给产品发布版本(`production releases`)打补丁,这是唯一可以直接从`master`分支`fork`出来的分支。修复完成,修改应该马上合并回`master`分支和`develop`分支(当前的发布分支),`master`分支应该用新的版本号打好`Tag`。
-
-为`Bug`修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。你可以把维护分支想成是一个直接在`master`分支上处理的临时发布。
-
-## git-flow 代码示例
-
-a. 创建develop分支
-
-```sh
-git branch develop
-git push -u origin develop
-```
-
-b. 开始新Feature开发
-
-```sh
-git checkout -b some-feature develop
-# Optionally, push branch to origin:
-git push -u origin some-feature
-
-# 做一些改动
-git status
-git add some-file
-git commit
-```
-
-c. 完成Feature
-
-```sh
-git pull origin develop
-git checkout develop
-git merge --no-ff some-feature
-git push origin develop
-
-git branch -d some-feature
-
-# If you pushed branch to origin:
-git push origin --delete some-feature
-```
-
-d. 开始Relase
-
-```sh
-git checkout -b release-0.1.0 develop
-
-# Optional: Bump version number, commit
-# Prepare release, commit
-```
-
-e. 完成Release
-
-```sh
-git checkout master
-git merge --no-ff release-0.1.0
-git push
-
-git checkout develop
-git merge --no-ff release-0.1.0
-git push
-
-git branch -d release-0.1.0
-
-# If you pushed branch to origin:
-git push origin --delete release-0.1.0
-
-
-git tag -a v0.1.0 master
-git push --tags
-```
-
-f. 开始Hotfix
-
-```sh
-git checkout -b hotfix-0.1.1 master
-```
-
-g. 完成Hotfix
-
-```sh
-git checkout master
-git merge --no-ff hotfix-0.1.1
-git push
-
-
-git checkout develop
-git merge --no-ff hotfix-0.1.1
-git push
-
-git branch -d hotfix-0.1.1
-
-git tag -a v0.1.1 master
-git push --tags
-```
-
-## git-flow 工具
-
-实际上,当你理解了上面的流程后,你完全不用使用工具,但是实际上我们大部分人很多命令就是记不住呀,流程就是记不住呀,肿么办呢?
-
-总有聪明的人创造好的工具给大家用, 那就是Git flow script.
-
-### git-flow
-
-#### 安装
-
-你需要有一个可以工作的 git 作为前提。
-
-Git flow 可以工作在 OSX, Linux 和 Windows之下
-
-- OSX Homebrew
-
-```
-$ brew install git-flow
-```
-
-- OSX Macports
-
-```
-$ port install git-flow
-```
-
-- Linux
-
-```
-$ apt-get install git-flow
-```
-
-- Windows (Cygwin):
-
-安装 git-flow, 你需要 wget 和 util-linux。
-
-```
-$ wget -q -O - --no-check-certificate https://github.com/nvie/gitflow/raw/develop/contrib/gitflow-installer.sh | bash
-```
-
-#### 使用
-
-- **初始化:** git flow init
-- **开始新Feature:** git flow feature start MYFEATURE
-- **Publish一个Feature(也就是push到远程):** git flow feature publish MYFEATURE
-- **获取Publish的Feature:** git flow feature pull origin MYFEATURE
-- **完成一个Feature:** git flow feature finish MYFEATURE
-- **开始一个Release:** git flow release start RELEASE [BASE]
-- **Publish一个Release:** git flow release publish RELEASE
-- **发布Release:** git flow release finish RELEASE
- 别忘了git push --tags
-- **开始一个Hotfix:** git flow hotfix start VERSION [BASENAME]
-- **发布一个Hotfix:** git flow hotfix finish VERSION
-
-
-
-### Source Tree
-
-[Source Tree](https://www.sourcetreeapp.com/) 支持Mac, Windows, Linux,是一个不过的 git-flow GUI 工具。
-
-## 资料
-
-[git 官网](https://git-scm.com/) | [git 官方 Github](https://github.com/git/git)
-
-[廖雪峰的 git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
-
-[https://github.com/arslanbilal/git-cheat-sheet/blob/master/other-sheets/git-cheat-sheet-zh.md](https://github.com/arslanbilal/git-cheat-sheet/blob/master/other-sheets/git-cheat-sheet-zh.md)
diff --git a/os/git/appendix/git-command.md b/os/git/appendix/git-command.md
deleted file mode 100644
index 2610843088..0000000000
--- a/os/git/appendix/git-command.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Git 命令
-
-[github-git-cheat-sheet](https://services.github.com/on-demand/downloads/github-git-cheat-sheet.pdf)
diff --git a/os/git/appendix/git-faq.md b/os/git/appendix/git-faq.md
deleted file mode 100644
index bdec72157f..0000000000
--- a/os/git/appendix/git-faq.md
+++ /dev/null
@@ -1,914 +0,0 @@
-
-
-- [Git 常见问题](#git-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98)
- - [编辑提交(editting commits)](#%E7%BC%96%E8%BE%91%E6%8F%90%E4%BA%A4editting-commits)
- - [我刚才提交了什么](#%E6%88%91%E5%88%9A%E6%89%8D%E6%8F%90%E4%BA%A4%E4%BA%86%E4%BB%80%E4%B9%88)
- - [我的提交信息(commit message)写错了](#%E6%88%91%E7%9A%84%E6%8F%90%E4%BA%A4%E4%BF%A1%E6%81%AFcommit-message%E5%86%99%E9%94%99%E4%BA%86)
- - [我提交(commit)里的用户名和邮箱不对](#%E6%88%91%E6%8F%90%E4%BA%A4commit%E9%87%8C%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E9%82%AE%E7%AE%B1%E4%B8%8D%E5%AF%B9)
- - [我想从一个提交(commit)里移除一个文件](#%E6%88%91%E6%83%B3%E4%BB%8E%E4%B8%80%E4%B8%AA%E6%8F%90%E4%BA%A4commit%E9%87%8C%E7%A7%BB%E9%99%A4%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6)
- - [我想删除我的的最后一次提交(commit)](#%E6%88%91%E6%83%B3%E5%88%A0%E9%99%A4%E6%88%91%E7%9A%84%E7%9A%84%E6%9C%80%E5%90%8E%E4%B8%80%E6%AC%A1%E6%8F%90%E4%BA%A4commit)
- - [删除任意提交(commit)](#%E5%88%A0%E9%99%A4%E4%BB%BB%E6%84%8F%E6%8F%90%E4%BA%A4commit)
- - [我尝试推一个修正后的提交(amended commit)到远程,但是报错:](#%E6%88%91%E5%B0%9D%E8%AF%95%E6%8E%A8%E4%B8%80%E4%B8%AA%E4%BF%AE%E6%AD%A3%E5%90%8E%E7%9A%84%E6%8F%90%E4%BA%A4amended-commit%E5%88%B0%E8%BF%9C%E7%A8%8B%EF%BC%8C%E4%BD%86%E6%98%AF%E6%8A%A5%E9%94%99%EF%BC%9A)
- - [我意外的做了一次硬重置(hard reset),我想找回我的内容](#%E6%88%91%E6%84%8F%E5%A4%96%E7%9A%84%E5%81%9A%E4%BA%86%E4%B8%80%E6%AC%A1%E7%A1%AC%E9%87%8D%E7%BD%AEhard-reset%EF%BC%8C%E6%88%91%E6%83%B3%E6%89%BE%E5%9B%9E%E6%88%91%E7%9A%84%E5%86%85%E5%AE%B9)
- - [暂存(Staging)](#%E6%9A%82%E5%AD%98staging)
- - [我需要把暂存的内容添加到上一次的提交(commit)](#%E6%88%91%E9%9C%80%E8%A6%81%E6%8A%8A%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E6%B7%BB%E5%8A%A0%E5%88%B0%E4%B8%8A%E4%B8%80%E6%AC%A1%E7%9A%84%E6%8F%90%E4%BA%A4commit)
- - [我想要暂存一个新文件的一部分,而不是这个文件的全部](#%E6%88%91%E6%83%B3%E8%A6%81%E6%9A%82%E5%AD%98%E4%B8%80%E4%B8%AA%E6%96%B0%E6%96%87%E4%BB%B6%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%EF%BC%8C%E8%80%8C%E4%B8%8D%E6%98%AF%E8%BF%99%E4%B8%AA%E6%96%87%E4%BB%B6%E7%9A%84%E5%85%A8%E9%83%A8)
- - [我想把在一个文件里的变化(changes)加到两个提交(commit)里](#%E6%88%91%E6%83%B3%E6%8A%8A%E5%9C%A8%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E9%87%8C%E7%9A%84%E5%8F%98%E5%8C%96changes%E5%8A%A0%E5%88%B0%E4%B8%A4%E4%B8%AA%E6%8F%90%E4%BA%A4commit%E9%87%8C)
- - [我想把暂存的内容变成未暂存,把未暂存的内容暂存起来](#%E6%88%91%E6%83%B3%E6%8A%8A%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E5%8F%98%E6%88%90%E6%9C%AA%E6%9A%82%E5%AD%98%EF%BC%8C%E6%8A%8A%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E6%9A%82%E5%AD%98%E8%B5%B7%E6%9D%A5)
- - [未暂存(Unstaged)的内容](#%E6%9C%AA%E6%9A%82%E5%AD%98unstaged%E7%9A%84%E5%86%85%E5%AE%B9)
- - [我想把未暂存的内容移动到一个新分支](#%E6%88%91%E6%83%B3%E6%8A%8A%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E7%A7%BB%E5%8A%A8%E5%88%B0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%88%86%E6%94%AF)
- - [我想把未暂存的内容移动到另一个已存在的分支](#%E6%88%91%E6%83%B3%E6%8A%8A%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9%E7%A7%BB%E5%8A%A8%E5%88%B0%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%B7%B2%E5%AD%98%E5%9C%A8%E7%9A%84%E5%88%86%E6%94%AF)
- - [我想丢弃本地未提交的变化(uncommitted changes)](#%E6%88%91%E6%83%B3%E4%B8%A2%E5%BC%83%E6%9C%AC%E5%9C%B0%E6%9C%AA%E6%8F%90%E4%BA%A4%E7%9A%84%E5%8F%98%E5%8C%96uncommitted-changes)
- - [我想丢弃某些未暂存的内容](#%E6%88%91%E6%83%B3%E4%B8%A2%E5%BC%83%E6%9F%90%E4%BA%9B%E6%9C%AA%E6%9A%82%E5%AD%98%E7%9A%84%E5%86%85%E5%AE%B9)
- - [分支(Branches)](#%E5%88%86%E6%94%AFbranches)
- - [我从错误的分支拉取了内容,或把内容拉取到了错误的分支](#%E6%88%91%E4%BB%8E%E9%94%99%E8%AF%AF%E7%9A%84%E5%88%86%E6%94%AF%E6%8B%89%E5%8F%96%E4%BA%86%E5%86%85%E5%AE%B9%EF%BC%8C%E6%88%96%E6%8A%8A%E5%86%85%E5%AE%B9%E6%8B%89%E5%8F%96%E5%88%B0%E4%BA%86%E9%94%99%E8%AF%AF%E7%9A%84%E5%88%86%E6%94%AF)
- - [我想扔掉本地的提交(commit),以便我的分支与远程的保持一致](#%E6%88%91%E6%83%B3%E6%89%94%E6%8E%89%E6%9C%AC%E5%9C%B0%E7%9A%84%E6%8F%90%E4%BA%A4commit%EF%BC%8C%E4%BB%A5%E4%BE%BF%E6%88%91%E7%9A%84%E5%88%86%E6%94%AF%E4%B8%8E%E8%BF%9C%E7%A8%8B%E7%9A%84%E4%BF%9D%E6%8C%81%E4%B8%80%E8%87%B4)
- - [我需要提交到一个新分支,但错误的提交到了master](#%E6%88%91%E9%9C%80%E8%A6%81%E6%8F%90%E4%BA%A4%E5%88%B0%E4%B8%80%E4%B8%AA%E6%96%B0%E5%88%86%E6%94%AF%EF%BC%8C%E4%BD%86%E9%94%99%E8%AF%AF%E7%9A%84%E6%8F%90%E4%BA%A4%E5%88%B0%E4%BA%86master)
- - [我想保留来自另外一个ref-ish的整个文件](#%E6%88%91%E6%83%B3%E4%BF%9D%E7%95%99%E6%9D%A5%E8%87%AA%E5%8F%A6%E5%A4%96%E4%B8%80%E4%B8%AAref-ish%E7%9A%84%E6%95%B4%E4%B8%AA%E6%96%87%E4%BB%B6)
- - [我把几个提交(commit)提交到了同一个分支,而这些提交应该分布在不同的分支里](#%E6%88%91%E6%8A%8A%E5%87%A0%E4%B8%AA%E6%8F%90%E4%BA%A4commit%E6%8F%90%E4%BA%A4%E5%88%B0%E4%BA%86%E5%90%8C%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF%EF%BC%8C%E8%80%8C%E8%BF%99%E4%BA%9B%E6%8F%90%E4%BA%A4%E5%BA%94%E8%AF%A5%E5%88%86%E5%B8%83%E5%9C%A8%E4%B8%8D%E5%90%8C%E7%9A%84%E5%88%86%E6%94%AF%E9%87%8C)
- - [我想删除上游(upstream)分支被删除了的本地分支](#%E6%88%91%E6%83%B3%E5%88%A0%E9%99%A4%E4%B8%8A%E6%B8%B8upstream%E5%88%86%E6%94%AF%E8%A2%AB%E5%88%A0%E9%99%A4%E4%BA%86%E7%9A%84%E6%9C%AC%E5%9C%B0%E5%88%86%E6%94%AF)
- - [我不小心删除了我的分支](#%E6%88%91%E4%B8%8D%E5%B0%8F%E5%BF%83%E5%88%A0%E9%99%A4%E4%BA%86%E6%88%91%E7%9A%84%E5%88%86%E6%94%AF)
- - [我想删除一个分支](#%E6%88%91%E6%83%B3%E5%88%A0%E9%99%A4%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF)
- - [我想从别人正在工作的远程分支签出(checkout)一个分支](#%E6%88%91%E6%83%B3%E4%BB%8E%E5%88%AB%E4%BA%BA%E6%AD%A3%E5%9C%A8%E5%B7%A5%E4%BD%9C%E7%9A%84%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF%E7%AD%BE%E5%87%BAcheckout%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF)
- - [Rebasing 和合并(Merging)](#rebasing-%E5%92%8C%E5%90%88%E5%B9%B6merging)
- - [我想撤销rebase/merge](#%E6%88%91%E6%83%B3%E6%92%A4%E9%94%80rebasemerge)
- - [我已经rebase过, 但是我不想强推(force push)](#%E6%88%91%E5%B7%B2%E7%BB%8Frebase%E8%BF%87-%E4%BD%86%E6%98%AF%E6%88%91%E4%B8%8D%E6%83%B3%E5%BC%BA%E6%8E%A8force-push)
- - [我需要组合(combine)几个提交(commit)](#%E6%88%91%E9%9C%80%E8%A6%81%E7%BB%84%E5%90%88combine%E5%87%A0%E4%B8%AA%E6%8F%90%E4%BA%A4commit)
- - [安全合并(merging)策略](#%E5%AE%89%E5%85%A8%E5%90%88%E5%B9%B6merging%E7%AD%96%E7%95%A5)
- - [我需要将一个分支合并成一个提交(commit)](#%E6%88%91%E9%9C%80%E8%A6%81%E5%B0%86%E4%B8%80%E4%B8%AA%E5%88%86%E6%94%AF%E5%90%88%E5%B9%B6%E6%88%90%E4%B8%80%E4%B8%AA%E6%8F%90%E4%BA%A4commit)
- - [我只想组合(combine)未推的提交(unpushed commit)](#%E6%88%91%E5%8F%AA%E6%83%B3%E7%BB%84%E5%90%88combine%E6%9C%AA%E6%8E%A8%E7%9A%84%E6%8F%90%E4%BA%A4unpushed-commit)
- - [检查是否分支上的所有提交(commit)都合并(merge)过了](#%E6%A3%80%E6%9F%A5%E6%98%AF%E5%90%A6%E5%88%86%E6%94%AF%E4%B8%8A%E7%9A%84%E6%89%80%E6%9C%89%E6%8F%90%E4%BA%A4commit%E9%83%BD%E5%90%88%E5%B9%B6merge%E8%BF%87%E4%BA%86)
- - [交互式rebase(interactive rebase)可能出现的问题](#%E4%BA%A4%E4%BA%92%E5%BC%8Frebaseinteractive-rebase%E5%8F%AF%E8%83%BD%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98)
- - [这个rebase 编辑屏幕出现'noop'](#%E8%BF%99%E4%B8%AArebase-%E7%BC%96%E8%BE%91%E5%B1%8F%E5%B9%95%E5%87%BA%E7%8E%B0noop)
- - [有冲突的情况](#%E6%9C%89%E5%86%B2%E7%AA%81%E7%9A%84%E6%83%85%E5%86%B5)
- - [杂项(Miscellaneous Objects)](#%E6%9D%82%E9%A1%B9miscellaneous-objects)
- - [克隆所有子模块](#%E5%85%8B%E9%9A%86%E6%89%80%E6%9C%89%E5%AD%90%E6%A8%A1%E5%9D%97)
- - [删除标签(tag)](#%E5%88%A0%E9%99%A4%E6%A0%87%E7%AD%BEtag)
- - [恢复已删除标签(tag)](#%E6%81%A2%E5%A4%8D%E5%B7%B2%E5%88%A0%E9%99%A4%E6%A0%87%E7%AD%BEtag)
- - [已删除补丁(patch)](#%E5%B7%B2%E5%88%A0%E9%99%A4%E8%A1%A5%E4%B8%81patch)
- - [跟踪文件(Tracking Files)](#%E8%B7%9F%E8%B8%AA%E6%96%87%E4%BB%B6tracking-files)
- - [我只想改变一个文件名字的大小写,而不修改内容](#%E6%88%91%E5%8F%AA%E6%83%B3%E6%94%B9%E5%8F%98%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%E5%90%8D%E5%AD%97%E7%9A%84%E5%A4%A7%E5%B0%8F%E5%86%99%EF%BC%8C%E8%80%8C%E4%B8%8D%E4%BF%AE%E6%94%B9%E5%86%85%E5%AE%B9)
- - [我想从Git删除一个文件,但保留该文件](#%E6%88%91%E6%83%B3%E4%BB%8Egit%E5%88%A0%E9%99%A4%E4%B8%80%E4%B8%AA%E6%96%87%E4%BB%B6%EF%BC%8C%E4%BD%86%E4%BF%9D%E7%95%99%E8%AF%A5%E6%96%87%E4%BB%B6)
- - [配置(Configuration)](#%E9%85%8D%E7%BD%AEconfiguration)
- - [我想给一些Git命令添加别名(alias)](#%E6%88%91%E6%83%B3%E7%BB%99%E4%B8%80%E4%BA%9Bgit%E5%91%BD%E4%BB%A4%E6%B7%BB%E5%8A%A0%E5%88%AB%E5%90%8Dalias)
- - [我想缓存一个仓库(repository)的用户名和密码](#%E6%88%91%E6%83%B3%E7%BC%93%E5%AD%98%E4%B8%80%E4%B8%AA%E4%BB%93%E5%BA%93repository%E7%9A%84%E7%94%A8%E6%88%B7%E5%90%8D%E5%92%8C%E5%AF%86%E7%A0%81)
- - [我不知道我做错了些什么](#%E6%88%91%E4%B8%8D%E7%9F%A5%E9%81%93%E6%88%91%E5%81%9A%E9%94%99%E4%BA%86%E4%BA%9B%E4%BB%80%E4%B9%88)
- - [资源](#%E8%B5%84%E6%BA%90)
-
-
-
-# Git 常见问题
-
-## 编辑提交(editting commits)
-
-### 我刚才提交了什么
-
-如果你用 `git commit -a` 提交了一次变化(changes),而你又不确定到底这次提交了哪些内容。 你就可以用下面的命令显示当前`HEAD`上的最近一次的提交(commit):
-
-```sh
-(master)$ git show
-```
-
-或者
-
-```sh
-$ git log -n1 -p
-```
-
-### 我的提交信息(commit message)写错了
-
-如果你的提交信息(commit message)写错了且这次提交(commit)还没有推(push), 你可以通过下面的方法来修改提交信息(commit message):
-
-```sh
-$ git commit --amend
-```
-
-这会打开你的默认编辑器, 在这里你可以编辑信息. 另一方面, 你也可以用一条命令一次完成:
-
-```sh
-$ git commit --amend -m 'xxxxxxx'
-```
-
-如果你已经推(push)了这次提交(commit), 你可以修改这次提交(commit)然后强推(force push), 但是不推荐这么做。
-
-### 我提交(commit)里的用户名和邮箱不对
-
-如果这只是单个提交(commit),修改它:
-
-```sh
-$ git commit --amend --author "New Authorname "
-```
-
-如果你需要修改所有历史, 参考 'git filter-branch'的指南页.
-
-### 我想从一个提交(commit)里移除一个文件
-
-通过下面的方法,从一个提交(commit)里移除一个文件:
-
-```sh
-$ git checkout HEAD^ myfile
-$ git add -A
-$ git commit --amend
-```
-
-这将非常有用,当你有一个开放的补丁(open patch),你往上面提交了一个不必要的文件,你需要强推(force push)去更新这个远程补丁。
-
-### 我想删除我的的最后一次提交(commit)
-
-如果你需要删除推了的提交(pushed commits),你可以使用下面的方法。可是,这会不可逆的改变你的历史,也会搞乱那些已经从该仓库拉取(pulled)了的人的历史。简而言之,如果你不是很确定,千万不要这么做。
-
-```sh
-$ git reset HEAD^ --hard
-$ git push -f [remote] [branch]
-```
-
-如果你还没有推到远程, 把Git重置(reset)到你最后一次提交前的状态就可以了(同时保存暂存的变化):
-
-```
-(my-branch*)$ git reset --soft HEAD@{1}
-
-```
-
-这只能在没有推送之前有用. 如果你已经推了, 唯一安全能做的是 `git revert SHAofBadCommit`, 那会创建一个新的提交(commit)用于撤消前一个提交的所有变化(changes); 或者, 如果你推的这个分支是rebase-safe的 (例如: 其它开发者不会从这个分支拉), 只需要使用 `git push -f`; 更多, 请参考 [the above section](#deleteremove-last-pushed-commit)。
-
-### 删除任意提交(commit)
-
-同样的警告:不到万不得已的时候不要这么做.
-
-```sh
-$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
-$ git push -f [remote] [branch]
-```
-
-或者做一个 [交互式rebase](#interactive-rebase) 删除那些你想要删除的提交(commit)里所对应的行。
-
-### 我尝试推一个修正后的提交(amended commit)到远程,但是报错:
-
-```sh
-To https://github.com/yourusername/repo.git
-! [rejected] mybranch -> mybranch (non-fast-forward)
-error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
-hint: Updates were rejected because the tip of your current branch is behind
-hint: its remote counterpart. Integrate the remote changes (e.g.
-hint: 'git pull ...') before pushing again.
-hint: See the 'Note about fast-forwards' in 'git push --help' for details.
-```
-
-注意, rebasing(见下面)和修正(amending)会用一个**新的提交(commit)代替旧的**, 所以如果之前你已经往远程仓库上推过一次修正前的提交(commit),那你现在就必须强推(force push) (`-f`)。 注意 – *总是* 确保你指明一个分支!
-
-```sh
-(my-branch)$ git push origin mybranch -f
-```
-
-一般来说, **要避免强推**. 最好是创建和推(push)一个新的提交(commit),而不是强推一个修正后的提交。后者会使那些与该分支或该分支的子分支工作的开发者,在源历史中产生冲突。
-
-### 我意外的做了一次硬重置(hard reset),我想找回我的内容
-
-如果你意外的做了 `git reset --hard`, 你通常能找回你的提交(commit), 因为Git对每件事都会有日志,且都会保存几天。
-
-```sh
-(master)$ git reflog
-```
-
-你将会看到一个你过去提交(commit)的列表, 和一个重置的提交。 选择你想要回到的提交(commit)的SHA,再重置一次:
-
-```sh
-(master)$ git reset --hard SHA1234
-```
-
-这样就完成了。
-
-## 暂存(Staging)
-
-### 我需要把暂存的内容添加到上一次的提交(commit)
-
-```sh
-(my-branch*)$ git commit --amend
-
-```
-
-### 我想要暂存一个新文件的一部分,而不是这个文件的全部
-
-一般来说, 如果你想暂存一个文件的一部分, 你可这样做:
-
-```sh
-$ git add --patch filename.x
-```
-
-`-p` 简写。这会打开交互模式, 你将能够用 `s` 选项来分隔提交(commit); 然而, 如果这个文件是新的, 会没有这个选择, 添加一个新文件时, 这样做:
-
-```sh
-$ git add -N filename.x
-```
-
-然后, 你需要用 `e` 选项来手动选择需要添加的行,执行 `git diff --cached` 将会显示哪些行暂存了哪些行只是保存在本地了。
-
-### 我想把在一个文件里的变化(changes)加到两个提交(commit)里
-
-`git add` 会把整个文件加入到一个提交. `git add -p` 允许交互式的选择你想要提交的部分.
-
-### 我想把暂存的内容变成未暂存,把未暂存的内容暂存起来
-
-这个有点困难, 我能想到的最好的方法是先stash未暂存的内容, 然后重置(reset),再pop第一步stashed的内容, 最后再add它们。
-
-```sh
-$ git stash -k
-$ git reset --hard
-$ git stash pop
-$ git add -A
-```
-
-## 未暂存(Unstaged)的内容
-
-### 我想把未暂存的内容移动到一个新分支
-
-```sh
-$ git checkout -b my-branch
-```
-
-### 我想把未暂存的内容移动到另一个已存在的分支
-
-```sh
-$ git stash
-$ git checkout my-branch
-$ git stash pop
-```
-
-### 我想丢弃本地未提交的变化(uncommitted changes)
-
-如果你只是想重置源(origin)和你本地(local)之间的一些提交(commit),你可以:
-
-```sh
-# one commit
-(my-branch)$ git reset --hard HEAD^
-# two commits
-(my-branch)$ git reset --hard HEAD^^
-# four commits
-(my-branch)$ git reset --hard HEAD~4
-# or
-(master)$ git checkout -f
-```
-
-重置某个特殊的文件, 你可以用文件名做为参数:
-
-```sh
-$ git reset filename
-```
-
-### 我想丢弃某些未暂存的内容
-
-如果你想丢弃工作拷贝中的一部分内容,而不是全部。
-
-签出(checkout)不需要的内容,保留需要的。
-
-```sh
-$ git checkout -p
-# Answer y to all of the snippets you want to drop
-```
-
-另外一个方法是使用 `stash`, Stash所有要保留下的内容, 重置工作拷贝, 重新应用保留的部分。
-
-```sh
-$ git stash -p
-# Select all of the snippets you want to save
-$ git reset --hard
-$ git stash pop
-```
-
-或者, stash 你不需要的部分, 然后stash drop。
-
-```sh
-$ git stash -p
-# Select all of the snippets you don't want to save
-$ git stash drop
-```
-
-## 分支(Branches)
-
-### 我从错误的分支拉取了内容,或把内容拉取到了错误的分支
-
-这是另外一种使用 `git reflog` 情况,找到在这次错误拉(pull) 之前HEAD的指向。
-
-```sh
-(master)$ git reflog
-ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
-c5bc55a HEAD@{1}: checkout: checkout message goes here
-```
-
-重置分支到你所需的提交(desired commit):
-
-```sh
-$ git reset --hard c5bc55a
-```
-
-完成。
-
-### 我想扔掉本地的提交(commit),以便我的分支与远程的保持一致
-
-先确认你没有推(push)你的内容到远程。
-
-`git status` 会显示你领先(ahead)源(origin)多少个提交:
-
-```sh
-(my-branch)$ git status
-# On branch my-branch
-# Your branch is ahead of 'origin/my-branch' by 2 commits.
-# (use "git push" to publish your local commits)
-#
-```
-
-一种方法是:
-
-```sh
-(master)$ git reset --hard origin/my-branch
-```
-
-### 我需要提交到一个新分支,但错误的提交到了master
-
-在master下创建一个新分支,不切换到新分支,仍在master下:
-
-```sh
-(master)$ git branch my-branch
-```
-
-把master分支重置到前一个提交:
-
-```sh
-(master)$ git reset --hard HEAD^
-```
-
-`HEAD^` 是 `HEAD^1` 的简写,你可以通过指定要设置的`HEAD`来进一步重置。
-
-或者, 如果你不想使用 `HEAD^`, 找到你想重置到的提交(commit)的hash(`git log` 能够完成), 然后重置到这个hash。 使用`git push` 同步内容到远程。
-
-例如, master分支想重置到的提交的hash为`a13b85e`:
-
-```sh
-(master)$ git reset --hard a13b85e
-HEAD is now at a13b85e
-```
-
-签出(checkout)刚才新建的分支继续工作:
-
-```sh
-(master)$ git checkout my-branch
-```
-
-### 我想保留来自另外一个ref-ish的整个文件
-
-假设你正在做一个原型方案(原文为working spike (see note)), 有成百的内容,每个都工作得很好。现在, 你提交到了一个分支,保存工作内容:
-
-```sh
-(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit."
-```
-
-当你想要把它放到一个分支里 (可能是`feature`, 或者 `develop`), 你关心是保持整个文件的完整,你想要一个大的提交分隔成比较小。
-
-假设你有:
-
- * 分支 `solution`, 拥有原型方案, 领先 `develop` 分支。
- * 分支 `develop`, 在这里你应用原型方案的一些内容。
-
-我去可以通过把内容拿到你的分支里,来解决这个问题:
-
-```sh
-(develop)$ git checkout solution -- file1.txt
-```
-
-这会把这个文件内容从分支 `solution` 拿到分支 `develop` 里来:
-
-```sh
-# On branch develop
-# Your branch is up-to-date with 'origin/develop'.
-# Changes to be committed:
-# (use "git reset HEAD ..." to unstage)
-#
-# modified: file1.txt
-```
-
-然后, 正常提交。
-
-Note: Spike solutions are made to analyze or solve the problem. These solutions are used for estimation and discarded once everyone gets clear visualization of the problem. ~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices).
-
-### 我把几个提交(commit)提交到了同一个分支,而这些提交应该分布在不同的分支里
-
-假设你有一个`master`分支, 执行`git log`, 你看到你做过两次提交:
-
-```sh
-(master)$ git log
-
-commit e3851e817c451cc36f2e6f3049db528415e3c114
-Author: Alex Lee
-Date: Tue Jul 22 15:39:27 2014 -0400
-
- Bug #21 - Added CSRF protection
-
-commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
-Author: Alex Lee
-Date: Tue Jul 22 15:39:12 2014 -0400
-
- Bug #14 - Fixed spacing on title
-
-commit a13b85e984171c6e2a1729bb061994525f626d14
-Author: Aki Rose
-Date: Tue Jul 21 01:12:48 2014 -0400
-
- First commit
-```
-
-让我们用提交hash(commit hash)标记bug (`e3851e8` for #21, `5ea5173` for #14).
-
-首先, 我们把`master`分支重置到正确的提交(`a13b85e`):
-
-```sh
-(master)$ git reset --hard a13b85e
-HEAD is now at a13b85e
-```
-
-现在, 我们对 bug #21 创建一个新的分支:
-
-```sh
-(master)$ git checkout -b 21
-(21)$
-```
-
-接着, 我们用 *cherry-pick* 把对bug #21的提交放入当前分支。 这意味着我们将应用(apply)这个提交(commit),仅仅这一个提交(commit),直接在HEAD上面。
-
-```sh
-(21)$ git cherry-pick e3851e8
-```
-
-这时候, 这里可能会产生冲突, 参见[交互式 rebasing 章](#interactive-rebase) [**冲突节**](#merge-conflict) 解决冲突.
-
-再者, 我们为bug #14 创建一个新的分支, 也基于`master`分支
-
-```sh
-(21)$ git checkout master
-(master)$ git checkout -b 14
-(14)$
-```
-
-最后, 为 bug #14 执行 `cherry-pick`:
-
-```sh
-(14)$ git cherry-pick 5ea5173
-```
-
-### 我想删除上游(upstream)分支被删除了的本地分支
-一旦你在github 上面合并(merge)了一个pull request, 你就可以删除你fork里被合并的分支。 如果你不准备继续在这个分支里工作, 删除这个分支的本地拷贝会更干净,使你不会陷入工作分支和一堆陈旧分支的混乱之中。
-
-```sh
-$ git fetch -p
-```
-
-### 我不小心删除了我的分支
-
-如果你定期推送到远程, 多数情况下应该是安全的,但有些时候还是可能删除了还没有推到远程的分支。 让我们先创建一个分支和一个新的文件:
-
-```sh
-(master)$ git checkout -b my-branch
-(my-branch)$ git branch
-(my-branch)$ touch foo.txt
-(my-branch)$ ls
-README.md foo.txt
-```
-
-添加文件并做一次提交
-
-```sh
-(my-branch)$ git add .
-(my-branch)$ git commit -m 'foo.txt added'
-(my-branch)$ foo.txt added
- 1 files changed, 1 insertions(+)
- create mode 100644 foo.txt
-(my-branch)$ git log
-
-commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
-Author: siemiatj
-Date: Wed Jul 30 00:34:10 2014 +0200
-
- foo.txt added
-
-commit 69204cdf0acbab201619d95ad8295928e7f411d5
-Author: Kate Hudson
-Date: Tue Jul 29 13:14:46 2014 -0400
-
- Fixes #6: Force pushing after amending commits
-```
-
-现在我们切回到主(master)分支,‘不小心的’删除`my-branch`分支
-
-```sh
-(my-branch)$ git checkout master
-Switched to branch 'master'
-Your branch is up-to-date with 'origin/master'.
-(master)$ git branch -D my-branch
-Deleted branch my-branch (was 4e3cd85).
-(master)$ echo oh noes, deleted my branch!
-oh noes, deleted my branch!
-```
-
-在这时候你应该想起了`reflog`, 一个升级版的日志,它存储了仓库(repo)里面所有动作的历史。
-
-```
-(master)$ git reflog
-69204cd HEAD@{0}: checkout: moving from my-branch to master
-4e3cd85 HEAD@{1}: commit: foo.txt added
-69204cd HEAD@{2}: checkout: moving from master to my-branch
-```
-
-正如你所见,我们有一个来自删除分支的提交hash(commit hash),接下来看看是否能恢复删除了的分支。
-
-```sh
-(master)$ git checkout -b my-branch-help
-Switched to a new branch 'my-branch-help'
-(my-branch-help)$ git reset --hard 4e3cd85
-HEAD is now at 4e3cd85 foo.txt added
-(my-branch-help)$ ls
-README.md foo.txt
-```
-
-看! 我们把删除的文件找回来了。 Git的 `reflog` 在rebasing出错的时候也是同样有用的。
-
-### 我想删除一个分支
-
-删除一个远程分支:
-
-```sh
-(master)$ git push origin --delete my-branch
-```
-
-你也可以:
-
-```sh
-(master)$ git push origin :my-branch
-```
-
-删除一个本地分支:
-
-```sh
-(master)$ git branch -D my-branch
-```
-
-### 我想从别人正在工作的远程分支签出(checkout)一个分支
-
-首先, 从远程拉取(fetch) 所有分支:
-
-```sh
-(master)$ git fetch --all
-```
-
-假设你想要从远程的`daves`分支签出到本地的`daves`
-
-```sh
-(master)$ git checkout --track origin/daves
-Branch daves set up to track remote branch daves from origin.
-Switched to a new branch 'daves'
-```
-
-(`--track` 是 `git checkout -b [branch] [remotename]/[branch]` 的简写)
-
-这样就得到了一个`daves`分支的本地拷贝, 任何推过(pushed)的更新,远程都能看到.
-
-## Rebasing 和合并(Merging)
-
-### 我想撤销rebase/merge
-
-你可以合并(merge)或rebase了一个错误的分支, 或者完成不了一个进行中的rebase/merge。 Git 在进行危险操作的时候会把原始的HEAD保存在一个叫ORIG_HEAD的变量里, 所以要把分支恢复到rebase/merge前的状态是很容易的。
-
-```sh
-(my-branch)$ git reset --hard ORIG_HEAD
-```
-
-### 我已经rebase过, 但是我不想强推(force push)
-
-不幸的是,如果你想把这些变化(changes)反应到远程分支上,你就必须得强推(force push)。 是因你快进(Fast forward)了提交,改变了Git历史, 远程分支不会接受变化(changes),除非强推(force push)。这就是许多人使用 merge 工作流, 而不是 rebasing 工作流的主要原因之一, 开发者的强推(force push)会使大的团队陷入麻烦。使用时需要注意,一种安全使用 rebase 的方法是,不要把你的变化(changes)反映到远程分支上, 而是按下面的做:
-
-```sh
-(master)$ git checkout my-branch
-(my-branch)$ git rebase -i master
-(my-branch)$ git checkout master
-(master)$ git merge --ff-only my-branch
-```
-
-更多, 参见 [this SO thread](http://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push).
-
-### 我需要组合(combine)几个提交(commit)
-
-假设你的工作分支将会做对于 `master` 的pull-request。 一般情况下你不关心提交(commit)的时间戳,只想组合 *所有* 提交(commit) 到一个单独的里面, 然后重置(reset)重提交(recommit)。 确保主(master)分支是最新的和你的变化都已经提交了, 然后:
-
-```sh
-(my-branch)$ git reset --soft master
-(my-branch)$ git commit -am "New awesome feature"
-```
-
-如果你想要更多的控制, 想要保留时间戳, 你需要做交互式rebase (interactive rebase):
-
-```sh
-(my-branch)$ git rebase -i master
-```
-
-如果没有相对的其它分支, 你将不得不相对自己的`HEAD` 进行 rebase。 例如:你想组合最近的两次提交(commit), 你将相对于`HEAD~2` 进行rebase, 组合最近3次提交(commit), 相对于`HEAD~3`, 等等。
-
-```sh
-(master)$ git rebase -i HEAD~2
-```
-
-在你执行了交互式 rebase的命令(interactive rebase command)后, 你将在你的编辑器里看到类似下面的内容:
-
-```vim
-pick a9c8a1d Some refactoring
-pick 01b2fd8 New awesome feature
-pick b729ad5 fixup
-pick e3851e8 another fix
-
-# Rebase 8074d12..b729ad5 onto 8074d12
-#
-# Commands:
-# p, pick = use commit
-# r, reword = use commit, but edit the commit message
-# e, edit = use commit, but stop for amending
-# s, squash = use commit, but meld into previous commit
-# f, fixup = like "squash", but discard this commit's log message
-# x, exec = run command (the rest of the line) using shell
-#
-# These lines can be re-ordered; they are executed from top to bottom.
-#
-# If you remove a line here THAT COMMIT WILL BE LOST.
-#
-# However, if you remove everything, the rebase will be aborted.
-#
-# Note that empty commits are commented out
-```
-
-所有以 `#` 开头的行都是注释, 不会影响 rebase.
-
-然后,你可以用任何上面命令列表的命令替换 `pick`, 你也可以通过删除对应的行来删除一个提交(commit)。
-
-例如, 如果你想 **单独保留最旧(first)的提交(commit),组合所有剩下的到第二个里面**, 你就应该编辑第二个提交(commit)后面的每个提交(commit) 前的单词为 `f`:
-
-```vim
-pick a9c8a1d Some refactoring
-pick 01b2fd8 New awesome feature
-f b729ad5 fixup
-f e3851e8 another fix
-```
-
-如果你想组合这些提交(commit) **并重命名这个提交(commit)**, 你应该在第二个提交(commit)旁边添加一个`r`,或者更简单的用`s` 替代 `f`:
-
-```vim
-pick a9c8a1d Some refactoring
-pick 01b2fd8 New awesome feature
-s b729ad5 fixup
-s e3851e8 another fix
-```
-
-你可以在接下来弹出的文本提示框里重命名提交(commit)。
-
-```vim
-Newer, awesomer features
-
-# Please enter the commit message for your changes. Lines starting
-# with '#' will be ignored, and an empty message aborts the commit.
-# rebase in progress; onto 8074d12
-# You are currently editing a commit while rebasing branch 'master' on '8074d12'.
-#
-# Changes to be committed:
-# modified: README.md
-#
-
-```
-
-如果成功了, 你应该看到类似下面的内容:
-
-```sh
-(master)$ Successfully rebased and updated refs/heads/master.
-```
-
-#### 安全合并(merging)策略
-`--no-commit` 执行合并(merge)但不自动提交, 给用户在做提交前检查和修改的机会。 `no-ff` 会为特性分支(feature branch)的存在过留下证据, 保持项目历史一致。
-
-```sh
-(master)$ git merge --no-ff --no-commit my-branch
-```
-
-#### 我需要将一个分支合并成一个提交(commit)
-
-```sh
-(master)$ git merge --squash my-branch
-```
-
-#### 我只想组合(combine)未推的提交(unpushed commit)
-
-有时候,在将数据推向上游之前,你有几个正在进行的工作提交(commit)。这时候不希望把已经推(push)过的组合进来,因为其他人可能已经有提交(commit)引用它们了。
-
-```sh
-(master)$ git rebase -i @{u}
-```
-
-这会产生一次交互式的rebase(interactive rebase), 只会列出没有推(push)的提交(commit), 在这个列表时进行reorder/fix/squash 都是安全的。
-
-### 检查是否分支上的所有提交(commit)都合并(merge)过了
-
-检查一个分支上的所有提交(commit)是否都已经合并(merge)到了其它分支, 你应该在这些分支的head(或任何 commits)之间做一次diff:
-
-```sh
-(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
-```
-
-这会告诉你在一个分支里有而另一个分支没有的所有提交(commit), 和分支之间不共享的提交(commit)的列表。 另一个做法可以是:
-
-```sh
-(master)$ git log master ^feature/120-on-scroll --no-merges
-```
-
-### 交互式rebase(interactive rebase)可能出现的问题
-
-#### 这个rebase 编辑屏幕出现'noop'
-
-如果你看到的是这样:
-```
-noop
-```
-
-这意味着你rebase的分支和当前分支在同一个提交(commit)上, 或者 *领先(ahead)* 当前分支。 你可以尝试:
-
-* 检查确保主(master)分支没有问题
-* rebase `HEAD~2` 或者更早
-
-#### 有冲突的情况
-
-如果你不能成功的完成rebase, 你可能必须要解决冲突。
-
-首先执行 `git status` 找出哪些文件有冲突:
-
-```sh
-(my-branch)$ git status
-On branch my-branch
-Changes not staged for commit:
- (use "git add ..." to update what will be committed)
- (use "git checkout -- ..." to discard changes in working directory)
-
- modified: README.md
-```
-
-在这个例子里面, `README.md` 有冲突。 打开这个文件找到类似下面的内容:
-
-```vim
- <<<<<<< HEAD
- some code
- =========
- some code
- >>>>>>> new-commit
-```
-
-你需要解决新提交的代码(示例里, 从中间`==`线到`new-commit`的地方)与`HEAD` 之间不一样的地方.
-
-有时候这些合并非常复杂,你应该使用可视化的差异编辑器(visual diff editor):
-
-```sh
-(master*)$ git mergetool -t opendiff
-```
-
-在你解决完所有冲突和测试过后, `git add` 变化了的(changed)文件, 然后用`git rebase --continue` 继续rebase。
-
-```sh
-(my-branch)$ git add README.md
-(my-branch)$ git rebase --continue
-```
-
-如果在解决完所有的冲突过后,得到了与提交前一样的结果, 可以执行`git rebase --skip`。
-
-任何时候你想结束整个rebase 过程,回来rebase前的分支状态, 你可以做:
-
-```sh
-(my-branch)$ git rebase --abort
-```
-
-## 杂项(Miscellaneous Objects)
-
-### 克隆所有子模块
-
-```sh
-$ git clone --recursive git://github.com/foo/bar.git
-```
-
-如果已经克隆了:
-
-```sh
-$ git submodule update --init --recursive
-```
-
-### 删除标签(tag)
-
-```sh
-$ git tag -d
-$ git push :refs/tags/
-```
-
-### 恢复已删除标签(tag)
-
-如果你想恢复一个已删除标签(tag), 可以按照下面的步骤: 首先, 需要找到无法访问的标签(unreachable tag):
-
-```sh
-$ git fsck --unreachable | grep tag
-```
-
-记下这个标签(tag)的hash,然后用Git的 [update-ref](http://git-scm.com/docs/git-update-ref):
-
-```sh
-$ git update-ref refs/tags/
-```
-
-这时你的标签(tag)应该已经恢复了。
-
-### 已删除补丁(patch)
-
-如果某人在 GitHub 上给你发了一个pull request, 但是然后他删除了他自己的原始 fork, 你将没法克隆他们的提交(commit)或使用 `git am`。在这种情况下, 最好手动的查看他们的提交(commit),并把它们拷贝到一个本地新分支,然后做提交。
-
-做完提交后, 再修改作者,参见[变更作者](#commit-wrong-author)。 然后, 应用变化, 再发起一个新的pull request。
-
-## 跟踪文件(Tracking Files)
-
-### 我只想改变一个文件名字的大小写,而不修改内容
-
-```sh
-(master)$ git mv --force myfile MyFile
-```
-
-### 我想从Git删除一个文件,但保留该文件
-
-```sh
-(master)$ git rm --cached log.txt
-```
-
-## 配置(Configuration)
-
-### 我想给一些Git命令添加别名(alias)
-
-在 OS X 和 Linux 下, 你的 Git的配置文件储存在 ```~/.gitconfig```。我在```[alias]``` 部分添加了一些快捷别名(和一些我容易拼写错误的),如下:
-
-```vim
-[alias]
- a = add
- amend = commit --amend
- c = commit
- ca = commit --amend
- ci = commit -a
- co = checkout
- d = diff
- dc = diff --changed
- ds = diff --staged
- f = fetch
- loll = log --graph --decorate --pretty=oneline --abbrev-commit
- m = merge
- one = log --pretty=oneline
- outstanding = rebase -i @{u}
- s = status
- unpushed = log @{u}
- wc = whatchanged
- wip = rebase -i @{u}
- zap = fetch -p
-```
-
-### 我想缓存一个仓库(repository)的用户名和密码
-
-你可能有一个仓库需要授权,这时你可以缓存用户名和密码,而不用每次推/拉(push/pull)的时候都输入,Credential helper能帮你。
-
-```sh
-$ git config --global credential.helper cache
-# Set git to use the credential memory cache
-```
-
-```sh
-$ git config --global credential.helper 'cache --timeout=3600'
-# Set the cache to timeout after 1 hour (setting is in seconds)
-```
-
-## 我不知道我做错了些什么
-
-你把事情搞砸了:你 `重置(reset)` 了一些东西, 或者你合并了错误的分支, 亦或你强推了后找不到你自己的提交(commit)了。有些时候, 你一直都做得很好, 但你想回到以前的某个状态。
-
-这就是 `git reflog` 的目的, `reflog` 记录对分支顶端(the tip of a branch)的任何改变, 即使那个顶端没有被任何分支或标签引用。基本上, 每次HEAD的改变, 一条新的记录就会增加到`reflog`。遗憾的是,这只对本地分支起作用,且它只跟踪动作 (例如,不会跟踪一个没有被记录的文件的任何改变)。
-
-```sh
-(master)$ git reflog
-0a2e358 HEAD@{0}: reset: moving to HEAD~2
-0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
-c10f740 HEAD@{2}: checkout: moving from master to 2.2
-```
-
-上面的reflog展示了从master分支签出(checkout)到2.2 分支,然后再签回。 那里,还有一个硬重置(hard reset)到一个较旧的提交。最新的动作出现在最上面以 `HEAD@{0}`标识.
-
-如果事实证明你不小心回移(move back)了提交(commit), reflog 会包含你不小心回移前master上指向的提交(0254ea7)。
-
-```sh
-$ git reset --hard 0254ea7
-```
-
-然后使用git reset就可以把master改回到之前的commit,这提供了一个在历史被意外更改情况下的安全网。
-
-## 资源
-
-* https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md
-* https://github.com/521xueweihan/git-tips
\ No newline at end of file
diff --git a/os/git/appendix/git-resource.md b/os/git/appendix/git-resource.md
deleted file mode 100644
index e5cf4e0394..0000000000
--- a/os/git/appendix/git-resource.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Git 资源
-
-## 官方资源
-
-* [Git 官网](https://git-scm.com/)
-* [Git Github](https://github.com/git/git)
-
-## 模板
-
-* [gitignore 模板](https://github.com/github/gitignore) - .gitignore 文件模板
-* [gitattributes 模板](https://github.com/alexkaratarakis/gitattributes) - .gitattributes 文件模板
-* [github-cheat-sheet](https://github.com/tiimgreen/github-cheat-sheet) - git 命令简略图表
-
-## Git 书
-
-* [Git 官方推荐教程](https://git-scm.com/book/zh/v2) - Scott Chacon 的 Git 书。
-
-## Git 教程
-
-* [Git中文教程](https://github.com/geeeeeeeeek/git-recipes)
-* [廖雪峰的Git教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
-* [有关 git 的学习资料](https://github.com/xirong/my-git)
-
-## 文章
-
-* [Git Cookbook](https://github.com/k88hudson/git-flight-rules/blob/master/README_zh-CN.md)
-* [Git 奇技淫巧](https://github.com/521xueweihan/git-tips)
-* [Git 风格指南](https://github.com/aseaday/git-style-guide)
-
-## Git 工具
-
-* [guis](https://git-scm.com/downloads/guis) - Git 官网展示的客户端工具列表。
-* [gogs](https://github.com/gogits/gogs) - 极易搭建的自助 Git 服务。
-* [gitflow](https://github.com/nvie/gitflow) - 应用 [fit-flow](http://nvie.com/posts/a-successful-git-branching-model/) 模型的工具。
-* [firstaidgit.io](http://firstaidgit.io/) 一个可搜索的最常被问到的Git的问题
-* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - 一堆有用的额外的Git脚本
-* [git-extras](https://github.com/tj/git-extras) - GIT 工具集 -- repo summary, repl, changelog population, author commit percentages and more
-* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire 是一个 Git 插件,用于帮助在紧急情况下添加所有当前文件, 做提交(committing), 和推(push)到一个新分支(阻止合并冲突)。
-* [git-tips](https://github.com/git-tips/tips) - Git小提示
-* [git-town](https://github.com/Originate/git-town) - 通用,高级Git工作流支持! http://www.git-town.com
-
-## GUI客户端(GUI Clients)
-
-* [GitKraken](https://www.gitkraken.com/) - 豪华的Git客户端 Windows, Mac & Linux
-* [git-cola](https://git-cola.github.io/) - 另外一个Git客户端 Windows & OS X
-* [GitUp](https://github.com/git-up/GitUp) - 一个新的Git客户端,在处理Git的复杂性上有自己的特点
-* [gitx-dev](https://rowanj.github.io/gitx/) - 图形化的Git客户端 OS X
-* [Source Tree](https://www.sourcetreeapp.com/) - 免费的图形化Git客户端 Windows & OS X
-* [Tower](http://www.git-tower.com/) - 图形化Git客户端 OS X(付费)
\ No newline at end of file
diff --git a/os/git/appendix/github-git-cheat-sheet.pdf b/os/git/appendix/github-git-cheat-sheet.pdf
deleted file mode 100644
index 64ddc41968..0000000000
Binary files a/os/git/appendix/github-git-cheat-sheet.pdf and /dev/null differ
diff --git a/os/git/basics/git-configuration.md b/os/git/basics/git-configuration.md
deleted file mode 100644
index 860c47eab8..0000000000
--- a/os/git/basics/git-configuration.md
+++ /dev/null
@@ -1,220 +0,0 @@
-# Git 配置
-
-## 配置 Git
-
-Git 使用一系列配置文件来保存你自定义的行为。 它首先会查找 `/etc/gitconfig` 文件,该文件含有系统里每位用户及他们所拥有的仓库的配置值。 如果你传递 `--system` 选项给 `git config`,它就会读写该文件。
-
-接下来 Git 会查找每个用户的 `~/.gitconfig` 文件(或者 `~/.config/git/config` 文件)。 你可以传递 `--global` 选项让 Git 读写该文件。
-
-最后 Git 会查找你正在操作的版本库所对应的 Git 目录下的配置文件(`.git/config`)。 这个文件中的值只对该版本库有效。
-
-以上三个层次中每层的配置(系统、全局、本地)都会覆盖掉上一层次的配置,所以 `.git/config` 中的值会覆盖掉 `/etc/gitconfig` 中所对应的值。
-
-## 客户端基本配置
-
-### `core.editor`
-
-默认情况下,Git 会调用环境变量(`$VISUAL` 或 `$EDITOR`)设置的任意文本编辑器,如果没有设置,会调用 `vi` 来创建和编辑你的提交以及标签信息。 你可以使用 `core.editor` 选项来修改默认的编辑器:
-
-```
-$ git config --global core.editor emacs
-```
-
-现在,无论你定义了什么终端编辑器,Git 都会调用 Emacs 编辑信息。
-
-### `commit.template`
-
-如果把此项指定为你的系统上某个文件的路径,当你提交的时候, Git 会使用该文件的内容作为提交的默认信息。 例如:假设你创建了一个叫 `~/.gitmessage.txt` 的模板文件,类似这样:
-
-```
-subject line
-
-what happened
-
-[ticket: X]
-```
-
-要想让 Git 把它作为运行 `git commit` 时显示在你的编辑器中的默认信息, 如下设置 `commit.template`:
-
-```
-$ git config --global commit.template ~/.gitmessage.txt
-$ git commit
-```
-
-然后当你提交时,编辑器中就会显示如下的提交信息占位符:
-
-```
-subject line
-
-what happened
-
-[ticket: X]
-# Please enter the commit message for your changes. Lines starting
-# with '#' will be ignored, and an empty message aborts the commit.
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD ..." to unstage)
-#
-# modified: lib/test.rb
-#
-~
-~
-".git/COMMIT_EDITMSG" 14L, 297C
-```
-
-如果你的团队对提交信息有格式要求,可以在系统上创建一个文件,并配置 Git 把它作为默认的模板,这样可以更加容易地使提交信息遵循格式。
-
-### `core.pager`
-
-该配置项指定 Git 运行诸如 `log` 和 `diff` 等命令所使用的分页器。 你可以把它设置成用 `more` 或者任何你喜欢的分页器(默认用的是 `less`),当然也可以设置成空字符串,关闭该选项:
-
-```
-$ git config --global core.pager ''
-```
-
-这样不管命令的输出量多少,Git 都会在一页显示所有内容。
-
-### `user.signingkey`
-
-如果你要创建经签署的含附注的标签(正如 [签署工作](https://git-scm.com/book/zh/v2/ch00/r_signing) 所述),那么把你的 GPG 签署密钥设置为配置项会更好。 如下设置你的密钥 ID:
-
-```
-$ git config --global user.signingkey
-```
-
-现在,你每次运行 `git tag` 命令时,即可直接签署标签,而无需定义密钥:
-
-```
-$ git tag -s
-```
-
-### `core.excludesfile`
-
-正如 [忽略文件](https://git-scm.com/book/zh/v2/ch00/r_ignoring) 所述,你可以在你的项目的 `.gitignore` 文件里面规定无需纳入 Git 管理的文件的模板,这样它们既不会出现在未跟踪列表,也不会在你运行 `git add` 后被暂存。
-
-不过有些时候,你想要在你所有的版本库中忽略掉某一类文件。 如果你的操作系统是 OS X,很可能就是指`.DS_Store`。 如果你把 Emacs 或 Vim 作为首选的编辑器,你肯定知道以 `~` 结尾的临时文件。
-
-这个配置允许你设置类似于全局生效的 `.gitignore` 文件。 如果你按照下面的内容创建一个`~/.gitignore_global` 文件:
-
-```
-*~
-.DS_Store
-```
-
-……然后运行 `git config --global core.excludesfile ~/.gitignore_global`,Git 将把那些文件永远地拒之门外。
-
-### `help.autocorrect`
-
-假如你打错了一条命令,会显示:
-
-```
-$ git chekcout master
-git:'chekcout' 不是一个 git 命令。参见 'git --help'。
-
-您指的是这个么?
- checkout
-```
-
-Git 会尝试猜测你的意图,但是它不会越俎代庖。 如果你把 `help.autocorrect` 设置成 1,那么只要有一个命令被模糊匹配到了,Git 会自动运行该命令。
-
-```
-$ git chekcout master
-警告:您运行一个不存在的 Git 命令 'chekcout'。继续执行假定您要要运行的
-是 'checkout'
-在 0.1 秒钟后自动运行...
-```
-
-注意提示信息中的“0.1 秒”。`help.autocorrect` 接受一个代表十分之一秒的整数。 所以如果你把它设置为 50, Git 将在自动执行命令前给你 5 秒的时间改变主意。
-
-## 格式化与多余的空白字符
-
-格式化与多余的空白字符是许多开发人员在协作时,特别是在跨平台情况下,不时会遇到的令人头疼的琐碎的问题。 由于编辑器的不同或者文件行尾的换行符在 Windows 下被替换了,一些细微的空格变化会不经意地混入提交的补丁或其它协作成果中。 不用怕,Git 提供了一些配置项来帮助你解决这些问题。
-
-### `core.autocrlf`
-
-假如你正在 Windows 上写程序,而你的同伴用的是其他系统(或相反),你可能会遇到 CRLF 问题。 这是因为 Windows 使用回车(CR)和换行(LF)两个字符来结束一行,而 Mac 和 Linux 只使用换行(LF)一个字符。 虽然这是小问题,但它会极大地扰乱跨平台协作。许多 Windows 上的编辑器会悄悄把行尾的换行字符转换成回车和换行,或在用户按下 Enter 键时,插入回车和换行两个字符。
-
-Git 可以在你提交时自动地把回车和换行转换成换行,而在检出代码时把换行转换成回车和换行。 你可以用 `core.autocrlf` 来打开此项功能。 如果是在 Windows 系统上,把它设置成 `true`,这样在检出代码时,换行会被转换成回车和换行:
-
-```
-$ git config --global core.autocrlf true
-```
-
-如果使用以换行作为行结束符的 Linux 或 Mac,你不需要 Git 在检出文件时进行自动的转换;然而当一个以回车加换行作为行结束符的文件不小心被引入时,你肯定想让 Git 修正。 你可以把 `core.autocrlf` 设置成 input 来告诉 Git 在提交时把回车和换行转换成换行,检出时不转换:
-
-```
-$ git config --global core.autocrlf input
-```
-
-这样在 Windows 上的检出文件中会保留回车和换行,而在 Mac 和 Linux 上,以及版本库中会保留换行。
-
-如果你是 Windows 程序员,且正在开发仅运行在 Windows 上的项目,可以设置 `false` 取消此功能,把回车保留在版本库中:
-
-```
-$ git config --global core.autocrlf false
-```
-
-### `core.whitespace`
-
-Git 预先设置了一些选项来探测和修正多余空白字符问题。 它提供了六种处理多余空白字符的主要选项 —— 其中三个默认开启,另外三个默认关闭,不过你可以自由地设置它们。
-
-默认被打开的三个选项是:`blank-at-eol`,查找行尾的空格;`blank-at-eof`,盯住文件底部的空行;`space-before-tab`,警惕行头 tab 前面的空格。
-
-默认被关闭的三个选项是:`indent-with-non-tab`,揪出以空格而非 tab 开头的行(你可以用 `tabwidth`选项控制它);`tab-in-indent`,监视在行头表示缩进的 tab;`cr-at-eol`,告诉 Git 忽略行尾的回车。
-
-通过设置 `core.whitespace`,你可以让 Git 按照你的意图来打开或关闭以逗号分割的选项。 要想关闭某个选项,你可以在输入设置选项时不指定它或在它前面加个 `-`。 例如,如果你想要打开除 `cr-at-eol` 之外的所有选项:
-
-```
-$ git config --global core.whitespace \
- trailing-space,space-before-tab,indent-with-non-tab
-```
-
-当你运行 `git diff` 命令并尝试给输出着色时,Git 将探测到这些问题,因此你在提交前就能修复它们。 用`git apply` 打补丁时你也会从中受益。 如果正准备应用的补丁存有特定的空白问题,你可以让 Git 在应用补丁时发出警告:
-
-```
-$ git apply --whitespace=warn
-```
-
-或者让 Git 在打上补丁前自动修正此问题:
-
-```
-$ git apply --whitespace=fix
-```
-
-这些选项也能运用于 `git rebase`。 如果提交了有空白问题的文件,但还没推送到上游,你可以运行 `git rebase --whitespace=fix` 来让 Git 在重写补丁时自动修正它们。
-
-## 服务器端配置
-
-Git 服务器端的配置项相对来说并不多,但仍有一些饶有生趣的选项值得你一看。
-
-### `receive.fsckObjects`
-
-Git 能够确认每个对象的有效性以及 SHA-1 检验和是否保持一致。 但 Git 不会在每次推送时都这么做。这个操作很耗时间,很有可能会拖慢提交的过程,特别是当库或推送的文件很大的情况下。 如果想在每次推送时都要求 Git 检查一致性,设置 `receive.fsckObjects` 为 true 来强迫它这么做:
-
-```
-$ git config --system receive.fsckObjects true
-```
-
-现在 Git 会在每次推送生效前检查库的完整性,确保没有被有问题的客户端引入破坏性数据。
-
-### `receive.denyNonFastForwards`
-
-如果你变基已经被推送的提交,继而再推送,又或者推送一个提交到远程分支,而这个远程分支当前指向的提交不在该提交的历史中,这样的推送会被拒绝。 这通常是个很好的策略,但有时在变基的过程中,你确信自己需要更新远程分支,可以在 push 命令后加 `-f` 标志来强制更新(force-update)。
-
-要禁用这样的强制更新推送(force-pushes),可以设置 `receive.denyNonFastForwards`:
-
-```
-$ git config --system receive.denyNonFastForwards true
-```
-
-稍后我们会提到,用服务器端的接收钩子也能达到同样的目的。 那种方法可以做到更细致的控制,例如禁止某一类用户做非快进(non-fast-forwards)推送。
-
-### `receive.denyDeletes`
-
-有一些方法可以绕过 `denyNonFastForwards` 策略。其中一种是先删除某个分支,再连同新的引用一起推送回该分支。 把 `receive.denyDeletes` 设置为 true 可以把这个漏洞补上:
-
-```
-$ git config --system receive.denyDeletes true
-```
-
-这样会禁止通过推送删除分支和标签 — 没有用户可以这么做。 要删除远程分支,必须从服务器手动删除引用文件。 通过用户访问控制列表(ACL)也能够在用户级的粒度上实现同样的功能,你将在 [使用强制策略的一个例子](https://git-scm.com/book/zh/v2/ch00/r_an_example_git_enforced_policy) 一节学到具体的做法。
diff --git a/os/git/basics/git-installation.md b/os/git/basics/git-installation.md
deleted file mode 100644
index d57f25ea92..0000000000
--- a/os/git/basics/git-installation.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Git 安装
-
-## 安装方法
-
-> Git 可以安装在 Mac、Windows、Linux/Unix 环境。
-
-### Mac
-
-在 [mac 下载页面](https://git-scm.com/download/mac) 选择合适版本,按照安装向导安装。
-
-### Windows
-
-在 [win 下载页面](https://git-scm.com/download/win) 选择合适版本,按照安装向导安装。
-
-### Linux/Unix
-
-参考 [linux 下载页面](https://git-scm.com/download/linux) 的指导,根据实际发行版本下载安装 Git。
-
-## 资料
-
-* [https://git-scm.com/downloads](https://git-scm.com/downloads)
\ No newline at end of file
diff --git a/os/git/git-quickstart.md b/os/git/git-quickstart.md
deleted file mode 100644
index 97246767d8..0000000000
--- a/os/git/git-quickstart.md
+++ /dev/null
@@ -1,531 +0,0 @@
----
-title: git 快速指南
-date: 2017-12-06
-categories:
-- linux
-tags:
-- linux
-- vcs
-- git
----
-
-## 简介
-
-### Git 是什么?
-
-Git 是一个开源的分布式版本控制系统。
-
-### 什么是版本控制?
-
-版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。
-
-### 什么是分布式版本控制系统?
-
-介绍分布式版本控制系统前,有必要先了解一下传统的集中式版本控制系统。
-
-**集中化的版本控制系统**,诸如 CVS,Subversion 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
-
-这么做最显而易见的缺点是中央服务器的单点故障。如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。要是中央服务器的磁盘发生故障,碰巧没做备份,或者备份不够及时,就会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录。
-
-
-
-**分布式版本控制系统**的客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份。
-
-
-
-### 为什么使用 Git?
-
-Git 是分布式的。这是 Git 和其它非分布式的版本控制系统,例如 svn,cvs 等,最核心的区别。分布式带来以下好处:
-
-**工作时不需要联网**
-
-首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
-
-**更加安全**
-
-集中式版本控制系统,一旦中央服务器出了问题,所有人都无法工作。
-
-分布式版本控制系统,每个人电脑中都有完整的版本库,所以某人的机器挂了,并不影响其它人。
-
-## 原理
-
-### 版本库
-
-当你一个项目到本地或创建一个 git 项目,项目目录下会有一个隐藏的 `.git` 子目录。这个目录是 git 用来跟踪管理版本库的,千万不要手动修改。
-
-### 哈希值
-
-Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
-
-Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:
-
-```
-24b9da6552252987aa493b52f8696cd6d3b00373
-```
-
-Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
-
-### 文件状态
-
-在 GIt 中,你的文件可能会处于三种状态之一:
-
-- **已修改(modified)**
-
- 已修改表示修改了文件,但还没保存到数据库中。
-
-- **已暂存(staged)**
-
- 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
-
-- **已提交(committed)**
-
- 已提交表示数据已经安全的保存在本地数据库中。
-
-### 工作区域
-
-与文件状态对应的,不同状态的文件在 Git 中处于不同的工作区域。
-
-- **工作区(working)**
-
- 当你 `git clone` 一个项目到本地,相当于在本地克隆了项目的一个副本。
-
- 工作区是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
-
-
-- **暂存区(staging)**
-
- 暂存区是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作`‘索引’',不过一般说法还是叫暂存区。
-
-
-- **本地仓库(local)**
-
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 本地仓库。
-
-- **远程仓库(remote)**
-
- 以上几个工作区都是在本地。为了让别人可以看到你的修改,你需要将你的更新推送到远程仓库。
-
- 同理,如果你想同步别人的修改,你需要从远程仓库拉取更新。
-
-
-
-## 安装
-
-### Linux
-
-#### Debian/Ubuntu
-
-如果你使用的系统是 Debian/Ubuntu , 安装命令为:
-
-```sh
-$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
-> libz-dev libssl-dev
-$ apt-get install git-core
-$ git --version
-git version 1.8.1.2
-```
-
-#### Centos/RedHat
-
-如果你使用的系统是 Centos/RedHat ,安装命令为:
-
-```sh
-$ yum install curl-devel expat-devel gettext-devel \
-> openssl-devel zlib-devel
-$ yum -y install git-core
-$ git --version
-git version 1.7.1
-```
-
-### Windows
-
-在[Git 官方下载地址](https://git-scm.com/downloads)下载 exe 安装包。按照安装向导安装即可。
-
-建议安装 Git Bash 这个 git 的命令行工具。
-
-### Mac
-
-在[Git 官方下载地址](https://git-scm.com/downloads)下载 mac 安装包。按照安装向导安装即可。
-
-## 配置
-
-Git 自带一个 `git config` 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:
-
-1. `/etc/gitconfig` 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果使用带有 `--system` 选项的 `git config` 时,它会从此文件读写配置变量。
-2. `~/.gitconfig` 或 `~/.config/git/config` 文件:只针对当前用户。 可以传递 `--global` 选项让 Git 读写此文件。
-3. 当前使用仓库的 Git 目录中的 `config` 文件(就是 `.git/config`):针对该仓库。
-
-每一个级别覆盖上一级别的配置,所以 `.git/config` 的配置变量会覆盖 `/etc/gitconfig` 中的配置变量。
-
-在 Windows 系统中,Git 会查找 `$HOME` 目录下(一般情况下是 `C:\Users\$USER`)的 `.gitconfig` 文件。 Git 同样也会寻找 `/etc/gitconfig` 文件,但只限于 MSys 的根目录下,即安装 Git 时所选的目标位置。
-
-### 用户信息
-
-当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。 这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:
-
-```
-$ git config --global user.name "John Doe"
-$ git config --global user.email johndoe@example.com
-```
-
-再次强调,如果使用了 `--global` 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 `--global` 选项的命令来配置。
-
-很多 GUI 工具都会在第一次运行时帮助你配置这些信息。
-
-### .gitignore
-
-`.gitignore` 文件可能从字面含义也不难猜出:这个文件里配置的文件或目录,会自动被 git 所忽略,不纳入版本控制。
-
-在日常开发中,我们的项目经常会产生一些临时文件,如编译 Java 产生的 `*.class` 文件,又或是 IDE 自动生成的隐藏目录(Intellij 的 `.idea` 目录、Eclipse 的 `.settings` 目录等)等等。这些文件或目录实在没必要纳入版本管理。在这种场景下,你就需要用到 `.gitignore` 配置来过滤这些文件或目录。
-
-配置的规则很简单,也没什么可说的,看几个例子,自然就明白了。
-
-这里推荐一下 Github 的开源项目:https://github.com/github/gitignore
-
-在这里,你可以找到很多常用的模板,如:Java、Nodejs、C++ 的 `.gitignore` 模板等等。
-
-## 命令
-
-国外网友制作了一张 Git Cheat Sheet,总结很精炼,各位不妨收藏一下。
-
-本节选择性介绍 git 中比较常用的命令行场景。
-
-
-
-### 创建
-
-#### 克隆一个已创建的仓库
-
-```sh
-# 通过 SSH
-$ git clone ssh://user@domain.com/repo.git
-
-#通过 HTTP
-$ git clone http://domain.com/user/repo.git
-```
-
-#### 创建一个新的本地仓库
-
-```sh
-$ git init
-```
-
-### 添加修改
-
-#### 添加修改到暂存区
-
-```sh
-# 把指定文件添加到暂存区
-$ git add xxx
-
-# 把当前所有修改添加到暂存区
-$ git add .
-
-# 把所有修改添加到暂存区
-$ git add -A
-```
-
-#### 提交修改到本地仓库
-
-```sh
-# 提交本地的所有修改
-$ git commit -a
-
-# 提交之前已标记的变化
-$ git commit
-
-# 附加消息提交
-$ git commit -m 'commit message'
-```
-
-#### 储藏
-
-有时,我们需要在同一个项目的不同分支上工作。当需要切换分支时,偏偏本地的工作还没有完成,此时,提交修改显得不严谨,但是不提交代码又无法切换分支。这时,你可以使用 `git stash` 将本地的修改内容作为草稿储藏起来。
-
-官方称之为储藏,但我个人更喜欢称之为存草稿。
-
-```sh
-# 1. 将修改作为当前分支的草稿保存
-$ git stash
-
-# 2. 查看草稿列表
-$ git stash list
-stash@{0}: WIP on master: 6fae349 :memo: Writing docs.
-
-# 3.1 删除草稿
-$ git stash drop stash@{0}
-
-# 3.2 读取草稿
-$ git stash apply stash@{0}
-```
-
-### 撤销修改
-
-#### 撤销本地修改
-
-```sh
-# 移除缓存区的所有文件(i.e. 撤销上次git add)
-$ git reset HEAD
-
-# 将HEAD重置到上一次提交的版本,并将之后的修改标记为未添加到缓存区的修改
-$ git reset
-
-# 将HEAD重置到上一次提交的版本,并保留未提交的本地修改
-$ git reset --keep
-
-# 放弃工作目录下的所有修改
-$ git reset --hard HEAD
-
-# 将HEAD重置到指定的版本,并抛弃该版本之后的所有修改
-$ git reset --hard
-
-# 用远端分支强制覆盖本地分支
-$ git reset --hard e.g., upstream/master, origin/my-feature
-
-# 放弃某个文件的所有本地修改
-$ git checkout HEAD
-```
-
-##### 删除添加`.gitignore`文件前错误提交的文件
-
-```sh
-$ git rm -r --cached .
-$ git add .
-$ git commit -m "remove xyz file"
-```
-
-#### 撤销远程修改
-
-#### 创建一个新的提交,并回滚到指定版本
-
-```sh
-$ git revert
-```
-
-#### 彻底删除指定版本
-
-```sh
-# 执行下面命令后,commit-hash 提交后的记录都会被彻底删除,使用需谨慎
-$ git reset --hard
-$ git push -f
-```
-
-### 更新与推送
-
-#### 更新
-
-```sh
-# 下载远程端版本,但不合并到HEAD中
-$ git fetch
-
-# 将远程端版本合并到本地版本中
-$ git pull origin master
-
-# 以rebase方式将远端分支与本地合并
-$ git pull --rebase
-```
-
-#### 推送
-
-```sh
-# 将本地版本推送到远程端
-$ git push remote
-
-# 删除远程端分支
-$ git push : (since Git v1.5.0)
-$ git push --delete (since Git v1.7.0)
-
-# 发布标签
-$ git push --tags
-```
-
-### 查看信息
-
-#### 显示工作路径下已修改的文件
-
-```sh
-$ git status
-```
-
-#### 显示与上次提交版本文件的不同
-
-```sh
-$ git diff
-```
-
-#### 显示提交历史
-
-```sh
-# 从最新提交开始,显示所有的提交记录(显示hash, 作者信息,提交的标题和时间)
-$ git log
-
-# 显示某个用户的所有提交
-$ git log --author="username"
-
-# 显示某个文件的所有修改
-$ git log -p
-```
-
-#### 显示搜索内容
-
-```sh
-# 从当前目录的所有文件中查找文本内容
-$ git grep "Hello"
-
-# 在某一版本中搜索文本
-$ git grep "Hello" v2.5
-```
-
-### 分支与标签
-
-#### 增删查分支
-
-```sh
-# 列出所有的分支
-$ git branch
-
-# 列出所有的远端分支
-$ git branch -r
-
-# 基于当前分支创建新分支
-$ git branch
-
-# 基于远程分支创建新的可追溯的分支
-$ git branch --track
-
-# 删除本地分支
-$ git branch -d
-
-# 强制删除本地分支,将会丢失未合并的修改
-$ git branch -D
-```
-
-#### 切换分支
-
-```sh
-# 切换分支
-$ git checkout
-
-# 创建并切换到新分支
-$ git checkout -b
-```
-
-#### 标签
-
-```sh
-# 给当前版本打标签
-$ git tag
-
-# 给当前版本打标签并附加消息
-$ git tag -a
-```
-
-### 合并与重置
-
-> merge 与 rebase 虽然是 git 常用功能,但是强烈建议不要使用 git 命令来完成这项工作。
->
-> 因为如果出现代码冲突,在没有代码比对工具的情况下,实在太艰难了。
->
-> 你可以考虑使用各种 Git GUI 工具。
-
-#### 合并
-
-```sh
-# 将分支合并到当前HEAD中
-$ git merge
-```
-
-#### 重置
-
-```sh
-# 将当前HEAD版本重置到分支中,请勿重置已发布的提交
-$ git rebase
-```
-
-### Github
-
-Github 作为最著名的代码开源协作社区,在程序员圈想必无人不知,无人不晓。
-
-这里不赘述 Github 的用法,确实有不会用的新手同学,可以参考官方教程:https://guides.github.com/
-
-#### clone 方式
-
-Git 支持三种协议:HTTPS / SSH / GIT
-
-而 Github 上支持 HTTPS 和 SSH。
-
-HTTPS 这种方式要求你每次 push 时都要输入用户名、密码,有些繁琐。
-
-而 SSH 要求你本地生成证书,然后在你的 Github 账户中注册。第一次配置麻烦是麻烦了点,但是以后就免去了每次 push 需要输入用户名、密码的繁琐。
-
-
-
-以下介绍以下,如何生成证书,以及在 Github 中注册。
-
-#### 生成 SSH 公钥
-
-如前所述,许多 Git 服务器都使用 SSH 公钥进行认证。 为了向 Git 服务器提供 SSH 公钥,如果某系统用户尚未拥有密钥,必须事先为其生成一份。 这个过程在所有操作系统上都是相似的。 首先,你需要确认自己是否已经拥有密钥。 默认情况下,用户的 SSH 密钥存储在其 `~/.ssh` 目录下。 进入该目录并列出其中内容,你便可以快速确认自己是否已拥有密钥:
-
-```
-$ cd ~/.ssh
-$ ls
-authorized_keys2 id_dsa known_hosts
-config id_dsa.pub
-```
-
-我们需要寻找一对以 `id_dsa` 或 `id_rsa` 命名的文件,其中一个带有 `.pub` 扩展名。 `.pub` 文件是你的公钥,另一个则是私钥。 如果找不到这样的文件(或者根本没有 `.ssh` 目录),你可以通过运行 `ssh-keygen` 程序来创建它们。在 Linux/Mac 系统中,`ssh-keygen` 随 SSH 软件包提供;在 Windows 上,该程序包含于 MSysGit 软件包中。
-
-```
-$ ssh-keygen
-Generating public/private rsa key pair.
-Enter file in which to save the key (/home/schacon/.ssh/id_rsa):
-Created directory '/home/schacon/.ssh'.
-Enter passphrase (empty for no passphrase):
-Enter same passphrase again:
-Your identification has been saved in /home/schacon/.ssh/id_rsa.
-Your public key has been saved in /home/schacon/.ssh/id_rsa.pub.
-The key fingerprint is:
-d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local
-```
-
-首先 `ssh-keygen` 会确认密钥的存储位置(默认是 `.ssh/id_rsa`),然后它会要求你输入两次密钥口令。如果你不想在使用密钥时输入口令,将其留空即可。
-
-现在,进行了上述操作的用户需要将各自的公钥发送给任意一个 Git 服务器管理员(假设服务器正在使用基于公钥的 SSH 验证设置)。 他们所要做的就是复制各自的 `.pub` 文件内容,并将其通过邮件发送。 公钥看起来是这样的:
-
-```
-$ cat ~/.ssh/id_rsa.pub
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
-GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
-Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
-t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
-mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
-NrRFi9wrf+M7Q== schacon@mylaptop.local
-```
-
-在你的 Github 账户中,依次点击 **Settings** > **SSH and GPG keys** > **New SSH key**
-
-然后,将上面生成的公钥内容粘贴到 `Key` 编辑框并保存。至此大功告成。
-
-后面,你在克隆你的 Github 项目时使用 SSH 方式即可。
-
-
-
-如果觉得我的讲解还不够细致,可以参考:https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/
-
-## 小结
-
-最后,放一张我总结的脑图总结一下以上的知识点。
-
-
-
-## 资料
-
-[git 官网](https://git-scm.com/) | [git 官方 Github](https://github.com/git/git)
-
-[廖雪峰的 git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
-
-[git-cheat-sheet](https://github.com/arslanbilal/git-cheat-sheet)
-
-[github-cheat-sheet](https://github.com/tiimgreen/github-cheat-sheet)
-
-[Github gitignore 模板](https://github.com/github/gitignore)
diff --git a/os/git/git.xmind b/os/git/git.xmind
deleted file mode 100644
index 147612d998..0000000000
Binary files a/os/git/git.xmind and /dev/null differ
diff --git a/os/kubernetes/kubernetes.md b/os/kubernetes/kubernetes.md
deleted file mode 100644
index 283fcb2afc..0000000000
--- a/os/kubernetes/kubernetes.md
+++ /dev/null
@@ -1,176 +0,0 @@
-# Kubernetes
-
-> Kubernetes 是用于自动部署,扩展和管理 Docker 应用程序的开源系统。简称 K8S
->
-> 关键词: `docker`
-
-
-
-- [功能](#功能)
-- [简介](#简介)
-- [基础](#基础)
-- [进阶](#进阶)
-- [命令](#命令)
-- [引用和引申](#引用和引申)
-
-
-
-## 功能
-
-- 基于容器的应用部署、维护和滚动升级
-- 负载均衡和服务发现
-- 跨机器和跨地区的集群调度
-- 自动伸缩
-- 无状态服务和有状态服务
-- 广泛的 Volume 支持
-- 插件机制保证扩展性
-
-## 简介
-
-Kubernetes 主控组件(Master) 包含三个进程,都运行在集群中的某个节上,通常这个节点被称为 master 节点。这些进程包括:`kube-apiserver`、`kube-controller-manager` 和 `kube-scheduler`。
-
-集群中的每个非 master 节点都运行两个进程:
-
-- kubelet,和 master 节点进行通信。
-- kube-proxy,一种网络代理,将 Kubernetes 的网络服务代理到每个节点上。
-
-### Kubernetes 对象
-
-Kubernetes 包含若干抽象用来表示系统状态,包括:已部署的容器化应用和负载、与它们相关的网络和磁盘资源以及有关集群正在运行的其他操作的信息。
-
-
-- Pod - kubernetes 对象模型中最小的单元,它代表集群中一个正在运行的进程。
-- Service
-- Volume
-- Namespace
-
-
-
-高级对象
-
-- ReplicaSet
-- Deployment
-- StatefulSet
-- DaemonSet
-- Job
-
-## 基础
-
-## 进阶
-
-## 命令
-
-### 客户端配置
-
-```sh
-# Setup autocomplete in bash; bash-completion package should be installed first
-source <(kubectl completion bash)
-
-# View Kubernetes config
-kubectl config view
-
-# View specific config items by json path
-kubectl config view -o jsonpath='{.users[?(@.name == "k8s")].user.password}'
-
-# Set credentials for foo.kuberntes.com
-kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword
-```
-
-### 查找资源
-
-```sh
-# List all services in the namespace
-kubectl get services
-
-# List all pods in all namespaces in wide format
-kubectl get pods -o wide --all-namespaces
-
-# List all pods in json (or yaml) format
-kubectl get pods -o json
-
-# Describe resource details (node, pod, svc)
-kubectl describe nodes my-node
-
-# List services sorted by name
-kubectl get services --sort-by=.metadata.name
-
-# List pods sorted by restart count
-kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
-
-# Rolling update pods for frontend-v1
-kubectl rolling-update frontend-v1 -f frontend-v2.json
-
-# Scale a replicaset named 'foo' to 3
-kubectl scale --replicas=3 rs/foo
-
-# Scale a resource specified in "foo.yaml" to 3
-kubectl scale --replicas=3 -f foo.yaml
-
-# Execute a command in every pod / replica
-for i in 0 1; do kubectl exec foo-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done
-```
-
-### 资源管理
-
-```sh
-# Get documentation for pod or service
-kubectl explain pods,svc
-
-# Create resource(s) like pods, services or daemonsets
-kubectl create -f ./my-manifest.yaml
-
-# Apply a configuration to a resource
-kubectl apply -f ./my-manifest.yaml
-
-# Start a single instance of Nginx
-kubectl run nginx --image=nginx
-
-# Create a secret with several keys
-cat < Linux 中有非常多的命令,想全部背下来是很困难的事。所以,我认为学习 Linux 的第一步,就是了解如何快速检索命令说明。
->
-> 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man`
-
-
-
-- [查看 Linux 命令帮助信息的要点](#查看-linux-命令帮助信息的要点)
-- [命令常见用法](#命令常见用法)
- - [help](#help)
- - [whatis](#whatis)
- - [info](#info)
- - [which](#which)
- - [whereis](#whereis)
- - [man](#man)
-- [参考资料](#参考资料)
-
-
-
-## 查看 Linux 命令帮助信息的要点
-
-- 查看 Shell 内部命令的帮助信息 - 使用 [help](#help)
-- 查看命令的简要说明 - 使用 [whatis](#whatis)
-- 查看命令的详细说明 - 使用 [info](#info)
-- 查看命令的位置 - 使用 [which](#which)
-- 定位指令的二进制程序、源代码文件和 man 手册页等相关文件的路径 - 使用 [whereis](#whereis)
-- 查看命令的帮助手册(包含说明、用法等信息) - 使用 [man](#man)
-- 只记得部分命令关键字 - 使用 man -k
-
-> 注:推荐一些 Linux 命令中文手册:
->
-> - [Linux 命令大全](http://man.linuxde.net/)
-> - [linux-command](https://github.com/jaywcjlove/linux-command)
-
-## 命令常见用法
-
-### help
-
-> help 命令用于查看 Shell 内部命令的帮助信息。而对于外部命令的帮助信息只能使用 man 或者 info 命令查看。
->
-> 参考:http://man.linuxde.net/help
-
-### whatis
-
-> whatis 用于查询一个命令执行什么功能。
->
-> 参考:http://man.linuxde.net/whatis
-
-示例:
-
-```sh
-# 查看 man 命令的简要说明
-$ whatis man
-
-# 查看以 loca 开拓的命令的简要说明
-$ whatis -w "loca*"
-```
-
-### info
-
-> info 是 Linux 下 info 格式的帮助指令。
->
-> 参考:http://man.linuxde.net/info
-
-示例:
-
-```sh
-# 查看 man 命令的详细说明
-$ info man
-```
-
-### which
-
-> which 命令用于查找并显示给定命令的绝对路径,环境变量 PATH 中保存了查找命令时需要遍历的目录。which 指令会在环境变量$PATH 设置的目录里查找符合条件的文件。也就是说,使用 which 命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。
->
-> 参考:http://man.linuxde.net/which
-
-示例:
-
-```sh
-which pwd # 查找命令的路径
-```
-
-说明:which 是根据使用者所配置的 PATH 变量内的目录去搜寻可运行档的!所以,不同的 PATH 配置内容所找到的命令当然不一样的!
-
-```sh
-[root@localhost ~]# which cd
-cd: shell built-in command
-```
-
-cd 这个常用的命令竟然找不到啊!为什么呢?这是因为 cd 是 bash 内建的命令!但是 which 默认是找 PATH 内所规范的目录,所以当然一定找不到的!
-
-### whereis
-
-> whereis 命令用来定位指令的二进制程序、源代码文件和 man 手册页等相关文件的路径。
->
-> whereis 命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man 说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。
->
-> 参考:http://man.linuxde.net/whereis
-
-示例:
-
-```sh
-whereis git # 将相关的文件都查找出来
-```
-
-### man
-
-> man 命令是 Linux 下的帮助指令,通过 man 指令可以查看 Linux 中的指令帮助、配置文件帮助和编程帮助等信息。
->
-> 参考:http://man.linuxde.net/man
-
-示例:
-
-```sh
-$ man date # 查看 date 命令的帮助手册
-$ man 3 printf # 查看 printf 命令的帮助手册中的第 3 类
-$ man -k keyword # 根据命令中部分关键字来查询命令
-```
-
-#### man 要点
-
-在 man 的帮助手册中,可以使用 page up 和 page down 来上下翻页。
-
-man 的帮助手册中,将帮助文档分为了 9 个类别,对于有的关键字可能存在多个类别中, 我们就需要指定特定的类别来查看;(一般我们查询 bash 命令,归类在 1 类中)。
-
-man 页面的分类(常用的是分类 1 和分类 3):
-
-1. 可执行程序或 shell 命令
-2. 系统调用(内核提供的函数)
-3. 库调用(程序库中的函数)
-4. 特殊文件(通常位于 /dev)
-5. 文件格式和规范,如 /etc/passwd
-6. 游戏
-7. 杂项(包括宏包和规范,如 man(7),groff(7))
-8. 系统管理命令(通常只针对 root 用户)
-9. 内核例程 [非标准]
-
-前面说到使用 whatis 会显示命令所在的具体的文档类别,我们学习如何使用它
-
-```sh
-$ whatis printf
-printf (1) - format and print data
-printf (1p) - write formatted output
-printf (3) - formatted output conversion
-printf (3p) - print formatted output
-printf [builtins](1) - bash built-in commands, see bash(1)
-```
-
-我们看到 printf 在分类 1 和分类 3 中都有;分类 1 中的页面是命令操作及可执行文件的帮助;而 3 是常用函数库说明;如果我们想看的是 C 语言中 printf 的用法,可以指定查看分类 3 的帮助:
-
-```sh
-$ man 3 printf
-```
-
-## 参考资料
-
-https://linuxtools-rst.readthedocs.io/zh_CN/latest/base/01_use_man.html
diff --git "a/os/linux/02.Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md" "b/os/linux/02.Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md"
deleted file mode 100644
index c46e9e4490..0000000000
--- "a/os/linux/02.Linux\346\226\207\344\273\266\347\233\256\345\275\225\347\256\241\347\220\206.md"
+++ /dev/null
@@ -1,482 +0,0 @@
----
-title: Linux 文件目录管理命令
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 文件目录管理
-
-> 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm`
-
-
-
-- [Linux 文件目录工作机制](#linux-文件目录工作机制)
- - [Linux 目录结构](#linux-目录结构)
- - [Linux 文件属性](#linux-文件属性)
-- [Linux 文件目录管理要点](#linux-文件目录管理要点)
- - [目录管理](#目录管理)
- - [文件管理](#文件管理)
- - [文件和目录通用管理](#文件和目录通用管理)
-- [命令常见用法](#命令常见用法)
- - [cd](#cd)
- - [ls](#ls)
- - [pwd](#pwd)
- - [mkdir](#mkdir)
- - [rmdir](#rmdir)
- - [tree](#tree)
- - [touch](#touch)
- - [ln](#ln)
- - [rename](#rename)
- - [stat](#stat)
- - [file](#file)
- - [chmod](#chmod)
- - [chown](#chown)
- - [locate](#locate)
- - [find](#find)
- - [cp](#cp)
- - [mv](#mv)
- - [rm](#rm)
-
-
-
-## Linux 文件目录工作机制
-
-### Linux 目录结构
-
-linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用:
-
-
-
-### Linux 文件属性
-
-Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限。为了保护系统的安全性,Linux 系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定。
-在 Linux 中我们可以使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组,如:
-
-```sh
-$ ls -l
-total 64
-dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin
-dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot
-```
-
-实例中,bin 文件的第一个属性用 `d` 表示。`d` 在 Linux 中代表该文件是一个目录文件。
-在 Linux 中第一个字符代表这个文件是目录、文件或链接文件等等。
-
-- 当为 `d` 则是目录
-- 当为 `-` 则是文件;
-- 若是 `l` 则表示为链接文档(link file);
-- 若是 `b` 则表示为装置文件里面的可供储存的接口设备(可随机存取装置);
-- 若是 `c` 则表示为装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。
-
-接下来的字符中,以三个为一组,且均为『rwx』 的三个参数的组合。其中,`r` 代表可读(read)、`w` 代表可写(write)、`x` 代表可执行(execute)。 要注意的是,这三个权限的位置不会改变,如果没有权限,就会出现减号 `-` 而已。
-
-每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。
-
-
-
-从左至右用 0-9 这些数字来表示。
-
-- 第 0 位确定文件类型
-- 第 1-3 位确定属主(该文件的拥有者)拥有该文件的权限。
-- 第 4-6 位确定属组(拥有者的同组用户)拥有该文件的权限。
-- 第 7-9 位确定其他用户拥有该文件的权限。
-- 第 1、4、7 位表示读权限,如果用"r"字符表示,则有读权限,如果用"-"字符表示,则没有读权限。
-- 第 2、5、8 位表示写权限,如果用"w"字符表示,则有写权限,如果用"-"字符表示没有写权限。
-- 第 3、6、9 位表示可执行权限,如果用"x"字符表示,则有执行权限,如果用"-"字符表示,则没有执行权限。
-
-#### Linux 文件属主和属组
-
-```sh
-$ ls -l
-total 64
-dr-xr-xr-x 2 root root 4096 Dec 14 2012 bin
-dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot
-```
-
-- 对于文件来说,它都有一个特定的拥有者,也就是对该文件具有所有权的用户。
-- 同时,在 Linux 系统中,用户是按组分类的,一个用户属于一个或多个组。
-- 文件拥有者以外的用户又可以分为文件拥有者的同组用户和其他用户。
-- 因此,Linux 系统按文件拥有者、文件拥有者同组用户和其他用户来规定了不同的文件访问权限。
-- 在以上实例中,bin 文件是一个目录文件,属主和属组都为 root,属主有可读、可写、可执行的权限;与属主同组的其他用户有可读和可执行的权限;其他用户也有可读和可执行的权限。
-
-## Linux 文件目录管理要点
-
-### 目录管理
-
-- 切换目录 - 使用 [cd](#cd)
-- 查看目录信息 - 使用 [ls](#ls)
-- 显示当前目录的绝对路径 - 使用 [pwd](#pwd)
-- 树状显示目录的内容 - 使用 [tree](#tree)
-- 创建目录 - 使用 [mkdir](#mkdir)
-- 删除目录 - 使用 [rmdir](#rmdir)
-
-### 文件管理
-
-- 创建空文件 - 使用 [touch](#touch)
-- 为文件创建连接 - 使用 [ln](#ln)
-- 批量重命名 - 使用 [rename](#rename)
-- 显示文件的详细信息 - 使用 [stat](#stat)
-- 探测文件类型 - 使用 [file](#file)
-- 设置文件或目录的权限 - 使用 [chmod](#chmod)
-- 设置文件或目录的拥有者或所属群组 - 使用 [chown](#chown)
-- 查找文件或目录 - 使用 [locate](#locate)
-- 在指定目录下查找文件 - 使用 [find](#find)
-- 查找命令的绝对路径 - 使用 [which](#which)
-- 查找命令的程序、源代码等相关文件 - 使用 [whereis](#whereis)
-
-### 文件和目录通用管理
-
-- 复制文件或目录 - 使用 [cp](#cp)
-- 移动文件或目录 - 使用 [mv](#mv)
-- 删除文件或目录 - 使用 [rm](#rm)
-
-## 命令常见用法
-
-### cd
-
-> cd 命令用来切换工作目录。
->
-> 参考:http://man.linuxde.net/cd
-
-示例:
-
-```sh
-cd # 切换到用户主目录
-cd ~ # 切换到用户主目录
-cd - # 切换到上一个工作目录
-cd .. # 切换到上级目录
-cd ../.. # 切换到上两级目录
-```
-
-### ls
-
-> ls 命令用来显示目录信息。
->
-> 参考:http://man.linuxde.net/ls
-
-示例:
-
-```sh
-ls # 列出当前目录可见文件
-ls -l # 列出当前目录可见文件详细信息
-ls -la # 列出所有文件(包括隐藏)的详细信息
-ls -lh # 列出详细信息并以可读大小显示文件大小
-ls -lt # 按时间列出文件和文件夹详细信息
-ls -ltr # 按修改时间列出文件和文件夹详细信息
-ls --color=auto # 列出文件并标记颜色分类
-```
-
-### pwd
-
-> pwd 命令用来显示当前目录的绝对路径。
->
-> 参考:http://man.linuxde.net/pwd
-
-### mkdir
-
-> mkdir 命令用来创建目录。
->
-> 参考:http://man.linuxde.net/mkdir
-
-示例:
-
-```sh
-# 在当前目录中创建 zp 和 zp 的子目录 test
-mkdir -p zp/test
-
-# 在当前目录中创建 zp 和 zp 的子目录 test;权限设置为文件主可读、写、执行,同组用户可读和执行,其他用户无权访问
-mkdir -p -m 750 zp/test
-```
-
-### rmdir
-
-> rmdir 命令用来删除空目录。
->
-> 参考:http://man.linuxde.net/rmdir
-
-示例:
-
-```sh
-# 删除子目录 test 和其父目录 zp
-rmdir -p zp/test
-```
-
-### tree
-
-> tree 命令以树状显示目录的内。
->
-> 参考:http://man.linuxde.net/tree
-
-示例:
-
-```sh
-# 列出目录 /private 第一级文件名
-tree /private -L 1
-/private/
-├── etc
-├── tftpboot
-├── tmp
-└── var
-
-# 忽略文件夹
-tree -I node_modules # 忽略当前目录文件夹 node_modules
-tree -P node_modules # 列出当前目录文件夹 node_modules 的目录结构
-tree -P node_modules -L 2 # 显示目录 node_modules 两层的目录树结构
-tree -L 2 > /home/www/tree.txt # 当前目录结果存到 tree.txt 文件中
-
-# 忽略多个文件夹
-tree -I 'node_modules|icon|font' -L 2
-```
-
-### touch
-
-> touch 命令有两个功能:一是用于把已存在文件的时间标签更新为系统当前的时间(默认方式),它们的数据将原封不动地保留下来;二是用来创建空文件。
->
-> 参考:http://man.linuxde.net/touch
-
-示例:
-
-```
-touch ex2
-```
-
-### ln
-
-> ln 命令用来为文件创建连接,连接类型分为硬连接和符号连接两种,默认的连接类型是硬连接。如果要创建符号连接必须使用"-s"选项。
->
-> 注意:符号链接文件不是一个独立的文件,它的许多属性依赖于源文件,所以给符号链接文件设置存取权限是没有意义的。
->
-> 参考:http://man.linuxde.net/ln
-
-示例:
-
-```sh
-# 将目录 /usr/mengqc/mub1 下的文件 m2.c 链接到目录 /usr/liu 下的文件 a2.c
-cd /usr/mengqc
-ln /mub1/m2.c /usr/liu/a2.c
-
-# 在目录 /usr/liu 下建立一个符号链接文件 abc,使它指向目录 /usr/mengqc/mub1
-# 执行该命令后,/usr/mengqc/mub1 代表的路径将存放在名为 /usr/liu/abc 的文件中
-ln -s /usr/mengqc/mub1 /usr/liu/abc
-```
-
-### rename
-
-> rename 命令用字符串替换的方式批量重命名。
->
-> 参考:http://man.linuxde.net/rename
-
-示例:
-
-```sh
-# 将 main1.c 重命名为 main.c
-rename main1.c main.c main1.c
-
-rename "s/AA/aa/" * # 把文件名中的 AA 替换成 aa
-rename "s//.html//.php/" * # 把 .html 后缀的改成 .php 后缀
-rename "s/$//.txt/" * # 把所有的文件名都以 txt 结尾
-rename "s//.txt//" * # 把所有以 .txt 结尾的文件名的.txt 删掉
-```
-
-### stat
-
-> stat 命令用于显示文件的状态信息。stat 命令的输出信息比 ls 命令的输出信息要更详细。
->
-> 参考:http://man.linuxde.net/stat
-
-示例:
-
-```sh
-stat myfile
-```
-
-### file
-
-> file 命令用来探测给定文件的类型。file 命令对文件的检查分为文件系统、魔法幻数检查和语言检查 3 个过程。
->
-> 参考:http://man.linuxde.net/file
-
-示例:
-
-```sh
-file install.log # 显示文件类型
-file -b install.log # 不显示文件名称
-file -i install.log # 显示 MIME 类型
-file -L /var/spool/mail # 显示符号链接的文件类型
-```
-
-### chmod
-
-> chmod 命令用来变更文件或目录的权限。在 UNIX 系统家族里,文件或目录权限的控制分别以读取、写入、执行 3 种一般权限来区分,另有 3 种特殊权限可供运用。用户可以使用 chmod 指令去变更文件与目录的权限,设置方式采用文字或数字代号皆可。符号连接的权限无法变更,如果用户对符号连接修改权限,其改变会作用在被连接的原始文件。
->
-> 参考:http://man.linuxde.net/chmod
-
-知识扩展:
-
-Linux 用 户分为:拥有者、组群(Group)、其他(other),Linux 系统中,预设的情況下,系统中所有的帐号与一般身份使用者,以及 root 的相关信 息, 都是记录在`/etc/passwd`文件中。每个人的密码则是记录在`/etc/shadow`文件下。 此外,所有的组群名称记录在`/etc/group`內!
-
-linux 文件的用户权限的分析图
-
-```bash
- -rw-r--r-- 1 user staff 651 Oct 12 12:53 .gitmodules
-# ↑╰┬╯╰┬╯╰┬╯
-# ┆ ┆ ┆ ╰┈ 0 其他人
-# ┆ ┆ ╰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ g 属组
-# ┆ ╰┈┈┈┈ u 属组
-# ╰┈┈ 第一个字母 `d` 代表目录,`-` 代表普通文件
-```
-
-例:rwx rw- r--
-
-r=读取属性 //值= 4
-w=写入属性 //值= 2
-x=执行属性 //值= 1
-
-示例:
-
-```sh
-chmod u+x,g+w f01 # 为文件f01设置自己可以执行,组员可以写入的权限
-chmod u=rwx,g=rw,o=r f01
-chmod 764 f01
-chmod a+x f01 # 对文件f01的u,g,o都设置可执行属性
-
-# 将/home/wwwroot/里的所有文件和文件夹设置为755权限
-chmod -R 755 /home/wwwroot/*
-```
-
-### chown
-
-> chown 命令改变某个文件或目录的所有者和所属的组,该命令可以向某个用户授权,使该用户变成指定文件的所有者或者改变文件所属的组。用户可以是用户或者是用户 D,用户组可以是组名或组 id。文件名可以使由空格分开的文件列表,在文件名中可以包含通配符。
->
-> 只有文件拥有者和超级用户才可以便用该命令。
->
-> 参考:http://man.linuxde.net/chown
-
-示例:
-
-```sh
-# 将目录/usr/meng及其下面的所有文件、子目录的文件主改成 liu
-chown -R liu /usr/meng
-```
-
-### locate
-
-> locate 命令和 slocate 命令都用来查找文件或目录。
->
-> locate 命令其实是 find -name 的另一种写法,但是要比后者快得多,原因在于它不搜索具体目录,而是搜索一个数据库/var/lib/locatedb,这个数据库中含有本地所有文件信息。Linux 系统自动创建这个数据库,并且每天自动更新一次,所以使用 locate 命令查不到最新变动过的文件。为了避免这种情况,可以在使用 locate 之前,先使用 updatedb 命令,手动更新数据库。
->
-> 参考:http://man.linuxde.net/locate_slocate
-
-示例:
-
-```sh
-locate pwd # 查找和 pwd 相关的所有文件
-locate /etc/sh # 搜索 etc 目录下所有以 sh 开头的文件
-```
-
-### find
-
-> find 命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则 find 命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。
->
-> 参考:http://man.linuxde.net/find
-
-```sh
-# 当前目录搜索所有文件,文件内容 包含 “140.206.111.111” 的内容
-find . -type f -name "*" | xargs grep "140.206.111.111"
-
-# 列出当前目录及子目录下所有文件和文件夹
-find .
-
-# 在 /home 目录下查找以 .txt 结尾的文件名
-find /home -name "*.txt"
-# 同上,但忽略大小写
-find /home -iname "*.txt"
-
-# 当前目录及子目录下查找所有以 .txt 和 .pdf 结尾的文件
-find . -name "*.txt" -o -name "*.pdf"
-
-# 匹配文件路径或者文件
-find /usr/ -path "*local*"
-
-# 基于正则表达式匹配文件路径
-find . -regex ".*\(\.txt\|\.pdf\)$"
-# 同上,但忽略大小写
-find . -iregex ".*\(\.txt\|\.pdf\)$"
-
-# 找出 /home 下不是以 .txt 结尾的文件
-find /home ! -name "*.txt"
-```
-
-### cp
-
-> cp 命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录。它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下。cp 命令还支持同时复制多个文件,当一次复制多个文件时,目标文件参数必须是一个已经存在的目录,否则将出现错误。
->
-> 参考:http://man.linuxde.net/cp
-
-示例:
-
-#### 参数
-
-- 源文件:制定源文件列表。默认情况下,cp 命令不能复制目录,如果要复制目录,则必须使用`-R`选项;
-- 目标文件:指定目标文件。当“源文件”为多个文件时,要求“目标文件”为指定的目录。
-
-示例:
-
-```sh
-# 将文件 file 复制到目录 /usr/men/tmp 下,并改名为 file1
-cp file /usr/men/tmp/file1
-
-# 将目录 /usr/men下的所有文件及其子目录复制到目录 /usr/zh 中
-cp -r /usr/men /usr/zh
-
-# 强行将 /usr/men下的所有文件复制到目录 /usr/zh 中,无论是否有文件重复
-cp -rf /usr/men/* /usr/zh
-
-# 将目录 /usr/men 中的以 m 打头的所有 .c 文件复制到目录 /usr/zh 中
-cp -i /usr/men m*.c /usr/zh
-```
-
-### mv
-
-> mv 命令用来对文件或目录重新命名,或者将文件从一个目录移到另一个目录中。source 表示源文件或目录,target 表示目标文件或目录。如果将一个文件移到一个已经存在的目标文件中,则目标文件的内容将被覆盖。
->
-> 参考:http://man.linuxde.net/mv
-
-示例:
-
-```sh
-mv file1.txt /home/office/ # 移动单个文件
-mv file2.txt file3.txt file4.txt /home/office/ # 移动多个文件
-mv *.txt /home/office/ # 移动所有 txt 文件
-mv dir1/ /home/office/ # 移动目录
-mv /usr/men/* . # 将指定目录中的所有文件移到当前目录中
-
-mv file1.txt file2.txt # 重命名文件
-mv dir1/ dir2/ # 重命名目录
-mv -v *.txt /home/office # 打印移动信息
-mv -i file1.txt /home/office # 提示是否覆盖文件
-
-mv -uv *.txt /home/office # 源文件比目标文件新时才执行更新
-mv -vn *.txt /home/office # 不要覆盖任何已存在的文件
-mv -f *.txt /home/office # 无条件覆盖已经存在的文件
-mv -bv *.txt /home/office # 复制时创建备份
-```
-
-### rm
-
-> rm 命令可以删除一个目录中的一个或多个文件或目录,也可以将某个目录及其下属的所有文件及其子目录均删除掉。对于链接文件,只是删除整个链接文件,而原有文件保持不变。
->
-> 参考:http://man.linuxde.net/rm
-
-```sh
-rm test.txt # 删除文件
-rm -i test.txt test2.txt # 交互式删除文件
-rm -r * # 删除当前目录下的所有文件和目录
-rm -r testdir # 删除目录下的所有文件和目录
-rm -rf testdir # 强制删除目录下的所有文件和目录
-rm -v testdir # 显示当前删除操作的详情
-```
diff --git "a/os/linux/03.Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md" "b/os/linux/03.Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md"
deleted file mode 100644
index f0fac7a55d..0000000000
--- "a/os/linux/03.Linux\346\226\207\344\273\266\345\206\205\345\256\271\346\237\245\347\234\213\347\274\226\350\276\221.md"
+++ /dev/null
@@ -1,188 +0,0 @@
----
-title: Linux 文件内容查看编辑
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 文件内容查看编辑
-
-> 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep`
-
-
-
-- [Linux 文件内容查看编辑要点](#linux-文件内容查看编辑要点)
-- [命令常见用法](#命令常见用法)
- - [cat](#cat)
- - [head](#head)
- - [tail](#tail)
- - [more](#more)
- - [less](#less)
- - [sed](#sed)
- - [vi](#vi)
- - [grep](#grep)
-- [参考资料](#参考资料)
-
-
-
-## Linux 文件内容查看编辑要点
-
-- 连接文件并打印到标准输出设备 - 使用 [cat](#cat)
-- 显示指定文件的开头若干行 - 使用 [head](#head)
-- 显示指定文件的末尾若干行,常用于实时打印日志文件内容 - 使用 [tail](#tail)
-- 显示文件内容,每次显示一屏 - 使用 [more](#more)
-- 显示文件内容,每次显示一屏 - 使用 [less](#less)
-- 自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等 - 使用 [sed](#sed)
-- 文本编辑器 - 使用 [vi](#vi)
-- 使用正则表达式搜索文本,并把匹配的行打印出来 - 使用 [grep](#grep)
-
-## 命令常见用法
-
-### cat
-
-> cat 命令用于连接文件并打印到标准输出设备上。
->
-> 参考:http://man.linuxde.net/cat
-
-示例:
-
-```sh
-cat m1 # 在屏幕上显示文件 ml 的内容
-cat m1 m2 # 同时显示文件 ml 和 m2 的内容
-cat m1 m2 > file # 将文件 ml 和 m2 合并后放入文件 file 中
-```
-
-### head
-
-> head 命令用于显示文件的开头内容。在默认情况下,head 命令显示文件的头部 10 行内容。
->
-> 参考:http://man.linuxde.net/head
-
-### tail
-
-> tail 命令用于显示文件的尾部内容。在默认情况下,tail 命令显示文件的尾部 10 行内容。如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题。如果没有指定文件或者文件名为“-”,则读取标准输入。
->
-> 参考:http://man.linuxde.net/tail
-
-示例:
-
-```sh
-tail file # 显示文件file的最后10行
-tail -n +20 file # 显示文件file的内容,从第20行至文件末尾
-tail -c 10 file # 显示文件file的最后10个字符
-```
-
-### more
-
-> more 命令是一个基于 vi 编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持 vi 中的关键字定位操作。more 名单中内置了若干快捷键,常用的有 H(获得帮助信息),Enter(向下翻滚一行),空格(向下滚动一屏),Q(退出命令)。
->
-> 该命令一次显示一屏文本,满屏后停下来,并且在屏幕的底部出现一个提示信息,给出至今己显示的该文件的百分比:--More--(XX%)可以用下列不同的方法对提示做出回答:
->
-> - 按 Space 键:显示文本的下一屏内容。
-> - 按 Enier 键:只显示文本的下一行内容。
-> - 按斜线符|:接着输入一个模式,可以在文本中寻找下一个相匹配的模式。
-> - 按 H 键:显示帮助屏,该屏上有相关的帮助信息。
-> - 按 B 键:显示上一屏内容。
-> - 按 Q 键:退出 rnore 命令。
->
-> 参考:http://man.linuxde.net/more
-
-示例:
-
-```sh
-# 显示文件 file 的内容,但在显示之前先清屏,并且在屏幕的最下方显示完核的百分比。
-more -dc file
-
-# 显示文件 file 的内容,每 10 行显示一次,而且在显示之前先清屏。
-more -c -10 file
-```
-
-### less
-
-less 命令的作用与 more 十分相似,都可以用来浏览文字档案的内容,不同的是 less 命令允许用户向前或向后浏览文件,而 more 命令只能向前浏览。用 less 命令显示文件时,用 PageUp 键向上翻页,用 PageDown 键向下翻页。要退出 less 程序,应按 Q 键。
-
-示例:
-
-```sh
-less /var/log/shadowsocks.log
-```
-
-### sed
-
-> sed 是一种流编辑器,它是文本处理工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed 主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。
->
-> 参考:http://man.linuxde.net/sed
-
-示例:
-
-```sh
-# 替换文本中的字符串
-sed 's/book/books/' file
-
-# -n 选项 和 p 命令 一起使用表示只打印那些发生替换的行
-sed -n 's/test/TEST/p' file
-
-# 直接编辑文件选项 -i ,会匹配 file 文件中每一行的第一个 book 替换为 books
-sed -i 's/book/books/g' file
-
-# 使用后缀 /g 标记会替换每一行中的所有匹配
-sed 's/book/books/g' file
-
-# 删除空白行
-sed '/^$/d' file
-
-# 删除文件的第2行
-sed '2d' file
-
-# 删除文件的第2行到末尾所有行
-sed '2,$d' file
-
-# 删除文件最后一行
-sed '$d' file
-
-# 删除文件中所有开头是test的行
-sed '/^test/'d file
-```
-
-### vi
-
-> vi 命令是 UNIX 操作系统和类 UNIX 操作系统中最通用的全屏幕纯文本编辑器。Linux 中的 vi 编辑器叫 vim,它是 vi 的增强版(vi Improved),与 vi 编辑器完全兼容,而且实现了很多增强功能。
->
-> 参考:http://man.linuxde.net/vi
->
-> 引申阅读:[Vim 快速指南](https://github.com/dunwu/OS/blob/master/docs/vim.md)
-
-### grep
-
-> grep(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。
->
-> 参考:http://man.linuxde.net/grep
-
-示例:
-
-```sh
-# 在多级目录中对文本递归搜索(程序员搜代码的最爱):
-$ grep "class" . -R -n
-
-# 忽略匹配样式中的字符大小写
-$ echo "hello world" | grep -i "HELLO"
-
-# 匹配多个模式:
-$ grep -e "class" -e "vitural" file
-
-# 只在目录中所有的.php和.html文件中递归搜索字符"main()"
-$ grep "main()" . -r --include *.{php,html}
-
-# 在搜索结果中排除所有README文件
-$ grep "main()" . -r --exclude "README"
-
-# 在搜索结果中排除filelist文件列表里的文件
-$ grep "main()" . -r --exclude-from filelist
-```
-
-## 参考资料
-
-- [Linux 命令大全](http://man.linuxde.net/)
diff --git "a/os/linux/04.Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md" "b/os/linux/04.Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md"
deleted file mode 100644
index db5c0e8d78..0000000000
--- "a/os/linux/04.Linux\346\226\207\344\273\266\345\216\213\347\274\251\345\222\214\350\247\243\345\216\213.md"
+++ /dev/null
@@ -1,97 +0,0 @@
----
-title: Linux 文件压缩和解压
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 文件压缩和解压
-
-> 关键词:`tar`, `gzip`, `zip`, `unzip`
-
-
-
-- [Linux 文件压缩和解压要点](#linux-文件压缩和解压要点)
-- [命令常见用法](#命令常见用法)
- - [tar](#tar)
- - [gzip](#gzip)
- - [zip](#zip)
- - [unzip](#unzip)
-
-
-
-## Linux 文件压缩和解压要点
-
-- 压缩和解压 tar 文件 - 使用 [tar](#tar)
-- 压缩和解压 gz 文件 - 使用 [gzip](#gzip)
-- 压缩和解压 zip 文件 - 分别使用 [zip](#zip)、[unzip](#unzip)
-
-## 命令常见用法
-
-### tar
-
-> tar 命令可以为 linux 的文件和目录创建档案。利用 tar,可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件。tar 最初被用来在磁带上创建档案,现在,用户可以在任何设备上创建档案。利用 tar 命令,可以把一大堆的文件和目录全部打包成一个文件,这对于备份文件或将几个文件组合成为一个文件以便于网络传输是非常有用的。
->
-> 参考:http://man.linuxde.net/tar
-
-示例:
-
-```sh
-tar -cvf log.tar log2012.log # 仅打包,不压缩
-tar -zcvf log.tar.gz log2012.log # 打包后,以 gzip 压缩
-tar -jcvf log.tar.bz2 log2012.log # 打包后,以 bzip2 压缩
-
-tar -ztvf log.tar.gz # 查阅上述 tar 包内有哪些文件
-tar -zxvf log.tar.gz # 将 tar 包解压缩
-tar -zxvf log30.tar.gz log2013.log # 只将 tar 内的部分文件解压出来
-```
-
-### gzip
-
-> gzip 命令用来压缩文件。gzip 是个使用广泛的压缩程序,文件经它压缩过后,其名称后面会多出“.gz”扩展名。
->
-> gzip 是在 Linux 系统中经常使用的一个对文件进行压缩和解压缩的命令,既方便又好用。gzip 不仅可以用来压缩大的、较少使用的文件以节省磁盘空间,还可以和 tar 命令一起构成 Linux 操作系统中比较流行的压缩文件格式。据统计,gzip 命令对文本文件有 60%~ 70%的压缩率。减少文件大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。
->
-> 参考:http://man.linuxde.net/gzip
-
-示例:
-
-```sh
-gzip * # 将所有文件压缩成 .gz 文件
-gzip -l * # 详细显示压缩文件的信息,并不解压
-gzip -dv * # 解压上例中的所有压缩文件,并列出详细的信息
-gzip -r log.tar # 压缩一个 tar 备份文件,此时压缩文件的扩展名为.tar.gz
-gzip -rv test/ # 递归的压缩目录
-gzip -dr test/ # 递归地解压目录
-```
-
-### zip
-
-> zip 命令可以用来解压缩文件,或者对文件进行打包操作。zip 是个使用广泛的压缩程序,文件经它压缩后会另外产生具有“.zip”扩展名的压缩文件。
->
-> 参考:http://man.linuxde.net/zip
-
-示例:
-
-```sh
-# 将 /home/Blinux/html/ 这个目录下所有文件和文件夹打包为当前目录下的 html.zip
-zip -q -r html.zip /home/Blinux/html
-```
-
-### unzip
-
-> unzip 命令用于解压缩由 zip 命令压缩的“.zip”压缩包。
->
-> 参考:http://man.linuxde.net/unzip
-
-示例:
-
-```sh
-unzip test.zip # 解压 zip 文件
-unzip -n test.zip -d /tmp/ # 在指定目录下解压缩
-unzip -o test.zip -d /tmp/ # 在指定目录下解压缩,如果有相同文件存在则覆盖
-unzip -v test.zip # 查看压缩文件目录,但不解压
-```
diff --git "a/os/linux/05.Linux\347\224\250\346\210\267\347\256\241\347\220\206.md" "b/os/linux/05.Linux\347\224\250\346\210\267\347\256\241\347\220\206.md"
deleted file mode 100644
index c6422baeb2..0000000000
--- "a/os/linux/05.Linux\347\224\250\346\210\267\347\256\241\347\220\206.md"
+++ /dev/null
@@ -1,221 +0,0 @@
----
-title: Linux 用户管理
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 用户管理
-
-> 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo`
-
-
-
-- [Linux 用户管理要点](#linux-用户管理要点)
-- [命令常见用法](#命令常见用法)
- - [groupadd](#groupadd)
- - [groupdel](#groupdel)
- - [groupmod](#groupmod)
- - [useradd](#useradd)
- - [userdel](#userdel)
- - [usermod](#usermod)
- - [passwd](#passwd)
- - [su](#su)
- - [sudo](#sudo)
-
-
-
-## Linux 用户管理要点
-
-- 创建用户组 - 使用 [groupadd](#groupadd)
-- 删除用户组 - 使用 [groupdel](#groupdel)
-- 修改用户组信息 - 使用 [groupmod](#groupmod)
-- 创建用户 - 使用 [useradd](#useradd)
-- 删除用户 - 使用 [userdel](#userdel)
-- 修改用户信息 - 使用 [usermod](#usermod)
-- 设置用户认证信息 - 使用 [passwd](#passwd)
-- 切换用户 - 使用 [su](#su)
-- 当前用户想执行没有权限执行的命令时,使用其他用户身份去执行 - 使用 [sudo](#sudo)
-
-## 命令常见用法
-
-### groupadd
-
-> groupadd 命令用于创建一个新的用户组,新用户组的信息将被添加到系统文件中。
->
-> 参考:http://man.linuxde.net/groupadd
-
-示例:
-
-```sh
-# 建立一个新组,并设置组 ID 加入系统
-$ groupadd -g 344 jsdigname
-```
-
-### groupdel
-
-> groupdel 命令用于删除指定的用户组,本命令要修改的系统文件包括 `/ect/group` 和 `/ect/gshadow`。若该群组中仍包括某些用户,则必须先删除这些用户后,方能删除群组。
->
-> 参考:http://man.linuxde.net/groupdel
-
-示例:
-
-```sh
-$ groupadd damon # 创建damon用户组
-$ groupdel damon # 删除这个用户组
-```
-
-### groupmod
-
-> groupmod 命令更改群组识别码或名称。需要更改群组的识别码或名称时,可用 groupmod 指令来完成这项工作。
->
-> 参考:http://man.linuxde.net/groupmod
-
-### useradd
-
-> useradd 命令用于 Linux 中创建的新的系统用户。useradd 可用来建立用户帐号。帐号建好之后,再用 passwd 设定帐号的密码.而可用 userdel 删除帐号。使用 useradd 指令所建立的帐号,实际上是保存在 `/etc/passwd` 文本文件中。
->
-> 参考:http://man.linuxde.net/useradd
-
-示例:
-
-```sh
-# 新建用户加入组
-$ useradd –g sales jack –G company,employees # -g:加入主要组、-G:加入次要组
-
-# 建立一个新用户账户,并设置 ID
-$ useradd caojh -u 544
-```
-
-### userdel
-
-> userdel 命令用于删除给定的用户,以及与用户相关的文件。若不加选项,则仅删除用户帐号,而不删除相关文件。
->
-> 参考:http://man.linuxde.net/userdel
-
-示例:
-
-userdel 命令很简单,比如我们现在有个用户 linuxde,其 home 目录位于`/var`目录中,现在我们来删除这个用户:
-
-```sh
-$ userdel linuxde # 删除用户linuxde,但不删除其家目录及文件;
-$ userdel -r linuxde # 删除用户linuxde,其 home 目录及文件一并删除;
-```
-
-### usermod
-
-> usermod 命令用于修改用户的基本信息。usermod 命令不允许你改变正在线上的使用者帐号名称。当 usermod 命令用来改变 user id,必须确认这名 user 没在电脑上执行任何程序。你需手动更改使用者的 crontab 档。也需手动更改使用者的 at 工作档。采用 NIS server 须在 server 上更动相关的 NIS 设定。
->
-> 参考:http://man.linuxde.net/usermod
-
-示例:
-
-```sh
-# 将 newuser2 添加到组 staff 中
-$ usermod -G staff newuser2
-
-# 修改 newuser 的用户名为 newuser1
-$ usermod -l newuser1 newuser
-
-# 锁定账号 newuser1
-$ usermod -L newuser1
-
-# 解除对 newuser1 的锁定
-$ usermod -U newuser1
-```
-
-### passwd
-
-> passwd 命令用于设置用户的认证信息,包括用户密码、密码过期时间等。系统管理者则能用它管理系统用户的密码。只有管理者可以指定用户名称,一般用户只能变更自己的密码。
->
-> 参考:http://man.linuxde.net/passwd
-
-示例:
-
-```sh
-# 如果是普通用户执行 passwd 只能修改自己的密码。
-# 如果新建用户后,要为新用户创建密码,则用 passwd 用户名,注意要以 root 用户的权限来创建。
-$ passwd linuxde # 更改或创建linuxde用户的密码;
-Changing password for user linuxde.
-New UNIX password: # 请输入新密码;
-Retype new UNIX password: # 再输入一次;
-passwd: all authentication tokens updated successfully. # 成功;
-
-# 普通用户如果想更改自己的密码,直接运行 passwd 即可,比如当前操作的用户是 linuxde。
-$ passwd
-Changing password for user linuxde. # 更改linuxde用户的密码;
-(current) UNIX password: # 请输入当前密码;
-New UNIX password: # 请输入新密码;
-Retype new UNIX password: # 确认新密码;
-passwd: all authentication tokens updated successfully. # 更改成功;
-
-# 比如我们让某个用户不能修改密码,可以用`-l`选项来锁定:
-$ passwd -l linuxde # 锁定用户linuxde不能更改密码;
-Locking password for user linuxde.
-passwd: Success # 锁定成功;
-
-$ su linuxde # 通过su切换到linuxde用户;
-$ passwd # linuxde来更改密码;
-Changing password for user linuxde.
-Changing password for linuxde
-(current) UNIX password: # 输入linuxde的当前密码;
-passwd: Authentication token manipulation error # 失败,不能更改密码;
-
-$ passwd -d linuxde # 清除linuxde用户密码;
-Removing password for user linuxde.
-passwd: Success # 清除成功;
-
-$ passwd -S linuxde # 查询linuxde用户密码状态;
-Empty password. # 空密码,也就是没有密码;
-```
-
-### su
-
-> su 命令用于切换当前用户身份到其他用户身份,变更时须输入所要变更的用户帐号与密码。
->
-> 参考:http://man.linuxde.net/su
-
-示例:
-
-```sh
-# 变更帐号为 root 并在执行 ls 指令后退出变回原使用者:
-$ su -c ls root
-
-# 变更帐号为 root 并传入`-f`选项给新执行的 shell:
-$ su root -f
-
-# 变更帐号为 test 并改变工作目录至 test 的家目录:
-$ su -test
-```
-
-### sudo
-
-> sudo 命令用来以其他身份来执行命令,预设的身份为 root。在 `/etc/sudoers` 中设置了可执行 sudo 指令的用户。若其未经授权的用户企图使用 sudo,则会发出警告的邮件给管理员。用户使用 sudo 时,必须先输入密码,之后有 5 分钟的有效期限,超过期限则必须重新输入密码。
->
-> 参考:http://man.linuxde.net/sudo
-
-示例:
-
-```sh
-# 指定用户执行命令
-$ sudo -u userb ls -l
-# 列出目前的权限
-$ sudo -l
-# 显示sudo设置
-$ sudo -L
-```
-
-#### 给普通用户授权 sudo
-
-假设要给普通用户 mary 配置 sudo 权限:
-
-1. `/etc/sudoers` 文件存放了 sudo 的相关用户,但是默认是没有写权限的,所以需要设为可写:`chmod u+w /etc/sudoers`
-2. 在该文件中添加 `mary ALL=(ALL) ALL` ,保存并退出,让 mary 具有 sudo 的所有权限
-3. 再将 `/etc/sudoers` 的权限恢复到默认状态:`chmod u-w /etc/sudoers`
-
-#### 免密码授权 sudo
-
-与给普通用户授权 sudo 类似,区别仅在于第 2 步:`mary ALL=(ALL) NOPASSWD: ALL`。
diff --git "a/os/linux/06.Linux\347\263\273\347\273\237\347\256\241\347\220\206.md" "b/os/linux/06.Linux\347\263\273\347\273\237\347\256\241\347\220\206.md"
deleted file mode 100644
index b1284b4173..0000000000
--- "a/os/linux/06.Linux\347\263\273\347\273\237\347\256\241\347\220\206.md"
+++ /dev/null
@@ -1,395 +0,0 @@
----
-title: Linux 系统管理
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 系统管理
-
-> 关键词:`lsb_release`, `reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab`
-
-
-
-- [Linux 系统管理要点](#linux-系统管理要点)
-- [命令常见用法](#命令常见用法)
- - [lsb_release](#lsb_release)
- - [reboot](#reboot)
- - [exit](#exit)
- - [shutdown](#shutdown)
- - [date](#date)
- - [mount](#mount)
- - [umount](#umount)
- - [ps](#ps)
- - [kill](#kill)
- - [systemctl](#systemctl)
- - [service](#service)
- - [crontab](#crontab)
-
-
-
-## Linux 系统管理要点
-
-- 查看 Linux 系统发行版本
- - 使用 [lsb_release](#lsb_release)(此命令适用于所有的 Linux 发行版本)
- - 使用 `cat /etc/redhat-release`(此方法只适合 Redhat 系的 Linux)
-- 查看 CPU 信息 - 使用 `cat /proc/cpuinfo`
-- 重新启动 Linux 操作系统 - 使用 [reboot](#reboot)
-- 退出 shell,并返回给定值 - 使用 [exit](#exit)
-- 关闭系统 - 使用 [shutdown](#shutdown)
-- 查看或设置系统时间与日期 - 使用 [date](#date)
-- 挂载文件系统 - 使用 [mount](#mount)
-- 取消挂载文件系统 - 使用 [umount](#umount)
-- 查看系统当前进程状态 - 使用 [ps](#ps)
-- 删除当前正在运行的进程 - 使用 [kill](#kill)
-- 启动、停止、重启、关闭、显示系统服务(Centos7),使用 [systemctl](#systemctl)
-- 启动、停止、重启、关闭、显示系统服务(Centos7 以前),使用 [service](#service)
-- 管理需要周期性执行的任务,使用 [crontab](#crontab)
-
-## 命令常见用法
-
-### lsb_release
-
-lsb_release 不是 bash 默认命令,如果要使用,需要先安装。
-
-安装方法:
-
-1. 执行 `yum provides lsb_release`,查看支持 lsb_release 命令的包。
-2. 选择合适版本,执行类似这样的安装命令:`yum install -y redhat-lsb-core-4.1-27.el7.centos.1.x86_64`
-
-### reboot
-
-> reboot 命令用来重新启动正在运行的 Linux 操作系统。
->
-> 参考:http://man.linuxde.net/reboot
-
-示例:
-
-```sh
-reboot # 重开机。
-reboot -w # 做个重开机的模拟(只有纪录并不会真的重开机)。
-```
-
-### exit
-
-> exit 命令同于退出 shell,并返回给定值。在 shell 脚本中可以终止当前脚本执行。执行 exit 可使 shell 以指定的状态值退出。若不设置状态值参数,则 shell 以预设值退出。状态值 0 代表执行成功,其他值代表执行失败。
->
-> 参考:http://man.linuxde.net/exit
-
-示例:
-
-```sh
-# 退出当前 shell
-[root@localhost ~]# exit
-logout
-
-# 在脚本中,进入脚本所在目录,否则退出
-cd $(dirname $0) || exit 1
-
-# 在脚本中,判断参数数量,不匹配就打印使用方式,退出
-if [ "$#" -ne "2" ]; then
- echo "usage: $0 "
- exit 2
-fi
-
-# 在脚本中,退出时删除临时文件
-trap "rm -f tmpfile; echo Bye." EXIT
-
-# 检查上一命令的退出码
-./mycommand.sh
-EXCODE=$?
-if [ "$EXCODE" == "0" ]; then
- echo "O.K"
-fi
-```
-
-### shutdown
-
-> shutdown 命令用来系统关机命令。shutdown 指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动作。
->
-> 参考:http://man.linuxde.net/shutdown
-
-示例:
-
-```sh
-# 指定现在立即关机
-shutdown -h now
-
-# 指定 5 分钟后关机,同时送出警告信息给登入用户
-shutdown +5 "System will shutdown after 5 minutes"
-```
-
-### date
-
-> date 命令是显示或设置系统时间与日期。
->
-> 参考:http://man.linuxde.net/date
-
-示例:
-
-```sh
-# 格式化输出
-date +"%Y-%m-%d"
-2009-12-07
-
-# 输出昨天日期
-date -d "1 day ago" +"%Y-%m-%d"
-2012-11-19
-
-# 2 秒后输出
-date -d "2 second" +"%Y-%m-%d %H:%M.%S"
-2012-11-20 14:21.31
-
-# 传说中的 1234567890 秒
-date -d "1970-01-01 1234567890 seconds" +"%Y-%m-%d %H:%m:%S"
-2009-02-13 23:02:30
-
-# 普通转格式
-date -d "2009-12-12" +"%Y/%m/%d %H:%M.%S"
-2009/12/12 00:00.00
-
-# apache 格式转换
-date -d "Dec 5, 2009 12:00:37 AM" +"%Y-%m-%d %H:%M.%S"
-2009-12-05 00:00.37
-
-# 格式转换后时间游走
-date -d "Dec 5, 2009 12:00:37 AM 2 year ago" +"%Y-%m-%d %H:%M.%S"
-2007-12-05 00:00.37
-
-# 加减操作
-date +%Y%m%d # 显示前天年月日
-date -d "+1 day" +%Y%m%d # 显示前一天的日期
-date -d "-1 day" +%Y%m%d # 显示后一天的日期
-date -d "-1 month" +%Y%m%d # 显示上一月的日期
-date -d "+1 month" +%Y%m%d # 显示下一月的日期
-date -d "-1 year" +%Y%m%d # 显示前一年的日期
-date -d "+1 year" +%Y%m%d # 显示下一年的日期
-
-# 设定时间
-date -s # 设置当前时间,只有root权限才能设置,其他只能查看
-date -s 20120523 # 设置成20120523,这样会把具体时间设置成空00:00:00
-date -s 01:01:01 # 设置具体时间,不会对日期做更改
-date -s "01:01:01 2012-05-23" # 这样可以设置全部时间
-date -s "01:01:01 20120523" # 这样可以设置全部时间
-date -s "2012-05-23 01:01:01" # 这样可以设置全部时间
-date -s "20120523 01:01:01" # 这样可以设置全部时间
-
-# 有时需要检查一组命令花费的时间
-#!/bin/bash
-
-start=$(date +%s)
-nmap man.linuxde.net &> /dev/null
-
-end=$(date +%s)
-difference=$(( end - start ))
-echo $difference seconds.
-```
-
-### mount
-
-> mount 命令用于挂载文件系统到指定的挂载点。此命令的最常用于挂载 cdrom,使我们可以访问 cdrom 中的数据,因为你将光盘插入 cdrom 中,Linux 并不会自动挂载,必须使用 Linux mount 命令来手动完成挂载。
->
-> 参考:http://man.linuxde.net/mount > https://blog.csdn.net/weishujie000/article/details/76531924
-
-示例:
-
-```sh
-# 将 /dev/hda1 挂在 /mnt 之下
-mount /dev/hda1 /mnt
-
-# 将 /dev/hda1 用唯读模式挂在 /mnt 之下
-mount -o ro /dev/hda1 /mnt
-
-# 将 /tmp/image.iso 这个光碟的 image 档使用 loop 模式挂在 /mnt/cdrom 之下
-# 用这种方法可以将一般网络上可以找到的 Linux ISO 在不烧录成光碟的情况下检视其内容
-mount -o loop /tmp/image.iso /mnt/cdrom
-```
-
-### umount
-
-> umount 命令用于卸载已经挂载的文件系统。利用设备名或挂载点都能 umount 文件系统,不过最好还是通过挂载点卸载,以免使用绑定挂载(一个设备,多个挂载点)时产生混乱。
->
-> 参考:http://man.linuxde.net/umount
-
-示例:
-
-```sh
-# 通过设备名卸载
-umount -v /dev/sda1
-/dev/sda1 umounted
-
-# 通过挂载点卸载
-umount -v /mnt/mymount/
-/tmp/diskboot.img umounted
-```
-
-### ps
-
-> ps 命令用于报告当前系统的进程状态。可以搭配 kill 指令随时中断、删除不必要的程序。ps 命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等,总之大部分信息都是可以通过执行该命令得到的。
->
-> 参考:http://man.linuxde.net/ps
-
-示例:
-
-```sh
-# 按内存资源的使用量对进程进行排序
-ps aux | sort -rnk 4
-
-# 按 CPU 资源的使用量对进程进行排序
-ps aux | sort -nk 3
-```
-
-### kill
-
-> kill 命令用来删除执行中的程序或工作。kill 可将指定的信息送至程序。预设的信息为 SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9) 信息尝试强制删除程序。程序或工作的编号可利用 ps 指令或 job 指令查看。
->
-> 参考:http://man.linuxde.net/kill
-
-示例:
-
-```sh
-# 列出所有信号名称
- kill -l
- 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
- 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE
- 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
-13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT
-17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
-21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
-25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
-29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
-35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
-39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
-43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
-47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
-51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
-55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
-59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
-63) SIGRTMAX-1 64) SIGRTMAX
-
-# 先用 ps 查找进程,然后用 kill 杀掉
-ps -ef | grep vim
-root 3268 2884 0 16:21 pts/1 00:00:00 vim install.log
-root 3370 2822 0 16:21 pts/0 00:00:00 grep vim
-
-kill 3268
-kill 3268
--bash: kill: (3268) - 没有那个进程
-```
-
-### systemctl
-
-> systemctl 命令是系统服务管理器指令,它实际上将 service 和 chkconfig 这两个命令组合到一起。
->
-> 参考:http://man.linuxde.net/systemctl
-
-示例:
-
-```sh
-# 1.启动 nfs 服务
-systemctl start nfs-server.service
-
-# 2.设置开机自启动
-systemctl enable nfs-server.service
-
-# 3.停止开机自启动
-systemctl disable nfs-server.service
-
-# 4.查看服务当前状态
-systemctl status nfs-server.service
-
-# 5.重新启动某服务
-systemctl restart nfs-server.service
-
-# 6.查看所有已启动的服务
-systemctl list -units --type=service
-
-# 7. 开启防火墙 22 端口
-iptables -I INPUT -p tcp --dport 22 -j accept
-
-# 8. 彻底关闭防火墙
-sudo systemctl status firewalld.service
-sudo systemctl stop firewalld.service
-sudo systemctl disable firewalld.service
-```
-
-### service
-
-> service 命令是 Redhat Linux 兼容的发行版中用来控制系统服务的实用工具,它以启动、停止、重新启动和关闭系统服务,还可以显示所有系统服务的当前状态。
->
-> 参考:http://man.linuxde.net/service
-
-示例:
-
-```sh
-service network status
-配置设备:
-lo eth0
-当前的活跃设备:
-lo eth0
-
-service network restart
-正在关闭接口 eth0: [ 确定 ]
-关闭环回接口: [ 确定 ]
-设置网络参数: [ 确定 ]
-弹出环回接口: [ 确定 ]
-弹出界面 eth0: [ 确定 ]
-```
-
-### crontab
-
-> crontab 命令被用来提交和管理用户的需要周期性执行的任务,与 windows 下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动 crond 进程,crond 进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。
->
-> 参考:http://man.linuxde.net/crontab
-
-示例:
-
-```sh
-# 每 1 分钟执行一次 command
-* * * * * command
-
-# 每小时的第 3 和第 15 分钟执行
-3,15 * * * * command
-
-# 在上午 8 点到 11 点的第 3 和第 15 分钟执行
-3,15 8-11 * * * command
-
-# 每隔两天的上午 8 点到 11 点的第 3 和第 15 分钟执行
-3,15 8-11 */2 * * command
-
-# 每个星期一的上午 8 点到 11 点的第 3 和第 15 分钟执行
-3,15 8-11 * * 1 command
-
-# 每晚的 21:30 重启 smb
-30 21 * * * /etc/init.d/smb restart
-
-# 每月 1、10、22 日的 4 : 45 重启 smb
-45 4 1,10,22 * * /etc/init.d/smb restart
-
-# 每周六、周日的 1:10 重启 smb
-10 1 * * 6,0 /etc/init.d/smb restart
-
-# 每天 18 : 00 至 23 : 00 之间每隔 30 分钟重启 smb
-0,30 18-23 * * * /etc/init.d/smb restart
-
-# 每星期六的晚上 11:00 pm 重启 smb
-0 23 * * 6 /etc/init.d/smb restart
-
-# 每一小时重启 smb
-* */1 * * * /etc/init.d/smb restart
-
-# 晚上 11 点到早上 7 点之间,每隔一小时重启 smb
-* 23-7/1 * * * /etc/init.d/smb restart
-
-# 每月的 4 号与每周一到周三的 11 点重启 smb
-0 11 4 * mon-wed /etc/init.d/smb restart
-
-# 一月一号的 4 点重启 smb
-0 4 1 jan * /etc/init.d/smb restart
-
-# 每小时执行`/etc/cron.hourly`目录内的脚本
-01 * * * * root run-parts /etc/cron.hourly
-```
diff --git "a/os/linux/07.Linux\347\275\221\347\273\234\347\256\241\347\220\206.md" "b/os/linux/07.Linux\347\275\221\347\273\234\347\256\241\347\220\206.md"
deleted file mode 100644
index fd8f58b65d..0000000000
--- "a/os/linux/07.Linux\347\275\221\347\273\234\347\256\241\347\220\206.md"
+++ /dev/null
@@ -1,460 +0,0 @@
----
-title: Linux 网络管理
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 网络管理
-
-> 关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat`
-
-
-
-- [Linux 网络应用要点](#linux-网络应用要点)
-- [命令常见用法](#命令常见用法)
- - [curl](#curl)
- - [wget](#wget)
- - [telnet](#telnet)
- - [ip](#ip)
- - [hostname](#hostname)
- - [ifconfig](#ifconfig)
- - [route](#route)
- - [ssh](#ssh)
- - [ssh-keygen](#ssh-keygen)
- - [firewalld](#firewalld)
- - [iptables](#iptables)
- - [host](#host)
- - [nslookup](#nslookup)
- - [nc/netcat](#ncnetcat)
- - [ping](#ping)
- - [traceroute](#traceroute)
- - [netstat](#netstat)
-
-
-
-## Linux 网络应用要点
-
-- 下载文件 - 使用 [curl](#curl)、[wget](#wget)
-- telnet 方式登录远程主机,对远程主机进行管理 - 使用 [telnet](#telnet)
-- 查看或操纵 Linux 主机的路由、网络设备、策略路由和隧道 - 使用 [ip](#ip)
-- 查看和设置系统的主机名 - 使用 [hostname](#hostname)
-- 查看和配置 Linux 内核中网络接口的网络参数 - 使用 [ifconfig](#ifconfig)
-- 查看和设置 Linux 内核中的网络路由表 - 使用 [route](#route)
-- ssh 方式连接远程主机 - 使用 ssh
-- 为 ssh 生成、管理和转换认证密钥 - 使用 [ssh-keygen](#ssh-keygen)
-- 查看、设置防火墙(Centos7),使用 [firewalld](#firewalld)
-- 查看、设置防火墙(Centos7 以前),使用 [iptables](#iptables)
-- 查看域名信息 - 使用 [host](#host), [nslookup](#nslookup)
-- 设置路由 - 使用 [nc/netcat](#ncnetcat)
-- 测试主机之间网络是否连通 - 使用 [ping](#ping)
-- 追踪数据在网络上的传输时的全部路径 - 使用 [traceroute](#traceroute)
-- 查看当前工作的端口信息 - 使用 [netstat](#netstat)
-
-## 命令常见用法
-
-### curl
-
-> curl 命令是一个利用 URL 规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称 curl 为下载工具。作为一款强力工具,curl 支持包括 HTTP、HTTPS、ftp 等众多协议,还支持 POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl 可以祝一臂之力。
->
-> 参考:http://man.linuxde.net/curl
-
-示例:
-
-```sh
-# 下载文件
-$ curl http://man.linuxde.net/text.iso --silent
-
-# 下载文件,指定下载路径,并查看进度
-$ curl http://man.linuxde.net/test.iso -o filename.iso --progress
-########################################## 100.0%
-```
-
-### wget
-
-> wget 命令用来从指定的 URL 下载文件。
->
-> 参考:http://man.linuxde.net/wget
-
-示例:
-
-```sh
-# 使用 wget 下载单个文件
-$ wget http://www.linuxde.net/testfile.zip
-```
-
-### telnet
-
-> telnet 命令用于登录远程主机,对远程主机进行管理。
->
-> 参考:http://man.linuxde.net/telnet
-
-示例:
-
-```sh
-telnet 192.168.2.10
-Trying 192.168.2.10...
-Connected to 192.168.2.10 (192.168.2.10).
-Escape character is '^]'.
-
- localhost (Linux release 2.6.18-274.18.1.el5 #1 SMP Thu Feb 9 12:45:44 EST 2012) (1)
-
-login: root
-Password:
-Login incorrect
-```
-
-### ip
-
-> ip 命令用来查看或操纵 Linux 主机的路由、网络设备、策略路由和隧道,是 Linux 下较新的功能强大的网络配置工具。
->
-> 参考:http://man.linuxde.net/ip
-
-示例:
-
-```sh
-$ ip link show # 查看网络接口信息
-$ ip link set eth0 upi # 开启网卡
-$ ip link set eth0 down # 关闭网卡
-$ ip link set eth0 promisc on # 开启网卡的混合模式
-$ ip link set eth0 promisc offi # 关闭网卡的混个模式
-$ ip link set eth0 txqueuelen 1200 # 设置网卡队列长度
-$ ip link set eth0 mtu 1400 # 设置网卡最大传输单元
-$ ip addr show # 查看网卡IP信息
-$ ip addr add 192.168.0.1/24 dev eth0 # 设置eth0网卡IP地址192.168.0.1
-$ ip addr del 192.168.0.1/24 dev eth0 # 删除eth0网卡IP地址
-
-$ ip route show # 查看系统路由
-$ ip route add default via 192.168.1.254 # 设置系统默认路由
-$ ip route list # 查看路由信息
-$ ip route add 192.168.4.0/24 via 192.168.0.254 dev eth0 # 设置192.168.4.0网段的网关为192.168.0.254,数据走eth0接口
-$ ip route add default via 192.168.0.254 dev eth0 # 设置默认网关为192.168.0.254
-$ ip route del 192.168.4.0/24 # 删除192.168.4.0网段的网关
-$ ip route del default # 删除默认路由
-$ ip route delete 192.168.1.0/24 dev eth0 # 删除路由
-```
-
-### hostname
-
-> hostname 命令用于查看和设置系统的主机名称。环境变量 HOSTNAME 也保存了当前的主机名。在使用 hostname 命令设置主机名后,系统并不会永久保存新的主机名,重新启动机器之后还是原来的主机名。如果需要永久修改主机名,需要同时修改 `/etc/hosts` 和 `/etc/sysconfig/network` 的相关内容。
->
-> 参考:http://man.linuxde.net/hostname
-
-示例:
-
-```sh
-$ hostname
-AY1307311912260196fcZ
-```
-
-### ifconfig
-
-> ifconfig 命令被用于查看和配置 Linux 内核中网络接口的网络参数。用 ifconfig 命令配置的网卡信息,在网卡重启后机器重启后,配置就不存在。要想将上述的配置信息永远的存的电脑里,那就要修改网卡的配置文件了。
->
-> 参考:http://man.linuxde.net/ifconfig
-
-示例:
-
-```sh
-# 查看网络设备信息(激活状态的)
-[root@localhost ~]# ifconfig
-eth0 Link encap:Ethernet HWaddr 00:16:3E:00:1E:51
- inet addr:10.160.7.81 Bcast:10.160.15.255 Mask:255.255.240.0
- UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
- RX packets:61430830 errors:0 dropped:0 overruns:0 frame:0
- TX packets:88534 errors:0 dropped:0 overruns:0 carrier:0
- collisions:0 txqueuelen:1000
- RX bytes:3607197869 (3.3 GiB) TX bytes:6115042 (5.8 MiB)
-
-lo Link encap:Local Loopback
- inet addr:127.0.0.1 Mask:255.0.0.0
- UP LOOPBACK RUNNING MTU:16436 Metric:1
- RX packets:56103 errors:0 dropped:0 overruns:0 frame:0
- TX packets:56103 errors:0 dropped:0 overruns:0 carrier:0
- collisions:0 txqueuelen:0
- RX bytes:5079451 (4.8 MiB) TX bytes:5079451 (4.8 MiB)
-```
-
-### route
-
-> route 命令用来查看和设置 Linux 内核中的网络路由表,route 命令设置的路由主要是静态路由。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。
->
-> 参考:http://man.linuxde.net/route
-
-示例:
-
-```sh
-# 查看当前路由
-route
-Kernel IP routing table
-Destination Gateway Genmask Flags Metric Ref Use Iface
-112.124.12.0 * 255.255.252.0 U 0 0 0 eth1
-10.160.0.0 * 255.255.240.0 U 0 0 0 eth0
-192.168.0.0 10.160.15.247 255.255.0.0 UG 0 0 0 eth0
-172.16.0.0 10.160.15.247 255.240.0.0 UG 0 0 0 eth0
-10.0.0.0 10.160.15.247 255.0.0.0 UG 0 0 0 eth0
-default 112.124.15.247 0.0.0.0 UG 0 0 0 eth1
-
-route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 # 添加网关/设置网关
-route add -net 224.0.0.0 netmask 240.0.0.0 reject # 屏蔽一条路由
-route del -net 224.0.0.0 netmask 240.0.0.0 # 删除路由记录
-route add default gw 192.168.120.240 # 添加默认网关
-route del default gw 192.168.120.240 # 删除默认网关
-```
-
-### ssh
-
-> ssh 命令是 openssh 套件中的客户端连接工具,可以给予 ssh 加密协议实现安全的远程登录服务器。
->
-> 参考:http://man.linuxde.net/ssh
-
-示例:
-
-```bash
-# ssh 用户名@远程服务器地址
-ssh user1@172.24.210.101
-# 指定端口
-ssh -p 2211 root@140.206.185.170
-```
-
-引申阅读:[ssh 背后的故事](https://linux.cn/article-8476-1.html)
-
-### ssh-keygen
-
-> ssh-keygen 命令用于为 ssh 生成、管理和转换认证密钥,它支持 RSA 和 DSA 两种认证密钥。
->
-> 参考:http://man.linuxde.net/ssh-keygen
-
-### firewalld
-
-> firewalld 命令是 Linux 上的防火墙软件(Centos7 默认防火墙)。
->
-> 参考:https://www.cnblogs.com/moxiaoan/p/5683743.html
-
-#### firewalld 的基本使用
-
-- 启动 - systemctl start firewalld
-- 关闭 - systemctl stop firewalld
-- 查看状态 - systemctl status firewalld
-- 开机禁用 - systemctl disable firewalld
-- 开机启用 - systemctl enable firewalld
-
-#### 使用 systemctl 管理 firewalld 服务
-
-systemctl 是 CentOS7 的服务管理工具中主要的工具,它融合之前 service 和 chkconfig 的功能于一体。
-
-- 启动一个服务 - systemctl start firewalld.service
-- 关闭一个服务 - systemctl stop firewalld.service
-- 重启一个服务 - systemctl restart firewalld.service
-- 显示一个服务的状态 - systemctl status firewalld.service
-- 在开机时启用一个服务 - systemctl enable firewalld.service
-- 在开机时禁用一个服务 - systemctl disable firewalld.service
-- 查看服务是否开机启动 - systemctl is-enabled firewalld.service
-- 查看已启动的服务列表 - systemctl list-unit-files|grep enabled
-- 查看启动失败的服务列表 - systemctl --failed
-
-#### 配置 firewalld-cmd
-
-- 查看版本 - firewall-cmd --version
-- 查看帮助 - firewall-cmd --help
-- 显示状态 - firewall-cmd --state
-- 查看所有打开的端口 - firewall-cmd --zone=public --list-ports
-- 更新防火墙规则 - firewall-cmd --reload
-- 查看区域信息: firewall-cmd --get-active-zones
-- 查看指定接口所属区域 - firewall-cmd --get-zone-of-interface=eth0
-- 拒绝所有包:firewall-cmd --panic-on
-- 取消拒绝状态 - firewall-cmd --panic-off
-- 查看是否拒绝 - firewall-cmd --query-panic
-
-#### 在防火墙中开放一个端口
-
-- 添加(--permanent 永久生效,没有此参数重启后失效) - firewall-cmd --zone=public --add-port=80/tcp --permanent
-- 重新载入 - firewall-cmd --reload
-- 查看 - firewall-cmd --zone= public --query-port=80/tcp
-- 删除 - firewall-cmd --zone= public --remove-port=80/tcp --permanent
-
-### iptables
-
-> iptables 命令是 Linux 上常用的防火墙软件,是 netfilter 项目的一部分。可以直接配置,也可以通过许多前端和图形界面配置。
->
-> 参考:http://man.linuxde.net/iptables
-
-示例:
-
-```sh
-# 开放指定的端口
-iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT #允许本地回环接口(即运行本机访问本机)
-iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #允许已建立的或相关连的通行
-iptables -A OUTPUT -j ACCEPT #允许所有本机向外的访问
-iptables -A INPUT -p tcp --dport 22 -j ACCEPT #允许访问22端口
-iptables -A INPUT -p tcp --dport 80 -j ACCEPT #允许访问80端口
-iptables -A INPUT -p tcp --dport 21 -j ACCEPT #允许ftp服务的21端口
-iptables -A INPUT -p tcp --dport 20 -j ACCEPT #允许FTP服务的20端口
-iptables -A INPUT -j reject #禁止其他未允许的规则访问
-iptables -A FORWARD -j REJECT #禁止其他未允许的规则访问
-
-# 屏蔽IP
-iptables -I INPUT -s 123.45.6.7 -j DROP #屏蔽单个IP的命令
-iptables -I INPUT -s 123.0.0.0/8 -j DROP #封整个段即从123.0.0.1到123.255.255.254的命令
-iptables -I INPUT -s 124.45.0.0/16 -j DROP #封IP段即从123.45.0.1到123.45.255.254的命令
-iptables -I INPUT -s 123.45.6.0/24 -j DROP #封IP段即从123.45.6.1到123.45.6.254的命令是
-
-# 查看已添加的iptables规则
-iptables -L -n -v
-Chain INPUT (policy DROP 48106 packets, 2690K bytes)
- pkts bytes target prot opt in out source destination
- 5075 589K ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
- 191K 90M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
-1499K 133M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
-4364K 6351M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
- 6256 327K ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
-
-Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
- pkts bytes target prot opt in out source destination
-
-Chain OUTPUT (policy ACCEPT 3382K packets, 1819M bytes)
- pkts bytes target prot opt in out source destination
- 5075 589K ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0
-```
-
-### host
-
-> host 命令是常用的分析域名查询工具,可以用来测试域名系统工作是否正常。
->
-> 参考:http://man.linuxde.net/host
-
-示例:
-
-```sh
-[root@localhost ~]# host www.jsdig.com
-www.jsdig.com is an alias for host.1.jsdig.com.
-host.1.jsdig.com has address 100.42.212.8
-
-[root@localhost ~]# host -a www.jsdig.com
-Trying "www.jsdig.com"
-;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34671
-;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
-
-;; QUESTION SECTION:
-;www.jsdig.com. IN ANY
-
-;; ANSWER SECTION:
-www.jsdig.com. 463 IN CNAME host.1.jsdig.com.
-
-Received 54 bytes from 202.96.104.15#53 in 0 ms
-```
-
-### nslookup
-
-> nslookup 命令是常用域名查询工具,就是查 DNS 信息用的命令。
->
-> 参考:http://man.linuxde.net/nslookup
-
-示例:
-
-```sh
-[root@localhost ~]# nslookup www.jsdig.com
-Server: 202.96.104.15
-Address: 202.96.104.15#53
-
-Non-authoritative answer:
-www.jsdig.com canonical name = host.1.jsdig.com.
-Name: host.1.jsdig.com
-Address: 100.42.212.8
-```
-
-### nc/netcat
-
-> nc 命令是 netcat 命令的简称,都是用来设置路由器。
->
-> 参考:http://man.linuxde.net/nc_netcat
-
-示例:
-
-```sh
-# TCP 端口扫描
-[root@localhost ~]# nc -v -z -w2 192.168.0.3 1-100
-192.168.0.3: inverse host lookup failed: Unknown host
-(UNKNOWN) [192.168.0.3] 80 (http) open
-(UNKNOWN) [192.168.0.3] 23 (telnet) open
-(UNKNOWN) [192.168.0.3] 22 (ssh) open
-
-# UDP 端口扫描
-[root@localhost ~]# nc -u -z -w2 192.168.0.1 1-1000 # 扫描192.168.0.3 的端口 范围是 1-1000
-```
-
-### ping
-
-> ping 命令用来测试主机之间网络的连通性。执行 ping 指令会使用 ICMP 传输协议,发出要求回应的信息,若远端主机的网络功能没有问题,就会回应该信息,因而得知该主机运作正常。
->
-> 参考:http://man.linuxde.net/ping
-
-示例:
-
-```sh
-[root@AY1307311912260196fcZ ~]# ping www.jsdig.com
-PING host.1.jsdig.com (100.42.212.8) 56(84) bytes of data.
-64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=1 ttl=50 time=177 ms
-64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=2 ttl=50 time=178 ms
-64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=3 ttl=50 time=174 ms
-64 bytes from 100-42-212-8.static.webnx.com (100.42.212.8): icmp_seq=4 ttl=50 time=177 ms
-...按Ctrl+C结束
-
---- host.1.jsdig.com ping statistics ---
-4 packets transmitted, 4 received, 0% packet loss, time 2998ms
-rtt min/avg/max/mdev = 174.068/176.916/178.182/1.683 ms
-```
-
-### traceroute
-
-> traceroute 命令用于追踪数据包在网络上的传输时的全部路径,它默认发送的数据包大小是 40 字节。
->
-> 参考:http://man.linuxde.net/traceroute
-
-示例:
-
-```sh
-traceroute www.58.com
-traceroute to www.58.com (211.151.111.30), 30 hops max, 40 byte packets
- 1 unknown (192.168.2.1) 3.453 ms 3.801 ms 3.937 ms
- 2 221.6.45.33 (221.6.45.33) 7.768 ms 7.816 ms 7.840 ms
- 3 221.6.0.233 (221.6.0.233) 13.784 ms 13.827 ms 221.6.9.81 (221.6.9.81) 9.758 ms
- 4 221.6.2.169 (221.6.2.169) 11.777 ms 122.96.66.13 (122.96.66.13) 34.952 ms 221.6.2.53 (221.6.2.53) 41.372 ms
- 5 219.158.96.149 (219.158.96.149) 39.167 ms 39.210 ms 39.238 ms
- 6 123.126.0.194 (123.126.0.194) 37.270 ms 123.126.0.66 (123.126.0.66) 37.163 ms 37.441 ms
- 7 124.65.57.26 (124.65.57.26) 42.787 ms 42.799 ms 42.809 ms
- 8 61.148.146.210 (61.148.146.210) 30.176 ms 61.148.154.98 (61.148.154.98) 32.613 ms 32.675 ms
- 9 202.106.42.102 (202.106.42.102) 44.563 ms 44.600 ms 44.627 ms
-10 210.77.139.150 (210.77.139.150) 53.302 ms 53.233 ms 53.032 ms
-11 211.151.104.6 (211.151.104.6) 39.585 ms 39.502 ms 39.598 ms
-12 211.151.111.30 (211.151.111.30) 35.161 ms 35.938 ms 36.005 ms
-```
-
-### netstat
-
-> netstat 命令用来打印 Linux 中网络系统的状态信息,可让你得知整个 Linux 系统的网络情况。
->
-> 参考:http://man.linuxde.net/netstat
-
-示例:
-
-```sh
-# 列出所有端口 (包括监听和未监听的)
-netstat -a #列出所有端口
-netstat -at #列出所有tcp端口
-netstat -au #列出所有udp端口
-
-# 列出所有处于监听状态的 Sockets
-netstat -l #只显示监听端口
-netstat -lt #只列出所有监听 tcp 端口
-netstat -lu #只列出所有监听 udp 端口
-netstat -lx #只列出所有监听 UNIX 端口
-
-# 显示每个协议的统计信息
-netstat -s 显示所有端口的统计信息
-netstat -st 显示TCP端口的统计信息
-netstat -su 显示UDP端口的统计信息
-```
diff --git "a/os/linux/08.Linux\347\241\254\344\273\266\347\256\241\347\220\206.md" "b/os/linux/08.Linux\347\241\254\344\273\266\347\256\241\347\220\206.md"
deleted file mode 100644
index 5e55376c10..0000000000
--- "a/os/linux/08.Linux\347\241\254\344\273\266\347\256\241\347\220\206.md"
+++ /dev/null
@@ -1,177 +0,0 @@
----
-title: Linux 硬件管理
-date: 2018/02/27
-categories:
- - linux
-tags:
- - linux
- - command
----
-
-# Linux 硬件管理
-
-> 关键词:`df`, `du`, `top`, `free`, `iotop`
-
-
-
-- [Linux 硬件管理要点](#linux-硬件管理要点)
-- [命令常见用法](#命令常见用法)
- - [df](#df)
- - [du](#du)
- - [top](#top)
- - [free](#free)
- - [iotop](#iotop)
-
-
-
-## Linux 硬件管理要点
-
-- 查看磁盘空间 - 使用 [df](#df)
-- 查看文件或目录的磁盘空间 - 使用 [du](#du)
-- 实时查看系统整体运行状态(如:CPU、内存) - 使用 [top](#top)
-- 查看已使用和未使用的内存 - 使用 [free](#free)
-- 查看磁盘 I/O 使用状况 - 使用 [iotop](#iotop)
-
-## 命令常见用法
-
-### df
-
-> df 命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为 KB。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。
->
-> 参考:http://man.linuxde.net/df
-
-示例:
-
-```sh
-# 查看系统磁盘设备,默认是 KB 为单位
-[root@LinServ-1 ~]# df
-文件系统 1K-块 已用 可用 已用% 挂载点
-/dev/sda2 146294492 28244432 110498708 21% /
-/dev/sda1 1019208 62360 904240 7% /boot
-tmpfs 1032204 0 1032204 0% /dev/shm
-/dev/sdb1 2884284108 218826068 2518944764 8% /data1
-
-# 使用 -h 选项以 KB 以上的单位来显示,可读性高
-[root@LinServ-1 ~]# df -h
-文件系统 容量 已用 可用 已用% 挂载点
-/dev/sda2 140G 27G 106G 21% /
-/dev/sda1 996M 61M 884M 7% /boot
-tmpfs 1009M 0 1009M 0% /dev/shm
-/dev/sdb1 2.7T 209G 2.4T 8% /data1
-
-# 查看全部文件系统
-[root@LinServ-1 ~]# df -a
-文件系统 1K-块 已用 可用 已用% 挂载点
-/dev/sda2 146294492 28244432 110498708 21% /
-proc 0 0 0 - /proc
-sysfs 0 0 0 - /sys
-devpts 0 0 0 - /dev/pts
-/dev/sda1 1019208 62360 904240 7% /boot
-tmpfs 1032204 0 1032204 0% /dev/shm
-/dev/sdb1 2884284108 218826068 2518944764 8% /data1
-none 0 0 0 - /proc/sys/fs/binfmt_misc
-```
-
-### du
-
-> du 命令也是查看使用空间的,但是与 df 命令不同的是:du 命令是对文件和目录磁盘使用的空间的查看,还是和 df 命令有一些区别的。
->
-> 参考:http://man.linuxde.net/du
-
-示例:
-
-```sh
-# 显示目录或者文件所占空间
-root@localhost [test]# du
-608 ./test6
-308 ./test4
-4 ./scf/lib
-4 ./scf/service/deploy/product
-4 ./scf/service/deploy/info
-12 ./scf/service/deploy
-16 ./scf/service
-4 ./scf/doc
-4 ./scf/bin
-32 ./scf
-8 ./test3
-1288 .
-
-# 显示指定文件所占空间
-[root@localhost test]# du log2012.log
-300 log2012.log
-
-# 查看指定目录的所占空间
-[root@localhost test]# du scf
-4 scf/lib
-4 scf/service/deploy/product
-4 scf/service/deploy/info
-12 scf/service/deploy
-16 scf/service
-4 scf/doc
-4 scf/bin
-32 scf
-
-# 显示多个文件所占空间
-[root@localhost test]# du log30.tar.gz log31.tar.gz
-4 log30.tar.gz
-4 log31.tar.gz
-
-# 只显示总和的大小
-[root@localhost test]# du -s
-1288 .
-
-[root@localhost test]# du -s scf
-32 scf
-```
-
-### top
-
-> top 命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过 top 命令所提供的互动式界面,用热键可以管理。
->
-> 参考:http://man.linuxde.net/top
-
-### free
-
-> free 命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。
->
-> 参考:http://man.linuxde.net/free
-
-示例:
-
-```sh
-free -t # 以总和的形式显示内存的使用信息
-free -s 10 # 周期性的查询内存使用信息,每10s 执行一次命令
-
-# 显示内存使用情况
-
-free -m
- total used free shared buffers cached
-Mem: 2016 1973 42 0 163 1497
--/+ buffers/cache: 312 1703
-Swap: 4094 0 4094
-```
-
-### iotop
-
-> iotop 命令是一个用来监视磁盘 I/O 使用状况的 top 类工具。iotop 具有与 top 相似的 UI,其中包括 PID、用户、I/O、进程等相关信息。Linux 下的 IO 统计工具如 iostat,nmon 等大多数是只能统计到 per 设备的读写情况,如果你想知道每个进程是如何使用 IO 的就比较麻烦,使用 iotop 命令可以很方便的查看。
->
-> 参考:http://man.linuxde.net/iotop
-
-示例:
-
-```sh
-Total DISK read: 0.00 B/s | Total DISK write: 0.00 B/s
- TID PRIO USER DISK READ DISK WRITE SWAPIN IO> command
- 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init [3]
- 2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd]
- 3 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/0]
- 4 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0]
- 5 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/0]
- 6 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/1]
- 7 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/1]
- 8 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/1]
- 9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/0]
- 10 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [events/1]
- 11 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [khelper]
-2572 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [bluetooth]
-```
diff --git "a/os/linux/09.Linux\350\275\257\344\273\266\347\256\241\347\220\206.md" "b/os/linux/09.Linux\350\275\257\344\273\266\347\256\241\347\220\206.md"
deleted file mode 100644
index 7132590551..0000000000
--- "a/os/linux/09.Linux\350\275\257\344\273\266\347\256\241\347\220\206.md"
+++ /dev/null
@@ -1,210 +0,0 @@
----
-title: Linux 软件管理
-date: 2018/02/26
-categories:
- - linux
-tags:
- - linux
----
-
-# Linux 软件管理
-
-> 关键词:`rpm`, `yum`, `apt-get`
-
-
-
-- [rpm](#rpm)
-- [yum](#yum)
- - [yum 源](#yum-源)
-- [apt-get](#apt-get)
-- [参考资料](#参考资料)
-
-
-
-## rpm
-
-> rpm 命令是 RPM 软件包的管理工具。rpm 原本是 Red Hat Linux 发行版专门用来管理 Linux 各项套件的程序,由于它遵循 GPL 规则且功能强大方便,因而广受欢迎。逐渐受到其他发行版的采用。RPM 套件管理方式的出现,让 Linux 易于安装,升级,间接提升了 Linux 的适用度。
->
-> 参考:http://man.linuxde.net/rpm
-
-示例:
-
-(1)安装 rpm 包
-
-```
-rpm -ivh xxx.rpm
-```
-
-(2)安装.src.rpm 软件包
-
-这类软件包是包含了源代码的 rpm 包,在安装时需要进行编译
-
-```sh
-rpm -i xxx.src.rpm
-cd /usr/src/redhat/SPECS
-rpmbuild -bp xxx.specs #一个和你的软件包同名的specs文件
-cd /usr/src/redhat/BUILD/xxx/ #一个和你的软件包同名的目录
-./configure #这一步和编译普通的源码软件一样,可以加上参数
-make
-make install
-```
-
-(3)卸载 rpm 软件包
-
-使用命令 `rpm -e 包名`,包名可以包含版本号等信息,但是不可以有后缀.rpm,比如卸载软件包 proftpd-1.2.8-1,可以使用下列格式:
-
-```sh
-rpm -e proftpd-1.2.8-1
-rpm -e proftpd-1.2.8
-rpm -e proftpd-
-rpm -e proftpd
-```
-
-不可以是下列格式:
-
-```sh
-rpm -e proftpd-1.2.8-1.i386.rpm
-rpm -e proftpd-1.2.8-1.i386
-rpm -e proftpd-1.2
-rpm -e proftpd-1
-```
-
-有时会出现一些错误或者警告:
-
-```
-... is needed by ...
-```
-
-这说明这个软件被其他软件需要,不能随便卸载,可以用 rpm -e --nodeps 强制卸载
-
-(4)查看与 rpm 包相关的文件和其他信息
-
-```sh
-rpm -qa # 列出所有安装过的包
-```
-
-## yum
-
-> yum 命令是在 Fedora 和 RedHat 以及 SUSE 中基于 rpm 的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理 RPM 软件包,能够从指定的服务器自动下载 RPM 包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。
->
-> 参考:http://man.linuxde.net/yum
-
-示例:
-
-部分常用的命令包括:
-
-- 自动搜索最快镜像插件:`yum install yum-fastestmirror`
-- 安装 yum 图形窗口插件:`yum install yumex`
-- 查看可能批量安装的列表:`yum grouplist`
-
-**安装**
-
-```
-yum install #全部安装
-yum install package1 #安装指定的安装包package1
-yum groupinsall group1 #安装程序组group1
-```
-
-**更新和升级**
-
-```
-yum update #全部更新
-yum update package1 #更新指定程序包package1
-yum check-update #检查可更新的程序
-yum upgrade package1 #升级指定程序包package1
-yum groupupdate group1 #升级程序组group1
-```
-
-**查找和显示**
-
-```
-yum info package1 #显示安装包信息package1
-yum list #显示所有已经安装和可以安装的程序包
-yum list package1 #显示指定程序包安装情况package1
-yum groupinfo group1 #显示程序组group1信息yum search string 根据关键字string查找安装包
-yum search #查找软件包
-```
-
-**删除程序**
-
-```
-yum remove #删除程序包package_name
-yum groupremove group1 #删除程序组group1
-yum deplist package1 #查看程序package1依赖情况
-```
-
-**清除缓存**
-
-```
-yum clean packages #清除缓存目录下的软件包
-yum clean headers #清除缓存目录下的 headers
-yum clean oldheaders #清除缓存目录下旧的 headers
-```
-
-### yum 源
-
-yum 的默认源是国外的,下载速度比较慢,所以最好替换为一个国内的 yum 源。
-
-| 推荐 yum 国内源 | 源地址 |
-| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
-| | Centos6:http://mirrors.aliyun.com/repo/Centos-6.repo Centos7:http://mirrors.aliyun.com/repo/Centos-7.repo |
-| | Centos6:http://mirrors.163.com/.help/CentOS6-Base-163.repo Centos7:http://mirrors.163.com/.help/CentOS7-Base-163.repo |
-
-> 注意:Cento5 已废弃,只能使用 http://vault.centos.org/ 替换,但由于是国外镜像,速度较慢。
-
-替换方法,以 aliyun CentOS7 为例:
-
-```
-cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
-wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
-yum clean all
-yum makecache
-```
-
-## apt-get
-
-> apt-get 命令是 Debian Linux 发行版中的 APT 软件包管理工具。所有基于 Debian 的发行都使用这个包管理系统。deb 包可以把一个应用的文件包在一起,大体就如同 Windows 上的安装文件。
->
-> 参考:http://man.linuxde.net/apt-get
-
-示例:
-
-使用 apt-get 命令的第一步就是引入必需的软件库,Debian 的软件库也就是所有 Debian 软件包的集合,它们存在互联网上的一些公共站点上。把它们的地址加入,apt-get 就能搜索到我们想要的软件。/etc/apt/sources.list 是存放这些地址列表的配置文件,其格式如下:
-
-deb [web 或 ftp 地址][发行版名字] [main/contrib/non-free]
-我们常用的 Ubuntu 就是一个基于 Debian 的发行,我们使用 apt-get 命令获取这个列表,以下是我整理的常用命令:
-
-在修改 /etc/apt/sources.list 或者 /etc/apt/preferences 之后运行该命令。
-
-```sh
-# 更新 apt-get
-apt-get update
-
-# 安装一个软件包
-apt-get install packagename
-
-# 卸载一个已安装的软件包(保留配置文件)
-apt-get remove packagename
-
-# 卸载一个已安装的软件包(删除配置文件)
-apt-get –purge remove packagename
-
-# 如果需要空间的话,可以让这个命令来删除你已经删掉的软件
-apt-get autoclean apt
-
-# 把安装的软件的备份也删除,不过这样不会影响软件的使用的
-apt-get clean
-
-# 更新所有已安装的软件包
-apt-get upgrade
-
-# 将系统升级到新版本
-apt-get dist-upgrade
-```
-
-## 参考资料
-
-- http://man.linuxde.net/rpm
-- http://man.linuxde.net/yum
-- http://man.linuxde.net/apt-get
-- http://www.runoob.com/linux/linux-yum.html
diff --git a/os/linux/README.md b/os/linux/README.md
deleted file mode 100644
index 283ebe499a..0000000000
--- a/os/linux/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Linux
-
-## 内容
-
-- [查看 Linux 命令帮助信息](01.查看Linux命令帮助信息.md) - 关键词:`help`, `whatis`, `info`, `which`, `whereis`, `man`
-- [Linux 文件目录管理](02.Linux文件目录管理.md) - 关键词:`cd`, `ls`, `pwd`, `mkdir`, `rmdir`, `tree`, `touch`, `ln`, `rename`, `stat`, `file`, `chmod`, `chown`, `locate`, `find`, `cp`, `mv`, `rm`
-- [Linux 文件内容查看命令](03.Linux文件内容查看编辑.md) - 关键词:`cat`, `head`, `tail`, `more`, `less`, `sed`, `vi`, `grep`
-- [Linux 文件压缩和解压](04.Linux文件压缩和解压.md) - 关键词:`tar`, `gzip`, `zip`, `unzip`
-- [Linux 用户管理](05.Linux用户管理.md) - 关键词:`groupadd`, `groupdel`, `groupmod`, `useradd`, `userdel`, `usermod`, `passwd`, `su`, `sudo`
-- [Linux 系统管理](06.Linux系统管理.md) - 关键词:`reboot`, `exit`, `shutdown`, `date`, `mount`, `umount`, `ps`, `kill`, `systemctl`, `service`, `crontab`
-- [Linux 网络管理](07.Linux网络管理.md) - 关键词:关键词:`curl`, `wget`, `telnet`, `ip`, `hostname`, `ifconfig`, `route`, `ssh`, `ssh-keygen`, `firewalld`, `iptables`, `host`, `nslookup`, `nc`/`netcat`, `ping`, `traceroute`, `netstat`
-- [Linux 硬件管理](08.Linux硬件管理.md) - 关键词:`df`, `du`, `top`, `free`, `iotop`
-- [Linux 软件管理](09.Linux软件管理.md) - 关键词:`rpm`, `yum`, `apt-get`
-- [samba 使用详解](samba使用详解.md)
-- [命令行的艺术(转载)](命令行的艺术.md)
-
-## 资料
-
-### Linux 资源汇总
-
-- [awesome-linux](https://github.com/aleksandar-todorovic/awesome-linux) - Linux 资源汇总
-- [awesome-linux-software](https://github.com/LewisVo/Awesome-Linux-Software) - Linux 软件汇总
-
-### Linux 教程
-
-- [鸟哥的私房菜](http://cn.linux.vbird.org/) - 久负盛名的 Linux 教程
-- [菜鸟教程-Linux](http://www.runoob.com/linux/linux-tutorial.html) - 入门级 Linux 教程
-- [Linux 工具快速教程](https://github.com/me115/linuxtools_rst)
-
-### Linux 帮助手册
-
-- [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) - Linux 命令 cheat sheet
-- [Linux 命令大全](http://man.linuxde.net/) - Linux 命令在线帮助手册
-- [linux-command](https://github.com/jaywcjlove/linux-command) - Linux 命令在线帮助手册
-- [linux-tutorial](https://github.com/judasn/Linux-Tutorial) - Linux 环境下各种软件安装部署
diff --git "a/os/linux/samba\344\275\277\347\224\250\350\257\246\350\247\243.md" "b/os/linux/samba\344\275\277\347\224\250\350\257\246\350\247\243.md"
deleted file mode 100644
index e615301898..0000000000
--- "a/os/linux/samba\344\275\277\347\224\250\350\257\246\350\247\243.md"
+++ /dev/null
@@ -1,477 +0,0 @@
----
-title: samba 使用详解
-date: 2018/09/28
-categories:
- - linux
-tags:
- - linux
- - windows
----
-
-# samba 使用详解
-
-> samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的一个免费软件。
->
-> samba 提供了在不同计算机(即使操作系统不同)上共享服务的能力。
->
-> 关键词:`samba`, `selinux`
-
-
-
-- [1. 安装配置 samba](#1-安装配置-samba)
- - [1.1. 查看是否已经安装 samba](#11-查看是否已经安装-samba)
- - [1.2. 安装 samba 工具](#12-安装-samba-工具)
- - [1.3. 配置 samba](#13-配置-samba)
- - [1.4. 创建 samba 用户](#14-创建-samba-用户)
- - [1.5. 启动 samba 服务](#15-启动-samba-服务)
- - [1.6. 为 samba 添加防火墙规则](#16-为-samba-添加防火墙规则)
- - [1.7. 测试 samba 服务](#17-测试-samba-服务)
- - [1.8. 访问 samba 服务共享的目录](#18-访问-samba-服务共享的目录)
-- [2. 配置详解](#2-配置详解)
- - [2.1. samba 默认配置](#21-samba-默认配置)
- - [2.2. 全局参数 [global]](#22-全局参数-global)
- - [2.3. 共享参数 [共享名]](#23-共享参数-共享名)
-- [3. 常见问题](#3-常见问题)
- - [3.1. 你可能没有权限访问网络资源](#31-你可能没有权限访问网络资源)
- - [3.2. window 下对 samba 的清理操作](#32-window-下对-samba-的清理操作)
-- [4. 参考资料](#4-参考资料)
-
-
-
-## 1. 安装配置 samba
-
-本文将以一个完整的示例来展示如何配置 samba 来实现 Linux 和 Windows 的文件共享。
-
-目标:假设希望共享 Linux 服务器上的 /share/fs 目录。
-
-### 1.1. 查看是否已经安装 samba
-
-- CentOS:`rpm -qa | grep samba`
-- Ubuntu:`dpkg -l | grep samba`
-
-### 1.2. 安装 samba 工具
-
-- CentOS:`yum install -y samba samba-client samba-common`
-- Ubuntu:`sudo apt-get install -y samba samba-client`
-
-### 1.3. 配置 samba
-
-samba 服务的配置文件是 `/etc/samba/smb.conf`,如果没有则 samba 无法启动。
-
-执行以下命令,编辑配置文件:
-
-```sh
-vim /etc/samba/smb.conf
-```
-
-修改配置如下:
-
-```ini
-[global]
- workgroup = SAMBA
- security = user
-
- passdb backend = tdbsam
-
- printing = cups
- printcap name = cups
- load printers = yes
- cups options = raw
-
-[homes]
- comment = Home Directories
- valid users = %S, %D%w%S
- browseable = No
- read only = No
- inherit acls = Yes
-
-[printers]
- comment = All Printers
- path = /var/tmp
- printable = Yes
- create mask = 0600
- browseable = No
-
-[print$]
- comment = Printer Drivers
- path = /var/lib/samba/drivers
- write list = @printadmin root
- force group = @printadmin
- create mask = 0664
- directory mask = 0775
-
-[fs]
- comment = share folder
- path = /share/fs
- browseable = yes
- writable = yes
- read only = no
- guest ok = yes
- create mask = 0777
- directory mask = 0777
- public = yes
- valid users = root
-```
-
-> 说明:
->
-> - 我在这里添加了一个 **[fs]** 标签,这就是共享区域的配置。
-> - 这里设置 `path` 属性为 `/share/fs`,意味着准备共享 `/share/fs` 目录,需要根据实际需要设置路径。`/share/fs` 目录的权限要设置为 **777**:`chmod 777 /share/fs`。
-> - `browseable`、`writable` 等属性就比较容易理解了,即配置共享目录的访问权限。
-> - `valid users` 属性指定允许访问的用户,需要注意的是指定的用户必须是 Linux 机器上实际存在的用户。
-
-### 1.4. 创建 samba 用户
-
-创建的 samba 用户必须是 Linux 机器上实际存在的用户。
-
-```sh
-$ sudo smbpasswd -a root
-New SMB password:
-Retype new SMB password:
-Added user root.
-```
-
-根据提示输入 samba 用户的密码。当 samba 服务成功安装、启动后,通过 Windows 系统访问机器共享目录时,就要输入这里配置的用户名、密码。
-
-- 查看 samba 服务器中已拥有哪些用户 - `pdbedit -L`
-- 删除 samba 服务中的某个用户 - `smbpasswd -x 用户名`
-
-### 1.5. 启动 samba 服务
-
-CentOS 6
-
-```sh
-$ sudo service samba restart # 重启 samba
-$ sudo service smb restart # 重启 samba
-```
-
-CentOS 7
-
-```sh
-$ sudo systemctl start smb.service # 启动 samba
-$ sudo systemctl restart smb.service # 重启 samba
-$ sudo systemctl enable smb.service # 设置开机自动启动
-$ sudo systemctl status smb.service # 查询 samba 状态
-```
-
-Ubuntu 16.04.3
-
-```
-$ sudo service smbd restart
-```
-
-### 1.6. 为 samba 添加防火墙规则
-
-```
-$ sudo firewall-cmd --permanent --zone=public --add-service=samba
-$ sudo firewall-cmd --reload
-```
-
-### 1.7. 测试 samba 服务
-
-```
-$ smbclient //localhost/fs -U root
-```
-
-输入 samba 用户的密码,如果成功,就会进入 `smb: \>`。
-
-### 1.8. 访问 samba 服务共享的目录
-
-Windows:
-
-访问:`\\<你的ip>\<你的共享路径>` :
-
-
-
-Mac:
-
-与 Windows 类似,直接在 Finder 中访问 `smb://<你的ip>/<你的共享路径>` 即可。
-
-## 2. 配置详解
-
-### 2.1. samba 默认配置
-
-你可以从 [这里](https://git.samba.org/samba.git/?p=samba.git;a=blob_plain;f=examples/smb.conf.default;hb=HEAD) 获取到默认配置文件:
-
-```
-$ cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
-$ wget "https://git.samba.org/samba.git/?p=samba.git;a=blob_plain;f=examples/smb.conf.default;hb=HEAD" -O /etc/samba/smb.conf
-```
-
-smb.conf 默认内容如下:
-
-```ini
-[global]
- workgroup = SAMBA
- security = user
-
- passdb backend = tdbsam
-
- printing = cups
- printcap name = cups
- load printers = yes
- cups options = raw
-
-[homes]
- comment = Home Directories
- valid users = %S, %D%w%S
- browseable = No
- read only = No
- inherit acls = Yes
-
-[printers]
- comment = All Printers
- path = /var/tmp
- printable = Yes
- create mask = 0600
- browseable = No
-
-[print$]
- comment = Printer Drivers
- path = /var/lib/samba/drivers
- write list = root
- create mask = 0664
- directory mask = 0775
-```
-
-### 2.2. 全局参数 [global]
-
-```ini
-[global]
-
-config file = /usr/local/samba/lib/smb.conf.%m
-说明:config file可以让你使用另一个配置文件来覆盖缺省的配置文件。如果文件 不存在,则该项无效。这个参数很有用,可以使得samba配置更灵活,可以让一台samba服务器模拟多台不同配置的服务器。比如,你想让PC1(主机名)这台电脑在访问Samba Server时使用它自己的配置文件,那么先在/etc/samba/host/下为PC1配置一个名为smb.conf.pc1的文件,然后在smb.conf中加入:config file=/etc/samba/host/smb.conf.%m。这样当PC1请求连接Samba Server时,smb.conf.%m就被替换成smb.conf.pc1。这样,对于PC1来说,它所使用的Samba服务就是由smb.conf.pc1定义的,而其他机器访问Samba Server则还是应用smb.conf。
-
-workgroup = WORKGROUP
-说明:设定 Samba Server 所要加入的工作组或者域。
-
-server string = Samba Server Version %v
-说明:设定 Samba Server 的注释,可以是任何字符串,也可以不填。宏%v表示显示Samba的版本号。
-
-netbios name = smbserver
-说明:设置Samba Server的NetBIOS名称。如果不填,则默认会使用该服务器的DNS名称的第一部分。netbios name和workgroup名字不要设置成一样了。
-
-interfaces = lo eth0 192.168.12.2/24 192.168.13.2/24
-说明:设置Samba Server监听哪些网卡,可以写网卡名,也可以写该网卡的IP地址。
-
-hosts allow = 127.192.168.1 192.168.10.1
-说明:表示允许连接到Samba Server的客户端,多个参数以空格隔开。可以用一个IP表示,也可以用一个网段表示。hosts deny 与hosts allow 刚好相反。
-例如:
-# 表示容许来自172.17.2.*.*的主机连接,但排除172.17.2.50
-hosts allow=172.17.2.EXCEPT172.17.2.50
-# 表示容许来自172.17.2.0/255.255.0.0子网中的所有主机连接
-hosts allow=172.17.2.0/255.255.0.0
-# 表示容许来自M1和M2两台计算机连接
-hosts allow=M1,M2
-# 表示容许来自SC域的所有计算机连接
-hosts allow=@SC
-max connections = 0
-说明:max connections用来指定连接Samba Server的最大连接数目。如果超出连接数目,则新的连接请求将被拒绝。0表示不限制。
-
-deadtime = 0
-说明:deadtime用来设置断掉一个没有打开任何文件的连接的时间。单位是分钟,0代表Samba Server不自动切断任何连接。
-
-time server = yes/no
-说明:time server用来设置让nmdb成为windows客户端的时间服务器。
-
-log file = /var/log/samba/log.%m
-说明:设置Samba Server日志文件的存储位置以及日志文件名称。在文件名后加个宏%m(主机名),表示对每台访问Samba Server的机器都单独记录一个日志文件。如果pc1、pc2访问过Samba Server,就会在/var/log/samba目录下留下log.pc1和log.pc2两个日志文件。
-
-max log size = 50
-说明:设置Samba Server日志文件的最大容量,单位为kB,0代表不限制。
-
-security = user
-说明:设置用户访问Samba Server的验证方式,一共有四种验证方式。
-1. share:用户访问Samba Server不需要提供用户名和口令, 安全性能较低。
-2. user:Samba Server共享目录只能被授权的用户访问,由Samba Server负责检查账号和密码的正确性。账号和密码要在本Samba Server中建立。
-3. server:依靠其他Windows NT/2000或Samba Server来验证用户的账号和密码,是一种代理验证。此种安全模式下,系统管理员可以把所有的Windows用户和口令集中到一个NT系统上,使用Windows NT进行Samba认证, 远程服务器可以自动认证全部用户和口令,如果认证失败,Samba将使用用户级安全模式作为替代的方式。
-4. domain:域安全级别,使用主域控制器(PDC)来完成认证。
-
-passdb backend = tdbsam
-说明:passdb backend就是用户后台的意思。目前有三种后台:smbpasswd、tdbsam和ldapsam。sam应该是security account manager(安全账户管理)的简写。
-
-smbpasswd:该方式是使用smb自己的工具smbpasswd来给系统用户(真实
-用户或者虚拟用户)设置一个Samba密码,客户端就用这个密码来访问Samba的资源。
-1. smbpasswd文件默认在/etc/samba目录下,不过有时候要手工建立该文件。
-2. tdbsam:该方式则是使用一个数据库文件来建立用户数据库。数据库文件叫passdb.tdb,默认在/etc/samba目录下。passdb.tdb用户数据库可以使用smbpasswd –a来建立Samba用户,不过要建立的Samba用户必须先是系统用户。我们也可以使用pdbedit命令来建立Samba账户。pdbedit命令的参数很多,我们列出几个主要的。
- pdbedit –a username:新建Samba账户。
- pdbedit –x username:删除Samba账户。
- pdbedit –L:列出Samba用户列表,读取passdb.tdb数据库文件。
- pdbedit –Lv:列出Samba用户列表的详细信息。
- pdbedit –c “[D]” –u username:暂停该Samba用户的账号。
- pdbedit –c “[]” –u username:恢复该Samba用户的账号。
-3. ldapsam:该方式则是基于LDAP的账户管理方式来验证用户。首先要建立LDAP服务,然后设置“passdb backend = ldapsam:ldap://LDAP Server”
-
-encrypt passwords = yes/no
-说明:是否将认证密码加密。因为现在windows操作系统都是使用加密密码,所以一般要开启此项。不过配置文件默认已开启。
-
-smb passwd file = /etc/samba/smbpasswd
-说明:用来定义samba用户的密码文件。smbpasswd文件如果没有那就要手工新建。
-
-username map = /etc/samba/smbusers
-说明:用来定义用户名映射,比如可以将root换成administrator、admin等。不过要事先在smbusers文件中定义好。比如:root = administrator admin,这样就可以用administrator或admin这两个用户来代替root登陆Samba Server,更贴近windows用户的习惯。
-
-guest account = nobody
-说明:用来设置guest用户名。
-
-socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
-说明:用来设置服务器和客户端之间会话的Socket选项,可以优化传输速度。
-
-domain master = yes/no
-说明:设置Samba服务器是否要成为网域主浏览器,网域主浏览器可以管理跨子网域的浏览服务。
-
-local master = yes/no
-说明:local master用来指定Samba Server是否试图成为本地网域主浏览器。如果设为no,则永远不会成为本地网域主浏览器。但是即使设置为yes,也不等于该Samba Server就能成为主浏览器,还需要参加选举。
-
-preferred master = yes/no
-说明:设置Samba Server一开机就强迫进行主浏览器选举,可以提高Samba Server成为本地网域主浏览器的机会。如果该参数指定为yes时,最好把domain master也指定为yes。使用该参数时要注意:如果在本Samba Server所在的子网有其他的机器(不论是windows NT还是其他Samba Server)也指定为首要主浏览器时,那么这些机器将会因为争夺主浏览器而在网络上大发广播,影响网络性能。如果同一个区域内有多台Samba Server,将上面三个参数设定在一台即可。
-
-os level = 200
-说明:设置samba服务器的os level。该参数决定Samba Server是否有机会成为本地网域的主浏览器。os level从0到255,winNT的os level是32,win95/98的os level是1。Windows 2000的os level是64。如果设置为0,则意味着Samba Server将失去浏览选择。如果想让Samba Server成为PDC,那么将它的os level值设大些。
-
-domain logons = yes/no
-说明:设置Samba Server是否要做为本地域控制器。主域控制器和备份域控制器都需要开启此项。
-
-logon . = %u.bat
-说明:当使用者用windows客户端登陆,那么Samba将提供一个登陆档。如果设置成%u.bat,那么就要为每个用户提供一个登陆档。如果人比较多,那就比较麻烦。可以设置成一个具体的文件名,比如start.bat,那么用户登陆后都会去执行start.bat,而不用为每个用户设定一个登陆档了。这个文件要放置在[netlogon]的path设置的目录路径下。
-
-wins support = yes/no
-说明:设置samba服务器是否提供wins服务。
-
-wins server = wins服务器IP地址
-说明:设置Samba Server是否使用别的wins服务器提供wins服务。
-
-wins proxy = yes/no
-说明:设置Samba Server是否开启wins代理服务。
-
-dns proxy = yes/no
-说明:设置Samba Server是否开启dns代理服务。
-
-load printers = yes/no
-说明:设置是否在启动Samba时就共享打印机。
-
-printcap name = cups
-说明:设置共享打印机的配置文件。
-
-printing = cups
-说明:设置Samba共享打印机的类型。现在支持的打印系统有:bsd, sysv, plp, lprng, aix, hpux, qnx
-```
-
-### 2.3. 共享参数 [共享名]
-
-```ini
-[共享名]
-
-comment = 任意字符串
-说明:comment是对该共享的描述,可以是任意字符串。
-
-path = 共享目录路径
-说明:path用来指定共享目录的路径。可以用%u、%m这样的宏来代替路径里的unix用户和客户机的Netbios名,用宏表示主要用于[homes]共享域。例如:如果我们不打算用home段做为客户的共享,而是在/home/share/下为每个Linux用户以他的用户名建个目录,作为他的共享目录,这样path就可以写成:path = /home/share/%u; 。用户在连接到这共享时具体的路径会被他的用户名代替,要注意这个用户名路径一定要存在,否则,客户机在访问时会找不到网络路径。同样,如果我们不是以用户来划分目录,而是以客户机来划分目录,为网络上每台可以访问samba的机器都各自建个以它的netbios名的路径,作为不同机器的共享资源,就可以这样写:path = /home/share/%m 。
-
-browseable = yes/no
-说明:browseable用来指定该共享是否可以浏览。
-
-writable = yes/no
-说明:writable用来指定该共享路径是否可写。
-
-available = yes/no
-说明:available用来指定该共享资源是否可用。
-
-admin users = 该共享的管理者
-说明:admin users用来指定该共享的管理员(对该共享具有完全控制权限)。在samba 3.0中,如果用户验证方式设置成“security=share”时,此项无效。
-例如:admin users =bobyuan,jane(多个用户中间用逗号隔开)。
-
-valid users = 允许访问该共享的用户
-说明:valid users用来指定允许访问该共享资源的用户。
-例如:valid users = bobyuan,@bob,@tech(多个用户或者组中间用逗号隔开,如果要加入一个组就用“@+组名”表示。)
-
-invalid users = 禁止访问该共享的用户
-说明:invalid users用来指定不允许访问该共享资源的用户。
-例如:invalid users = root,@bob(多个用户或者组中间用逗号隔开。)
-
-write list = 允许写入该共享的用户
-说明:write list用来指定可以在该共享下写入文件的用户。
-例如:write list = bobyuan,@bob
-
-public = yes/no
-说明:public用来指定该共享是否允许guest账户访问。
-
-guest ok = yes/no
-说明:意义同“public”。
-
-几个特殊共享:
-[homes]
-comment = Home Directories
-browseable = no
-writable = yes
-valid users = %S
-; valid users = MYDOMAIN\%S
-
-[printers]
-comment = All Printers
-path = /var/spool/samba
-browseable = no
-guest ok = no
-writable = no
-printable = yes
-
-[netlogon]
-comment = Network Logon Service
-path = /var/lib/samba/netlogon
-guest ok = yes
-writable = no
-share modes = no
-
-[Profiles]
-path = /var/lib/samba/profiles
-browseable = no
-guest ok = yes
-```
-
-## 3. 常见问题
-
-### 3.1. 你可能没有权限访问网络资源
-
-问题现象:
-
-- 出现 **NT_STATUS_ACCESS_DENIED** 错误
-- Windows 下成功登陆 samba 后,点击共享目录仍然提示——你可能没有权限访问网络资源。
-
-解决步骤:
-
-1. 检查是否配置了防火墙规则
-
-```sh
-# 一种方法是强行关闭防火墙
-$ sudo service iptables stop
-
-# 另一种方法是配置防火墙规则
-$ sudo firewall-cmd --permanent --zone=public --add-service=samba
-$ sudo firewall-cmd --reload
-```
-
-2. 关闭 selinux
-
-```sh
-# 将 /etc/selinux/config 文件中的 SELINUX 设为 disabled
-$ sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
-
-# 重启生效
-$ reboot
-```
-
-### 3.2. window 下对 samba 的清理操作
-
-1. windows 清除访问 samba 局域网密码缓存
- - 在 dos 窗口中输入 `control userpasswords2` 或者 `control keymgr.dll`,然后【高级】/【密码管理】,删掉保存的该机器密码。
-2. windows 清除连接的 linux 的 samba 服务缓存
- 1. 打开 win 的命令行。
- 2. 输入 net use,就会打印出当前缓存的连接上列表。
- 3. 根据列表,一个个删除连接: net use 远程连接名称 /del;或者一次性全部删除:`net use * /del`。
-
-## 4. 参考资料
-
-- http://blog.51cto.com/yuanbin/115761
-- https://www.jianshu.com/p/750be209a6f0
-- https://github.com/judasn/Linux-Tutorial/blob/master/markdown-file/Samba.md
-- https://blog.csdn.net/lan120576664/article/details/50396511
diff --git "a/os/linux/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md" "b/os/linux/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md"
deleted file mode 100644
index ea9ceb485b..0000000000
--- "a/os/linux/\345\221\275\344\273\244\350\241\214\347\232\204\350\211\272\346\234\257.md"
+++ /dev/null
@@ -1,614 +0,0 @@
-> 转载自 https://github.com/jlevy/the-art-of-command-line
-
-*[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](README-el.md) ∙ [English](README.md) ∙ [Español](README-es.md) ∙ [Français](README-fr.md) ∙ [Indonesia](README-id.md) ∙ [Italiano](README-it.md) ∙ [日本語](README-ja.md) ∙ [한국어](README-ko.md) ∙ [Português](README-pt.md) ∙ [Română](README-ro.md) ∙ [Русский](README-ru.md) ∙ [Slovenščina](README-sl.md) ∙ [Українська](README-uk.md) ∙ [简体中文](README-zh.md) ∙ [繁體中文](README-zh-Hant.md)*
-
-
-# 命令行的艺术
-
-[](https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
-- [前言](#前言)
-- [基础](#基础)
-- [日常使用](#日常使用)
-- [文件及数据处理](#文件及数据处理)
-- [系统调试](#系统调试)
-- [单行脚本](#单行脚本)
-- [冷门但有用](#冷门但有用)
-- [仅限 OS X 系统](#仅限-os-x-系统)
-- [仅限 Windows 系统](#仅限-windows-系统)
-- [更多资源](#更多资源)
-- [免责声明](#免责声明)
-
-
-
-
-熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际上,它会提高你作为工程师的灵活性以及生产力。本文是一份我在 Linux 上工作时,发现的一些命令行使用技巧的摘要。有些技巧非常基础,而另一些则相当复杂,甚至晦涩难懂。这篇文章并不长,但当你能够熟练掌握这里列出的所有技巧时,你就学会了很多关于命令行的东西了。
-
-这篇文章是[许多作者和译者](AUTHORS.md)共同的成果。
-这里的部分内容
-[首次](http://www.quora.com/What-are-some-lesser-known-but-useful-Unix-commands)
-[出现](http://www.quora.com/What-are-the-most-useful-Swiss-army-knife-one-liners-on-Unix)
-于 [Quora](http://www.quora.com/What-are-some-time-saving-tips-that-every-Linux-user-should-know),
-但已经迁移到了 Github,并由众多高手做出了许多改进。
-如果你在本文中发现了错误或者存在可以改善的地方,请[**贡献你的一份力量**](/CONTRIBUTING.md)。
-
-## 前言
-
-涵盖范围:
-
-- 这篇文章不仅能帮助刚接触命令行的新手,而且对具有经验的人也大有裨益。本文致力于做到*覆盖面广*(涉及所有重要的内容),*具体*(给出具体的最常用的例子),以及*简洁*(避免冗余的内容,或是可以在其他地方轻松查到的细枝末节)。在特定应用场景下,本文的内容属于基本功或者能帮助您节约大量的时间。
-- 本文主要为 Linux 所写,但在[仅限 OS X 系统](#仅限-os-x-系统)章节和[仅限 Windows 系统](#仅限-windows-系统)章节中也包含有对应操作系统的内容。除去这两个章节外,其它的内容大部分均可在其他类 Unix 系统或 OS X,甚至 Cygwin 中得到应用。
-- 本文主要关注于交互式 Bash,但也有很多技巧可以应用于其他 shell 和 Bash 脚本当中。
-- 除去“标准的”Unix 命令,本文还包括了一些依赖于特定软件包的命令(前提是它们具有足够的价值)。
-
-注意事项:
-
-- 为了能在一页内展示尽量多的东西,一些具体的信息可以在引用的页面中找到。我们相信机智的你知道如何使用 Google 或者其他搜索引擎来查阅到更多的详细信息。文中部分命令需要您使用 `apt-get`,`yum`,`dnf`,`pacman`,
- `pip` 或 `brew`(以及其它合适的包管理器)来安装依赖的程序。
-- 遇到问题的话,请尝试使用 [Explainshell](http://explainshell.com/) 去获取相关命令、参数、管道等内容的解释。
-
-
-## 基础
-
-- 学习 Bash 的基础知识。具体地,在命令行中输入 `man bash` 并至少全文浏览一遍; 它理解起来很简单并且不冗长。其他的 shell 可能很好用,但 Bash 的功能已经足够强大并且到几乎总是可用的( 如果你*只*学习 zsh,fish 或其他的 shell 的话,在你自己的设备上会显得很方便,但过度依赖这些功能会给您带来不便,例如当你需要在服务器上工作时)。
-
-- 熟悉至少一个基于文本的编辑器。通常而言 Vim (`vi`) 会是你最好的选择,毕竟在终端中编辑文本时 Vim 是最好用的工具(甚至大部分情况下 Vim 要比 Emacs、大型 IDE 或是炫酷的编辑器更好用)。
-
-- 学会如何使用 `man` 命令去阅读文档。学会使用 `apropos` 去查找文档。知道有些命令并不对应可执行文件,而是在 Bash 内置好的,此时可以使用 `help` 和 `help -d` 命令获取帮助信息。你可以用 `type 命令` 来判断这个命令到底是可执行文件、shell 内置命令还是别名。
-
-- 学会使用 `>` 和 `<` 来重定向输出和输入,学会使用 `|` 来重定向管道。明白 `>` 会覆盖了输出文件而 `>>` 是在文件末添加。了解标准输出 stdout 和标准错误 stderr。
-
-- 学会使用通配符 `*` (或许再算上 `?` 和 `[`...`]`) 和引用以及引用中 `'` 和 `"` 的区别(后文中有一些具体的例子)。
-
-- 熟悉 Bash 中的任务管理工具:`&`,**ctrl-z**,**ctrl-c**,`jobs`,`fg`,`bg`,`kill` 等。
-
-- 学会使用 `ssh` 进行远程命令行登录,最好知道如何使用 `ssh-agent`,`ssh-add` 等命令来实现基础的无密码认证登录。
-
-- 学会基本的文件管理工具:`ls` 和 `ls -l` (了解 `ls -l` 中每一列代表的意义),`less`,`head`,`tail` 和 `tail -f` (甚至 `less +F`),`ln` 和 `ln -s` (了解硬链接与软链接的区别),`chown`,`chmod`,`du` (硬盘使用情况概述:`du -hs *`)。 关于文件系统的管理,学习 `df`,`mount`,`fdisk`,`mkfs`,`lsblk`。知道 inode 是什么(与 `ls -i` 和 `df -i` 等命令相关)。
-
-- 学习基本的网络管理工具:`ip` 或 `ifconfig`,`dig`。
-
-- 学习并使用一种版本控制管理系统,例如 `git`。
-
-- 熟悉正则表达式,学会使用 `grep`/`egrep`,它们的参数中 `-i`,`-o`,`-v`,`-A`,`-B` 和 `-C` 这些是很常用并值得认真学习的。
-
-- 学会使用 `apt-get`,`yum`,`dnf` 或 `pacman` (具体使用哪个取决于你使用的 Linux 发行版)来查找和安装软件包。并确保你的环境中有 `pip` 来安装基于 Python 的命令行工具 (接下来提到的部分程序使用 `pip` 来安装会很方便)。
-
-
-## 日常使用
-
-- 在 Bash 中,可以通过按 **Tab** 键实现自动补全参数,使用 **ctrl-r** 搜索命令行历史记录(按下按键之后,输入关键字便可以搜索,重复按下 **ctrl-r** 会向后查找匹配项,按下 **Enter** 键会执行当前匹配的命令,而按下右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改)。
-
-- 在 Bash 中,可以按下 **ctrl-w** 删除你键入的最后一个单词,**ctrl-u** 可以删除行内光标所在位置之前的内容,**alt-b** 和 **alt-f** 可以以单词为单位移动光标,**ctrl-a** 可以将光标移至行首,**ctrl-e** 可以将光标移至行尾,**ctrl-k** 可以删除光标至行尾的所有内容,**ctrl-l** 可以清屏。键入 `man readline` 可以查看 Bash 中的默认快捷键。内容有很多,例如 **alt-.** 循环地移向前一个参数,而 **alt-*** 可以展开通配符。
-
-
-- 你喜欢的话,可以执行 `set -o vi` 来使用 vi 风格的快捷键,而执行 `set -o emacs` 可以把它改回来。
-
-- 为了便于编辑长命令,在设置你的默认编辑器后(例如 `export EDITOR=vim`),**ctrl-x** **ctrl-e** 会打开一个编辑器来编辑当前输入的命令。在 vi 风格下快捷键则是 **escape-v**。
-
-- 键入 `history` 查看命令行历史记录,再用 `!n`(`n` 是命令编号)就可以再次执行。其中有许多缩写,最有用的大概就是 `!$`, 它用于指代上次键入的参数,而 `!!` 可以指代上次键入的命令了(参考 man 页面中的“HISTORY EXPANSION”)。不过这些功能,你也可以通过快捷键 **ctrl-r** 和 **alt-.** 来实现。
-
-- `cd` 命令可以切换工作路径,输入 `cd ~` 可以进入 home 目录。要访问你的 home 目录中的文件,可以使用前缀 `~`(例如 `~/.bashrc`)。在 `sh` 脚本里则用环境变量 `$HOME` 指代 home 目录的路径。
-
-- 回到前一个工作路径:`cd -`。
-
-- 如果你输入命令的时候中途改了主意,按下 **alt-#** 在行首添加 `#` 把它当做注释再按下回车执行(或者依次按下 **ctrl-a**, **#**, **enter**)。这样做的话,之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令。
-
-- 使用 `xargs` ( 或 `parallel`)。他们非常给力。注意到你可以控制每行参数个数(`-L`)和最大并行数(`-P`)。如果你不确定它们是否会按你想的那样工作,先使用 `xargs echo` 查看一下。此外,使用 `-I{}` 会很方便。例如:
-```bash
- find . -name '*.py' | xargs grep some_function
- cat hosts | xargs -I{} ssh root@{} hostname
-```
-
-
-- `pstree -p` 以一种优雅的方式展示进程树。
-
-- 使用 `pgrep` 和 `pkill` 根据名字查找进程或发送信号(`-f` 参数通常有用)。
-
-- 了解你可以发往进程的信号的种类。比如,使用 `kill -STOP [pid]` 停止一个进程。使用 `man 7 signal` 查看详细列表。
-
-- 使用 `nohup` 或 `disown` 使一个后台进程持续运行。
-
-- 使用 `netstat -lntp` 或 `ss -plat` 检查哪些进程在监听端口(默认是检查 TCP 端口; 添加参数 `-u` 则检查 UDP 端口)或者 `lsof -iTCP -sTCP:LISTEN -P -n` (这也可以在 OS X 上运行)。
-
-- `lsof` 来查看开启的套接字和文件。
-
-- 使用 `uptime` 或 `w` 来查看系统已经运行多长时间。
-
-- 使用 `alias` 来创建常用命令的快捷形式。例如:`alias ll='ls -latr'` 创建了一个新的命令别名 `ll`。
-
-- 可以把别名、shell 选项和常用函数保存在 `~/.bashrc`,具体看下这篇[文章](http://superuser.com/a/183980/7106)。这样做的话你就可以在所有 shell 会话中使用你的设定。
-
-- 把环境变量的设定以及登陆时要执行的命令保存在 `~/.bash_profile`。而对于从图形界面启动的 shell 和 `cron` 启动的 shell,则需要单独配置文件。
-
-- 要想在几台电脑中同步你的配置文件(例如 `.bashrc` 和 `.bash_profile`),可以借助 Git。
-
-- 当变量和文件名中包含空格的时候要格外小心。Bash 变量要用引号括起来,比如 `"$FOO"`。尽量使用 `-0` 或 `-print0` 选项以便用 NULL 来分隔文件名,例如 `locate -0 pattern | xargs -0 ls -al` 或 `find / -print0 -type d | xargs -0 ls -al`。如果 for 循环中循环访问的文件名含有空字符(空格、tab 等字符),只需用 `IFS=$'\n'` 把内部字段分隔符设为换行符。
-
-- 在 Bash 脚本中,使用 `set -x` 去调试输出(或者使用它的变体 `set -v`,它会记录原始输入,包括多余的参数和注释)。尽可能地使用严格模式:使用 `set -e` 令脚本在发生错误时退出而不是继续运行;使用 `set -u` 来检查是否使用了未赋值的变量;试试 `set -o pipefail`,它可以监测管道中的错误。当牵扯到很多脚本时,使用 `trap` 来检测 ERR 和 EXIT。一个好的习惯是在脚本文件开头这样写,这会使它能够检测一些错误,并在错误发生时中断程序并输出信息:
-```bash
- set -euo pipefail
- trap "echo 'error: Script failed: see failed command above'" ERR
-```
-
-- 在 Bash 脚本中,子 shell(使用括号 `(...)`)是一种组织参数的便捷方式。一个常见的例子是临时地移动工作路径,代码如下:
-```bash
- # do something in current dir
- (cd /some/other/dir && other-command)
- # continue in original dir
-```
-
-- 在 Bash 中,变量有许多的扩展方式。`${name:?error message}` 用于检查变量是否存在。此外,当 Bash 脚本只需要一个参数时,可以使用这样的代码 `input_file=${1:?usage: $0 input_file}`。在变量为空时使用默认值:`${name:-default}`。如果你要在之前的例子中再加一个(可选的)参数,可以使用类似这样的代码 `output_file=${2:-logfile}`,如果省略了 $2,它的值就为空,于是 `output_file` 就会被设为 `logfile`。数学表达式:`i=$(( (i + 1) % 5 ))`。序列:`{1..10}`。截断字符串:`${var%suffix}` 和 `${var#prefix}`。例如,假设 `var=foo.pdf`,那么 `echo ${var%.pdf}.txt` 将输出 `foo.txt`。
-
-- 使用括号扩展(`{`...`}`)来减少输入相似文本,并自动化文本组合。这在某些情况下会很有用,例如 `mv foo.{txt,pdf} some-dir`(同时移动两个文件),`cp somefile{,.bak}`(会被扩展成 `cp somefile somefile.bak`)或者 `mkdir -p test-{a,b,c}/subtest-{1,2,3}`(会被扩展成所有可能的组合,并创建一个目录树)。
-
-- 通过使用 `<(some command)` 可以将输出视为文件。例如,对比本地文件 `/etc/hosts` 和一个远程文件:
-```sh
- diff /etc/hosts <(ssh somehost cat /etc/hosts)
-```
-
-- 编写脚本时,你可能会想要把代码都放在大括号里。缺少右括号的话,代码就会因为语法错误而无法执行。如果你的脚本是要放在网上分享供他人使用的,这样的写法就体现出它的好处了,因为这样可以防止下载不完全代码被执行。
-```bash
-{
- # 在这里写代码
-}
-```
-
-- 了解 Bash 中的“here documents”,例如 `cat <logfile 2>&1` 或者 `some-command &>logfile`。通常,为了保证命令不会在标准输入里残留一个未关闭的文件句柄捆绑在你当前所在的终端上,在命令后添加 `>> 2+3
-5
-```
-
-
-## 文件及数据处理
-
-- 在当前目录下通过文件名查找一个文件,使用类似于这样的命令:`find . -iname '*something*'`。在所有路径下通过文件名查找文件,使用 `locate something` (但注意到 `updatedb` 可能没有对最近新建的文件建立索引,所以你可能无法定位到这些未被索引的文件)。
-
-- 使用 [`ag`](https://github.com/ggreer/the_silver_searcher) 在源代码或数据文件里检索(`grep -r` 同样可以做到,但相比之下 `ag` 更加先进)。
-
-- 将 HTML 转为文本:`lynx -dump -stdin`。
-
-- Markdown,HTML,以及所有文档格式之间的转换,试试 [`pandoc`](http://pandoc.org/)。
-
-- 当你要处理棘手的 XML 时候,`xmlstarlet` 算是上古时代流传下来的神器。
-
-- 使用 [`jq`](http://stedolan.github.io/jq/) 处理 JSON。
-
-- 使用 [`shyaml`](https://github.com/0k/shyaml) 处理 YAML。
-
-- 要处理 Excel 或 CSV 文件的话,[csvkit](https://github.com/onyxfish/csvkit) 提供了 `in2csv`,`csvcut`,`csvjoin`,`csvgrep` 等方便易用的工具。
-
-- 当你要处理 Amazon S3 相关的工作的时候,[`s3cmd`](https://github.com/s3tools/s3cmd) 是一个很方便的工具而 [`s4cmd`](https://github.com/bloomreach/s4cmd) 的效率更高。Amazon 官方提供的 [`aws`](https://github.com/aws/aws-cli) 以及 [`saws`](https://github.com/donnemartin/saws) 是其他 AWS 相关工作的基础,值得学习。
-
-- 了解如何使用 `sort` 和 `uniq`,包括 uniq 的 `-u` 参数和 `-d` 参数,具体内容在后文单行脚本节中。另外可以了解一下 `comm`。
-
-- 了解如何使用 `cut`,`paste` 和 `join` 来更改文件。很多人都会使用 `cut`,但遗忘了 `join`。
-
-- 了解如何运用 `wc` 去计算新行数(`-l`),字符数(`-m`),单词数(`-w`)以及字节数(`-c`)。
-
-- 了解如何使用 `tee` 将标准输入复制到文件甚至标准输出,例如 `ls -al | tee file.txt`。
-
-- 要进行一些复杂的计算,比如分组、逆序和一些其他的统计分析,可以考虑使用 [`datamash`](https://www.gnu.org/software/datamash/)。
-
-- 注意到语言设置(中文或英文等)对许多命令行工具有一些微妙的影响,比如排序的顺序和性能。大多数 Linux 的安装过程会将 `LANG` 或其他有关的变量设置为符合本地的设置。要意识到当你改变语言设置时,排序的结果可能会改变。明白国际化可能会使 sort 或其他命令运行效率下降*许多倍*。某些情况下(例如集合运算)你可以放心的使用 `export LC_ALL=C` 来忽略掉国际化并按照字节来判断顺序。
-
-- 你可以单独指定某一条命令的环境,只需在调用时把环境变量设定放在命令的前面,例如 `TZ=Pacific/Fiji date` 可以获取斐济的时间。
-
-- 了解如何使用 `awk` 和 `sed` 来进行简单的数据处理。 参阅 [One-liners](#one-liners) 获取示例。
-
-- 替换一个或多个文件中出现的字符串:
-```sh
- perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt
-```
-
-- 使用 [`repren`](https://github.com/jlevy/repren) 来批量重命名文件,或是在多个文件中搜索替换内容。(有些时候 `rename` 命令也可以批量重命名,但要注意,它在不同 Linux 发行版中的功能并不完全一样。)
-```sh
- # 将文件、目录和内容全部重命名 foo -> bar:
- repren --full --preserve-case --from foo --to bar .
- # 还原所有备份文件 whatever.bak -> whatever:
- repren --renames --from '(.*)\.bak' --to '\1' *.bak
- # 用 rename 实现上述功能(若可用):
- rename 's/\.bak$//' *.bak
-```
-
-- 根据 man 页面的描述,`rsync` 是一个快速且非常灵活的文件复制工具。它闻名于设备之间的文件同步,但其实它在本地情况下也同样有用。在安全设置允许下,用 `rsync` 代替 `scp` 可以实现文件续传,而不用重新从头开始。它同时也是删除大量文件的[最快方法](https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html)之一:
-```sh
-mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir
-```
-
-- 若要在复制文件时获取当前进度,可使用 `pv`,[`pycp`](https://github.com/dmerejkowsky/pycp),[`progress`](https://github.com/Xfennec/progress),`rsync --progress`。若所执行的复制为block块拷贝,可以使用 `dd status=progress`。
-
-- 使用 `shuf` 可以以行为单位来打乱文件的内容或从一个文件中随机选取多行。
-
-- 了解 `sort` 的参数。显示数字时,使用 `-n` 或者 `-h` 来显示更易读的数(例如 `du -h` 的输出)。明白排序时关键字的工作原理(`-t` 和 `-k`)。例如,注意到你需要 `-k1,1` 来仅按第一个域来排序,而 `-k1` 意味着按整行排序。稳定排序(`sort -s`)在某些情况下很有用。例如,以第二个域为主关键字,第一个域为次关键字进行排序,你可以使用 `sort -k1,1 | sort -s -k2,2`。
-
-- 如果你想在 Bash 命令行中写 tab 制表符,按下 **ctrl-v** **[Tab]** 或键入 `$'\t'` (后者可能更好,因为你可以复制粘贴它)。
-
-- 标准的源代码对比及合并工具是 `diff` 和 `patch`。使用 `diffstat` 查看变更总览数据。注意到 `diff -r` 对整个文件夹有效。使用 `diff -r tree1 tree2 | diffstat` 查看变更的统计数据。`vimdiff` 用于比对并编辑文件。
-
-- 对于二进制文件,使用 `hd`,`hexdump` 或者 `xxd` 使其以十六进制显示,使用 `bvi`,`hexedit` 或者 `biew` 来进行二进制编辑。
-
-- 同样对于二进制文件,`strings`(包括 `grep` 等工具)可以帮助在二进制文件中查找特定比特。
-
-- 制作二进制差分文件(Delta 压缩),使用 `xdelta3`。
-
-- 使用 `iconv` 更改文本编码。需要更高级的功能,可以使用 `uconv`,它支持一些高级的 Unicode 功能。例如,这条命令移除了所有重音符号:
-```sh
- uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt
-```
-
-- 拆分文件可以使用 `split`(按大小拆分)和 `csplit`(按模式拆分)。
-
-- 操作日期和时间表达式,可以用 [`dateutils`](http://www.fresse.org/dateutils/) 中的 `dateadd`、`datediff`、`strptime` 等工具。
-
-- 使用 `zless`、`zmore`、`zcat` 和 `zgrep` 对压缩过的文件进行操作。
-
-- 文件属性可以通过 `chattr` 进行设置,它比文件权限更加底层。例如,为了保护文件不被意外删除,可以使用不可修改标记:`sudo chattr +i /critical/directory/or/file`
-
-- 使用 `getfacl` 和 `setfacl` 以保存和恢复文件权限。例如:
-```sh
- getfacl -R /some/path > permissions.txt
- setfacl --restore=permissions.txt
-```
-
-- 为了高效地创建空文件,请使用 `truncate`(创建[稀疏文件](https://zh.wikipedia.org/wiki/稀疏文件)),`fallocate`(用于 ext4,xfs,btrf 和 ocfs2 文件系统),`xfs_mkfile`(适用于几乎所有的文件系统,包含在 xfsprogs 包中),`mkfile`(用于类 Unix 操作系统,比如 Solaris 和 Mac OS)。
-
-## 系统调试
-
-- `curl` 和 `curl -I` 可以被轻松地应用于 web 调试中,它们的好兄弟 `wget` 也是如此,或者也可以试试更潮的 [`httpie`](https://github.com/jkbrzt/httpie)。
-
-- 获取 CPU 和硬盘的使用状态,通常使用使用 `top`(`htop` 更佳),`iostat` 和 `iotop`。而 `iostat -mxz 15` 可以让你获悉 CPU 和每个硬盘分区的基本信息和性能表现。
-
-- 使用 `netstat` 和 `ss` 查看网络连接的细节。
-
-- `dstat` 在你想要对系统的现状有一个粗略的认识时是非常有用的。然而若要对系统有一个深度的总体认识,使用 [`glances`](https://github.com/nicolargo/glances),它会在一个终端窗口中向你提供一些系统级的数据。
-
-- 若要了解内存状态,运行并理解 `free` 和 `vmstat` 的输出。值得留意的是“cached”的值,它指的是 Linux 内核用来作为文件缓存的内存大小,而与空闲内存无关。
-
-- Java 系统调试则是一件截然不同的事,一个可以用于 Oracle 的 JVM 或其他 JVM 上的调试的技巧是你可以运行 `kill -3 ` 同时一个完整的栈轨迹和堆概述(包括 GC 的细节)会被保存到标准错误或是日志文件。JDK 中的 `jps`,`jstat`,`jstack`,`jmap` 很有用。[SJK tools](https://github.com/aragozin/jvm-tools) 更高级。
-
-- 使用 [`mtr`](http://www.bitwizard.nl/mtr/) 去跟踪路由,用于确定网络问题。
-
-- 用 [`ncdu`](https://dev.yorhel.nl/ncdu) 来查看磁盘使用情况,它比寻常的命令,如 `du -sh *`,更节省时间。
-
-- 查找正在使用带宽的套接字连接或进程,使用 [`iftop`](http://www.ex-parrot.com/~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs)。
-
-- `ab` 工具(Apache 中自带)可以简单粗暴地检查 web 服务器的性能。对于更复杂的负载测试,使用 `siege`。
-
-- [`wireshark`](https://wireshark.org/),[`tshark`](https://www.wireshark.org/docs/wsug_html_chunked/AppToolstshark.html) 和 [`ngrep`](http://ngrep.sourceforge.net/) 可用于复杂的网络调试。
-
-- 了解 `strace` 和 `ltrace`。这俩工具在你的程序运行失败、挂起甚至崩溃,而你却不知道为什么或你想对性能有个总体的认识的时候是非常有用的。注意 profile 参数(`-c`)和附加到一个运行的进程参数 (`-p`)。
-
-- 了解使用 `ldd` 来检查共享库。但是[永远不要在不信任的文件上运行](http://www.catonmat.net/blog/ldd-arbitrary-code-execution/)。
-
-- 了解如何运用 `gdb` 连接到一个运行着的进程并获取它的堆栈轨迹。
-
-- 学会使用 `/proc`。它在调试正在出现的问题的时候有时会效果惊人。比如:`/proc/cpuinfo`,`/proc/meminfo`,`/proc/cmdline`,`/proc/xxx/cwd`,`/proc/xxx/exe`,`/proc/xxx/fd/`,`/proc/xxx/smaps`(这里的 `xxx` 表示进程的 id 或 pid)。
-
-- 当调试一些之前出现的问题的时候,[`sar`](http://sebastien.godard.pagesperso-orange.fr/) 非常有用。它展示了 cpu、内存以及网络等的历史数据。
-
-- 关于更深层次的系统分析以及性能分析,看看 `stap`([SystemTap](https://sourceware.org/systemtap/wiki)),[`perf`](https://en.wikipedia.org/wiki/Perf_(Linux)),以及[`sysdig`](https://github.com/draios/sysdig)。
-
-- 查看你当前使用的系统,使用 `uname`,`uname -a`(Unix/kernel 信息)或者 `lsb_release -a`(Linux 发行版信息)。
-
-- 无论什么东西工作得很欢乐(可能是硬件或驱动问题)时可以试试 `dmesg`。
-
-- 如果你删除了一个文件,但通过 `du` 发现没有释放预期的磁盘空间,请检查文件是否被进程占用:
- `lsof | grep deleted | grep "filename-of-my-big-file"`
-
-
-## 单行脚本
-
-一些命令组合的例子:
-
-- 当你需要对文本文件做集合交、并、差运算时,`sort` 和 `uniq` 会是你的好帮手。具体例子请参照代码后面的,此处假设 `a` 与 `b` 是两内容不同的文件。这种方式效率很高,并且在小文件和上 G 的文件上都能运用(注意尽管在 `/tmp` 在一个小的根分区上时你可能需要 `-T` 参数,但是实际上 `sort` 并不被内存大小约束),参阅前文中关于 `LC_ALL` 和 `sort` 的 `-u` 参数的部分。
-```sh
- sort a b | uniq > c # c 是 a 并 b
- sort a b | uniq -d > c # c 是 a 交 b
- sort a b b | uniq -u > c # c 是 a - b
-```
-
-- 使用 `grep . *`(每行都会附上文件名)或者 `head -100 *`(每个文件有一个标题)来阅读检查目录下所有文件的内容。这在检查一个充满配置文件的目录(如 `/sys`、`/proc`、`/etc`)时特别好用。
-
-
-- 计算文本文件第三列中所有数的和(可能比同等作用的 Python 代码快三倍且代码量少三倍):
-```sh
- awk '{ x += $3 } END { print x }' myfile
-```
-
-- 如果你想在文件树上查看大小/日期,这可能看起来像递归版的 `ls -l` 但比 `ls -lR` 更易于理解:
-```sh
- find . -type f -ls
-```
-
-- 假设你有一个类似于 web 服务器日志文件的文本文件,并且一个确定的值只会出现在某些行上,假设一个 `acct_id` 参数在 URI 中。如果你想计算出每个 `acct_id` 值有多少次请求,使用如下代码:
-```sh
- egrep -o 'acct_id=[0-9]+' access.log | cut -d= -f2 | sort | uniq -c | sort -rn
-```
-
-- 要持续监测文件改动,可以使用 `watch`,例如检查某个文件夹中文件的改变,可以用 `watch -d -n 2 'ls -rtlh | tail'`;或者在排查 WiFi 设置故障时要监测网络设置的更改,可以用 `watch -d -n 2 ifconfig`。
-
-- 运行这个函数从这篇文档中随机获取一条技巧(解析 Markdown 文件并抽取项目):
-```sh
- function taocl() {
- curl -s https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/README-zh.md|
- pandoc -f markdown -t html |
- iconv -f 'utf-8' -t 'unicode' |
- xmlstarlet fo --html --dropdtd |
- xmlstarlet sel -t -v "(html/body/ul/li[count(p)>0])[$RANDOM mod last()+1]" |
- xmlstarlet unesc | fmt -80
- }
-```
-
-## 冷门但有用
-
-- `expr`:计算表达式或正则匹配
-
-- `m4`:简单的宏处理器
-
-- `yes`:多次打印字符串
-
-- `cal`:漂亮的日历
-
-- `env`:执行一个命令(脚本文件中很有用)
-
-- `printenv`:打印环境变量(调试时或在写脚本文件时很有用)
-
-- `look`:查找以特定字符串开头的单词或行
-
-- `cut`,`paste` 和 `join`:数据修改
-
-- `fmt`:格式化文本段落
-
-- `pr`:将文本格式化成页/列形式
-
-- `fold`:包裹文本中的几行
-
-- `column`:将文本格式化成多个对齐、定宽的列或表格
-
-- `expand` 和 `unexpand`:制表符与空格之间转换
-
-- `nl`:添加行号
-
-- `seq`:打印数字
-
-- `bc`:计算器
-
-- `factor`:分解因数
-
-- [`gpg`](https://gnupg.org/):加密并签名文件
-
-- `toe`:terminfo 入口列表
-
-- `nc`:网络调试及数据传输
-
-- `socat`:套接字代理,与 `netcat` 类似
-
-- [`slurm`](https://github.com/mattthias/slurm):网络流量可视化
-
-- `dd`:文件或设备间传输数据
-
-- `file`:确定文件类型
-
-- `tree`:以树的形式显示路径和文件,类似于递归的 `ls`
-
-- `stat`:文件信息
-
-- `time`:执行命令,并计算执行时间
-
-- `timeout`:在指定时长范围内执行命令,并在规定时间结束后停止进程
-
-- `lockfile`:使文件只能通过 `rm -f` 移除
-
-- `logrotate`: 切换、压缩以及发送日志文件
-
-- `watch`:重复运行同一个命令,展示结果并/或高亮有更改的部分
-
-- [`when-changed`](https://github.com/joh/when-changed):当检测到文件更改时执行指定命令。参阅 `inotifywait` 和 `entr`。
-
-- `tac`:反向输出文件
-
-- `shuf`:文件中随机选取几行
-
-- `comm`:一行一行的比较排序过的文件
-
-- `strings`:从二进制文件中抽取文本
-
-- `tr`:转换字母
-
-- `iconv` 或 `uconv`:文本编码转换
-
-- `split` 和 `csplit`:分割文件
-
-- `sponge`:在写入前读取所有输入,在读取文件后再向同一文件写入时比较有用,例如 `grep -v something some-file | sponge some-file`
-
-- `units`:将一种计量单位转换为另一种等效的计量单位(参阅 `/usr/share/units/definitions.units`)
-
-- `apg`:随机生成密码
-
-- `xz`:高比例的文件压缩
-
-- `ldd`:动态库信息
-
-- `nm`:提取 obj 文件中的符号
-
-- `ab` 或 [`wrk`](https://github.com/wg/wrk):web 服务器性能分析
-
-- `strace`:调试系统调用
-
-- [`mtr`](http://www.bitwizard.nl/mtr/):更好的网络调试跟踪工具
-
-- `cssh`:可视化的并发 shell
-
-- `rsync`:通过 ssh 或本地文件系统同步文件和文件夹
-
-- [`wireshark`](https://wireshark.org/) 和 [`tshark`](https://www.wireshark.org/docs/wsug_html_chunked/AppToolstshark.html):抓包和网络调试工具
-
-- [`ngrep`](http://ngrep.sourceforge.net/):网络层的 grep
-
-- `host` 和 `dig`:DNS 查找
-
-- `lsof`:列出当前系统打开文件的工具以及查看端口信息
-
-- `dstat`:系统状态查看
-
-- [`glances`](https://github.com/nicolargo/glances):高层次的多子系统总览
-
-- `iostat`:硬盘使用状态
-
-- `mpstat`: CPU 使用状态
-
-- `vmstat`: 内存使用状态
-
-- `htop`:top 的加强版
-
-- `last`:登入记录
-
-- `w`:查看处于登录状态的用户
-
-- `id`:用户/组 ID 信息
-
-- [`sar`](http://sebastien.godard.pagesperso-orange.fr/):系统历史数据
-
-- [`iftop`](http://www.ex-parrot.com/~pdw/iftop/) 或 [`nethogs`](https://github.com/raboof/nethogs):套接字及进程的网络利用情况
-
-- `ss`:套接字数据
-
-- `dmesg`:引导及系统错误信息
-
-- `sysctl`: 在内核运行时动态地查看和修改内核的运行参数
-
-- `hdparm`:SATA/ATA 磁盘更改及性能分析
-
-- `lsblk`:列出块设备信息:以树形展示你的磁盘以及磁盘分区信息
-
-- `lshw`,`lscpu`,`lspci`,`lsusb` 和 `dmidecode`:查看硬件信息,包括 CPU、BIOS、RAID、显卡、USB设备等
-
-- `lsmod` 和 `modinfo`:列出内核模块,并显示其细节
-
-- `fortune`,`ddate` 和 `sl`:额,这主要取决于你是否认为蒸汽火车和莫名其妙的名人名言是否“有用”
-
-
-## 仅限 OS X 系统
-
-以下是*仅限于* OS X 系统的技巧。
-
-- 用 `brew` (Homebrew)或者 `port` (MacPorts)进行包管理。这些可以用来在 OS X 系统上安装以上的大多数命令。
-
-- 用 `pbcopy` 复制任何命令的输出到桌面应用,用 `pbpaste` 粘贴输入。
-
-- 若要在 OS X 终端中将 Option 键视为 alt 键(例如在上面介绍的 **alt-b**、**alt-f** 等命令中用到),打开 偏好设置 -> 描述文件 -> 键盘 并勾选“使用 Option 键作为 Meta 键”。
-
-- 用 `open` 或者 `open -a /Applications/Whatever.app` 使用桌面应用打开文件。
-
-- Spotlight:用 `mdfind` 搜索文件,用 `mdls` 列出元数据(例如照片的 EXIF 信息)。
-
-- 注意 OS X 系统是基于 BSD UNIX 的,许多命令(例如 `ps`,`ls`,`tail`,`awk`,`sed`)都和 Linux 中有微妙的不同( Linux 很大程度上受到了 System V-style Unix 和 GNU 工具影响)。你可以通过标题为 "BSD General Commands Manual" 的 man 页面发现这些不同。在有些情况下 GNU 版本的命令也可能被安装(例如 `gawk` 和 `gsed` 对应 GNU 中的 awk 和 sed )。如果要写跨平台的 Bash 脚本,避免使用这些命令(例如,考虑 Python 或者 `perl` )或者经过仔细的测试。
-
-- 用 `sw_vers` 获取 OS X 的版本信息。
-
-## 仅限 Windows 系统
-
-以下是*仅限于* Windows 系统的技巧。
-
-### 在 Winodws 下获取 Unix 工具
-
-- 可以安装 [Cygwin](https://cygwin.com/) 允许你在 Microsoft Windows 中体验 Unix shell 的威力。这样的话,本文中介绍的大多数内容都将适用。
-
-- 在 Windows 10 上,你可以使用 [Bash on Ubuntu on Windows](https://msdn.microsoft.com/commandline/wsl/about),它提供了一个熟悉的 Bash 环境,包含了不少 Unix 命令行工具。好处是它允许 Linux 上编写的程序在 Windows 上运行,而另一方面,Windows 上编写的程序却无法在 Bash 命令行中运行。
-
-- 如果你在 Windows 上主要想用 GNU 开发者工具(例如 GCC),可以考虑 [MinGW](http://www.mingw.org/) 以及它的 [MSYS](http://www.mingw.org/wiki/msys) 包,这个包提供了例如 bash,gawk,make 和 grep 的工具。MSYS 并不包含所有可以与 Cygwin 媲美的特性。当制作 Unix 工具的原生 Windows 端口时 MinGW 将特别地有用。
-
-- 另一个在 Windows 下实现接近 Unix 环境外观效果的选项是 [Cash](https://github.com/dthree/cash)。注意在此环境下只有很少的 Unix 命令和命令行可用。
-
-### 实用 Windows 命令行工具
-
-- 可以使用 `wmic` 在命令行环境下给大部分 Windows 系统管理任务编写脚本以及执行这些任务。
-
-- Windows 实用的原生命令行网络工具包括 `ping`,`ipconfig`,`tracert`,和 `netstat`。
-
-- 可以使用 `Rundll32` 命令来实现[许多有用的 Windows 任务](http://www.thewindowsclub.com/rundll32-shortcut-commands-windows) 。
-
-### Cygwin 技巧
-
-- 通过 Cygwin 的包管理器来安装额外的 Unix 程序。
-
-- 使用 `mintty` 作为你的命令行窗口。
-
-- 要访问 Windows 剪贴板,可以通过 `/dev/clipboard`。
-
-- 运行 `cygstart` 以通过默认程序打开一个文件。
-
-- 要访问 Windows 注册表,可以使用 `regtool`。
-
-- 注意 Windows 驱动器路径 `C:\` 在 Cygwin 中用 `/cygdrive/c` 代表,而 Cygwin 的 `/` 代表 Windows 中的 `C:\cygwin`。要转换 Cygwin 和 Windows 风格的路径可以用 `cygpath`。这在需要调用 Windows 程序的脚本里很有用。
-
-- 学会使用 `wmic`,你就可以从命令行执行大多数 Windows 系统管理任务,并编成脚本。
-
-- 要在 Windows 下获得 Unix 的界面和体验,另一个办法是使用 [Cash](https://github.com/dthree/cash)。需要注意的是,这个环境支持的 Unix 命令和命令行参数非常少。
-
-- 要在 Windows 上获取 GNU 开发者工具(比如 GCC)的另一个办法是使用 [MinGW](http://www.mingw.org/) 以及它的 [MSYS](http://www.mingw.org/wiki/msys) 软件包,该软件包提供了 bash、gawk、make、grep 等工具。然而 MSYS 提供的功能没有 Cygwin 完善。MinGW 在创建 Unix 工具的 Windows 原生移植方面非常有用。
-
-## 更多资源
-
-- [awesome-shell](https://github.com/alebcay/awesome-shell):一份精心组织的命令行工具及资源的列表。
-- [awesome-osx-command-line](https://github.com/herrbischoff/awesome-osx-command-line):一份针对 OS X 命令行的更深入的指南。
-- [Strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/):为了编写更好的脚本文件。
-- [shellcheck](https://github.com/koalaman/shellcheck):一个静态 shell 脚本分析工具,本质上是 bash/sh/zsh 的 lint。
-- [Filenames and Pathnames in Shell](http://www.dwheeler.com/essays/filenames-in-shell.html):有关如何在 shell 脚本里正确处理文件名的细枝末节。
-- [Data Science at the Command Line](http://datascienceatthecommandline.com/#tools):用于数据科学的一些命令和工具,摘自同名书籍。
-
-## 免责声明
-
-除去特别小的工作,你编写的代码应当方便他人阅读。能力往往伴随着责任,你 *有能力* 在 Bash 中玩一些奇技淫巧并不意味着你应该去做!;)
-
-
-## 授权条款
-
-[](http://creativecommons.org/licenses/by-sa/4.0/)
-
-本文使用授权协议 [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)。
diff --git a/os/tool/elastic/README.md b/os/tool/elastic/README.md
deleted file mode 100644
index 6bebd4edd6..0000000000
--- a/os/tool/elastic/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Elastic 技术栈
-
-> **Elastic 技术栈通常被用来作为日志中心。**
->
-> ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/products/elasticsearch) 、[Logstash](https://www.elastic.co/products/logstash) 、[Kibana](https://www.elastic.co/products/kibana) 的首字母组合。
->
-> [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 [Lucene](http://lucene.apache.org/core/documentation.html) 构建的开源,分布式,RESTful 搜索引擎。
->
-> [Logstash](https://www.elastic.co/products/logstash) 传输和处理你的日志、事务或其他数据。
->
-> [Kibana](https://www.elastic.co/products/kibana) 将 Elasticsearch 的数据分析并渲染为可视化的报表。
->
-> Elastic 技术栈,在 ELK 的基础上扩展了一些新的产品,如:[Beats](https://www.elastic.co/products/beats) 、[X-Pack](https://www.elastic.co/products/x-pack) 。
-
-## 目录
-
-[Elastic 技术栈之快速指南](elastic-quickstart.md)
-
-[Elastic 技术栈之 Logstash 基础](elastic-logstash.md)
-
-## 资源
-
-**官方资源**
-
-[Elastic 官方文档](https://www.elastic.co/guide/index.html)
-
-**第三方工具**
-
-[logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder)
-
-**教程**
-
-[Elasticsearch 权威指南(中文版)](https://es.xiaoleilu.com/index.html)
-
-[ELK Stack权威指南](https://github.com/chenryn/logstash-best-practice-cn)
-
-**博文**
-
-[Elasticsearch+Logstash+Kibana教程](https://www.cnblogs.com/xing901022/p/4704319.html)
-
-[ELK(Elasticsearch、Logstash、Kibana)安装和配置](https://github.com/judasn/Linux-Tutorial/blob/master/ELK-Install-And-Settings.md)
diff --git a/os/tool/elastic/elastic-beats.md b/os/tool/elastic/elastic-beats.md
deleted file mode 100644
index 43380e9623..0000000000
--- a/os/tool/elastic/elastic-beats.md
+++ /dev/null
@@ -1,296 +0,0 @@
----
-title: Elastic 技术栈之 Filebeat
-date: 2017-01-03
-categories:
-- javatool
-tags:
-- java
-- javatool
-- log
-- elastic
----
-
-# Elastic 技术栈之 Filebeat
-
-## 简介
-
-Beats 是安装在服务器上的数据中转代理。
-
-Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。
-
-
-
-Beats 有多种类型,可以根据实际应用需要选择合适的类型。
-
-常用的类型有:
-
-- **Packetbeat:**网络数据包分析器,提供有关您的应用程序服务器之间交换的事务的信息。
-- **Filebeat:**从您的服务器发送日志文件。
-- **Metricbeat:**是一个服务器监视代理程序,它定期从服务器上运行的操作系统和服务收集指标。
-- **Winlogbeat:**提供Windows事件日志。
-
-> **参考**
->
-> 更多 Beats 类型可以参考:[community-beats](https://www.elastic.co/guide/en/beats/libbeat/current/community-beats.html)
->
-> **说明**
->
-> 由于本人工作中只应用了 FileBeat,所以后面内容仅介绍 FileBeat 。
-
-### FileBeat 的作用
-
-相比 Logstash,FileBeat 更加轻量化。
-
-在任何环境下,应用程序都有停机的可能性。 Filebeat 读取并转发日志行,如果中断,则会记住所有事件恢复联机状态时所在位置。
-
-Filebeat带有内部模块(auditd,Apache,Nginx,System和MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。
-
-FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。
-
-
-
-## 安装
-
-Unix / Linux 系统建议使用下面方式安装,因为比较通用。
-
-```
-wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.1.1-linux-x86_64.tar.gz
-tar -zxf filebeat-6.1.1-linux-x86_64.tar.gz
-```
-
-> **参考**
->
-> 更多内容可以参考:[filebeat-installation](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html)
-
-## 配置
-
-### 配置文件
-
-首先,需要知道的是:`filebeat.yml` 是 filebeat 的配置文件。配置文件的路径会因为你安装方式的不同而变化。
-
-Beat 所有系列产品的配置文件都基于 [YAML](http://www.yaml.org/) 格式,FileBeat 当然也不例外。
-
-filebeat.yml 部分配置示例:
-
-```yaml
-filebeat:
- prospectors:
- - type: log
- paths:
- - /var/log/*.log
- multiline:
- pattern: '^['
- match: after
-```
-
-> **参考**
->
-> 更多 filebeat 配置内容可以参考:[配置 filebeat](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-howto-filebeat.html)
->
-> 更多 filebeat.yml 文件格式内容可以参考:[filebeat.yml 文件格式](https://www.elastic.co/guide/en/beats/libbeat/6.1/config-file-format.html)
-
-### 重要配置项
-
-#### filebeat.prospectors
-
-(文件监视器)用于指定需要关注的文件。
-
-**示例**
-
-```yaml
-filebeat.prospectors:
-- type: log
- enabled: true
- paths:
- - /var/log/*.log
-```
-
-#### output.elasticsearch
-
-如果你希望使用 filebeat 直接向 elasticsearch 输出数据,需要配置 output.elasticsearch 。
-
-**示例**
-
-```yaml
-output.elasticsearch:
- hosts: ["192.168.1.42:9200"]
-```
-
-#### output.logstash
-
-如果你希望使用 filebeat 向 logstash输出数据,然后由 logstash 再向elasticsearch 输出数据,需要配置 output.logstash。
-
-> **注意**
->
-> 相比于向 elasticsearch 输出数据,个人更推荐向 logstash 输出数据。
->
-> 因为 logstash 和 filebeat 一起工作时,如果 logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。这样,可以减少管道超负荷的情况。
-
-**示例**
-
-```yaml
-output.logstash:
- hosts: ["127.0.0.1:5044"]
-```
-
-此外,还需要在 logstash 的配置文件(如 logstash.conf)中指定 beats input 插件:
-
-```yaml
-input {
- beats {
- port => 5044 # 此端口需要与 filebeat.yml 中的端口相同
- }
-}
-
-# The filter part of this file is commented out to indicate that it is
-# optional.
-# filter {
-#
-# }
-
-output {
- elasticsearch {
- hosts => "localhost:9200"
- manage_template => false
- index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
- document_type => "%{[@metadata][type]}"
- }
-}
-```
-
-#### setup.kibana
-
-如果打算使用 Filebeat 提供的 Kibana 仪表板,需要配置 setup.kibana 。
-
-**示例**
-
-```yaml
-setup.kibana:
- host: "localhost:5601"
-```
-
-#### setup.template.settings
-
-在 Elasticsearch 中,[索引模板](https://www.elastic.co/guide/en/elasticsearch/reference/6.1/indices-templates.html)用于定义设置和映射,以确定如何分析字段。
-
-在 Filebeat 中,setup.template.settings 用于配置索引模板。
-
-Filebeat 推荐的索引模板文件由 Filebeat 软件包安装。如果您接受 filebeat.yml 配置文件中的默认配置,Filebeat在成功连接到 Elasticsearch 后自动加载模板。
-
-您可以通过在 Filebeat 配置文件中配置模板加载选项来禁用自动模板加载,或加载自己的模板。您还可以设置选项来更改索引和索引模板的名称。
-
-> **参考**
->
-> 更多内容可以参考:[filebeat-template](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-template.html)
->
-> **说明**
->
-> 如无必要,使用 Filebeat 配置文件中的默认索引模板即可。
-
-#### setup.dashboards
-
-Filebeat 附带了示例 Kibana 仪表板。在使用仪表板之前,您需要创建索引模式 `filebeat- *`,并将仪表板加载到Kibana 中。为此,您可以运行 `setup` 命令或在 `filebeat.yml` 配置文件中配置仪表板加载。
-
-为了在 Kibana 中加载 Filebeat 的仪表盘,需要在 `filebeat.yml` 配置中启动开关:
-
-```
-setup.dashboards.enabled: true
-```
-
-> **参考**
->
-> 更多内容可以参考:[configuration-dashboards](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-dashboards.html)
->
-
-## 命令
-
-filebeat 提供了一系列命令来完成各种功能。
-
-执行命令方式:
-
-```sh
-./filebeat COMMAND
-```
-
-> **参考**
->
-> 更多内容可以参考:[command-line-options](https://www.elastic.co/guide/en/beats/filebeat/current/command-line-options.html)
->
-> **说明**
->
-> 个人认为命令行没有必要一一掌握,因为绝大部分功能都可以通过配置来完成。且通过命令行指定功能这种方式要求每次输入同样参数,不利于固化启动方式。
->
-> 最重要的当然是启动命令 run 了。
->
-> **示例** 指定配置文件启动
->
-> ```sh
-> ./filebeat run -e -c filebeat.yml -d "publish"
-> ./filebeat -e -c filebeat.yml -d "publish" # run 可以省略
-> ```
-
-## 模块
-
-Filebeat 提供了一套预构建的模块,让您可以快速实施和部署日志监视解决方案,并附带示例仪表板和数据可视化。这些模块支持常见的日志格式,例如Nginx,Apache2和MySQL 等。
-
-### 运行模块的步骤
-
-- 配置 elasticsearch 和 kibana
-
-```
-output.elasticsearch:
- hosts: ["myEShost:9200"]
- username: "elastic"
- password: "elastic"
-setup.kibana:
- host: "mykibanahost:5601"
- username: "elastic"
- password: "elastic
-```
-
-> username 和 password 是可选的,如果不需要认证则不填。
-
-- 初始化环境
-
-执行下面命令,filebeat 会加载推荐索引模板。
-
-```
-./filebeat setup -e
-```
-
-- 指定模块
-
-执行下面命令,指定希望加载的模块。
-
-```
-./filebeat -e --modules system,nginx,mysql
-```
-
-> **参考**
->
-> 更多内容可以参考: [配置 filebeat 模块](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-filebeat-modules.html) | [filebeat 支持模块](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html)
-
-## 原理
-
-Filebeat 有两个主要组件:
-
-harvester:负责读取一个文件的内容。它会逐行读取文件内容,并将内容发送到输出目的地。
-
-prospector:负责管理 harvester 并找到所有需要读取的文件源。比如类型是日志,prospector 就会遍历制定路径下的所有匹配要求的文件。
-
-```yaml
-filebeat.prospectors:
-- type: log
- paths:
- - /var/log/*.log
- - /var/path2/*.log
-```
-
-Filebeat保持每个文件的状态,并经常刷新注册表文件中的磁盘状态。状态用于记住 harvester 正在读取的最后偏移量,并确保发送所有日志行。
-
-Filebeat 将每个事件的传递状态存储在注册表文件中。所以它能保证事件至少传递一次到配置的输出,没有数据丢失。
-
-## 资料
-
-[Beats 官方文档](https://www.elastic.co/guide/en/beats/libbeat/current/index.html)
-
diff --git a/os/tool/elastic/elastic-kibana.md b/os/tool/elastic/elastic-kibana.md
deleted file mode 100644
index be50fb4b38..0000000000
--- a/os/tool/elastic/elastic-kibana.md
+++ /dev/null
@@ -1,307 +0,0 @@
-# Elastic 技术栈之 Kibana
-
-## Discover
-
-单击侧面导航栏中的 `Discover` ,可以显示 `Kibana` 的数据查询功能功能。
-
-
-
-在搜索栏中,您可以输入Elasticsearch查询条件来搜索您的数据。您可以在 `Discover` 页面中浏览结果并在 `Visualize` 页面中创建已保存搜索条件的可视化。
-
-当前索引模式显示在查询栏下方。索引模式确定提交查询时搜索哪些索引。要搜索一组不同的索引,请从下拉菜单中选择不同的模式。要添加索引模式(index pattern),请转至 `Management/Kibana/Index Patterns` 并单击 `Add New`。
-
-您可以使用字段名称和您感兴趣的值构建搜索。对于数字字段,可以使用比较运算符,如大于(>),小于(<)或等于(=)。您可以将元素与逻辑运算符 `AND`,`OR` 和 `NOT` 链接,全部使用大写。
-
-默认情况下,每个匹配文档都显示所有字段。要选择要显示的文档字段,请将鼠标悬停在“可用字段”列表上,然后单击要包含的每个字段旁边的添加按钮。例如,如果只添加account_number,则显示将更改为包含五个帐号的简单列表:
-
-
-
-### 查询语义
-
-kibana 的搜索栏遵循 [query-string-syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html#query-string-syntax) 文档中所说明的查询语义。
-
-这里说明一些最基本的查询语义。
-
-查询字符串会被解析为一系列的术语和运算符。一个术语可以是一个单词(如:quick、brown)或用双引号包围的短语(如"quick brown")。
-
-查询操作允许您自定义搜索 - 下面介绍了可用的选项。
-
-#### 字段名称
-
-正如查询字符串查询中所述,将在搜索条件中搜索default_field,但可以在查询语法中指定其他字段:
-
-例如:
-
-* 查询 `status` 字段中包含 `active` 关键字
-
-```
-status:active
-```
-
-* `title` 字段包含 `quick` 或 `brown` 关键字。如果您省略 `OR` 运算符,则将使用默认运算符
-
-```
-title:(quick OR brown)
-title:(quick brown)
-```
-
-* author 字段查找精确的短语 "john smith",即精确查找。
-
-```
-author:"John Smith"
-```
-
-* 任意字段 `book.title`,`book.content` 或 `book.date` 都包含 `quick` 或 `brown`(注意我们需要如何使用 `\*` 表示通配符)
-
-```
-book.\*:(quick brown)
-```
-
-* title 字段包含任意非 null 值
-
-```
-_exists_:title
-```
-
-#### 通配符
-
-ELK 提供了 ? 和 * 两个通配符。
-
-* `?` 表示任意单个字符;
-* `*` 表示任意零个或多个字符。
-
-```
-qu?ck bro*
-```
-
-> **注意:通配符查询会使用大量的内存并且执行性能较为糟糕,所以请慎用。**
-> **提示**:纯通配符 \* 被写入 [exsits](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html) 查询,从而提高了查询效率。因此,通配符 `field:*` 将匹配包含空值的文档,如:```{“field”:“”}```,但是如果字段丢失或显示将值置为null则不匹配,如:```“field”:null}```
-> **提示**:在一个单词的开头(例如:`*ing`)使用通配符这种方式的查询量特别大,因为索引中的所有术语都需要检查,以防万一匹配。通过将 `allow_leading_wildcard` 设置为 `false`,可以禁用。
-
-#### 正则表达式
-
-可以通过 `/` 将正则表达式包裹在查询字符串中进行查询
-
-例:
-
-```
-name:/joh?n(ath[oa]n)/
-```
-
-支持的正则表达式语义可以参考:[Regular expression syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html#regexp-syntax)
-
-#### 模糊查询
-
-我们可以使用 `~` 运算符来进行模糊查询。
-
-例:
-
-假设我们实际想查询
-
-```
-quick brown forks
-```
-
-但是,由于拼写错误,我们的查询关键字变成如下情况,依然可以查到想要的结果。
-
-```
-quikc~ brwn~ foks~
-```
-
-这种模糊查询使用 Damerau-Levenshtein 距离来查找所有匹配最多两个更改的项。所谓的更改是指单个字符的插入,删除或替换,或者两个相邻字符的换位。
-
-默认编辑距离为 `2`,但编辑距离为 `1` 应足以捕捉所有人类拼写错误的80%。它可以被指定为:
-
-```
-quikc~1
-```
-
-#### 近似检索
-
-尽管短语查询(例如,`john smith`)期望所有的词条都是完全相同的顺序,但是近似查询允许指定的单词进一步分开或以不同的顺序排列。与模糊查询可以为单词中的字符指定最大编辑距离一样,近似搜索也允许我们指定短语中单词的最大编辑距离:
-
-例
-
-```
-"fox quick"~5
-```
-
-字段中的文本越接近查询字符串中指定的原始顺序,该文档就越被认为是相关的。当与上面的示例查询相比时,短语 `"quick fox"` 将被认为比 `"quick brown fox"` 更近似查询条件。
-
-#### 范围
-
-可以为日期,数字或字符串字段指定范围。闭区间范围用方括号 `[min TO max]` 和开区间范围用花括号 `{min TO max}` 来指定。
-
-我们不妨来看一些示例。
-
-* 2012 年的所有日子
-
-```
-date:[2012-01-01 TO 2012-12-31]
-```
-
-* 数字 1 到 5
-
-```
-count:[1 TO 5]
-```
-
-* 在 `alpha` 和 `omega` 之间的标签,不包括 `alpha` 和 `omega`
-
-```
-tag:{alpha TO omega}
-```
-
-* 10 以上的数字
-
-```
-count:[10 TO *]
-```
-
-* 2012 年以前的所有日期
-
-```
-date:{* TO 2012-01-01}
-```
-
-此外,开区间和闭区间也可以组合使用
-
-* 数组 1 到 5,但不包括 5
-
-```
-count:[1 TO 5}
-```
-
-一边无界的范围也可以使用以下语法:
-
-```
-age:>10
-age:>=10
-age:<10
-age:<=10
-```
-
-当然,你也可以使用 AND 运算符来得到连个查询结果的交集
-
-```
-age:(>=10 AND <20)
-age:(+>=10 +<20)
-```
-
-#### Boosting
-
-使用操作符 `^` 使一个术语比另一个术语更相关。例如,如果我们想查找所有有关狐狸的文档,但我们对狐狸特别感兴趣:
-
-```
-quick^2 fox
-```
-
-默认提升值是1,但可以是任何正浮点数。 0到1之间的提升减少了相关性。
-
-增强也可以应用于短语或组:
-
-```
-"john smith"^2 (foo bar)^4
-```
-
-#### 布尔操作
-
-默认情况下,只要一个词匹配,所有词都是可选的。搜索 `foo bar baz` 将查找包含 `foo` 或 `bar` 或 `baz` 中的一个或多个的任何文档。我们已经讨论了上面的`default_operator`,它允许你强制要求所有的项,但也有布尔运算符可以在查询字符串本身中使用,以提供更多的控制。
-
-首选的操作符是 `+`(此术语必须存在)和 `-` (此术语不得存在)。所有其他条款是可选的。例如,这个查询:
-
-```
-quick brown +fox -news
-```
-
-这条查询意味着:
-
-* fox 必须存在
-* news 必须不存在
-* quick 和 brown 是可有可无的
-
-熟悉的运算符 `AND`,`OR` 和 `NOT`(也写成 `&&`,`||` 和 `!`)也被支持。然而,这些操作符有一定的优先级:`NOT` 优先于 `AND`,`AND` 优先于 `OR`。虽然 `+` 和 `-` 仅影响运算符右侧的术语,但 `AND` 和 `OR` 会影响左侧和右侧的术语。
-
-#### 分组
-
-多个术语或子句可以用圆括号组合在一起,形成子查询
-
-```
-(quick OR brown) AND fox
-```
-
-可以使用组来定位特定的字段,或者增强子查询的结果:
-
-```
-status:(active OR pending) title:(full text search)^2
-```
-
-#### 保留字
-
-如果你需要使用任何在你的查询本身中作为操作符的字符(而不是作为操作符),那么你应该用一个反斜杠来转义它们。例如,要搜索(1 + 1)= 2,您需要将查询写为 `\(1\+1\)\=2`
-
-保留字符是:`+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /`
-
-无法正确地转义这些特殊字符可能会导致语法错误,从而阻止您的查询运行。
-
-#### 空查询
-
-如果查询字符串为空或仅包含空格,则查询将生成一个空的结果集。
-
-## Visualize
-
-要想使用可视化的方式展示您的数据,请单击侧面导航栏中的 `Visualize`。
-
-Visualize工具使您能够以多种方式(如饼图、柱状图、曲线图、分布图等)查看数据。要开始使用,请点击蓝色的 `Create a visualization` 或 `+` 按钮。
-
-
-
-有许多可视化类型可供选择。
-
-
-
-下面,我们来看创建几个图标示例:
-
-### Pie
-
-您可以从保存的搜索中构建可视化文件,也可以输入新的搜索条件。要输入新的搜索条件,首先需要选择一个索引模式来指定要搜索的索引。
-
-默认搜索匹配所有文档。最初,一个“切片”包含整个饼图:
-
-
-
-要指定在图表中展示哪些数据,请使用Elasticsearch存储桶聚合。分组汇总只是将与您的搜索条件相匹配的文档分类到不同的分类中,也称为分组。
-
-为每个范围定义一个存储桶:
-
-1. 单击 `Split Slices`。
-2. 在 `Aggregation` 列表中选择 `Terms`。_注意:这里的 Terms 是 Elk 采集数据时定义好的字段或标签。_
-3. 在 `Field` 列表中选择 `level.keyword`。
-4. 点击  按钮来更新图表。
-
-
-
-完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。
-
-### Vertical Bar
-
-我们在展示一下如何创建柱状图。
-
-1. 点击蓝色的 `Create a visualization` 或 `+` 按钮。选择 `Vertical Bar`
-2. 选择索引模式。由于您尚未定义任何 bucket ,因此您会看到一个大栏,显示与默认通配符查询匹配的文档总数。
-3. 指定 Y 轴所代表的字段
-4. 指定 X 轴所代表的字段
-5. 点击  按钮来更新图表。
-
-
-
-完成后,如果想要保存这个图表,可以点击页面最上方一栏中的 `Save` 按钮。
-
-## Dashboard
-
-`Dashboard` 可以整合和共享 `Visualize` 集合。
-
-1. 点击侧面导航栏中的 Dashboard。
-2. 点击添加显示保存的可视化列表。
-3. 点击之前保存的 `Visualize`,然后点击列表底部的小向上箭头关闭可视化列表。
-4. 将鼠标悬停在可视化对象上会显示允许您编辑,移动,删除和调整可视化对象大小的容器控件。
diff --git a/os/tool/elastic/elastic-logstash.md b/os/tool/elastic/elastic-logstash.md
deleted file mode 100644
index 04daf562a7..0000000000
--- a/os/tool/elastic/elastic-logstash.md
+++ /dev/null
@@ -1,519 +0,0 @@
----
-title: Elastic 技术栈之 Logstash 基础
-date: 2017-12-26
-categories:
-- javatool
-tags:
-- java
-- javatool
-- log
-- elastic
----
-
-# Elastic 技术栈之 Logstash 基础
-
-> 本文是 Elastic 技术栈(ELK)的 Logstash 应用。
->
-> 如果不了解 Elastic 的安装、配置、部署,可以参考:[Elastic 技术栈之快速入门](https://github.com/dunwu/JavaStack/blob/master/docs/javatool/elastic/elastic-quickstart.md)
-
-## 简介
-
-Logstash 可以传输和处理你的日志、事务或其他数据。
-
-### 功能
-
-Logstash 是 Elasticsearch 的最佳数据管道。
-
-Logstash 是插件式管理模式,在输入、过滤、输出以及编码过程中都可以使用插件进行定制。Logstash 社区有超过 200 种可用插件。
-
-### 工作原理
-
-Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`filter`。
-
-这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。
-
-
-
-- input 负责从数据源采集数据。
-- filter 将数据修改为你指定的格式或内容。
-- output 将数据传输到目的地。
-
-在实际应用场景中,通常输入、输出、过滤器不止一个。Logstash 的这三个元素都使用插件式管理方式,用户可以根据应用需要,灵活的选用各阶段需要的插件,并组合使用。
-
-后面将对插件展开讲解,暂且不表。
-
-## 设置
-
-### 设置文件
-
-- **`logstash.yml`**:logstash 的默认启动配置文件
-- **`jvm.options`**:logstash 的 JVM 配置文件。
-- **`startup.options`** (Linux):包含系统安装脚本在 `/usr/share/logstash/bin` 中使用的选项为您的系统构建适当的启动脚本。安装 Logstash 软件包时,系统安装脚本将在安装过程结束时执行,并使用 `startup.options` 中指定的设置来设置用户,组,服务名称和服务描述等选项。
-
-
-### logstash.yml 设置项
-
-节选部分设置项,更多项请参考:https://www.elastic.co/guide/en/logstash/current/logstash-settings-file.html
-
-| 参数 | 描述 | 默认值 |
-| -------------------------- | ---------------------------------------- | ---------------------------------------- |
-| `node.name` | 节点名 | 机器的主机名 |
-| `path.data` | Logstash及其插件用于任何持久性需求的目录。 | `LOGSTASH_HOME/data` |
-| `pipeline.workers` | 同时执行管道的过滤器和输出阶段的工作任务数量。如果发现事件正在备份,或CPU未饱和,请考虑增加此数字以更好地利用机器处理能力。 | Number of the host’s CPU cores |
-| `pipeline.batch.size` | 尝试执行过滤器和输出之前,单个工作线程从输入收集的最大事件数量。较大的批量处理大小一般来说效率更高,但是以增加的内存开销为代价。您可能必须通过设置 `LS_HEAP_SIZE` 变量来有效使用该选项来增加JVM堆大小。 | `125` |
-| `pipeline.batch.delay` | 创建管道事件批处理时,在将一个尺寸过小的批次发送给管道工作任务之前,等待每个事件需要多长时间(毫秒)。 | `5` |
-| `pipeline.unsafe_shutdown` | 如果设置为true,则即使在内存中仍存在inflight事件时,也会强制Logstash在关闭期间退出。默认情况下,Logstash将拒绝退出,直到所有接收到的事件都被推送到输出。启用此选项可能会导致关机期间数据丢失。 | `false` |
-| `path.config` | 主管道的Logstash配置路径。如果您指定一个目录或通配符,配置文件将按字母顺序从目录中读取。 | Platform-specific. See [[dir-layout\]](https://github.com/elastic/logstash/blob/6.1/docs/static/settings-file.asciidoc#dir-layout). |
-| `config.string` | 包含用于主管道的管道配置的字符串。使用与配置文件相同的语法。 | None |
-| `config.test_and_exit` | 设置为true时,检查配置是否有效,然后退出。请注意,使用此设置不会检查grok模式的正确性。 Logstash可以从目录中读取多个配置文件。如果将此设置与log.level:debug结合使用,则Logstash将记录组合的配置文件,并注掉其源文件的配置块。 | `false` |
-| `config.reload.automatic` | 设置为true时,定期检查配置是否已更改,并在配置更改时重新加载配置。这也可以通过SIGHUP信号手动触发。 | `false` |
-| `config.reload.interval` | Logstash 检查配置文件更改的时间间隔。 | `3s` |
-| `config.debug` | 设置为true时,将完全编译的配置显示为调试日志消息。您还必须设置`log.level:debug`。警告:日志消息将包括任何传递给插件配置作为明文的“密码”选项,并可能导致明文密码出现在您的日志! | `false` |
-| `config.support_escapes` | 当设置为true时,带引号的字符串将处理转义字符。 | `false` |
-| `modules` | 配置时,模块必须处于上表所述的嵌套YAML结构中。 | None |
-| `http.host` | 绑定地址 | `"127.0.0.1"` |
-| `http.port` | 绑定端口 | `9600` |
-| `log.level` | 日志级别。有效选项:fatal > error > warn > info > debug > trace | `info` |
-| `log.format` | 日志格式。json (JSON 格式)或 plain (原对象) | `plain` |
-| `path.logs` | Logstash 自身日志的存储路径 | `LOGSTASH_HOME/logs` |
-| `path.plugins` | 在哪里可以找到自定义的插件。您可以多次指定此设置以包含多个路径。 | |
-
-## 启动
-
-### 命令行
-
-通过命令行启动 logstash 的方式如下:
-
-```
-bin/logstash [options]
-```
-
-其中 [options] 是您可以指定用于控制 Logstash 执行的命令行标志。
-
-在命令行上设置的任何标志都会覆盖 Logstash 设置文件(`logstash.yml`)中的相应设置,但设置文件本身不会更改。
-
-> **注**
->
-> 虽然可以通过指定命令行参数的方式,来控制 logstash 的运行方式,但显然这么做很麻烦。
->
-> 建议通过指定配置文件的方式,来控制 logstash 运行,启动命令如下:
->
-> ```
-> bin/logstash -f logstash.conf
-> ```
-> 若想了解更多的命令行参数细节,请参考:https://www.elastic.co/guide/en/logstash/current/running-logstash-command-line.html
->
-
-### 配置文件
-
-上节,我们了解到,logstash 可以执行 `bin/logstash -f logstash.conf` ,按照配置文件中的参数去覆盖默认设置文件(`logstash.yml`)中的设置。
-
-这节,我们就来学习一下这个配置文件如何配置参数。
-
-#### 配置文件结构
-
-在工作原理一节中,我们已经知道了 Logstash 主要有三个工作阶段 input 、filter、output。而 logstash 配置文件文件结构也与之相对应:
-
-```
-input {}
-
-filter {}
-
-output {}
-```
-
-> 每个部分都包含一个或多个插件的配置选项。如果指定了多个过滤器,则会按照它们在配置文件中的显示顺序应用它们。
-
-#### 插件配置
-
-插件的配置由插件名称和插件的一个设置块组成。
-
-下面的例子中配置了两个输入文件配置:
-
-```
-input {
- file {
- path => "/var/log/messages"
- type => "syslog"
- }
-
- file {
- path => "/var/log/apache/access.log"
- type => "apache"
- }
-}
-```
-
-您可以配置的设置因插件类型而异。你可以参考: [Input Plugins](https://www.elastic.co/guide/en/logstash/current/input-plugins.html), [Output Plugins](https://www.elastic.co/guide/en/logstash/current/output-plugins.html), [Filter Plugins](https://www.elastic.co/guide/en/logstash/current/filter-plugins.html), 和 [Codec Plugins](https://www.elastic.co/guide/en/logstash/current/codec-plugins.html) 。
-
-#### 值类型
-
-一个插件可以要求设置的值是一个特定的类型,比如布尔值,列表或哈希值。以下值类型受支持。
-
-- Array
-
-```
- users => [ {id => 1, name => bob}, {id => 2, name => jane} ]
-```
-
-- Lists
-
-```
- path => [ "/var/log/messages", "/var/log/*.log" ]
- uris => [ "http://elastic.co", "http://example.net" ]
-```
-
-- Boolean
-
-```
- ssl_enable => true
-```
-
-- Bytes
-
-```
- my_bytes => "1113" # 1113 bytes
- my_bytes => "10MiB" # 10485760 bytes
- my_bytes => "100kib" # 102400 bytes
- my_bytes => "180 mb" # 180000000 bytes
-```
-
-- Codec
-
-```
- codec => "json"
-```
-
-- Hash
-
-```
-match => {
- "field1" => "value1"
- "field2" => "value2"
- ...
-}
-```
-
-- Number
-
-```
- port => 33
-```
-
-- Password
-
-```
- my_password => "password"
-```
-
-- URI
-
-```
- my_uri => "http://foo:bar@example.net"
-```
-
-- Path
-
-```
- my_path => "/tmp/logstash"
-```
-
-- String
-
-
-- 转义字符
-
-## 插件
-
-### input
-
-> Logstash 支持各种输入选择 ,可以在同一时间从众多常用来源捕捉事件。能够以连续的流式传输方式,轻松地从您的日志、指标、Web 应用、数据存储以及各种 AWS 服务采集数据。
-
-#### 常用 input 插件
-
-- **file**:从文件系统上的文件读取,就像UNIX命令 `tail -0F` 一样
-- **syslog:**在众所周知的端口514上侦听系统日志消息,并根据RFC3164格式进行解析
-- **redis:**从redis服务器读取,使用redis通道和redis列表。 Redis经常用作集中式Logstash安装中的“代理”,它将来自远程Logstash“托运人”的Logstash事件排队。
-- **beats:**处理由Filebeat发送的事件。
-
-更多详情请见:[Input Plugins](https://www.elastic.co/guide/en/logstash/current/input-plugins.html)
-
-### filter
-
-> 过滤器是Logstash管道中的中间处理设备。如果符合特定条件,您可以将条件过滤器组合在一起,对事件执行操作。
-
-#### 常用 filter 插件
-
-- **grok:**解析和结构任意文本。 Grok目前是Logstash中将非结构化日志数据解析为结构化和可查询的最佳方法。
-- **mutate:**对事件字段执行一般转换。您可以重命名,删除,替换和修改事件中的字段。
-
-- **drop:**完全放弃一个事件,例如调试事件。
-
-- **clone:**制作一个事件的副本,可能会添加或删除字段。
-
-- **geoip:**添加有关IP地址的地理位置的信息(也可以在Kibana中显示惊人的图表!)
-
-
-更多详情请见:[Filter Plugins](https://www.elastic.co/guide/en/logstash/current/filter-plugins.html)
-
-### output
-
-> 输出是Logstash管道的最后阶段。一个事件可以通过多个输出,但是一旦所有输出处理完成,事件就完成了执行。
-
-#### 常用 output 插件
-
-- **elasticsearch:**将事件数据发送给 Elasticsearch(推荐模式)。
-- **file:**将事件数据写入文件或磁盘。
-- **graphite:**将事件数据发送给 graphite(一个流行的开源工具,存储和绘制指标。 http://graphite.readthedocs.io/en/latest/)。
-- **statsd:**将事件数据发送到 statsd (这是一种侦听统计数据的服务,如计数器和定时器,通过UDP发送并将聚合发送到一个或多个可插入的后端服务)。
-
-更多详情请见:[Output Plugins](https://www.elastic.co/guide/en/logstash/current/output-plugins.html)
-
-### codec
-
-用于格式化对应的内容。
-
-#### 常用 codec 插件
-
-- **json:**以JSON格式对数据进行编码或解码。
-- **multiline:**将多行文本事件(如java异常和堆栈跟踪消息)合并为单个事件。
-
-更多插件请见:[Codec Plugins](https://www.elastic.co/guide/en/logstash/current/codec-plugins.html)
-
-## 实战
-
-前面的内容都是对 Logstash 的介绍和原理说明。接下来,我们来实战一些常见的应用场景。
-
-### 传输控制台数据
-
-> stdin input 插件从标准输入读取事件。这是最简单的 input 插件,一般用于测试场景。
->
-
-**应用**
-
-(1)创建 `logstash-input-stdin.conf` :
-
-```
-input { stdin { } }
-output {
- elasticsearch { hosts => ["localhost:9200"] }
- stdout { codec => rubydebug }
-}
-```
-
-更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-stdin.html
-
-(2)执行 logstash,使用 `-f` 来指定你的配置文件:
-
-```
-bin/logstash -f logstash-input-stdin.conf
-```
-
-### 传输 logback 日志
-
-> elk 默认使用的 Java 日志工具是 log4j2 ,并不支持 logback 和 log4j。
->
-> 想使用 logback + logstash ,可以使用 [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) 。[logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder) 提供了 UDP / TCP / 异步方式来传输日志数据到 logstash。
->
-> 如果你使用的是 log4j ,也不是不可以用这种方式,只要引入桥接 jar 包即可。如果你对 log4j 、logback ,或是桥接 jar 包不太了解,可以参考我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。
-
-#### TCP 应用
-
-1. logstash 配置
-
- (1)创建 `logstash-input-tcp.conf` :
-
-```
-input {
-tcp {
- port => 9251
- codec => json_lines
- mode => server
-}
-}
-output {
- elasticsearch { hosts => ["localhost:9200"] }
- stdout { codec => rubydebug }
-}
-```
-
- 更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-tcp.html
-
- (2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-udp.conf`
-
-
-2. java 应用配置
-
- (1)在 Java 应用的 pom.xml 中引入 jar 包:
-
-```xml
-
- net.logstash.logback
- logstash-logback-encoder
- 4.11
-
-
-
-
- ch.qos.logback
- logback-core
- 1.2.3
-
-
- ch.qos.logback
- logback-classic
- 1.2.3
-
-
- ch.qos.logback
- logback-access
- 1.2.3
-
-```
-
- (2)接着,在 logback.xml 中添加 appender
-
-```xml
-
-
- 192.168.28.32:9251
-
-
-
-
-
-```
-
- (3)接下来,就是 logback 的具体使用 ,如果对此不了解,不妨参考一下我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。
-
- **实例:**[我的logback.xml](https://github.com/dunwu/JavaStack/blob/master/codes/javatool/src/main/resources/logback.xml)
-
-#### UDP 应用
-
-UDP 和 TCP 的使用方式大同小异。
-
-1. logstash 配置
-
- (1)创建 `logstash-input-udp.conf` :
-
-```
-input {
-udp {
- port => 9250
- codec => json
-}
-}
-output {
- elasticsearch { hosts => ["localhost:9200"] }
- stdout { codec => rubydebug }
-}
-```
-
- 更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-udp.html
-
- (2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-udp.conf`
-
-
-2. java 应用配置
-
- (1)在 Java 应用的 pom.xml 中引入 jar 包:
-
- 与 **TCP 应用** 一节中的引入依赖包完全相同。
-
- (2)接着,在 logback.xml 中添加 appender
-
- ```xml
-
- 192.168.28.32
- 9250
-
-
-
-
- ```
-
- (3)接下来,就是 logback 的具体使用 ,如果对此不了解,不妨参考一下我的这篇博文:[细说 Java 主流日志工具库](https://github.com/dunwu/JavaStack/blob/master/docs/javalib/java-log.md) 。
-
- **实例:**[我的logback.xml](https://github.com/dunwu/JavaStack/blob/master/codes/javatool/src/main/resources/logback.xml)
-
-### 传输文件
-
-> 在 Java Web 领域,需要用到一些重要的工具,例如 Tomcat 、Nginx 、Mysql 等。这些不属于业务应用,但是它们的日志数据对于定位问题、分析统计同样很重要。这时无法使用 logback 方式将它们的日志传输到 logstash。
->
-> 如何采集这些日志文件呢?别急,你可以使用 logstash 的 file input 插件。
->
-> 需要注意的是,传输文件这种方式,必须在日志所在的机器上部署 logstash 。
-
-**应用**
-
-logstash 配置
-
-(1)创建 `logstash-input-file.conf` :
-
-```
-input {
- file {
- path => ["/var/log/nginx/access.log"]
- type => "nginx-access-log"
- start_position => "beginning"
- }
-}
-
-output {
- if [type] == "nginx-access-log" {
- elasticsearch {
- hosts => ["localhost:9200"]
- index => "nginx-access-log"
- }
- }
-}
-```
-
-(2)执行 logstash,使用 `-f` 来指定你的配置文件:`bin/logstash -f logstash-input-file.conf`
-
-更多配置项可以参考:https://www.elastic.co/guide/en/logstash/current/plugins-inputs-file.html
-
-## 小技巧
-
-### 启动、终止应用
-
-如果你的 logstash 每次都是通过指定配置文件方式启动。不妨建立一个启动脚本。
-
-```
-# cd xxx 进入 logstash 安装目录下的 bin 目录
-logstash -f logstash.conf
-```
-
-如果你的 logstash 运行在 linux 系统下,不妨使用 nohup 来启动一个守护进程。这样做的好处在于,即使关闭终端,应用仍会运行。
-
-**创建 startup.sh**
-
-```
-nohup ./logstash -f logstash.conf >> nohup.out 2>&1 &
-```
-
-终止应用没有什么好方法,你只能使用 ps -ef | grep logstash ,查出进程,将其kill 。不过,我们可以写一个脚本来干这件事:
-
-**创建 shutdown.sh**
-
-脚本不多解释,请自行领会作用。
-
-```
-PID=`ps -ef | grep logstash | awk '{ print $2}' | head -n 1`
-kill -9 ${PID}
-```
-
-## 资料
-
-- [Logstash 官方文档](https://www.elastic.co/guide/en/logstash/current/index.html)
-- [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder)
-- [ELK Stack权威指南](https://github.com/chenryn/logstash-best-practice-cn)
-- [ELK(Elasticsearch、Logstash、Kibana)安装和配置](https://github.com/judasn/Linux-Tutorial/blob/master/ELK-Install-And-Settings.md)
-
-## 推荐阅读
-
-- [Elastic 技术栈](https://github.com/dunwu/JavaStack/blob/master/docs/javatool/elastic/README.md)
-- [JavaStack](https://github.com/dunwu/JavaStack)
diff --git a/os/tool/elastic/elastic-quickstart.md b/os/tool/elastic/elastic-quickstart.md
deleted file mode 100644
index 0e3f3250f3..0000000000
--- a/os/tool/elastic/elastic-quickstart.md
+++ /dev/null
@@ -1,289 +0,0 @@
----
-title: Elastic 技术栈之快速入门
-date: 2017-12-06
-categories:
-- javatool
-tags:
-- java
-- javatool
-- log
-- elastic
----
-
-# Elastic 技术栈之快速入门
-
-## 概念
-
-### ELK 是什么
-
-ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/products/elasticsearch) 、[Logstash](https://www.elastic.co/products/logstash) 、[Kibana](https://www.elastic.co/products/kibana) 的首字母组合。
-
-[ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 [Lucene](http://lucene.apache.org/core/documentation.html) 构建的开源,分布式,RESTful 搜索引擎。
-
-[Logstash](https://www.elastic.co/products/logstash) 传输和处理你的日志、事务或其他数据。
-
-[Kibana](https://www.elastic.co/products/kibana) 将 Elasticsearch 的数据分析并渲染为可视化的报表。
-
-### 为什么使用 ELK ?
-
-对于有一定规模的公司来说,通常会很多个应用,并部署在大量的服务器上。运维和开发人员常常需要通过查看日志来定位问题。如果应用是集群化部署,试想如果登录一台台服务器去查看日志,是多么费时费力。
-
-而通过 ELK 这套解决方案,可以同时实现日志收集、日志搜索和日志分析的功能。
-
-### Elastic 架构
-
-
-
-> **说明**
->
-> 以上是 ELK 技术栈的一个架构图。从图中可以清楚的看到数据流向。
->
-> [Beats](https://www.elastic.co/products/beats) 是单一用途的数据传输平台,它可以将多台机器的数据发送到 Logstash 或 ElasticSearch。但 Beats 并不是不可或缺的一环,所以本文中暂不介绍。
->
-> [Logstash](https://www.elastic.co/products/logstash) 是一个动态数据收集管道。支持以 TCP/UDP/HTTP 多种方式收集数据(也可以接受 Beats 传输来的数据),并对数据做进一步丰富或提取字段处理。
->
-> [ElasticSearch](https://www.elastic.co/products/elasticsearch) 是一个基于 JSON 的分布式的搜索和分析引擎。作为 ELK 的核心,它集中存储数据。
->
-> [Kibana](https://www.elastic.co/products/kibana) 是 ELK 的用户界面。它将收集的数据进行可视化展示(各种报表、图形化数据),并提供配置、管理 ELK 的界面。
-
-## 安装
-
-### 准备
-
-ELK 要求本地环境中安装了 JDK 。如果不确定是否已安装,可使用下面的命令检查:
-
-```sh
-java -version
-```
-
-> **注意**
->
-> 本文使用的 ELK 是 6.0.0,要求 jdk 版本不低于 JDK8。
->
-> 友情提示:安装 ELK 时,三个应用请选择统一的版本,避免出现一些莫名其妙的问题。例如:由于版本不统一,导致三个应用间的通讯异常。
-
-### Elasticsearch
-
-安装步骤如下:
-
-1. [elasticsearch 官方下载地址](https://www.elastic.co/downloads/elasticsearch)下载所需版本包并解压到本地。
-2. 运行 `bin/elasticsearch` (Windows 上运行 `bin\elasticsearch.bat`)
-3. 验证运行成功:linux 上可以执行 `curl http://localhost:9200/` ;windows 上可以用访问 REST 接口的方式来访问 http://localhost:9200/
-
-> **说明**
->
-> Linux 上可以执行下面的命令来下载压缩包:
->
-> ```
-> curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0.tar.gz
-> ```
->
-> Mac 上可以执行以下命令来进行安装:
->
-> ```
-> brew install elasticsearch
-> ```
->
-> Windows 上可以选择 MSI 可执行安装程序,将应用安装到本地。
-
-### Logstash
-
-安装步骤如下:
-
-1. 在 [logstash 官方下载地址](https://www.elastic.co/downloads/logstash)下载所需版本包并解压到本地。
-
-2. 添加一个 `logstash.conf` 文件,指定要使用的插件以及每个插件的设置。举个简单的例子:
-
- ```
- input { stdin { } }
- output {
- elasticsearch { hosts => ["localhost:9200"] }
- stdout { codec => rubydebug }
- }
- ```
-
-3. 运行 `bin/logstash -f logstash.conf` (Windows 上运行`bin/logstash.bat -f logstash.conf`)
-
-### Kibana
-
-安装步骤如下:
-
-1. 在 [kibana 官方下载地址](https://www.elastic.co/downloads/kibana)下载所需版本包并解压到本地。
-2. 修改 `config/kibana.yml` 配置文件,设置 `elasticsearch.url` 指向 Elasticsearch 实例。
-3. 运行 `bin/kibana` (Windows 上运行 `bin\kibana.bat`)
-4. 在浏览器上访问 http://localhost:5601
-
-### 安装 FAQ
-
-#### elasticsearch 不允许以 root 权限来运行
-
-**问题:**在 Linux 环境中,elasticsearch 不允许以 root 权限来运行。
-
-如果以 root 身份运行 elasticsearch,会提示这样的错误:
-
-```
-can not run elasticsearch as root
-```
-
-**解决方法:**使用非 root 权限账号运行 elasticsearch
-
-```sh
-# 创建用户组
-groupadd elk
-# 创建新用户,-g elk 设置其用户组为 elk,-p elk 设置其密码为 elk
-useradd elk -g elk -p elk
-# 更改 /opt 文件夹及内部文件的所属用户及组为 elk:elk
-chown -R elk:elk /opt # 假设你的 elasticsearch 安装在 opt 目录下
-# 切换账号
-su elk
-```
-
-#### vm.max_map_count 不低于 262144
-
-**问题:**`vm.max_map_count` 表示虚拟内存大小,它是一个内核参数。elasticsearch 默认要求 `vm.max_map_count` 不低于 262144。
-
-```
-max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
-```
-
-**解决方法:**
-
-你可以执行以下命令,设置 `vm.max_map_count` ,但是重启后又会恢复为原值。
-
-```
-sysctl -w vm.max_map_count=262144
-```
-
-持久性的做法是在 `/etc/sysctl.conf` 文件中修改 `vm.max_map_count` 参数:
-
-```
-echo "vm.max_map_count=262144" > /etc/sysctl.conf
-sysctl -p
-```
-
-> **注意**
->
-> 如果运行环境为 docker 容器,可能会限制执行 sysctl 来修改内核参数。
->
-> 这种情况下,你只能选择直接修改宿主机上的参数了。
-
-#### nofile 不低于 65536
-
-**问题:** `nofile` 表示进程允许打开的最大文件数。elasticsearch 进程要求可以打开的最大文件数不低于 65536。
-
-```
-max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
-```
-
-**解决方法:**
-
-在 `/etc/security/limits.conf` 文件中修改 `nofile` 参数:
-
-```
-echo "* soft nofile 65536" > /etc/security/limits.conf
-echo "* hard nofile 131072" > /etc/security/limits.conf
-```
-
-#### nproc 不低于 2048
-
-**问题:** `nproc` 表示最大线程数。elasticsearch 要求最大线程数不低于 2048。
-
-```
-max number of threads [1024] for user [user] is too low, increase to at least [2048]
-```
-
-**解决方法:**
-
-在 `/etc/security/limits.conf` 文件中修改 `nproc` 参数:
-
-```
-echo "* soft nproc 2048" > /etc/security/limits.conf
-echo "* hard nproc 4096" > /etc/security/limits.conf
-```
-
-#### Kibana No Default Index Pattern Warning
-
-**问题:**安装 ELK 后,访问 kibana 页面时,提示以下错误信息:
-
-```
-Warning No default index pattern. You must select or create one to continue.
-...
-Unable to fetch mapping. Do you have indices matching the pattern?
-```
-
-这就说明 logstash 没有把日志写入到 elasticsearch。
-
-**解决方法:**
-
-检查 logstash 与 elasticsearch 之间的通讯是否有问题,一般问题就出在这。
-
-## 使用
-
-本人使用的 Java 日志方案为 slf4j + logback,所以这里以 logback 来讲解。
-
-### Java 应用输出日志到 ELK
-
-**修改 logstash.conf 配置**
-
-首先,我们需要修改一下 logstash 服务端 logstash.conf 中的配置
-
-```
-input {
- # stdin { }
- tcp {
- # host:port就是上面appender中的 destination,
- # 这里其实把logstash作为服务,开启9250端口接收logback发出的消息
- host => "127.0.0.1" port => 9250 mode => "server" tags => ["tags"] codec => json_lines
- }
-}
-output {
- elasticsearch { hosts => ["localhost:9200"] }
- stdout { codec => rubydebug }
-}
-```
-
-> **说明**
->
-> 这个 input 中的配置其实是 logstash 服务端监听 9250 端口,接收传递来的日志数据。
-
-然后,在 Java 应用的 pom.xml 中引入 jar 包:
-
-```xml
-
- net.logstash.logback
- logstash-logback-encoder
- 4.11
-
-```
-
-接着,在 logback.xml 中添加 appender
-
-```xml
-
-
- 127.0.0.1:9250
-
-
-
-
-
-```
-
-大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。
-
-
-
-## 资料
-
-- [elastic 官方文档](https://www.elastic.co/guide/index.html)
-
-- [elasticsearch github](https://github.com/elastic/elasticsearch)
-
-- [logstash github](https://github.com/elastic/logstash)
-
-- [kibana github](https://github.com/elastic/kibana)
-
-
diff --git a/os/tool/gitlab/gitlab-ci.md b/os/tool/gitlab/gitlab-ci.md
deleted file mode 100644
index ccc7da295e..0000000000
--- a/os/tool/gitlab/gitlab-ci.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Gitlab
-
-
diff --git a/os/tool/gitlab/gitlab-install.md b/os/tool/gitlab/gitlab-install.md
deleted file mode 100644
index 7e42747020..0000000000
--- a/os/tool/gitlab/gitlab-install.md
+++ /dev/null
@@ -1,299 +0,0 @@
-# Gitlab 安装
-
-> 环境:
->
-> OS: CentOS7
-
-
-
-- [安装 gitlab](#安装-gitlab)
- - [常规安装 gitlab](#常规安装-gitlab)
- - [Docker 安装 gitlab](#docker-安装-gitlab)
-- [安装 gitlab-ci-multi-runner](#安装-gitlab-ci-multi-runner)
- - [常规安装 gitlab-ci-multi-runner](#常规安装-gitlab-ci-multi-runner)
- - [Docker 安装 gitlab-ci-multi-runner](#docker-安装-gitlab-ci-multi-runner)
-- [自签名证书](#自签名证书)
- - [创建证书](#创建证书)
-- [gitlab 配置](#gitlab-配置)
-- [引申和引用](#引申和引用)
-
-
-
-## 安装 gitlab
-
-### 常规安装 gitlab
-
-进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。
-
-
-
-以 CentOS7 为例:
-
-#### 安装和配置必要依赖
-
-在系统防火墙中启用 HTTP 和 SSH
-
-```
-sudo yum install -y curl policycoreutils-python openssh-server
-sudo systemctl enable sshd
-sudo systemctl start sshd
-sudo firewall-cmd --permanent --add-service=http
-sudo systemctl reload firewalld
-```
-
-安装 Postfix ,使得 Gitlab 可以发送通知邮件
-
-```
-sudo yum install postfix
-sudo systemctl enable postfix
-sudo systemctl start postfix
-```
-
-#### 添加 Gitlab yum 仓库并安装包
-
-添加 Gitlab yum 仓库
-
-```
-curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
-```
-
-通过 yum 安装 gitlab-ce
-
-```
-sudo EXTERNAL_URL="http://gitlab.example.com" yum install -y gitlab-ce
-```
-
-安装完成后,即可通过默认的 root 账户进行登录。更多细节可以参考:[documentation for detailed instructions on installing and configuration](https://docs.gitlab.com/omnibus/README.html#installation-and-configuration-using-omnibus-package)
-
-### Docker 安装 gitlab
-
-拉取镜像
-
-```
-docker pull docker.io/gitlab/gitlab-ce
-```
-
-启动
-
-```
-docker run -d \
- --hostname gitlab.zp.io \
- --publish 8443:443 --publish 80:80 --publish 2222:22 \
- --name gitlab \
- --restart always \
- --volume $GITLAB_HOME/config:/etc/gitlab \
- --volume $GITLAB_HOME/logs:/var/log/gitlab \
- --volume $GITLAB_HOME/data:/var/opt/gitlab \
- gitlab/gitlab-ce
-```
-
-
-
-## 安装 gitlab-ci-multi-runner
-
-> 参考:https://docs.gitlab.com/runner/install/
-
-### 常规安装 gitlab-ci-multi-runner
-
-#### 下载
-
-```
-sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
-```
-
-#### 配置执行权限
-
-```
-sudo chmod +x /usr/local/bin/gitlab-runner
-```
-
-#### 如果想使用 Docker,安装 Docker(可选的)
-
-```
-curl -sSL https://get.docker.com/ | sh
-```
-
-#### 创建 CI 用户
-
-```
-sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
-```
-
-#### 安装并启动服务
-
-```
-sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
-sudo gitlab-runner start
-```
-
-#### 注册 Runner
-
-(1)执行命令:
-
-```
-sudo gitlab-runner register
-```
-
-(2)输入 Gitlab URL 和 令牌
-
-URL 和令牌信息在 Gitlab 的 Runner 管理页面获取:
-
-
-
-```
-Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
-https://gitlab.com
-
-Please enter the gitlab-ci token for this runner
-xxx
-```
-
-(3)输入 Runner 的描述
-
-```
- Please enter the gitlab-ci description for this runner
- [hostame] my-runner
-```
-
-(4)输入 Runner 相关的标签
-
-```
- Please enter the gitlab-ci tags for this runner (comma separated):
- my-tag,another-tag
-```
-
-(5)输入 Runner 执行器
-
-```
- Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
- docker
-```
-
-如果想选择 Docker 作为执行器,你需要指定默认镜像( `.gitlab-ci.yml` 中没有此配置)
-
-```
- Please enter the Docker image (eg. ruby:2.1):
- alpine:latest
-```
-
-### Docker 安装 gitlab-ci-multi-runner
-
-拉取镜像
-
-```
-docker pull docker.io/gitlab/gitlab-runner
-```
-
-启动
-
-```
-docker run -d --name gitlab-runner --restart always \
- -v /srv/gitlab-runner/config:/etc/gitlab-runner \
- -v /var/run/docker.sock:/var/run/docker.sock \
- gitlab/gitlab-runner:latest
-```
-
-## 自签名证书
-
-首先,创建认证目录
-
-```
-sudo mkdir -p /etc/gitlab/ssl
-sudo chmod 700 /etc/gitlab/ssl
-```
-
-### 创建证书
-
-#### 创建 Private Key
-
-```
-sudo openssl genrsa -des3 -out /etc/gitlab/ssl/gitlab.domain.com.key 2048
-```
-
-会提示输入密码,请记住
-
-#### 生成 Certificate Request
-
-```
-sudo openssl req -new -key /etc/gitlab/ssl/gitlab.domain.com.key -out /etc/gitlab/ssl/gitlab.domain.com.csr
-```
-
-根据提示,输入信息
-
-```
-Country Name (2 letter code) [XX]:CN
-State or Province Name (full name) []:JS
-Locality Name (eg, city) [Default City]:NJ
-Organization Name (eg, company) [Default Company Ltd]:xxxxx
-Organizational Unit Name (eg, section) []:
-Common Name (eg, your name or your server's hostname) []:gitlab.xxxx.io
-Email Address []:
-
-Please enter the following 'extra' attributes
-to be sent with your certificate request
-A challenge password []:
-An optional company name []:
-```
-
-#### 移除 Private Key 中的密码短语
-
-```
-sudo cp -v /etc/gitlab/ssl/gitlab.domain.com.{key,original}
-sudo openssl rsa -in /etc/gitlab/ssl/gitlab.domain.com.original -out /etc/gitlab/ssl/gitlab.domain.com.key
-sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.original
-```
-
-#### 创建证书
-
-```
-sudo openssl x509 -req -days 1460 -in /etc/gitlab/ssl/gitlab.domain.com.csr -signkey /etc/gitlab/ssl/gitlab.domain.com.key -out /etc/gitlab/ssl/gitlab.domain.com.crt
-```
-
-#### 移除证书请求文件
-
-```
-sudo rm -v /etc/gitlab/ssl/gitlab.domain.com.csr
-```
-
-#### 设置文件权限
-
-```
-sudo chmod 600 /etc/gitlab/ssl/gitlab.domain.com.*
-```
-
-## gitlab 配置
-
-```
-sudo vim /etc/gitlab/gitlab.rb
-external_url 'https://gitlab.domain.com'
-```
-
-#### gitlab 网站 https:
-
-```
-nginx['redirect_http_to_https'] = true
-```
-
-#### gitlab ci 网站 https:
-
-```
-ci_nginx['redirect_http_to_https'] = true
-```
-
-#### 复制证书到 gitlab 目录:
-
-```
-sudo cp /etc/gitlab/ssl/gitlab.domain.com.crt /etc/gitlab/trusted-certs/
-```
-
-#### gitlab 重新配置+更新:
-
-```
-sudo gitlab-ctl reconfigure
-sudo gitlab-ctl restart
-```
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
diff --git a/os/tool/gitlab/gitlab-quickstart.md b/os/tool/gitlab/gitlab-quickstart.md
deleted file mode 100644
index 9ce9acb12f..0000000000
--- a/os/tool/gitlab/gitlab-quickstart.md
+++ /dev/null
@@ -1,76 +0,0 @@
-# Gitlab 快速教程
-
-> 准备
->
-> Git - 如果不熟悉 Git ,可以先阅读:[Git 教程](https://github.com/dunwu/OS/tree/master/docs/git)
-
-## 创建你的 SSH key
-
-1. 使用 Gitlab 的第一步是生成你自己的 SSH 密钥对(Github 也类似)。
-
-2. 登录 Gitlab
-
-3. 打开 **Profile settings**.
-
- 
-
-4. 跳转到 **SSH keys** tab 页
-
- 
-
-5. 黏贴你的 SSH 公钥内容到 Key 文本框
-
- 
-
-6. 为了便于识别,你可以为其命名
-
- 
-
-7. 点击 **Add key** 将 SSH 公钥添加到 GitLab
-
- 
-
-## 创建项目
-
-
-
-输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。
-
-
-
-## 克隆项目到本地
-
-可以选择 SSH 或 HTTPS 方式克隆项目到本地(推荐 SSH)
-
-拷贝项目地址,然后在本地执行 `git clone `
-
-
-
-## 创建 Issue
-
-依次点击 **Project’s Dashboard** > **Issues** > **New Issue** 可以新建 Issue
-
-
-
-在项目中直接添加 issue
-
-
-
-在未关闭 issue 中,点击 **New Issue** 添加 issue
-
-
-
-通过项目面板添加 issue
-
-
-
-通过 issue 面板添加 issue
-
-
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
-- **引用**
- - [官网](https://about.gitlab.com/)
diff --git a/os/tool/jdk.md b/os/tool/jdk.md
deleted file mode 100644
index 73ff2aeffc..0000000000
--- a/os/tool/jdk.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# JDK 安装
-
-> 关键词:JDK, JAVA_HOME, CLASSPATH, PATH
-
-
-
-- [JDK 安装步骤](#jdk-安装步骤)
-- [Windows 系统安装方法](#windows-系统安装方法)
-- [Linux 系统安装方法](#linux-系统安装方法)
- - [RedHat 发行版本使用 rpm 安装方法](#redhat-发行版本使用-rpm-安装方法)
-- [参考资料](#参考资料)
-
-
-
-## JDK 安装步骤
-
-JDK 安装步骤:
-
-(1)下载 JDK
-
-a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/javase/downloads/index.html);
-
-b. 选择需要的版本:
-
-
-
-c. 选择对应操作系统的安装包:
-
-Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。
-
-
-
-(2)运行安装包,按提示逐步安装
-
-(3)配置系统环境变量:`JAVA_HOME`, `CLASSPATH`, `PATH`
-
-(4)验证 Java 是否安装成功
-
-## Windows 系统安装方法
-
-(1)下载 JDK
-
-需要根据 Windows 系统实际情况,选择 exe 安装文件:
-
-- 32 位计算机选择 Windows x86
-- 64 位计算机选择 Windows x64
-
-(2)运行安装包,按提示逐步安装
-
-(3)配置系统环境变量
-
-a. 安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置";
-
-
-
-b. 选择"高级"选项卡,点击"环境变量";
-
-
-
-然后就会出现如下图所示的画面:
-
-在"系统变量"中设置 3 项属性,JAVA_HOME,PATH,CLASSPATH(大小写无所谓),若已存在则点击"编辑",不存在则点击"新建"。
-
-变量设置参数如下:
-
-- 变量名:**JAVA_HOME**
-- 变量值:**C:\Program Files (x86)\Java\jdk1.8.0_91** // 要根据自己的实际路径配置
-
-- 变量名:**CLASSPATH**
-- 变量值:**.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;** //记得前面有个"."
-
-- 变量名:**Path**
-- 变量值:**%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;**
-
-(4)验证 Java 是否安装成功
-
-a. "开始"->"运行",键入"cmd";
-
-b. 键入命令: **java -version**、**java**、**javac** 几个命令,出现以下信息,说明环境变量配置成功;
-
-
-
-## Linux 系统安装方法
-
-(1)下载 JDK
-
-需要根据 Linux 系统实际情况,选择 tar.gz 压缩包:
-
-- 32 位计算机选择 Linux x86
-- 64 位计算机选择 Linux x64
-
-(2)解压压缩包到本地
-
-```sh
-$ tar -zxf jdk-8u162-linux-x64.tar.gz
-```
-
-(3)配置系统环境变量
-
-执行 `/etc/profile` 命令,添加以下内容:
-
-```sh
-# JDK 的根路径
-export JAVA_HOME=/opt/java/jdk1.8.0_162
-export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
-export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
-```
-
-执行 `source /etc/profile` ,立即生效
-
-(4)验证 Java 是否安装成功
-
-执行 `java -version` 命令,验证安装是否成功。
-
-### RedHat 发行版本使用 rpm 安装方法
-
-(1)下载 JDK
-
-下载 rpm 安装包
-
-(2)选择一个合适的版本安装
-
-```sh
-$ rpm -ivh jdk-8u181-linux-x64.rpm
-```
-
-安装成功后,默认安装路径在 `/usr/local` 下:
-
-(3)设置环境变量,同压缩包安装。
-
-(4)检验是否安装成功,执行 `java -version` 命令
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
-- **引用**
-- http://www.runoob.com/java/java-environment-setup.html
-- https://blog.csdn.net/deliciousion/article/details/78046007
diff --git a/os/tool/jenkins.md b/os/tool/jenkins.md
deleted file mode 100644
index 83067af8dd..0000000000
--- a/os/tool/jenkins.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# Jenkins 安装
-
-> 环境要求
->
-> - JDK:JDK7+,官网推荐是 JDK 8
-
-
-
-- [安装](#安装)
-- [启动](#启动)
-- [脚本](#脚本)
-- [FAQ](#faq)
- - [登录密码](#登录密码)
- - [忘记密码](#忘记密码)
- - [卡在 check 页面](#卡在-check-页面)
- - [卡在 getting startted 页面](#卡在-getting-startted-页面)
-- [参考资料](#参考资料)
-
-
-
-## 安装
-
-安装步骤如下:
-
-(1)下载并解压到本地
-
-进入官网下载地址:https://jenkins.io/download/ ,选择合适的版本下载。
-
-我选择的是最新稳定 war 版本 2.89.4:http://mirrors.jenkins.io/war-stable/latest/jenkins.war
-
-我个人喜欢存放在:`/opt/software/jenkins`
-
-```
-mkdir -p /opt/software/jenkins
-wget -O /opt/software/jenkins/jenkins.war http://mirrors.jenkins.io/war-stable/latest/jenkins.war
-```
-
-## 启动
-
-如果你和我一样,选择 war 版本,那么你可以将 war 移到 Tomcat 的 webapps 目录下,通过 Tomcat 来启动。
-
-当然,也可以通过 `java -jar` 方式来启动。
-
-**启动 jenkins 服务**
-
-```
-cd /opt/software/jenkins
-nohup java -jar jenkins.war --httpPort=8080 >> nohup.out 2>&1 &
-```
-
-## 脚本
-
-| [安装脚本](https://github.com/dunwu/OS/tree/master/codes/deploy/tool/jenkins) |
-
-## FAQ
-
-### 登录密码
-
-如果不知道初始登录密码,可以通过以下方式查看:
-
-执行命令 `cat /root/.jenkins/secrets/initialAdminPassword`,打印出来的即是初始登录密码。
-
-### 忘记密码
-
-1.执行 `vim /root/.jenkins/config.xml` ,删除以下内容
-
-```xml
-true
-
- true
-
-
- true
- false
-
-```
-
-2.重启 Jenkins 服务;
-
-3.进入首页>“系统管理”>“Configure Global Security”;
-
-4.勾选“启用安全”;
-
-5.点选“Jenkins 专有用户数据库”,并点击“保存”;
-
-6.重新点击首页>“系统管理”,发现此时出现“管理用户”;
-
-7.点击进入展示“用户列表”;
-
-8.点击右侧进入修改密码页面,修改后即可重新登录。
-
-### 卡在 check 页面
-
-**现象**:输入密码后,卡在 check 页面
-
-
-
-**原因**:jenkins 在安装插件前总是尝试连接 www.google.com,来判断网络是否连通。谷歌的网站在大陆是连不上的,所以会出现这个问题。
-
-**解决方案**:执行`vim /root/.jenkins/updates/default.json`,将 `connectionCheckUrl` 后的 `www.google.com` 改为 `www.baidu.com` 。然后重启即可。
-
-或者直接执行命令:
-
-```sh
-sed -i 's/www.google.com/www.baidu.com/g' /root/.jenkins/updates/default.json
-```
-
-### 卡在 getting startted 页面
-
-**现象**:卡在 getting startted 页面
-
-**原因**:jenkins 默认的插件下载服务器地址在国外,如果不翻墙下载不了。
-
-**解决方案**:执行`vim /root/.jenkins/hudson.model.UpdateCenter.xml`,将 `` 改为 `http://mirror.xmission.com/jenkins/updates/update-center.json` 。然后重启即可。
-
-或者直接执行命令:
-
-```sh
-sed -i '/^/s/.*/http:\/\/mirror.xmission.com\/jenkins\/updates\/update-center.json<\/url>/g' /root/.jenkins/hudson.model.UpdateCenter.xml
-```
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
-- **引用**
- - https://jenkins.io/doc/pipeline/tour/getting-started/
- - https://www.cnblogs.com/austinspark-jessylu/p/6894944.html
- - http://blog.csdn.net/jlminghui/article/details/54952148
diff --git a/os/tool/kafka.md b/os/tool/kafka.md
deleted file mode 100644
index a10da699a3..0000000000
--- a/os/tool/kafka.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# Kafka 安装部署
-
-> 环境要求:
->
-> - JDK8
-> - ZooKeeper
-
-
-
-- [下载解压](#下载解压)
-- [启动服务器](#启动服务器)
-- [停止服务器](#停止服务器)
-- [创建主题](#创建主题)
-- [生产者生产消息](#生产者生产消息)
-- [消费者消费消息](#消费者消费消息)
-- [集群部署](#集群部署)
-
-
-
-## 下载解压
-
-进入官方下载地址:http://kafka.apache.org/downloads,选择合适版本。
-
-解压到本地:
-
-```
-> tar -xzf kafka_2.11-1.1.0.tgz
-> cd kafka_2.11-1.1.0
-```
-
-现在您已经在您的机器上下载了最新版本的 Kafka。
-
-## 启动服务器
-
-由于 Kafka 依赖于 ZooKeeper,所以运行前需要先启动 ZooKeeper
-
-```
-> bin/zookeeper-server-start.sh config/zookeeper.properties
-[2013-04-22 15:01:37,495] INFO Reading configuration from: config/zookeeper.properties (org.apache.zookeeper.server.quorum.QuorumPeerConfig)
-...
-```
-
-然后,启动 Kafka
-
-```
-> bin/kafka-server-start.sh config/server.properties
-[2013-04-22 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties)
-[2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties)
-...
-```
-
-## 停止服务器
-
-执行所有操作后,可以使用以下命令停止服务器
-
-```
-$ bin/kafka-server-stop.sh config/server.properties
-```
-
-## 创建主题
-
-创建一个名为 test 的 Topic,这个 Topic 只有一个分区以及一个备份:
-
-```
-> bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
-```
-
-## 生产者生产消息
-
-运行生产者,然后可以在控制台中输入一些消息,这些消息会发送到服务器:
-
-```
-> bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
-This is a message
-This is another message
-```
-
-## 消费者消费消息
-
-启动消费者,然后获得服务器中 Topic 下的消息:
-
-```
-> bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
-This is a message
-This is another message
-```
-
-## 集群部署
-
-复制配置为多份(Windows 使用 copy 命令代理):
-
-```
-> cp config/server.properties config/server-1.properties
-> cp config/server.properties config/server-2.properties
-```
-
-修改配置:
-
-```
-config/server-1.properties:
- broker.id=1
- listeners=PLAINTEXT://:9093
- log.dir=/tmp/kafka-logs-1
-
-config/server-2.properties:
- broker.id=2
- listeners=PLAINTEXT://:9094
- log.dir=/tmp/kafka-logs-2
-```
-
-其中,broker.id 这个参数必须是唯一的。
-
-端口故意配置的不一致,是为了可以在一台机器启动多个应用节点。
-
-根据这两份配置启动三个服务器节点:
-
-```
-> bin/kafka-server-start.sh config/server.properties &
-...
-> bin/kafka-server-start.sh config/server-1.properties &
-...
-> bin/kafka-server-start.sh config/server-2.properties &
-...
-```
-
-创建一个新的 Topic 使用 三个备份:
-
-```
-> bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic
-```
-
-查看主题:
-
-```
-> bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic
-Topic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs:
- Topic: my-replicated-topic Partition: 0 Leader: 1 Replicas: 1,2,0 Isr: 1,2,0
-```
-
-- leader - 负责指定分区的所有读取和写入的节点。每个节点将成为随机选择的分区部分的领导者。
-- replicas - 是复制此分区日志的节点列表,无论它们是否为领导者,或者即使它们当前处于活动状态。
-- isr - 是“同步”复制品的集合。这是副本列表的子集,该列表当前处于活跃状态并且已经被领导者捕获。
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
diff --git a/os/tool/nexus.md b/os/tool/nexus.md
deleted file mode 100644
index a481534e20..0000000000
--- a/os/tool/nexus.md
+++ /dev/null
@@ -1,203 +0,0 @@
-# Nexus 安装
-
-> 关键词:maven, nexus, settings.xml, pom.xml
->
-> 环境要求:
->
-> - Nexus 3.13.0
-> - JDK 1.8
-> - Maven 3.5.4
-
-
-
-- [下载安装 Nexus](#下载安装-nexus)
-- [启动停止 Nexus](#启动停止-nexus)
-- [使用 Nexus](#使用-nexus)
- - [配置 settings.xml](#配置-settingsxml)
- - [配置 pom.xml](#配置-pomxml)
- - [执行 maven 构建](#执行-maven-构建)
-- [参考资料](#参考资料)
-
-
-
-## 下载安装 Nexus
-
-进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。
-
-
-
-本人希望将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。
-
-这里,如果想通过命令方式直接下载(比如用脚本安装),可以在[官方历史发布版本页面](https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3)中找到合适版本,然后执行以下命令:
-
-```sh
-wget -O /opt/maven/nexus-unix.tar.gz http://download.sonatype.com/nexus/3/nexus-3.13.0-01-unix.tar.gz
-tar -zxf nexus-unix.tar.gz
-```
-
-解压后,有两个目录:
-
-- nexus-3.13.0-01 - 包含了 Nexus 运行所需要的文件。是 Nexus 运行必须的。
-- sonatype-work - 包含了 Nexus 生成的配置文件、日志文件、仓库文件等。当我们需要备份 Nexus 的时候默认备份此目录即可。
-
-## 启动停止 Nexus
-
-进入 nexus-3.13.0-01/bin 目录,有一个可执行脚本 nexus。
-
-执行 `./nexus`,可以查看允许执行的参数,如下所示,含义可谓一目了然:
-
-```sh
-$ ./nexus
-Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload}
-```
-
-- 启动 nexus - `./nexus start`
-- 停止 nexus -
-
-启动成功后,在浏览器中访问 `http://:8081`,欢迎页面如下图所示:
-
-
-
-点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。
-
-有必要提一下的是,在 Nexus 的 Repositories 管理页面,展示了可用的 maven 仓库,如下图所示:
-
-
-
-> 说明:
->
-> - maven-central - maven 中央库(如果没有配置 mirror,默认就从这里下载 jar 包),从 https://repo1.maven.org/maven2/ 获取资源
-> - maven-releases - 存储私有仓库的发行版 jar 包
-> - maven-snapshots - 存储私有仓库的快照版(调试版本) jar 包
-> - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。
-
-## 使用 Nexus
-
-如果要使用 Nexus,还必须在 settings.xml 和 pom.xml 中配置认证信息。
-
-### 配置 settings.xml
-
-一份完整的 `settings.xml`:
-
-```xml
-
-
-
-
- org.sonatype.plugins
-
-
-
-
-
- releases
- admin
- admin123
-
-
- snapshots
- admin
- admin123
-
-
-
-
-
-
- public
- *
- http://10.255.255.224:8081/repository/maven-public/
-
-
-
- aliyun
- central
- Aliyun Mirror
- http://maven.aliyun.com/nexus/content/groups/public/
-
-
-
-
-
- zp
-
-
- central
- http://central
-
- true
-
-
- true
-
-
-
-
-
- central
- http://central
-
- true
-
-
- true
- always
-
-
-
-
-
-
-
- zp
-
-
-```
-
-### 配置 pom.xml
-
-在 pom.xml 中添加如下配置:
-
-```xml
-
-
- releases
- Releases
- http://10.255.255.224:8081/repository/maven-releases
-
-
- snapshots
- Snapshot
- http://10.255.255.224:8081/repository/maven-snapshots
-
-
-```
-
-> 注意:
->
-> - `` 和 `` 的 id 必须和 `settings.xml` 配置文件中的 `` 标签中的 id 匹配。
-> - `` 标签的地址需要和 maven 私服的地址匹配。
-
-### 执行 maven 构建
-
-如果要使用 settings.xml 中的私服配置,必须通过指定 `-P zp` 来激活 profile。
-
-示例:
-
-```sh
-# 编译并打包 maven 项目
-$ mvn clean package -Dmaven.skip.test=true -P zp
-
-# 编译并上传 maven 交付件(jar 包)
-$ mvn clean deploy -Dmaven.skip.test=true -P zp
-```
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
-- **引用**
- - https://www.cnblogs.com/hoobey/p/6102382.html
- - https://blog.csdn.net/wzygis/article/details/49276779
- - https://blog.csdn.net/clj198606061111/article/details/52200928
diff --git a/os/tool/nodejs.md b/os/tool/nodejs.md
deleted file mode 100644
index 4df849d701..0000000000
--- a/os/tool/nodejs.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Nodejs 安装
-
-
-
-- [安装方法](#安装方法)
- - [先安装 nvm](#先安装-nvm)
- - [安装 Nodejs](#安装-nodejs)
-- [脚本](#脚本)
-
-
-
-## 安装方法
-
-### 先安装 nvm
-
-推荐安装 nvm(Node Version Manager) ,来管理 node.js 版本。
-
-安装步骤如下:
-
-(1)执行安装脚本
-
-```
-rm -rf ~/.nvm
-curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
-. ~/.nvm/nvm.sh
-```
-
-(2)检验是否安装成功
-
-执行 `nvm --version` 命令。
-
-注意:如果出现 `nvm: command not found` ,关闭终端,然后再打开终端试试。
-
-### 安装 Nodejs
-
-安装步骤如下:
-
-(1)使用 nvm 安装 nodejs 指定版本
-
-执行以下命令:
-
-```
-nvm install 8.9.4
-nvm use 8.9.4
-```
-
-(2)检验是否安装成功
-
-执行 `node --version` 命令。
-
-注意:如果出现 `node: command not found` ,关闭终端,然后再打开终端试试。
-
-## 脚本
-
-| [安装脚本](https://github.com/dunwu/OS/tree/master/codes/deploy/tool/nodejs) |
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
diff --git a/os/tool/rocketmq.md b/os/tool/rocketmq.md
deleted file mode 100644
index 3f44d52c70..0000000000
--- a/os/tool/rocketmq.md
+++ /dev/null
@@ -1,126 +0,0 @@
-# RocketMQ 安装部署
-
-
-
-- [环境要求](#环境要求)
-- [下载解压](#下载解压)
-- [启动 Name Server](#启动-name-server)
-- [启动 Broker](#启动-broker)
-- [收发消息](#收发消息)
-- [关闭服务器](#关闭服务器)
-- [FAQ](#faq)
- - [connect to <172.17.0.1:10909> failed](#connect-to-172170110909-failed)
-- [参考资料](#参考资料)
-
-
-
-## 环境要求
-
-- 推荐 64 位操作系统:Linux/Unix/Mac
-- 64bit JDK 1.8+
-- Maven 3.2.x
-- Git
-
-## 下载解压
-
-进入官方下载地址:https://rocketmq.apache.org/dowloading/releases/,选择合适版本
-
-建议选择 binary 版本。
-
-解压到本地:
-
-```sh
-> unzip rocketmq-all-4.2.0-source-release.zip
-> cd rocketmq-all-4.2.0/
-```
-
-## 启动 Name Server
-
-```sh
-> nohup sh bin/mqnamesrv &
-> tail -f ~/logs/rocketmqlogs/namesrv.log
-The Name Server boot success...
-```
-
-## 启动 Broker
-
-```sh
-> nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &
-> tail -f ~/logs/rocketmqlogs/broker.log
-The broker[%s, 172.30.30.233:10911] boot success...
-```
-
-## 收发消息
-
-执行收发消息操作之前,不许告诉客户端命名服务器的位置。在 RocketMQ 中有多种方法来实现这个目的。这里,我们使用最简单的方法——设置环境变量 `NAMESRV_ADDR` :
-
-```sh
-> export NAMESRV_ADDR=localhost:9876
-> sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
-SendResult [sendStatus=SEND_OK, msgId= ...
-
-> sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
-ConsumeMessageThread_%d Receive New Messages: [MessageExt...
-```
-
-## 关闭服务器
-
-```sh
-> sh bin/mqshutdown broker
-The mqbroker(36695) is running...
-Send shutdown request to mqbroker(36695) OK
-
-> sh bin/mqshutdown namesrv
-The mqnamesrv(36664) is running...
-Send shutdown request to mqnamesrv(36664) OK
-```
-
-## FAQ
-
-### connect to <172.17.0.1:10909> failed
-
-启动后,生产者客户端连接 RocketMQ 时报错:
-
-```java
-org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to <172.17.0.1:10909> failed
- at org.apache.rocketmq.remoting.netty.NettyRemotingClient.invokeSync(NettyRemotingClient.java:357)
- at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessageSync(MQClientAPIImpl.java:343)
- at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:327)
- at org.apache.rocketmq.client.impl.MQClientAPIImpl.sendMessage(MQClientAPIImpl.java:290)
- at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendKernelImpl(DefaultMQProducerImpl.java:688)
- at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendSelectImpl(DefaultMQProducerImpl.java:901)
- at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:878)
- at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.send(DefaultMQProducerImpl.java:873)
- at org.apache.rocketmq.client.producer.DefaultMQProducer.send(DefaultMQProducer.java:369)
- at com.emrubik.uc.mdm.sync.utils.MdmInit.sendMessage(MdmInit.java:62)
- at com.emrubik.uc.mdm.sync.utils.MdmInit.main(MdmInit.java:2149)
-```
-
-原因:RocketMQ 部署在虚拟机上,内网 ip 为 10.10.30.63,该虚拟机一个 docker0 网卡,ip 为 172.17.0.1。RocketMQ broker 启动时默认使用了 docker0 网卡,生产者客户端无法连接 172.17.0.1,造成以上问题。
-
-解决方案
-
-(1)干掉 docker0 网卡或修改网卡名称
-
-(2)停掉 broker,修改 broker 配置文件,重启 broker。
-
-修改 conf/broker.conf,增加两行来指定启动 broker 的 IP:
-
-```
-namesrvAddr = 10.10.30.63:9876
-brokerIP1 = 10.10.30.63
-```
-
-启动时需要指定配置文件
-
-```sh
-nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &
-```
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
-- **引用**
- - [RocketMQ 官方文档](http://rocketmq.apache.org/docs/quick-start/)
- - [RocketMQ 搭建及刨坑](http://laciagin.me/2017/12/07/RocketMQ%E6%90%AD%E5%BB%BA%E5%8F%8A%E5%88%A8%E5%9D%91/)
diff --git a/os/tool/svn.md b/os/tool/svn.md
deleted file mode 100644
index cc9562c5c6..0000000000
--- a/os/tool/svn.md
+++ /dev/null
@@ -1,156 +0,0 @@
-# Svn 安装、配置、使用指南
-
-Svn 是 Subversion 的简称,是一个开放源代码的版本控制系统,它采用了分支管理系统。
-
-
-
-- [1. 安装配置](#1-安装配置)
- - [1.1. 安装 svn](#11-安装-svn)
- - [1.2. 创建 svn 仓库](#12-创建-svn-仓库)
- - [1.3. 配置 svnserve.conf](#13-配置-svnserveconf)
- - [1.4. 配置 passwd](#14-配置-passwd)
- - [1.5. 配置 authz](#15-配置-authz)
- - [1.6. 启动关闭 svn](#16-启动关闭-svn)
- - [1.7. 开机自启动 svn 方法](#17-开机自启动-svn-方法)
- - [1.8. svn 客户端访问](#18-svn-客户端访问)
-- [2. 引申和引用](#2-引申和引用)
-
-
-
-## 1. 安装配置
-
-### 1.1. 安装 svn
-
-```sh
-$ yum install -y subversion
-```
-
-### 1.2. 创建 svn 仓库
-
-```sh
-$ mkdir -p /share/svn
-$ svnadmin create /share/svn
-$ ls /share/svn
-conf db format hooks locks README.txt
-```
-
-在 conf 目录下有三个重要的配置文件
-
-- authz - 是权限控制文件
-- passwd - 是帐号密码文件
-- svnserve.conf - 是 SVN 服务配置文件
-
-### 1.3. 配置 svnserve.conf
-
-```sh
-$ vim /share/svn/conf/svnserve.conf
-```
-
-打开下面的 5 个注释
-
-```ini
-anon-access = read #匿名用户可读
-auth-access = write #授权用户可写
-password-db = passwd #使用哪个文件作为账号文件
-authz-db = authz #使用哪个文件作为权限文件
-realm = /share/svn # 认证空间名,版本库所在目录
-```
-
-### 1.4. 配置 passwd
-
-```sh
-$ vim /share/svn/conf/passwd
-```
-
-添加内容如下:
-
-```ini
-[users]
-user1 = 123456
-user2 = 123456
-user3 = 123456
-```
-
-### 1.5. 配置 authz
-
-```sh
-$ vim /share/svn/conf/authz
-```
-
-添加内容如下:
-
-```ini
-[/]
-user1 = rw
-user2 = rw
-user3 = rw
-*=
-```
-
-### 1.6. 启动关闭 svn
-
-```sh
-$ svnserve -d -r /share/svn # 启动 svn
-$ killall svnserve # 关闭 svn
-```
-
-### 1.7. 开机自启动 svn 方法
-
-安装好 svn 服务后,默认是没有随系统启动自动启动的,而一般我们有要求 svn 服务稳定持续的提供服务。所以,有必要配置开机自启动 svn 服务。
-
-#### Centos7 以前
-
-编辑 `/etc/rc.d/rc.local` 文件:
-
-```sh
-$ vi /etc/rc.d/rc.local
-```
-
-输入以下内容:
-
-```sh
-# 开机自动启动 svn,默认端口是 3690
-$ /usr/bin/svnserve -d -r /share/svn --listen-port 3690
-```
-
-注意:
-
-我们在用终端操作的时候,可以直接使用以下命令启动 SVN:`svnserve -d -r /share/svn`,但是在 `/etc/rc.d/rc.local` 文件中必须写上完整的路径!
-
-如果不知道 svnserve 命令安装在哪儿,可以使用 whereis svnserve 查找。
-
-#### Centos7
-
-CentOS 7 中的 `/etc/rc.d/rc.local` 是没有执行权限的,系统建议创建 `systemd service` 启动服务。
-
-找到 svn 的 service 配置文件 `/etc/sysconfig/svnserve` 编辑配置文件
-
-```sh
-$ vi /etc/sysconfig/svnserve
-```
-
-将 `OPTIONS="-r /var/svn"` 改为 svn 版本库存放的目录,:wq 保存退出。
-
-执行 `systemctl enable svnserve.service`
-
-重启服务器后,执行 `ps -ef | grep svn` 应该可以看到 svn 服务的进程已经启动。
-
-### 1.8. svn 客户端访问
-
-进入 [svn 官方下载地址](https://tortoisesvn.net/downloads.html),选择合适的版本,下载并安装。
-
-新建一个目录,然后打开鼠标右键菜单,选择 **SVN Checkout**。
-
-在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示:
-
-
-
-## 2. 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
-- **引用**
- - https://www.cnblogs.com/liuxianan/p/linux_install_svn_server.html
- - https://blog.csdn.net/testcs_dn/article/details/45395645
- - https://www.cnblogs.com/moxiaoan/p/5683743.html
- - https://blog.csdn.net/realghost/article/details/52396648
diff --git a/os/tool/tomcat.md b/os/tool/tomcat.md
deleted file mode 100644
index c6c7f88239..0000000000
--- a/os/tool/tomcat.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Tomcat 安装
-
-
-
-- [安装](#安装)
-- [启动](#启动)
-- [脚本](#脚本)
-
-
-
-## 安装
-
-安装步骤如下:
-
-(1)下载并解压到本地
-
-进入官网下载地址:https://tomcat.apache.org/download-80.cgi ,选择合适的版本下载。
-
-我选择的是最新稳定版本 8.5.28:http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.28/bin/apache-tomcat-8.5.28.tar.gz
-
-我个人喜欢存放在:`/opt/tomcat`
-
-```
-wget -O /opt/tomcat/apache-tomcat-8.5.28.tar.gz http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.28/bin/apache-tomcat-8.5.28.tar.gz
-cd /opt/tomcat
-tar zxvf apache-tomcat-8.5.28.tar.gz
-```
-
-## 启动
-
-**启动 tomcat 服务**
-
-```
-cd /opt/tomcat/apache-tomcat-8.5.28/bin
-./catalina.sh start
-```
-
-**停止 tomcat 服务**
-
-```
-cd /opt/tomcat/apache-tomcat-8.5.28/bin
-./catalina.sh stop
-```
-
-## 脚本
-
-| [安装脚本](https://github.com/dunwu/OS/tree/master/codes/deploy/tool/tomcat) |
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
diff --git a/os/tool/zookeeper.md b/os/tool/zookeeper.md
deleted file mode 100644
index 33232932d3..0000000000
--- a/os/tool/zookeeper.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# ZooKeeper 安装部署
-
-> 环境要求:JDK6+
-
-
-
-- [下载解压 ZooKeeper](#下载解压-zookeeper)
-- [创建配置文件](#创建配置文件)
-- [启动 ZooKeeper 服务器](#启动-zookeeper-服务器)
-- [启动 CLI](#启动-cli)
-- [停止 ZooKeeper 服务器](#停止-zookeeper-服务器)
-
-
-
-在安装 ZooKeeper 之前,请确保你的系统是在以下任一操作系统上运行:
-
-- **任意 Linux OS** - 支持开发和部署。适合演示应用程序。
-- **Windows OS** - 仅支持开发。
-- **Mac OS** - 仅支持开发。
-
-安装步骤如下:
-
-## 下载解压 ZooKeeper
-
-进入官方下载地址:http://zookeeper.apache.org/releases.html#download ,选择合适版本。
-
-解压到本地:
-
-```
-$ tar -zxf zookeeper-3.4.6.tar.gz
-$ cd zookeeper-3.4.6
-```
-
-## 创建配置文件
-
-你必须创建 `conf/zoo.cfg` 文件,否则启动时会提示你没有此文件。
-
-初次尝试,不妨直接使用 Kafka 提供的模板配置文件 `conf/zoo_sample.cfg`:
-
-```
-$ cp conf/zoo_sample.cfg conf/zoo.cfg
-```
-
-## 启动 ZooKeeper 服务器
-
-执行以下命令
-
-```
-$ bin/zkServer.sh start
-```
-
-执行此命令后,你将收到以下响应
-
-```
-$ JMX enabled by default
-$ Using config: /Users/../zookeeper-3.4.6/bin/../conf/zoo.cfg
-$ Starting zookeeper ... STARTED
-```
-
-## 启动 CLI
-
-键入以下命令
-
-```
-$ bin/zkCli.sh
-```
-
-键入上述命令后,将连接到 ZooKeeper 服务器,你应该得到以下响应。
-
-```
-Connecting to localhost:2181
-................
-................
-................
-Welcome to ZooKeeper!
-................
-................
-WATCHER::
-WatchedEvent state:SyncConnected type: None path:null
-[zk: localhost:2181(CONNECTED) 0]
-```
-
-## 停止 ZooKeeper 服务器
-
-连接服务器并执行所有操作后,可以使用以下命令停止 zookeeper 服务器。
-
-```
-$ bin/zkServer.sh stop
-```
-
-> 本节安装内容参考:[Zookeeper 安装](https://www.w3cschool.cn/zookeeper/zookeeper_installation.html)
-
-## 引申和引用
-
-- **引申**
- - [操作系统、运维部署总结系列](https://github.com/dunwu/OS)
diff --git a/os/vim.md b/os/vim.md
deleted file mode 100644
index a4413f552a..0000000000
--- a/os/vim.md
+++ /dev/null
@@ -1,374 +0,0 @@
----
-title: Vim 快速指南
-date: 2017/11/17
-categories:
- - linux
-tags:
- - linux
- - vim
----
-
-# Vim 快速指南
-
-
-
-- [1. 概念](#1-概念)
- - [1.1. 什么是 vim](#11-什么是-vim)
- - [1.2. Vim 的模式](#12-vim-的模式)
-- [2. Vim 渐进学习](#2-vim-渐进学习)
- - [2.1. 存活](#21-存活)
- - [2.2. 感觉良好](#22-感觉良好)
- - [2.3. 更好,更强,更快](#23-更好更强更快)
- - [2.4. Vim 超能力](#24-vim-超能力)
-- [3. Vim Cheat Sheet](#3-vim-cheat-sheet)
- - [3.1. 经典版](#31-经典版)
- - [3.2. 入门版](#32-入门版)
- - [3.3. 进阶版](#33-进阶版)
- - [3.4. 增强版](#34-增强版)
- - [3.5. 文字版](#35-文字版)
-- [4. 资料](#4-资料)
-
-
-
-## 1. 概念
-
-### 1.1. 什么是 vim
-
-Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。和 Emacs 并列成为类 Unix 系统用户最喜欢的编辑器。
-
-### 1.2. Vim 的模式
-
-基本上 vi/vim 共分为三种模式,分别是**命令模式(Command mode)**,**插入模式(Insert mode)**和**底线命令模式(Last line mode)**。
-
-#### 命令模式
-
-**用户刚刚启动 vi/vim,便进入了命令模式。**
-
-此状态下敲击键盘动作会被 Vim 识别为命令,而非输入字符。
-
-#### 插入模式
-
-**在命令模式下按下 `i` 就进入了输入模式。**
-
-在输入模式下,你可以输入文本内容。
-
-#### 底线命令模式
-
-**在命令模式下按下 `:`(英文冒号)就进入了底线命令模式。**
-
-底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。
-
-## 2. Vim 渐进学习
-
-### 2.1. 存活
-
-1. 安装 [vim](http://www.vim.org/)
-2. 启动 vim
-3. **什么也别干!**请先阅读
-
-当你安装好一个编辑器后,你一定会想在其中输入点什么东西,然后看看这个编辑器是什么样子。但 vim 不是这样的,请按照下面的命令操作:
-
-- 启 动 Vim 后,vim 在 _Normal_ 模式下。
-- 让我们进入 _Insert_ 模式,请按下键 i 。(注:你会看到 vim 左下角有一个–insert–字样,表示,你可以以插入的方式输入了)
-- 此时,你可以输入文本了,就像你用“记事本”一样。
-- 如果你想返回 _Normal_ 模式,请按 `ESC` 键。
-
-现在,你知道如何在 _Insert_ 和 _Normal_ 模式下切换了。下面是一些命令,可以让你在 _Normal_ 模式下幸存下来:
-
-> - `i` → _Insert_ 模式,按 `ESC` 回到 _Normal_ 模式.
-> - `x` → 删当前光标所在的一个字符。
-> - `:wq` → 存盘 + 退出 (`:w` 存盘, `:q` 退出) (注::w 后可以跟文件名)
-> - `dd` → 删除当前行,并把删除的行存到剪贴板里
-> - `p` → 粘贴剪贴板
->
-> **推荐**
->
-> - `hjkl` (强例推荐使用其移动光标,但不必需) → 你也可以使用光标键 (←↓↑→). 注: `j` 就像下箭头。
-> - `:help ` → 显示相关命令的帮助。你也可以就输入 `:help` 而不跟命令。(注:退出帮助需要输入:q)
-
-你能在 vim 幸存下来只需要上述的那 5 个命令,你就可以编辑文本了,你一定要把这些命令练成一种下意识的状态。于是你就可以开始进阶到第二级了。
-
-当是,在你进入第二级时,需要再说一下 _Normal_ 模式。在一般的编辑器下,当你需要 copy 一段文字的时候,你需要使用 `Ctrl` 键,比如:`Ctrl-C`。也就是说,`Ctrl` 键就好像功能键一样,当你按下了功能键 `Ctrl` 后,C 就不在是 C 了,而且就是一个命令或是一个快键键了,**在 vim 的 Normal 模式下,所有的键都是功能键**。这个你需要知道。
-
-> **标记**
->
-> - 下面的文字中,如果是 `Ctrl-λ`我会写成 ``.
-> - 以 `:` 开始的命令你需要输入 ``回车,例如 — 如果我写成 `:q` 也就是说你要输入 `:q`.
-
-### 2.2. 感觉良好
-
-上面的那些命令只能让你存活下来,现在是时候学习一些更多的命令了,下面是我的建议:(注:所有的命令都需要在 Normal 模式下使用,如果你不知道现在在什么样的模式,你就狂按几次 ESC 键)
-
-1. 各种插入模式
-
- > - `a` → 在光标后插入
- > - `o` → 在当前行后插入一个新行
- > - `O` → 在当前行前插入一个新行
- > - `cw` → 替换从光标所在位置后到一个单词结尾的字符
-
-2. 简单的移动光标
-
- > - `0` → 数字零,到行头
- > - `^` → 到本行第一个不是 blank 字符的位置(所谓 blank 字符就是空格,tab,换行,回车等)
- > - `$` → 到本行行尾
- > - `g_` → 到本行最后一个不是 blank 字符的位置。
- > - `/pattern` → 搜索 `pattern` 的字符串(注:如果搜索出多个匹配,可按 n 键到下一个)
-
-3. 拷贝/粘贴
-
- (注:p/P 都可以,p 是表示在当前位置之后,P 表示在当前位置之前)
-
- > - `P` → 粘贴
- > - `yy` → 拷贝当前行当行于 `ddP`
-
-4. Undo/Redo
-
- > - `u` → undo
- > - `` → redo
-
-5. 打开/保存/退出/改变文件
-
- (Buffer)
-
- > - `:e ` → 打开一个文件
- > - `:w` → 存盘
- > - `:saveas ` → 另存为 `