Compare commits

...
22 Commits
Author SHA1 Message Date
ascoders 63e2ca4d0d 121 2019-09-16 12:03:11 +08:00
黄子毅 9dbd1fb7b9 Merge pull request #205 from leiyaguang/patch-1
Update 104.精读《Function Component 入门》.md
2019-09-14 20:06:47 +08:00
黄子毅 7d8816c2c2 Merge pull request #206 from leiyaguang/patch-2
Update 079.精读《React Hooks》.md
2019-09-14 20:06:35 +08:00
黄子毅 44ae420f52 Merge pull request #207 from leiyaguang/patch-3
Update 080.精读《怎么用 React Hooks 造轮子》.md
2019-09-14 20:06:24 +08:00
mr_left 683b22d1ae Update 080.精读《怎么用 React Hooks 造轮子》.md 2019-09-11 17:22:13 +08:00
mr_left 4e58379585 Update 079.精读《React Hooks》.md 2019-09-11 16:38:29 +08:00
mr_left 1342144fef Update 104.精读《Function Component 入门》.md
修改了一处代码bug
2019-09-11 16:18:03 +08:00
ascoders a41f452df0 120 2019-09-09 09:42:48 +08:00
ascoders 0950c4cdaf 119 2019-09-04 14:54:04 +08:00
ascoders 46ad26c1a4 修复图片 2019-09-02 09:56:18 +08:00
ascoders 6dd26bee54 Merge branch 'v2' of https://github.com/dt-fe/weekly into v2 2019-09-02 09:37:32 +08:00
ascoders 37d6b5f3f0 118 2019-09-02 09:37:21 +08:00
黄子毅 f23e88338f Merge pull request #200 from txs1992/patch-2
fix: typo
2019-08-29 23:20:43 +08:00
MT 3dda46d760 Update 001.精读 js 模块化发展.md
fix:修改错别字。
2019-08-29 15:08:53 +08:00
MT cd6a00bf3a Update 001.精读 js 模块化发展.md
fix: typo.
2019-08-29 15:04:50 +08:00
ascoders 6aad1da704 117 2019-08-26 08:58:18 +08:00
ascoders 03a5a0f484 add travis icon 2019-08-21 19:03:50 +08:00
ascoders 0066b890d4 add precommit hooks for lint-md 2019-08-21 19:00:29 +08:00
黄子毅 e0caf77d0a Merge pull request #198 from ForkRepo/v2
test(*): add lint-md-cli, and auto fix all issues
2019-08-21 17:30:23 +08:00
hustcc 1517e86999 test(*): add lint-md-cli, and auto fix all issues 2019-08-21 17:19:13 +08:00
黄子毅 c8f86df3f3 Merge pull request #197 from xuhongbo/patch-6
Update 116.精读《刷新》.md
2019-08-19 13:40:17 +08:00
leoxu 752d598d27 Update 116.精读《刷新》.md 2019-08-19 11:24:37 +08:00
60 changed files with 1520 additions and 277 deletions
+2
View File
@@ -0,0 +1,2 @@
/node_modules
/yarn.lock
+7
View File
@@ -0,0 +1,7 @@
{
"excludeFiles": [],
"rules": {
"no-long-code": 0,
"no-trailing-punctuation": 0
}
}
+6
View File
@@ -0,0 +1,6 @@
language: node_js
node_js:
- "10"
before_install:
- npm i -g lint-md-cli
script: lint-md ./
+5 -5
View File
@@ -10,7 +10,7 @@
<img src="assets/1/cube.jpeg" alt="logo" width="500" />
> 如今,Javascript 模块化规范非常方便、自然,但这个新规范仅执行了2年,就在 4 年前,js 的模块化还停留在运行时支持,10 年前,通过后端模版定义、注释定义模块依赖。对经历过来的人来说,历史的模块化方式还停留在脑海中,反而新上手的同学会更快接受现代的模块化规范。
> 如今,Javascript 模块化规范非常方便、自然,但这个新规范仅执行了 2 年,就在 4 年前,js 的模块化还停留在运行时支持,10 年前,通过后端模版定义、注释定义模块依赖。对经历过来的人来说,历史的模块化方式还停留在脑海中,反而新上手的同学会更快接受现代的模块化规范。
但为什么要了解 Javascript 模块化发展的历史呢?因为凡事都有两面性,了解 Javascript 模块化规范,有利于我们思考出更好的模块化方案,纵观历史,从 1999 年开始,模块化方案最多维持两年,就出现了新的替代方案,比原有的模块化更清晰、强壮,我们不能被现代模块化方式限制住思维,因为现在的 ES2015 模块化方案距离发布也仅仅过了两年。
@@ -26,7 +26,7 @@
**外部依赖定义 (2007)**: 这种定义方式在 cocos2d-js 开发中普遍使用,其核心思想是将依赖抽出单独文件定义,这种方式不利于项目管理,毕竟依赖抽到代码之外,我是不是得两头找呢?所以才有通过 webpack 打包为一个文件的方式暴力替换为 commonjs 的方式出现。
**Sandbox模式 (2009)**: 这种模块化方式很简单,暴力,将所有模块塞到一个 `sanbox` 变量中,硬伤是无法解决明明冲突问题,毕竟都塞到一个 `sandbox` 对象里,而 `Sandbox` 对象也需要定义在全局,存在被覆盖的风险。模块化需要保证全局变量尽量干净,目前为止的模块化方案都没有很好的做到这一点。
**Sandbox 模式 (2009)**: 这种模块化方式很简单,暴力,将所有模块塞到一个 `sandbox` 变量中,硬伤是无法解决命名冲突问题,毕竟都塞到一个 `sandbox` 对象里,而 `Sandbox` 对象也需要定义在全局,存在被覆盖的风险。模块化需要保证全局变量尽量干净,目前为止的模块化方案都没有很好的做到这一点。
**依赖注入 (2009)**: 就是大家熟知的 angular1.0,依赖注入的思想现在已广泛运用在 react、vue 等流行框架中。但依赖注入和解决模块化问题还差得远。
@@ -52,7 +52,7 @@
这篇文章所提供的模块化历史的方案都是逻辑模块化,**从 CommonJS 方案开始前端把服务端的解决方案搬过来之后,算是看到标准物理与逻辑统一的模块化**。但之后前端工程不得不引入模块化构建这一步。正是这一步给前端开发无疑带来了诸多的不便,尤其是现在我们开发过程中经常为了优化这个工具带了很多额外的成本。
从 CommonJS 之前其实都只是封装,并没有一套模块化规范,这个就有些像类与包的概念。我在10年左右用的最多的还是 YUI2YUI2 是用 namespace 来做模块化的,但有很多问题没有解决,比如多版本共存,因此后来 YUI3 出来了。
从 CommonJS 之前其实都只是封装,并没有一套模块化规范,这个就有些像类与包的概念。我在 10 年左右用的最多的还是 YUI2YUI2 是用 namespace 来做模块化的,但有很多问题没有解决,比如多版本共存,因此后来 YUI3 出来了。
```javascript
YUI().use('node', 'event', function (Y) {
@@ -118,7 +118,7 @@ YUI3 的 sandbox 像极了差不多同时出现的 AMD 规范,但早期 yahoo
### 补充阅读
- [JavaScript 模块化七日谈](https://huangxuan.me/2015/07/09/js-module-7day/)
- [JavaScript模块化编程简史(2009-2016](https://yuguo.us/weblog/javascript-module-development-history/)
- [JavaScript 模块化编程简史(2009-2016](https://yuguo.us/weblog/javascript-module-development-history/)
# 总结
@@ -136,4 +136,4 @@ YUI3 的 sandbox 像极了差不多同时出现的 AMD 规范,但早期 yahoo
至此,对于 javascript 模块化讨论已接近尾声,对其优缺点也基本达成了一致。前端复杂度不断提高,促使着模块化的改进,代理(浏览器、node) 的支持程度,与前端特殊性(流量、缓存)可能前端永远也离不开构建工具,新的标准会让这些工作做的更好,同时取代、增强部分特征,前端的未来是更加美好的,复杂度也更高。
**如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。**
**如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。**
+1 -1
View File
@@ -72,7 +72,7 @@
### 可访问性的反思
Accessibility 翻译过来是『无障碍访问』,是对不同终端用户的体验完善。每一个模态框,都要有通过键盘关闭的功能,通常使用ESC键。似乎我们程序员多少总会把我们自我的惯性思维带进实现的产品,尤其是当我们敲着外置的键盘,用着 PC 的时候。
Accessibility 翻译过来是『无障碍访问』,是对不同终端用户的体验完善。每一个模态框,都要有通过键盘关闭的功能,通常使用 ESC 键。似乎我们程序员多少总会把我们自我的惯性思维带进实现的产品,尤其是当我们敲着外置的键盘,用着 PC 的时候。
下面的这些问题都是对可访问性的反思:
+6 -6
View File
@@ -17,7 +17,7 @@
**前端渲染的优势**
- 局部刷新。无需每次都进行完整页面请求
- 懒加载。如在页面初始时只加载可视区域内的数据,滚动后rp加载其它数据,可以通过 react-lazyload 实现
- 懒加载。如在页面初始时只加载可视区域内的数据,滚动后 rp 加载其它数据,可以通过 react-lazyload 实现
- 富交互。使用 JS 实现各种酷炫效果
- 节约服务器成本。省电省钱,JS 支持 CDN 部署,且部署极其简单,只需要服务器支持静态文件即可
- 天生的关注分离设计。服务器来访问数据库提供接口,JS 只关注数据获取和展现
@@ -36,7 +36,7 @@
本次提出独到观点的同学有:[@javie007](http://link.zhihu.com/?target=https%3A//github.com/javie007) [@杨森](https://www.zhihu.com/people/c93b7957f6308990c7e3b16103c9356b) [@流形](https://www.zhihu.com/people/6c772f9726a914ed4a4b90c88010461c) [@camsong](https://www.zhihu.com/people/078cc0fb15845759ad8295b0f0e50099) [@Turbe Xue](https://www.zhihu.com/people/turbe-xue) [@淡苍](https://www.zhihu.com/people/5ac53c9c0484e83672e1c1716bdf0ff9) [@留影](https://www.zhihu.com/people/38c3c75795824de1bc5d99cff904a832) [@FrankFang](http://link.zhihu.com/?target=https%3A//github.com/FrankFang) [@alcat2008](http://link.zhihu.com/?target=https%3A//github.com/alcat2008) [@xile611](http://link.zhihu.com/?target=https%3A//github.com/xile611) [@twobin](http://link.zhihu.com/?target=https%3A//github.com/twobin) [@黄子毅](https://www.zhihu.com/people/3ec85a04bc9eaa35b1830874cc463a52) 精读由此归纳。
大家对前端和后端渲染的现状基本达成共识。即前端渲染是未来趋势,但前端渲染遇到了首屏性能和SEO的问题。对于同构争议最多,在此我归纳一下。
大家对前端和后端渲染的现状基本达成共识。即前端渲染是未来趋势,但前端渲染遇到了首屏性能和 SEO 的问题。对于同构争议最多,在此我归纳一下。
### 前端渲染遇到的问题
@@ -46,7 +46,7 @@ SEO 很好理解。由于传统的搜索引擎只会从 HTML 中抓取数据,
### 同构的优点
同构恰恰就是为了解决前端渲染遇到的问题才产生的,至 2014 年底伴随着 React 的崛起而被认为是前端框架应具备的一大杀器,以至于当时很多人为了用此特性而[放弃 Angular 1 而转向 React](http://link.zhihu.com/?target=https%3A//blog.risingstack.com/from-angularjs-to-react-the-isomorphic-way/)。然而近3年过去了,很多产品逐渐从全栈同构的理想化逐渐转到首屏或部分同构。让我们再一次思考同构的优点真是优点吗?
同构恰恰就是为了解决前端渲染遇到的问题才产生的,至 2014 年底伴随着 React 的崛起而被认为是前端框架应具备的一大杀器,以至于当时很多人为了用此特性而[放弃 Angular 1 而转向 React](http://link.zhihu.com/?target=https%3A//blog.risingstack.com/from-angularjs-to-react-the-isomorphic-way/)。然而近 3 年过去了,很多产品逐渐从全栈同构的理想化逐渐转到首屏或部分同构。让我们再一次思考同构的优点真是优点吗?
1. 有助于 SEO
@@ -65,7 +65,7 @@ SEO 很好理解。由于传统的搜索引擎只会从 HTML 中抓取数据,
1. 性能
把原来放在几百万浏览器端的工作拿过来给你几台服务器做,这还是花挺多计算力的。尤其是涉及到图表类需要大量计算的场景。这方面调优,可以参考 [walmart的调优策略](https://medium.com/walmartlabs/reactjs-ssr-profiling-and-caching-5d8e9e49240c)。
把原来放在几百万浏览器端的工作拿过来给你几台服务器做,这还是花挺多计算力的。尤其是涉及到图表类需要大量计算的场景。这方面调优,可以参考 [walmart 的调优策略](https://medium.com/walmartlabs/reactjs-ssr-profiling-and-caching-5d8e9e49240c)。
个性化的缓存是遇到的另外一个问题。可以把每个用户个性化信息缓存到浏览器,这是一个天生的分布式缓存系统。我们有个数据类应用通过在浏览器合理设置缓存,双十一当天节省了 70% 的请求量。试想如果这些缓存全部放到服务器存储,需要的存储空间和计算都是很非常大。
@@ -97,7 +97,7 @@ export const isSsr = () => (
5. simple storeredux
这个 store 是必须以字符串形式塞到前端,所以复杂类型是无法转义成字符串的,比如function。
这个 store 是必须以字符串形式塞到前端,所以复杂类型是无法转义成字符串的,比如 function。
总的来说,同构渲染实施难度大,不够优雅,无论在前端还是服务端,都需要额外改造。
@@ -136,7 +136,7 @@ Next.js 是时下非常流行的基于 React 的同构开发框架。作者之
1. 巧妙地用标准化的解决了请求的问题。同构和页面开发类似,异步是个大难题,异步中难点又在接口请求。Next.js 给组件新增了 getInitialProps 方法来专门处理初始化请求,再也不用手动往页面上塞 DATA 和调用 ReactDOMServer.renderToString
2. 使用 [styled-jsx](https://github.com/zeit/styled-jsx) 解决了 css-in-js 的问题。这种方案虽然不像 styled-component 那样强大,但足够简单,可以说是最小的成本解决了问题
3. Fast by default。页面默认拆分文件方式打包,支持Prefetch页面预加载
3. Fast by default。页面默认拆分文件方式打包,支持 Prefetch 页面预加载
全家桶式的的解决方案。简洁清晰的目录结构,这一点 Redux 等框架真应该学一学。不过全家桶的方案比较适合全新项目使用,旧项目使用要评估好成本
+3 -3
View File
@@ -22,7 +22,7 @@
3. 局部与全局状态的归一
4. 分形思想
5. action 分散执行
5. app级别数据处理,推荐前端 `Orm`
5. app 级别数据处理,推荐前端 `Orm`
整体来看,核心思路是推荐组件内部完成数据流的处理,不用关心使用了 `Redux` `Mobx` 或者 `Rxjs`,也不用关心这些库是否有全局管理的野心,如果全局管理那就挂载到全局,但组件内部还是局部管理。
@@ -42,7 +42,7 @@
以 Mobx 为代表,轻前端用的较多,因为复杂度集中在后端,前端做好数据展示即可,那么直接拥抱 js 这种基于对象的语言,结合原生 `Map` `Proxy` `Reflect` 将副作用进行到底,开发速度快得飞起。
数据存储方式按照视图形态来,因为视图之间几乎毫无关联,而且特别是数据产品,后端数据量巨大,把数据处理过程搬到前端是不可能的(为了推导出一个视图形态数据,需要动辄几GB的原始数据运算,存储和性能都不适合在前端做)。
数据存储方式按照视图形态来,因为视图之间几乎毫无关联,而且特别是数据产品,后端数据量巨大,把数据处理过程搬到前端是不可能的(为了推导出一个视图形态数据,需要动辄几 GB 的原始数据运算,存储和性能都不适合在前端做)。
#### 函数式
@@ -75,7 +75,7 @@
### 分形的缺点
对于聊天室或者在线IDE等,全局数据居多,很多交叉绑定的情况,就不适合分形思想,反而纯 Redux 思想更合适。
对于聊天室或者在线 IDE 等,全局数据居多,很多交叉绑定的情况,就不适合分形思想,反而纯 Redux 思想更合适。
## 3.3 数据形态,是原始数据还是视图数据?
+3 -3
View File
@@ -14,7 +14,7 @@
Stack 部分主要在阐明 js 中函数调用栈的概念,它符合栈的基本特性『当调用时,压入栈顶。当它执行完毕时,被弹出栈』,简单看下面的代码:
```
```plain
function c() {
try {
var bar = baz;
@@ -46,7 +46,7 @@ a();
## 如何使用堆栈追踪
该部分以 NodeJS 环境为例,讲解了 `Error.captureStackTrace `,将 stack 信息作为属性存储在一个对象当中,同时可以过滤掉一些无用的堆栈信息。这样可以隐藏掉用户不需要了解的内部细节。作者也以 Chai 为例,内部使用该方法对代码的调用者屏蔽了不相关的实现细节。通过以 Assertion 对象为例,讲述了具体的内部实现,简单来说通过一个 addChainableMethod 链式调用工具方法,在运行一个 Assertion 时,将它设为标记,其后面的堆栈会被移除;如果 assertion 失败移除起后面所有内部堆栈;如果有内嵌 assertion,将当前 assertion 的方法放到 ssfi 中作为标记,移除后面堆栈帧;
该部分以 NodeJS 环境为例,讲解了 `Error.captureStackTrace`,将 stack 信息作为属性存储在一个对象当中,同时可以过滤掉一些无用的堆栈信息。这样可以隐藏掉用户不需要了解的内部细节。作者也以 Chai 为例,内部使用该方法对代码的调用者屏蔽了不相关的实现细节。通过以 Assertion 对象为例,讲述了具体的内部实现,简单来说通过一个 addChainableMethod 链式调用工具方法,在运行一个 Assertion 时,将它设为标记,其后面的堆栈会被移除;如果 assertion 失败移除起后面所有内部堆栈;如果有内嵌 assertion,将当前 assertion 的方法放到 ssfi 中作为标记,移除后面堆栈帧;
# 3. 精读
参与本次精读的同学有:[范洪春](https://www.zhihu.com/people/fanhc/activities)、[黄子毅](https://www.zhihu.com/people/huang-zi-yi-83/answers)、[杨森](https://www.zhihu.com/people/yangsen/answers)、[camsong](https://www.zhihu.com/people/camsong/answers),该部分由他们的观点总结而出。
@@ -83,7 +83,7 @@ describe('should.js和power-assert的区别', () => {
- 区分操作异常和程序员的失误。操作异常指可预测的不可避免的异常,如无法连接服务器
- 操作异常应该被处理。程序员的失误不需要处理,如果处理了反而会影响错误排查
- 操作异常有两种处理方式:同步 (try…catch) 和异步(callback, event - emitter)两种处理方式,但只能选择其中一种。
- 操作异常有两种处理方式:同步 (try…catch) 和异步(callback, event - emitter)两种处理方式,但只能选择其中一种。
- 函数定义时应该用文档写清楚参数类型,及可能会发生的合理的失败。以及错误是同步还是异步传给调用者的
- 缺少参数或参数无效是程序员的错误,一旦发生就应该 throw。
传递错误时,使用标准的 Error 对象,并附件尽可能多的错误信息,可以使用标准的属性名
+1 -1
View File
@@ -81,7 +81,7 @@ react-css-modules 引入了 styleName,将本地变量和全局变量很清晰
另外,使用 react-css-modules,可以方便的覆盖本地变量的样式:
```
```plain
import customStyles from './table-custom-styles.css';
<Table styles={customStyles} />;
+1 -1
View File
@@ -12,7 +12,7 @@
# 2 内容概要
使用 Object.assign 作用于大对象时,速度会成为瓶颈,比如拥有 `100,000` 个属性的对象,这个操作耗费了 134ms。性能损失主要原因是 “结构共享” 操作需要遍历近10万个属性,而这些引用操作耗费了100ms以上的时间。
使用 Object.assign 作用于大对象时,速度会成为瓶颈,比如拥有 `100,000` 个属性的对象,这个操作耗费了 134ms。性能损失主要原因是 “结构共享” 操作需要遍历近 10 万个属性,而这些引用操作耗费了 100ms 以上的时间。
解决办法就是减少引用指向的操作数量,而且由于引用指向到任何对象的损耗都几乎一致(无论目标对象极限小或者无穷大,引用消耗时间都几乎没有区别),我们需要一种精心设计的树状结构将打平的引用建立深度,以减少引用操作次数,`vector tries` 就是一种解决思路:
+16 -16
View File
@@ -7,29 +7,29 @@
我为什么要选这篇文章呢?
就在前几天的 Google I/O 2017上, Polymer 正式发布了 [Polymer 2.0](https://www.polymer-project.org/blog/2017-05-15-time-for-two) 版本.
就在前几天的 Google I/O 2017 上, Polymer 正式发布了 [Polymer 2.0](https://www.polymer-project.org/blog/2017-05-15-time-for-two) 版本.
来看一下 Polymer 2.0 的一些变化:
- 使用 Shadow DOM v1 代替 Polymer.dom. Shady DOM从 Polymer 中分离出来。
- 使用 Shadow DOM v1 代替 Polymer.dom. Shady DOM 从 Polymer 中分离出来。
- 使用 标准的 ES6 类和 Custom Elements v1 来自定义元素.
- 还有数据系统的改进和生命周期的变更.
可以看到, Polymer 的这次升级主要是将 Shadow Dom 和 Custom Elements 升级到 v1 版本, 以获得更多浏览器的原生支持. 下一 代Web Components v1规范,Chrome 已经支持了,Web Components 规范中的2个主要部分 [Shadow Dom](https://www.chromestatus.com/feature/4667415417847808) 和 [Custom Elements](https://www.chromestatus.com/feature/4696261944934400). Safari10版本中, 支持了 [Shadow DOM v1](https://webkit.org/status/#feature-shadow-dom) 规范并且完成了在Webkit内核中对 [Custom Elements v1](https://webkit.org/blog/7027/introducing-custom-elements/) 规范的实现;Firefox对 [Shadow DOM](https://platform-status.mozilla.org/#shadow-dom) 和 [Custom Elements v1规范](https://platform-status.mozilla.org/#custom-elements) 支持正在开发中;Edge也将对 [Shadow DOM](https://developer.microsoft.com/en-us/microsoft-edge/platform/status/shadowdom/) 和 [Custom Elements](https://developer.microsoft.com/en-us/microsoft-edge/platform/status/customelements/) 支持规划到他们的开发roadmap中。
可以看到, Polymer 的这次升级主要是将 Shadow Dom 和 Custom Elements 升级到 v1 版本, 以获得更多浏览器的原生支持. 下一 代 Web Components v1 规范,Chrome 已经支持了,Web Components 规范中的 2 个主要部分 [Shadow Dom](https://www.chromestatus.com/feature/4667415417847808) 和 [Custom Elements](https://www.chromestatus.com/feature/4696261944934400). Safari10 版本中, 支持了 [Shadow DOM v1](https://webkit.org/status/#feature-shadow-dom) 规范并且完成了在 Webkit 内核中对 [Custom Elements v1](https://webkit.org/blog/7027/introducing-custom-elements/) 规范的实现;Firefox 对 [Shadow DOM](https://platform-status.mozilla.org/#shadow-dom) 和 [Custom Elements v1 规范](https://platform-status.mozilla.org/#custom-elements) 支持正在开发中;Edge 也将对 [Shadow DOM](https://developer.microsoft.com/en-us/microsoft-edge/platform/status/shadowdom/) 和 [Custom Elements](https://developer.microsoft.com/en-us/microsoft-edge/platform/status/customelements/) 支持规划到他们的开发 roadmap 中。
这段时间, 大家都在讨论 react, vue, angular, 这些框架. 或者 该使用 redux 还 是 mobx 做数据管理. 在这个契机下, 我想我们可以不单单去思考这些框架, 也可以更多地去思考和了解 Web Components 标准. 对于 Web Components标准有一些思考. 所以我选了一篇关于 Web Components 的文章, 想让大家对于 Web Components 的发展, 和 Web Componets 与现在的主流框架如何协作有更多的思考和讨论.
这段时间, 大家都在讨论 react, vue, angular, 这些框架. 或者 该使用 redux 还 是 mobx 做数据管理. 在这个契机下, 我想我们可以不单单去思考这些框架, 也可以更多地去思考和了解 Web Components 标准. 对于 Web Components 标准有一些思考. 所以我选了一篇关于 Web Components 的文章, 想让大家对于 Web Components 的发展, 和 Web Componets 与现在的主流框架如何协作有更多的思考和讨论.
# 2 内容概要
**The broken promise of Web Components**
原文作者dmitriid主要是在喷Web Components2011年到2017年这6年间毫无进展, 一共产出了6份标准, 其中两份已经被弃用. 几乎只有一个主流浏览器(chrome) 支持.
原文作者 dmitriid 主要是在喷 Web Components2011 年到 2017 年这 6 年间毫无进展, 一共产出了 6 份标准, 其中两份已经被弃用. 几乎只有一个主流浏览器(chrome) 支持.
![image](https://dmitriid.com/assets/img/blog/web-components-support.png)
- Web Components 这些规范强依赖 JS 的实现
- Custom Elements 是 JS 脚本的一部分
- HTML Templates 的出现就是为了被JS 脚本使用
- HTML Templates 的出现就是为了被 JS 脚本使用
- Shadow Dom 也需要配合 JS 脚本使用
- 只有 HTML imports 可以脱离 JS 脚本使用
- Web Components 操作 DOM
@@ -38,16 +38,16 @@
- 为了突破限制使用不同的方法来传递数据
- CSS 作用域, 可以见上次精读[《请停止 css-in-js 的行为》](https://github.com/dt-fe/weekly/issues/12)
**来看一下Polymer 的 核心成员 Rob Dodson 对于本文的回应: Regarding the broken promise of Web Components**
**来看一下 Polymer 的 核心成员 Rob Dodson 对于本文的回应: Regarding the broken promise of Web Components**
- Web Components 特性需要被浏览器支持,必须有平缓的过渡,良好的兼容,以及成熟的方案,因此推进速度会比较慢一些。
- React 很棒, 但是也不要忽略其他基于 Web Components 的优秀库比如 [Amp](https://www.ampproject.org/)
- 对于 DOM 更新的抽象比如 React/JSX很赞, 但是也带来了一些损耗. 在旧的移动设备上, 加载一个大的js 包性能依旧不理想, 最佳的做法是拆分你的 JS 包, 按需加载.
- 使用 JSX 和 虚拟 DOM是很酷, 也可以直接把 JSX 用在 Web Components 内, 像[SkateJS](https://github.com/skatejs/skatejs)库, 已经在做这个事情了.
- 没有标准的数据绑定, Polymer的数据绑定, 现在是基于[MDV](https://github.com/toolkitchen/mdv), 很多开发者更倾向于基于 Observables或者 ES6 Proxies的数据绑定方案.
- 处理组件的字符串属性是很烦人, 但是由于每一个组件都是一个类的实例, 可以利用ES6 的 getters/setters来改变属性.
- 对于 DOM 更新的抽象比如 React/JSX 很赞, 但是也带来了一些损耗. 在旧的移动设备上, 加载一个大的 js 包性能依旧不理想, 最佳的做法是拆分你的 JS 包, 按需加载.
- 使用 JSX 和 虚拟 DOM 是很酷, 也可以直接把 JSX 用在 Web Components 内, 像[SkateJS](https://github.com/skatejs/skatejs)库, 已经在做这个事情了.
- 没有标准的数据绑定, Polymer 的数据绑定, 现在是基于[MDV](https://github.com/toolkitchen/mdv), 很多开发者更倾向于基于 Observables 或者 ES6 Proxies 的数据绑定方案.
- 处理组件的字符串属性是很烦人, 但是由于每一个组件都是一个类的实例, 可以利用 ES6 的 getters/setters 来改变属性.
Rob Dodson对于 Web Components 依然充满信心, 但是也承认推进标准总会有各种阻碍, 不可能像推荐框架一样快速把事情解决.
Rob Dodson 对于 Web Components 依然充满信心, 但是也承认推进标准总会有各种阻碍, 不可能像推荐框架一样快速把事情解决.
# 3 精读
@@ -55,11 +55,11 @@ Rob Dodson对于 Web Components 依然充满信心, 但是也承认推进标准
[@camsong](https://www.zhihu.com/people/078cc0fb15845759ad8295b0f0e50099) [@黄子毅](https://github.com/ascoders) [@杨森](https://www.zhihu.com/people/c93b7957f6308990c7e3b16103c9356b) [@rccoder](https://github.com/rccoder) [@alcat2008](https://github.com/alcat2008)精读由此归纳。
### 标准与框架
Web Components 作为一个标准,骨子里的进度就会落后于当前可行的技术体系。正如文中所说,浏览器厂商 ship 一个新功能是很严肃的,很可能会影响到一票的线上业务,甚至会影响到一个产业(遥想当年 [Chrome Extension 禁用 NPAPI](https://blog.chromium.org/2013/09/saying-goodbye-to-our-old-friend-npapi.html)时的一片哀鸿遍野,许多返利插件都使用了这种技术)。那么 Web Components的缓慢推进也在情理之中了.
即使真的有一天这个标准建立起来,Web Components作为浏览器底层特性不应该拿出来和React这类应用层框架相比较. 未来Web Components会做为浏览器非常重要的特性存在。API偏低层操作,会易用性不够. 在很长时间内开发者依旧会使用 React/Vue/Angular/Polymer 这样的框架,Web Components可能会做为这些框架的底层做一些 浏览器层面上的支持.
Web Components 作为一个标准,骨子里的进度就会落后于当前可行的技术体系。正如文中所说,浏览器厂商 ship 一个新功能是很严肃的,很可能会影响到一票的线上业务,甚至会影响到一个产业(遥想当年 [Chrome Extension 禁用 NPAPI](https://blog.chromium.org/2013/09/saying-goodbye-to-our-old-friend-npapi.html)时的一片哀鸿遍野,许多返利插件都使用了这种技术)。那么 Web Components 的缓慢推进也在情理之中了.
即使真的有一天这个标准建立起来,Web Components 作为浏览器底层特性不应该拿出来和 React 这类应用层框架相比较. 未来 Web Components 会做为浏览器非常重要的特性存在。API 偏低层操作,会易用性不够. 在很长时间内开发者依旧会使用 React/Vue/Angular/Polymer 这样的框架,Web Components 可能会做为这些框架的底层做一些 浏览器层面上的支持.
### 不需要 vendor 的自定义组件间调用
在 Webpack 大行其道的时代,想在运行时做到组件即引即用变得很困难,因为这些组件大多是通过 React/Vue/Angular 开发的。不得不考虑引入一大堆 Vendor 包,这些 Vendor 里可能还必须包含 React 这类两个版本不能同时使用的库。目前我们团队在做组件化方案时就遇到这个问题,只能想办法避免两个版本的出现。你可以说这是 React 或 Webpack 引入的问题,但并没有看到 Web Compnents 标准化的解决方案。我想未来Web Components可能会作为浏览器的底层, 出现基于底层的标准方案来做组件间的相互应用的方法.
在 Webpack 大行其道的时代,想在运行时做到组件即引即用变得很困难,因为这些组件大多是通过 React/Vue/Angular 开发的。不得不考虑引入一大堆 Vendor 包,这些 Vendor 里可能还必须包含 React 这类两个版本不能同时使用的库。目前我们团队在做组件化方案时就遇到这个问题,只能想办法避免两个版本的出现。你可以说这是 React 或 Webpack 引入的问题,但并没有看到 Web Compnents 标准化的解决方案。我想未来 Web Components 可能会作为浏览器的底层, 出现基于底层的标准方案来做组件间的相互应用的方法.
### 为什么对 Web components 讨论不断
@@ -69,7 +69,7 @@ Web Components 作为一个标准,骨子里的进度就会落后于当前可
但使用前端框架的问题也日益暴露,随着前端框架种类的增多,同一个框架不同版本之间无法共存,导致组件无法跨框架复用,甚至只能固定在框架的某个版本,这与前端未来的模块化发展是相违背的,我们越是与之抗衡,就越希望 Web components 能站出来解决这个问题,因为浏览器原生支持模块化,相当于将 react angular vue 的能力内置在浏览器中,而且一定会向前兼容(这也是 Web components 推进缓慢的原因)。
# 4 总结
我觉得 Web Components作为浏览器底层特性不应该拿出来和React, vue 这类应用层框架相比较. Web Components 的方向以及提供的价值都不会跟 应用框架一致. 而 Web Components 作为未来的 Web 组件标准 , 它在任何生态中都可以运行良好. 我倒是更加期待应用层去基于 Web Components 去做更多的实现, 让组件超越框架存在, 可以在不同技术栈中使用.
我觉得 Web Components 作为浏览器底层特性不应该拿出来和 React, vue 这类应用层框架相比较. Web Components 的方向以及提供的价值都不会跟 应用框架一致. 而 Web Components 作为未来的 Web 组件标准 , 它在任何生态中都可以运行良好. 我倒是更加期待应用层去基于 Web Components 去做更多的实现, 让组件超越框架存在, 可以在不同技术栈中使用.
> 讨论地址是:[精读《Web Components 的困境》 · Issue #15 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/15)
+1 -1
View File
@@ -62,7 +62,7 @@ Chrome Dev Tools 非常强大,[dev-tips](https://umaar.com/dev-tips/) 列出
### 移动端控制台
- [Chrome远程调试](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews) app 支持后,连接 usb 或者局域网,即可通过 Dev Tools 调试 webview 页面。
- [Chrome 远程调试](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews) app 支持后,连接 usb 或者局域网,即可通过 Dev Tools 调试 webview 页面。
- [Weinre](http://people.apache.org/~pmuellr/weinre/docs/latest/Home.html) 通过页面加载脚本,与 pc 端调试器通信。
- 通过内嵌控制台解决,比如 [eruda](http://eruda.liriliri.io/) [VConsole](https://github.com/WechatFE/vConsole)
- [Rosin](http://alloyteam.github.io/Rosin/) fiddler 的一个插件,协助移动页面调试。
+2 -2
View File
@@ -107,7 +107,7 @@ connect(props => ({
## HOC 的具体实践
HOC 在真实场景下的运行非常多,之前笔者在 [基于Decorator的组件扩展实践](https://zhuanlan.zhihu.com/p/22054582) 一文中也提过使用高阶组件将更细粒度的组件组合成 Selector 与 Search。结合精读文章,这次让我们通过 Form 组件的抽象来表现 HOC 具有的良好扩展机制。
HOC 在真实场景下的运行非常多,之前笔者在 [基于 Decorator 的组件扩展实践](https://zhuanlan.zhihu.com/p/22054582) 一文中也提过使用高阶组件将更细粒度的组件组合成 Selector 与 Search。结合精读文章,这次让我们通过 Form 组件的抽象来表现 HOC 具有的良好扩展机制。
Form 中会包含各种不同的组件,常见的有 Input、Selector、Checkbox 等等,也会有根据业务需求加入的自定义组件。Form 灵活多变,从功能上看,表单校验可能为单组件值校验,也可能为全表单值校验,可能为常规检验,比如:非空、输入限制,也可能需要与服务端配合,甚至需要根据业务特点进行定制。从 UI 上看,检验结果显示的位置,可能在组件下方,也可能是在组件右侧。
@@ -115,7 +115,7 @@ Form 中会包含各种不同的组件,常见的有 Input、Selector、Checkbo
![image](https://user-images.githubusercontent.com/9314735/27116337-3f1f16a8-5103-11e7-8dc6-c7197e1b1eab.png)
至于 HOC 在 Form 上的具体实现,首先将表单中的组件(Input、Selector...)与相应 validator 与组件值回调函数名(trigger)传入 Decorator,将 validator 与 trigger 相绑定。Decorator 完成了各种不同组件与 Form 内置 Store 间 value 的传递、校验功能的抽象,即精读文章中提到 Props Proxy 方式的其中两种作用:**提取state** 与 **操作props**
至于 HOC 在 Form 上的具体实现,首先将表单中的组件(Input、Selector...)与相应 validator 与组件值回调函数名(trigger)传入 Decorator,将 validator 与 trigger 相绑定。Decorator 完成了各种不同组件与 Form 内置 Store 间 value 的传递、校验功能的抽象,即精读文章中提到 Props Proxy 方式的其中两种作用:**提取 state** 与 **操作 props**
```javascript
function formFactoryFactory({
+1 -1
View File
@@ -215,7 +215,7 @@ var bar = foo.bind(obj)
bar()
```
### 3.2.2 es6绑定
### 3.2.2 es6 绑定
这种情况类似使用箭头函数创建成员变量,以下方式等于创建了没有挂载到原型链的匿名函数,因此 this 不会丢失。
+21 -21
View File
@@ -2,8 +2,8 @@
# 1 引言
随着前端ES6 ES7 的一路前行, 我们大前端借鉴和引进了各种其他编程语言中的概念、特性、模式;
我们可以使用函数式Functional编程设计,可以使用面向对象OOP的设计,可以使用面向接口的思想,也可以使用AOP,
随着前端 ES6 ES7 的一路前行, 我们大前端借鉴和引进了各种其他编程语言中的概念、特性、模式;
我们可以使用函数式 Functional 编程设计,可以使用面向对象 OOP 的设计,可以使用面向接口的思想,也可以使用 AOP,
可以使用注解,代理、反射,各种设计模式; 在大前端辉煌发展、在数据时代的当下 我们一起阅读了一篇设计相关的老文:
《The DCI Architecture》
一起来再探索和复习一下 相关的设计和思想
@@ -11,15 +11,15 @@
# 2 内容摘要
DCI是数据Data 场景Context 交互Interactions 简称, 重点是关注 数据的不同场景的交互行为, 是面向对象系统 状态和行为的一种范式设计;
DCI在许多方面是许多过去范式的统一,多年来这些模式已经成为面向对象编程的辅助工具。
DCI 是数据 Data 场景 Context 交互 Interactions 简称, 重点是关注 数据的不同场景的交互行为, 是面向对象系统 状态和行为的一种范式设计;
DCI 在许多方面是许多过去范式的统一,多年来这些模式已经成为面向对象编程的辅助工具。
尽管面向切面的编程(AOP)也有其他用途,但DCI满足了许多AOP的应用以及Aspects在解决问题方面的许多目标。根据AOP的基本原理,DCI基于深层次的反射或元编程。
与Aspects不同,角色聚合并组合得很好。Context提供角色集之间的关联的范围关闭,而Aspect仅与应用它们的对象配对。
在许多时候,虽然混合本身缺乏我们在Context语义中发现的动力 ,但DCI反映了混合风格策略。
DCI实现了多范式设计的许多简单目标,能够将过程逻辑与对象逻辑分开。然而,DCI具有比多范式设计提供的更强大的技术更好的耦合和内聚效果
尽管面向切面的编程(AOP)也有其他用途,但 DCI 满足了许多 AOP 的应用以及 Aspects 在解决问题方面的许多目标。根据 AOP 的基本原理,DCI 基于深层次的反射或元编程。
Aspects 不同,角色聚合并组合得很好。Context 提供角色集之间的关联的范围关闭,而 Aspect 仅与应用它们的对象配对。
在许多时候,虽然混合本身缺乏我们在 Context 语义中发现的动力 ,但 DCI 反映了混合风格策略。
DCI 实现了多范式设计的许多简单目标,能够将过程逻辑与对象逻辑分开。然而,DCI 具有比多范式设计提供的更强大的技术更好的耦合和内聚效果
结合ATM 汇款场景案例,讲解了一下 DCI
结合 ATM 汇款场景案例,讲解了一下 DCI
角色提供了和用户相关 自然的边界,以转账为例,我们实际谈论的是钱的转移,以及源账户和目标账户的角色,算法(用例 角色行为集合)应该是这样:
1.账户拥有人选择从一个账户到另外一个账户的钞票转移。
2.系统显示有效账户
@@ -35,12 +35,12 @@ DCI实现了多范式设计的许多简单目标,能够将过程逻辑与对
2.源账户确认余额可用
3.源账户减少其帐目
4.源账户请求目标账户增加其帐目
5.源账户请求目标账户更新其日志log
5.源账户请求目标账户更新其日志 log
6.源账户结束交易事务
7.源账户显示给账户拥有人转账成功。
```
```plain
template <class ConcreteAccountType>
class TransferMoneySourceAccount: public MoneySource
{
@@ -88,7 +88,7 @@ DCI 尝试从人类思维角度出发,举一个例子:为什么在看电影
2. 人或物发生了什么行为、交互?
3. 现在在哪?厨房?太空舱?或者原始森林?
很快把这三件事弄清楚,我们就能快速理解当前场景的逻辑,并且**轻松理解该场景继续发生的状况**,即便是盗梦空间这种烧脑的电影,当我们搞清楚这三个问题后,就算街道发生了180度扭曲,也不会存在理解障碍,反而可以吃着爆米花享受,直到切换到下一个场景为止。
很快把这三件事弄清楚,我们就能快速理解当前场景的逻辑,并且**轻松理解该场景继续发生的状况**,即便是盗梦空间这种烧脑的电影,当我们搞清楚这三个问题后,就算街道发生了 180 度扭曲,也不会存在理解障碍,反而可以吃着爆米花享受,直到切换到下一个场景为止。
当我们把街道扭曲 180 度的能力放在街道对象上时,理解就变的复杂了:这个函数什么时候被调用?为什么不好好承载车辆而自己发生扭曲?这就像电影开始时,把电影里播放的所有关于街道的状态都走马灯过一遍:我们看到街道通过了车辆、又卷曲、又发生了爆炸,实在觉得莫名其妙。
@@ -210,23 +210,23 @@ object MoneyTransferApp extends App {
- [Comparison of Architecture presentation patterns MVP(SC),MVP(PV),PM,MVVM and MVC](https://www.codeproject.com/Articles/66585/Comparison-of-Architecture-presentation-patterns-M)
- [The DCI Architecture: A New Vision of Object-Oriented Programming](http://www.artima.com/articles/dci_vision.html)
- [干净的架构The Clean Architecture](https://www.bbsmax.com/A/pRdBWY3ezn/)
- [MVC的替代方案](https://gxnotes.com/article/71237.html)
- [展示模式架构比较MVP(SC)MVP(PV)PMMVVMMVC](http://blog.csdn.net/lihenair/article/details/51791915)
- [干净的架构 The Clean Architecture](https://www.bbsmax.com/A/pRdBWY3ezn/)
- [MVC 的替代方案](https://gxnotes.com/article/71237.html)
- [展示模式架构比较 MVP(SC)MVP(PV)PMMVVMMVC](http://blog.csdn.net/lihenair/article/details/51791915)
- [Software Architecture Design](https://github.com/zenany/weekly/blob/master/resources/software_architecture.md)
- [【译】什么是 Flux 架构?(兼谈 DDD 和 CQRS](https://blog.jimmylv.info/2016-07-07-what-the-flux-on-flux-ddd-and-cqrs/)
## 结合DCI 设想开发的过程中使用到一些设计方法和原则
## 结合 DCI 设想开发的过程中使用到一些设计方法和原则
我们在开发的过程中多多少少都会使用到一些设计方法和原则
DCI 重点是关注 数据的不同场景的交互行为, 是面向对象系统 状态和行为的一种范式设计;
它能够将过程逻辑与对象逻辑分开,是一种典型的行为模式设计;
很好的点是 它根据AOP的基本原理,DCI 提出基于AOP 深层次的元编程(可以理解成面向接口编程), 去促使系统的内聚效果和降低耦合度;
很好的点是 它根据 AOP 的基本原理,DCI 提出基于 AOP 深层次的元编程(可以理解成面向接口编程), 去促使系统的内聚效果和降低耦合度;
举个例子:
在一个BI系统中, 在业务的发展中, 这个系统使用到了多套的 底层图表库,比如: Echarts, G2Recharts, FusionChart; 等等;
在一个 BI 系统中, 在业务的发展中, 这个系统使用到了多套的 底层图表库,比如: Echarts, G2Recharts, FusionChart; 等等;
那么问题来了,
1. 如何去同时支持 这些底层库, 并且达到很容易切换的一个效果?
@@ -234,19 +234,19 @@ DCI 重点是关注 数据的不同场景的交互行为, 是面向对象系
3. 如何去考虑扩展业务 对图表的日益增强的业务功能(如: 行列转换、智能格式化 等等)
带着这些问题, 我们再来看下 DCI 给我们的启示, 我们来试试看相应的解法:
1. 图表的模型数据就是 数据Data , 我们可以把[日益增强的业务功能] 认为是各个场景交互Interactions;
1. 图表的模型数据就是 数据 Data , 我们可以把[日益增强的业务功能] 认为是各个场景交互 Interactions;
2. 接入更多类型的图表咋么搞?
不同类型的图表其实是图表数据模型的转换,我们也可以把这些转换的行为过程作为一个个的切片(Aspect),每个切片都是独立的, 松耦合的 ;
![image](https://user-images.githubusercontent.com/1456421/27744526-67fd0e3e-5d85-11e7-9b48-e1934d9b15f3.png)
3. 接入多套底层库怎么搞? 每个图形库的 build方法,render 方法 resize 方法,repaint 方法 都不一样 ,怎么搞 ? 我们可以使用 DCI 提到的元编程- 我们在这里理解为面向接口编程, 我们分装一层 统一的接口;
3. 接入多套底层库怎么搞? 每个图形库的 build 方法,render 方法 resize 方法,repaint 方法 都不一样 ,怎么搞 ? 我们可以使用 DCI 提到的元编程- 我们在这里理解为面向接口编程, 我们分装一层 统一的接口;
利用面向接口的父类引用指向子类对象 我们就可以很方便的 接入更多的 implement 接入更多的图形库(当然,一个系统统一一套是最好的);
# 4 总结
DCI是数据Data 场景Context 交互Interactions的简称,DCI是一种特别关注行为的设计模式(行为模式),
DCI 是数据 Data 场景 Context 交互 Interactions 的简称,DCI 是一种特别关注行为的设计模式(行为模式),
DCI 关注数据不同场景的交互行为, 是面向对象 状态和行为的一种范式设计;DCI 尝试从人类思维,过程化设计一些行为;
DCI 也会使用一些面向切面和接口编程的设计思想去达到高内聚低耦合的目标。
+12 -12
View File
@@ -8,7 +8,7 @@
# 2 内容概要
### TC39是什么?包括哪些人?
### TC39 是什么?包括哪些人?
一个推动 JavaScript 发展的委员会,由各个主流浏览器厂商的代表构成。
@@ -18,7 +18,7 @@
### TC39 这群人主要的工作是什么?
制定ECMAScript标准,标准生成的流程,并实现。
制定 ECMAScript 标准,标准生成的流程,并实现。
### 标准的流程是什么样的?
@@ -26,34 +26,34 @@
- stage0 `strawman`
任何讨论、想法、改变或者还没加到提案的特性都在这个阶段。只有TC39成员可以提交。
任何讨论、想法、改变或者还没加到提案的特性都在这个阶段。只有 TC39 成员可以提交。
- stage1 `proposal`
1)产出一个正式的提案。
(2)发现潜在的问题,例如与其他特性的关系,实现难题。
(3)提案包括详细的API描述,使用例子,以及关于相关的语义和算法。
3)提案包括详细的 API 描述,使用例子,以及关于相关的语义和算法。
- stage2 `draft`
(1)提供一个初始的草案规范,与最终标准中包含的特性不会有太大差别。草案之后,原则上只接受增量修改。
(2)开始实验如何实现,实现形式包括polyfill, 实现引擎(提供草案执行本地支持),或者编译转换(例如babel)
2)开始实验如何实现,实现形式包括 polyfill, 实现引擎(提供草案执行本地支持),或者编译转换(例如 babel
- stage3 `candidate`
(1)候选阶段,获得具体实现和用户的反馈。此后,只有在实现和使用过程中出现了重大问题才会修改。
(1)规范文档必须是完整的,评审人和ECMAScript的编辑要在规范上签字。
(2)至少要在一个浏览器中实现,提供polyfill或者babel插件。
1)规范文档必须是完整的,评审人和 ECMAScript 的编辑要在规范上签字。
2)至少要在一个浏览器中实现,提供 polyfill 或者 babel 插件。
- stage4 `finished`
(1)已经准备就绪,该特性会出现在下个版本的ECMAScript规范之中。。
2)需要通过有2个独立的实现并通过验收测试,以获取使用过程中的重要实践经验。
(1)已经准备就绪,该特性会出现在下个版本的 ECMAScript 规范之中。。
2)需要通过有 2 个独立的实现并通过验收测试,以获取使用过程中的重要实践经验。
### 一般可以去哪里查看TC39标准的进程呢?
### 一般可以去哪里查看 TC39 标准的进程呢?
stage0 的提案 https://github.com/tc39/proposals/blob/master/stage-0-proposals.md
stage1 - 4 的提案 https://github.com/tc39/proposals
### 我们怎么在程序中应用这些新特性呢?
babel的插件:`babel-presets-stage-0` `babel-presets-stage-1` `babel-presets-stage-2` `babel-presets-stage-3` `babel-presets-stage-4`
babel 的插件:`babel-presets-stage-0` `babel-presets-stage-1` `babel-presets-stage-2` `babel-presets-stage-3` `babel-presets-stage-4`
# 3 精读
@@ -627,7 +627,7 @@ const firstName =
### [Math.signbit: IEEE-754 sign bit](http://jfbastien.github.io/papers/Math.signbit.html)
当值为 负数 或 -0 时返回 `true`。由于 `Math.sign` 不区分 +0 与 -0,因此提案建议增加此函数,而且此函数在 c、c++、go语言都有实现。
当值为 负数 或 -0 时返回 `true`。由于 `Math.sign` 不区分 +0 与 -0,因此提案建议增加此函数,而且此函数在 c、c++、go 语言都有实现。
### [Error stacks](https://github.com/tc39/proposal-error-stacks)
@@ -1,6 +1,6 @@
# 1. 摘要
日期选择器作为基础组件重要不可或缺的一员,大家已经快习惯它一成不变的样子,输入框+日期选择弹出层。但到业务中,这种墨守成规的样子真的能百分百契合业务需求吗。这篇文章从多个网站的日期选择场景出发,企图归纳出日期选择器的最佳实践。这篇文章对移动端的日期选择暂无涉猎,都是PC端,列举出通用场景,每个类型日期选择器需要考虑的设计。
日期选择器作为基础组件重要不可或缺的一员,大家已经快习惯它一成不变的样子,输入框+日期选择弹出层。但到业务中,这种墨守成规的样子真的能百分百契合业务需求吗。这篇文章从多个网站的日期选择场景出发,企图归纳出日期选择器的最佳实践。这篇文章对移动端的日期选择暂无涉猎,都是 PC 端,列举出通用场景,每个类型日期选择器需要考虑的设计。
文章链接:Designing The Perfect Date And Time Picker
感谢本期评论官 @黄子毅 @流形 @王亮 @赵阳 @不知名的花瓣工程师
# 2. 设计原则
@@ -21,9 +21,9 @@
2)用户自定义输入如何保证日期格式正确性?
3)是否需要提供预设场景输入? 比如昨天,三天前,七天前,30天前?像很多数据分析场景,分析师会关注数据周期,比如流量的周环比,月环比,年环比。
3)是否需要提供预设场景输入? 比如昨天,三天前,七天前,30 天前?像很多数据分析场景,分析师会关注数据周期,比如流量的周环比,月环比,年环比。
4)是否需要包含默认值?如果有默认,应该是什么?像google flight 根据用户历史数据提供默认值,临近节假日默认填充节假日。同时像有些数据场景,数据存在延迟,需要默认提供T-1/T-2 ,避免用户选择当天。
4)是否需要包含默认值?如果有默认,应该是什么?像 google flight 根据用户历史数据提供默认值,临近节假日默认填充节假日。同时像有些数据场景,数据存在延迟,需要默认提供 T-1/T-2 ,避免用户选择当天。
5)当用户激活输入框时,是否保留默认值?
@@ -60,7 +60,7 @@
2)用户选中后是否立刻做背景色提示?
3)当用户选择时,区间是否需要随着用户动作改变?比如用户hover时,动态改变选中区间。
3)当用户选择时,区间是否需要随着用户动作改变?比如用户 hover 时,动态改变选中区间。
4)是否提供快捷键切换 日、月、年选择?
@@ -97,7 +97,7 @@
![](https://pic1.zhimg.com/v2-cd4874c5dc98505c56b05dbd3193fa78_b.gif)
采用与用户交互的方式选择日期,如果今后应用上AI,单纯的日期选择器是不是会消失不见呢?..
采用与用户交互的方式选择日期,如果今后应用上 AI,单纯的日期选择器是不是会消失不见呢?..
## 3.5 特殊标识周末
![](https://pic1.zhimg.com/v2-d8410bede19d7bd4c212ad216ebd0770_b.png)
在机票、旅行场景中,周末是大家最有可能出行的时间点,采用竖线划分的方式着重标注提醒。
@@ -106,6 +106,6 @@
![](https://pic3.zhimg.com/v2-ec840145feb22eeac76e5a0503828436_b.png)
总得来说,日期选择器是一个业务组件,虽然现有很多组件库把它纳入UI基础组件。但在每个不通的业务场景和需求下的展现形式、交互都会有所有不同。首先一定一定要明确确定需要日期选择器的场景,尤其是与日期强关联的业务,比如机票定价、日程安排,结合到日期选择器中更直观,提高用户对信息的检索效率。满足用户需求场景的同时,尽量减少用户操作链路。
总得来说,日期选择器是一个业务组件,虽然现有很多组件库把它纳入 UI 基础组件。但在每个不通的业务场景和需求下的展现形式、交互都会有所有不同。首先一定一定要明确确定需要日期选择器的场景,尤其是与日期强关联的业务,比如机票定价、日程安排,结合到日期选择器中更直观,提高用户对信息的检索效率。满足用户需求场景的同时,尽量减少用户操作链路。
看到最后点个赞呗,给你比小心心 ❤ ~~
@@ -40,7 +40,7 @@
正如上面所说,我推荐以开放性问题开场,这样便于了解候选人的经历、熟悉哪些技术点,便于后面的技术提问。如果开场就以准备好的题目展开车轮战,容易引起候选人心里紧张,同时我们问的问题不一定是候选人所在行的,技术问题不是每一个都那么重要,很多时候我们只看到了候选人的冰山一角,但此时气氛已经尴尬,很多时候会遗漏优秀人才。
开放性问题最好基于行为面试法询问(Star法则):
开放性问题最好基于行为面试法询问(Star 法则):
- Situation: 场景 - 当时是怎样的场景
- Task: 任务 - 当时的任务是什么
@@ -83,7 +83,7 @@
面试主要是看候选人基础有多扎实,和思维能力。基础主要指的是,候选人提前了解了多少前端相关知识,比如对闭包的理解,对原生 api 的理解?如果候选人没接触过这两个知识点,会有两种情况:
- **这些知识点看完需要多久?如果是闭包和原生api的定义与用法,候选人这方面的缺陷可以通过5分钟来弥补,那么这种问题到底想考什么?我们真的在乎这5分钟看文档的时间吗?此时应该了解候选人对知识点的感悟,或者学习方式,因为这两点的差距可能几年都无法弥补**
- **这些知识点看完需要多久?如果是闭包和原生 api 的定义与用法,候选人这方面的缺陷可以通过 5 分钟来弥补,那么这种问题到底想考什么?我们真的在乎这 5 分钟看文档的时间吗?此时应该了解候选人对知识点的感悟,或者学习方式,因为这两点的差距可能几年都无法弥补**
- **如果候选人学习能力非常强,但几乎所有前端知识点都不了解,弥补完大概一共要花 1000*5 分钟,这时候量变引发质变了,是不是说明候选人本身对技术的热情存在问题?**
通过了基础问题还远远不够。甚至当问一个复杂的问题的时候,如果候选人瞬间把答案完美流畅表达出来,说明这个问题基本上白问了。
@@ -1,54 +1,54 @@
# 精读《Web fonts: when you need them, when you dont》
本期精读让我们来聊一聊Web Fonts,文章地址:[https://hackernoon.com/web-fonts-when-you-need-them-when-you-dont-a3b4b39fe0ae](https://hackernoon.com/web-fonts-when-you-need-them-when-you-dont-a3b4b39fe0ae)
本期精读让我们来聊一聊 Web Fonts,文章地址:[https://hackernoon.com/web-fonts-when-you-need-them-when-you-dont-a3b4b39fe0ae](https://hackernoon.com/web-fonts-when-you-need-them-when-you-dont-a3b4b39fe0ae)
## 文章简介
文章分析了Web Fonts的优劣具体使用场景。
文章分析了 Web Fonts 的优劣具体使用场景。
## 主要观点
- 作者用一张流程图非常言简意赅地概括了文章的上半部分。
![流程图](https://cdn-images-1.medium.com/max/1000/1*MpuDht99XGlRIFlhjFb2yQ.png)
- 当然上半部分作者也讲了很多案例,其中一个很明显的案例就是维基百科利用字体来提升阅读体验,通过文章内的对比,能直观感受到这一点
- 文章后半部分着力介绍了怎么解决Web Font的带来的弊端:认识FOUT带来的问题,如何使用现有的前端解决方案来尽可能避免这个问题,以及样式上优雅降级的几个方案。
- 文章后半部分着力介绍了怎么解决 Web Font 的带来的弊端:认识 FOUT 带来的问题,如何使用现有的前端解决方案来尽可能避免这个问题,以及样式上优雅降级的几个方案。
## 把文章带入自己的开发环境
作为一个中文开发者,在我们的开发技术栈中,Web Fonts绝对是属于使用频率比较低的那一类的。本次精读选择这篇文章,也正是一探这一个不常见的领域。
作为一个中文开发者,在我们的开发技术栈中,Web Fonts 绝对是属于使用频率比较低的那一类的。本次精读选择这篇文章,也正是一探这一个不常见的领域。
对于英文字母,26个字母可以解决大部分的问题,算上大小写和基本符号,一张ASCII码标就可以包含住。让我再扩展一下,到大部分的西文书写系统,几百个字符就能解决多语言显示的问题了。但是对于汉语而言,Web Fonts真的是,想说爱你不容易,因为常用的汉字就有几千个(你想象中国还有《千字文》这种儿童读物……)。字体这东西跟字符数量直接挂钩,是很难通过压缩来获得性能提升的。
对于英文字母,26 个字母可以解决大部分的问题,算上大小写和基本符号,一张 ASCII 码标就可以包含住。让我再扩展一下,到大部分的西文书写系统,几百个字符就能解决多语言显示的问题了。但是对于汉语而言,Web Fonts 真的是,想说爱你不容易,因为常用的汉字就有几千个(你想象中国还有《千字文》这种儿童读物……)。字体这东西跟字符数量直接挂钩,是很难通过压缩来获得性能提升的。
通常的想法就是用多少,取多少,但是这个方法也就只能适用于标题美化等场景。对于一个系统性的前端工程,我们不可能去实现一个动态字符的字体文件(就是统计这个页面上会产生多少个字符,为这个字符集去生成一个字符子集)
虽然汉字书写系统和西文书写系统天生存在差异,但是把作者在文章中提出来的几个问题再站在中文的角度上再来看一下,也可以得到一个比较客观的答案。以下是我作为一个普通开发者的自问自答:
1. **字体对你的品牌很关键吗?**(需要特性字体的中文LOGO基本都用PNGSVG解决了,Web Font不实用。)
2. **字体让你的文字阅读起来更容易了吗?**(我平时开发产品没有成片聚集的文字,用无衬线字体就能满足需求。Web Font很好,我选择“微软雅黑”。)(注:泛指那些好用的支持全字集的系统自带字体;成片的文字适用衬线字体,个人认为中文的衬线字体,不同的字体带来的阅读体验还是有明显差别的。)
3. **你需要在不同设备上显示一样的字体吗?**(好像还没这么苛求吧……微软雅黑好看,安卓上的Roboto也很不错啊,Roboto这种字体还针对移动设备有优化,何乐而不为。)
4. **用了Web Font你会更开心吗?**(在icon中使用iconfont让我们告别了PNG Sprite图,嗯这很开心。至于文字上用Web Font,有好用的系统字体你不用,你这是何苦呢)(作者也说了,可能折腾半天还没系统字体看着舒服,那就是一行font-family的事情)
1. **字体对你的品牌很关键吗?**(需要特性字体的中文 LOGO 基本都用 PNGSVG 解决了,Web Font 不实用。)
2. **字体让你的文字阅读起来更容易了吗?**(我平时开发产品没有成片聚集的文字,用无衬线字体就能满足需求。Web Font 很好,我选择“微软雅黑”。)(注:泛指那些好用的支持全字集的系统自带字体;成片的文字适用衬线字体,个人认为中文的衬线字体,不同的字体带来的阅读体验还是有明显差别的。)
3. **你需要在不同设备上显示一样的字体吗?**(好像还没这么苛求吧……微软雅黑好看,安卓上的 Roboto 也很不错啊,Roboto 这种字体还针对移动设备有优化,何乐而不为。)
4. **用了 Web Font 你会更开心吗?**(在 icon 中使用 iconfont 让我们告别了 PNG Sprite 图,嗯这很开心。至于文字上用 Web Font,有好用的系统字体你不用,你这是何苦呢)(作者也说了,可能折腾半天还没系统字体看着舒服,那就是一行 font-family 的事情)
不同产品有着不同的场景,多像文章里问问自己会有最合适的答案。
## 关于FOUTFOIT
文章中大篇幅地在安利你使用Web Font,但是也很直白地指出了Web Font最大的问题,就是这个FOUT——Flash Of Unstyled Text。连作者毫不避讳地说了句:“噢我的老天,这太丑了!”
## 关于 FOUTFOIT
文章中大篇幅地在安利你使用 Web Font,但是也很直白地指出了 Web Font 最大的问题,就是这个 FOUT——Flash Of Unstyled Text。连作者毫不避讳地说了句:“噢我的老天,这太丑了!”
具体表现是采用了Web Font的文案会存在闪动,这个的根本原因在于相比于系统字体,Web Font最大的弊端在于它是异步加载的,你没有办法避免下载它所用的时间。文章中举了一个例子,在一个图文为主的页面中,一个542KB的字体文件,在第9秒才加载完成。在那之前只能以系统字体来展示,而在第9秒加载完成的时候,还会出现替换字体的情况,文字会突然跳动。
具体表现是采用了 Web Font 的文案会存在闪动,这个的根本原因在于相比于系统字体,Web Font 最大的弊端在于它是异步加载的,你没有办法避免下载它所用的时间。文章中举了一个例子,在一个图文为主的页面中,一个 542KB 的字体文件,在第 9 秒才加载完成。在那之前只能以系统字体来展示,而在第 9 秒加载完成的时候,还会出现替换字体的情况,文字会突然跳动。
比FOUT更为极端的情况的是FOIT——Flash Of Invisible Text。很多浏览器的行为,并不是默认展示系统字体,而是直接隐藏。那么即使在极快的网速下也很难避免存在一个几百毫秒的时间滞后。
FOUT 更为极端的情况的是 FOIT——Flash Of Invisible Text。很多浏览器的行为,并不是默认展示系统字体,而是直接隐藏。那么即使在极快的网速下也很难避免存在一个几百毫秒的时间滞后。
不过好在,有一个font-display的属性,可以在声明@font-face的时候配合使用。对于未加载Web Fonts的时候,auto属性可以选择隐藏也就是会产生FOIT,swap会产生替换也就是会产生FOUT,还有fallbackoptional可以控制先FOITFOUT来达到折中方案。
不过好在,有一个 font-display 的属性,可以在声明@font-face 的时候配合使用。对于未加载 Web Fonts 的时候,auto 属性可以选择隐藏也就是会产生 FOITswap 会产生替换也就是会产生 FOUT,还有 fallbackoptional 可以控制先 FOITFOUT 来达到折中方案。
还有一个思路,那就是预加载,对于字体,浏览器还是能够有效缓存的,如果能够做好预加载,还是不会太影响用户体验的。文章中就提到了一个方案,调用linkrel=preload来做预加载。因为通常加载字体是在CSS中的@font-face被读到的时候才去加载的,那么就会出现先加载CSS,后加载字体的情况。如果利用link预加载,那么在CSS中的@font-face被读到前就已经开始加载了,那么字体加载和CSS加载就可以同时加载,提升速度。
还有一个思路,那就是预加载,对于字体,浏览器还是能够有效缓存的,如果能够做好预加载,还是不会太影响用户体验的。文章中就提到了一个方案,调用 linkrel=preload 来做预加载。因为通常加载字体是在 CSS 中的@font-face 被读到的时候才去加载的,那么就会出现先加载 CSS,后加载字体的情况。如果利用 link 预加载,那么在 CSS 中的@font-face 被读到前就已经开始加载了,那么字体加载和 CSS 加载就可以同时加载,提升速度。
当然JS是万能的,也有一些库在支持这方面功能,例如bramstein/fontfaceobserver这样的。
当然 JS 是万能的,也有一些库在支持这方面功能,例如 bramstein/fontfaceobserver 这样的。
愚以为,FO*T这种情况既然无法避免还是要具体情况具体分析的。如果你的用户网速够快,那么隐藏文字会更好,用户无感知;如果网速不确定,而且是文章为主的内容,那么内容至上就应该先用替代字体显示;如果你正在将Web Font应用在图标等东西上,那么我们自然不愿意看到满屏的方框方框,这种时候就选择隐藏吧。
愚以为,FO*T 这种情况既然无法避免还是要具体情况具体分析的。如果你的用户网速够快,那么隐藏文字会更好,用户无感知;如果网速不确定,而且是文章为主的内容,那么内容至上就应该先用替代字体显示;如果你正在将 Web Font 应用在图标等东西上,那么我们自然不愿意看到满屏的方框方框,这种时候就选择隐藏吧。
文章也提了一点,如果你的字体授权很贵,但用户端深受FO*T折磨,那你还费这钱干嘛。
文章也提了一点,如果你的字体授权很贵,但用户端深受 FO*T 折磨,那你还费这钱干嘛。
## 结论
如果能解决FO*T的副作用,Web Font怎么舒服怎么用。但是中文字体大,常用西文字体诸如Google字体库又时常被墙,对于中国开发者,Web Font想说爱你不容易。(还是乖乖用微软雅黑吧,逃……
如果能解决 FO*T 的副作用,Web Font 怎么舒服怎么用。但是中文字体大,常用西文字体诸如 Google 字体库又时常被墙,对于中国开发者,Web Font 想说爱你不容易。(还是乖乖用微软雅黑吧,逃……
## 彩蛋
文章里有一段很精彩的话,摘抄出来翻译一下:
> 如果这个世界上有这样一个Sketch或者Photoshop的插件,可以在你每次打开一个文件的时候,延迟十秒才显示出字体;那么世界上就没有那么多多余的字体了。
> 如果这个世界上有这样一个 Sketch 或者 Photoshop 的插件,可以在你每次打开一个文件的时候,延迟十秒才显示出字体;那么世界上就没有那么多多余的字体了。
>
> (译者注:删光设计师的电脑里的奇怪字体也能达到相同目的,哈哈哈~)
@@ -5,7 +5,7 @@
<img src="assets/22/v8.png" width="500" alt="logo" />
定时刷新一下对 js 的三观,防止经验变成坑。(对IE等浏览器的三观需保持不变)
定时刷新一下对 js 的三观,防止经验变成坑。(对 IE 等浏览器的三观需保持不变)
# 2 内容概要
+5 -5
View File
@@ -1,4 +1,4 @@
本期精读的文章是:[API设计原则](https://coolshell.cn/articles/18024.html)
本期精读的文章是:[API 设计原则](https://coolshell.cn/articles/18024.html)
# 1 引言
@@ -12,9 +12,9 @@
由于本文已经是翻译后的文章,概要只列出不涉及 c++ 概念的思路框架,细节请移步[译文](https://coolshell.cn/articles/18024.html)。
## 好 API 的6个特质
## 好 API 的 6 个特质
极简且完备、语义清晰简单、符合直觉、易于记忆和引导API使用者写出可读代码。
极简且完备、语义清晰简单、符合直觉、易于记忆和引导 API 使用者写出可读代码。
## 静态多态
@@ -76,7 +76,7 @@ function (const num) {
## 统一关键字库
所有api定义之前,先抽离业务和功能语义的关键字,统一关键字库; 可以更好的让多人协作看起来如出一辙, 而且关键字库 更能够让调用者感觉到 符合直觉、语义清晰; 关键字库也是项目组新同学 PREDO 的内容之一, 很有带入感;
所有 api 定义之前,先抽离业务和功能语义的关键字,统一关键字库; 可以更好的让多人协作看起来如出一辙, 而且关键字库 更能够让调用者感觉到 符合直觉、语义清晰; 关键字库也是项目组新同学 PREDO 的内容之一, 很有带入感;
## 单一职责
@@ -119,6 +119,6 @@ const { setVisible } = this.props.store.article
最后,如果有精力,最好每半年重构一次(然后完整跑一遍测试)!
> 讨论地址是:[精读《API设计原则》 · Issue #34 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/34)
> 讨论地址是:[精读《API 设计原则》 · Issue #34 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/34)
> 如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。
+22 -22
View File
@@ -9,14 +9,14 @@
我为什么要选这篇文章呢?
之所以选这篇文章, 是因为非常认同作者写这两篇文章的原因. 作者在文中说, 现代JavaScript 的很多概念和思想在快速被传播和扩展, 很多新概念出现在前端相关的博客和文档中, 这些概念对于很多前端开发人员来说, 仍然很陌生. 因此我们有必要来学习一下现代的这些 JavaScript的概念, 看这些概念在现在 JavaScript 的库或应用中是怎么被使用的.
之所以选这篇文章, 是因为非常认同作者写这两篇文章的原因. 作者在文中说, 现代 JavaScript 的很多概念和思想在快速被传播和扩展, 很多新概念出现在前端相关的博客和文档中, 这些概念对于很多前端开发人员来说, 仍然很陌生. 因此我们有必要来学习一下现代的这些 JavaScript 的概念, 看这些概念在现在 JavaScript 的库或应用中是怎么被使用的.
# 2 内容概要
文章讲了很多现代JavaScript中的概念, 罗列如下:
文章讲了很多现代 JavaScript 中的概念, 罗列如下:
## 纯函数和副作用
在了解纯函数之前, 首先要了解副作用. 副作用是指改变了其作用域外的状态. 副作用的举例有调用了一个 API, 操作了一个 DOM节点, 弹出了一个弹窗, 或者改变了一条数据等.
在了解纯函数之前, 首先要了解副作用. 副作用是指改变了其作用域外的状态. 副作用的举例有调用了一个 API, 操作了一个 DOM 节点, 弹出了一个弹窗, 或者改变了一条数据等.
而纯函数则是指 函数的返回值仅仅由参数决定, 当给同样的参数时, 返回值是固定的.
@@ -27,7 +27,7 @@ Stateless 无状态, 有点像纯函数, 不管理自己的数据或状态, 结
可变对象与不可变对象概念很清楚, 可变对象指的是在创建后值仍可以被改变, 不可变对象指的是创建后值无法被改变.
相比于其他语言, 可变对象与不可变对象在 JavaScript 中更加模糊, 当你了解函数式编程时, 你会听到很多不可变对象的好处. 在 JavaScript 中, 你可以通过Object.freeze(obj), 让一个对象变得不可变, 但是注意这是浅层的冻结对象, 如果有一个属性的值是个对象, 那这个对象中的属性是可以被修改的. 现在 JavaScript 也出现了 npm deep-freeze , Immutable.js 这些库来帮助你在 JavaScript 中实现不可变对象.
相比于其他语言, 可变对象与不可变对象在 JavaScript 中更加模糊, 当你了解函数式编程时, 你会听到很多不可变对象的好处. 在 JavaScript 中, 你可以通过 Object.freeze(obj), 让一个对象变得不可变, 但是注意这是浅层的冻结对象, 如果有一个属性的值是个对象, 那这个对象中的属性是可以被修改的. 现在 JavaScript 也出现了 npm deep-freeze , Immutable.js 这些库来帮助你在 JavaScript 中实现不可变对象.
## Imperative and Declarative Programming(命令式和声明式编程)
命令式编程, 描述一段代码的逻辑怎么被显式调用去改变程序的状态. 声明式编程, 描述一段代码的逻辑, 而不需要描述如何完成这段逻辑.
@@ -46,9 +46,9 @@ JavaScript 可以同时被写为命令式和声明式编程方式, 但是随着
- 声明式代码去管理副作用和执行命令式编程
## Hot and Cold Observables
Observables 和数组类似, 只不过数组是被保存在内存中, 而Observables的每一个元素则是异步加入进来. 我们可以订阅这些 observables.
Observables 和数组类似, 只不过数组是被保存在内存中, 而 Observables 的每一个元素则是异步加入进来. 我们可以订阅这些 observables.
Hot Observables 容易会被执行, 即使我们没有订阅它们. 比如说 用户的操作界面的 按钮点击事件, 鼠标移动, 窗口大小改变, 这些都是 Hot Observables. 而cold observable则是需要我们去订阅, 并且会在我们订阅的时候开始执行.
Hot Observables 容易会被执行, 即使我们没有订阅它们. 比如说 用户的操作界面的 按钮点击事件, 鼠标移动, 窗口大小改变, 这些都是 Hot Observables. 而 cold observable 则是需要我们去订阅, 并且会在我们订阅的时候开始执行.
## 响应式编程 RP
响应式编程, 可以看作是面向异步事件流的编程, 声明式的, 表述去做什么, 而不是怎么做.
@@ -69,18 +69,18 @@ Hot Observables 容易会被执行, 即使我们没有订阅它们. 比如说
随着现在各种 SPA 框架的兴起, 理解数据流概念, 对于现在 JS 开发者越来越重要, React 被认为是单向数据流的典范, 使用 Model 作为唯一的数据来源, 控制 View 的渲染. 在 View 层用事件的方式通知 Model 更新, 在反应到 View 层的变化上. 数据沿着一个方向流动, UI 永远不会更新 Model, 而是通过事件或者 setState 方法.
在双向数据绑定中, 数据是在两个方向上流动的, JS可以更新 Model 数据, View 层 也可以更新 Model 数据. AngularJs 的1.x 版本是双向数据流的典型实现.
在双向数据绑定中, 数据是在两个方向上流动的, JS 可以更新 Model 数据, View 层 也可以更新 Model 数据. AngularJs 的 1.x 版本是双向数据流的典型实现.
早在2009年, 双向绑定是 Angualr 最受欢迎的特性之一, 但是 Angular 把这一特性抛弃了. 现在很多流行的框架和库都使用了单向数据流(ReactAngularInfernoRedux等). 单向数据流倡导的是清晰的架构, 数据流动更加清晰和易管理. 对于单向数据流来说说了点View自动更新数据的便利, 但也得到了清晰的数据流.
早在 2009 年, 双向绑定是 Angualr 最受欢迎的特性之一, 但是 Angular 把这一特性抛弃了. 现在很多流行的框架和库都使用了单向数据流(ReactAngularInfernoRedux 等). 单向数据流倡导的是清晰的架构, 数据流动更加清晰和易管理. 对于单向数据流来说说了点 View 自动更新数据的便利, 但也得到了清晰的数据流.
## JS框架中的变化侦测: 脏检查, getter 和 setter, 虚拟 DOM
变化侦测对于现代 SPA应用来说很重要. 当用户更新一些内容时, 应用必须以一种方法知道这种变化, 并做出反应更新.
## JS 框架中的变化侦测: 脏检查, getter 和 setter, 虚拟 DOM
变化侦测对于现代 SPA 应用来说很重要. 当用户更新一些内容时, 应用必须以一种方法知道这种变化, 并做出反应更新.
AngularJS 1.x 使用的是脏检查的方式, 具体做法是对View 中涉及到的 Model 进行深度比较. 脏检查的优点在于它的简单和可预测, 不涉及到 API 和对象的变更. 但是正因为涉及到大量比较, 也很低效.
AngularJS 1.x 使用的是脏检查的方式, 具体做法是对 View 中涉及到的 Model 进行深度比较. 脏检查的优点在于它的简单和可预测, 不涉及到 API 和对象的变更. 但是正因为涉及到大量比较, 也很低效.
Ember 和 Backbone 是使用getters 和 setters 来做变化侦测, 这样涉及到数据修改时, 都会触发变更事件. 而React 是使用了虚拟 Dom 来做变化侦测, React 通过 setState方法来通知变更, 使用虚拟 Dom 来比较是否发生了数据变化.
Ember 和 Backbone 是使用 getters 和 setters 来做变化侦测, 这样涉及到数据修改时, 都会触发变更事件. 而 React 是使用了虚拟 Dom 来做变化侦测, React 通过 setState 方法来通知变更, 使用虚拟 Dom 来比较是否发生了数据变化.
## Web Components组件
## Web Components 组件
Web 组件是 Web 平台上可复用的基础组件, 而 Web Components 则定义了一些规范来实现这些可复用组件.
规范包括:
- 自定义元素
@@ -88,20 +88,20 @@ Web 组件是 Web 平台上可复用的基础组件, 而 Web Components 则定
- Shadow Dom
- HTML imports 引入
Web Components本身并不能代替 SPA框架的功能, 但是它的想法和核心概念, 在很多 SPA 框架中都有体现.
Web Components 本身并不能代替 SPA 框架的功能, 但是它的想法和核心概念, 在很多 SPA 框架中都有体现.
## Smart 和 Dumb 组件
现在Web 的开发严重依赖组件, 而很多时候我们把组件分成 Smart 组件和 Dumb 组件.
现在 Web 的开发严重依赖组件, 而很多时候我们把组件分成 Smart 组件和 Dumb 组件.
Smart 组件, 又叫容器组件, 在组件内处理各种业务逻辑, 通常也管理 Dumb 组件,响应 Dumb 组件的事件.
Dumb 组件, 又叫展示组件, 通常被写成纯函数, 依赖于外部的数据和方法, 专注于展现数据.
## JIT 编译
Just-In-timeJIT)编译指的是代码的运行时, 被编译成机器代码的过程. 在JavaScript 运行时, JIT 能够找到代码的特定模式, 而这些模式可以让 JavaScript 更快的被执行.
Just-In-timeJIT)编译指的是代码的运行时, 被编译成机器代码的过程. 在 JavaScript 运行时, JIT 能够找到代码的特定模式, 而这些模式可以让 JavaScript 更快的被执行.
## AOT 编译
Ahead-Of-Time(AOT), 指的是编写的代码在运行之前, 被翻译成机器代码的过程. AOT给 tree shaking 带来了可能, 使用AOT 预编译, 对于生产环境下的代码有以下好处:
Ahead-Of-Time(AOT), 指的是编写的代码在运行之前, 被翻译成机器代码的过程. AOT 给 tree shaking 带来了可能, 使用 AOT 预编译, 对于生产环境下的代码有以下好处:
- 更少的异步请求, 模板和样式内联在 JS 内
- 更小的体积
@@ -111,24 +111,24 @@ Ahead-Of-Time(AOT), 指的是编写的代码在运行之前, 被翻译成机器
## Tree Shaking
Tree Shaking 是指打包 JS 模块时, 通过对代码的静态分析, 排除掉不用的代码的机制.
Tree Shaking 技术建立在 ES2015模块的, import和 export上, 支持我们导入特定的内容,而不是整个库.
Tree Shaking 技术建立在 ES2015 模块的, import 和 export 上, 支持我们导入特定的内容,而不是整个库.
```
```plain
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
```
这样我们只导入了 BehaviorSubject, 而没有导入整个 Rxjs 库.
# 3 精读
文中讲到的现代 JavaScript 已经很多了, 再对理解的现代JavaScript补充几条:
文中讲到的现代 JavaScript 已经很多了, 再对理解的现代 JavaScript 补充几条:
## Dependent injection(依赖注入)
通过控制反转,父级不需要关心子实现细节,将子类可能用到的实例都初始化好,由子类决定引入哪些依赖。还有一个好处是维持了单实例,这一点在数据流中尤为重要,如果 store 不是单例的,那数据流必然乱了套,既希望传给子类使用,又要维持单例,依赖注入是很好的解决方案。
## Symbol Reflect Proxy
Symbol 是 ES6中加入的一种新的数据类型, 每一个 Symbol 都是独一无二的, 不与其它 Symbol 重复.
Symbol 是 ES6 中加入的一种新的数据类型, 每一个 Symbol 都是独一无二的, 不与其它 Symbol 重复.
ES6中的 Proxy , 则是通过 Proxy 方法, 实现对于对象的一层拦截. 提供一种机制, 代理对象的操作. 而 Reflect 是一个内置的对象,它提供可拦截 JavaScript 操作的方法。方法与代理处理程序的方法相同。
ES6 中的 Proxy , 则是通过 Proxy 方法, 实现对于对象的一层拦截. 提供一种机制, 代理对象的操作. 而 Reflect 是一个内置的对象,它提供可拦截 JavaScript 操作的方法。方法与代理处理程序的方法相同。
这三篇文章非常详细介绍了这三位 API:[symbol](https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/) [reflect](https://www.keithcirkel.co.uk/metaprogramming-in-es6-part-2-reflect/) [proxy](https://www.keithcirkel.co.uk/metaprogramming-in-es6-part-3-proxies/)
@@ -6,7 +6,7 @@
# 精读加密媒体扩展(Encrypted Media ExtensionsEME
本期精读的文章是: [W3C发布加密媒体扩展(Encrypted Media ExtensionsEME)正式推荐标准](http://www.chinaw3c.org/2017-09-pressrelease-eme-recommendation.html)
本期精读的文章是: [W3C 发布加密媒体扩展(Encrypted Media ExtensionsEME)正式推荐标准](http://www.chinaw3c.org/2017-09-pressrelease-eme-recommendation.html)
感谢 [xekri](https://github.com/xekri) 提供 hacker news 上的热门讨论帖:[https://news.ycombinator.com/item?id=15278883](https://news.ycombinator.com/item?id=15278883)
@@ -17,8 +17,8 @@
>
> —— 摘自《[HTML5 DRM 正式成为 Web 标准,EFF 辞职抗议](http://www.solidot.org/comments?sid=53884&op=reply&type=story)》
以上,是我17年9月19日晚收到的一条推送消息。我当时在写《[关于 React 系前端技术的思考](https://tingge.github.io/html/react-redux-reactrouter.html)》,可是它让我意识到,该关注下 <video /> 背后的故事了。
17年下半年发生了两件有趣的撕 X 事件:Facebook 将部分开源项目的防专利流氓证书 “BSD + Pattern” 重新授权为 “MIT” 和 W3C 发布 HTML5 版权保护的 EME 推荐标准。一时,似乎著作权、版权和开源、分享,甚至普世、网络中立性,这些声音开始在不少人耳边盘绕。
以上,是我 17 年 9 月 19 日晚收到的一条推送消息。我当时在写《[关于 React 系前端技术的思考](https://tingge.github.io/html/react-redux-reactrouter.html)》,可是它让我意识到,该关注下 <video /> 背后的故事了。
17 年下半年发生了两件有趣的撕 X 事件:Facebook 将部分开源项目的防专利流氓证书 “BSD + Pattern” 重新授权为 “MIT” 和 W3C 发布 HTML5 版权保护的 EME 推荐标准。一时,似乎著作权、版权和开源、分享,甚至普世、网络中立性,这些声音开始在不少人耳边盘绕。
“无论如何,在当前的现实中,法律是保护著作权的。” 那么,我以 EME 为切入点,和大家聊聊 HTML 5 中如何保护知识产权吧。
@@ -34,24 +34,24 @@
- EME:加密媒体扩展(Encrypted Media Extensions)是 W3C 提出的一种规范,用于在 Web 浏览器和 DRM 代理软件之间提供通信通道。
- MSE:媒体源扩展(Media Source Extensions)是一项 [W3C](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F) 规范,它扩展了HTMLMediaElement,允许 JavaScript 生成媒体流以支持回放。这可以用于自适应流(adaptive streaming)及随时间变化的视频直播流(live streaming)等应用场景。
- MSE:媒体源扩展(Media Source Extensions)是一项 [W3C](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F) 规范,它扩展了 HTMLMediaElement,允许 JavaScript 生成媒体流以支持回放。这可以用于自适应流(adaptive streaming)及随时间变化的视频直播流(live streaming)等应用场景。
- CDM:内容解密模块(Content Decryption Module),客户端或者使用端软件或硬件提供的一个机制,可以播放加密内容。
#### 背景
长期以来,“多方利益”模式的 W3C ,以或标准化引领、或被各方优良实践推动再制定标准的方式,来影响着互联网的发展。
2011年时 Silverlight 、HTML5 及 Flash 还是最受热捧的 RIA (富互联网应用) 技术。当时,Silverlight 的PlayReady DRM、 Flash 的 Flash Media Rights Management(FMRM),在版权保护上已十分成熟。而 HTML5 还处于 <video> 未指明编码标准的萌芽状态、更谈不上版权保护。
2011 年时 Silverlight 、HTML5 及 Flash 还是最受热捧的 RIA (富互联网应用) 技术。当时,Silverlight 的 PlayReady DRM、 Flash 的 Flash Media Rights Management(FMRM),在版权保护上已十分成熟。而 HTML5 还处于 <video> 未指明编码标准的萌芽状态、更谈不上版权保护。
随着移动互联网、视频直播、职能家电等等互联网快速发展,浏览器插件一度成为网络恶意攻击的重灾区,给网络用户安全性带来很大隐患。微软和许多企业都鼓励用户、开发者使用 HTML5 的通信协议,标准化通信可以极大增加网络安全性。其中包括 W3C 的 Media Source Extensions (MSE)、 Encrypted Media Extensions (EME)MPEG的 MPEG-DASH 和 Common Encryption (CENC)。
随着移动互联网、视频直播、职能家电等等互联网快速发展,浏览器插件一度成为网络恶意攻击的重灾区,给网络用户安全性带来很大隐患。微软和许多企业都鼓励用户、开发者使用 HTML5 的通信协议,标准化通信可以极大增加网络安全性。其中包括 W3C 的 Media Source Extensions (MSE)、 Encrypted Media Extensions (EME)MPEG 的 MPEG-DASH 和 Common Encryption (CENC)。
终于,内容提供商(如 Netflix、Adobe、CableLabs 等)从 Flash、Silverlight 插件播放器过渡到统一的 HTML5 视频播放;各大浏览器公司(如 Google, Microsoft, Apple)也逐步抛弃了过时的媒体插件。
EME 作为 HTML 5 DRM 版权保护方案中的一员,虽然从2012年提案开始就颇多争议,但是事实上已被各浏览器以捆绑闭源的 CDM 的沙箱化方式“悄悄”分发。现在,W3C 只是给了它应有的名分罢了。
EME 作为 HTML 5 DRM 版权保护方案中的一员,虽然从 2012 年提案开始就颇多争议,但是事实上已被各浏览器以捆绑闭源的 CDM 的沙箱化方式“悄悄”分发。现在,W3C 只是给了它应有的名分罢了。
#### EME 对 Web 产生的影响
W3C理事长 Tim Berners-Lee 在《[W3C Blog: 关于HTML5标准中的加密媒体扩展(EME)](http://www.chinaw3c.org/archives/1742/)》中阐述了 EME 对内容分发商、媒体、用户、开发者、安全技术研究人员的影响。
W3C 理事长 Tim Berners-Lee 在《[W3C Blog: 关于 HTML5 标准中的加密媒体扩展(EME](http://www.chinaw3c.org/archives/1742/)》中阐述了 EME 对内容分发商、媒体、用户、开发者、安全技术研究人员的影响。
对多数人的影响大概是,可以提供一个相对安全的在线环境使用户可以获取高品质商业级的 Web 音视频等内容,并便捷的就此进行在线互动。
@@ -69,7 +69,7 @@ W3C理事长 Tim Berners-Lee 在《[W3C Blog: 关于HTML5标准中的加密媒
以下是截取 caniuse 网站统计的 EME 和 ESM 的支持情况(点击图片可跳转到对应网址):
[![](./assets/26/eme-support.jpg) ](http://caniuse.com/#search=EME)
[![](./assets/26/eme-support.jpg)](http://caniuse.com/#search=EME)
@@ -95,7 +95,7 @@ W3C理事长 Tim Berners-Lee 在《[W3C Blog: 关于HTML5标准中的加密媒
今天,在传输工作室生产的付费内容的时候,DRM 是必要的。这些内容必须防止被盗,因此 DRM 的代码和工作过程都向终端用户和开发者屏蔽了。解密过的内容不会离开解码层,因此也不会被拦截。
为了标准化 DRM 以及为各平台的实现提供一定的互通性,几个 Web 巨头一起创建了通用加密标准[Common Encryption (CENC)]() 和通用的多媒体加密扩展[Encrypted Media Extensions](),以便为多个 DRM 提供商(例如,EME 可用于 Edge 平台上的 Playready 和 Chrome 平台上的 Widewine)构建一套通用的 API,这些 API 能够从 DRM 授权模块读取视频内容加密密钥用于解密。
为了标准化 DRM 以及为各平台的实现提供一定的互通性,几个 Web 巨头一起创建了通用加密标准[Common Encryption (CENC)](.) 和通用的多媒体加密扩展[Encrypted Media Extensions](.),以便为多个 DRM 提供商(例如,EME 可用于 Edge 平台上的 Playready 和 Chrome 平台上的 Widewine)构建一套通用的 API,这些 API 能够从 DRM 授权模块读取视频内容加密密钥用于解密。
CENC 声明了一套标准的加密和密钥映射方法,它可用于在多个 DRM 系统上解密相同的内容,只需要提供相同的密钥即可。
@@ -111,7 +111,7 @@ CENC 没有规定授权的发放、授权的格式、授权的存储、以及使
- index.html:模拟内容服务商视频播放网页,获取 EME 设置(本例中 eme.js),通过调用 MSE 模块(本例中 mse.js) 逐块加载视频片段并控制播放。
- resources.js:模拟 LicenseKey server,与 CDM 模块交互并提供解密媒体资源所需的 `key`
- media:模拟Key System 和 Packaging service。主要功能是提供一种内容保护(DRM)机制,实际应用中常见的 Key System 有 Clear Key、Playready、Widevine 等;另外,作为 Packaging Service,提供编码并加密媒体资源以供发布和播放使用。
- media:模拟 Key System 和 Packaging service。主要功能是提供一种内容保护(DRM)机制,实际应用中常见的 Key System 有 Clear Key、Playready、Widevine 等;另外,作为 Packaging Service,提供编码并加密媒体资源以供发布和播放使用。
- eme.js: 模拟 EME 通信模块。主要包括监听 MediaKeys 的 message 和 keystatuseschange 变化;发起证书请求;最后,通过 Licensekey 解密 video/audio 流;
- mse.js:模拟媒体源扩展模块,通过调用浏览器提供的 MSE API,来控制视频流播放逻辑。
@@ -120,7 +120,7 @@ CENC 没有规定授权的发放、授权的格式、授权的存储、以及使
| 开源的视频播放器 | 个人点评 |
| ---------------------------------------- | ---------------------------------------- |
| [video.js](https://github.com/videojs/video.js) 和其[插件](https://github.com/videojs/video.js/wiki/Plugins#community-plugins)。设备检测与配置逻辑的 [videojs-contrib-hls](https://github.com/videojs/videojs-contrib-hls) 、广告 [videojs-contrib-ads](https://github.com/videojs/videojs-contrib-ads) | 免费开源的 HTML5 和 Flash 播放器,通过强大的插件应用于 [400,000 网站](https://trends.builtwith.com/media/VideoJS)。采用 Apache License, Version 2.0 授权 |
| [JW Player](https://github.com/jwplayer/jwplayer) | 号称世界上最流行的嵌入播放器,应用于200万网站、每月13亿播放次数。采用 [Creative Commons license](http://creativecommons.org/licenses/by-nc-sa/3.0/) 授权 |
| [JW Player](https://github.com/jwplayer/jwplayer) | 号称世界上最流行的嵌入播放器,应用于 200 万网站、每月 13 亿播放次数。采用 [Creative Commons license](http://creativecommons.org/licenses/by-nc-sa/3.0/) 授权 |
| [Shaka Player](https://github.com/google/shaka-player) | Google 开源的基于 MSE + EME 的 JavaScript 库,支持 DASH、HLS 等。采用 Apache License 2.0 授权 |
| [dash.js](https://github.com/Dash-Industry-Forum/dash.js) | 一个支持 MPEG DASH 的参考实现,适合研究学习。采用 BSD 授权 |
@@ -130,5 +130,5 @@ CENC 没有规定授权的发放、授权的格式、授权的存储、以及使
期待随着标准的发布,注重著作权、版权的互联网能够很快地向有序方向发展。
> 讨论地址是:[精读《W3C发布加密媒体扩展(Encrypted Media ExtensionsEME)正式推荐标准》 · Issue #37 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/37)
> 讨论地址是:[精读《W3C 发布加密媒体扩展(Encrypted Media ExtensionsEME)正式推荐标准》 · Issue #37 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/37)
> 如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。
+1 -1
View File
@@ -93,7 +93,7 @@ OOCSS 成为 css 的面向对象加强版,每个 class 只处理一件事:
## 3.4 SMACSS
### 为css分类
### 为 css 分类
SMACSS 认为 css 有 5 个类别:
@@ -1,6 +1,6 @@
本期精读的文章是:[Front End Performance Checklist 2017](https://www.smashingmagazine.com/2016/12/front-end-performance-checklist-2017-pdf-pages/)
现在随着 web 应用的复杂性日益增加,其性能优化就会显得尤为必要,同时会给性能指标分析带来新的挑战,因为性能指标之间的差异性非常大,这取决于使用的设备、浏览器、协议、网络类型以及其它能够对性能产生影响的潜在因素(如:CDN、ISP、cache、proxy、firewall、load balancer、server等)。
现在随着 web 应用的复杂性日益增加,其性能优化就会显得尤为必要,同时会给性能指标分析带来新的挑战,因为性能指标之间的差异性非常大,这取决于使用的设备、浏览器、协议、网络类型以及其它能够对性能产生影响的潜在因素(如:CDN、ISP、cache、proxy、firewall、load balancer、server 等)。
# 1 引言
@@ -18,11 +18,11 @@
根据 [psychological research](https://www.smashingmagazine.com/2015/09/why-performance-matters-the-perception-of-time/#the-need-for-performance-optimization-the-20-rule) 指出,网站最少在速度上比别人快 20%,才能让用户感觉到比别人的更快。这个速度说的并不是整个页面的加载时间,而是[启动渲染时间](http://www.websiteoptimization.com/speed/tweak/start-render/)[首次有效渲染时间](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint)[交互时间](https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive)。
### 控制响应时间在100ms,控制帧速在60帧/秒
### 控制响应时间在 100ms,控制帧速在 60 帧/秒
[RAIL performance model](https://www.smashingmagazine.com/2015/10/rail-user-centric-model-performance/) 提出的性能优化指标:务必在用户初始操作后的 100ms 内提供反馈。考虑到存在响应时间不足 100ms 的情况,页面最迟要在 50ms 的时候,把控制权交给主线程。
针对动画,其每一帧都需要在 16ms 内完成,这样才能保证每秒 60帧(一秒/60=16.6ms),如果可以的话最好能在 10ms 内完成。
针对动画,其每一帧都需要在 16ms 内完成,这样才能保证每秒 60 帧(一秒/60=16.6ms),如果可以的话最好能在 10ms 内完成。
### 控制首次有效渲染时间在 1.25s,控制 SpeedIndex 在 1000
@@ -32,7 +32,7 @@
### 做好构建工具的选型
不要过度使用那些酷炫的技术栈,坚持选择适合开发环境的工具,如Grunt、Gulp、Webpack、PostCSS,或者组合起来的工具。只要这个工具运行的速度够快,而且没有给项目维护带来太大问题,就够了。
不要过度使用那些酷炫的技术栈,坚持选择适合开发环境的工具,如 Grunt、Gulp、Webpack、PostCSS,或者组合起来的工具。只要这个工具运行的速度够快,而且没有给项目维护带来太大问题,就够了。
### 渐进增强
@@ -40,11 +40,11 @@
### 前端框架
最好使用那些支持服务器端渲染的框架,如Angular,React,Ember 等。所选的框架要保证是被广泛使用并且经过考验的。不同框架对性能有着不同程度的影响,同时对应着不同的优化策略,所以要清楚的了解所选择框架的每个方面。
最好使用那些支持服务器端渲染的框架,如 AngularReactEmber 等。所选的框架要保证是被广泛使用并且经过考验的。不同框架对性能有着不同程度的影响,同时对应着不同的优化策略,所以要清楚的了解所选择框架的每个方面。
### AMP 或 Instant Articles
- Google 的 [AMP](https://www.ampproject.org/) 技术会提供一套可靠的性能优化框架(基于免费的CDN网络)
- Google 的 [AMP](https://www.ampproject.org/) 技术会提供一套可靠的性能优化框架(基于免费的 CDN 网络)
- Facebook 的 [Instant Articles](https://instantarticles.fb.com/) 技术可以在 Facebook 上提升网站的性能。
### 合理利用 CDN
@@ -63,7 +63,7 @@
### micro-optimization 和 progressive booting
- 使用 [skeleton screens](https://twitter.com/lukew/status/665288063195594752) 代替loading indicator 展示
- 使用 [skeleton screens](https://twitter.com/lukew/status/665288063195594752) 代替 loading indicator 展示
- 使用能够加速 App 初始化渲染的技术,如 [tree-shaking](https://medium.com/@richavyas/aha-moments-from-ngconf-2016-part-1-angular-2-0-compile-cycle-6f462f68632e#.8b9afnsub)、[code-splitting](https://webpack.github.io/docs/code-splitting.html)
- 针对[服务端渲染](https://www.smashingmagazine.com/2016/03/server-side-rendering-react-node-express)增加[预编译](https://www.lucidchart.com/techblog/2016/09/26/improving-angular-2-load-times/)环节
- 使用 [Optimize.js](https://github.com/nolanlawson/optimize-js) 来加快初始加载速度,其原理是包装优先级高的调用函数
@@ -71,7 +71,7 @@
### 正确设置 HTTP cache header
需要正确设置 expires、cache-control、max-age以及其它 HTTP 缓存响应头。请使用 Cache-control: immutable,可以参考 [Herokus primer on HTTP caching headers](https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers)、[HTTP caching primer](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en)以及[缓存之最佳实践](https://jakearchibald.com/2016/caching-best-practices/)。
需要正确设置 expires、cache-control、max-age 以及其它 HTTP 缓存响应头。请使用 Cache-control: immutable,可以参考 [Herokus primer on HTTP caching headers](https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers)、[HTTP caching primer](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en)以及[缓存之最佳实践](https://jakearchibald.com/2016/caching-best-practices/)。
### 减少使用第三方库,异步加载 JS
@@ -98,16 +98,16 @@
- 可以使用 service worker 来达到字体缓存持久化
- [关于如何快速入门字体优化的教程](https://pixelambacht.nl/2016/font-awesome-fixed/)
### 快速推送 critical CSS文件
### 快速推送 critical CSS 文件
为了保证能够让浏览器快速渲染,会将所有用于首屏渲染的 CSS 文件整合成一个文件(即 [critical CSS](https://www.smashingmagazine.com/2015/08/understanding-critical-css/)),以 `<style>` 的行内形式内嵌到 `<head>`,这样可以减少 [critical 渲染路径](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/optimizing-critical-rendering-path?hl=en)。由于 HTTP 数据包大小的限制,因此 critical CSS 文件大小不能超过14KB。
为了保证能够让浏览器快速渲染,会将所有用于首屏渲染的 CSS 文件整合成一个文件(即 [critical CSS](https://www.smashingmagazine.com/2015/08/understanding-critical-css/)),以 `<style>` 的行内形式内嵌到 `<head>`,这样可以减少 [critical 渲染路径](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/optimizing-critical-rendering-path?hl=en)。由于 HTTP 数据包大小的限制,因此 critical CSS 文件大小不能超过 14KB。
HTTP/2 协议可以让 critical CSS 用单个 CSS 文件存储,通过服务器推送 CSS 文件的传输方式来减少HTML 文件数据量,由于存在高速缓存问题,因此需要建立[带有缓存的 HTTP/2 服务器传输机制](https://css-tricks.com/cache-aware-server-push/)。
HTTP/2 协议可以让 critical CSS 用单个 CSS 文件存储,通过服务器推送 CSS 文件的传输方式来减少 HTML 文件数据量,由于存在高速缓存问题,因此需要建立[带有缓存的 HTTP/2 服务器传输机制](https://css-tricks.com/cache-aware-server-push/)。
### tree-shaking 和 code-splitting 机制减轻负载
- [Tree-shaking](https://medium.com/@roman01la/dead-code-elimination-and-tree-shaking-in-javascript-build-systems-fb8512c86edf) 机制能够帮助清理生产环境中的冗余代码。可以通过 [Webpack2 Tree-Shaking](http://www.2ality.com/2015/12/webpack-tree-shaking.html) 机制来清理冗余的 exports 代码或者使用 [UnCSS](https://github.com/giakki/uncss)、[Helium](https://github.com/geuis/helium-css) 工具来清理冗余的CSS代码
- [code splitting](https://webpack.github.io/docs/code-splitting.html) 机制是Webpack 的另一个特性,它能够将构建的代码分成多个 chunk,并且对 chunk 按需载入。只要在代码中定义了分离点(split point),Webpack 便会处理好相关的输出文件,不仅能够较少文件数据量,而且还能对代码做到按需载入。
- [Tree-shaking](https://medium.com/@roman01la/dead-code-elimination-and-tree-shaking-in-javascript-build-systems-fb8512c86edf) 机制能够帮助清理生产环境中的冗余代码。可以通过 [Webpack2 Tree-Shaking](http://www.2ality.com/2015/12/webpack-tree-shaking.html) 机制来清理冗余的 exports 代码或者使用 [UnCSS](https://github.com/giakki/uncss)、[Helium](https://github.com/geuis/helium-css) 工具来清理冗余的 CSS 代码
- [code splitting](https://webpack.github.io/docs/code-splitting.html) 机制是 Webpack 的另一个特性,它能够将构建的代码分成多个 chunk,并且对 chunk 按需载入。只要在代码中定义了分离点(split point),Webpack 便会处理好相关的输出文件,不仅能够较少文件数据量,而且还能对代码做到按需载入。
- 用 [Rollup](http://rollupjs.org/) 来 export 代码也能够取得不错的效果
### 提升渲染性能
@@ -131,7 +131,7 @@ HTTP/2 协议可以让 critical CSS 用单个 CSS 文件存储,通过服务器
从目前来看,浏览器对 HTTP/2 支持度还不错,使用 HTTP/2 后,就可以利用 service worker 以及 HTTP/2 的服务器推送功能来获取更显著的性能提升。
在项目进行 HTTPS 改造时,需要评估 HTTP/1.1 项目的用户基数,需要针对这类用户构建并发送符合HTTP2规范的报头。
在项目进行 HTTPS 改造时,需要评估 HTTP/1.1 项目的用户基数,需要针对这类用户构建并发送符合 HTTP2 规范的报头。
### 正确部署 HTTP/2
@@ -142,7 +142,7 @@ HTTP/2 协议可以让 critical CSS 用单个 CSS 文件存储,通过服务器
### 确保服务器的安全性
需要检查是否[正确设置 HTTP 请求头部](https://securityheaders.io/),如 strict-transport-security[使用 Snyk 工具](https://www.w3ctech.com/www.smashingmagazine.com/2016/01/eliminating-known-security-vulnerabilities-with-snyk/)排除已知的漏洞以及[使用 SSL Server Test](https://www.ssllabs.com/ssltest/) 网站来检查证书是否失效。 尽量保证从外部引入的插件以及 js 脚本的载入是通过 HTTPS 协议的,发起 HTTP 请求同时设置 strict-transport-security 以及 content-security-policy HTTP请求头。
需要检查是否[正确设置 HTTP 请求头部](https://securityheaders.io/),如 strict-transport-security[使用 Snyk 工具](https://www.w3ctech.com/www.smashingmagazine.com/2016/01/eliminating-known-security-vulnerabilities-with-snyk/)排除已知的漏洞以及[使用 SSL Server Test](https://www.ssllabs.com/ssltest/) 网站来检查证书是否失效。 尽量保证从外部引入的插件以及 js 脚本的载入是通过 HTTPS 协议的,发起 HTTP 请求同时设置 strict-transport-security 以及 content-security-policy HTTP 请求头。
### 服务器和 CDN 是否支持 HTTP/2
@@ -151,7 +151,7 @@ HTTP/2 协议可以让 critical CSS 用单个 CSS 文件存储,通过服务器
### Brotli 或 Zopfli 压缩算法
- [Brotli](https://samsaffron.com/archive/2016/06/15/the-current-state-of-brotli-compression),是 Google 开源的无损数据格式,其压缩效率要远高于 Gzip 和 Deflate
- [Zopfli压缩算法](https://blog.codinghorror.com/zopfli-optimization-literally-free-bandwidth/),能够将数据编码成 Deflate、Gzip、Zlib 数据格式。用 Zopfli 算法压缩过后的文件能够比同样用 Zlib 算法压缩的文件小 3%-8%
- [Zopfli 压缩算法](https://blog.codinghorror.com/zopfli-optimization-literally-free-bandwidth/),能够将数据编码成 Deflate、Gzip、Zlib 数据格式。用 Zopfli 算法压缩过后的文件能够比同样用 Zlib 算法压缩的文件小 3%-8%
### 激活 OCSP stapling
@@ -186,7 +186,7 @@ HTTP/2 协议可以让 critical CSS 用单个 CSS 文件存储,通过服务器
### 持续监控
在进行快速、无限制的测试时,最好使用一个个人的WebPageTest实例。建立一个能自动预警的性能预算监听。建立自己的用户时间标记从而测量并监测具体商用的数据。使用SpeedCurve对性能的变化进行监控,同时利用New Relic获取WebPageTest没法提供的数据。SpeedTrackerLighthouseCalibre都是不错的选择。
在进行快速、无限制的测试时,最好使用一个个人的 WebPageTest 实例。建立一个能自动预警的性能预算监听。建立自己的用户时间标记从而测量并监测具体商用的数据。使用 SpeedCurve 对性能的变化进行监控,同时利用 New Relic 获取 WebPageTest 没法提供的数据。SpeedTrackerLighthouseCalibre 都是不错的选择。
部署私密的 [WebPageTest](http://www.webpagetest.org/) 测试环境,有助于快速构建测试用例。针对性能开销大的环节建立自动报警机制,可以使用 [SpeedCurve](https://speedcurve.com/) 对性能的变化进行监控,利用 [New Relic](https://newrelic.com/browser-monitoring) 获取 WebPageTest 无法提供的数据。
@@ -251,9 +251,9 @@ render 部分包括 Recalculate Style 和 Layout,如果发现 render 部分耗
#### 降低样式计算和复杂度
添加或移除一个DOM元素、修改元素属性和样式类、应用动画效果等操作,都会引起DOM结构的改变,从而导致浏览器需要重新计算每个元素的样式、对页面或其一部分重新布局(多数情况下),这就是所谓的样式计算。
添加或移除一个 DOM 元素、修改元素属性和样式类、应用动画效果等操作,都会引起 DOM 结构的改变,从而导致浏览器需要重新计算每个元素的样式、对页面或其一部分重新布局(多数情况下),这就是所谓的样式计算。
因此需要减少执行样式计算的元素的个数,降低样式选择器的复杂度,使用基于 class 的方式,如以BEM (Block, Element, Modifier)的方式编写 CSS 代码,能达到最好的样式计算的性能,因为这种方式建议对每个DOM元素都只使用一个样式class。
因此需要减少执行样式计算的元素的个数,降低样式选择器的复杂度,使用基于 class 的方式,如以 BEM (Block, Element, Modifier)的方式编写 CSS 代码,能达到最好的样式计算的性能,因为这种方式建议对每个 DOM 元素都只使用一个样式 class。
#### 避免大规模、复杂的布局
@@ -280,7 +280,7 @@ Timeline 中绿色部分就是 Paint 部分,Summary 会展示绘制的总体
#### 如何优化 Paint
- 提升元素渲染层为合成层,页面的绘制并非是在单层画面里完成的,浏览器的渲染原理,是浏览器将 DOM tree 映射成 GraphicsLayer tree,中间是经过了 RenderObject、RenderLayer 的一系列映射。元素所在的层提升为合成层后可以减少 Repaint
- 使用 transform 或 opacity 实现动画,对于独立的合成层应用 transform 和 opacity 是不会触发 Repaint的,因此尽量对 transform 或 opactiy 应用动画来实现效果
- 使用 transform 或 opacity 实现动画,对于独立的合成层应用 transform 和 opacity 是不会触发 Repaint 的,因此尽量对 transform 或 opactiy 应用动画来实现效果
- 减少绘制区域,对于不需要重新绘制的区域应尽量避免绘制,已减少绘制区域,比如一个 fix 在页面顶部的固定不变的导航 header,在页面底部某个区域 Repaint 时,整个屏幕包括 fix 的 header 也会被重绘,而对于固定不变的区域,期望其并不会被重绘,因此可以通过之前的方法,将其提升为独立的合成层
- 降低绘制复杂度,对于无法避免的 Paint,需要尽可能的减少 Paint 的消耗,有些效果的 Paint 代价十分昂贵,比如绘制一个阴影可能就比绘制一个边框更加耗时,因此开发过程中,需要研究能够实现相同的效果,同时却能达到更小的 Paint 消耗的方法
@@ -311,6 +311,6 @@ Timeline 中绿色部分就是 Paint 部分,Summary 会展示绘制的总体
现在随着 web 应用的复杂性日益增加,其性能优化的重要性越来越突出,且性能优化的方法、技巧、工具也越来越丰富和复杂,本文所展示的内容仅仅只是管中窥豹,希望读者们可以在此讨论一些在实际场景中的性能优化问题以及解决方案。
> 讨论地址是:[精读《2017前端性能优化备忘录》 · Issue #39 · dt-fe/weekly](http://github.com/dt-fe/weekly/issues/39)
> 讨论地址是:[精读《2017 前端性能优化备忘录》 · Issue #39 · dt-fe/weekly](http://github.com/dt-fe/weekly/issues/39)
> 如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。
+11 -11
View File
@@ -6,7 +6,7 @@
我为什么要选这篇文章呢?
sessionstack最近接连发了好几篇文章, 深入探讨JS, 以及 JS 中一些内部原理. 文中也讲到了, 伴随深入了解 JS 中的一些工作原理, 才有可能写出更好的代码和程序.
sessionstack 最近接连发了好几篇文章, 深入探讨 JS, 以及 JS 中一些内部原理. 文中也讲到了, 伴随深入了解 JS 中的一些工作原理, 才有可能写出更好的代码和程序.
而 JS 中的内存管理, 我的感觉就像 JS 中的一门副科, 我们平时不会太重视, 但是一旦出问题又很棘手. 所以可以通过平时多了解一些 JS 中内存管理问题, 在写代码中通过一些习惯, 避免内存泄露的问题.
@@ -23,11 +23,11 @@ sessionstack最近接连发了好几篇文章, 深入探讨JS, 以及 JS 中一
2. 使用分配到的内存(读, 写)
3. 不需要时将其释放/归还
在 C语言中, 有专门的内存管理接口, 像`malloc()``free()`. 而在 JS 中, 没有专门的内存管理接口, 所有的内存管理都是"自动"的. JS 在创建变量时, 自动分配内存, 并在不使用的时候, 自动释放. 这种"自动"的内存回收, 造成了很多 JS 开发并不关心内存回收, 实际上, 这是错误的.
在 C 语言中, 有专门的内存管理接口, 像`malloc()``free()`. 而在 JS 中, 没有专门的内存管理接口, 所有的内存管理都是"自动"的. JS 在创建变量时, 自动分配内存, 并在不使用的时候, 自动释放. 这种"自动"的内存回收, 造成了很多 JS 开发并不关心内存回收, 实际上, 这是错误的.
## JS 中的内存回收
### 引用
垃圾回收算法主要依赖于引用的概念. 在内存管理的环境中, 一个对象如果有访问另一个对象的权限(隐式或者显式), 叫做一个对象引用另一个对象. 例如: 一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用).
垃圾回收算法主要依赖于引用的概念. 在内存管理的环境中, 一个对象如果有访问另一个对象的权限(隐式或者显式), 叫做一个对象引用另一个对象. 例如: 一个 Javascript 对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用).
### 引用计数垃圾收集
这是最简单的垃圾收集算法.此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”. 如果没有引用指向该对象(零引用, 对象将被垃圾回收机制回收.
@@ -64,13 +64,13 @@ window.onload = function(){
### 标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”.
这个算法假定设置一个叫做根`root`的对象(在Javascript里,根是全局对象). 定期的, 垃圾回收器将从根开始, 找所有从根开始引用的对象, 然后找这些对象引用的对象, 从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象.
这个算法假定设置一个叫做根`root`的对象(在 Javascript 里,根是全局对象). 定期的, 垃圾回收器将从根开始, 找所有从根开始引用的对象, 然后找这些对象引用的对象, 从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象.
从2012年起, 所有现代浏览器都使用了标记-清除内存回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进.
2012 年起, 所有现代浏览器都使用了标记-清除内存回收算法。所有对 JavaScript 垃圾回收算法的改进都是基于标记-清除算法的改进.
![](https://raw.githubusercontent.com/dt-fe/weekly/master/assets/29/3.gif)
### 自动 GC 的问题
尽管自动 GC 很方便, 但是我们不知道GC 什么时候会进行. 这意味着如果我们在使用过程中使用了大量的内存, 而 GC 没有运行的情况下, 或者 GC 无法回收这些内存的情况下, 程序就有可能假死, 这个就需要我们在程序中手动做一些操作来触发内存回收.
尽管自动 GC 很方便, 但是我们不知道 GC 什么时候会进行. 这意味着如果我们在使用过程中使用了大量的内存, 而 GC 没有运行的情况下, 或者 GC 无法回收这些内存的情况下, 程序就有可能假死, 这个就需要我们在程序中手动做一些操作来触发内存回收.
### 什么是内存泄露?
本质上讲, 内存泄露就是不再被需要的内存, 由于某种原因, 无法被释放.
@@ -94,11 +94,11 @@ function foo() {
// Foo 被调用时, this 指向全局变量(window)
foo();
```
在这种情况下调用`foo`, this被指向了全局变量`window`, 意外的创建了全局变量.
在这种情况下调用`foo`, this 被指向了全局变量`window`, 意外的创建了全局变量.
我们谈到了一些意外情况下定义的全局变量, 代码中也有一些我们明确定义的全局变量. 如果使用这些全局变量用来暂存大量的数据, 记得在使用后, 对其重新赋值为 null.
#### 2. 未销毁的定时器和回调函数
在很多库中, 如果使用了观察者模式, 都会提供回调方法, 来调用一些回调函数. 要记得回收这些回调函数. 举一个 setInterval的例子.
在很多库中, 如果使用了观察者模式, 都会提供回调方法, 来调用一些回调函数. 要记得回收这些回调函数. 举一个 setInterval 的例子.
```javascript
var serverData = loadData();
setInterval(function() {
@@ -133,7 +133,7 @@ setInterval(replaceThing, 1000);
这个范例的关键在于, 闭包之间是共享作用域的, 尽管`unused`可能一直没有被调用, 但是`someMethod` 可能会被调用, 就会导致内存无法对其进行回收. 当这段代码被反复执行时, 内存会持续增长.
该问题的更多描述可见[Meteor团队的这篇文章](https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156).
该问题的更多描述可见[Meteor 团队的这篇文章](https://blog.meteor.com/an-interesting-kind-of-javascript-memory-leak-8b47d2e7f156).
#### 4. DOM 引用
很多时候, 我们对 Dom 的操作, 会把 Dom 的引用保存在一个数组或者 Map 中.
```Javascript
@@ -153,7 +153,7 @@ function removeImage() {
另外需要注意的一个点是, 对于一个 Dom 树的叶子节点的引用. 举个例子: 如果我们引用了一个表格中的`td`元素, 一旦在 Dom 中删除了整个表格, 我们直观的觉得内存回收应该回收除了被引用的 `td`外的其他元素. 但是事实上, 这个`td` 元素是整个表格的一个子元素, 并保留对于其父元素的引用. 这就会导致对于整个表格, 都无法进行内存回收. 所以我们要小心处理对于 Dom 元素的引用.
# 3 精读
ES6中引入`WeakSet``WeakMap`两个新的概念, 来解决引用造成的内存回收问题. `WeakSet``WeakMap`对于值的引用可以忽略不计, 他们对于值的引用是弱引用,内存回收机制, 不会考虑这种引用. 当其他引用被消除后, 引用就会从内存中被释放.
ES6 中引入`WeakSet``WeakMap`两个新的概念, 来解决引用造成的内存回收问题. `WeakSet``WeakMap`对于值的引用可以忽略不计, 他们对于值的引用是弱引用,内存回收机制, 不会考虑这种引用. 当其他引用被消除后, 引用就会从内存中被释放.
JS 这类高级语言,隐藏了内存管理功能。但无论开发人员是否注意,内存管理都在那,所有编程语言最终要与操作系统打交道,在内存大小固定的硬件上工作。不幸的是,即使不考虑垃圾回收对性能的影响,2017 年最新的垃圾回收算法,也无法智能回收所有极端的情况。
@@ -161,7 +161,7 @@ JS 这类高级语言,隐藏了内存管理功能。但无论开发人员是
所以在 JS 这类高级语言中,有必要掌握基础内存分配原理,在对内存敏感的场景,比如 nodejs 代码做严格检查与优化。谨慎使用 dom 操作、主动删除没有业务意义的变量、避免提前优化、过度优化,在保证代码可读性的前提下,利用性能监控工具,通过调用栈定位问题代码。
同时对于如何利用 chrome调试工具, 分析内存泄露的方法和技巧. 可以参考上期精读[精读《2017前端性能优化备忘录》](https://zhuanlan.zhihu.com/p/30349982)
同时对于如何利用 chrome 调试工具, 分析内存泄露的方法和技巧. 可以参考上期精读[精读《2017 前端性能优化备忘录》](https://zhuanlan.zhihu.com/p/30349982)
# 4 总结
@@ -6,13 +6,13 @@
我为什么要选这篇文章呢?
sessionstack最近接连发了好几篇文章, 深入探讨JS, 以及 JS 中一些内部原理. 文中也讲到了, 伴随深入了解 JS 中的一些工作原理, 才有可能写出更好的代码和程序.
sessionstack 最近接连发了好几篇文章, 深入探讨 JS, 以及 JS 中一些内部原理. 文中也讲到了, 伴随深入了解 JS 中的一些工作原理, 才有可能写出更好的代码和程序.
而 JS 中 Event Loop, 我的感觉就像 JS 中的一门内科, 我们平时只注意外科创伤,却忽视了内科问题往往容易莫名其妙的生病。了解 JS Event Loop 的原理,对 `setTimeout` `Promise` 这种基础概念不再浮在表层,可以写出更可靠的代码,如果你是前端新人,不要总是因为这个问题挂在一面 :p。
# 2 内容概要
从前我对 Event Loop 的理解也并不透彻,通过仔细阅读此文后, Event Loop 、宿主环境、js线程三者之间关系更加透明了,希望读者读完后也能有所体会。
从前我对 Event Loop 的理解也并不透彻,通过仔细阅读此文后, Event Loop 、宿主环境、js 线程三者之间关系更加透明了,希望读者读完后也能有所体会。
文中 Promise、async/await 部分就忽略了,本篇重点介绍 Event Loop 。
@@ -1,21 +1,21 @@
本期精读的文章是:[30js代码创建神经网络](https://medium.freecodecamp.org/how-to-create-a-neural-network-in-javascript-in-only-30-lines-of-code-343dafc50d49)。
本期精读的文章是:[30js 代码创建神经网络](https://medium.freecodecamp.org/how-to-create-a-neural-network-in-javascript-in-only-30-lines-of-code-343dafc50d49)。
懒得看文章?没关系,稍后会附上文章内容概述,同时,更希望能通过阅读这一期的精读,穿插着深入阅读原文。
## 1 引言
![Google Dream](https://cdn-images-1.medium.com/max/2000/1*Z6kowWUGajls6aYusTy4oA.jpeg)
自从Alpha Go 打败了李世石,大家对深度学习的体感更加的强烈,人工智能也越来越多的出现在大家的生活之中。很多人也会谈论,程序员什么时候会被人工智能给替代?与其慌张,在人工智能的潮流下,不断学习新的人工智能相关技术,武装自己,才是硬道理。 本文介绍了如何使用[Synaptic.js](https://synaptic.juancazala.com/#/) 创建简单的神经网络,解决异或运算的问题。
自从 Alpha Go 打败了李世石,大家对深度学习的体感更加的强烈,人工智能也越来越多的出现在大家的生活之中。很多人也会谈论,程序员什么时候会被人工智能给替代?与其慌张,在人工智能的潮流下,不断学习新的人工智能相关技术,武装自己,才是硬道理。 本文介绍了如何使用[Synaptic.js](https://synaptic.juancazala.com/#/) 创建简单的神经网络,解决异或运算的问题。
## 2 内容概要
### 神经网络中的神经元和突触
对神经网络有所了解的人都知道,神经网络就是构建类似人脑的神经系统,在人脑的神经系统中,存在一种非常重要的细胞,叫神经元。在神经网络中,你可以把神经元理解为一个函数,它接受一些输入返回一些输出结果,其中[Sigmoid神经元](https://en.wikipedia.org/wiki/Sigmoid_function)是一种非常常用的神经元,这种神经元以 Sigmoid 函数 作为[激活函数](https://www.zhihu.com/question/22334626)。Sigmoid 函数接受任意的数值,输出 `0``1` 之间的值,大家可以看看常见的几种 Sigmoid 函数的函数曲线。
对神经网络有所了解的人都知道,神经网络就是构建类似人脑的神经系统,在人脑的神经系统中,存在一种非常重要的细胞,叫神经元。在神经网络中,你可以把神经元理解为一个函数,它接受一些输入返回一些输出结果,其中[Sigmoid 神经元](https://en.wikipedia.org/wiki/Sigmoid_function)是一种非常常用的神经元,这种神经元以 Sigmoid 函数 作为[激活函数](https://www.zhihu.com/question/22334626)。Sigmoid 函数接受任意的数值,输出 `0``1` 之间的值,大家可以看看常见的几种 Sigmoid 函数的函数曲线。
![sigmoid](https://img.alicdn.com/tfs/TB1kwPfcOqAXuNjy1XdXXaYcVXa-1398-698.png)
知道了 Sigmoid 函数了,我们可以看一个具体的 **Sigmoid神经元** 例子。
知道了 Sigmoid 函数了,我们可以看一个具体的 **Sigmoid 神经元** 例子。
![Sigmoid example](https://img.alicdn.com/tfs/TB186N_ffDH8KJjy1XcXXcpdXXa-1478-626.png)
@@ -71,11 +71,11 @@ for (var i = 0; i < 20000; i++) {
}
```
简单而言,运行 `myNetwork.activate([0,0])` 时,`[0, 0]`是输入值,它对应的异或运算的结果是false, 也就是 `0`。 这个是前向的传播,所以称为 **激活** 网络,每次前向传播之后,我们需要做一次**反向传播**来更新权重和偏差。
简单而言,运行 `myNetwork.activate([0,0])` 时,`[0, 0]`是输入值,它对应的异或运算的结果是 false, 也就是 `0`。 这个是前向的传播,所以称为 **激活** 网络,每次前向传播之后,我们需要做一次**反向传播**来更新权重和偏差。
反向传播就是通过这行代码来做的: `myNetwork.propagate(learningRate, [0])`,其中 `learningRate `是告诉神经网络如何调整权重的常量,第二个参数 `[0]`是异或运算的结果。
经过20000次学习,我们得到如下的结果:
经过 20000 次学习,我们得到如下的结果:
```js
console.log(myNetwork.activate([0,0]));
@@ -129,7 +129,7 @@ console.log(myNetwork.activate([1,1]));
## 4. 总结
本文介绍了使用[Synaptic.js](https://synaptic.juancazala.com/#/) 创建简单的神经网络,解决异或运算的问题过程,也对**反向传播**的过程进行了简单的解释。文中实现神经网络的代码非常简单,包行注释也不超过30行,但是只会代码会有点囫囵吞枣的感觉,大家可以参考文章中给的引文,了解更多的算法原理。
本文介绍了使用[Synaptic.js](https://synaptic.juancazala.com/#/) 创建简单的神经网络,解决异或运算的问题过程,也对**反向传播**的过程进行了简单的解释。文中实现神经网络的代码非常简单,包行注释也不超过 30 行,但是只会代码会有点囫囵吞枣的感觉,大家可以参考文章中给的引文,了解更多的算法原理。
### 相关资料
@@ -144,6 +144,6 @@ console.log(myNetwork.activate([1,1]));
### 更多讨论
> 讨论地址是:[精读《30js代码创建神经网络》 · Issue #45 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/45)
> 讨论地址是:[精读《30js 代码创建神经网络》 · Issue #45 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/45)
**如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周五发布。**
+1 -1
View File
@@ -2,7 +2,7 @@
本篇是 《框架实现》。
本周精读的文章是 [dob文档](https://dobjs.github.io/dob-docs/v2/guide/introduction.html),如果不熟悉 API,可以简单读一读,文中有些地方会提到一些函数。
本周精读的文章是 [dob 文档](https://dobjs.github.io/dob-docs/v2/guide/introduction.html),如果不熟悉 API,可以简单读一读,文中有些地方会提到一些函数。
## 1 引言
@@ -88,7 +88,7 @@ git 的版本控制实际就是对文件进行管理和控制,其管理方法
![](https://user-images.githubusercontent.com/3983192/34076544-9e24bb62-e325-11e7-8a82-c71577d9589f.png)
其实是 40位 hash-key 的前两位作为目录名,后 38位 作为文件名,这个对象里面的内容就是 file1.js 的内容,可以查看该对象的内容和类型。
其实是 40 位 hash-key 的前两位作为目录名,后 38 位 作为文件名,这个对象里面的内容就是 file1.js 的内容,可以查看该对象的内容和类型。
执行 git cat-file -p [hash-key] 可以查看已经存在的 object 对象内容;
@@ -141,7 +141,7 @@ blob 对象用于存储对应文件的内容,tree 对象可以理解为存储
![](https://user-images.githubusercontent.com/3983192/34078157-79aeaa84-e34f-11e7-93d9-49d807daa3f9.png)
目前每个目录里有一个对象,共5个对象,之前的总体 tree 图只包含了4个对象,执行 git log 查看 commit 记录。
目前每个目录里有一个对象,共 5 个对象,之前的总体 tree 图只包含了 4 个对象,执行 git log 查看 commit 记录。
![](https://user-images.githubusercontent.com/3983192/34078169-ed3a2db6-e34f-11e7-8a8d-f5d902584ed0.png)
@@ -173,7 +173,7 @@ git 版本控制住要就是围绕这三类内部对象展开,分别为 blob
# 4 总结
本文从一个实际问题即如何使用 git 维护丢失的 commit 入手,并给出相应的解决思路和方案,以及通过git 内部三种对象来分析其内部工作机制,希望能过解决读者们对 git 存在困惑的地方,同时也希望读者们积极参与每周精度的讨论,各抒己见,分享自身在实际工作中遇到的问题及其解决思路。
本文从一个实际问题即如何使用 git 维护丢失的 commit 入手,并给出相应的解决思路和方案,以及通过 git 内部三种对象来分析其内部工作机制,希望能过解决读者们对 git 存在困惑的地方,同时也希望读者们积极参与每周精度的讨论,各抒己见,分享自身在实际工作中遇到的问题及其解决思路。
> 讨论地址是:[精读《When You “Git” in Trouble - a Version Control Story》 · Issue #49 · dt-fe/weekly](http://github.com/dt-fe/weekly/issues/49)
@@ -1,9 +1,9 @@
# 摘要
本期精读文章以一个简单的例子,抽丝剥茧细数讲述如何面向用户可视化设计,探索用户最终的目的,化繁为简,化多为少,揉和N张图至一张图,并传达更多的深意。本文原文:http://www.storytellingwithdata.com/blog/2017/12/14/how-we-position-and-what-we-compare
本期精读文章以一个简单的例子,抽丝剥茧细数讲述如何面向用户可视化设计,探索用户最终的目的,化繁为简,化多为少,揉和 N 张图至一张图,并传达更多的深意。本文原文:http://www.storytellingwithdata.com/blog/2017/12/14/how-we-position-and-what-we-compare
下面是案例优化的具体步骤:
# 庖丁解牛 - 可视化案例优化
可视化的时候,一定要优先考虑用户能够比较什么,然后把这些数据放到一个基准上,并把要比较的东西放到最近的地方。这样用户就可以快速简便的处理比较数据。图中有一系列类似分面的柱状图,代表了 Q1 ~ Q3 三个季度的账户 targeted筛选 -> engaged订阅 -> pitched投放 -> adopted采用的四个状态的百分比,这四个状态是呈漏斗式的数据,分别是上一个数据的子集。这个案列中,用户希望能够在一张图中比较所有的数据。如下是基础原始图:
可视化的时候,一定要优先考虑用户能够比较什么,然后把这些数据放到一个基准上,并把要比较的东西放到最近的地方。这样用户就可以快速简便的处理比较数据。图中有一系列类似分面的柱状图,代表了 Q1 ~ Q3 三个季度的账户 targeted 筛选 -> engaged 订阅 -> pitched 投放 -> adopted 采用的四个状态的百分比,这四个状态是呈漏斗式的数据,分别是上一个数据的子集。这个案列中,用户希望能够在一张图中比较所有的数据。如下是基础原始图:
<div>
<img src="http://images2017.cnblogs.com/blog/395937/201712/395937-20171224171406912-710179221.png" width = "500" height = "500" alt="图片名称" align=center />
</div>
@@ -11,9 +11,9 @@
1. 左上角,我们可以对比 Q1 北美 的四条柱状图,因为这四个数据相邻,并且是在一个坐标系中。
2. 最上面一排,我们可以对比 Q1 北美和EMEA engaged订阅(两条紫色柱子)的数据,这个对比需要我们横向用手指去比较,需要参照物,存在一定的门槛。
2. 最上面一排,我们可以对比 Q1 北美和 EMEA engaged 订阅(两条紫色柱子)的数据,这个对比需要我们横向用手指去比较,需要参照物,存在一定的门槛。
基于第2点,我们可以考虑转换下思维,转换下数据呈现的类型可以更快速的比较。文中作者从自己在银行的职业生涯中发现线性图形的角度可以更快速的比较。所以就有了下面一张图
基于第 2 点,我们可以考虑转换下思维,转换下数据呈现的类型可以更快速的比较。文中作者从自己在银行的职业生涯中发现线性图形的角度可以更快速的比较。所以就有了下面一张图
<div>
<img src="http://images2017.cnblogs.com/blog/395937/201712/395937-20171224215602928-500155458.png" width = "500" height = "500" alt="图片名称" align=center />
</div>
@@ -34,9 +34,9 @@
1. 每个阶段,北美的数据都比其他地区低
2. 相比其他区域,为什么APAC 的传播度有了突然的提升?
2. 相比其他区域,为什么 APAC 的传播度有了突然的提升?
3. 最需要关注Q3的数据,以及最终阶段的数据
3. 最需要关注 Q3 的数据,以及最终阶段的数据
# 最后
从这篇文章的可视化案例优化样板,可以总结出以下步骤:
@@ -1,4 +1,4 @@
本期精读的文章是: [coinhive官方文档](https://coinhive.com)及[Monero官方文档](https://getmonero.org/)
本期精读的文章是: [coinhive 官方文档](https://coinhive.com)及[Monero 官方文档](https://getmonero.org/)
懒得看文章?没关系。
@@ -6,13 +6,13 @@
本期精读有所不同,注重实操,先操作获取感性认识,然后再介绍相关的概念,由浅入深,力求不纠缠细节,但不遮盖环节。阅读本文需要对加密货币有一些基本常识 (如果你是一个开发者但是完全没了解过加密货币,可以参考[这里](http://piotrpasich.com/introduction-bitcoin-for-developers/))。希望同学们看完本文能对加密货币领域有一个更深更切实的感受。如果要了解更多细节,文末总结的延伸阅读链接列表是最好的开始。
要注意一点,文中很多说明是默认基于XMRBTC的,他们两个又同源,机制非常相似。**所以很多命题判断并不适用于所有的成千上万的加密货币.** 正相反,新的币种层出不穷,几乎所有的惯例都被打破,所有可能性都被尝试。这一点以下不再做说明。
要注意一点,文中很多说明是默认基于 XMRBTC 的,他们两个又同源,机制非常相似。**所以很多命题判断并不适用于所有的成千上万的加密货币.** 正相反,新的币种层出不穷,几乎所有的惯例都被打破,所有可能性都被尝试。这一点以下不再做说明。
# 1 引言
首先,干货。10行代码,5分钟,不需部署不需构建直接浏览器开挖。
首先,干货。10 行代码,5 分钟,不需部署不需构建直接浏览器开挖。
- 本地创建文件test.html,粘贴如下代码:
```
- 本地创建文件 test.html,粘贴如下代码:
```plain
<!doctype html>
<html>
<head>
@@ -29,15 +29,15 @@
</html>
```
- 本地双击打开。等待JS加载,点击widget "Start Mining"。开始挖矿! 如下图
- 本地双击打开。等待 JS 加载,点击 widget "Start Mining"。开始挖矿! 如下图
![mining](https://img.alicdn.com/tfs/TB13KIljiqAXuNjy1XdXXaYcVXa-880-686.png)
不错,数字已经在跳动,风扇开始工作,永无尽头的挖矿已经开始了。那么重要的是,挖出来的加密货币在哪呢?原来上面的代码里用的还是我的 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.00002 个 Monero(Symbol:XMR)。当前难度为 66G 一个 block,一个 block reward 5.87 XMR,得到一个 XMR 是 11.268G。264K/11.268G = 0.0000234。这就是你目前的收益。
- 替换上面代码中的 data-key 部分,重新开始挖矿。好了,现在挖出的 Monero (这是啥?详见下一节) 已经会到你自己的 coinhive 账户中。用下图来说明,你名下总计算过的 Hash 个数为 264K,当前难度换算为 0.00002 个 Monero(Symbol:XMR)。当前难度为 66G 一个 block,一个 block reward 5.87 XMR,得到一个 XMR 是 11.268G。264K/11.268G = 0.0000234。这就是你目前的收益。
![coinhive dashbaord](https://img.alicdn.com/tfs/TB1LnMljiqAXuNjy1XdXXaYcVXa-2424-1118.png)
@@ -53,19 +53,19 @@
## 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 值,那么民用级 CPU 和 GPU,浏览器或任何沙盒,虚拟机,移动设备当然就都可以。在我们的例子中,计算过程被做成分布式,每个用户可以各自计算,结果按 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)
读完两个算法我们就有了以上疑问的答案:
### 2.1.1 为什么要算XMR而不是比特币或者其他
### 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,就失去了分布到终端用户的意义。而 CryptoNight 在 GPU 上只比同价值 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 循环体的结构:
@@ -89,7 +89,7 @@ XMR 不是唯一选择,但是 BTC 是一个不可选的选择。因为 double
废话不多说,打开源码。本项目没有开源,构建完成后的在https://coinhive.com/lib/coinhive.min.js。先做初步 format 处理,发现有些工作完成在后端,worker shard 一侧。以下用松散的伪码总结一下 client side 的[main success scenario](https://wiki.ihris.org/wiki/Understanding_Use_Cases)流程 (注意很多地方简化了):
```
```plain
- start
- loadWorkerResource
- load worker-asmjs.min.js
@@ -116,7 +116,7 @@ XMR 不是唯一选择,但是 BTC 是一个不可选的选择。因为 double
```
看一下这个过程,结合[cns003 XMR blockchain specs](https://cryptonote.org/cns/cns003.txt)。XMR 的整体 hash input 很小,是:
```
```plain
- size of [block_headerMerkle root hashand the number of
transactions] in bytes (varint)
- block_header,
@@ -128,7 +128,7 @@ XMR 不是唯一选择,但是 BTC 是一个不可选的选择。因为 double
这样整条链路就比较清晰了。再思考一下以下问题:
### 2.2.1 为什么XMR适宜分布式客户端计算
### 2.2.1 为什么 XMR 适宜分布式客户端计算
因为能够利用每个用户的 CPU 和其中的**高速 L3 Cache**。这是中心化执行难以具备的条件.
任何时候当考虑要不要把某项操作推到客户端进行时,都要想明白可以利用客户端的哪个资源,这个资源在客户端是否有明显优势,是否比后端中心化执行更有利。**很多时候答案是,优势并不明显。那么引入的网络通信成本,法规成本,额外开销可能就并不值得.**
@@ -136,7 +136,7 @@ XMR 不是唯一选择,但是 BTC 是一个不可选的选择。因为 double
## 2.3 最后的步骤
到了这里,最后剩余步骤就很标准模式化了。coinhive 作为矿场,代管着用户生产的加密货币。用户发请求提出 XMR,就需要提到一个自己的钱包地址保管,比如 [MyMonero](https://mymonero.com)。也可以直接提到 Exchange 交易所,在其中交易成其他币种,包括法币,然后电汇等等形式提现.
如果对钱包或者交易所感兴趣 (这两个也是很大的话题,比如钱包分硬件钱包和软件钱包,离线冷钱包和线上热钱包,private keyrecover seeds。交易所有多种多样的交易对,有杠杆,期货,空和多,多种挂单类型等等),可以看[这里](https://www.blockchain-council.org/cryptocurrency/hot-wallet-vs-cold-wallet/)和[这里](https://github.com/txbits/txbits).
如果对钱包或者交易所感兴趣 (这两个也是很大的话题,比如钱包分硬件钱包和软件钱包,离线冷钱包和线上热钱包,private keyrecover seeds。交易所有多种多样的交易对,有杠杆,期货,空和多,多种挂单类型等等),可以看[这里](https://www.blockchain-council.org/cryptocurrency/hot-wallet-vs-cold-wallet/)和[这里](https://github.com/txbits/txbits).
# 3 更多讨论
@@ -2,7 +2,7 @@
## 引言
2018 年初,蚂蚁金服 See Conf 上第一个分享《Ant Design 3.0 背后的故事》给很多人带来了启发。主题精彩又深刻,值得反复咀嚼。
## 内容概要
## 内容概要
### 设计体系
Atomic Design 书中提到模块化思路以及原子级的模块抽象的方法启发了很多设计师。而解读者中较为说法较作者认同。设计体系是一个具包容性且充满生命力的东西。包容性指的是从组件库到设计语言到设计方法等所有和产品设计相关的方面。而生命力指的是它并非静态的内容,而是可以应对不断变化的环境,是一个不断进化的过程。
+6 -6
View File
@@ -1,14 +1,14 @@
# 增强现实与可视化
## 引言
增强现实,Augmented Reality,简称AR。在VR的热潮已经褪去,AI当下正红的技术圈里,AR似乎已经成为了过气网红。但似乎在现在,我们可以来冷静地看待一下增强现实这个概念。
增强现实,Augmented Reality,简称 AR。在 VR 的热潮已经褪去,AI 当下正红的技术圈里,AR 似乎已经成为了过气网红。但似乎在现在,我们可以来冷静地看待一下增强现实这个概念。
做前端的最终还是要和用户打交道的,增强现实是不是为用户界面带来了新的可能?可视化作为前端的一个细分领域,这篇文章正是从这个角度出发来重新认识增强现实与可视化。
## 内容概要
移动设备为我们带来了巨大的便利,但是在各种好处下,有一点不容忽视:屏幕太小,根本没有更多的空间来放置更多的内容。对于数据可视化而言,更大的空间意味着更好的数据分析能力。而在空间这件事情上,AR正是很好地解决了这个问题。当下,AR的主要形态还是将虚拟信息作为图层叠加在现实图像之上的形态,这样的AR其实更接近于混合现实。例如使用面部追踪给你脸上加上小动物的Snapchat Lenses,可以在沙发茶几上玩MineCraft的微软HoloLens,和苹果最近新出的ARKit都是这种。
移动设备为我们带来了巨大的便利,但是在各种好处下,有一点不容忽视:屏幕太小,根本没有更多的空间来放置更多的内容。对于数据可视化而言,更大的空间意味着更好的数据分析能力。而在空间这件事情上,AR 正是很好地解决了这个问题。当下,AR 的主要形态还是将虚拟信息作为图层叠加在现实图像之上的形态,这样的 AR 其实更接近于混合现实。例如使用面部追踪给你脸上加上小动物的 Snapchat Lenses,可以在沙发茶几上玩 MineCraft 的微软 HoloLens,和苹果最近新出的 ARKit 都是这种。
所以要怎么通过增强现实来解决当下移动端屏幕空间不足的问题呢?在AR的概念下,一切皆屏幕,现实之上的任何地方都可以展示信息。对于增强现实的未来,文章给出了3个可能的方向。首先,需要提供更好的针对个人的视觉体验。其次,要更好地利用3D展示。最后,让屏幕悬浮在任何地方。
所以要怎么通过增强现实来解决当下移动端屏幕空间不足的问题呢?在 AR 的概念下,一切皆屏幕,现实之上的任何地方都可以展示信息。对于增强现实的未来,文章给出了 3 个可能的方向。首先,需要提供更好的针对个人的视觉体验。其次,要更好地利用 3D 展示。最后,让屏幕悬浮在任何地方。
但是我们也不妨从反面重新理性地看到了增强现实。是不是有了足够空间展示东西我们就要把东西一股脑都展示出来呢?并不是的,增强现实是和真实世界的混合。所以增强现实下的可视化,也不是也不是简单地照搬网页。在增强现实下,我们所展示的每一个虚拟物体,是需要有现实依据的。但是也不必拘泥于现实,增强现实也可以改造所展示的世界。增强现实并不是简单的做加法,只能构建出各类虚拟物品叠加在世界上。同样也能改造世界,使事物变形和消失。
@@ -18,11 +18,11 @@
前端是什么?对于这样一个本源性的话题。每个人都有自己不同的理解。我也不会说书式得非得确定这个问题的答案。但是可以确定的是,如果一切的描述要带上设备端的前提,是短视的。
从PC Web到移动App,前端并没有消失,各大网站也都纷纷开始将自己的网站搬到了手机上以移动APP的形式存在。同样的,如果有一天增强现实来到我们的生活。前端作为和用户界面直接接触的工作,毫无疑问已经会是很重要的一部分。
PC Web 到移动 App,前端并没有消失,各大网站也都纷纷开始将自己的网站搬到了手机上以移动 APP 的形式存在。同样的,如果有一天增强现实来到我们的生活。前端作为和用户界面直接接触的工作,毫无疑问已经会是很重要的一部分。
再看可视化。移动化的大潮让我们开始习惯移动设备,触屏之类的交互的确让我们的信息获取变得更加方便。但是这并不代表全部,特殊行业的人依旧离不开传统的PC,尤其是数据分析人员,对于他们而言,更大的屏幕等于一切。尤其是金融分析师,你给他们四块屏幕都不够用。增强现实下的可视化的确是对这些人的一个重要利好。原因无非就是增强现实的可展现区域相当于整个人眼的视角,远远大于任何我们可以感知到的屏幕。
再看可视化。移动化的大潮让我们开始习惯移动设备,触屏之类的交互的确让我们的信息获取变得更加方便。但是这并不代表全部,特殊行业的人依旧离不开传统的 PC,尤其是数据分析人员,对于他们而言,更大的屏幕等于一切。尤其是金融分析师,你给他们四块屏幕都不够用。增强现实下的可视化的确是对这些人的一个重要利好。原因无非就是增强现实的可展现区域相当于整个人眼的视角,远远大于任何我们可以感知到的屏幕。
如果只是在我们的眼睛上盖上一层信息,那是远远不够的,这样的增强现实仅仅是对屏幕的扩展。增强现实最关键的一点是和现实世界的充分结合:例如配合地理信息和我们的地理定位所带来的信息可视化展示;通过图像识别对物体的感知后,在物体上进行信息可视化展示;等等。文章的几个建议中,最后一个特别有趣——让屏幕悬浮在任何地方。这有两层含义,一是要充分利用好上面所提到的增强现实下的空间,二是我们需要清楚地认识到,人类对于二维世界的感知能力还是远远超出三维世界的。过度利用现实世界所构建出的3D场景,可能并不能为我们的分析带来太多进步。所以,二维的图形信息展示可能依然是增强现实可视化中很重要的一部分。
如果只是在我们的眼睛上盖上一层信息,那是远远不够的,这样的增强现实仅仅是对屏幕的扩展。增强现实最关键的一点是和现实世界的充分结合:例如配合地理信息和我们的地理定位所带来的信息可视化展示;通过图像识别对物体的感知后,在物体上进行信息可视化展示;等等。文章的几个建议中,最后一个特别有趣——让屏幕悬浮在任何地方。这有两层含义,一是要充分利用好上面所提到的增强现实下的空间,二是我们需要清楚地认识到,人类对于二维世界的感知能力还是远远超出三维世界的。过度利用现实世界所构建出的 3D 场景,可能并不能为我们的分析带来太多进步。所以,二维的图形信息展示可能依然是增强现实可视化中很重要的一部分。
这篇文章更加引起我兴趣的地方在于它对于可视化基本元素进行了分析——颜色,亮度,纹理,尺寸,方向、形状、位置……这些从我们平时的显示器屏幕或者手机屏幕上所展示的可视化元素会带来完全不一样的表现。增强现实的增强在于我们可以对现实的展示进行改造,纹理是很有趣的一块。一般来说我们平时做可视化分析,常用到阴影,虚线这些纹理,但是现实世界里的纹理就太丰富了,我们可以充分利用现实花纹了,让展现更直观更融合。
+1 -1
View File
@@ -48,7 +48,7 @@ Rekit Studio 以逻辑视图重新组织了项目,文件目录不见了,取
同时支持在线管理本地文件、集成了 [https://microsoft.github.io/monaco-editor/](monaco-editor) 在线编辑文件,以及在线构建、测试等功能。
同时利用和弦图分析了路由与数据流之间绑定关系,路由与文件绑定关系,可以很轻松找到被遗弃的孤立节点。项目维护时,以看图代替看代码,效率至少提升2 3倍。
同时利用和弦图分析了路由与数据流之间绑定关系,路由与文件绑定关系,可以很轻松找到被遗弃的孤立节点。项目维护时,以看图代替看代码,效率至少提升 2 3 倍。
### Cli 与可视化等效
@@ -1,33 +1,33 @@
# 精读《快速上手构建ARKit应用》
# 精读《快速上手构建 ARKit 应用》
原文地址: [how-to-make-your-own-arkit-app-in-5-minutes-using-react-native](https://medium.com/@HippoAR/how-to-make-your-own-arkit-app-in-5-minutes-using-react-native-9d7ce109a4c2)
## 引言
ARKit是苹果推出的增强现实套装,而react-native-arkit是基于此的上层封装。对于前端开发而言,这可能是最快上手ARKit的方式了,本周精读让我们来初窥ARKitReact Native ARKit这个库。
ARKit 是苹果推出的增强现实套装,而 react-native-arkit 是基于此的上层封装。对于前端开发而言,这可能是最快上手 ARKit 的方式了,本周精读让我们来初窥 ARKitReact Native ARKit 这个库。
## 概要
本次精读我们带来的是一篇《快速上手构建ARKit应用》,原文链接如上。原文标题更加直接,直译的话是“如何在5分钟里利用react native搭建出你自己的ARKit应用”。确实,这篇文章整体也非常明确,以跑起整个ARKit Demo为最直接最主要的目的。
本次精读我们带来的是一篇《快速上手构建 ARKit 应用》,原文链接如上。原文标题更加直接,直译的话是“如何在 5 分钟里利用 react native 搭建出你自己的 ARKit 应用”。确实,这篇文章整体也非常明确,以跑起整个 ARKit Demo 为最直接最主要的目的。
跑起ARKit,也很简单。硬件上,只要有一台iPhone 6S以上的手机;软件上,只要准备好最新版本的XCode和日常开发要用的Node环境了就好。按照`react-native-arkit`的里面的README就可以跑起来了。这个库不
跑起 ARKit,也很简单。硬件上,只要有一台 iPhone 6S 以上的手机;软件上,只要准备好最新版本的 XCode 和日常开发要用的 Node 环境了就好。按照`react-native-arkit`的里面的 README 就可以跑起来了。这个库不
## 3 精读
在开始精读前,我先抛出我的问题三连:Why AR? Why ARKit? Why React Native ARKit?
### 3.1 Why AR?
在之前的第43期精读评论中,我们探讨了AR对于和前端结合的可能性。总的来说,AR把前端开发不再局限在有限的屏幕空间上,对于可视化等对前端展示空间有强烈需求的细分领域,AR是一个很值得研究的内容。如果对于这一块内容有兴趣,欢迎回看第43期精读评论 [《精读〈增强现实与可视化〉》](https://github.com/dt-fe/weekly/blob/master/43.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A2%9E%E5%BC%BA%E7%8E%B0%E5%AE%9E%E4%B8%8E%E5%8F%AF%E8%A7%86%E5%8C%96%E3%80%8B.md)。
在之前的第 43 期精读评论中,我们探讨了 AR 对于和前端结合的可能性。总的来说,AR 把前端开发不再局限在有限的屏幕空间上,对于可视化等对前端展示空间有强烈需求的细分领域,AR 是一个很值得研究的内容。如果对于这一块内容有兴趣,欢迎回看第 43 期精读评论 [《精读〈增强现实与可视化〉》](https://github.com/dt-fe/weekly/blob/master/43.%E7%B2%BE%E8%AF%BB%E3%80%8A%E5%A2%9E%E5%BC%BA%E7%8E%B0%E5%AE%9E%E4%B8%8E%E5%8F%AF%E8%A7%86%E5%8C%96%E3%80%8B.md)。
### 3.2 Why ARKit?
为什么选择 ARKit 入手进行实验?其因有二。第一,相比于 Microsoft HoloLens 的价格,售价只有它三分之一的iPhone X无论是体积重量,还是性价比,抑或是保有量都是大大占优的。噢对,说到保有量,iPhone 6S及以上都支持ARKit。所以说iPhone是我们身边最容易接触到的AR设备是不为过的。第二,ARKit对于硬件的利用能力非一般的前端库可以做到的。大部分的AR前端库可以做到利用陀螺仪来构建一个三维立体空间。但是ARKit更进一步,他利用高频调用摄像头,通过对图像进行识别分析,可以进行空间感知,例如可以识别出一个平面。而这些都是ARKit所提供的,我们只需要调用它的能力就好了。对于开发者而言,ARKit会比一般的AR库更近一步。
为什么选择 ARKit 入手进行实验?其因有二。第一,相比于 Microsoft HoloLens 的价格,售价只有它三分之一的 iPhone X 无论是体积重量,还是性价比,抑或是保有量都是大大占优的。噢对,说到保有量,iPhone 6S 及以上都支持 ARKit。所以说 iPhone 是我们身边最容易接触到的 AR 设备是不为过的。第二,ARKit 对于硬件的利用能力非一般的前端库可以做到的。大部分的 AR 前端库可以做到利用陀螺仪来构建一个三维立体空间。但是 ARKit 更进一步,他利用高频调用摄像头,通过对图像进行识别分析,可以进行空间感知,例如可以识别出一个平面。而这些都是 ARKit 所提供的,我们只需要调用它的能力就好了。对于开发者而言,ARKit 会比一般的 AR 库更近一步。
### 3.3 Why React Native ARKit?
对于当下的前端开发,所有事情可以分为两种——0. 可以用 JavaScript 写的 1. 其他。至于为什么选择`react-native-arkit`这个库,原因自然也可以理解。相比于用原生的Swift来开发,React Native 的开发方式对于前端而言明显是更加容易上手了。对于尝试新东西,这也未尝不可。
对于当下的前端开发,所有事情可以分为两种——0. 可以用 JavaScript 写的 1. 其他。至于为什么选择`react-native-arkit`这个库,原因自然也可以理解。相比于用原生的 Swift 来开发,React Native 的开发方式对于前端而言明显是更加容易上手了。对于尝试新东西,这也未尝不可。
### 3.4 About Demo
相比于原文中从初始化开始的步骤,官方还提供了一个已经配置好的[官方Demo](https://github.com/HippoAR/ReactNativeARKit)。使用这个,如果环境没有问题,的确只需要5分钟就可以跑起来一个ARKit应用了。
相比于原文中从初始化开始的步骤,官方还提供了一个已经配置好的[官方 Demo](https://github.com/HippoAR/ReactNativeARKit)。使用这个,如果环境没有问题,的确只需要 5 分钟就可以跑起来一个 ARKit 应用了。
![](https://img.alicdn.com/tfs/TB11dGFiFmWBuNjSspdXXbugXXa-540-960.jpg)
上面的图片来自原文,可以看到,在`react-native-arkit`这个库里面的所支持的9种基本图形和文字。使用如下已经封装好的React Native组件就可以直接使用了。
上面的图片来自原文,可以看到,在`react-native-arkit`这个库里面的所支持的 9 种基本图形和文字。使用如下已经封装好的 React Native 组件就可以直接使用了。
```javasctipt
<ARKit.Box
@@ -37,16 +37,16 @@ ARKit是苹果推出的增强现实套装,而react-native-arkit是基于此的
```
[几何构造](http://v.youku.com/v_show/id_XMzUxMjk3NjUxMg==.html)
上面的一个视频片段是我们在跑起来Demo后的立体效果。可以很清楚地看到,ARKit感知到了房间这个立方体空间后所构建出来的AR的效果。
上面的一个视频片段是我们在跑起来 Demo 后的立体效果。可以很清楚地看到,ARKit 感知到了房间这个立方体空间后所构建出来的 AR 的效果。
[平面识别](http://v.youku.com/v_show/id_XMzUxMjk3OTc0NA==.html)
而最后的这段视频会更加有趣一些,中央的红圈的出现逻辑是停留在最近识别出的一个平面上。我们可以看到首先识别出了地面,红圈随地面而动;再移向桌面时,很快又识别出了桌面,重新生成了一个停留在桌面上的红圈。通过这一段可以看出无论是明暗划分明显的地面,还是堆满杂物的桌面,ARKit都可以很轻松的识别出来。
而最后的这段视频会更加有趣一些,中央的红圈的出现逻辑是停留在最近识别出的一个平面上。我们可以看到首先识别出了地面,红圈随地面而动;再移向桌面时,很快又识别出了桌面,重新生成了一个停留在桌面上的红圈。通过这一段可以看出无论是明暗划分明显的地面,还是堆满杂物的桌面,ARKit 都可以很轻松的识别出来。
## 4. 总结
苹果的ARKit对空间平面的感知能力胜过了一般的AR渲染库。而iPhone 6S就能跑的特性又让我们觉得AR其实并没有那么遥远。在此基础之上的React Native封装`react-native-arkit`,让我们通过JS就拥有操作ARKit的能力。这的确是一个快速上手ARKit的方式。
苹果的 ARKit 对空间平面的感知能力胜过了一般的 AR 渲染库。而 iPhone 6S 就能跑的特性又让我们觉得 AR 其实并没有那么遥远。在此基础之上的 React Native 封装`react-native-arkit`,让我们通过 JS 就拥有操作 ARKit 的能力。这的确是一个快速上手 ARKit 的方式。
## 5 更多讨论
讨论地址是:[精读《快速上手构建ARKit应用》 · Issue #70 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/70)
讨论地址是:[精读《快速上手构建 ARKit 应用》 · Issue #70 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/70)
如果你想参与讨论,请[点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,每周末发布。
+2 -2
View File
@@ -1,10 +1,10 @@
# 1 引言
本周精读, 来一起总结web开发的环节, 知识块和技能点. 是不是像xx速成班宣传的一样, 培训三个月, 经验顶三年, 入职BAT, 年薪三十万?
本周精读, 来一起总结 web 开发的环节, 知识块和技能点. 是不是像 xx 速成班宣传的一样, 培训三个月, 经验顶三年, 入职 BAT, 年薪三十万?
本文虽然是罗列知识点, 但我想很有意义. 对于学习的人来说, 提供一个路线图. 对从业者来说, 对全局有更好的把控, 利于看到自己的强项和不足. 对组建团队, 更能起到一个点将谱的作用.
在网上我没有搜到任何深入全面的总结, 提供的那几篇已经算稍微好一些的了. 其他的要么太过笼统(前端-后端-数据-运维, 完毕)要么太细太窄(并不是不好, 只是和本文性质不一样). GeneralistSpecialist之间永远是一对辩证矛盾, 持续思考.
在网上我没有搜到任何深入全面的总结, 提供的那几篇已经算稍微好一些的了. 其他的要么太过笼统(前端-后端-数据-运维, 完毕)要么太细太窄(并不是不好, 只是和本文性质不一样). GeneralistSpecialist 之间永远是一对辩证矛盾, 持续思考.
本文提供了有层级的列表形式, 如果有兴趣的读者可以把它做成概念图形式, 相互关联与距离相关, 可能会有意料之外的效果.
+2 -2
View File
@@ -94,9 +94,9 @@ CJS 的做法很不同,主要是由于相对于通过网络请求从文件系
![](https://hacks.mozilla.org/files/2018/03/25_module_map-500x239.png)
在浏览器中你只要将 `type="module" ` 放在 script 标签上。这会通知浏览器这个文件应该被转化为一个模块。同样,只有模块才能够被导入,浏览器也就知道了模块中有哪些引用。
在浏览器中你只要将 `type="module"` 放在 script 标签上。这会通知浏览器这个文件应该被转化为一个模块。同样,只有模块才能够被导入,浏览器也就知道了模块中有哪些引用。
不过在 Node 中,并没有 HTML 标签,所以也没有地方声明 type 属性。社区内的一种方式就是使用 `.mjs` 扩展。使用这个扩展告诉 Node这个文件是一个模块。
不过在 Node 中,并没有 HTML 标签,所以也没有地方声明 type 属性。社区内的一种方式就是使用 `.mjs` 扩展。使用这个扩展告诉 Node 这个文件是一个模块。
无论哪种方式,加载器将决定是否将文件转化为一个模块。如果是一个模块并且有导入的话,它就会开始处理直到所有的文件被获取和转化。
@@ -6,7 +6,7 @@
我们将一块语法规则称为 **产生式**,使用 “Left → Right” 表示任意产生式,用 “Left => Right” 表示产生式的推导过程,比如对于产生式:
```
```plain
E → i
E → E + E
```
@@ -17,7 +17,7 @@ E → E + E
举个例子,比如 `SELECT * FROM table` 可以被表达为:
```
```plain
S → SELECT * FROM table
```
@@ -31,7 +31,7 @@ S → SELECT * FROM table
终结符就是语句的终结,读到它表示产生式分析结束,相反,非终结符就是一个新产生式的开始,比如:
```
```plain
<selectStatement> ::= SELECT <selectList> FROM <tableName>
<selectList> ::= <selectField> [ , <selectList> ]
@@ -43,7 +43,7 @@ S → SELECT * FROM table
对于有二义性的文法,可以通过 **上下文相关文法** 方式描述,也就是在产生式左侧补全条件,解决二义性:
```
```plain
aBc -> a1c | a2c
dBe -> d3e
```
@@ -52,7 +52,7 @@ dBe -> d3e
上面表示,非终结符 `B``ac` 之间时,可以解析为 `1``2`,而在 `de` 之间时,解析为 `3`。但我们可以增加一个非终结符让产生式可读性更好:
```
```plain
B -> 1 | 2
C -> 3
```
@@ -86,7 +86,7 @@ SELECT * from bees WHERE bee = 'red';
上面提到的推导符号 `=>` 在实际运行过程中,显然有两种方向左和右:
```
```plain
E + E => ?
```
@@ -100,7 +100,7 @@ E + E => ?
比如 `select <selectList>``selectList` 产生式,它可以表示为:
```
```plain
<SelectList> ::= <SelectList> , <SelectField>
| <SelectField>
```
@@ -115,7 +115,7 @@ E + E => ?
> Token 见上一期精读 [精读《手写 SQL 编译器 - 词法分析》](https://github.com/dt-fe/weekly/blob/master/64.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90%E3%80%8B.md)
```
```plain
<SelectList> ::= <SelectField> <G>
<G> ::= , <SelectList>
@@ -124,12 +124,12 @@ E + E => ?
这其实是一个通用处理,可以抽象出来:
```
```plain
E → E + F
E → F
```
```
```plain
E → FG
G → + FG
G → null
@@ -139,7 +139,7 @@ G → null
笔者建议此处不要生硬的套公式,在套了公式后,再对产生式做一些修饰,让其更具有语义:
```
```plain
<SelectList> ::= <SelectField>
| , <SelectList>
```
@@ -152,7 +152,7 @@ G → null
设想如下的 sql 文法:
```
```plain
<Field> ::= <Text> as <Text>
| <Text> as<String>
| <Text> <Text>
@@ -161,7 +161,7 @@ G → null
其实 Text 本身也是比较复杂的产生式,最坏的情况需要对 Text 连续匹配六遍。我们将 Text 公因式提取出来就可以仅匹配一遍,因为无论是何种 Field 产生式,都必定先遇到 Text:
```
```plain
<Field> ::= <Text> <F>
<F> ::= <G>
@@ -131,7 +131,7 @@ const selectList =
显然这样做不具备通用性,因为我们将参数名与数量固定了。考虑到上期精读学到的[文法](https://github.com/dt-fe/weekly/blob/master/65.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%96%87%E6%B3%95%E4%BB%8B%E7%BB%8D%E3%80%8B.md),我们可以这样描述 `selectList`:
```
```plain
selectList ::= word (',' selectList)?
word ::= [a-zA-Z]
```
@@ -183,7 +183,7 @@ const field = () => word()
这时注意 `field` 作为一个字段,也可能是文本或函数,我们假设拥有函数处理函数 `functional`,那么用文法描述 `field` 就是:
```
```plain
field ::= text | functional
```
@@ -228,7 +228,7 @@ function tree(...args: any[]) {
可选函数就是分支函数的一个特例,可以描述为:
```
```plain
func? => func | ε
```
@@ -244,25 +244,25 @@ const optional = fn => tree(fn, () => true)
上面通过对 SQL 语句的实践,发现了 `match` 匹配单个单词、 `&&` 连接、`tree` 分支、`ε` 空字符串的产生式这四种基本用法,这是符合下面四个基本文法组合思想的:
```
```plain
G ::= ε
```
空字符串产生式,对应 `() => true`,不消耗 Token,总是返回 `true`
```
```plain
G ::= t
```
单词匹配,对应 `match(t)`
```
```plain
G ::= x y
```
连接运算,对应 `match(x) && match(y)`
```
```plain
G ::= x
G ::= y
```
@@ -8,7 +8,7 @@
为了更加详细的描述这个问题,举一个例子,存在以下岔路:
```
```plain
a -> tree() -> c
-> b1 -> b1'
-> b2 -> b2'
@@ -174,7 +174,7 @@ ChainNode 是对链表节点的定义,这里给出了和当前文章内容相
整个链表结构可能是这样的:
```
```plain
node1 <-> node2 <-> node3 <-> node4
|- function2-1
|- matchToken2-1
@@ -249,7 +249,7 @@ const App = epitath(function*() {
通过 immutagen,依次调用 `next`,生成新组件,且下一个组件是上一个组件的子组件,因此会产生下面的效果:
```
```plain
yield <A>
yield <B>
yield <C>
@@ -49,7 +49,7 @@ runPromiseByQueue([
得到的输出是:
```
```plain
promise 1
promise 2
promise 3
+1 -1
View File
@@ -214,7 +214,7 @@ class Component extends React.PureComponent<Props, State> {
this.rootDom = ReactDOM.findDOMNode(this.rootDomRef) as HTMLDivElement;
this.chart = new G2.Chart({
container: document.getElementById("chart"),
container: this.rootDom,
forceFit: true,
height: 300
});
@@ -259,7 +259,7 @@ const { loading, error, result } = useAsync(fetchUser, [id]);
实现:在 Promise 的初期设置 loading,结束后设置 result,如果出错则设置 error,这里可以将请求对象包装成 `useAsyncState` 来处理,这里就不放出来了。
```tsx
export function useAsync(asyncFunction) {
export function useAsync(asyncFunction: any, params: any[]) {
const asyncState = useAsyncState(options);
useEffect(() => {
@@ -326,8 +326,8 @@ const fetchUser = id =>
});
function useFetchUser(id) {
const asyncFetchUser = useAsync(fetchUser, id);
return asyncUser;
const asyncFetchUser = useAsync(fetchUser, [id]);
return asyncFetchUser;
}
```
+1 -1
View File
@@ -31,7 +31,7 @@ html`
`;
```
很显然,由于跳过了 JSX 编译,换成了原生的 [Template Strings ](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings) ,所以所有组件、属性部分都需要改成 `${}` 语法,比如:
很显然,由于跳过了 JSX 编译,换成了原生的 [Template Strings](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings) ,所以所有组件、属性部分都需要改成 `${}` 语法,比如:
`<${Header}>` 这种写法略显别扭,但整体上还是蛮直观的。
+1 -1
View File
@@ -247,7 +247,7 @@ const functionC = () => chain("y", "c")();
我们就得到了如下的链表:
```
```plain
ChainNode(main)
└── FunctionNode(functionA) ─ TreeNode ─ FunctionNode(functionC)
│── FunctionNode(functionB1)
@@ -163,7 +163,7 @@ export const backendMain = () => {
在文件夹视图下,可以做如下结构规划:
```
```plain
.
├── client # 前端入口
├── server # 后端入口
+1 -1
View File
@@ -292,7 +292,7 @@ ReactDOM.render(
根据笔者的经验,**从上层业务到底层通用组件之间,本地状态数量是递增的:**
```
```plain
业务
-> 全局数据流
-> 页面(完全依赖全局数据流,几乎没有自己的状态)
+5 -5
View File
@@ -71,7 +71,7 @@ function Counter() {
如果我们 **在三秒内连续点击三次**,那么 `count` 的值最终会变成 `3`,而随之而来的输出结果是。。?
```
```plain
0
1
2
@@ -109,7 +109,7 @@ class Counter extends Component {
嗯,结果应该等价吧?3 秒内快速点击三次按钮,这次的结果是:
```
```plain
3
3
3
@@ -505,7 +505,7 @@ function Counter() {
我们将 `count` 作为了 `useEffect` 的依赖项,就得到了正确的结果:
```
```plain
1
2
3
@@ -964,7 +964,7 @@ function Parent() {
}
```
虽然 `Child` 可以通过 `memo``useMemo` 进行优化,**但当程序复杂时,可能存在多个函数在所有 Function Component 间共享的情况 **,此时就需要新 Hook: `useContext` 来拯救了。
虽然 `Child` 可以通过 `memo``useMemo` 进行优化,**但当程序复杂时,可能存在多个函数在所有 Function Component 间共享的情况**,此时就需要新 Hook: `useContext` 来拯救了。
### 使用 Context 做批量透传
@@ -1130,7 +1130,7 @@ const Step = () => {
一个普通的 Redux 组件:
```js
const mapStateToProps = state => (count: state.count);
const mapStateToProps = state => ({count: state.count});
const mapDispatchToProps = dispatch => dispatch;
+2 -2
View File
@@ -6,7 +6,7 @@
但读完这本书后,笔者发现不同人站在不同视角会有不同的理解:如果你是一名数据行业从业者,你可以理解数据在当今行业发展中如何起到作用;如果你是企业高管,你会领悟到商业平台发展的规则;如果你是一名创业者,你能体会到点线面体的存在,找到自己的定位;如果你是一名管理者,你能领域到管理模式正在发生的变化;如果你是一名传统行业从业者,你能体会到为什么互联网会对传统行业带来这么大的冲击;如果你是一名社会评论家,你会找到衡量智能时代对人类社会带来影响的标尺,等等。商业是推动人类社会发展的源动力,甚至也是文化与战争的源头,智能商业正因为将商业讲的通透,才摆脱了普通商业书籍枯燥的理论体系,从社会实践中总结理论,最终能上升到富有哲理的思考。
智能商业一书中有许多关键词,比如 “三浪叠加” “网络协同” “数据智能” “C2B” “S2B2C” “点线面体” “创造力革命” “网红” “互联网X” 等等,能将这些关键词串起来的,笔者认为是 “商业演化”,在近几十年范围内,商业模式存在一些不变底层逻辑(“三浪叠加” “网络协同” “数据智能”),而在大趋势下存在不断演变的商业模式(“C2B” “S2B2C” “点线面体” “创造力革命” “网红” “互联网X”)。
智能商业一书中有许多关键词,比如 “三浪叠加” “网络协同” “数据智能” “C2B” “S2B2C” “点线面体” “创造力革命” “网红” “互联网 X” 等等,能将这些关键词串起来的,笔者认为是 “商业演化”,在近几十年范围内,商业模式存在一些不变底层逻辑(“三浪叠加” “网络协同” “数据智能”),而在大趋势下存在不断演变的商业模式(“C2B” “S2B2C” “点线面体” “创造力革命” “网红” “互联网 X”)。
读完书后会发现,这么多的关键词,最终都为了实现 “C2B” 这个商业最终演化目标,即便是远在十八世纪的工业革命,也在为 C2B 模式打下让物质资源极大丰富的生产力基础,而网络协同和数据智能,都为了让商业规模更大,精准度更强,可以个性化识别每个用户的需求。新的组织模式也是为了更高效服务用户,整合社会 “点线面体” 的生态关系最终可以形成 “C2B” 的服务网络,而网红、互联网 X 都是 C2B 转型在不同阶段、不同行业的尝试。
@@ -48,7 +48,7 @@
**关于未来**
第六章是对未来的判断,重点在互联网与传统产业如何碰撞,提出的 互联网x 概念背后有着更深刻的含义。如果你今年听说了 “产业物联网” 这个名词,可以甄别一下相应的企业,是仅仅将互联网技术运用到了传统行业,还是将传统行业从底层的运作逻辑就互联网化了呢?互联网不仅是一种技术,更是一种思维,互联网思维可以将被传统行业束缚住的各个流程逐渐还原到最原始、高效的模样。
第六章是对未来的判断,重点在互联网与传统产业如何碰撞,提出的 互联网 x 概念背后有着更深刻的含义。如果你今年听说了 “产业物联网” 这个名词,可以甄别一下相应的企业,是仅仅将互联网技术运用到了传统行业,还是将传统行业从底层的运作逻辑就互联网化了呢?互联网不仅是一种技术,更是一种思维,互联网思维可以将被传统行业束缚住的各个流程逐渐还原到最原始、高效的模样。
比如说传统工程需要提前计算销量固化产能,但加入了互联网快速反馈的网络,就可以实时调整产能,当然这需要整个生产流程的互联网化,将整个环节都做到快速反馈。
+1 -1
View File
@@ -140,7 +140,7 @@ const App = epitath(function*() {
其核心是利用 `generator` 的迭代,将 React 组件的平级结构还原成嵌套结构,将嵌套写法打平了:
```
```plain
yield <A>
yield <B>
yield <C>
+2 -2
View File
@@ -2,7 +2,7 @@
微软的市值已经突破一万亿美元了,我们很难想象当年僵化而封闭的微软是怎么涅槃重生的。从仅支持自家 Windows 到收购 Github、从失去移动操作系统市场到与 AWS 平分云服务市场、从 Windows 收费升级到 Win10 限时免费升级、从咒骂 Linux 是癌症到大部分云服务都跑在 Linux 操作系统上、从反垄断、数据隐私被诉讼大户,到 Facebook Google 被监管部门调查时却可以置身事外,微软一定从内部发生了彻底的变革。
新微软的变革经验支持我们学习,[《刷新》](https://www.baidu.com/link?url=tVqSngscNfJokBYi3Jwg66SDAJy8Sn5Y4nChDn1gJRAdsrbNYqXSQl43UyyryLtsNhlf4e0UjUfseUAUpPTuMK&wd=&eqid=c6d6c373000d8ee4000000045d58efd4) 就是一本介绍这场变革的书,它的作者是领导这场变革的现任微软 CEO 萨提亚·纳德拉。
新微软的变革经验值得我们学习,[《刷新》](https://www.baidu.com/link?url=tVqSngscNfJokBYi3Jwg66SDAJy8Sn5Y4nChDn1gJRAdsrbNYqXSQl43UyyryLtsNhlf4e0UjUfseUAUpPTuMK&wd=&eqid=c6d6c373000d8ee4000000045d58efd4) 就是一本介绍这场变革的书,它的作者是领导这场变革的现任微软 CEO 萨提亚·纳德拉。
这本书的关键词是:**同理心、文化变革、成长型思维**。
@@ -12,7 +12,7 @@
## 萨提亚的家庭
作者萨提亚·纳德拉的母亲是一名教师,父亲是一个勤奋的印度高级官员,然而他的成长环境相当宽松,**使作者从小就懂得独立思考并按照自己的意愿做事**。母亲难以兼顾事业与家庭而选择放弃工作,**让作者体会到女性工作的不公平**,父亲**追求上心态与行为帮助作者得到更多职业发展机会**。而孩子扎因天生的重度大脑性瘫痪**使作者学着站在孩子的角度思考问题,学会真正理解同理心。**
作者萨提亚·纳德拉的母亲是一名教师,父亲是一个勤奋的印度高级官员,然而他的成长环境相当宽松,**使作者从小就懂得独立思考并按照自己的意愿做事**。母亲难以兼顾事业与家庭而选择放弃工作,**让作者体会到女性工作的不公平**,父亲**追求上心态与行为帮助作者得到更多职业发展机会**。而孩子扎因天生的重度大脑性瘫痪**使作者学着站在孩子的角度思考问题,学会真正理解同理心。**
作者爱好的运动是板球,这是印度最受欢迎的运动。这项运动带给作者的除了热血沸腾之外,还有对团队合作的理解:**好的领导不仅自己能力要出色,还要能帮助队员提升信心,发挥队员的潜力**,而专业技能优秀的球员,如果不能进行良好的团队合作,最坏的情况甚至会损害团队整体利益。
+541
View File
@@ -0,0 +1,541 @@
# 1. 引言
Tableau 探索式分析功能非常强大,各种功能组合似乎有着无限的可能性。
今天笔者会分析这种探索式模型解题思路,一起看看这种探索式分析功能是如何做到的。
# 2. 精读
要掌握探索式分析,先要掌握探索式分析背后的思维模型。
## 理解数据
有分析意义的数据一般是表结构,即分为行与列,列定义了数据含义,行则构成了数据明细。
当我们将数据作为 “原材料” 使用时,需要将这些明细数据封装为 “数据集” 的概念来理解,数据集概念中,数据就是一个个字段,对于字段,要理解 “维度” 与 “度量” 这两个概念。
### 维度
维度是不能被计数的字段,一般为字符串或离散的值,用来描述数据的维度。
### 度量
度量是可以被计数的字段,一般为数字、日期等连续的值,用来描述数据的量。
<img width=172 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566632483137-9e0268d9-f890-45e6-a3e5-805355b35af9.png#align=left&display=inline&height=464&name=image.png&originHeight=1096&originWidth=406&size=83329&status=done&width=172">
我们首先要将数据集字段归类到维度与度量,才能提高数据分析的效率。**数据分析就是从不同维度下看度量值**,先想清楚要看的是什么数据,比如销量还是利润?这些字段都属于度量,然后想一想要怎么看这些度量,是看总数、拆解到年看、还是按地区看呢?这些字段都属于维度。
**维度和度量是可以单独看的,如果单看维度,那只能看这个维度的明细,比如看 订单日期 这个字段**:
<img width=190 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566633158647-cba541bd-673c-498c-95e6-53c89346c458.png#align=left&display=inline&height=149&name=image.png&originHeight=298&originWidth=380&size=24178&status=done&width=190">
需要注意的时,维度与度量字段还可以分为 **连续** 与 **离散** 。
### 连续 
值是连续关系,即任意两个值之间可以计算差值。
### 离散 
值是离散关系,即任意两个值之间无法计算差值,无法以连续的方式去理解。
**一般来说,维度字段都是离散的,度量字段都是连续的。**从字段类型意义上也能得出相同的结论:维度字段一般为字符串或日期类型,字符串类型都是离散的,度量字段一般为数字类型,数字天生就可以连续。
值得注意的是,连续与离散其实与字段类型、维度度量并无关系,比如维度的日期字段就是可连续的,而就算是字符串类型,也可以以字符串长度等方式 “定义” 一种连续的计算方式。对数字类型的度量字段来说,我们也可以忽略数字之间的联系,将数字看待为字符串,这样数字之间就是离散的。
**上图的 “离散方式看日期” 就是看维度的直观方式,但仍可以用 “连续方式看日期”:**
<img width=309 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566633194083-b17c1c2c-7023-47cd-a94c-48d57fd37217.png#align=left&display=inline&height=308&name=image.png&originHeight=616&originWidth=618&size=37644&status=done&width=309">
离散方式下单看维度只有一条条数据,数据间并无排序规则,而以连续方式看维度,维度就会以某种方式排序:比如上图以时间类型进行排序。此时展示方式也从表格切换为了柱状图,因为表格适合展示离散数据,柱状图的一根柱子就可以展示连续数据。
单看度量时,由于 **度量要依附于维度展示**,因此仅有度量时,只能看这个度量的 **聚合** 概念:
<img width=200 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566633468748-6ffb22b0-8c8d-4f6c-bdb6-a16616e29e70.png#align=left&display=inline&height=107&name=image.png&originHeight=214&originWidth=400&size=15238&status=done&width=200">
如上图所示,单看销量这个度量字段时,我们只能将数据集中所有销量字段聚合在一起来看,**但这种聚合方式也可以分成若干种计算类型 - 求和、平均值、中位数、计数、计数去重、最小值、最大值、方差等等:**
<img width=414 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566633611811-f0bef366-b9ca-47dd-adbc-4e2d311f52de.png#align=left&display=inline&height=502&name=image.png&originHeight=1004&originWidth=828&size=228956&status=done&width=414">
这些能力之间都是 “正交” 的,即单看度量这一个字段,可以以这么多种类型进行计算,那么按维度拆分后,度量依然可以享受如上不同的计算方式。
**也可以用连续方式看度量:**
<img width=184 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566633797820-b7980691-1be7-4644-a321-cac98cc36e5f.png#align=left&display=inline&height=304&name=image.png&originHeight=608&originWidth=368&size=23542&status=done&width=184">
与连续-维度不同,连续-度量图形中除了最后一个值,其他过渡数值都是无效的,因为连续-度量只有一个值。连续-维度也要注意,由于以连续的方式画出图形,中间不存在的点也被 “无缝连接” 了。
数据之间也可以存在父子级关系,有父子级关系就可以进行上卷下钻了,这种父子级关系被称为 “层系字段”:
<img width=209 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566634402929-9e23bff4-4810-4827-bf3b-c6af9ef617ea.png#align=left&display=inline&height=217&name=image.png&originHeight=434&originWidth=418&size=33356&status=done&width=209">
上图的 Orders 就是一个层系字段。层系字段是几个字段的排序组合,**由上到下依次构成下钻关系,从下到上则是上卷的关系。**
### 层系
**只有维度字段才能有层系,**因为度量是不能被拆分的,只有维度才可以被拆分。
维度的拆分可以是有逻辑含义的,也可以是任意的。
**有逻辑含义的层系** 
最典型有逻辑含义的层系字段就是时间了。一个好的 BI 系统识别到日期字段后,应该将拿到的日期字段进行归类,比如判断日期字段粒度到天,则自动生成一个日期层系字段,自动聚合到年,并允许用户随意切换:
<img width=277 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566634723539-8f80cdd3-f8af-41a2-9e89-db94ecfa3200.png#align=left&display=inline&height=161&name=image.png&originHeight=322&originWidth=554&size=51469&status=done&width=277">
如果数据集字段值精确到月,则层系只能最多展开到月。
日期层系的逻辑含义在于,年、季度、月、天这种下钻关系是天然从大到小的关系,符合自然理解。
**任意层系** 
如果层系字段不代表日期,就只能以业务含义组合层系字段了。**比如可以将层系按照 订单日期 -> 商品 ID -> 运货日期的方式组合:**
<img width=624 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566634964577-6dad1f7c-b01b-419a-8b7c-6e0832d6c8b6.png#align=left&display=inline&height=132&name=image.png&originHeight=308&originWidth=1454&size=41917&status=done&width=624">
这种下钻方式,可以看到每个订单日期下有哪些商品,每个商品分别运货日期是什么。
**也可以按照商品 ID 拆分出不同的订单日期与运货日期,这种层系组合方式就是以商品 ID 为主要视角:**
<img width=622 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566635114693-57a2b260-d7cc-4945-9050-361ce4608e09.png#align=left&display=inline&height=129&name=image.png&originHeight=302&originWidth=1456&size=44825&status=done&width=622">
可以看到,不同思维角度会按照不同的方式组合层系。比如一家大公司要查看财务问题,维度有:BU、日期,度量有:销量。
那么有两种下钻方式:BU -> 日期、日期 -> BU。无论哪种下钻方式,都能看到每个 BU 按日期销量的明细,但 BU -> 日期 能看到每个 BU 按日期聚合的总销量,而 日期 -> BU 能看到不同日期按 BU 聚合的总销量,前者更易对比出 BU 之间差异,后者更易对比出日期之间的差异。
## 理解配置
配置是探索式分析的入口,要理解分析模型首先得理解配置模型。
Table 主要配置分为行、列、标记与筛选。通过这四个配置区域可以组合成千变万化的数据洞察模型。既然如此,让我们看看这种配置思路是什么,以及为何这四种配置相互组合就能覆盖整个探索式分析场景?
我们不需要考虑三维数据分析场景,因为三维透视的关系,图形丢失了精确大小关系,没有精度的数据是没有分析价值的。由于在二位平面中分析数据,**大部分图表都可以用 “行、列” 方式进行配置**。
**也许有人会问,为什么不用维度与度量替代行列呢**?这是一个很好的问题,有数据分析经验的人会站在维度与度量角度思考问题,因此对于任意图表,只要配置维度、度量即可呀?笔者从三个方面说说自己的理解:
1. 探索式分析思路中,不关心图表是什么,也不关心图表如何展示,因此图表是千变万化的,比如折线图可以横过来,条形图也可以变成柱状图,因此 **你将维度放到列,就是一个柱状图,你将维度放到行,就是一个条形图** 。
2. 将精力真正放到你要拖拽的字段上。由于字段已经有维度、度量的区别,配置区域就不要再限定维度与度量了,减少理解成本。
3. 维度与度量可以同时放在行或列上,这是探索式分析的另一个精髓能力,看下图:
<img width=306 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566636365926-c5d37423-0e32-4382-ae7d-2b9760caeb97.png#align=left&display=inline&height=435&name=image.png&originHeight=1240&originWidth=872&size=94620&status=done&width=306">
做探索式分析功能时,要跳出思维定式:**为什么条形图的纵轴不能放维度呢?**如上图所示,如果行拖拽了两个不同的度量,那么可以出现两条线或者双轴图,但当拖拽一个维度一个度量时,可以对图表进行 **分面** ,比如观察 2013 ~ 2016 年不同顾客对销量的贡献。
### 行
表格类的行、图表类的纵轴。一般建议放置度量字段。
### 列
表格类的列、图表类的横轴。一般建议放置维度字段。
如上所示,无论行还是列,都可以进行任意维度度量组合,且字段数量不限,而且可以在任何层级进行下钻。**对图表来说,多个维度时需要进行分面处理:**
<img width=476 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566637370597-d52b7677-9ec4-40f3-aa54-a0382ac56de1.png#align=left&display=inline&height=428&name=image.png&originHeight=1448&originWidth=1612&size=106528&status=done&width=476">
如上图所示,将列放置两个维度字段成为柱状图,那么横轴就要同时表示两个维度,如上图所示。如果横轴还有更多的维度,可以再不断对横轴进行拆分。
横轴(列)多维度字段的顺序也会影响图表的展现。**上图最后一个字段是 Category 默认是离散的,所以这个离值就决定了图表使用柱状图,图表类型由维度周最后一个字段连续或离散决定。**
比如我们对调 Order Date 与 Category 会怎样?
<img width=476 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566637657990-0387a37c-f4dc-45ca-a53a-aa10b4573318.png#align=left&display=inline&height=420&name=image.png&originHeight=1456&originWidth=1620&size=137950&status=done&width=467">
我们得到了三个不同类目近 12 个月的趋势,之所以是折线图,因为图表的维度轴(列)是连续的。**如果我们对 Order Date 进行天级别的下钻:**
<img width=462 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566637772126-a9825860-4851-41ae-a56d-df984ea49b4c.png#align=left&display=inline&height=328&name=image.png&originHeight=1462&originWidth=2062&size=128020&status=done&width=462">
可以看到,**下钻功能本质上就是维度轴支持对多个维度字段拆分处理。只要图表支持了维度轴任意维度字段的分面展示,那么配置端就可以将下钻按照拖了多个字段的方式去理解了。**
**如果我们将折线图切换为表格,会发生什么?**
<img width=578 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566637986444-1a78b31f-6bb6-4c3e-b69b-0bd5a32c399f.png#align=left&display=inline&height=211&name=image.png&originHeight=738&originWidth=2018&size=111516&status=done&width=578">
我们会发现,原本存在于列的 Category 被自动挪到了行,原本存在于行的 Sales 被挪到了 “标记” 区域。在正式介绍 “标记” 区域前,先理解一下为何会发生这种转变:
**表格类组件是双维度组件,折线图是单维度组件。**也就是表格的行与列都是维度,而折线图横轴作为维度后,纵轴就要作为度量。上面的例子中,折线图维度有两个字段,虽然通过分面方式渲染出来了,但当切换为支持双维度的表格后, **可以将多余的一个维度挪到表格组件另一个维度区域中**
而表格行与列都是维度的情况下,单元格的值就需要用 “标记” 中文本来表示,因此原折线图的度量字段自动转移到了 “标记” 区域。
### 标记
标记区域也采取字段拖拽的方式,即对字段进行标记。
标记区域分为 **颜色、大小、标签、详细信息、工具提示、路径。**标记正如其名,是作用于图表上的标记,**即不会对图表框架有实质性影响的辅助标记信息。**
对不同图表来说,影响最大的是行与列,它能决定用什么图表,如何拆分数据。而标记往往是改变图表中辅助性元素,比如文字或者颜色等等。
#### 工具提示
不影响任何图像显示,仅仅在提示信息中新增字段信息。
**对图表来说,指的是 Tooltip 提示信息增加对应的字段:**
<img width=424 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566655533184-4ea4560f-8fef-40aa-9910-c301842c53b4.png#align=left&display=inline&height=363&name=image.png&originHeight=1000&originWidth=1168&size=91870&status=done&width=424">
从上图可以看到,利润字段放在工具提示区域,则图表的 Tooltip 会新增利润这个字段的信息。**值得关注的是,Tableau 所有图表都支持 Tooltip 包括表格:**
<img width=623 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566655642537-e7a18aa6-f619-45c4-b2b3-f95274d40107.png#align=left&display=inline&height=157&name=image.png&originHeight=374&originWidth=1480&size=42938&status=done&width=623">
这保证了配置统一,行为统一。
#### 大小
控制图表大小。
对于线图,控制线的粗细;对于气泡图控制气泡大小;对于柱状图控制柱子粗细;但是对面积图与表格没有明显作用。这得益于 Tableau 将每个图表大小属性尽可能抽象出来。
<img width=360 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566655966495-9241a848-b8c0-48b7-b0a4-8fbcc44ff58c.png#align=left&display=inline&height=273&name=image.png&originHeight=748&originWidth=988&size=58386&status=done&width=360">
<img width=360 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566656245320-7f29da9d-363f-4fd3-95f3-15b0ec2a3522.png#align=left&display=inline&height=223&name=image.png&originHeight=982&originWidth=1602&size=99303&status=done&width=364">
#### 文本
即直接展示在图表上的文本。
对普通图表来说,文本体现为 Label,即直接展示在图表上的文字。比如柱状图默认是没有 Label 文字的,要将对应字段拖拽到文本标记上才会出现。
<img width=404 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566656520351-588602a5-db01-4e46-bfdd-6f771353d7a8.png#align=left&display=inline&height=379&name=image.png&originHeight=934&originWidth=996&size=77362&status=done&width=404">
这体现出与普通报表构思的不同。对普通报表来说,Label 是通过一个勾选项开启的,Label 对应的值就是图表度量这个字段的值。而 Tableau 将标签值以字段方式开放拖拽,就有了展示与值分开的可能性,可适用范围更广。
> 有人觉得长度和数字一定要对应上,这也是对数据理解不同导致的。Tableau 将文本(标签)列在标记里,说明文本和颜色、大小一样,都是一种附加的信息展示维度,很多时候不需要两种方式展示同一种信息,反而需要图形以更多方式以不同维度展示信息。
#### 颜色
控制图表的颜色。
比如在度量为销量时,可以将利润作为颜色,甚至再将折扣作为文本,通过一个折线图同时看多种度量信息:
<img width=386 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566656981186-8f2441d6-78d0-4d34-af7f-139b0f21cb30.png#align=left&display=inline&height=344&name=image.png&originHeight=888&originWidth=996&size=82343&status=done&width=386">
与之对比,我们可以将利润放在右 Y 轴作为双轴图达到相同的效果:
<img width=439 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566657023624-c01b0008-e7d7-4991-99c1-beddc8c72762.png#align=left&display=inline&height=319&name=image.png&originHeight=886&originWidth=1218&size=112190&status=done&width=439">
**标记就是为了在不增加行、列字段数量基础上,通过颜色、大小、标签、工具提示等维度展示出额外信息。**
#### 详细信息
如果将度量拖拽到详细信息,会发现完全没有作用。因为 “详细信息” 只有拖拽维度字段才生效。“详细信息” 其实是用作下钻的,拖拽一个维度字段后,可以按照这个维度进行下钻。
<img width=533 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566657906186-3348cdf7-823f-4111-9685-dbf5fb05c0f8.png#align=left&display=inline&height=376&name=image.png&originHeight=1054&originWidth=1496&size=102786&status=done&width=533">
如上图所示,将销售按照产品线拆解成三条线。但这三条线无法分辨,因此可以使用颜色来拆分维度:
<img width=533 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566657988073-e8e50316-feb2-4c7f-98dc-45f4a2ffd624.png#align=left&display=inline&height=372&name=image.png&originHeight=1052&originWidth=1510&size=112469&status=done&width=534">
这样就能将拆解的内容按不同颜色展示。因此, **对标记作用的字段如果是维度字段,且作用于颜色、大小、标签、详细信息时,会额外进行维度进行拆解,并对拆解后的内容进行颜色或大小区分。** 
相信读到这里会有个疑问:按照维度进行拆解与维度拖拽多个字段进行字段有什么区别?我们试一下看看效果,将产品类目维度拖拽到销量所在的行,对销量进行销量维度的拆分:
<img width=570 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566658461645-fbcc1b14-0111-47b1-b643-d23f36f96ef2.png#align=left&display=inline&height=512&name=image.png&originHeight=1058&originWidth=1178&size=92443&status=done&width=570">
**可以看到,在行、列进行的多维度拆分使用的是分面策略,而在标记中对维度进行拆分使用的是单图表多轴方式来实现。**
除此之外的区别在于,在标记进行的维度拆分默认作用于度量,而行列上的多维度拆分可以任意作用于维度或度量。
> 同时配置端要限制 **能拆分的只有维度或离散状态的度量** ,也就是只有离散状态的字段可以被拆分。如上图所示,我们不能将 Category 拖拽到 Sales 右侧,除非将 Sales 设置为离散类型。
> TipsTables 对维度与度量分别分配了蓝色、绿色,当我们将绿色度量字段设置为离散类型时,这个度量字段会变成蓝色,也就是当作了维度字段进行处理。
最后,标记区域不仅能拖拽字段,还可以单击后修改详细配置,比如修改颜色详细配置:
<img width=220 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566658916573-5c989763-bd8f-4f2d-8f57-9f52b9c39b20.png#align=left&display=inline&height=448&name=image.png&originHeight=896&originWidth=442&size=39310&status=done&width=221">
或者对工具提示的 Tooltip 内容进行定制:
<img width=557 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566658941101-e4eac6aa-d4f1-4397-9be2-ccc424211f00.png#align=left&display=inline&height=319&name=image.png&originHeight=910&originWidth=1590&size=207889&status=done&width=557">
### 筛选器
Tableau 将所有筛选条件都收敛到筛选器中,我们可以通过拖拽字段的方式对某个字段进行筛选:
<img width=635 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659069046-3e4f195e-1c9d-492d-bfe7-6f6d89997692.png#align=left&display=inline&height=210&name=image.png&originHeight=494&originWidth=1494&size=138806&status=done&width=635">
如上图所示,比如只看办公用品与科技产品。但其实除了这个通用功能之外,Tableau 还支持更强大的图表交互功能,即点击或圈选图表后,可以对选中的点(字段值)进行保留或排除:
<img width=606 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659178209-134b5fc5-b067-4481-bf99-ed36c8c458c7.png#align=left&display=inline&height=198&name=image.png&originHeight=442&originWidth=1350&size=55242&status=done&width=606">
**当我们选择排除这几个点时,会自动生成一份对维度字段的筛选条件排除掉选中日期,所以图表是完全数据驱动的:** 一般来说
<img wdith=576 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659259441-22ef9da8-e3bc-4cef-b32e-c538c1dfe3e0.png#align=left&display=inline&height=268&name=image.png&originHeight=696&originWidth=1494&size=189252&status=done&width=576">
如果属性存在下钻关系会如何呢?无论是行列中对维度的下钻,还是通过标记对维度进行了拆解,筛选都是对 **字段层系** 生效的:
<img width=575 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659420072-3e6e41f4-bc99-4d7d-a93f-553abe429a94.png#align=left&display=inline&height=169&name=image.png&originHeight=442&originWidth=1504&size=155974&status=done&width=575">
如上图所示,对下钻后的字段进行筛选,**那么筛选条件也会自动构造出临时的字段层系,并对这个临时层系进行筛选。** 可以看到,我们不仅能在字段配置区动态组成层系字段,在筛选器中也可以生成临时层系进行筛选,我们需要支持任意层系组合的字段,并作用于筛选器、行列,甚至是标记上。
顺带一提,我们还可以对设置了筛选的字段层系组合拖拽到任意地方使用:
<img width=393 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659649713-3428813a-d156-4c80-935f-e40de333a8fe.png#align=left&display=inline&height=434&name=image.png&originHeight=1050&originWidth=950&size=88795&status=done&width=393">
要处理这种场景,**我们需要让所有字段都拥有筛选能力**,普通字段等于没有筛选条件,我们也可以对一个包含了筛选条件的字段拖拽到任何位置作用。
刚才是对维度进行的筛选,有没有对度量进行筛选的场景呢?有,但我们只能手动将度量字段拖拽到筛选器位置进行手动筛选:
<img width=613 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659919516-77548c29-7c02-4857-9eec-fb1465826f9a.png#align=left&display=inline&height=312&name=image.png&originHeight=832&originWidth=1636&size=86512&status=done&width=613">
如果我们进行图表内的圈选操作,增加的筛选条件一定是按维度来的:
<img width=613 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566659955376-66830503-bce1-443d-ab98-9fbc90e54fdb.png#align=left&display=inline&height=273&name=image.png&originHeight=756&originWidth=1694&size=80004&status=done&width=612">
这么理解这一行为:维度是离散的,勾选操作能表达的含义有限,比如勾选折线图的某些点,如何知道我们要勾选的是维度的那几个月,还是度量的利润范围呢?
<img width=613 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566660078571-ecf7c466-44c1-46a5-9ef0-d3692bf22fb8.png#align=left&display=inline&height=555&name=image.png&originHeight=1468&originWidth=1620&size=113222&status=done&width=613">
**由于最终勾选操作落地在点上,而不是区间上(连续值也不适合进行圈选),所以默认按对维度进行筛选是最准确的理解。**如果上图的操作意图中,你想勾选的不是 6~12 月的区间,而是销量在 13k ~ 45.5k,则需要手动拖拽利润字段,并精确输入筛选范围:
<img width=482 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566660226577-102ca29f-b6ef-43a7-860a-041b04274da8.png#align=left&display=inline&height=353&name=image.png&originHeight=774&originWidth=1056&size=81734&status=done&width=482">
值得注意的是,对连续型度量进行筛选前,还可选择聚合方式:比如对求和的值进行范围筛选,或者对最大值进行范围筛选,功能十分强大。
## 理解图表
图表是数据可视化的载体,只有数据与配置,没有各式各样的图表,很难产生直观的数据洞察。
可以说, **按照探索式分析的思路,当配置好数据与配置后,可以有多种可视化载体去展示这种配置信息。** 比如行、列分别拖拽了日期与销量,那么折线图、表格、散点图、柱状图都可以满足需求,但如果行所在的字段是离散的,那么折线图、散点图就不适合了,这就需要图表推荐功能根据配置推荐合适的图形展示。
Tableau 内置的图表分为 N 大类 - **表格、地图、柱折面饼、散点/象限图** 、以及直方图、盒须图、甘特图、靶心图等。可见分析数据,不需要太多种类可视化展现方式,但对于每个图表组件来说,都需要修炼深厚的内功,做好一个表格、折线图并不简单。
### 行与列
表格、地图、柱折面饼、散点/象限图等都可以用行与列描述基本架构:
- 表格天然拥有行与列,对调后则代表转置。表格的行与列必须是维度字段,如果拖拽度量字段上去会自动切换为其他图表,再切回来则会把度量字段挪动到 “文本” 标记区域中。
- 地图行与列就是经纬度,当维度字段放到 “详细信息” 时,根据地理映射表转化为经纬度自动生成经纬度放在行与列。
- 柱折面饼、散点/象限图都是直角坐标系的图形,以维度字段作为维度轴,以度量字段作为度量轴。
#### 行列的下钻 
在行或列存在多个维度字段时,图表要进行相应下钻。表格对于行下钻如下图所示:
<img width=529 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566699040802-40a23a43-7a23-43d1-9d06-27a6413803e8.png#align=left&display=inline&height=314&name=image.png&originHeight=856&originWidth=1440&size=106220&status=done&width=529">
**上图也可以理解为展示出 Order Date 与 Order ID 的明细数据,按照 Order Date 分组且列合并。**下钻就是一步步接近明细数据的过程,但目的不是为了看明细表,而是看某些维度下按其他维度拆分的详细信息。
图表下钻和表格思路是一致的:
<img width=529 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566699822756-b087948b-3104-4bd4-a76b-1cb2067edd65.png#align=left&display=inline&height=335&name=image.png&originHeight=1338&originWidth=2114&size=109757&status=done&width=529">
对于维度轴多维度下钻,将每个维度轴下钻到更细粒度。图表在行与列同时下钻时,与表格的表现稍有不同。仅从轴来看拆解方式是相同的,内部展示了多套轴:
<img width=667 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566700041429-03bcb68a-dd00-4cab-a256-f37234bb2300.png#align=left&display=inline&height=423&name=image.png&originHeight=1350&originWidth=2128&size=155697&status=done&width=667">
**可以认为,当行或列上最后一个字段为度量时,就会切换为图表展示,因为图表适合展示连续状态。**如果排除上图蓝色区域,剩下的区域就是个交叉表,交叉表只是行与列同时存在维度字段的场景,仅有行或列时就变成了普通表格;而图形的下钻和表格下钻机理相同,只是把 “单元格” 的文本换成了柱子或线。
**所以对任何图表的下钻,都是对轴的下钻,**相同的是单元格属性永远不会改变,表格的单元格是文本,图形单元格是图形,一个简单折线图可以理解为对整体行与列单元格进行 “连续打通”:
<img width=406 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566700448492-a7b96c99-b600-41b0-9159-e88412f4402b.png#align=left&display=inline&height=329&name=image.png&originHeight=1342&originWidth=1654&size=105794&status=done&width=406">
如果继续对行列添加维度进行下钻,其实是对轴进行下钻。**排除度量字段不看,就是一个交叉表的下钻过程,如下图所示蓝色框圈住的部分就是一组大的单元格**:
<img width=629 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566700520147-c644eae8-8c04-4c7d-82aa-b5e4e1c2e4d8.png#align=left&display=inline&height=467&name=image.png&originHeight=1340&originWidth=1806&size=163645&status=done&width=629">
由于最后一个字段是度量,因此在叶子结点的展开就不是表格模式的单元格,而是连续的线条了。
经过上面的总结,我们要意识到,在探索式分析场景对行列的下钻,表格与图表的逻辑是通用的,实现时也要整体考虑。**将轴功能抽离成通用部分来做,表格与图表的区别只是对最后一个字段单元格是离散处理还是连续处理。**
#### 层系的下钻 
<img width=528 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566699587535-dd5e11f6-6fd2-42ce-be2b-e01d7dee650a.png#align=left&display=inline&height=300&name=image.png&originHeight=818&originWidth=1438&size=102881&status=done&width=528">
层系字段下钻与拖多个字段表现一致,但由于存在父子关系,因此在图表上可以展现出 “展开” “收起” 按钮,点击后并不是对图表本身进行操作,而是发送一个事件对 “行” 进行操作,最后通过数据驱动完成展开或收起动作。
#### 不适合行列的图表
饼图就不适合行列,因为饼图是根据离散维度进行拆分,扇叶大小可以由一个度量字段决定,因此对饼图来说,行就对应到 “颜色”、列就对应到新增的 “角度” 这个标记:
<img width=336 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566704242607-bab5e722-d27c-4ad5-8518-b470fddbf9da.png#align=left&display=inline&height=279&name=image.png&originHeight=558&originWidth=672&size=50344&status=done&width=336">
#### 没有维度轴的图表
只有行配置的图形推荐用表格,但柱状图、折线图也可以支持这种情况,只要把横轴忽略即可:
<img width=300 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566706245345-61b365db-793a-4d15-9677-b4e332381bda.png#align=left&display=inline&height=312&name=image.png&originHeight=912&originWidth=878&size=53419&status=done&width=300">
从样式上来看没有横轴,其实这种情况是把所有维度的横轴都聚合后的表现。
### 连续与离散值
我们分别看看连续与离散作用于维度和度量时的区别。
#### 作用于度量
图表要能适配对连续或离散值的处理。比如对销量来说,如果切换为离散值,则当成字符串展示:
<img width=632 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566662092893-32545363-82da-498a-868f-18a59bb41c24.png#align=left&display=inline&height=141&name=image.png&originHeight=472&originWidth=2122&size=63869&status=done&width=632">
如果将销量切换为连续值,则单元格就要使用线条长度代表值的大小,**即连续性的值要能够产生 “对比感”:**
<img width=632 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566662072534-23177d9d-f32c-4aa2-bea4-8fef0ab6e3c2.png#align=left&display=inline&height=161&name=image.png&originHeight=534&originWidth=2106&size=58097&status=done&width=635">
上图组件是表格,本身适合展示离散值,但可以看到对连续值展示做了适配。对于适合展示连续值的图形,则无法做离散适配:
<img width=200 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566703397222-9da012d8-9d13-4378-9d5a-50903f3ba777.png#align=left&display=inline&height=253&name=image.png&originHeight=976&originWidth=772&size=48952&status=done&width=200">
比如这个柱状图,如果将销量切换为离散,则会自动切换到表格,因为对于双离散值用柱折面饼展示是无意义的。
#### 作用于维度
如上图所示,就是维度使用了离散字段的例子,由于维度是离散的,因此使用柱状图展示,因为柱子间也是隔离的。
**对于连续型字段作用于维度,默认适合散点图,因为散点图的行与列都是度量,适合作为默认推荐:**
<img width=283 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566703779092-ce78066e-75ec-4b89-9fc1-f44c135e5b5d.png#align=left&display=inline&height=312&name=image.png&originHeight=1154&originWidth=1048&size=66333&status=done&width=283">
但能用散点图的就也能用线图, **当维度是连续日期字段时,适合用折线图而不是散点图。**因为日期虽然连续,但 **本身不适合做比较** ,因此作为一种连续型维度展示比较合适;而散点图两个轴都适合连续型度量,因此不适合方日期这种连续型维度字段。
<img width=406 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566703754082-6ec2f472-ea42-4003-aab3-c07f47b8a340.png#align=left&display=inline&height=289&name=image.png&originHeight=1166&originWidth=1636&size=96183&status=done&width=406">
当然也具备将折线图随时切换为散点图的能力,但这种图形没有什么业务价值:
<img width=406 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566704003468-7ba3e545-9abf-470f-841d-44dd42e1f754.png#align=left&display=inline&height=292&name=image.png&originHeight=1178&originWidth=1654&size=97442&status=done&width=410">
因此我们对折线图进行标记:行适合连续型维度字段,对散点图进行标记:行列都适合连续型度量字段,就可以根据配置 **实现推荐图表的功能**
### 标记
除了饼图支持 “角度”、线图支持 “路径” 这些特殊标记外,所有图表都支持下面五种通用标记:“工具提示”、“大小”、“文本”、“颜色”、“详细信息”。
**工具提示** 比较简单,所有图表都支持鼠标 Hover 后弹出 Tooltip 即可,并且这个 Tooltip 允许自定义和拓展工具提示字段。
**大小** 则只有折、柱、散三种图支持,因为这三种图分别有可以描述的大小的线条粗细、柱子宽度、圆圈半径。
**文本** 对应柱折面饼的 Label、对应表格,矩形树状图,地图的 **单元格内容。**
**颜色、详细信息** 则比较特殊,下面详细说明:
**拖拽已有字段到详细信息 - 没有任何效果:** 
<img width=476 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566705683330-21e04dfb-41f7-45dc-92b8-69dab98bf009.png#align=left&display=inline&height=249&name=image.png&originHeight=740&originWidth=1416&size=73008&status=done&width=476">
因为本身就在看这个字段的详细信息,因此没有效果。
**但如果拖拽已有字段到颜色,则可以根据数值大小或分类进行按颜色区分:**
<img width=479 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566705734424-b2bc735d-35fa-4cf9-8b67-e24e3dccd0f5.png#align=left&display=inline&height=250&name=image.png&originHeight=736&originWidth=1412&size=76772&status=done&width=479">
等于开启了图表筛选功能,当颜色筛选条件字段是连续型时,出现筛选滑块,**是离散型时,出现图例:**
<img wdith=479 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566705795546-6a5e79b7-b32a-4c37-90bc-43926a3a6283.png#align=left&display=inline&height=245&name=image.png&originHeight=720&originWidth=1408&size=80186&status=done&width=480">
**如果拖拽字段不存在于行和列上,对于度量字段,会根据值进行颜色排序(度量拖拽到详细信息依然没有效果):**
<img width=479 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566705874311-ebc4ec5d-021f-4601-9f8d-d5becfa9b1ce.png#align=left&display=inline&height=244&name=image.png&originHeight=720&originWidth=1422&size=79431&status=done&width=482">
如上图所示,我们可以从长度看利润,从颜色深度看销量。
**如果拖拽字段不存在于行和列上,且是维度字段,则会先进行维度拆分,之后如果选择的是 “颜色” 标记区域,还会对同一组的拆分标记颜色区分。**
**由于标记区域对维度的拆分是不分行于列的,因此每个图表会根据自身情况进行合适的拆分。**
比如条形图如果按某个新维度拆分,则会采取 “堆积柱状图” 的策略:
<img width=471 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566706116218-919d2ce7-3fb2-4a32-8624-a3f6cb3122fb.png#align=left&display=inline&height=327&name=image.png&originHeight=986&originWidth=1422&size=117354&status=done&width=471">
如果是折线图,则会采取 “多条线” 的策略:
<img width=471 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566706404581-01866c81-0553-4245-9b56-176bc5708bfe.png#align=left&display=inline&height=319&name=image.png&originHeight=960&originWidth=1416&size=127313&status=done&width=471">
如果是散点图,只要将拆分后多出来的点打散出来即可。由于散点图的维度拆分不像折线图和柱状图可以分段,因此如果不采用按颜色打散,是无法分辨分组的:
<img width=471 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566706443331-c817102a-5134-493f-bddf-04fc73f9548f.png#align=left&display=inline&height=319&name=image.png&originHeight=958&originWidth=1414&size=107140&status=done&width=471">
之所以说探索式分析的复杂度很高,是因为其可能性公式为:
**字段 x 离散连续 x 行列 x 行列下钻 x 标记种类 x 筛选 x 图表**
这种组合的笛卡尔积几乎是无穷无尽的。
### 轴交互
图表一些特定功能是隐藏在轴交互里的。拿折线图来说,一共有 5 个拖拽交互位置,如下图所示:
<img width=439 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566706982072-996f5f6e-b922-40ec-8066-761857fb2e30.png#align=left&display=inline&height=354&name=image.png&originHeight=1324&originWidth=1642&size=100488&status=done&width=439">
一般这些区域是用来拖拽度量字段的,所以如果拖拽了维度字段过来,最终会被归类到行列或标记上。
#### 拖拽维度
**维度拖拽到底部 1 区域等于替换列字段** :
<img width=195 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707169109-6e38483a-123a-4064-81ff-fd0ed82010ed.png#align=left&display=inline&height=344&name=image.png&originHeight=1010&originWidth=572&size=44840&status=done&width=195">
**维度拖拽到图表中 4 区域等于拖到了颜色标记** :
<img width=387 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707229005-d6ed84f8-8639-4dc6-96b0-ae2bebe70dc0.png#align=left&display=inline&height=331&name=image.png&originHeight=974&originWidth=1138&size=111933&status=done&width=387">
**维度拖拽到左侧 3 区域等于对行进行下钻:** 
<img width=406 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707279972-a22eb90b-2ebc-4727-911c-10c1d329a06c.png#align=left&display=inline&height=285&name=image.png&originHeight=1012&originWidth=1444&size=106415&status=done&width=406">
同理拖拽到最上面区域等于对列进行下钻。
#### 拖拽度量
让我们看看拖拽度量时的情况。度量能拖拽的范围更多。**比如拖拽到右轴 5 区域,则形成了双轴图:**
<img width=447 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707391460-e528c513-bc22-4b02-9717-e9339b83d419.png#align=left&display=inline&height=360&name=image.png&originHeight=994&originWidth=1234&size=120248&status=done&width=447">
**拖拽到左侧 2 区域则表示在图中额外增加一个轴:**
<img width=571 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707459746-8af4aa6e-acf4-4600-8fce-32c5fbe3400c.png#align=left&display=inline&height=333&name=image.png&originHeight=1020&originWidth=1748&size=126450&status=done&width=571">
要注意的是,上图的行显示 “度量值”,这是个特殊的字段,并通过筛选器筛选出拖拽的两个字段 Profit 和 Sales。除了拖拽以外,还可以通过将左侧 “度量值” 字段直接拖入行实现:
<img width=579 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707593546-3670ea98-6e42-4fd2-8438-402c42c318d6.png#align=left&display=inline&height=275&name=image.png&originHeight=1042&originWidth=2190&size=243455&status=done&width=579">
如上图所示,将度量值放到行,并按度量名称进行颜色标记,就得到了拖拽度量到左侧 2 区域的效果。 **这也说明了所有图表交互最终都是通过映射到配置完成,所有能拖拽的操作都可以通过配置配出来** 。
对表格来说,能拖拽的区域是行、列、单元格:
<img width=521 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707733747-55ea9f8c-d2c2-4766-b7b1-57d7abb351d2.png#align=left&display=inline&height=132&name=image.png&originHeight=358&originWidth=1416&size=29996&status=done&width=521">
拖拽到行或列于拖拽到字段配置区域的行或列没有区别,拖拽到单元格等于拖拽到文本标记区域。通过图表于配置区域结合的方式,即便不完全理解配置的人也可以通过将字段拖拽到图表上得到直观的操作感。
### 点击、圈选交互
所有图表都支持点击、圈选的方式选中 “点”。对表格来说,点就是单元格:
<img width=505 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707894327-f5b88abb-d84e-4839-9c48-5fbbc9ccd929.png#align=left&display=inline&height=128&name=image.png&originHeight=356&originWidth=1408&size=42932&status=done&width=505">
对柱状图来说,点就是柱子:
<img width=307 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707923552-680d890f-bc57-4692-b29e-ae787522bfb6.png#align=left&display=inline&height=268&name=image.png&originHeight=972&originWidth=1114&size=85024&status=done&width=307">
对折线图来说,点就是节点:
<img width=427 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707968058-fd8a598a-48fe-45c8-99f9-94c09f686435.png#align=left&display=inline&height=189&name=image.png&originHeight=640&originWidth=1428&size=59096&status=done&width=421">
对饼图来说,点就是扇叶:
<img width=374 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566707997384-1f1e4755-0863-48de-8abc-f56ae9feaad1.png#align=left&display=inline&height=169&name=image.png&originHeight=338&originWidth=748&size=26955&status=done&width=374">
所有的点被选中后都有基本高亮功能,最重要的是能对选中的点进行保留、排除、局部排序等等。
**比如我们可以对上图饼图选中的几个扇形区域进行从小到大排序:**
<img width=316 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566708115948-0dc7a73e-97bd-4099-b8bc-12dc16f56501.png#align=left&display=inline&height=243&name=image.png&originHeight=568&originWidth=740&size=48022&status=done&width=316">
我们也可以排除某些点,这个在配置章节有提到过,这个操作最终将转化为新增筛选条件:
<img width=283 src="https://cdn.nlark.com/yuque/0/2019/png/201572/1566708205782-b74a2d1f-8141-4a05-bea4-8824240a5e67.png#align=left&display=inline&height=280&name=image.png&originHeight=658&originWidth=666&size=52520&status=done&width=283">
最后,选中状态在单图表中看似只有高亮效果,但是在多图表联动时,高亮的选中区域会组成一个临时的筛选条件,作用于所有相同数据集的图表,并对这些图表的筛选结果做高亮处理。
# 3. 总结
理解了探索模型对数据、配置、图表的理解,就能学会探索式思维分析数据,对制作探索式 BI 也有借鉴意义。
> 讨论地址是:[精读《Tableau 探索式模型》 · Issue #199 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/199)
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
> 关注 **前端精读微信公众号**
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)
@@ -0,0 +1,111 @@
> 作者:五灵
本周工作中遇到类似颜色主题的问题,在查资料的时候,看到这个视频,觉得讲得很清楚,而且趣味性丰富,所以想拿出来讲讲这个很有意思的主题。
视频链接: [CSSconf EU 2018 | Dag-Inge Aas & Ida Aalen: Generating Colors with JS and CSS Custom Properties](https://www.youtube.com/watch?v=zi6L0ZqrKfA)
## 1. 精读
### CSS 变量
CSS 变量及 CSS Variables(Custom Properties),目前几乎都已经被主流浏览器所支持,但是估计还有一部分读者不熟悉这个功能,简单列举一下使用方法:
```css
:root {
--bg-color: brown; // 定义颜色变量
}
.btn {
// 直接使用颜色预定义的颜色变量
background-color: var(--bg-color);
}
```
### Web 内容无障碍指南的对比度
Web 内容无障碍指南的对比度指的是 W3C 组织发布的 [《Web Content Accessibility Guidelines (WCAG)》](https://www.w3.org/TR/WCAG/#glossary),这个指南中涵盖了让 Web 内容更易于访问的各种建议,其中针对网页的颜色对比度发布了规范。
在 Chrome 中对于颜色编辑的时候,打开颜色选择器也会看到当前颜色的对比度值(Contrast ratio)。
![](https://img.alicdn.com/tfs/TB1VQUveRv0gK0jSZKbXXbK2FXa-260-388.png)
网页颜色的对比度值在 1:1 到 21:1 之间,文本和图像文本的的对比度最小值为 4.5:1,也就是说低于这个值得对比度都不符合标准。 我们看一下列举的几种颜色对比度,对比度越高,也越有利于阅读。对比度越低,对于一些存在视力障碍或色觉缺陷的用户,可能就无法阅读。
![](https://img.alicdn.com/tfs/TB1G1MveUz1gK0jSZLeXXb9kVXa-1000-410.png)
### 演讲中的颜色解决方案
演讲在最开始首先讲了挪威的一个法律,不符合 Web 内容无障碍指南的站点在挪威是非法的,所以挪威的 Web 开发者非常注重站点的内容无障碍。
首先讲了使用 css 变量的方式,支持各种颜色主题的切换。 利用 js 去设置颜色变量,支持主题的颜色切换。
但是紧接着就提出了问题,如果用户可以随意切换颜色主题背景色,那一些按钮的文字可读性如何去保障呢?如果用户选择了与按钮颜色想接近的背景色,我们又该怎么处理了,紧接着这个演讲给出了根据明度决定按钮文字颜色是黑色还是白色的方案。
- 根据明度决定是黑色还是白色
具体代码如下,大致原理是把彩色转为灰度的颜色,有一个著名的心理学公式:`Gray = R*0.299 + G*0.587 + B*0.114`,然后在根据颜色灰度决定使用黑色的主题还是白色的主题。
```javascript
if (red*0.299 + green*0.587 + blue*0.114) > 186 use #000000 else use #ffffff
```
![](https://img.alicdn.com/tfs/TB1zfcveUz1gK0jSZLeXXb9kVXa-1535-584.png)
可读性的问题解决了,但是紧接着又遇到了一个问题,如果用户选取的颜色很浅呢,与背景颜色的对比度小于 4.5,该怎么处理呢。
![](https://img.alicdn.com/tfs/TB14RsveQP2gK0jSZPxXXacQpXa-1254-402.png)
- 寻找对比度更强的颜色,增强可读性
演讲中给出的解决方法是不断的加深当前用户选择的颜色,循环获取到对比度最高的同色系颜色。代码如下:
![](https://img.alicdn.com/tfs/TB19J7veUH1gK0jSZSyXXXtlpXa-1457-663.png)
获取了一个更深的颜色后,通过给按钮加一个外边框的方式,优化整体的可读性。
![](https://img.alicdn.com/tfs/TB1aRQzeUY1gK0jSZFCXXcwqXXa-1802-571.png)
文章最后还介绍了,通过给定一个主题色,获取第二第三主题色的方式,通过将颜色放到 HSL 的颜色轮上,转动 hue 的值 60 度,得到一个新的第二主题色。不过演讲者也没有说清楚为什么要这么做,只是说了这么做是出于经验,觉得这样能够得到一个恰当的主题色盘。
### 衍生的纯 css 解决方案
演讲中提供颜色变更的解决方案基本都是基于 JS 计算的,后来有人在 [css-tricks](https://css-tricks.com/switch-font-color-for-different-backgrounds-with-css/) 抛出一篇文章说,这个功能基于 css 就可以完全实现,其实关于颜色的原理都是一致的,只是觉得这个实现更加 magic,但是功能都能够完全满足。比如这篇文章中,关于根据明度决定按钮文字是黑色还是白色的代码如下:
```css
:root {
--light: 80;
/* 文字颜色变化的临界值 */
--threshold: 60;
}
.btn {
/* 会被解析成黑色或者白色 */
--switch: calc((var(--light) - var(--threshold)) * -100%);
color: hsl(0, 0%, var(--switch));
}
```
### 可视化图表对于颜色的应用
在可视化图表当中,对于颜色的应用要比 Web 要谨慎的多。我们在做 Web 开发的时候,也不妨来看一下可视化图表当中对于颜色应用的一些规范。在可视化图表中,选择的颜色不可以过于随意,每次颜色的变更都是图表信息的改变,都为图表增加了新的数据,图表的每一种颜色也是要表达的信息。列举一些图表中的颜色使用规范,比如:
1. 不建议使用多种颜色表达同种数据
2. 在多条行图表中,不要使用不同的颜色或颜色轮中对立面的颜色。颜色对比过强会使读者无法专心于数据。
3. 一般而言,应避免颜色的主体性表现,避免使用具有特殊意义的颜色。比如使用红色和绿色表示销售额的变化。
当然对于可视化图表来说,并不是遵循了一些色彩使用的准则,就可以得到一个优雅呈现的可视化图表。注重图表呈现的最重要的视觉元素,在视觉信息角度减少用户,减少用户视觉疲劳也很重要。
## 3. 相关链接
CSS 前景背景自动配色技术简介: [https://www.zhangxinxu.com/wordpress/2018/11/css-background-color-font-auto-match/](https://www.zhangxinxu.com/wordpress/2018/11/css-background-color-font-auto-match/)<br />
纯 css 解决方案:[https://css-tricks.com/switch-font-color-for-different-backgrounds-with-css/](https://css-tricks.com/switch-font-color-for-different-backgrounds-with-css/)<br />
获取颜色的 Demo [https://confrere.com/a11y/test/](https://confrere.com/a11y/test/)<br />
颜色色盘推荐的文章:[https://blog.graphiq.com/finding-the-right-color-palettes-for-data-visualizations-fcd4e707a283](https://blog.graphiq.com/finding-the-right-color-palettes-for-data-visualizations-fcd4e707a283)
> 讨论地址是:[精读《使用 css 变量生成颜色主题》 · Issue #203 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/203)
**如果你想参与讨论,请 [点击这里](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)
+79
View File
@@ -0,0 +1,79 @@
> 作者:五灵
## 简介
其实关于前端深水区的讨论,已经有了很多,也有了很多相关的文章。我也想借这篇关于深水区的讨论文章,讲一下自己对于深水区的理解。
原文链接:[技术路线:前端开发已进入深水区](https://www.yuque.com/sxc/front/kvokg4)
本期精读,[@camsong](https://github.com/camsong)、[@arcthur](https://github.com/arcthur)、[@ascoders](https://github.com/ascoders) 都有贡献观点。
## 概述
原文对于深水区的想法,讲的很清楚,还是建议读者去读一下原文。
对比 2010 年,整个前端生态已经翻新了好几遍,直到近几年的 Node BFF、IDE Cloud,抑或是客户端 AI,还是 Serverless 的建设,,前端想要深度参与的话,单纯依靠原来的 HTML/CSS/JS 三件套技能也远远不够了。再抛开技术,整个互联网创业生态也重构了好几遍。无论是技术层面还是意识层面,如今的前端开发已经进入深水区。
- 深水区需要哪些技能
![image.png](https://img.alicdn.com/tfs/TB1oovQe8r0gK0jSZFnXXbRRXXa-1832-1032.png)
深水区需要是四个核心能力,分别是:技术、产品、业务和管理能力。
- 面对深水压力不需紧张
其实何止前端开发,整个技术行业都已步入深水区,只是前端工程师的感知来的晚一些而已。只要把眼光投向深水区,问题就会一个接一个的浮上来,当越来越多问题浮起来的时候,就是你慢慢沉向深水区的时候,这时候不需要太过紧张。
## 精读
深水区的理解首先需要达成一致,并不只是一个维度的加深,而是全方位多方面的困难同时加击,压强升高、光线减少、温度剧变等等。
对应到文中总结的解法就是需要『技术创新、流程优化、团队合作、影响大盘、驱动业务、商业决策和团队管理』。但你展开想一下,把这个角色换成后端、无线端、甚至是 UED,是不是也能完美匹配。所以这些能力应该是技术人员发展到一定程度面临的普遍问题而不仅仅是前端。
但这些能力是否有个更好的概括?当然有,就是明确一个方向并带领一群人完成目标并实线商业价值。这其实就是商业或者说业务的整个运作过程。
这其实也在抛一个命题,前端发展到一定程度就一定要转业务吗?
是也不是。当然要转,但并不是全转。全转业务你过去的积累有什么用?不转业务单纯前端能发挥的影响力就会受限。所以答案是利用前端技术优势同时补充业务能力推动商业流程。
所以此文并不是严格上讲前端技术的深水区,或者作者肯定认为他能接触的前端技术已经到瓶颈,且没有想到突破口。
怎么去定义深水区,@流形 认为是需要建立技术壁垒或学术壁垒。当我们看待一向技术,如果在投入一到两年就可以对齐,那么显然技术本身的深度是可观的,如果是十年才能对齐,这时候除了会影响经济或政治外,不会有人会去重做,只能使用。用另一个类似的概念反摩尔定律来对应深水区说,每隔两年,技术不能显著带来效能的成倍提升。
### 深水区值得关注的方向
#### 业务领导力
也就是原文提到的 “技术创新、流程优化、团队合作、影响大盘、驱动业务、商业决策、团队管理” 等能力,一个拥有领导力的人发挥的价值远超自身孤立的价值。
#### 业务价值
发挥业务价值是技术人的最终目标,比如数据库技术想发挥业务价值,就要做到高效、稳定,价值越大往往技术难度就越大。
值得庆幸的是,前端的业务价值与技术难度往往不成正比,有时候将客户的业务场景固化成一套模版,整合起来赋能给更多客户,这等于将商业模型作为能力赋予了其他客户,但本身并没有用到一些高级技术。前端能做的不仅是内部提效和外部体验,因为前端是人机交互的入口,才有机会将业务思考打包到代码中,直接透出给客户。
#### 端技术的发展
1. 数字孪生。那么在端上的仿真能力需要大幅提高,那么结合模型自动生成,不同物体的建模能力等都是很大挑战
2. 虚拟实现。这点上就不赘述,从 FB 重点发展 Oculus,微软发展 HoloLens 可以看到这个趋势,从互动的未来来看,这不是终局,但是最适合今天要突破的技术。
3. 可视分析。数据在人类面前还是过分难懂,结合数据的分析系统在各行各业正在渗透,端上结合可视化的能力就显得非常重要。
4. 更多的,像边缘计算,前端安全等领域都是非常深入的领域。
这些问题,已经不是一年就能完全突破的,需要 3-5 年,甚至 10 年时间。
#### 前端深入体系
1. 但对于我所处的大数据环境来说,确实接触了前端技术深水区。来源于端计算能力 + 网络基建 + 大数据的爆炸式增长。
编辑器:复杂的开发离不开代码,前端们一直孜孜不倦的把 IDE 引入 web,VS Code 做了很成功的尝试但还是需要一层壳套着。且对于大数据处理这样的领域,需要定制的能力远超过通用的 Manaco editor 等能提供。
2. 表格类数据处理能力:比尔盖茨最引以为豪的微软软件是 Excel。你永远不知道 Excel 有多少种酷的用法来解决用户问题。能否把 Excel 引入到 web?同时对数百万条数据做交叉分析,这对性能和架构都有很大的挑战。
3. 可视化数据展现:大数据的一个典型特征就是价值稀疏性,如何把蕴含的价值展现出来,需要了解图形学、统计学、交互色彩等各种能力。大学老师教的内容终于能派生用场了。
## 总结
在局部领域前端已经有可能深入,当然前端技能上说这些也不能用 HTML, CSS, JS 来解决,需要开发者有深入学科的背景。但今天前端面向还是产品功能的需要,在端上更强调的还是产品功能为主。我们做一款复杂产品,更多还会在工程上纠结。如果没在功能的深入性上思考更多,以对应真正技术发展,那么深水区还远。
正如前面所说,深水区会压强升高、光线减少、温度剧变,需要自己发光发热和更多的坚持。
跨过深水区,让其他人处在浅水区就能做事,这或许就是你走出深水区的标志。就像 Alan Perlis 说的一句话『简单不先于复杂,而是在复杂之后』,也许未来看来你今天挣扎的深水区只是个小泥坑。
> 讨论地址是:[精读《前端深水区》 · Issue #193 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/193)
**如果你想参与讨论,请 [点击这里](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)
+290
View File
@@ -0,0 +1,290 @@
## 简介
React 16.8 于 2019.2 正式发布,这是一个能提升代码质量和开发效率的特性,笔者就抛砖引玉先列出一些实践点,希望得到大家进一步讨论。
然而需要理解的是,没有一个完美的最佳实践规范,对一个高效团队来说,稳定的规范比合理的规范更重要,因此这套方案只是最佳实践之一。
## 精读
### 环境要求
- 拥有较为稳定且理解函数式编程的前端团队。
- 开启 ESLint 插件:[eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)。
### 组件定义
Function Component 采用 `const` + 箭头函数方式定义:
```tsx
const App: React.FC<{ title: string }> = ({ title }) => {
return React.useMemo(() => <div>{title}</div>, [title]);
};
App.defaultProps = {
title: 'Function Component'
}
```
上面的例子包含了:
1. 用 `React.FC` 申明 Function Component 组件类型与定义 Props 参数类型。
2. 用 `React.useMemo`  优化渲染性能。
3. 用 `App.defaultProps` 定义 Props 的默认值。
#### FAQ
> 为什么不用 React.memo?
推荐使用 `React.useMemo` 而不是 `React.memo`,因为在组件通信时存在 `React.useContext` 的用法,这种用法会使所有用到的组件重渲染,只有 `React.useMemo` 能处理这种场景的按需渲染。
> 没有性能问题的组件也要使用 useMemo 吗?
要,考虑未来维护这个组件的时候,随时可能会通过 `useContext` 等注入一些数据,这时候谁会想起来添加 `useMemo` 呢?
> 为什么不用解构方式代替 defaultProps?
虽然解构方式书写 `defaultProps` 更优雅,但存在一个硬伤:对于对象类型每次 Rerender 时引用都会变化,这会带来性能问题,因此不要这么做。
### 局部状态
局部状态有三种,根据常用程度依次排列: `useState` `useRef` `useReducer` 。
#### useState
```tsx
const [hide, setHide] = React.useState(false);
const [name, setName] = React.useState('BI');
```
状态函数名要表意,尽量聚集在一起申明,方便查阅。
#### useRef
```tsx
const dom = React.useRef(null);
```
`useRef` 尽量少用,大量 Mutable 的数据会影响代码的可维护性。
但对于不需重复初始化的对象推荐使用 `useRef` 存储,比如 `new G2()` 。
#### useReducer
局部状态不推荐使用 `useReducer` ,会导致函数内部状态过于复杂,难以阅读。 `useReducer` 建议在多组件间通信时,结合 `useContext` 一起使用。
#### FAQ
> 可以在函数内直接申明普通常量或普通函数吗?
不可以,Function Component 每次渲染都会重新执行,常量推荐放到函数外层避免性能问题,函数推荐使用 `useCallback` 申明。
### 函数
所有 Function Component 内函数必须用 `React.useCallback` 包裹,以保证准确性与性能。
```tsx
const [hide, setHide] = React.useState(false);
const handleClick = React.useCallback(() => {
setHide(isHide => !isHide)
}, [])
```
`useCallback` 第二个参数必须写,[eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) 插件会自动填写依赖项。
### 发请求
发请求分为操作型发请求与渲染型发请求。
#### 操作型发请求
操作型发请求,作为回调函数:
```tsx
return React.useMemo(() => {
return (
<div onClick={requestService.addList} />
)
}, [requestService.addList])
```
#### 渲染型发请求
渲染型发请求在 `useAsync` 中进行,比如刷新列表页,获取基础信息,或者进行搜索, **都可以抽象为依赖了某些变量,当这些变量变化时要重新取数**
```tsx
const { loading, error, value } = useAsync(async () => {
return requestService.freshList(id);
}, [requestService.freshList, id]);
```
### 组件间通信
简单的组件间通信使用透传 Props 变量的方式,而频繁组件间通信使用 `React.useContext` 。
以一个复杂大组件为例,如果组件内部拆分了很多模块, **但需要共享很多内部状态** ,最佳实践如下:
#### 定义组件内共享状态 - store.ts
```tsx
export const StoreContext = React.createContext<{
state: State;
dispatch: React.Dispatch<Action>;
}>(null)
export interface State {};
export interface Action { type: 'xxx' } | { type: 'yyy' };
export const initState: State = {};
export const reducer: React.Reducer<State, Action> = (state, action) => {
switch (action.type) {
default:
return state;
}
};
```
#### 根组件注入共享状态 - main.ts
```tsx
import { StoreContext, reducer, initState } from './store'
const AppProvider: React.FC = props => {
const [state, dispatch] = React.useReducer(reducer, initState);
return React.useMemo(() => (
<StoreContext.Provider value={{ state, dispatch }}>
<App />
</StoreContext.Provider>
), [state, dispatch])
};
```
#### 任意子组件访问/修改共享状态 - child.ts
```tsx
import { StoreContext } from './store'
const app: React.FC = () => {
const { state, dispatch } = React.useContext(StoreContext);
return React.useMemo(() => (
<div>{state.name}</div>
), [state.name])
};
```
如上解决了 **多个联系紧密组件模块间便捷共享状态的问题** ,但有时也会遇到需要共享根组件 Props 的问题,**这种不可修改的状态不适合一并塞到 `StoreContext` 里**,我们新建一个 `PropsContext` 注入根组件的 Props
```tsx
const PropsContext = React.createContext<Props>(null)
const AppProvider: React.FC<Props> = props => {
return React.useMemo(() => (
<PropsContext.Provider value={props}>
<App />
</PropsContext.Provider>
), [props])
};
```
#### 结合项目数据流
参考 [react-redux hooks](https://github.com/reduxjs/react-redux/blob/master/docs/api/hooks.md)。
### debounce 优化
比如当输入框频繁输入时,为了保证页面流畅,我们会选择在 `onChange` 时进行 `debounce` 。然而在 Function Component 领域中,我们有更优雅的方式实现。
> 其实在 Input 组件 `onChange`  使用 `debounce` 有一个问题,就是当 Input 组件 **受控** 时, `debounce` 的值不能及时回填,导致甚至无法输入的问题。
我们站在 Function Component 思维模式下思考这个问题:
1. React [scheduling](https://github.com/dt-fe/weekly/blob/v2/099.%E7%B2%BE%E8%AF%BB%E3%80%8AScheduling%20in%20React%E3%80%8B.md) 通过智能调度系统优化渲染优先级,我们其实不用担心频繁变更状态会导致性能问题。
2. 如果联动一个文本还觉得慢吗? `onChange` 本不慢,大部分使用值的组件也不慢,没有必要从 `onChange` 源头开始就 `debounce` 。
3. 找到渲染性能最慢的组件(比如 iframe 组件),**对一些频繁导致其渲染的入参进行 `useDebounce`** 。
下面是一个性能很差的组件,引用了变化频繁的 `text` (这个 `text` 可能是 `onChange` 触发改变的),我们利用 `useDebounce` 将其变更的频率慢下来即可:
```typescript
const App: React.FC = ({ text }) => {
// 无论 text 变化多快,textDebounce 最多 1 秒修改一次
const textDebounce = useDebounce(text, 1000)
return useMemo(() => {
// 使用 textDebounce,但渲染速度很慢的一堆代码
}, [textDebounce])
};
```
使用 `textDebounce` 替代 `text` 可以将渲染频率控制在我们指定的范围内。
### useEffect 注意事项
事实上,`useEffect` 是最为怪异的 Hook,也是最难使用的 Hook。比如下面这段代码:
```tsx
useEffect(() => {
props.onChange(props.id)
}, [props.onChange, props.id])
```
如果 `id` 变化,则调用 `onChange`。但如果上层代码并没有对 `onChange` 进行合理的封装,导致每次刷新引用都会变动,则会产生严重后果。我们假设父级代码是这么写的:
```tsx
class App {
render() {
return <Child id={this.state.id} onChange={id => this.setState({ id })} />
}
}
```
这样会导致死循环。虽然看上去 `<App>` 只是将更新 id 的时机交给了子元素 `<Child>`,但由于 `onChange` 函数在每次渲染时都会重新生成,因此引用总是在变化,就会出现一个无限死循环:
`onChange` -> `useEffect` 依赖更新 -> `props.onChange` -> 父级重渲染 -> 新 `onChange`...
想要阻止这个循环的发生,只要改为 `onChange={this.handleChange}` 即可,**`useEffect` 对外部依赖苛刻的要求,只有在整体项目都注意保持正确的引用时才能优雅生效。**
然而被调用处代码怎么写并不受我们控制,这就导致了不规范的父元素可能导致 React Hooks 产生死循环。
因此在使用 `useEffect` 时要注意调试上下文,注意父级传递的参数引用是否正确,如果引用传递不正确,有两种做法:
1. 使用 [useDeepCompareEffect](https://github.com/streamich/react-use/blob/master/docs/useDeepCompareEffect.md) 对依赖进行深比较。
2. 使用 `useCurrentValue` 对引用总是变化的 props 进行包装:
```tsx
function useCurrentValue<T>(value: T): React.RefObject<T> {
const ref = React.useRef(null);
ref.current = value;
return ref;
}
const App: React.FC = ({ onChange }) => {
const onChangeCurrent = useCurrentValue(onChange)
};
```
`onChangeCurrent` 的引用保持不变,但每次都会指向最新的 `props.onChange`,从而可以规避这个问题。
## 总结
如果还有补充,欢迎在文末讨论。
如需了解 Function Component 或 Hooks 基础用法,可以参考往期精读:
- [精读《React Hooks》](https://github.com/dt-fe/weekly/blob/v2/079.%E7%B2%BE%E8%AF%BB%E3%80%8AReact%20Hooks%E3%80%8B.md)
- [精读《怎么用 React Hooks 造轮子》](https://github.com/dt-fe/weekly/blob/v2/080.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%80%8E%E4%B9%88%E7%94%A8%20React%20Hooks%20%E9%80%A0%E8%BD%AE%E5%AD%90%E3%80%8B.md)
- [精读《useEffect 完全指南》](https://github.com/dt-fe/weekly/blob/v2/096.%E7%B2%BE%E8%AF%BB%E3%80%8AuseEffect%20%E5%AE%8C%E5%85%A8%E6%8C%87%E5%8D%97%E3%80%8B.md)
- [精读《Function Component 入门》](https://github.com/dt-fe/weekly/blob/v2/104.精读《Function%20Component%20入门》.md)
> 讨论地址是:[精读《React Hooks 最佳实践》 · Issue #202 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/202)
**如果你想参与讨论,请 [点击这里](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)
+175
View File
@@ -0,0 +1,175 @@
## 简介
商业智能(Business Intelligence)简称 BI,即通过数据挖掘与分析找到商业洞察,助力商业成功。
一个完整的 BI 链路包含数据采集、数据清洗、数据挖掘、数据展现,其本质是对数据进行多维分析。前端的主要工作在数据展现环节,由于展示方式繁多、分析模型复杂且数据量大,前端环节的复杂度很高。
在 BI 做前端非常有挑战,开发者需要充分理解数据概念,而本身复杂度较高的可视化建站也只是 BI 的基础能力,想要建设 BI 的上层能力,比如探索式分析和数据洞察,都需要在前后端引入更复杂的计算模型。
本文作为一个引子,简单介绍笔者做 BI 的经验,后面如果有机会再写一个系列文章对细节进行阐述。
## 精读
国内目前处于 BI 1.0 阶段,也就是报表阶段,因此笔者将阐述这个阶段 BI 的核心开发概念。
> BI 2.0 探索式分析阶段是国内数据分析最前沿领域,这部分等开发完成后再分享。
BI 1.0 阶段的核心概念包括 **数据集、渲染引擎、数据模型、可视化** 这四个技术模块。
### 数据集
数据集即数据的集合,在 BI 领域更多指一种标准化的数据结构。
任何数据都可以封装成数据集,比如 txt 文本、excel、mysql 数据库等等。
数据集的基本形态是二维表格,列头表示字段,每一行就是一份数据,数据展示就是通过对这些数据字段进行多维度分析。
#### 数据集导入
一般来说数据集导入有两种方式,分别是本地文件上传与数据库链接。本地文件上传又分为多种文件类型处理,比如对 excel 的解析,可能还包括数据清洗;数据库链接分析可视化导入与 SQL 输入。
可视化导入需要提前对数据库进行结构分析,绘制出表结构与字段结构,不用理解 SQL 也可以进行可视化操作。
SQL 输入可以利用 [monaco-editor](https://github.com/microsoft/monaco-editor) 等 web 代码编辑器作为输入框,最好能结合智能提示提高 sql 编写效率。sql 智能提示可以参考往期精读 [精读《手写 SQL 编译器 - 智能提示》](https://github.com/dt-fe/weekly/blob/v2/085.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%89%8B%E5%86%99%20SQL%20%E7%BC%96%E8%AF%91%E5%99%A8%20-%20%E6%99%BA%E8%83%BD%E6%8F%90%E7%A4%BA%E3%80%8B.md)。
#### 数据集建模
数据集建模一般包含 **维度度量建模、字段配置、层系建模**
维度度量建模需要智能分析出字段属于维度还是度量,一般会结合字段实际的值或者字段名来智能判断字段类型,如果数据库信息中已存储了字段类型,就可以 100% 准确归类。
字段配置即对字段进行增删或修改,还可以新增聚合字段或对比字段。
聚合字段是指将一个字段表达式封装为一个新字段,这里也会用到一个简单的 sql 编辑器,只需要支持四则运算、字段提示、以及一些基本函数的组合即可。
对比字段是指新增的字段是基于已有字段在某个时间周期内的对比,比如对 UV 字段的年同比就可以封装为一个对比字段。对比字段在前端技术上没有什么难度,仅需理解概念即可。
### 渲染引擎
渲染引擎包括了对报表进行编辑与渲染的引擎,理论上可以合二为一。
渲染引擎的重要模块包括:画布拖拽、组件编辑、事件中心。
画布拖拽其实包含了组件自定义开发流程,到 CDN 发布、CDN 加载、组件拖拽、画布排版等一系列技术点,每个点展开都有写不完的细节,但好在这套功能属于通用建站基础功能点,本文就不再赘述。
组件编辑中,基本属性的编辑与属于通用建站领域的表单模型范畴,一般通过 UISchema 来描述通用表单,这块也不再赘述。组件编辑的另一部分就是数据编辑,这部分在后面数据模型章节里详细讲。
事件中心是渲染引擎部分,此功能在编辑状态需要禁用。这个功能可以实现图表联动、上卷下钻等数据能力。一个通用事件中心一般包括 **事件触发****事件响应** 两部分,基本结构如下:
```typescript
interface Event {
trigger:
| {
type: 'callback';
callbackName: string;
}
| {
type: 'listener';
eventName: string;
}
| {
type: 'system';
name: string;
};
action:
| {
type: 'dispatch';
eventName: string;
}
| {
type: 'jumpUrl';
url: string;
}
}
```
`trigger` 即事件触发,包括基本的系统事件 `system`,比如定时器或者初始化自动触发;组件的回调 `callback` 比如当按钮被点击时;事件监听 `listener` 比如另一个事件被触发时,这个事件可能来自于 `action`
`action` 即事件响应,包括基本的事件触发 `dispatch`,可以触发其他事件,可以构成一个事件链路;其他的 `action` 就是数据相关,可以用来做条件联动、字段联动、数据集联动等等,因为实现各异这里不做介绍。
事件机制还需要支持值传递,即事件触发源的值可以传递到事件响应方。值传递可以在触发源内部进行,比如当触发源是回调函数时,函数参数就自然作为值传递过去,触发源通过 `...args` 方式接收。
#### 数据钻取
配置了层系的字段都可以进行数据钻取。层系可以在数据集配置,也可以在报表编辑页配置,可以理解为一个顺序有关的文件夹,将文件夹作为字段使用时,默认生效的是第一个子元素,之后可以按照顺序分别进行下钻。
比如 “地区” 层系包含了国家、省、市、区,那么就可以按照这个层级进行数据上卷下钻。
如果一个字段是层系字段,图表需要有对应的操作区域进行上卷下钻,数据编辑区域也可以进行同样操作。数据钻取的计算过程不在图表内部处理,而是触发一个状态后,由渲染引擎将这个层系字段实例状态改为下钻到第 N 层,并且每下钻一次就多拿到一列的数据,由图表组件进行下钻展示。
一般来说下钻后数据仍是全量的,有时候为了避免数据量过大,比如在柱状图点击某个柱子进行下钻,只想看这个柱子下钻后的数据:比如 2017、2018、2019 年三年的数据,下钻到月后数据量是 3 x 12 = 36 条,但如果仅在 2019 年进行下钻,只想看 2019 年的 12 条数据,可以转化为下钻 + 筛选条件的模式:全局下钻展开后 36 条,在 2019 年上点击下钻后,增加一个筛选条件(年 = 2019),这样就达到了效果,整个流程对图表组件是无感知的。
### 数据模型
与通用表单模型 UISchema 相对应,数据模型笔者称之为 CubeSchema,因为 BI 领域对数据的多维处理模型成为 Cube 立方体,数据配置即表示如何对这个立方体进行查询,因此其配置表单成为 CubeSchema。
不管是探索式分析还是 BI 1.0 的报表阶段,数据模型的基本概念是通用的(探索式分析固定了行列,且增加了标记):将字段放置到不同的区域,这些区域的划分方式可以按照功能:横轴、纵轴;按照概念:维度、度量;按照探索分析思路:固化为行、列等等。
这块可能涉及到的技术点有:拖拽、批量选择+拖拽、双击后按照维度度量自动添加、图表切换后区域字段自动迁移、对字段拖拽的系列配置:限制数量、限制类型、限制数据集、是否重复等等。
拖拽可以用 [react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd) 等库,与渲染引擎拖拽方案基本类似,遇到有层系的数据集还需支持嵌套层级的拖拽。
图表切换后字段迁移,可以将每个拖拽区域设置若干类型:
```json
{
"dataType": ["dimension"]
}
```
这样在切换后,维度类型的字段可以自动迁移到维度类型区域,如果对应区域字段数量达到了 `limit` 限制,就继续填充到下一个区域,直到字段用尽或区域填充完为止。
如果在探索式分析场景里,需要提前对字段进行维度度量建模,在切换时按照图表情况进行相应的处理。比如折线图切换到表格的情况:折线图是天然一个维度(主轴) + N 个度量的场景,表格是天然两个维度(行、列)+ 1 个度量的场景(也可以支持多个,对单元格进行再切分即可),那么从折线图切换到表格时,度量就会落到标记的文本区域;如果从拥有行和列的表格切换到柱状图(之所以无法切换到折线图,是因为表格的度量值一般是离散的,而折线图度量值一般是连续的),表格的行与列的字段会落到柱状图的维度轴,表现效果是对维度轴进行下钻。
> [精读《Tableau 探索式模型》](https://github.com/dt-fe/weekly/blob/v2/117.%E7%B2%BE%E8%AF%BB%E3%80%8ATableau%20%E6%8E%A2%E7%B4%A2%E5%BC%8F%E6%A8%A1%E5%9E%8B%E3%80%8B.md) 了解更多探索式分析。
数据模型还包括数据分析相关配置,比如设置对比字段,或者均值线等分析功能。这些数据计算工作放在后端,前端需要将配置项整理到取数接口中,并按照数据驱动的方式展现。
对于对比字段等 “拓展字段” 的分析功能,可以拓展通用取数接口,图表组件无感知,相当于多添加了几个隐藏字段;去特殊值等对标准数据进行操作的情况图表组件也无需感知。
聚类、均值线等需要图表组件额外展示的部分抽象为一套固定的数据格式透传给图表组件,由图表组件自行处理。
可以看出来,都是取数 + 展示,普通的前端业务与 BI 业务开发的区别:
普通前端业务是以业务逻辑为核心的,根据业务需要确定接口格式;BI 业务是以数据为核心的,围绕数据计算模型确定一套固定的接口格式,取数不依赖组件,所有组件对标准数据都有对应的展现。
### 可视化
与普通可视化组件不同,BI 可视化组件需要对接 CubeSchema 模型,同时还要支持 **大数据性能优化、边界数据展示优化、交互响应**
对接 CubeSchema 即统一对接二维表格的数据,大部分组件都是二维以上结构展示,因此对接起来并不困难,有一些一维数据结构的组件比如单指标块就要舍弃其中的某一维,需要确定一套规则。
二维以上部分是较为通用的,虽然计算模型是基于 Cube N 维的,但组件可以通过标准轴进行多维度展开,或者说下钻来实现类似效果。对于折线图来说,轴的含义有限,可以用分面的方式展示多维数据。当然也有一些组件只适合展示特定维度数量的数据。
#### 大数据性能优化
可视化组件特别需要关注性能优化,因为 BI 查询出的数据量可能非常大,特别是多层下钻或基于地理的数据。
技术手段包括 GPU 渲染、缓存 canvas、多线程运算等,业务手段包括数据抽样、按需渲染可视区域、限制数据条数等等。
#### 边界数据展示优化
永远不知道数据集会给出怎样的数据,因此 BI 边界情况特别多,可能点非常密集,也可能丢失一些数据导致渲染异常。图表组件需要利用避让算法将密集的数据打散或着色,目的是为了容易阅读,对于丢失的异常数据也要有保护性的补全机制。
#### 交互响应
包括上卷下钻、点选、圈选、高亮等交互操作,这些操作反馈到渲染引擎导致数据变化并将新的数据灌入图表组件。
业务逻辑上这些交互操作并不复杂,难点在使用的可视化库是否有这个能力,以及如何统一交互行为。
## 总结
BI 领域的四大方向:数据集、渲染引擎、数据模型与可视化都有许多可以做深的技术点,每一块都需要深入沉淀几年技术经验才能做好,需要大量优秀人才通力协作才有可能做好。
目前我们在阿里数据中台正在打造一款面向未来的优秀 BI 工具,如果 BI 领域让你觉得有挑战,随时欢迎你的加入,联系邮箱:ziyi.hzy@alibaba-inc.com
> 讨论地址是:[精读《前端与 BI》 · Issue #208 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/208)
**如果你想参与讨论,请 [点击这里](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)
+28
View File
@@ -0,0 +1,28 @@
{
"name": "weekly",
"version": "1.0.0",
"description": "前端界的好文精读,每周更新!",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/dt-fe/weekly.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/dt-fe/weekly/issues"
},
"homepage": "https://github.com/dt-fe/weekly#readme",
"dependencies": {
"husky": "^3.0.4",
"lint-md-cli": "^0.1.1"
},
"husky": {
"hooks": {
"pre-commit": "npx lint-md ./"
}
}
}
+4
View File
@@ -1,5 +1,9 @@
# 前端精读
<a href="https://travis-ci.org/dt-fe/weekly">
<img src="https://travis-ci.org/dt-fe/weekly.svg?branch=v2" alt="CircleCI Status">
</a>
前端界的好文精读,每周更新!
- [周刊参考池](https://github.com/dt-fe/weekly/issues/2)