Compare commits

...
25 Commits
Author SHA1 Message Date
ascoders b9d95182a1 Merge branch 'v2' of https://github.com/dt-fe/weekly into v2 2019-08-12 08:17:19 +08:00
ascoders bfa3ab7b55 115 2019-08-12 08:17:13 +08:00
黄子毅 304e4d3aa3 Merge pull request #190 from vivaxy/patch-1
Update 092.精读《React PowerPlug 源码》.md
2019-08-05 14:30:23 +08:00
ascoders 62be743112 Merge branch 'v2' of https://github.com/dt-fe/weekly into v2 2019-08-05 09:11:22 +08:00
ascoders 6d27e723cd update 114 2019-08-05 09:11:20 +08:00
vivaxy 704c9cda9e Update 092.精读《React PowerPlug 源码》.md
补全句子
2019-08-05 09:00:49 +08:00
黄子毅 44dd8057e2 Merge pull request #188 from xuhongbo/patch-5
Update 039.精读《全链路体验浏览器挖矿》.md
2019-07-29 22:54:20 +08:00
leoxu d956b15ad0 Update 039.精读《全链路体验浏览器挖矿》.md
fix type
2019-07-29 20:13:54 +08:00
黄子毅 961d77b4d1 Merge pull request #186 from xuhongbo/patch-3
Update 113.精读《Nodejs V12》.md
2019-07-29 13:23:28 +08:00
黄子毅 c763fc15bd Merge pull request #187 from xuhongbo/patch-4
Update 068.精读衡量用户体验.md
2019-07-29 13:23:16 +08:00
leoxu f9f04d711f Update 068.精读衡量用户体验.md
修改一些用词错误
2019-07-29 11:20:01 +08:00
leoxu 5d40208d45 Update 113.精读《Nodejs V12》.md
不是很通顺
2019-07-29 11:03:00 +08:00
ascoders 771bb9f914 Merge branch 'v2' of https://github.com/dt-fe/weekly into v2 2019-07-29 08:56:57 +08:00
ascoders 96f850e29f 113 2019-07-29 08:53:44 +08:00
黄子毅 349e7df2b6 Merge pull request #185 from LiuL0703/patch-2
fix:typo
2019-07-27 22:16:19 +08:00
Linear-Enter b198d57cf6 fix:typo 2019-07-27 10:17:15 +08:00
黄子毅 03b8840291 Merge pull request #181 from cpprookie/cpprookie-patch-1
Update 112.精读《源码学习》.md
2019-07-23 09:45:31 +08:00
黄子毅 60899c0bf8 Merge pull request #183 from lengjing/patch-1
Update 082.精读《Htm - Hyperscript 源码》.md
2019-07-23 09:45:20 +08:00
lengjing 648e113e66 Update 082.精读《Htm - Hyperscript 源码》.md 2019-07-22 11:08:32 +08:00
chen tuo 61a5e74908 Update 112.精读《源码学习》.md
修改文字错误
2019-07-22 10:29:10 +08:00
ascoders d2273e72f6 Merge branch 'v2' of https://github.com/dt-fe/weekly into v2 2019-07-22 09:58:20 +08:00
ascoders 386b605c32 finish 112 2019-07-22 09:19:57 +08:00
黄子毅 b60cca25d7 Merge pull request #180 from LiuL0703/patch-1
fix:typo
2019-07-21 23:43:19 +08:00
Linear-Enter 68e6c23f82 fix:typo 2019-07-21 19:12:01 +08:00
ascoders a9d2c1d47a 111 2019-07-16 08:54:37 +08:00
11 changed files with 1031 additions and 50 deletions
@@ -1,17 +1,17 @@
本期精读的文章是: [coinhive官方文档](https://coinhive.com)及[Monero官方文档](https://getmonero.org/)
懒得看文章? 没关系.
懒得看文章没关系
, 怎么是官方文档?
怎么是官方文档
本期精读有所不同, 注重实操, 先操作获取感性认识, 然后再介绍相关的概念, 由浅入深, 力求不纠缠细节, 但不遮盖环节. 阅读本文需要对加密货币有一些基本常识 (如果你是一个开发者但是完全没了解过加密货币, 可以参考[这里](http://piotrpasich.com/introduction-bitcoin-for-developers/)). 希望同学们看完本文能对加密货币领域有一个更深更切实的体感. 如果要了解更多细节, 文末总结的延伸阅读链接列表是最好的开始.
本期精读有所不同注重实操先操作获取感性认识然后再介绍相关的概念由浅入深力求不纠缠细节但不遮盖环节阅读本文需要对加密货币有一些基本常识 (如果你是一个开发者但是完全没了解过加密货币可以参考[这里](http://piotrpasich.com/introduction-bitcoin-for-developers/))希望同学们看完本文能对加密货币领域有一个更深更切实的感受。如果要了解更多细节文末总结的延伸阅读链接列表是最好的开始
要注意一点, 文中很多说明是默认基于XMR和BTC的, 他们两个又同源, 机制非常相似. **所以很多命题判断并不适用于所有的成千上万的加密货币.** 正相反, 新的币种层出不穷, 几乎所有的惯例都被打破, 所有可能性都被尝试. 这一点以下不再做说明.
要注意一点文中很多说明是默认基于XMR和BTC的他们两个又同源机制非常相似**所以很多命题判断并不适用于所有的成千上万的加密货币.** 正相反新的币种层出不穷几乎所有的惯例都被打破所有可能性都被尝试这一点以下不再做说明
# 1 引言
首先, tl;dr干货. 10行代码, 5分钟, 不需部署不需构建直接浏览器开挖.
首先,干货。10行代码5分钟不需部署不需构建直接浏览器开挖
- 本地创建文件test.html, 粘贴如下代码:
- 本地创建文件test.html粘贴如下代码:
```
<!doctype html>
<html>
@@ -29,65 +29,65 @@
</html>
```
- 本地双击打开. 等待JS加载, 点击widget "Start Mining". 开始挖矿! 如下图
- 本地双击打开等待JS加载点击widget "Start Mining"开始挖矿! 如下图
![mining](https://img.alicdn.com/tfs/TB13KIljiqAXuNjy1XdXXaYcVXa-880-686.png)
不错, 数字已经在跳动, 风扇开始工作, 永无尽头的挖矿已经开始了. 那么重要的是, 挖出来的加密货币在哪呢? 原来上面的代码里用的还是我的API key, 所以还没挖到你自己那里 :P 继续下面的步骤
不错数字已经在跳动风扇开始工作永无尽头的挖矿已经开始了那么重要的是挖出来的加密货币在哪呢原来上面的代码里用的还是我的 API key所以还没挖到你自己那里,所以请继续下面的步骤
- 在coinhive注册账号并登陆. 它是做什么的? 别急, 后面会详细讲. 在coinhive/settings找到自己的API keypair, 把public key复制出来, 形如MUtCJzIDhrs01ERrf3qlqdawo35N0CYD
- 在coinhive注册账号并登陆它是做什么的?别急,后面会详细讲。在 `coinhive/settings` 找到自己的 `API keypair`,把 `public key` 复制出来,形如 `MUtCJzIDhrs01ERrf3qlqdawo35N0CYD`
- 替换上面代码中的data-key部分, 重新开始挖矿. 好了, 现在挖出的Monero (这是啥? 详见下一节) 已经会到你自己的coinhive账户中. 用下图来说明, 你名下总计算过的Hash个数为264K, 当前难度换算为0.00002Monero(Symbol:XMR). 当前难度为66G一个block, 一个block reward 5.87 XMR, 得到一个XMR11.268G. 264K/11.268G = 0.0000234. 这就是你目前的收益.
- 替换上面代码中的 data-key 部分重新开始挖矿。好了,现在挖出的 Monero (这是啥详见下一节) 已经会到你自己的 coinhive账户中用下图来说明你名下总计算过的 Hash 个数为 264K当前难度换算为 0.00002Monero(Symbol:XMR)当前难度为 66G 一个 block一个 block reward 5.87 XMR得到一个 XMR11.268G264K/11.268G = 0.0000234这就是你目前的收益
![coinhive dashbaord](https://img.alicdn.com/tfs/TB1LnMljiqAXuNjy1XdXXaYcVXa-2424-1118.png)
- 查bitfinex可得现在XMR价格在375美元 (当你看到本文的时候, 价格可能早就又波动到不知哪里去了), 所以你 (以及你忠实勤劳的电脑) 获得的实际收益为0.000234 * 375 USD = 0.008775 USD, 快到一分钱了 :)
- 查 bitfinex 可得现在 XMR 价格在 375 美元 (当你看到本文的时候价格可能早就又波动到不知哪里去了)所以你 (以及你忠实勤劳的电脑) 获得的实际收益为 0.000234 * 375 USD = 0.008775 USD快到一分钱了 :)
怎么样, 有没有一种浏览器点开即玩一刀999级的感觉. 以上操作的便捷直接, 是建立在无数前人大量的开发和基础设施建设之上的. 越是领域早期的工作, 越步履维艰, 收货也越丰厚. 如今加密货币已经走到了一个成年期, 逐渐稳定成熟起来.
怎么样有没有一种浏览器点开即玩一刀 999 级的感觉以上操作的便捷直接是建立在无数前人大量的开发和基础设施建设之上的越是领域早期的工作越步履维艰收货也越丰厚如今加密货币已经走到了一个成年期逐渐稳定成熟起来
接下来我们聚焦到上面过程的每个环节, 了解下拼图的每一块是怎样被构成全图的.
接下来我们聚焦到上面过程的每个环节了解下拼图的每一块是怎样被构成全图的
# 2 聚焦
让我们从最终端最接近用户的环节开始, 逐一聚焦, 最后走完整条链路.
让我们从最终端最接近用户的环节开始逐一聚焦最后走完整条链路
## 2.1 从浏览器说起
本文标题叫浏览器挖矿, 也是和贴合前端的部分. 那么为什么可以在浏览器里挖矿? 为什么可以很多用户在多个终端浏览器同时为同一个人 (你) 挖矿?
本文标题叫浏览器挖矿也是和贴合前端的部分那么为什么可以在浏览器里挖矿为什么可以很多用户在多个终端浏览器同时为同一个人 (你) 挖矿
我们知道, 挖矿是对加密货币产生机制的俗称. 主流大多采取Proof of Work (PoW) 机制. 最常见的PoW方式就是由网络中所有节点作为矿工, 每个节点都基于blockchain前面block已有信息计算一个新信息. 这个新信息的计算方式往往是某种hash function, 并且人为地被设置为需要巨大计算力和时间才能完成 (其具体难度一般也会实时调整). 当一个节点幸运地 (也依靠强大的算力) 第一个计算出结果后, 会把这个结果广播到网络中. 其他所有节点会验证这个结果 (我们知道非对称加密算法, 验证便宜而计算昂贵), 一旦证实就会停下手里的计算, 承认这个计算结果. 新的计算结果创造出新的块, 区块链的高度增加一层, 然后计算继续下去. 每一个块的生成一般在2-10分钟. 这个过程就被叫做挖矿.
我们知道挖矿是对加密货币产生机制的俗称主流大多采取 Proof of Work (PoW) 机制最常见的 PoW 方式就是由网络中所有节点作为矿工每个节点都基于 blockchain 前面 block 已有信息计算一个新信息这个新信息的计算方式往往是某种 hash function并且人为地被设置为需要巨大计算力和时间才能完成 (其具体难度一般也会实时调整)当一个节点幸运地 (也依靠强大的算力) 第一个计算出结果后会把这个结果广播到网络中其他所有节点会验证这个结果 (我们知道非对称加密算法验证便宜而计算昂贵)一旦证实就会停下手里的计算承认这个计算结果新的计算结果创造出新的块区块链的高度增加一层然后计算继续下去每一个块的生成一般在2-10分钟这个过程就被叫做挖矿.
既然是通用计算, 既然是算一个hash值, 那么民用级CPUGPU, 浏览器或任何沙盒, 虚拟机, 移动设备当然就都可以. 在我们的例子中, 计算过程被做成分布式, 每个用户可以各自计算, 结果按chunk发回master汇总. 这样就实现了终端用户 - 浏览器 - 共同贡献计算资源 - 换为XMR的过程.
既然是通用计算既然是算一个 hash 值,那么民用级 CPUGPU浏览器或任何沙盒虚拟机移动设备当然就都可以在我们的例子中计算过程被做成分布式每个用户可以各自计算结果按 chunk 发回 master 汇总这样就实现了终端用户 - 浏览器 - 共同贡献计算资源 - 换为 XMR 的过程
这就是对整个链路的一个描述. 从中我们会生出一些疑问, 比如:
这就是对整个链路的一个描述从中我们会生出一些疑问比如:
> 给我看看具体算什么hash? 为什么要算XMR而不是比特币或者其他? 既然第一个算出的赢家通吃所有, 为什么我的收益却是线性的? 这种描述来看岂不是算力最大的一方**永远**都能算出结果而其他人颗粒无收吗?
> 给我看看具体算什么 hash为什么要算XMR而不是比特币或者其他既然第一个算出的赢家通吃所有为什么我的收益却是线性的这种描述来看岂不是算力最大的一方**永远**都能算出结果而其他人颗粒无收吗?
要看具体算法, 没有问题. bitcoin在[这里](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html), XMR则看[CryptoNote Standard 008](https://cryptonote.org/cns/cns008.txt)
要看具体算法没有问题bitcoin 在[这里](http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html)XMR 则看[CryptoNote Standard 008](https://cryptonote.org/cns/cns008.txt)
读完两个算法我们就有了以上疑问的答案:
### 2.1.1 为什么要算XMR而不是比特币或者其他
XMR不是唯一选择, 但是BTC是一个不可选的选择. 因为double SHA-265在专业级GPU上会比CPU上快10^4倍 ([更多信息](https://en.bitcoin.it/wiki/Why_a_GPU_mines_faster_than_a_CPU)). 这样一百万用户合力浏览器挖矿还不如一架子双路Titan, 就失去了分布到终端用户的意义. 而CryptoNightGPU上只比同价值CPU快2倍. 另外CryptoNight算法也被设计为不适用[ASIC](https://en.wikipedia.org/wiki/Application-specific_integrated_circuit).
XMR 不是唯一选择但是 BTC 是一个不可选的选择因为 double SHA-265 在专业级 GPU 上会比 CPU 上快 10^4 倍 ([更多信息](https://en.bitcoin.it/wiki/Why_a_GPU_mines_faster_than_a_CPU))这样一百万用户合力浏览器挖矿还不如一架子双路 Titan就失去了分布到终端用户的意义。而 CryptoNightGPU 上只比同价值 CPU 快 2 倍。另外 CryptoNight 算法也被设计为不适用[ASIC](https://en.wikipedia.org/wiki/Application-specific_integrated_circuit)
怎么实现的? CryptoNight算法开宗明义地写明, 运算主体是Memory-Hard Loop, 而不是Computation-Hard Loop. 每个循环都要在内存中检索. 实际运行CryptoNight时, CPU都会用最快, 最接近ALU也是容量最小的L3 Cache. 换到GPU, 显存虽然很大, 却没有L3 Cache一样的极致读写速度优化, 而且由于内存读写成了瓶颈, GPU中的大量ALU也没了用武之地. 下图简略地描述了CryptoNight循环体的结构:
怎么实现的CryptoNight 算法开宗明义地写明运算主体是 Memory-Hard Loop而不是 Computation-Hard Loop每个循环都要在内存中检索实际运行 CryptoNight 时,CPU 都会用最快最接近 ALU 也是容量最小的 L3 Cache换到 GPU显存虽然很大却没有 L3 Cache 一样的极致读写速度优化而且由于内存读写成了瓶颈GPU 中的大量 ALU 也没了用武之地下图简略地描述了 CryptoNight 循环体的结构:
![CryptoNight](https://img.alicdn.com/tfs/TB1KrQzjiqAXuNjy1XdXXaYcVXa-682-509.png)
### 2.1.2 算力最大的一方永远都能算出结果
看了比特币具体算法, 你应该明白了hash是靠不停改变nonce来生成的. 随机取一个值, 算了不对, 再随机取另一个nonce值... 既然是随机取, 就不会存在赢家恒赢.
看了比特币具体算法你应该明白了 hash 是靠不停改变 nonce 来生成的随机取一个值算了不对再随机取另一个 nonce 值..既然是随机取就不会存在赢家恒赢.
### 2.1.3 为什么我的收益却是线性的
这是一个隐蔽但是却非常重要的问题. 答案是, 本来确实是赢家通吃. 如果你的算力足够大, 挖矿时间足够长之后总会轮到你, 但是收益会有大幅波动.
这是一个隐蔽但是却非常重要的问题答案是本来确实是赢家通吃如果你的算力足够大挖矿时间足够长之后总会轮到你但是收益会有大幅波动.
就是因为如此, 矿工们逐渐建立了矿池组织. 大家把算力都投入到一起, 合力算, 然后不管这次实际是谁算出来, 都按照贡献的算力比例分配收益. 矿池是一个加密货币建立之初, 完全推崇去中心化时没有预料到的结构, 也产生了深远的影响. 现在来自中国矿池的算力早已超过网络50%, 他们会在挖出的块中打上矿池标记, 而这些矿池在加密货币的分叉, 路线图中也扮演举足轻重的角色.
就是因为如此矿工们逐渐建立了矿池组织大家把算力都投入到一起合力算然后不管这次实际是谁算出来都按照贡献的算力比例分配收益矿池是一个加密货币建立之初完全推崇去中心化时没有预料到的结构也产生了深远的影响现在来自中国矿池的算力早已超过网络 50%他们会在挖出的块中打上矿池标记而这些矿池在加密货币的分叉路线图中也扮演举足轻重的角色.
所以你的算力并不是直接投入XMR网络中, 而是投入一个矿池. 在我们的例子里矿池就是coinhive, 只不过是一个比较特殊的矿池, 特殊在矿池成员都运作在浏览器中. 这就是为什么你会得到线性收益而不是all or none.
所以你的算力并不是直接投入 XMR 网络中而是投入一个矿池在我们的例子里矿池就是 coinhive只不过是一个比较特殊的矿池特殊在矿池成员都运作在浏览器中这就是为什么你会得到线性收益而不是 all or none.
自古以来各行业都会自发产生行业工会, 建立类似保险和行业守则 / 规范这些人人为我我为人人的机制. 在crypto行业也不例外. 这是意料之外而情理之中.
自古以来各行业都会自发产生行业工会建立类似保险和行业守则 / 规范这些人人为我我为人人的机制。在 crypto 行业也不例外这是意料之外而情理之中.
## 2.2 在浏览器里发生了什么, 或, coinhive干了什么
, 我们搞清了一些基本的Monero挖矿机制, 下面来看看coinhive. 已经知道coinhive帮我们接入它的矿池, 让再小的算力也能按比例得到产出. 但是还有什么呢? 最关键的一点, coinhive是怎样把一个完整的mining过程拆分成小块, 让一个或许并不强大的设备上的浏览器, 也能快速接收task, 快速完成并且即时上传的呢?
## 2.2 在浏览器里发生了什么,或 coinhive 干了什么
我们搞清了一些基本的 Monero 挖矿机制下面来看看 coinhive已经知道 coinhive 帮我们接入它的矿池让再小的算力也能按比例得到产出但是还有什么呢最关键的一点coinhive 是怎样把一个完整的 mining 过程拆分成小块让一个或许并不强大的设备上的浏览器也能快速接收 task快速完成并且即时上传的呢?
废话不多说, 打开源码. 本项目没有开源, 构建完成后的在https://coinhive.com/lib/coinhive.min.js. 先做初步format处理, 发现有些工作完成在后端, worker shard一侧. 以下用松散的伪码总结一下client side的[main success scenario](https://wiki.ihris.org/wiki/Understanding_Use_Cases)流程 (注意很多地方简化了):
废话不多说打开源码本项目没有开源构建完成后的在https://coinhive.com/lib/coinhive.min.js先做初步 format 处理发现有些工作完成在后端worker shard 一侧以下用松散的伪码总结一下 client side 的[main success scenario](https://wiki.ihris.org/wiki/Understanding_Use_Cases)流程 (注意很多地方简化了):
```
- start
@@ -107,36 +107,36 @@ XMR不是唯一选择, 但是BTC是一个不可选的选择. 因为double SHA-26
} else {
this.worker.postMessage(job)
}
// 实例化若干个JobThread, 每个对应一个worker, worker实际执行asmjs.min.js
- _connect // verify成功, 终于建立连接. 根据public key固定hash到一个shard池然后随机选一个shard, 建立websocket
// 实例化若干个JobThread每个对应一个workerworker实际执行asmjs.min.js
- _connect // verify成功终于建立连接根据public key固定hash到一个shard池然后随机选一个shard建立websocket
- websocket.onmessage: if (type==job) work()
- work:
do { hash(input, output) } while !(meetTarget(output));
websocket.postMessage({nonce, output}) // hash done successfully. submit
do { hash(inputoutput) } while !(meetTarget(output));
websocket.postMessage({nonceoutput}) // hash done successfullysubmit
```
看一下这个过程, 结合[cns003 XMR blockchain specs](https://cryptonote.org/cns/cns003.txt). XMR的整体hash input很小, 是:
看一下这个过程结合[cns003 XMR blockchain specs](https://cryptonote.org/cns/cns003.txt)XMR 的整体 hash input 很小是:
```
- size of [block_header, Merkle root hash, and the number of
- size of [block_headerMerkle root hashand the number of
transactions] in bytes (varint)
- block_header,
- Merkle root hash,
- number of transactions (varint).
```
这样用websocket发过来毫无问题. 之后就是完全独立的计算, 调整nonce来算不同的hash结果. target就是当前难度的一个指示.
这样用 websocket 发过来毫无问题之后就是完全独立的计算调整 nonce 来算不同的 hash 结果target 就是当前难度的一个指示.
这样整条链路就比较清晰了. 再思考一下以下问题:
这样整条链路就比较清晰了再思考一下以下问题:
### 2.2.1 为什么XMR适宜分布式客户端计算
因为能够利用每个用户的CPU和其中的**高速L3 Cache**. 这是中心化执行难以具备的条件.
因为能够利用每个用户的 CPU 和其中的**高速 L3 Cache**这是中心化执行难以具备的条件.
任何时候当考虑要不要把某项操作推到客户端进行时, 都要想明白可以利用客户端的哪个资源, 这个资源在客户端是否有明显优势, 是否比后端中心化执行更有利. **很多时候答案是, 优势并不明显. 那么引入的网络通信成本, 法规成本, 额外开销可能就并不值得.**
任何时候当考虑要不要把某项操作推到客户端进行时都要想明白可以利用客户端的哪个资源这个资源在客户端是否有明显优势是否比后端中心化执行更有利**很多时候答案是优势并不明显那么引入的网络通信成本法规成本额外开销可能就并不值得.**
## 2.3 最后的步骤
到了这里, 最后剩余步骤就很标准模式化了. coinhive作为矿场, 代管着用户生产的加密货币. 用户发请求提出XMR, 就需要提到一个自己的钱包地址保管, 比如[MyMonero](https://mymonero.com). 也可以直接提到Exchange交易所, 在其中交易成其他币种, 包括法币, 然后电汇等等形式提现.
到了这里最后剩余步骤就很标准模式化了coinhive 作为矿场代管着用户生产的加密货币用户发请求提出 XMR就需要提到一个自己的钱包地址保管比如 [MyMonero](https://mymonero.com)也可以直接提到 Exchange 交易所在其中交易成其他币种包括法币然后电汇等等形式提现.
如果对钱包或者交易所感兴趣 (这两个也是很大的话题, 比如钱包分硬件钱包和软件钱包, 离线冷钱包和线上热钱包, private key和recover seeds. 交易所有多种多样的交易对, 有杠杆, 期货, 空和多, 多种挂单类型等等), 可以看[这里](https://www.blockchain-council.org/cryptocurrency/hot-wallet-vs-cold-wallet/)和[这里](https://github.com/txbits/txbits).
如果对钱包或者交易所感兴趣 (这两个也是很大的话题比如钱包分硬件钱包和软件钱包离线冷钱包和线上热钱包private key和recover seeds交易所有多种多样的交易对有杠杆,期货,空和多多种挂单类型等等)可以看[这里](https://www.blockchain-council.org/cryptocurrency/hot-wallet-vs-cold-wallet/)和[这里](https://github.com/txbits/txbits).
# 3 更多讨论
@@ -152,7 +152,7 @@ https://en.wikipedia.org/wiki/Monero_(cryptocurrency)
http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html
https://cryptonote.org/cns/cns001.txt
cns001-008Specs集合
cns001-008Specs 集合
https://en.bitcoin.it/wiki/Why_a_GPU_mines_faster_than_a_CPU
+1 -1
View File
@@ -94,7 +94,7 @@ const incrementAsync = async count => {
### 将 action + reducer 改为两种 action
redux 抽象的 action 与 reducer 的责很清晰,action 负责改 store 以外所有事,而 reducer 负责改 store,偶尔用来做数据处理。这种概念其实比较模糊,因为往往不清楚数据处理放在 action 还是 reducer 里,同时过于简单的 reducer 又要写 action 与之匹配,感觉过于形式化,而且繁琐。
redux 抽象的 action 与 reducer 的责很清晰,action 负责改 store 以外所有事,而 reducer 负责改 store,偶尔用来做数据处理。这种概念其实比较模糊,因为往往不清楚数据处理放在 action 还是 reducer 里,同时过于简单的 reducer 又要写 action 与之匹配,感觉过于形式化,而且繁琐。
重新考虑这个问题,我们只有两类 action:`reducer action``effect action`
+2 -2
View File
@@ -12,7 +12,7 @@
<img src="assets/68/2.jpg" />
产品用户体验不仅是指交互视觉,我的理解用户体验反用户与产品从认知,使用到传播整个情感的连接和反馈。能力上包括了产品设计与功能实现,用户交互界面,以及系统承载能力。
产品用户体验不仅是指交互视觉,我的理解用户体验反用户与产品从认知,使用到传播整个情感的连接和反馈。能力上包括了产品设计与功能实现,用户交互界面,以及系统承载能力。
上图是 CUBI MobelCUBI UX - User Experience Model。完整地说明了用户体验从内容、商业目标、交互、用户目标四个方面组合。
@@ -39,7 +39,7 @@
我试着列了几类
1. 产品商业阶段性目标和最终目标。营收提高,优化结构,工程效率提高,质量提高,能力覆盖。
2. 工程研发过程及能力。投入产出比,系统成本,系统稳定性,创新性
2. 工程研发过程及能力。投入产出比,系统成本,系统稳定性,创新性。
3. 用户可用性。满意度,过程效率,过程质量。
## 总结
+1 -1
View File
@@ -57,7 +57,7 @@ interface VDom {
props: {
[attrKey: string]: string;
};
chindren: VDom[];
children: VDom[];
}
```
+2 -2
View File
@@ -149,9 +149,9 @@ Fiber 利用分片的思想,把一个耗时长的任务分成很多小片,
因此,在组件更新时有可能一个更新任务还没有完成,就被另一个更高优先级的更新过程打断,优先级高的更新任务会优先处理完,而低优先级更新任务所做的工作则会完全作废,然后等待机会重头再来。所以 React Fiber 把一个更新过程分为两个阶段:
- 第一个阶段 Reconciliation PhaseFiber 会找出需要更新的 DOM,这个阶段是可以被打断的;
- 第二个阶段 Commit Phase,是无法打断,完成 DOM 的更新并展示;
- 第二个阶段 Commit Phase,是无法打断,完成 DOM 的更新并展示;
在使用 Fiber 后,需要检查与第一阶段相关的生命周期函数,避免逻辑的多次或重复调用:
在使用 Fiber 后,需要检查与第一阶段相关的生命周期函数,避免逻辑的多次或重复调用:
- componentWillMount
- componentWillReceiveProps
+1 -1
View File
@@ -504,7 +504,7 @@ export default {
## 2.13. Field
与 Value 组件唯一的区别,就是
与 Value 组件唯一的区别,就是支持了 `bind`
### 用法
+128
View File
@@ -0,0 +1,128 @@
# 1. 引言
前端展望的文章越来越不好写了,随着前端发展的深入,需要拥有非常宽广的视野与格局才能看清前端的未来。
笔者根据自身经验,结合下面几篇文章发表一些总结与感悟:
- [A Look at JavaScripts Future](https://www.toptal.com/javascript/predicting-javascript-future)
- [前端开发 20 年变迁史](https://mp.weixin.qq.com/s/yNg7Q0XNLJMnqffTIJhNUg)
- [前端开发编程语言的过去、现在和未来](https://johnhax.net/2019/fe-lang/article1)
- [绕过技术纷争,哪些技术决定前端开发者的未来?](https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247491704&idx=1&sn=95ad66f7fe606801cdac74e296a41783)
- [未来前端的机会在哪里?](https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247490769&idx=1&sn=7ee6e01045a6fe7e15f16aa33afcc2ad&chksm=e92921dede5ea8c8e93489271e8877d2e8688bd511b32e22c287b6c468904c5466b40f6a2bec&xtrack=1&scene=90&subscene=93&sessionid=1562200039&clicktime=1562)
读完这几篇文章可以发现,即便是最资深的前端从业者,每个人看前端未来也有不同的侧重点。这倒不是因为视野的局限,而是现在前端领域太多了,专精其中某几个领域就足够了,适量比全面更好。
同时前端底层也在逐渐封闭,虽然目睹了前端几十年变迁的开发者仍会对一些底层知识津津乐道,但通往底层的大门已经一扇扇逐渐关闭了,将更多的开发者挤到上层区域建设,所以仅学会近几年的前端知识依然能找到不错的工作。
然而上层建设是不封顶的,有人看到了山,有人看到了星球,不同业务环境,不同视野的人看到的东西都不同。
有意思是的国内和国外看到前端未来的视角也不同:国内看到的是追求更多的参与感、影响力,国外看到的是对新特性的持续跟进。
# 2. 精读
前端可以从多个角度理解,比如规范、框架、语言、社区、场景以及整条研发链路。
看待前端未来的角度随着视野不同也会有变化,比如 Serverless 是未来,务实的思考是:前端在 Serverless 研发链路中仅处于使用方,并不会因为用了 Serverless 而提升了技术含量。更高格局的思考是:怎么推动 Serverless 的建设,不把自己局限在前端。
所以当我们读到不同的人对前端理解的时候,有人站在一线前端研发的角度,有人站在全栈的角度,也有人站在业务负责人的角度。其实国内前端发展也到了这个阶段,老一辈的前端开拓者们已经进入不同的业务领域,承担着更多不同的职能分工,甚至是整个大业务线的领导者,这说明两点:
1. 前辈已经用行动指出了前端突破天花板的各种方向。
2. 同是前端未来展望,不同的文章侧重的格局不同,两个标题相同的文章内容可能大相径庭。
笔者顺着这些文章分析角度,发表一些自己的看法。
## 框架
在前端早期,也就是 1990 年浏览器诞生的时候,JS 没有良好的设计,浏览器也没有全面的实现,框架还没出来,浏览器之间就打起来了。
这也给前端发展定了一个基调:凭实力说话。
后面诞生的 Prototype、jquery 都是为了解决时代问题而诞生的,所以有种时代造就前端框架的感觉。
但到了最近几年,React、Angular、Vue 大有前端框架引领新时代的势头,前端要做的不再是填坑,而是模式创新。国内出现的小程序浪潮是个意料之外的现象,虽然群雄割据为开发者适配带来了一定成本,但本质上是中国在前端底层领域争取话语权的行为,而之所以各大公司不约而同的推出自己的小程序,则是商业、经济发展到了这个阶段的自然产物。
在原生开发领域,像 RN、Flutter 也是比较靠谱的移动端开发框架,RN 就长在 React 上,而 Flutter 的声明式 UI 也借鉴了前端框架的思路。每个框架都想往其他框架的领域渗透,所以标准总是很相近,各自的特色并没有宣传的那么明显,这个阶段只选用一种框架是明智的选择,未来这些框架之间会有更多使用场景争夺,但更多的是融合,推动新的开发方式提高生产力。
在数据驱动 UI 的方式上,具有代表性的是 React 的 Immutable 模式与 Vue 的 MVVM 观察者模式,前者模式虽然新颖,但是符合 JS 语言自然运行机制,Vue 的 MVVM 模式也相当好,特别是 Vue3.0 的 API 巧妙的解决了 React Hooks 无法解决的难题。如果 Vue 继续保持蓬勃的发展势头,未来前端 MVVM 模式甚至可能标准化,那么 Vue 是作为标准化的事实规范,还是和 JQuery 一样的命运,还需观察。
## 语言
JS 语言本身有满多缺陷的,但通过 babel 前端工程师可以提前享受到大部分新特性,这在很大程度上抵消了早期语言设计带来的问题。
横向对比来看,我们还可以把编程语言分为:前端语言、后端语言、能编译到 JS 的语言。
之所以有 “能编译到 JS 的语言” 这一类,是因为 JS Runtime 几乎是前端跨平台的通用标准,能编译到 JS 就代表了可跨平台,然而现在 “能编译到 JS 的语言” 除了紧贴 JS 做类型增强的 TS 外,其他并没有火起来,有工具链生态不匹配的原因,也有各大公司之间利益争夺的原因。
后端语言越来越贴场景化,比如 Go 主打轻量级高并发方案,Python 以其易用性占领了大部分大数据、人工智能的运算场景。
与此对应的是前端语言的同质化,前端语言绑定在前端框架的趋势越来越明显,比如 IOS 平台只能用 OC 和 Swift,安卓只能用 JAVA 和 KotlinFlutter 只支持 Dart,与其说这些语言更适合这些平台特性,不如说背后是谷歌、苹果、微软等巨头对平台生态掌控权的争夺。Web 与移动端要解决的问题是类似的:如何高效管理 UI 状态,现在大部分都采用数据驱动的思路,通过 JSX 或 Template 的方式描述出 UI DSL(更多可参考 [前端开发编程语言的过去、现在和未来](https://johnhax.net/2019/fe-lang/article1) UI DSL 一节)、以及性能提升:渲染和计算分离(这里又分为并发与调度两种实现思路,目的和效果是类似的)。
所以编程语言的未来也没什么悬念,前端领域如果有的选就用 JS,没得选只能依附所在平台绑定的语言,而前端语言最近正在完成一轮升级大迁徙:JS -> TSJAVA -> KotlinOC -> Swift,前端语言的特性、易用性正在逐步趋同。需要说明的是,如果仅了解这些语言的语法,对编程能力是毫无帮助的,了解平台特性,解决业务问题,提供更好的交互体验才是前端应该不断追求的目标,随着前端、Native 开发者之间的流动,前端领域语言层面差异会会来越小,大家越关注上层,越倾向抹平语言差异,甚至可能 All in JS,这不是因为 JS 有多大野心,而是因为在解决的问题趋同、业务优先的大背景下,大家都需要减少语言不通带来的障碍,最好的办法就是统一语言,从人类语言的演变就可以发现,要解决的问题趋同(人类交流)、与国家绑定的小众语言一直都有生存空间、语法大同小异,但不同语言都有一定自己的特色(比如法语表意更精确)、跨语言学习成本高,所以当国际化协作频繁时,一定会催生一套官方语言(英语),而使用基数大的语言可能会发展为通用国际语言(中文)。
将编程语言的割裂、统一比作人类语言来看,就能理解现状,和未来发展趋势了。
## 可视化
前面也说过,前端的底层在逐渐封闭,而可视化就是前端的上层。
所以笔者很少提到工程化,原因就是未来前端开发者接触工程化的机会越来越少,工程化机制也越来越完善,前端会逐渐回归到自己的本质 - 人机交互,而交互的重要媒介就是图形,无论组件库还是智能化设计稿 To Code 都为了解放简单、模式化的交互工作,专业前端将更多聚集到图形化领域。
图形和数据是分不开的,所以图形化还要考虑性能问题与数据转换。
可视化是对性能要求最高的,因此像 web worker、GPU 加速都是常见处理手段,WASM 技术也会用到可视化中。具体到某个图表或大屏的性能优化,还会涉及数据抽样算法,分层渲染等,仅仅性能优化领域就有不少探索的空间。性能问题一般还伴随着数据量大,所以数据序列化方案也要一并考虑。
可视化图形学是非常学术的领域,从图形语法到交互语法,从一图一做的简单场景,到可视化分析场景的灵活拓展能力,再到探索式分析的图形语法完备性要求,可视化库想要一层层支持不同业务场景的需求,要有一个清晰的分层设计。
仅可视化的图形学领域,就足够将所有时间投入了,未来做可视化的前端会越来越专业,提供的工具库接口也越来越有一套最佳实践沉淀,对普通前端越来越友好。
BI 可视化分析就是前端深造的一个方向,跟随 BI 发展阶段,对前端的要求也在不断变化:工程化、组件化、搭建技术、渲染引擎、可视化、探索式、智能化,跟上产品对技术能力的要求,其实是相当有挑战性的。
## 编辑器
编辑器方向主要有 IDE(Web IDE)、富文本编辑器。
**IDE 方向** 国产做的比较好的是 HBuilder,国际上做的比较好的是 VSCode,由于微软还同时推出了 Web 版 MonacoEditor,让 Web IDE 开发的门槛大大降低。
作为使用者,现在和未来的主流可能都是微软系,毕竟微软在操作系统、IDE 方面人才储备和经验积累很多。但随着云服务的变迁,引导着开发方式升级,IDE 游戏规则可能迎来重大改变 - 云化。云化使得作为开发者拥有更多竞争的机会,因为云上 IDE 市场现在还是蓝海,现在很多创业公司和大公司内部都在走这个方向,这标志着中国计算机技术往更底层的技术发展,未来会有更多的话语权。
从发展阶段来说,前端也发展到了 Web IDE 这个时代。对大公司来说,内部有许许多多割裂的工程化孤岛,不仅消耗大量优秀的前端同学去维护,也造成内部物料体系、工程体系难以打通,阻碍了内部技术流通,而云 IDE 天生的中心化环境管理可以解决这个问题,同时还能带来抹平计算机环境差异、统一编译环境、源码不落盘、甚至实现自动的多人协作也成为了可能,而云 IDE 因为在云上,也不止于 IDE,还可以很方便的集成流程,将研发全链路打通,因此在阿里内部也成为了今年四大方向之一。
所以今年可以明显看到的是,前端又在逐步替代低水平重复的 UI 设计,从设计稿生成代码,到研发链路上云,这种顶层设计正在进一步收窄前端底层建设,所以未来会有更多专业前端涌入可视化领域。
**富文本编辑器方向** 是一个重要且小众的领域,老牌做的较好的是 UEditor 系列,现在论体验和周边功能完善度,做得最好的是语雀编辑器。开源也有很多优秀的实现,比如 Quill、DraftJS、Slate 等等,但现在富文本编辑器核心能力是功能完备性(是否支持视频、脑图、嵌入)、性能、服务化功能打通了多少(是否支持在线解析 pdf、ppt 等文件)、交互自然程度(拷贝内容的智能识别)等等。如果将眼光放到全球,那国外有大量优秀富文本编辑器案例,比如 Google Docs、Word Online、iCloud Pages 等等。
最好用的富文本编辑器往往不开源,因为投入的技术研发成本是巨大的,本身这项技术就是一个产品,卖点就是源码。
富文本编辑器功能强度可以分为三个级别:L0~L2:
- L0:利用浏览器自带的输入框,主要指 `contenteditable` 实现。
- L1:在 L0 的基础上通过 DOM API 自主实现增删改的功能,自定义能力非常强。
- L2:从输入框、光标开始自主研发,完全不依赖浏览器特性,如果研发团队能力强,可以实现任何功能,典型产品比如 Google Docs。
无论国内外都鲜有进入 L2 强度的产品,除了超级大公司或者主打编辑器的创业公司。
所以编辑器方向中,无论 IDE 方向,还是富文本编辑器方向,都值得深入探索,其中 IDE 方向更偏工程化一些,考验体系化思维,编辑器方向更偏经验与技术,考验基本功和架构设计能力。
## 智能化
笔者认为智能化离前端这个工种是比较远的,智能化最终服务前后端,给前后端开发效率带来一个质的提升,而在此之前,作为前端从业者无非有两种选择:加入智能化开拓者队伍,或者准备好放弃可能被智能化替代的工作内容,积极投身于智能化解放开发者双手后,更具有挑战性的工作。这种挑战性的工作恰好包括了上面分析过的四个点:语言、框架、可视化、编辑器。
类比商业智能化,商业智能化包括网络协同和数据智能,也就是大量的网络协同产生海量数据,通过数据智能算法促进更好的算法模型、更高效的网络协同,形成一个反馈闭环。前端智能化也是类似,不管是自动切图、生成图片、页面,或者自动生成代码,都需要算法和前端工程师之间形成协同关系,并完成一个高效的反馈闭环,算法将是前端工程师手中的开发利器,且越规模化的使用功效越大。
另一种智能化方向是探索 BI 与可视化结合的智能化,通过功能完备的底层图表库,与后端通用 Cube 计算模型,形成一种探索式分析型 BI 产品,Tableau 就是典型的案例,在这个智能化场景中,需要对数据、产品、可视化全面理解的综合性人才,是前端职业生涯另一个突破点。
# 3. 总结
本文列举的五点显然不能代表前端的全貌,还遗漏了太多方面,比如工程化、组件化、Serverless 等,但 **语言、框架、可视化、编辑器、智能化** 这五个点是笔者认为前端,特别是国内前端值得持续发力,可以做深的点,成为任何一个领域的专家都足以突破前端工程师成长的天花板。
最后,前端是最贴近业务的技术之一,业务的未来决定了前端的未来,创造的业务价值决定了前端的价值,从现在开始锻炼自己的商业化思考能力与产品意识,看得懂业务,才能看到未来。
> 讨论地址是:[精读《前端未来展望》 · Issue #178 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/178)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)
+303
View File
@@ -0,0 +1,303 @@
# 1. 引言
[javascript-knowledge-reading-source-code](https://www.smashingmagazine.com/2019/07/javascript-knowledge-reading-source-code/) 这篇文章介绍了阅读源码的重要性,精读系列也已有八期源码系列文章,分别是:
- [精读《Immer.js》源码](https://github.com/dt-fe/weekly/blob/v2/048.%E7%B2%BE%E8%AF%BB%E3%80%8AImmer.js%E3%80%8B%E6%BA%90%E7%A0%81.md)
- [精读《sqorn 源码》](https://github.com/dt-fe/weekly/blob/v2/073.%E7%B2%BE%E8%AF%BB%E3%80%8Asqorn%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
- [精读《Epitath 源码 - renderProps 新用法》](https://github.com/dt-fe/weekly/blob/v2/075.%E7%B2%BE%E8%AF%BB%E3%80%8AEpitath%20%E6%BA%90%E7%A0%81%20-%20renderProps%20%E6%96%B0%E7%94%A8%E6%B3%95%E3%80%8B.md)
- [精读《Htm - Hyperscript 源码》](https://github.com/dt-fe/weekly/blob/v2/082.%E7%B2%BE%E8%AF%BB%E3%80%8AHtm%20-%20Hyperscript%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
- [精读《React PowerPlug 源码》](https://github.com/dt-fe/weekly/blob/v2/092.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20PowerPlug%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
- [精读《syntax-parser 源码》](https://github.com/dt-fe/weekly/blob/v2/093.%E7%B2%BE%E8%AF%BB%E3%80%8Asyntax-parser%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
- [精读《react-easy-state 源码》](https://github.com/dt-fe/weekly/blob/v2/098.%E7%B2%BE%E8%AF%BB%E3%80%8Areact-easy-state%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
- [精读《Inject Instance 源码》](https://github.com/dt-fe/weekly/blob/v2/110.%E7%B2%BE%E8%AF%BB%E3%80%8AInject%20Instance%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
笔者自己的感悟是,读过大量源码的程序员有以下几个特质:
1. 思考具有系统性,主要体现在改一处代码模块时,会将项目所有文件串联起来整体考虑,提前评估影响面。
2. 思考具有前瞻性,对已实现的方案可以快速评价所处阶段(临时 or 标准 or 可拓展),将边界情况提前解决,将框架 BUG 降低到最小程度。
3. 代码实现更优雅,有大量源码经验做支撑,解决同样问题时,这些程序员可以用更短的行数、更合适的三方库解决问题,代码可读性更好,模块拆分更合理,更利于维护。
既然阅读源码这么重要,那么怎么才能读好源码呢?本周精读的文章就是一篇方法论文章,告诉你如何更好的阅读源码。
# 2. 概述
原文分三个部分:阅读源码的好处、阅读源码的技巧、以及 Redux Connect 的案例研究。
## 阅读源码的好处
阅读源码有助于理解抽象的概念,比如虚拟 DOM;有助于做方案调研,而不仅仅只看 Github star 数量;了解优秀框架目录结构的设计;看到一些陌生的工具函数,还可能激发你对 JS 规范的查阅,这种问题驱动的方式也是笔者推荐的 JS 规范学习方式。
## 阅读源码的技巧
最好的阅读源码方式是看文章,如果源码的作者有写源码解读文章,这就是最省力的方式。虽然直接看代码可以了解到所有细节,但当你不清楚设计思路时,仅看源码可能会找不到方向,而读源码的最终目的是找到核心的设计理念,如果一个框架没有自己核心设计理念,这个框架也不值得诞生,更不值得被阅读。如果框架的作者已经将框架核心理念写成了文章,那读文章就是最佳方案。
还有一种方式是断点,写一个最小程序,在框架执行入口出打下断点,然后按照执行路径一步步理解。虽然执行路径中会存在大量无关的函数干扰精力,但如果你足够有耐心,当断点走完时一定会有所收获。
原文还提到了一种看源码方式,即没有目的的寻宝。在寻找框架主要思路的过程中,遇到一些有意思的函数,可以停下来仔细阅读,可能会发现一些对你有启发的代码片段。
## Redux Connect 案例研究
原文以 Redux Connect 作为案例介绍研究思路。
首先看到 Connect 的功能 “包装组件” 后,就要问自己两个问题:
1. Connect 是如何实现包装组件后原样返回组件,但却增强组件功能的?(高阶组件知识)
2. 了解这个设计模式后,如何利用已有的文档实现它?
通过创建一个使用 Connect 的基本程序:
```js
class MarketContainer extends Component {
}
const mapDispatchToProps = dispatch => {
return {
updateSummary: (summary, start, today) => dispatch(updateSummary(summary, start, today))
}
}
export default connect(null, mapDispatchToProps)(MarketContainer);
```
比如从生成 connect 函数的 [createConnect](https://github.com/reduxjs/react-redux/blob/master/src/connect/connect.js#L46) 我们就可以学习到 [Facade Pattern](http://jargon.js.org/_glossary/FACADE_PATTERN.md) - 门面模式。
`createConnect` 函数调用处:
```js
export function createConnect({
connectHOC = connectAdvanced,
mapStateToPropsFactories = defaultMapStateToPropsFactories,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
mergePropsFactories = defaultMergePropsFactories,
selectorFactory = defaultSelectorFactory
} = {})
```
我们可以学习到解构默认函数参数的知识点。
总之,在学习源码的过程中,可以了解到一些新的 JS 特性,一些设计模式,这些都是额外的宝藏,不断理解并学会运用到自己写的框架里,就实现了源码学习的目的。
# 3. 精读
原文介绍了学习源码的两个技巧,并利用 Redux Connect 实例说明了源码学习过程中可以学到许多周边知识,都让我们受益匪浅。
笔者结合之前写过的八篇源码分析文章,把最重要的设计思路提取出来,以实际的例子展示阅读源码能给我们思维带来哪些帮助。
## Immerjs 源码的精华
Immer 可以让我们以 Mutable 的方式更新对象,最终得到一个 Immutable 对象:
```js
this.setState(produce(state => (state.isShow = true)))
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/048.%E7%B2%BE%E8%AF%BB%E3%80%8AImmer.js%E3%80%8B%E6%BA%90%E7%A0%81.md#3-%E7%B2%BE%E8%AF%BB)。
核心思路是利用 Proxy 把脏活累活做掉。上面的例子中,`state` 已经是一个代理(Proxy)对象,通过自定义 `setting` 不断递归进行浅拷贝,最后返回一个新引用的顶层对象作为 `produce` 的返回值。
从 Immerjs 中,我们学到了 Proxy 可以化腐朽为神奇的用法,比看任何 Proxy 介绍文章都直观。
## sqorn 源码的精华
sqorn 是一个 sql orm,举例来看:
```js
const sq = require("sqorn-pg")();
const Person = sq`person`,
Book = sq`book`;
// SELECT
const children = await Person`age < ${13}`;
// "select * from person where age < 13"
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/073.%E7%B2%BE%E8%AF%BB%E3%80%8Asqorn%20%E6%BA%90%E7%A0%81%E3%80%8B.md#3-%E7%B2%BE%E8%AF%BB)
核心思路是在链式调用过程中创建 context 存储结构,并在链式调用的时候不断填充 context 信息,最终拿到的是一个结构化 context 对象,生成 sql 语句也就简单了。
从 sqorn 中,我们学到了如何实现链式调用 `init().a().b().c().print()` 最后拿到一个综合的结果,原理是内部维护了一个不断修改的对象。不论前端 React Vue 还是后端框架 Koa 等,一般都有内置的 context,一般实现这种优雅语法的框架内部都会维护 context。
## Epitath 源码的精华
Epitath 在 React Hooks 之前出来,解决了高阶函数地狱的问题:
```js
const App = epitath(function*() {
const { count } = yield <Counter />
const { on } = yield <Toggle />
return (
<MyComponent counter={count} toggle={on} />
)
})
<App />
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/075.%E7%B2%BE%E8%AF%BB%E3%80%8AEpitath%20%E6%BA%90%E7%A0%81%20-%20renderProps%20%E6%96%B0%E7%94%A8%E6%B3%95%E3%80%8B.md#3-%E7%B2%BE%E8%AF%BB)
其核心是利用 `generator` 的迭代,将 React 组件的平级结构还原成嵌套结构,将嵌套写法打平了:
```
yield <A>
yield <B>
yield <C>
// 等价于
<A>
<B>
<C />
</B>
</A>
```
从 epitath 中,我们了解到 `generator` 原来可以这么用,正因为其执行是多次迭代的,因此我们可以利用这个特性,改变代码运行结构。
## Htm - Hyperscript 源码的精华
Htm 将模版语法很自然的融入到了 html 中:
```js
html`
<div class="app">
<${Header} name="ToDo's (${page})" />
<ul>
${todos.map(
todo => html`
<li>${todo}</li>
`
)}
</ul>
<button onClick=${() => this.addTodo()}>Add Todo</button>
<${Footer}>footer content here<//>
</div>
`;
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/082.%E7%B2%BE%E8%AF%BB%E3%80%8AHtm%20-%20Hyperscript%20%E6%BA%90%E7%A0%81%E3%80%8B.md#3-%E7%B2%BE%E8%AF%BB)
其核心是怎么根据模版拿到 dom 元素的 AST?拿到 AST 后就方便生成后续内容了。
作者的办法是:
```js
const TEMPLATE = document.createElement("template");
TEMPLATE.innerHTML = str;
```
这样 TEMPLATE 就自带了 AST 解析,这是利用浏览器自带的 AST 解析拿到了 AST。从 Htm 中,我们学到了 `innerHTML` 可以生成标准 AST,所以只要有浏览器运行环境,需要拿 AST 的时候,不需要其他库,`innerHTML` 就是最好的方案。
## React PowerPlug 源码的精华
React PowerPlug 是一个利用 render props 进行状态管理的工具库。
它可以在 JSX 中对任意粒度插入状态管理:
```js
<Value initial="React">
{({ value, set, reset }) => (
<>
<Select
label="Choose one"
options={["React", "Preact", "Vue"]}
value={value}
onChange={set}
/>
<Button onClick={reset}>Reset to initial</Button>
</>
)}
</Value>
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/092.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20PowerPlug%20%E6%BA%90%E7%A0%81%E3%80%8B.md#2-%E7%B2%BE%E8%AF%BB)
这个库的核心就是利用 render props 解决 JSX 局部状态管理的痛点,通过读源码了解 render props 的使用方式是这个源码带给你的最大价值。
## syntax-parser 源码的精华
syntax-parser 是一个 JS 版语法解器生成器,笔者也是作者,使用方式:
```js
import { createParser, chain, matchTokenType, many } from "syntax-parser";
const root = () => chain(addExpr)(ast => ast[0]);
const addExpr = () =>
chain(matchTokenType("word"), many(addPlus))(ast => ({
left: ast[0].value,
operator: ast[1] && ast[1][0].operator,
right: ast[1] && ast[1][0].term
}));
const addPlus = () =>
chain("+"), root)(ast => ({
operator: ast[0].value,
term: ast[1]
}));
const myParser = createParser(
root, // Root grammar.
myLexer // Created in lexer example.
);
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/093.%E7%B2%BE%E8%AF%BB%E3%80%8Asyntax-parser%20%E6%BA%90%E7%A0%81%E3%80%8B.md#2-%E7%B2%BE%E8%AF%BB)
syntax-parser 的核心是利用双向链表实现了可回溯的语法解析器,了解了这个库,你可以自己实现 JS 调用堆栈,并在任意时候返回某个之前的执行状态重新执行。同时这个库的源码也会加强你对链表的理解,以及拓展你对链表使用场景的想象。
## react-easy-state 源码的精华
react-easy-state 利用 Proxy 创建了一个简易的全局数据流管理方式:
```js
import React from "react";
import { store, view } from "react-easy-state";
const counter = store({ num: 0 });
const increment = () => counter.num++;
export default view(() => <button onClick={increment}>{counter.num}</button>);
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/098.%E7%B2%BE%E8%AF%BB%E3%80%8Areact-easy-state%20%E6%BA%90%E7%A0%81%E3%80%8B.md)
react-easy-state 利用了 [observer-util](https://github.com/nx-js/observer-util) 实现主要功能,从中我们能学到最有价值的就是 Proxy 与 React 结合的设计理念,即利用 `getter` `setter` 实现数据与视图的双向绑定,或者叫依赖追踪,更多细节就不在这里展开,感兴趣可以阅读笔者之前写的 [抽丝剥茧,实现依赖追踪](https://github.com/dt-fe/weekly/blob/master/35.%E7%B2%BE%E8%AF%BB%E3%80%8Adob%20-%20%E6%A1%86%E6%9E%B6%E5%AE%9E%E7%8E%B0%E3%80%8B.md#%E6%8A%BD%E4%B8%9D%E5%89%A5%E8%8C%A7%E5%AE%9E%E7%8E%B0%E4%BE%9D%E8%B5%96%E8%BF%BD%E8%B8%AA) 一节。
## Inject Instance 源码的精华
inject-instance 是一个 Class 实现依赖注入的库:
```js
import {inject} from 'inject-instance'
import B from './B'
class A {
@inject('B') private b: B
public name = 'aaa'
say() {
console.log('A inject B instance', this.b.name)
}
}
```
> 详细源码解读可以阅读 [这里](https://github.com/dt-fe/weekly/blob/v2/110.%E7%B2%BE%E8%AF%BB%E3%80%8AInject%20Instance%20%E6%BA%90%E7%A0%81%E3%80%8B.md#2-%E7%B2%BE%E8%AF%BB)
主要对我们有两个启发,第一可以利用装饰器为对象存储一些额外信息,这些信息在必要的时候我们可以用到;第二是依赖注入并不复杂,通过提前实例化后,可以解决循环依赖的问题,即所有循环依赖问题都可以通过加一个父级解决。
# 4. 总结
阅读代码不是目的,读懂源码背后要表达的核心设计思路才是目的。比如写脚手架,阅读了大量脚手架源码的人写出的代码,与一个没有经验的人写出的代码会有天壤之别,这之间的差距就是对一些设计模式、三方库、结构设计的经验差距。
只学习理论太空洞,只看代码又太局限,学会从代码中看出理论才是最佳学习方式。
> 讨论地址是:[精读《源码学习》 · Issue #179 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/179)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)
+96
View File
@@ -0,0 +1,96 @@
# 1. 引言
Node12 发布有几个月了,让我们跟随 [Nodejs 12](https://blog.logrocket.com/node-js-12/) 一起看看 Node12 带来了哪些改变。
# 2. 概述
Node12 与以往的版本不同,带来了许多重大升级,包括更多 V8 特性,Http 解析速度的提升,启动速度的提升,更好的诊断报告、内置堆分析工具,ESM 模块的更新等。
## V8 引擎升级
V8 升级带来了如下几个特性:
- [zero-cost async 堆栈信息](https://v8.dev/blog/v8-release-72#async-stack-traces) 原生支持了 async 堆栈信息,不会添加额外运行时内容。
- [参数数量不匹配时性能优化](https://v8.dev/blog/v8-release-74#faster-calls-with-arguments-mismatch) 即便参数传递多了或少了,现在都几乎不会影响 Node 的执行速度。
- [更快的 async](https://v8.dev/blog/v8-release-73#faster-await) async /await 已经比 promises 快了两个 microticks。
- [更快的 Js 解析速度](https://v8.dev/blog/v8-release-72#javascript-parsing) 网页中的 V8 引擎一般花费 9.5% 时间在 JS 解析上,经过解析加速后,现在花费在 JS 解析上的时间降低到平均 7.5%。
可见 V8 引擎的升级不仅给 Node12 带来了福音,也会一定程度上提升网页的运行效率。
## TLS 1.3 更好的安全性
随着 Node12 的发布,TLS 从 1.2 升级到了 1.3,更安全且更易配置。通过使用 TLS 1.3,Node 程序可以减少 Https 握手所需时间来提升请求性能。
## 默认堆被正确配置了
以前默认堆大小需要通过 `-max-old-space-size` 设置,而且默认值是一个固定值,现在这个默认值可以根据可用内存动态分配,这样当内存较小时,Node 不会让内存移除而报错,而是主动终止自己的进程。
## 默认的 http 解析器变为 llhttp
nodejs 的 [http-parser](https://github.com/nodejs/http-parser) 已经非常难以维护和优化了,因此 [llhttp](https://github.com/nodejs/llhttp#readme) 这个库,比 http-parser 快 156%,更重要的是,在 Node12 中,将默认解析器切换到了 llhttp。
## 提供诊断报告
Node12 有一项实验功能,根据用户需求提供诊断报告,包括崩溃、性能下降、内存泄露、CPU 使用高等等。
## 堆内存 dump
在以前,如果要将堆内存生成 dump 文件,需要在生产环境安装额外的模块,而 Node12 集成了这个功能。
## 更好的原生模块支持
C++ 拓展 [N-API](https://nodejs.org/api/n-api.html#n_api_n_api) 升级到版本 4,同时一个原生模块可以被 C++ 编写并发布到 npm,就像一个普通 JS 模块一样被引用。不过要注意一些区别:
| | | JS 模块 | 原生拓展 |
| --- | -------------------------------------- | ------- | ------------------ |
| 1. | ... 需要编译 | 否 | 如果预编译了则不用 |
| 2. | ... 是否可以运行在所有平台 | 是 | 如果预编译了则可以 |
| 3. | ... 是否兼容所有 Node 版本 | 是 | 否 |
| 4. | ... 会被加载多次 | 是 | 否 |
| 5. | ... 如果没有明确使用多线程,则线程安全 | 是 | 否 |
| 6. | ... 可以被销毁 | 是 | 否 |
## Worker 被正式启用了
`--experimental-worker` 实验开关已取消,默认支持 `worker_threads`
要注意的是,执行 CPU 密集型任务时适合用 worker(大量计算),而执行 I/O 密集型任务时,Worker 反而没有 Node 内置的 I/O 操作性能好(读写文件)。
## 启动速度优化
通过在构建时提前为内置库生成代码缓存,最终使启动时间加快 30%。
## 支持 ES6 module
Node12 对 ES6 module 的支持依然处于实验阶段,需要通过 `--experimental-modules` 开启。
简单来说,就是支持了 Import Export 语法,不需要再转成 `require` 了!如果在 `package.json` 增加 `"type": "module"` 的配置,Node 将按照 ES6 module 方式处理。
## 新的编译器和平台要求
由于升级到新的 V8 引擎以及内部改造,因此 Node12 在 Mac 与 Windows 之外的平台上,需要至少 GCC6 和 glibc 2.17。
# 3. 精读
对于 V8 引擎升级、TLS 升级、堆配置自动化、http-parser 升级到 llhttp、启动速度优化都属于被动优化,代码无需改动,只要升级 Node 版本就可以享受。
支持 ES6 module 这个特性其实比较鸡肋,毕竟源码用 Ts 写的话,这些升级并不会对源码产生影响。
`worker_threads` 可以被默认启用,就像以前支持 `async/await` 一样,会带来 Nodejs 多线程更广泛的使用。
Node12 更新了 V8 引擎,随着 V8 的更新,很多 ES 新规范也落地了,比如 Class 成员函数、私有成员变量等等。
# 4. 总结
Nodejs 仅有 10 年历史,但现在越来越被开发者欢迎,因为它可以让 JS 运行在服务端,是扩大 JS 生态的重要一环。从 Node 更新历史中可以看到,性能和语法能力稳步提升,一些服务端环境需要的诊断报告、堆栈分析能力都在逐渐完善,社区上也有 Alinode 与 egg、express、koa 等好用的服务框架,相对于前端翻天覆地的变化,对 Node 的评价只有一个字:稳。
> 讨论地址是:[精读《Nodejs V12》 · Issue #184 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/184)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)
+193
View File
@@ -0,0 +1,193 @@
# 1. 引言
[谁在世界中心](https://book.douban.com/subject/27045287/) 是一本介绍地缘政治的书,这本书以海洋为连接世界的主要桥梁,介绍了当今全球视野下海洋争霸的政治格局。
谁征服了海洋,谁就征服了世界。陆地霸权注定无法拥有全球视野,只有海洋霸权才能征服世界,如今中国已成为海洋贸易霸主,但海洋的武力霸主仍然是美国,如果中国想成为新的全球霸主,就要突破旧的海洋霸权封锁,成为新的海洋霸主。
当然想成为海洋霸主是非常困难的,这涉及到多方政治力量的博弈,但我们可以通过《谁在世界中心》这本书了解地缘政治关系,让我们看清当下,布局未来。
《谁在世界中心》共五章,分别介绍了当下谁在主宰世界、东亚与西太平洋、东南亚与南海、南亚与印度洋、俄罗斯与北冰洋。
之所以标题都是地区与海的关系,是因为陆地与海洋的博弈就是海洋霸权的逻辑。本书需要结合地图理解,因此笔者会贴一些书中地图,围绕着地图讲解本书。
# 2. 精读
## 谁在主宰世界
现在 **美、俄、欧、中** 是这个舞台的主角,但谁也不能仅凭一个地区征服世界,因此与一些重要地区结盟,并成为地区的领导者才可能成为世界的霸主。**边缘地带理论** 就是指,控制了大陆板块的边缘地区,就可以对大陆进行封锁,进而控制大陆。在将眼光放到边缘地区之前,先看看现在世界舞台上的主要政治力量:
1. 俄罗斯 - 大陆的征服者。俄罗斯一直有扩张的野心,但是在苏联解体后,值保有大部分欧亚大陆中心地带,目前已经失去领衔主演的资格。
2. 欧盟 - 世界的发现者。作为大航海时代的开启者,欧洲史就是浓缩的世界史,并且随着疯狂的资本掠夺积累了大量原始资本。但由于英国在欧洲板块处于海洋势力,无法完全控制大陆,因此极力避免欧洲土地上出现一家独大的情况,这导致了美国的崛起。当然现在欧盟的成立也标志着欧洲进入了漫长的整合时期,德国由于其较差的地缘位置(二战后海外利益尽失),更愿意以裹挟欧盟的方式让自己成为主角。
3. 印度 - 低纬度地区的代言人。由于低纬度炎热的气候,印度人并不热衷于国际事务,但和美国一样,印度也发展了自己的地缘优势以及人口优势,希望代表低纬度地区参与大国游戏。但是要承受另一个边缘地区国家 - 中国的压力。
4. 中国 - 世界中心最有力的挑战者。中国拥有极大的战略纵深,集体主义文化,拥有挑战世界霸主的潜力,但在这个道路上还需解决许多问题,尤其是如何突破由美国主导的 “新世界岛俱乐部” 的封锁。
5. 美国 - **“新世界岛俱乐部”** 的缔造者。北美是新世界岛的中心地区,英国和日本是新世界岛的外围地区,分别用来控制 “欧亚大陆西边缘地区(西欧)” 与 “欧亚大陆东边缘地区(中国)”。
为什么拥有海洋就拥有了世界?“海权论” 有三个主要观点:
1. **谁掌握了世界核心的咽喉航道、运河和航线,谁就掌握了世界经济和能源运输之门。**
2. **谁掌握了世界经济和能源运输之门,谁就掌握了世界各国的经济和安全命脉。**
3. **谁掌握了世界各国的经济和安全命脉,谁就控制了全世界。**
但独霸海洋非常困难,因此美国奉行的是 “边缘地带理论”。也就是**通过控制欧亚大陆东西两端的边缘地带,进而控制了欧亚大陆核心地区,封锁住欧亚大陆的强国,以此保证美国世界霸主的地位。**
<img width=400 src="https://img.alicdn.com/tfs/TB1R76hca61gK0jSZFlXXXDKFXa-1816-2648.jpg">
<img width=400 src="https://img.alicdn.com/tfs/TB1gaHecXP7gK0jSZFjXXc5aXXa-1816-2648.jpg">
从上图可以看出,以北美为 “新世界岛” 的中心地区,通过控制日本与英国,牵制住西欧与中国。美国实际上也做到了这一点。而随着印度的崛起,美国也找到了澳大利亚作为遏制印度的桥头堡。
那么中国怎么崛起呢?很显然,中国需要组建属于自己的 “世界岛俱乐部”,取代由美国主导的 “旧世界岛俱乐部”:
1. **与欧亚大陆中心地带的大部分国家(主要是俄罗斯)结盟。**
2. **将 “欧亚大陆南边缘地区”(印度)拉入同盟。**
3. **寻找可能的 “世界岛外围地区”,并使之倒向同盟(日本、韩国、朝鲜等)。**
但就目前状况来看,中印关系竞争与合作同时上升,俄国由于前苏联的老大地位暂时不愿意放下身段,日本更处于美国为中心的俱乐部中,因此这条路困难重重。之所以将日本拉进来,一方面是因为与印、俄结盟不足以取得与 “旧世界岛俱乐部” 竞争的优势,一方面是中日地缘距离近,且日本国民性格敬仰强大的对手,另一方面日本是美国牵制中国的力量,拉拢过来不仅可以打消美国的算盘,还能增强东亚整体实力。
第一章总览了世界地缘政治关系的全貌,并为中国崛起指出了道路。后面几章则具体介绍各个存在联动的政治板块间的具体博弈情况,做到知己知彼。
## 东亚与西太平洋
<img width=500 src="https://img.alicdn.com/tfs/TB1DbnfcoY1gK0jSZFMXXaWcVXa-1746-1480.png">
参与东亚与西太平洋博弈的主要国家有:**中国、朝鲜、韩国、日本、俄罗斯**。中国是参与板块博弈的核心,比如俄罗斯会在朝鲜半岛问题方面发表意见,但不会干涉钓鱼岛问题,而中国都参与其中。
东亚平原如此广袤,以至于东亚地区的民族都认为控制了这片核心区就控制了世界中心。但随着西方殖民者从海路上到来,**中国人才明白自己并不是世界的中心**,但长期 “中央之国的心态” 影响着我们每一个人。
<img width=500 src="https://img.alicdn.com/tfs/TB1fqvHceL2gK0jSZPhXXahvXXa-1594-1368.png">
**中国农耕区域总是受到来自北方三个势力的威胁:**“东北森林渔猎民族”、“蒙古高原草原游牧民族”、“青藏高原高原游牧民族”,这是由于农耕的生产方式稳定,创造的财富大,因此源源不断吸引这些外来者的入侵,有趣的是,每一次农耕区域都能同化外来的入侵者,而 “中国” 的传统观念也是同化他们的重要因素。所以到后面会讲到为何印度人进取心不如中国强,原因就在中国需要长期与北方威胁斗争,而印度不需要,印度由于地缘位置,导致不会受到太多来自边远民族的入侵,这个在分析印度时会讲到。
**日本、朝鲜半岛由于地理阻隔,在东亚大陆统一时得保持独立**。而朝鲜半岛与大陆相连却一直没有被征服的原因是,从地图上看,想要入侵朝鲜半岛必须沿着海岸线,但通过辽西走廊进入辽河平原时,辽河平原地理气候的不稳定性容易切断朝鲜半岛与东亚核心区脆弱的地缘联系,导致渗入半岛的人口要么退回,要么融于当地族群。
<img width=500 src="https://img.alicdn.com/tfs/TB1JdHKcoz1gK0jSZLeXXb9kVXa-1582-1496.png">
东亚面临西太平洋区域被外包包夹形成四个 “内海”,可以形容为 **“第一岛链” 与 “第二岛链”**,美国正是通过控制这些岛链来控制 “欧亚大陆东边缘地区” 的。
**第一岛链包括:日本群岛、琉球群岛、冲绳岛、台湾岛、南至菲律宾群岛、大巽他群岛**。其中日本是势力最大的岛链,在日本 “大东亚共荣圈” 计划中,极盛时期控制的范围如下图所示:
<img width=500 src="https://img.alicdn.com/tfs/TB1GBzJceT2gK0jSZFvXXXnFXXa-1610-1452.png">
而中国想要成为世界霸主,**就要构建以中国为主导核心的 “东亚核心圈 + 东盟十国”**,见下图。
对日本来说,如今已没有实力做这个核心圈的老大,但最起码希望和中国共同主导,但核心圈只有一家独大才能发挥称霸世界的力量,中国与日本还有很多问题需要解决。相比欧盟,虽然也在融合(3 + 10 模式,即三个核心 - 法、德、英 + 10 个其他国家 不包含俄罗斯),但由于地缘特点不可能出现一家独大的情况。
<img width=500 src="https://img.alicdn.com/tfs/TB1USbGcaL7gK0jSZFBXXXZZpXa-1864-1606.png">
**第二岛链包括:从日本岛作为起点,南经小笠原诸国、火山列岛、马里亚纳群岛、关岛、雅浦岛、帕劳群岛,直至哈马黑拉岛等岛群。** 不过第二岛链的威胁远没有第一岛链大。
<img width=500 src="https://img.alicdn.com/tfs/TB1uRPKchD1gK0jSZFsXXbldVXa-1604-1134.png">
从西太平洋向东看看美国。**对美国来说,太平洋所有岛屿都是他进攻的跳板**。如上图所示,美国通过诸多岛屿作为跳板进攻,在二战中,甚至跳过了对某些战略要地的争夺,通过前沿岛屿作为基地,直接攻击日本本岛。
## 东南亚与南海
<img width=500 src="https://img.alicdn.com/tfs/TB1H7HOckH0gK0jSZPiXXavapXa-1870-1514.png">
**东南亚区域分位:中南半岛(缅甸、越南与印度支那、泰国)、南洋群岛、文莱、巴厘岛以及东帝汶、马六甲海峡等重要区域。**
首先看中南半岛:
<img width=500 src="https://img.alicdn.com/tfs/TB1exPKcaL7gK0jSZFBXXXZZpXa-1742-2534.png">
**中南半岛由 5 个国家组成,从西到东分别是:缅甸、泰国、柬埔寨、老挝、越南**,其中缅、老、 越与中国接壤,除了老挝外都有足够的海岸线。这些国家大部分是殖民时代的遗产,英法分别在缅甸、越南发力,将泰国定位缓冲国。法国人曾将柬埔寨、老挝、越南合并成 “印支联邦” 与英国对抗,虽然现在又分裂成三个国家,因此却为越南埋下了大国梦。
缅甸在位置上,可以在陆地及海洋延伸中国的地缘影响力,而且也曾成为支持中国抗战的重要援助物资运输线。在缅甸西边是 “金三角地区”:
<img width=500 src="https://img.alicdn.com/tfs/TB1iwnRchD1gK0jSZFKXXcJrVXa-1396-1266.png">
**金三角地区** 盛产鸦片,首先是金三角环境适合种植鸦片,其次由于所处缅甸、老挝、泰国交界处特别适合逃避法律打击。解决问题的办法就是联合执法,在 “湄公河惨案” 后,由中国主导的联合执法开发形成常态,**金三角成为中国拓展自己地缘影响力的重要抓手**。
中国与中南半岛虽然地缘上存在天然阻隔,但在云贵高原与克钦邦之间存在的南方丝绸之路、中印缅之间存在因抗日战争运输物资而修建了史迪威公路。这些重要的交通枢纽对维系中、缅两国的共同利益有着推动作用。
**越南** 一直想成为中南半岛的强国,但先后被清朝打压、后与法美中几大国相继开战,始终没有得到什么实际利益。越南狭长的地形使其一直存在南北分裂的风险。
**泰国** 之所以能在西方殖民者将土地瓜分完毕时仍保持独立,是凭借其高超的平衡技巧,成为了英法殖民地之间的缓冲国。
<img width=500 src="https://img.alicdn.com/tfs/TB17bYZcXY7gK0jSZKzXXaikpXa-1420-980.png">
克拉地峡是继马六甲海峡后另一个有价值的航线,是否能开挖取决于各方利益平衡,尤其是这样会切断泰国南北,导致加大泰国南部的分裂倾向。
<img width=700 src="https://img.alicdn.com/tfs/TB12hvTckP2gK0jSZPxXXacQpXa-2534-1734.png">
**南洋群岛由 6 个国家组成,分别是:印尼、马拉西亚、菲律宾、文莱、新加坡、东帝汶。**
“下南洋” 期间,在西方殖民者的推动下,大量华人下南洋开发,因此南洋群岛留着部分华夏民族血液。在新加坡,甚至因为华人占据了 75% 的人口,马来西亚为了在脱离英国殖民统治后保证马来人获得多数票,因此将新加坡排除在马来西亚联邦之外,才使得新加坡独立建国。
接着看文莱、巴厘岛和东帝汶:
<img width=500 src="https://img.alicdn.com/tfs/TB1ZB24cfb2gK0jSZK9XXaEgFXa-1434-1274.png">
**文莱** 在马来西亚中是个弹丸小国,但因为在西方殖民者接入之前,文莱的前身 “渤泥国” 的势力范围很大,因此在被殖民者打碎野心的情况下,文莱有着强烈独立的愿望,从争取 “保护国” 的地位到最终独立,文莱一路走来很不容易。但是文莱被 “林梦地区” 一分为二,马来西亚也不会容忍文莱有更多的领土要求,两者僵持不下。但我们相信,身处这种状况的文莱更希望获得外部力量的支持,作为与南海隔海相望的中国将会是其理想的盟友。
**巴厘岛** 不仅是度假胜地,在 14 世纪末至 15 世纪初,在伊斯兰教强大压力下,坚守印度教的少数马来人从爪哇岛移民至巴厘岛,因为宗教信仰的不同,这里引起恐怖分子的关注。
**东帝汶** 是欧洲殖民者划分殖民地的产物,南部的澳大利亚觊觎其丰富油矿资源而积极干涉东帝汶的事物。反过来想,如果中国控制了东帝汶区域,就可以对澳大利亚施加政治影响力。
<img width=600 src="https://img.alicdn.com/tfs/TB1TVY6cXT7gK0jSZFpXXaTkpXa-1736-2532.png">
相比南洋群岛,**南海** 离中国更近。如果要控制南海,就要分别控制位于南海五个方向的:**东沙群岛、西沙群岛、黄岩岛、中沙大环礁、南沙群岛**。
中国想要经略南海,首先要提升自己的综合实力。最近能够在南海问题上有所突破,本质上还是中国综合实力得到了提高。但经略南海不代表占领南海,而是要与南海周边的国家进行博弈,合纵连横。搁置争议,共同开发是最好的策略,如果中国能够掌握深海石油勘采技术,至少能在投资、技术层面让多方面获益。
<img width=500 src="https://img.alicdn.com/tfs/TB1gpr9coz1gK0jSZLeXXb9kVXa-1640-1146.png">
从中国海上突围角度来看,有三条航线可选:**南海-马六甲海峡-印度洋航线、印尼通道-印度洋航线、西太平洋-南太平洋-印度洋航线**
**马六甲海峡** 是南海的咽喉,被新加坡控制,且战时容易被封锁。备选方案印尼通道是个不错的选择,而且相比马六甲海峡三国(新加坡、马来西亚、印度尼西亚),**印尼通道** 只要和印尼搞好关系即可。然而印尼也可能被日本拉拢,但由于印尼与中国没有直接利益冲突,站队日本对印尼来说得不到什么好处。
## 南亚与印度洋
<img width=500 src="https://img.alicdn.com/tfs/TB1dC2_coH1gK0jSZSyXXXtlpXa-1734-2532.png">
**南亚包括 7 个国家**,分别是南亚次大陆的:**尼泊尔、不丹、巴基斯坦、印度、孟加拉国** 和印度洋上的岛国:**斯里兰卡、马尔代夫。**
由于 “印巴分治”,巴基斯坦于 1947 年独立,但由于东西距离太远,中间隔着印度,因此东边独立成了孟加拉国。不丹处于印度保护国状态,而斯里兰卡除了地理阻隔外,有意识的选择了不同的宗教,也是一直保持独立的重要原因。
再往南的马尔代夫给人留下的印象就是度假胜地,但这个海拔只有 1.2 米的岛国,随着气候变暖可能是最先消失的国家。
**印度** 之所以走上与中国不同的道路,主要因为外部压力相对较小。之前也介绍了中国长期受到北方民族的入侵,是因为中国北方有足够大的阶梯地形让北方民族适应 “低原反应”,而印度北方的山脉没有足够的缓冲区,为印度形成了天然的防护屏障。
虽然热带气候可以让文明较早发展,但没有边缘民族入侵压力,会让文明变得非常脆弱,也缺乏扩张的动力。从融合的角度来说,印度虽然也融合了其他民族,但相比 **中国的 “家天下”,印度属于 “种姓” 文明框架。** “家天下” 的模式每个人都有平等的机会,而 “种姓” 制度确保了阶级固化,加上热带地区物产丰富,不至于出现被饿死的情况,因此这种制度得以稳定下来。
**克什米尔** 是印度河的上游,在工业化时代,掌握了上游就可以控制下游的水资源,现在印巴两国共享上印度河平原,任何一方都不会轻易放弃这块战略要地。
<img width=500 src="https://img.alicdn.com/tfs/TB1R93bcoT1gK0jSZFhXXaAtVXa-1414-1102.png">
中国想要扩大自己在印度洋的影响力,就需要找到 **缅甸、巴基斯坦、斯里兰卡、东帝汶、肯尼亚** 这五个点做支持。如今中国一带一路计划,为东亚各国修筑高铁等基础设施,就是拓展中国外交空间的良好手段,加深经济的合作才有可能迎来政治合作。
## 俄罗斯与北冰洋
**俄国** 虽然北临北冰洋,但是没有不冻港是无法通航的。俄国人通过不平等条约使中国东部边界从 **库页岛** 移到了 **乌苏里江**,因此俄国成为了第二个同时可以对三个洋(太平洋、大西洋、北冰洋)施加地缘影响力的国家。
<img width=500 src="https://img.alicdn.com/tfs/TB1tf7ccmf2gK0jSZFPXXXsopXa-1410-1108.png">
不过好在中俄存在 “背靠背” 的战略伙伴关系,因此有合作的空间(俄国要应对西欧,中国要应对东南亚)。但俄罗斯的海岸线很短,这导致俄国在海权争霸的舞台只能当配角,但这个地缘结构不是一成不变的,如果全球变暖导致北冰洋融化,看到的将是另一个格局:
<img width=500 src="https://img.alicdn.com/tfs/TB1x6RGbKbviK0jSZFNXXaApXXa-1440-1230.png">
如果北冰洋融化后可以通航,俄罗斯将成为北冰洋地缘势力最强大的国家,其次是加拿大与阿拉斯加。如果俄国没有短视将阿拉斯加卖给美国,俄国将为成为北冰洋唯一的霸主。
# 3. 总结
《谁在世界中心》这本书一定要看着地图读,这样会发现板块运动随机产生的变化竟然会对世界政治格局产生这么重大的影响,一个国家能否独立最重要的还是看地缘位置。
这本书更是一本中国崛起的地缘解决方案指南,其中一些解决方案在商业逻辑中可以拿来借鉴:
1. 竞争是一个过程,唯有不断参与其中,才有可能掌握话语权,主导权,最终达到政治目的。任何领土都是通过与周边地区漫长博弈后逐渐确立下来的,想得到利益首先得参与到游戏中。
2. 各玩家实力是动态变化的,即便是无法通航的北冰洋,都可能因为温室效应变成不冻港,因此提前看到趋势并提前准备是必须的。
3. 想从对方获得利益,首先要了解对方想获得什么利益,自己有什么筹码,这样才容易促成合作。在寻找盟友前,先站在对方角度掂量一下自己是否合适。
4. 不可能一家独大,想成为霸主,必须建立一个生态。以前是小弟听大哥的话,现在大哥得给小弟好处,才能得到小弟的忠诚。
5. 已有霸主的地位不是一朝一夕就能摧毁的,就像中国想突破马六甲海峡的封锁,在不突破整体海洋封锁的前提下是不可能的,因为海洋霸权是一个全球化整体,美国封锁亚洲有完整的第一岛链、第二岛链逻辑,解决问题的视角要全面。
如今处于大变革的和平时代,国家之间看似和平,实则在进行经济扩张,大国之间要学会不撕破脸的竞争方式。而这个多方博弈的复杂性,使得阴谋几乎不可能得逞,大国的政策都是阳谋,比的是谁更能顺势而为,拉拢更多合作者。
> 讨论地址是:[精读《谁在世界中心》 · Issue #189 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/189)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)
+261
View File
@@ -0,0 +1,261 @@
# 1. 引言
引用著名瑞典统计学家 Hans Rosling 的一句话:想法来源于数字、信息,再到理解。
分析数据的最好方式是可视化,因为可视化承载的信息密度更高,甚至可以从不同维护对数据进行交互式分析。今天要精读的文章就分析了经典可视化分析工具 Tableau:[data-visualisation-made-easy](https://www.analyticsvidhya.com/blog/2017/07/data-visualisation-made-easy/)。
# 2. 精读
[Tableau](https://www.tableau.com/) 是一款广泛用于智能商业的强大数据分析工具,通过不同可交互的图表和仪表盘帮助你获得业务洞见。
## 安装
Tableau 提供了三种使用方式:
**Tableau Desktop**
[拥有 14 天免费试用的桌面版](https://www.tableau.com/products/trial),可以将工作数据存储在计算机本地,如果你是学生或老师可以获得一年的免费使用权。
**Tableau Public**
[公开版完全免费](https://public.tableau.com/s/download),和桌面版的唯一区别是,所有数据都无法保存在本地,只能保存在 Tableau 服务器的云端,而且是公开的。
**Tableau Online**
[网页版也完全免费](https://sso.online.tableau.com/public/idp/SSO),是 Tableau Public 的网页版。
## 连接数据源
安装好 Tableau 后,第一步就是连接数据源。它支持连接本地或云端的数据源,本地最常用的数据源可以从 Excel 转换。这里是一份 [样例数据](https://github.com/pavleenkaur/TableauTutorial-On-AnalyticsVidhya/blob/master/Sample-Superstore.xls),包含了一个超市几年内的销售情况,我们可以用这份数据练手。
下载好这份数据后,选择从 Excel 导入,确认后将 **Orders** 表拖拽到右侧区域,如下图所示:
<img width=500 src="https://img.alicdn.com/tfs/TB1Cvh5cV67gK0jSZPfXXahhFXa-1440-900.png">
可以看到,导入的数据格式有些问题,这是因为这份 Excel 文件表头有一些描述信息干扰。勾选 **Use Data Interpreter** 后,可以开启数据解析功能,自动分析出你想要的表结构:
<img width=500 src="https://img.alicdn.com/tfs/TB1XLJ_c7T2gK0jSZPcXXcKkpXa-1440-900.png">
可以看到表结构已经正常了,在数据清洗的过程中,Tableau 强大的数据分析功能已经初见端倪。你甚至可以点击 **Review ths results** 看看它是如何清洗数据的:点击后会下载一份分析 Excel,其中过滤掉的数据会被标记,自动分析出的表结构会被高亮。
## 数据可视化
在页面最底部有几个切换项,依次是 **Data Source**:数据源、**Sheet**:工作簿,后面跟随的三个按钮可以继续创建多个 Sheet、Dashboard、Story,这些后面都会讲到。首先点击 Sheet 进入可视化分析的工作簿:
<img width=500 src="https://img.alicdn.com/tfs/TB1S2LzcebviK0jSZFNXXaApXXa-1440-900.png">
可以看到,Orders 表的字段已经被自动分析成 **维度** **度量** 了。维度和度量是数据分析中重要的概念:
- **维度:** 维度是不能被计数的字段,一般为字符串或离散的值,用来描述数据的维度。
- **度量:** 度量是可以被计数的字段,一般为数字、日期等连续的值,用来描述数据的量。
右侧空白区域是图表展示区域,**可以响应拖拽交互**,顶部的 Columns、Rows 表示列与行,Filters 是过滤器,拖拽字段上去可以对此字段进行过滤,Marks 是标记,Tableau 将图表所有辅助标记功能都抽象为:颜色、大小、文本、具体值、工具提示。举个例子,如果将销量 Sales 字段拖拽到大小区域,那么任何能描述大小的图表,都会以销量的多少来决定大小,比如散点图。
右上角的 **Show Me** 是图表自动推荐区域,当你拖拽不同字段的时候,Tableau 会自动展示合适的图表,但你也可以点击 Show Me 进行图表切换。
那么开始动手吧!**首先我们要看看大盘数据如何,也就是这家超市的总利润、质量、销量:**
> 在左侧维度栏目下,最后一个字段 **Measure Names** 表示所有度量的集合。
1.**Measure Names** 拖拽到画布的空白区域。
2. 移除我们不关心的 Row ID, Discount 等字段。
<img width=500 src="https://img.alicdn.com/tfs/TB1caeXcYH1gK0jSZFwXXc7aXXa-1440-900.png">
可以看到,总利润大概是总销量的 10%。如果想展示横向表格,将 Measure Names 从 Rows 拖拽到 Columns 即可。
> Tips: 为了方便区分,Tableau 贴心的将维度标记为蓝色,度量标记为绿色。
> 同时可以看到,Tableau 对于单指标拖拽,默认采取表格方式渲染。
**接下来我们要看每一年的详细销量与利润:**
1. 将 Order Date 与 Sales 拖拽到 Rows。
2. 右键 Sales,将类型从连续改成非连续,这样就会自动变成表格展示。
3. 为了展示利润,将 Profit 字段拖拽到 Marks 的 Text 字段上。
<img width=500 src="https://img.alicdn.com/tfs/TB1fxV_c4v1gK0jSZFFXXb0sXXa-1440-900.png">
我们可以看到,无论是销量还是利润都在逐年上升。**接下来我们想具体看看每个月份的数据**:
1. 右键 Order Date,将日期维度从年切换到月。
<img width=500 src="https://img.alicdn.com/tfs/TB1SCN9c.T1gK0jSZFrXXcNCXXa-1440-900.png">
我们可以看到,销量较高的月份分布在:3、9、11、12 月。注意由于没有对年份做筛选,这里的每月统计数据是整合了 2013~2016 四年份的。也就是 1 月的数据其实代表了 2013.1 + 2014.1 + 2015.1 + 2016.1 共四个 1 月份数据的总和。
**接下来我们想了解销量与利润增长的趋势:**
1. 将 Order Date 拖拽到 Columns。
2. 将 Sales 拖拽到 Rows,此时会出现一条线。接下来将 Profit 拖拽到 **左 Y 轴**
<img width=500 src="https://img.alicdn.com/tfs/TB1zVF_c7P2gK0jSZPxXXacQpXa-1440-900.png">
这里就涉及到线图拖拽交互设计了,线图一共有三种拖拽方式。如果将一个新字段拖拽到左 Y 轴,就会在左 Y 轴多出一条线;如果拖拽到中间图表区域,则这个字段会当作已有字段的工具提示;如果拖拽到右 Y 轴,则会自动变成双轴图。
从上图中能看到,销量增长明显,但利润增长缓慢,看来经营是存在一定问题的,还要继续分析问题在哪。
**我们再看看数据按月分布情况**,同样右击 Order Date,选择 月 粒度:
<img width=500 src="https://img.alicdn.com/tfs/TB1RmJ9c.H1gK0jSZSyXXXtlpXa-1440-900.png">
上图可以明显看到三个峰值出现在 3、9、11 月份,然而这段期间利润增长幅度却不大,可以看出这段期间采取了薄利多销的手段。
**再从地区维度分析数据:**
1. 将 Regions 和 Sales 拖拽到 Columns。
2. 切换到饼图。
3. 将 Sales 拖拽到 Marks Pane 的 Label 上。
<img width=500 src="https://img.alicdn.com/tfs/TB1KEJ_c7Y2gK0jSZFgXXc5OFXa-1440-900.png">
可以看到东西部地区是销量最高的区域。**接下来我们想看具体城市的销量:**
1. 将 States 拖拽到画布空白区域,此时会自动出现地图并定位到美国。将 Profits 拖拽到 Color。
2. 将地区切换到 Filled Map,将 Profits 拖拽到 Label。
这样就绘制了一张地区,颜色越深利润越高,数字表示销量。
<img width=500 src="https://img.alicdn.com/tfs/TB1CFWbc.Y1gK0jSZFCXXcwqXXa-1440-900.png">
可以看到数值越大的区域一般颜色也越深,但这不是分析利润/销量性价比的最佳方式,我们先只看到加州和纽约是销售业绩最好的区域,而科罗拉多州虽然销量不错,但利润却是负的。
上面的地图对地形比较直观,但要分析销售健康度,还是用散点图更合适。**我们想看看城市销量/利润的健康度分布:**
1. Profit 拖拽到 ColumnsSales 拖拽到 Rows,此时散点图出现,但只有一个点(之所以出现散点图,是因为横纵轴拖拽的都是度量)。
2. 我们想按城市下钻,只要把 State 拖拽到 Detail 即可。
<img width=500 src="https://img.alicdn.com/tfs/TB1EMl9cVY7gK0jSZKzXXaikpXa-1440-900.png">
可以看到,遥遥领先的城市有三个,加州是销售之王。
由于还没有介绍到筛选条件,这里简略介绍一下,其实还可以将年份拖拽到筛选条件,只看 2013 年的分布图,也可以点击或圈选其中某些点选择排除某些城市。
**现在需要进一步分析明细数据,将不同商品种类按年份细分,看按月的销量,并看看这些月份的利润如何:**
1. 此时需要用到高亮表格。首先将 Category 和 Order Date 拖拽到 Rows,简单的表格出现了。
2. 将 Order Date 再拖拽到 Columns,并右键将其粒度改为月。
3. 在 Show Me 中切换为 Highlight Table,重新将 Order DateYear)拖拽回 Rows。
4. 为了展示颜色与文字,将 Profit 拖拽到 ColorSales 拖拽到 Label。
<img width=500 src="https://img.alicdn.com/tfs/TB1Ud07cWL7gK0jSZFBXXXZZpXa-1440-900.png">
可以看到,办公套件和科技产品业绩最好,其中办公套件在 2015 年 12 月销量利润双丰收,科技产品在 2015 年 10 月与 2016 年 3 月销量利润双丰收。整体来看前半年是淡季。
但这张图无法看到销量与利润性价比关系,**我们要找出利润率最高的商品和利润率最低的商品:**
1. 将 Proft 拖拽到 Columns。
2. 将 Sub-Category 拖拽到 Rows。
3. 切换到 Horizontal Bars。
4. 将销量 Sales 拖拽到 Color。
<img width=500 src="https://img.alicdn.com/tfs/TB10T4.c9f2gK0jSZFPXXXsopXa-1440-900.png">
可以明显看到 Copiers 就是性价比之王,拥有最高的利润,但销量却不是很高(颜色深度中等),而桌子是性价比最低的,利润为负,而且销量不低。
## 其他功能
除了上面基本可视化分析能力之外,Tableau 还有许多辅助功能。
### 筛选器
在按月分布的折线图中,如果我们只想看某一年的,可以将 Order Date 拖拽到 Filters 区域,只勾选想要保留的年份:
<img width=500 src="https://img.alicdn.com/tfs/TB1jgWcc1H2gK0jSZJnXXaT1FXa-1440-900.png">
Tablueau 这种交互等价于 Sql 中 `in` 语句,当然 Tablueau 还支持更复杂的条件或代码表达式,这里只是将更友好的筛选方式优先展示区来。
### 上卷下钻
Tableau 支持任意维度之间的上卷下钻,只要你将他们分好组。
比如将 Order Date、Order ID、Ship Date、Ship Mode 拖拽到一起,成为 Orders 组;将 Category、Sub-Category、Product ID Product Name 形成 Product 组:
<img width=500 src="https://img.alicdn.com/tfs/TB11lmcc.Y1gK0jSZFCXXcwqXXa-1440-900.png">
我们就可以将 Product 直接拖拽到画布区域,并选择矩形树图,通过点击指标上的 “+” “-” 号进行上卷或下钻:
<img width=500 src="https://img.alicdn.com/tfs/TB1yud_cVT7gK0jSZFpXXaTkpXa-1440-900.png">
上卷下钻是顺序相关的,比如 Product - Order Date 表示在产品类目基础上,对每个类目按日期下钻。而 Order Date - Product 这个顺序,表示在日期分布的基础上,对日期按产品类目下钻,了解不同日期下每个产品的分布情况。
### 趋势线
为使用趋势线,先制作一个双轴图:
1. 将 Sales 与 Profit 拖拽到 Rows。
2. 将 Order Date 拖拽到 Columns 并切换到月维度。
3. 选择 Show Me 的 Dual Combination 即混合图。
<img width=500 src="https://img.alicdn.com/tfs/TB1YOeacV67gK0jSZPfXXahhFXa-1440-900.png">
点击 Analytics Tab,将 Trend Line 拖入 chart 中:
<img width=500 src="https://img.alicdn.com/tfs/TB1HL5gc.Y1gK0jSZFCXXcwqXXa-1440-900.png">
趋势图有几种算法,比如线性,Log 或指数,因此在做趋势分析前,首先要判断自己的业务属于哪种增长阶段,如果是爆发期可以选择指数,平稳期可以选择线性等等。
### 预测
回到按月分布的图表,如果我们想预测未来销量和利润的走势,可以使用预测功能:
1. 切换到 Analytics Tab,并将 Forecast 拖拽到图表中。
2. 可以点击右键配置预测参数。
<img width=500 src="https://img.alicdn.com/tfs/TB1Cumcc4D1gK0jSZFsXXbldVXa-1440-900.png">
预测趋势有一个浅色区域,表示预测范围。
### 聚类
象限图的四象限是多维度综合判断的法则,然而 Tableau 支持的聚类分析可以自动做到这些:
1. 切换到 Analytics Tab,选择 Clusters。
2. 可以选择自动聚类个数,也可以手动指定个数。
<img width=500 src="https://img.alicdn.com/tfs/TB1BsWgc1P2gK0jSZFoXXauIVXa-1440-900.png">
从上图可以看到,指定了 4 个分类,最右上角加州就是最突出的一组,整个聚类只有它一个元素,而画面偏左下角的也是一类,这些是业绩较差的一组数据。使用了 K 均值聚类算法,并且当你点击右键查看详细星系时,还能把组间、组内方差展示出来:
<img width=500 src="https://img.alicdn.com/tfs/TB1O7Oec1H2gK0jSZFEXXcqMpXa-1440-900.png">
## 仪表板
仪表板可以将多个 Sheets 内容聚合在一起并自由布局,但仪表板最精髓的功能是图表联动功能:
1. 点击任意图表,选择 “作为筛选条件”。
<img width=500 src="https://img.alicdn.com/tfs/TB1cVjIcebviK0jSZFNXXaApXXa-1440-900.png">
Tableau 的所有图表都支持点选,排除等操作,那么点选这类操作本质上其实是个筛选的过程,比如柱状图点击了某根柱子,可以认为是选择了这根柱子当前的维度值作为筛选条件。
当一个 Sheet 作为筛选条件后,类似点选这种操作产生的筛选就会作用于其他同数据集的图表,因此如上图所示,当点击了条形图的某一根柱子时,上面的销量地图也自动做了筛选,仅展示当前选中的产品的销量分布。
## 故事
Story 更像是 PPT,将分析后有价值或有意义的图表组合在一起,再配合上说明,得出一些结论:
<img width=500 src="https://img.alicdn.com/tfs/TB1wa1gc1L2gK0jSZFmXXc7iXXa-1018-870.png">
如上图所示,比如得到这家超市的大盘数据,这一般也是数据分析的最后一步,最后生成报表。
# 3. 总结
Tableau 的交互式分析思路印证了这句话:
数字、信息,再到理解最终才能产生 Idea。我们从拿到 Excel 导入数据集开始,数据就已经变成了维度和度量的信息,再经过主动思考,将同一份数据进行不同维度的展示,最终得出加州销量最好、家具销售业绩最差、而桌子是负利润的主要来源等等洞见。
通过原文对 Tablueau 功能的分析能看到,Tableau 的核心资产是具备交互式分析能力的图表,这些图表通过智能推荐的方式展示出来,可以在不知道如何分析数据时找到一些灵感,真正做到以数据角度思考,图表展示只是辅助的视觉效果。
目前国内还处于报表制作的时代,即先选择报表再配数据集,这种使用思路是展示数据优先,而不是分析数据优先,笔者认为原因在于国内大部分做报表的业务场景都处于最末端,也就是数据洞见已经有了,再使用 BI 将这个洞见还原出来。而 BI 工具真正想做的还是在前面 “分析洞见” 这一步,希望数据分析师能可以通过 BI 平台挖掘出商业洞见。
要走到这一步,需要国内 BI 平台与使用 BI 的人都发展到下一阶段,而这种探索式数据分析功能早在 2012 年就在国外由 Tableau 团队实现,相信未来三年内国内一定能迎来一波探索式数据分析浪潮!
> 讨论地址是:[精读《Tableau 入门》 · Issue #192 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/192)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)