From 19334cef4f2ced2d23fb2d002255124581402d01 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 01/58] =?UTF-8?q?Revert=20"=E6=9B=B4=E6=96=B0=E4=B8=83?= =?UTF-8?q?=E6=9C=88=E4=BB=BD=E6=95=B0=E6=8D=AE"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3a292a518913e5e9947aa1219522a1595d732e07. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16e43c898de..fc8d6b81273 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [掘金翻译计划](https://juejin.im/tag/%E6%8E%98%E9%87%91%E7%BF%BB%E8%AF%91%E8%AE%A1%E5%88%92) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖[区块链](#区块链)、[人工智能](#ai--deep-learning--machine-learning)、[Android](#android)、[iOS](#ios)、[前端](#前端)、[后端](#后端)、[设计](#设计)、[产品](#产品)和[其他](#其他) 等领域,以及各大型优质 [官方文档及手册](#官方文档及手册),读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [1726](#近期文章列表) 篇文章,官方文档及手册 [13](#官方文档及手册) 个,共有 [1000](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译和校对。 +掘金翻译计划目前翻译完成 [1684](#近期文章列表) 篇文章,官方文档及手册 [13](#官方文档及手册) 个,共有 [1000](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译和校对。 > ## [🥇掘金翻译计划 — 区块链分舵](https://github.com/xitu/blockchain-miner) From 940087de96e7e721bcc558bef8c87d9fe1e915d1 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 02/58] =?UTF-8?q?Revert=20"postMessage=20=E5=BE=88?= =?UTF-8?q?=E6=85=A2=E5=90=97=3F=20(#6224)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit e89d0bc43c72c5e6305fbde4a5c3b98dfb426711. --- TODO1/is-postmessage-slow.md | 160 +++++++++++++++++------------------ 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/TODO1/is-postmessage-slow.md b/TODO1/is-postmessage-slow.md index 2774acc6966..62d5f0cb500 100644 --- a/TODO1/is-postmessage-slow.md +++ b/TODO1/is-postmessage-slow.md @@ -2,51 +2,51 @@ > * 原文作者:[Surma](https://dassur.ma) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/is-postmessage-slow.md](https://github.com/xitu/gold-miner/blob/master/TODO1/is-postmessage-slow.md) -> * 译者:[linxiaowu66](https://github.com/linxiaowu66) -> * 校对者:[MarchYuanx](https://github.com/MarchYuanx), [TiaossuP](https://github.com/TiaossuP) +> * 译者: +> * 校对者: -# postMessage 很慢吗? +# Is postMessage slow? -不,不一定(视情况而定) +No, not really. (It depends.) -这里的“慢”是什么意思呢?[我之前在这里提及过](https://dassur.ma/things/less-snakeoil/),在这里再说一遍:如果你不度量它,它并不慢,即使你度量它,但是没有上下文,数字也是没有意义的。 +What does “slow” mean? [I said it before](/things/less-snakeoil/), and I will say it again: If you didn’t measure it, it is not slow, and even if you measure it, the numbers are meaningless without context. -话虽如此,人们甚至不会考虑采用 [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker),因为他们担心 `postMessage()` 的性能,这意味着这是值得研究的。[我的上一篇博客文章](https://dassur.ma/things/when-workers/)也得到了类似的[回复](https://twitter.com/dfabu/status/1139567716052930561)。让我们将实际的数字放在 `postMessage()` 的性能上,看看你会在什么时候冒着超出承受能力的风险。如果连普通的 `postMessage()` 在你的使用场景下都太慢,那么你还可以做什么呢? +That being said, the fact that people will not even consider adopting [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker) because of their concerns about the performance of `postMessage()`, means that this is worth investigating. [My last blog post](/things/when-workers/) on workers got [responses](https://twitter.com/dfabu/status/1139567716052930561) along these lines, too. Let’s put actual numbers to the performance of `postMessage()` and see at what point you risk blowing your budgets. What can you do if vanilla `postMessage()` is too slow for your use-case? -准备好了吗?继续往下阅读吧。 +Ready? Go. -## postMessage 是怎么工作的? +## How postMessage works -在开始度量之前,我们需要了解**什么是** `postMessage()`,以及我们想度量它的哪一部分。否则,[我们最终将收集无意义的数据](https://dassur.ma/things/deep-copy/)并得出无意义的结论。 +Before we start measuring, we need to understand **what** `postMessage()` is and which part of it we want to measure. Otherwise, [we’ll end up gathering meaningless data](/things/deep-copy/) and drawing meaningless conclusions. -`postMessage()` 是 [HTML规范](https://html.spec.whatwg.org/multipage/) 的一部分(而不是 [ECMA-262](http://www.ecma-international.org/ecma-262/10.0/index.html#Title)!)正如我在 [deep-copy 一文](https://dassur.ma/things/deep-copy/)中提到的,`postMessage()` 依赖于结构化克隆数据,将消息从一个 JavaScript 空间复制到另一个 JavaScript 空间。仔细研究一下 [`postMessage()` 的规范](https://html.spec.whatwg.org/multipage/webmessaging.html#message-port-post-message-steps),就会发现结构化克隆是一个分两步的过程: +`postMessage()` is part of the [HTML spec](https://html.spec.whatwg.org/multipage/) (not [ECMA-262](http://www.ecma-international.org/ecma-262/10.0/index.html#Title)!). As I mentioned in my [deep-copy post](/things/deep-copy/), `postMessage()` relies on structured cloning to copy the message from one JavaScript realm to another. Taking a closer look at [the specification of `postMessage()`](https://html.spec.whatwg.org/multipage/web-messaging.html#message-port-post-message-steps), it turns out that structured cloning is a two-step process: -### 结构化克隆算法 +### Structured Clone algorithm -1. 在消息上执行 `StructuredSerialize()` -2. 在接收方中任务队列中加入一个任务,该任务将执行以下步骤: - 1. 在序列化的消息上执行 `StructuredDeserialize()` - 2. 创建一个 `MessageEvent` 并派发一个带有该反序列化消息的 `MessageEvent` 事件到接收端口上 +1. Run `StructuredSerialize()` on the message +2. Queue a task in the receiving realm, that will execute the following steps: + 1. Run `StructuredDeserialize()` on the serialized message + 2. Create a `MessageEvent` and dispatch a `MessageEvent` with the deserialized message on the receiving port -这是算法的一个简化版本,因此我们可以关注这篇博客文章中重要的部分。虽然这在**技术上**是不正确的,但它却抓住了精髓。例如,`StructuredSerialize()` 和 `StructuredDeserialize()` 在实际场景中并不是真正的函数,因为它们不是通过 JavaScript([不过有一个 HTML 提案打算将它们暴露出去](https://github.com/whatwg/html/pull/3414))暴露出去的。那这两个函数实际上是做什么的呢?现在,**你可以将 `StructuredSerialize()` 和 `StructuredDeserialize()` 视为 `JSON.stringify()` 和 `JSON.parse()` 的智能版本**。从处理循环数据结构、内置数据类型(如 `Map`、`Set`和`ArrayBuffer`)等方面来说,它们更聪明。但是,这些聪明是有代价的吗?我们稍后再讨论这个问题。 +This is a simplified version of the algorithm so we can focus on the parts that matter for this blog post. It’s **technically** incorrect, but catches the spirit, if you will. For example, `StructuredSerialize()` and `StructuredDeserialize()` are not real functions in the sense that they are not exposed via JavaScript ([yet](https://github.com/whatwg/html/pull/3414)). What do these two functions actually do? For now, **you can think of `StructuredSerialize()` and `StructuredDeserialize()` as smarter versions of `JSON.stringify()` and `JSON.parse()`**, respectively. They are smarter in the sense that they handle cyclical data structures, built-in data types like `Map`, `Set` and `ArrayBuffer` etc. But do these smarts come at a cost? We’ll get back to that later. -上面的算法没有明确说明的是,**序列化会阻塞发送方,而反序列化会阻塞接收方。** 另外还有:Chrome 和 Safari 都推迟了运行 `StructuredDeserialize()`,直到你实际访问了 `MessageEvent` 上的 `.data` 属性。另一方面,Firefox 在派发事件之前会反序列化。 +Something that the algorithm above doesn’t spell out explicitly is the fact that **serialization blocks the sending realm, while deserialization blocks the receiving realm.** And there’s more: It turns out that both Chrome and Safari defer running `StructuredDeserialize()` until you actually access the `.data` property on the `MessageEvent`. Firefox on the other hand deserializes before dispatching the event. -> **注意:** 这两个行为**都是**兼容规范的,并且完全有效。[我在 Mozilla 上提了一个bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1564880),询问他们是否愿意调整他们的实现,因为这可以让开发人员去控制什么时候应该受到反序列化大负载的“性能冲击”。 +> **Note:** Both of these behaviors **are** spec-compatible and perfectly valid. [I opened a bug with Mozilla](https://bugzilla.mozilla.org/show_bug.cgi?id=1564880), asking if they are willing to align their implementation, as it puts the developer in control when to take the “performance hit” of deserializing big payloads. -考虑到这一点,我们必须选择对**什么**来进行基准测试:我们可以端到端进行度量,所以可以度量一个 worker 发送消息到主线程所花费的时间。然而,这个数字将捕获序列化和反序列化的时间总和,但是它们却分别发生在不同的空间下。记住:**与 worker 的整个通信的都是主动的,这是为了保持主线程自由和响应性。** 或者,我们可以将基准测试限制在 Chrome 和 Safari 上,并单独测量从 `StructuredDeserialize()` 到访问 `.data` 属性的时间,这个需要把 Firefox 排除在基准测试之外。我还没有找到一种方法来单独测量 `StructuredSerialize()`,除非运行的时候调试跟踪代码。这两种选择都不理想,但本着构建弹性 web 应用程序的精神,**我决定运行端到端基准测试,为 `postMessage()` 提供一个上限。** +With that in mind, we have to make a choice **what** to benchmark: We could measure end-to-end, so measuring how much time it takes to send a message from a worker to the main thread. However, that number would capture the sum of serialization and deserialization, each of which are happening in different realms. Remember: **This whole spiel with workers is motivated by wanting to keep the main thread free and responsive.** Alternatively, we could limit the benchmarks to Chrome and Safari and measure how long it takes to access the `.data` property to measure `StructuredDeserialize()` in isolation, which would exclude Firefox from the benchmark. I also haven’t found a way to measure `StructuredSerialize()` in isolation, short of running a trace. Neither of these choices are ideal, but in the spirit of building resilient web apps, **I decided to run the end-to-end benchmark to provide an **upper bound** for the cost of `postMessage()`.** -有了对 `postMessage()` 的概念理解和评测的决心,我将使用 ☠️ 微基准 ☠️。请注意这些数字与现实之间的差距。 +Armed with a conceptual understanding of `postMessage()` and the determination to measure, I shall use microbenchmarks. Please mind the gap between these numbers and reality. -## 基准测试 1:发送一条消息需要花费多少时间? +## Benchmark 1: How long does sending a message take? ![Two JSON objects showing depth and breadth](https://dassur.ma/things/is-postmessage-slow/breadth-depth.svg) -深度和宽度在 1 到 6 之间变化。对于每个置换,将生成 1000 个对象。 +Depth and breadth vary between 1 and 6. For each permutation, 1000 objects will be generated. -基准将生成具有特定“宽度”和“深度”的对象。宽度和深度的值介于 1 和 6 之间。**对于宽度和深度的每个组合,1000 个唯一的对象将从一个 worker `postMessage()` 到主线程**。这些对象的属性名都是随机的 16 位十六进制数字符串,这些值要么是一个随机布尔值,要么是一个随机浮点数,或者是一个来自 16 位十六进制数的随机字符串。**基准测试将测量传输时间并计算第 95 个百分位数。** +The benchmark will generate an object with a specific “breadth” and “depth”. The values for breadth and depth lie between 1 and 6. **For each combination of breadth and depth, 1000 unique objects will be `postMessage()`’d from a worker to the main thread**. The property names of these objects are random 16-digit hexadecimal numbers as a string, the values are either a random boolean, a random float or again a random string in the from of a 16-digit hexadecimal number. **The benchmark will measure the transfer time and calculate the 95th percentile.** -### 测量结果 +### Results ![](https://dassur.ma/things/is-postmessage-slow/nokia2-chrome.svg) @@ -58,46 +58,46 @@ ![](https://dassur.ma/things/is-postmessage-slow/macbook-safari.svg) -这一基准测试是在 2018 款的 MacBook Pro上的 Firefox、 Safari、和 Chrome 上运行,在 Pixel 3XL 上的 Chrome 上运行,在 诺基亚 2 上的 Chrome 上运行。 +The benchmark was run on Firefox, Safari and Chrome on a 2018 MacBook Pro, on Chrome on a Pixel 3XL and on Chrome on a Nokia 2. -> **注意:** 你可以在 [gist](https://gist.github.com/surma/08923b78c42fab88065461f9f507ee96) 中找到基准数据、生成基准数据的代码和可视化代码。而且,这是我人生中第一次编写 Python。别对我太苛刻。 +> **Note:** You can find the benchmark data, to code to generate it and the code for the visualization in [this gist](https://gist.github.com/surma/08923b78c42fab88065461f9f507ee96). Also, this was the first time in my life writing Python. Don’t be too harsh on me. -Pixel 3 的基准测试数据,尤其是 Safari 的数据,对你来说可能有点可疑。当 [Spectre & Meltdown](https://zhuanlan.zhihu.com/p/32784852) 被发现的时候,所有的浏览器会禁用 [SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) 并将我要测量使用的 [performance.now()](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) 函数实行计时器的精度减少。只有 Chrome 能够还原这些更改,因为它们将[站点隔离](https://www.chromium.org/home/chromisecurity/site-isolation)发布到 Chrome 桌面版。更具体地说,这意味着浏览器将 `performance.now()` 的精度限制在以下值上: +The benchmark data from the Pixel 3 and especially Safari might look a bit suspicious to you. When Spectre & Meltdown were discovered, all browsers disabled [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) and reduced the precision of timers like [`performance.now()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now), which I use to measure. Only Chrome was able to revert some these changes since they shipped [Site Isolation](https://www.chromium.org/Home/chromium-security/site-isolation) to Chrome on desktop. More concretely that means that browsers clamp the precision of `performance.now()` to the following values: -* Chrome(桌面版):5µs -* Chrome(安卓系统):100µs -* Firefox(桌面版):1ms(该限制可以禁用掉,我就是禁用掉的) -* Safari(桌面版):1ms +* Chrome (desktop): 5µs +* Chrome (Android): 100µs +* Firefox (desktop): 1ms (clamping can be disabled flag, which I did) +* Safari (desktop): 1ms -数据显示,对象的复杂性是决定对象序列化和反序列化所需时间的重要因素。这并不奇怪:序列化和反序列化过程都必须以某种方式遍历整个对象。数据还表明,对象 JSON 化后的大小可以很好地预测传输该对象所需的时间。 +The data shows that the complexity of the object is a strong factor in how long it takes to serialize and deserialize an object. This should not be surprising: Both the serialization and deserialization process have to traverse the entire object one way or another. The data indicates furthermore that the size of the JSON representation of an object is a good predictor for how long it takes to transfer that object. -## 基准测试 2:什么导致 postMessage 变慢了? +## Benchmark 2: What makes postMessage slow? -为了验证这个,我修改了基准测试:我生成了宽度和深度在 1 到 6 之间的所有排列,但除此之外,所有叶子属性都有一个长度在 16 字节到 2 KiB 之间的字符串值。 +To verify, I modified the benchmark: I generate all permutations of breadth and depth between 1 and 6, but in addition all leaf properties have a string value with a length between 16 bytes and 2KiB. -### 测试结果 +### Results ![A graph showing the correlation between payload size and transfer time for postMessage](https://dassur.ma/things/is-postmessage-slow/correlation.svg) -传输时间与 `JSON.stringify()` 返回的字符串长度有很强的相关性。 +Transfer time has a strong correlation with the length of the string returned by `JSON.stringify()`. -我认为这种相关性足够强,可以给出一个经验法则:**对象的 JSON 字符串化后的大小大致与它的传输时间成正比。** 然而,更需要注意的事实是,**这种相关性只与大对象相关**,我说的大是指超过 100 KiB 的任何对象。虽然这种相关性在数学上是成立的,但在较小的有效载荷下,这种差异更为明显(译者注:怀疑这句话作者应该是写错了,应该表述为差异不明显)。 +I think the correlation is strong enough to issue a rule of thumb: **The stringified JSON representation of an object is roughly proportional to its transfer time.** However, even more important to note is the fact that **this correlation only becomes relevant for big objects**, and by big I mean anything over 100KiB. While the correlation holds mathematically, the divergence is much more visible at smaller payloads. -## 评估:发送一条信息 +## Evaluation: It’s about sending a message -我们有数据,但如果我们不把它上下文化,它就没有意义。如果我们想得出**有意义的**结论,我们需要定义“慢”。预算在这里是一个有用的工具,我将再次回到 [RAIL](https://developer.google.com/web/fundamentals/performance/rail) 指南来确定我们的预期。 +We have data, but it’s meaningless if we don’t contextualize it. If we want to draw **meaningful** conclusions, we need to define “slow”. Budgets are a helpful tool here, and I will once again go back to the [RAIL](https://developers.google.com/web/fundamentals/performance/rail) guidelines to establish our budgets. -根据我的经验,一个 web worker 的核心职责至少是管理应用程序的状态对象。状态通常只在用户与你的应用程序交互时才会发生变化。根据 RAIL 的说法,我们有 100 ms 来响应用户交互,这意味着**即使在最慢的设备上,你也可以 `postMessage()` 高达 100 KiB 的对象,并保持在你的预期之内。** +In my experience, a web worker’s core responsibility is, at the very least, managing your app’s state object. State often only changes when the user interacts with your app. According to RAIL, we have 100ms to react to user interactions, which means that **even on the slowest devices, you can `postMessage()` objects up to 100KiB and stay within your budget.** -当运行 JS 驱动的动画时,这种情况会发生变化。动画的 RAIL 预算是 16 ms,因为每一帧的视觉效果都需要更新。如果我们从 worker 那里发送一条消息,该消息会阻塞主线程的时间超过这个时间,那么我们就有麻烦了。从我们的基准数据来看,任何超过 10 KiB 的动画都不会对你的动画预算构成风险。也就是说,**这就是我们更喜欢用 CSS animation 和 transition 而不是 JS 驱动主线程绘制动画的一个重要原因。** CSS animation 和 transition 运行在一个单独的线程 - 合成线程 - 不受阻塞的主线程的影响。 +This changes when you have JS-driven animations running. The RAIL budget for animations is 16ms, as the visuals need to get updated every frame. If we send a message from the worker that would block the main thread for longer than that, we are in trouble. Looking at the numbers from our benchmarks, everything up to 10KiB will not pose a risk to your animation budget. That being said, **this is a strong reason to prefer CSS animations and transitions over main-thread JS-driven animations.** CSS animations and transitions runs on a separate thread — the compositor thread — and are not affected by a blocked main thread. -## 必须发送更多的数据 +## Must. send. moar. data. -以我的经验,对于大多数采用非主线程架构的应用程序来说,`postMessage()` 并不是瓶颈。不过,我承认,在某些设置中,你的消息可能非常大,或者需要以很高的频率发送大量消息。如果普通 `postMessage()` 对你来说太慢的话,你还可以做什么? +In my experience, `postMessage()` is not the bottleneck for most apps that are adopting an off-main-thread architecture. I will admit, however, that there might be setups where your messages are either really big or you need to send a lot of them at a high frequency. What can you do if vanilla `postMessage()` is too slow for you? -### 打补丁 +### Patching -在状态对象的情况下,对象本身可能非常大,但通常只有少数几个嵌套很深的属性会发生变化。我们在 [PROXX](https://proxx.app) 中遇到了这个问题,我们的 PWA 版本扫雷:游戏状态由游戏网格的二维数组组成。每个单元格存储这些字段:是否有雷,以及是被发现的还是被标记的。 +In the case of state objects, the objects themselves can be quite big, but it’s often only a handful of deeply nested properties that change. We encountered this problem in [PROXX](https://proxx.app), our PWA Minesweeper clone: The game state consists of a 2-dimensional array for the game grid. Each cell stores whether it’s a mine, if it’s been revealed or if it’s been flagged: ```typescript interface Cell { @@ -109,12 +109,12 @@ interface Cell { } ``` -这意味着最大的网格( 40 × 40 个单元格)加起来的 JSON 大小约等于 134 KiB。发送整个状态对象是不可能的。**我们选择记录更改并发送一个补丁集,而不是在更改时发送整个新的状态对象。** 虽然我们没有使用 [ImmerJS](https://github.com/immerjs/immer),这是一个处理不可变对象的库,但它提供了一种快速生成和应用补丁集的方法: +That means the biggest possible grid of 40 by 40 cells adds up to ~134KiB of JSON. Sending an entire state object is out of the question. **Instead of sending the entire new state object whenever something changes, we chose to record the changes and send a patchset instead.** While we didn’t use [ImmerJS](https://github.com/immerjs/immer), a library for working with immutable objects, it does provide a quick way to generate and apply such patchsets: ```js // worker.js immer.produce(stateObject, draftState => { - // 在这里操作 `draftState` + // Manipulate `draftState` here }, patches => { postMessage(patches); }); @@ -122,11 +122,11 @@ immer.produce(stateObject, draftState => { // main.js worker.addEventListener("message", ({data}) => { state = immer.applyPatches(state, data); - // 对新状态的反应 + // React to new state } ``` -ImmerJS 生成的补丁如下所示: +The patches that ImmerJS generates look like this: ```json [ @@ -147,45 +147,45 @@ ImmerJS 生成的补丁如下所示: ] ``` -这意味着需要传输的数据量与更改的大小成比例,而不是与对象的大小成比例。 +This means that the amount that needs to get transferred is proportional to the size of your changes, not the size of the object. -### 分块 +### Chunking -正如我所说,对于状态对象,**通常**只有少数几个属性会改变。但并非总是如此。事实上,[PROXX](https://proxx.app) 有这样一个场景,补丁集可能会变得非常大:第一个展示可能会影响多达 80% 的游戏字段,这意味着补丁集有大约 70 KiB 的大小。当目标定位于功能手机时,这就太多了,特别是当我们可能运行 JS 驱动的 WebGL 动画时。 +As I said, for state objects it’s **often** only a handful of properties that change. But not always. In fact, [PROXX](https://proxx.app) has a scenario where patchsets could turn out quite big: The first reveal can affect up to 80% of the game field, which adds up to a patchset of about ~70KiB. When targeting feature phones, that is too much, especially as we might have JS-driven WebGL animations running. -我们问自己一个架构上的问题:我们的应用程序能支持部分更新吗?Patchsets 是补丁的集合。**你可以将补丁集“分块”到更小的分区中,并按顺序应用补丁,而不是一次性发送补丁集中的所有补丁。** 在第一个消息中发送补丁 1 - 10,在下一个消息中发送补丁 11 - 20,以此类推。如果你将这一点发挥到极致,那么你就可以有效地让你的补丁**流式化**,从而允许你使用你可能知道的设计模式以及喜爱的响应式编程。 +We asked ourselves an architectural question: Can our app support partial updates? Patchsets are a collection of patches. **Instead of sending all patches in the patchset at once, you can “chunk” the patchset into smaller partitions and apply them sequentially.** Send patches 1-10 in the first message, 11-20 on the next, and so on. If you take this to the extreme, you are effectively **streaming** your patches, allowing you to use all the patterns you might know and love from reactive programming. -当然,如果你不注意,这可能会导致不完整甚至破碎的视觉效果。然而,你可以控制分块如何进行,并可以重新排列补丁以避免任何不希望的效果。例如,你可以确保第一个块包含所有影响屏幕元素的补丁,并将其余的补丁放在几个补丁集中,以给主线程留出喘息的空间。 +Of course, this can result in incomplete or even broken visuals if you don’t pay attention. However, you are in control of how the chunking happens and could reorder the patches to avoid any undesired effects. For example, you could make sure that the first chunk contains all patches affecting on-screen elements, and put the remaining patches in to a couple of patchsets to give the main thread room to breathe. -我们在 [PROXX](https://proxx.app) 上做分块。当用户点击一个字段时,worker 遍历整个网格,确定需要更新哪些字段,并将它们收集到一个列表中。如果列表增长超过某个阈值,我们就将目前拥有的内容发送到主线程,清空列表并继续迭代游戏字段。这些补丁集足够小,即使在功能手机上, `postMessage()` 的成本也可以忽略不计,我们仍然有足够的主线程预算时间来更新我们的游戏 UI。迭代算法从第一个瓦片向外工作,这意味着我们的补丁以相同的方式排列。如果主线程只能在帧预算中容纳一条消息(就像 Nokia 8110),那么部分更新就会伪装成一个显示动画。如果我们在一台功能强大的机器上,主线程将继续处理消息事件,直到超出预算为止,这是 JavaScript 的事件循环的自然结果。 +We do chunking in [PROXX](https://proxx.app). When the user taps a field, the worker iterates over the entire grid to figure out which fields need to be updated and collects them in a list. If that list grows over a certain threshold, we send what we have so far to the main thread, empty the list and continue iterating the game field. These patchsets are small enough that even on a feature phone the cost of `postMessage()` is negligible and we still have enough main thread budget time to update our game’s UI. The iteration algorithm works from the first tile outwards, meaning our patches are ordered in the same fashion. If the main thread can only fit one message into its frame budget (like on the Nokia 8110), the partial updates disguise as a reveal animation. If we are on a powerful machine, the main thread will keep processing message events until it runs out of budget just by nature JavaScript’s event loop. 视频链接:https://dassur.ma/things/is-postmessage-slow/proxx-reveal.mp4 -经典手法:在 [PROXX] 中,补丁集的分块看起来像一个动画。这在支持 6x CPU 节流的台式机或低端手机上尤其明显。 +Classic sleight of hand: In [PROXX], the chunking of the patchset looks like an animation. This is especially visible on low-end mobile phones or on desktop with 6x CPU throttling enabled. -### 也许应该 JSON? +### Maybe JSON? -`JSON.parse()` 和 `JSON.stringify()` 非常快。JSON 是 JavaScript 的一个小子集,所以解析器需要处理的案例更少。由于它们的频繁使用,它们也得到了极大的优化。[Mathias 最近指出](https://twitter.com/mathias/status/1143551692732030979),有时可以通过将大对象封装到 `JSON.parse()` 中来缩短 JavaScript 的解析时间。**也许我们也可以使用 JSON 来加速 `postMessage()` ?遗憾的是,答案似乎是否定的:** +`JSON.parse()` and `JSON.stringify()` are incredibly fast. JSON is a small subset of JavaScript, so the parser has fewer cases to handle. Because of their frequent usage, they have also been heavily optimized. [Mathias recently pointed out](https://twitter.com/mathias/status/1143551692732030979), that you can sometimes reduce parse time of your JavaScript by wrapping big objects into `JSON.parse()`. **Maybe we can use JSON to speed up `postMessage()` as well? Sadly, the answer seems to be no:** -![将发送对象的持续时间与序列化、发送和反序列化对象进行比较的图](https://dassur.ma/things/is-postmessage-slow/serialize.svg) +![A graph comparing the duration of sending an object to serializing, sending, and deserializing an object.](https://dassur.ma/things/is-postmessage-slow/serialize.svg) -将手工 JSON 序列化的性能与普通的 `postMessage()` 进行比较,没有得到明确的结果。 +Comparing the performance of manual JSON serialization to vanilla `postMessage()` yields no clear result. -虽然没有明显的赢家,但是普通的 `postMessage()` 在最好的情况下表现得更好,在最坏的情况下表现得同样糟糕。 +While there is no clear winner, vanilla `postMessage()` seems to perform better in the best case, and equally bad in the worst case. -### 二进制格式 +### Binary formats -处理结构化克隆对性能影响的另一种方法是完全不使用它。除了结构化克隆对象外,`postMessage()` 还可以**传输**某些类型。`ArrayBuffer` 是这些[可转换](https://developer.mozilla.org/en-US/docs/Web/API/Transferable)类型之一。顾名思义,传输 `ArrayBuffer` 不涉及复制。发送方实际上失去了对缓冲区的访问,现在是属于接收方的。**传输一个 `ArrayBuffer` 非常快,并且独立于 `ArrayBuffer`的大小。** 缺点是 `ArrayBuffer` 只是一个连续的内存块。我们就不能再处理对象和属性。为了让 `ArrayBuffer` 发挥作用,我们必须自己决定如何对数据进行编组。这本身是有代价的,但是通过了解构建时数据的形状或结构,我们可以潜在地进行许多优化,而这些优化是一般克隆算法无法实现的。 +Another way to deal with the performance impact of structured cloning is to not use it at all. Apart from structured cloning objects, `postMessage()` can also **transfer** certain types. `ArrayBuffer` is one of these [transferable](https://developer.mozilla.org/en-US/docs/Web/API/Transferable) types. As the name implies, transferring an `ArrayBuffer` does not involve copying. The sending realm actually loses access to the buffer and it is now owned by the receiving realm. **Transferring an `ArrayBuffer` is extremely fast and independent of the size of the `ArrayBuffer`**. The downside is that `ArrayBuffer` are just a continuous chunk of memory. We are not working with objects and properties anymore. For an `ArrayBuffer` to be useful we have to decide how our data is marshalled ourselves. This in itself has a cost, but by knowing the shape or structure of our data at build time we can potentially tap into many optimizations that are unavailable to a generic cloning algorithm. -一种允许你使用这些优化的格式是 [FlatBuffers](https://google.github.io/flatbuffers/)。Flatbuffers 有 JavaScript (和其他语言)对应的编译器,可以将模式描述转换为代码。该代码包含用于序列化和反序列化数据的函数。更有趣的是:Flatbuffers 不需要解析(或“解包”)整个 `ArrayBuffer` 来返回它包含的值。 +One format that allows you to tap into these optimizations are [FlatBuffers](https://google.github.io/flatbuffers/). FlatBuffers have compilers for JavaScript (and other languages) that turn schema descriptions into code. That code contains functions to serialize and deserialize your data. Even more interestingly: FlatBuffers don’t need to parse (or “unpack”) the entire `ArrayBuffer` to return a value it contains. ### WebAssembly -那么使用每个人都喜欢的 WebAssembly 呢?一种方法是使用 WebAssembly 查看其他语言生态系统中的序列化库。[CBOR](https://cbor.io) 是一种受 json 启发的二进制对象格式,已经在许多语言中实现。[ProtoBuffers](https://developer.google.com/protocol-buffers/) 和前面提到的 [FlatBuffers](https://google.github.io/flatbuffers/) 也有广泛的语言支持。 +What about everyone’s favorite: WebAssembly? One approach is to use WebAssembly to look at serialization libraries in the ecosystems of other languages. [CBOR](https://cbor.io), a JSON-inspired binary object format, has been implemented in many languages. [ProtoBuffers](https://developers.google.com/protocol-buffers/) and the aforementioned [FlatBuffers](https://google.github.io/flatbuffers/) have wide language support as well. -然而,我们可以在这里更厚颜无耻:我们可以依赖该语言的内存布局作为序列化格式。我用 [Rust](https://www.rust-lang.org) 编写了[一个小例子](https://dassur.ma/things/is-postmessage-slow/binary-state-rust):它用一些 getter 和 setter 方法定义了一个 `State` 结构体(无论你的应用程序的状态如何,它都是符号),这样我就可以通过 JavaScript 检查和操作状态。要“序列化”状态对象,只需复制结构所占用的内存块。为了反序列化,我分配一个新的 `State` 对象,并用传递给反序列化函数的数据覆盖它。由于我在这两种情况下使用相同的 WebAssembly 模块,内存布局将是相同的。 +However, we can be more cheeky here: We can rely on the memory layout of the language as our serialization format. I wrote [a little example](./binary-state-rust) using [Rust](https://www.rust-lang.org): It defines a `State` struct (symbolic for whatever your app’s state looks like) with some getter and setter methods so I can inspect and manipulate the state from JavaScript. To “serialize” the state object, I just copy the chunk of memory occupied by the struct. To deserialize, I allocate a new `State` object, and overwrite it with the data passed to the deserialization function. Since I’m using the same WebAssembly module in both cases, the memory layout will be identical. -> 这只是一个概念的证明。如果你的结构包含指针(如 `Vec` 和 `String`),那么你就很容易陷入未定义的行为错误中。同时还有一些不必要的复制。所以请对代码负责任! +> This is just a proof-of-concept. You can easily tap into undefined behavior if your struct contains pointers (like `Vec` and `String` do). There’s also some unnecessary copying going on. Code responsibly! ```rust pub struct State { @@ -194,7 +194,7 @@ pub struct State { #[wasm_bindgen] impl State { - // 构造器, getters and setter... + // Constructors, getters and setter... pub fn serialize(&self) -> Vec { let size = size_of::(); @@ -230,29 +230,29 @@ pub fn deserialize(vec: Vec) -> Option { } ``` -> **注意:** [Ingvar](https://twitter.com/rreverser) 向我指出了 [Abomonation](https://github.com/TimelyDataflow/abomonation),是一个严重有问题的序列化库,虽然可以使用指针的概念。他的建议:“不要使用这个库!”。 +> **Note:** [Ingvar](https://twitter.com/rreverser) pointed me to [Abomonation](https://github.com/TimelyDataflow/abomonation), a seriously questionable serialization library that works even **with** pointers. His advice: “Do \[not\] try this!”. -WebAssembly 模块最终 gzip 格式大小约为 3 KiB,其中大部分来自内存管理和一些核心库函数。当某些东西发生变化时,就会发送整个状态对象,但是由于 `ArrayBuffers` 的可移植性,其成本非常低。换句话说:**该技术应该具有几乎恒定的传输时间,而不管状态大小。** 然而,访问状态数据的成本会更高。总是要权衡的! +The WebAssembly module ends up at about 3KiB gzip’d, most of which stems from memory management and some core library functions. The entire state object is sent whenever something changes, but due to the transferability of `ArrayBuffers`, this is extremely cheap. In other words: **This technique should have near-constant transfer time, regardless of state size.** It will, however, be more costly to access state data. There’s always a tradeoff! -这种技术还要求状态结构不使用指针之类的间接方法,因为当将这些值复制到新的 WebAssembly 模块实例时,这些值是无效。因此,你可能很难在高级语言中使用这种方法。我的建议是 C、 Rust 和 AssemblyScript,因为你可以完全控制内存并对内存布局有足够的了解。 +This technique also requires that the state struct does not make any use of indirection like pointers, as those values will become invalid when copied to a new WebAssembly module instance. As a result, you will probably struggle to use this approach with higher-level languages. My recommendations are C, Rust and AssemblyScript, as you are in full control and have sufficient insight into memory layout. -### SAB 和 WebAssembly +### SABs & WebAssembly -> **提示:** 本节适用于 `SharedArrayBuffer`,它在除桌面端的 Chrome 外的所有浏览器中都已禁用。这正在进行中,但是不能给出 ETA。 +> **Heads up:** This section works with `SharedArrayBuffer`, which have been disabled in all browsers except Chrome on desktop. This is being worked on, but no ETA can be given on this. -特别是从游戏开发人员那里,我听到了多个请求,要求 JavaScript 能够跨多个线程共享对象。我认为这不太可能添加到 JavaScript 本身,因为它打破了 JavaScript 引擎的一个基本假设。但是,有一个例外叫做 [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) ("SABs")。SABs 的行为完全类似于 `ArrayBuffers`,但是在传输时,不像 `ArrayBuffers` 那样会导致其中一方失去访问权, SAB 可以克隆它们,并且**双方**都可以访问到相同的底层内存块。**SABs 允许 JavaScript 空间采用共享内存模型。** 对于多个空间之间的同步,有 [`Atomics`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics) 提供互斥和原子操作。 +Especially from game developers, I have heard multiple requests to give JavaScript the capability to share objects across multiple threads. I think this is unlikely to ever be added to JavaScript itself, as it breaks one of the fundamentals assumptions of JavaScript engines. However, there is an exception to this called [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) (“SABs”). SABs behave exactly like `ArrayBuffers`, but instead of one realm losing access when being transferred , they can be cloned and **both** realms will have access to the same underlying chunk of memory. **SABs allows the JavaScript realms to adopt a shared memory model.** For synchronization between realms, there’s [`Atomics`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics) which provide Mutexes and atomic operations. -使用 SABs,你只需在应用程序启动时传输一块内存。然而,除了二进制表示问题之外,你还必须使用 `Atomics` 来防止其中一方在另一方还在写入的时候读取状态对象,反之亦然。这可能会对性能产生相当大的影响。 +With SABs, you’d only have to transfer a chunk of memory once at the start of your app. However, in addition to the binary representation problem, you’d have to use `Atomics` to prevent one realm from reading the state object while the other realm is still writing and vice-versa. This can have a considerable performance impact. -除了使用 SABs 和手动序列化/反序列化数据之外,你还可以使用**线程化**的 WebAssembly。WebAssembly 已经标准化了对线程的支持,但是依赖于 SABs 的可用性。**使用线程化的 WebAssembly,你可以使用与使用线程编程语言相同的模式编写代码**。当然,这是以开发复杂性、编排以及可能需要交付的更大、更完整的模块为代价的。 +As an alternative to using SABs and serializing/deserializing data manually, you could embrace **threaded** WebAssembly. WebAssembly has standardized support for threads, but is gated on the availability of SABs. **With threaded WebAssembly you can write code with the exact same patterns you are used to from threaded programming languages.** This, of course, comes at the cost of development complexity, orchestration and potentially bigger and monolithic modules that need to get shipped. -## 结论 +## Conclusion -我的结论是:即使在最慢的设备上,你也可以使用 `postMessage()` 最大 100 KiB 的对象,并保持在 100 ms 响应预算之内。如果你有 JS 驱动的动画,有效载荷高达 10 KiB 是无风险的。对于大多数应用程序来说,这应该足够了。**`postMessage()` 确实有一定的代价,但还不到让非主线程架构变得不可行的程度。** +Here’s my verdict: Even on the slowest devices, you can `postMessage()` objects up to 100KiB and stay within your 100ms response budget. If you have JS-driven animations, payloads up to 10KiB are risk-free. This should be sufficient for most apps. **`postMessage()` does have a cost, but not the extent that it makes off-main-thread architectures unviable.** -如果你的有效负载大于此值,你可以尝试发送补丁或切换到二进制格式。**从一开始就将状态布局、可移植性和可补丁性作为架构决策,可以帮助你的应用程序在更广泛的设备上运行。** 如果你觉得共享内存模型是你最好的选择,WebAssembly 将在不久的将来为你铺平道路。 +If your payloads are bigger than this, you can try sending patches or switching to a binary format. **Considering state layout, transferability and patchability as an architectural decision from the start can help your app run on a wider spectrum of devices.** If you feel like a shared memory model is your best bet, WebAssembly will pave that way for you in the near future. -我已经在[一篇旧的博文](https://dassur.ma/things/actormodel/)上暗示 Actor Model,我坚信我们可以在**如今**的 web 上实现高性能的非主线程架构,但这需要我们离开线程化语言的舒适区以及 web 中那种默认在所有主线程工作的模式。我们需要探索另一种架构和模型,**拥抱** Web 和 JavaScript 的约束。这些好处是值得的。 +As I already hinted at in [an older blog post](/things/actormodel/) about the Actor Model, I strongly believe we can implement performant off-main-thread architectures on the web **today**, but this requires us leaving our comfort zone of threaded languages and the web’s all-on-main-by-default. We need to explore alternative architectures and models that **embrace** the constraints of the Web and JavaScript. The benefits are worth it. > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 93e3f24a2b7dba8cba3657304297037d6da80f19 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 03/58] =?UTF-8?q?Revert=20"=E4=BB=80=E4=B9=88=E6=97=B6?= =?UTF-8?q?=E5=80=99=E9=9C=80=E8=A6=81=E8=BF=9B=E8=A1=8C=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=9A=84=E6=A0=87=E5=87=86=E5=8C=96=3F=20=E4=B8=BA=E4=BB=80?= =?UTF-8?q?=E4=B9=88=EF=BC=9F=20(#6214)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 941f4d2e9c566f0f5974e113dcc00fef6bf94647. --- TODO1/when-to-standardize-your-data.md | 75 +++++++++++++------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/TODO1/when-to-standardize-your-data.md b/TODO1/when-to-standardize-your-data.md index ebdc11a0026..95f6016d74e 100644 --- a/TODO1/when-to-standardize-your-data.md +++ b/TODO1/when-to-standardize-your-data.md @@ -2,87 +2,86 @@ > * 原文作者:[Zakaria Jaadi](https://medium.com/@zakaria.jaadi) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md](https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md) -> * 译者:[Ultrasteve](https://github.com/Ultrasteve) -> * 校对者:[TBLGSn](https://github.com/TBLGSn) +> * 译者: +> * 校对者: -# 什么时候需要进行数据的标准化?为什么? +# When and why to standardize your data? -> 一份告诉你什么时候应该进行数据标准化的指南 +> A simple guide on when to standardize your data and when not to. ![Credits : 365datascience.com](https://cdn-images-1.medium.com/max/NaN/1*dZlwWGNhFco5bmpfwYyLCQ.png) -数据标准化是一种重要的技术,通常来说,在使用许多机器学习模型之前,我们都要使用它来对数据进行预处理,它能对输入数据集里面的各个特征的范围进行标准化。 +Standardization is an important technique that is generally performed as a pre-processing step before many Machine Learning models, to standardize the range of features of input data set. -一些机器学习工程师通常在使用所有机器学习模型之前,倾向于盲目地对他们的数据进行标准化,然而,其实他们并不清楚数据标准化的理由,更不知道什么情况下使用这一技术是必要的,什么时候不是。因此,这篇文章的目标是解释如何,为什么以及何时标准化数据。 +Some ML developers tend to standardize their data blindly before “every” Machine Learning model without taking the effort to understand why it must be used, or even if it’s needed or not. So the goal of this post is to explain how, why and when to standardize data. -## 标准化 +## Standardization -当输入数据集的特征在它们的范围之间具有大差异时,或者它们各自使用的单位不同时(比如说一些用米,一些用厘米),我们会想到对数据进行标准化。 +Standardization comes into picture when features of input data set have large differences between their ranges, or simply when they are measured in different measurement units (e.g., Pounds, Meters, Miles … etc). -这些初始特征范围的差异,会给许多机器学习模型带来不必要的麻烦。例如,对于基于距离计算的模型来说,当其中一个特征值变化范围较大时,那么预测结果很大程度上就会受到它的影响。 +These differences in the ranges of initial features causes trouble to many machine learning models. For example, for the models that are based on distance computation, if one of the features has a broad range of values, the distance will be governed by this particular feature. -我们这里举一个例子。现在我们有一个二维的数据集,它有两个特征,以米为单位的高度(范围是 1 到 2 米)和以磅为单位的重量(范围是 10 到 200 磅)。无论你在这个数据集上使用什么基于距离的模型,重量特征对结果的影响都会大大的高于高度特征,因为它的数据变化范围相对更大。因此,为了预防这种问题的发生,我们会在这里用到数据标准化来约束重量特征的数据变化范围。 +To illustrate this with an example: say we have a 2-dimensional data set with two features, Height in Meters and Weight in Pounds, that range respectively from [1 to 2] Meters and [10 to 200] Pounds. No matter what distance based model you perform on this data set, the Weight feature will dominate over the Height feature and will have more contribution to the distance computation, just because it has bigger values compared to the Height. So, to prevent this problem, transforming features to comparable scales using standardization is the solution. -## 如何进行数据标准化? +## How to standardize data? ### Z-score -`Z-score` 是最受欢迎的数据标准化方法之一,在这种方法中,我们对每一项数据减去它的平均值并除以它的标准差。 +Z-score is one of the most popular methods to standardize data, and can be done by subtracting the mean and dividing by the standard deviation for each value of each feature. ![](https://cdn-images-1.medium.com/max/NaN/0*AgmY9auxftS9BI73.png) -一旦完成了数据标准化,所有特征对应的数据平均值变为 0,方差变为 1,因此,所有特征的数据变化范围现在是一致的。 +Once the standardization is done, all the features will have a mean of zero, a standard deviation of one, and thus, the same scale. -> 其实还有许多数据标准化的方法,但为了降低难度,我们在这篇文章中只使用这种方法。 +> There exist other standardization methods but for the sake of simplicity, in this story i settle for Z-score method. -## 什么时候需要进行数据的标准化?为什么? +## When to standardize data and why? -如上所示,在基于距离的模型中,数据标准化用于预防范围较大的特征对预测结果进行较大的影响。不过使用标准化的原因不仅仅只有这一个,对于不同的模型会有不同的原因。 +As seen above, for distance based models, standardization is performed to prevent features with wider ranges from dominating the distance metric. But the reason we standardize data is not the same for all machine learning models, and differs from one model to another. -那么,在使用什么机器学习方法和模型之前,我们需要进行数据标准化呢?原因又是什么? +So before which ML models and methods you have to standardize your data and why? -**1- 主成分分析:** +**1- Before PCA:** -在主成分分析中,方差较大或者范围较大的特征,相较于小方差小范围的数据获得更高的权重,这样会导致它们不合常理的主导第一主成分(方差最大的成分)的变化。为什么说这是不合常理的呢?因为导致这一特征比其他特征权重更大的理由,仅仅是因为它们是以不同的尺度测量的。 +In Principal Component Analysis, features with high variances/wide ranges, get more weight than those with low variance, and consequently, they end up illegitimately dominating the First Principal Components (Components with maximum variance). I used the word “Illegitimately” here, because the reason these features have high variances compared to the other ones is just because they were measured in different scales. -通过给予所有特征相同的权重,数据标准化可以预防这一点。 +Standardization can prevent this, by giving same wheightage to all features. -**2- 聚类:** +**2- Before Clustering:** -聚类模型是基于距离的算法。为了测量观测对象之间的相似性,并将它们聚集在一起,模型需要使用距离度量 -距离度量(Distance Metrics)。在这种算法中,范围较大的特征会对聚类结果产生更大的影响。因此,在进行聚类之前我们需要进行数据标准化。 +Clustering models are distance based algorithms, in order to measure similarities between observations and form clusters they use a distance metric. So, features with high ranges will have a bigger influence on the clustering. Therefore, standardization is required before building a clustering model. -**3- KNN:** +**3- Before KNN:** -k-最近邻(分类算法)是一个基于距离的分类器,其基于对训练集中已标记的观察结果的相似性度量(例如:距离度量)来对于新数据进行分类。标准化使所有变量对相似性度量的贡献相等。 +k-nearest neighbors is a distance based classifier that classifies new observations based on similarity measures (e.g., distance metrics) with labeled observations of the training set. Standardization makes all variables to contribute equally to the similarity measures . -**4- SVM:** +**4- Before SVM** -支持向量机尝试最大化决策平面与支持向量之间的距离。如果一个特征的值很大,那么相较于其他特征它会对计算结果造成更大的影响。因此,标准化使所有特征对距离度量具有相同的影响。 +Support Vector Machine tries to maximize the distance between the separating plane and the support vectors. If one feature has very large values, it will dominate over other features when calculating the distance. So Standardization gives all features the same influence on the distance metric. ![Credits : Arun Manglick ([arun-aiml.blogspot.com](http://arun-aiml.blogspot.com/))](https://cdn-images-1.medium.com/max/2000/0*_taflmQxrsa0vguT.PNG) -**5- 在回归模型中测量自变量的重要性** +**5- Before measuring variable importance in regression models** -你可以在回归分析中测量变量的重要程度。首先使用**标准化**过后的独立变量来训练模型,然后计算它们对应的标准化系数的绝对值差就能得出结论。然而,如果独立变量是未经标准化的,那比较它们的系数将毫无意义。 +You can measure variable importance in regression analysis, by fitting a regression model using the **standardized** independent variables and comparing the absolute value of their standardized coefficients. But, if the independent variables are not standardized, comparing their coefficients becomes meaningless. -**6- Lasso 回归和岭回归** +**6- Before Lasso and Ridge Regression** -Lasso 回归和岭回归对各变量对应的系数进行惩罚。变量的范围将会影响到他们对应系数受到什么程度的惩罚。因为方差大的变量对应的系数很小,因此它们会受到较小的惩罚。因此,在使用上面的两个回归之前需要进行标准化。 +LASSO and Ridge regressions place a penalty on the magnitude of the coefficients associated to each variable. And the scale of variables will affect how much penalty will be applied on their coefficients. Because coefficients of variables with large variance are small and thus less penalized. Therefore, standardization is required before fitting both regressions. -## 什么时候不需要标准化? +## Cases when standardization is not needed? -**逻辑回归和树形模型** +**Logistic Regression and Tree based models** -逻辑回归,树形模型(决策树,随机森林)和梯度提升树对于变量的大小并不敏感。所以数据标准化在这里并不必要。 +Logistic Regression and Tree based algorithms such as Decision Tree, Random forest and gradient boosting, are not sensitive to the magnitude of variables. So standardization is not needed before fitting this kind of models. -## 结论 +## Conclusion -综上所述,正确使用数据标准化的时机取决于你当前在使用什么模型,你想用模型达到怎么样的目的。因此,如果机器学习工程师想知道什么时候该进行数据标准化并建造一个成功的机器学习模型,理解机器学习算法的内在原理十分重要。 +As we saw in this post, when to standardize and when not to, depends on which model you want to use and what you want to do with it. So, it’s very important for a ML developer to understand the internal functioning of machine learning algorithms, to be able to know when to standardize data and to build a successful machine learning model. -> 注:这篇文章并没有列出所有需要标准化的模型和方法。 +> N.B: The list of models and methods when standardization is required, presented in this post is not exhaustive. -### 参考文献: +### References: * [**365DataScience.com**]: Explaining Standardization Step-By-Step * [**Listendata.com** ]: when and why to standardize a variable From c5cebbbeaa0b51a262d933261304905da342d076 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 04/58] =?UTF-8?q?Revert=20"=E6=95=B0=E6=8D=AE=E7=A7=91?= =?UTF-8?q?=E5=AD=A6=E5=AE=B6=E9=9C=80=E8=A6=81=E6=8E=8C=E6=8F=A1=E7=9A=84?= =?UTF-8?q?=E5=8D=81=E7=A7=8D=E7=BB=9F=E8=AE=A1=E6=8A=80=E6=9C=AF=20(#6194?= =?UTF-8?q?)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 88b4ca46736d76a2ed0e2689f5d6b62ee33544e5. --- ...chniques-data-scientists-need-to-master.md | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md b/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md index 86be6e7888c..067774a2f0d 100644 --- a/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md +++ b/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md @@ -2,152 +2,152 @@ > * 原文作者:[James Le](https://medium.com/@james_aka_yale) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md) -> * 译者:[HearFishle](https://github.com/hearfishle) -> * 校对者:[mymmon](https://github.com/mymmon), [hu7may](https://github.com/hu7may) +> * 译者: +> * 校对者: -# 数据科学家需要掌握的十种统计技术 +# The 10 Statistical Techniques Data Scientists Need to Master ![](https://cdn-images-1.medium.com/max/3840/1*itOusDBOUogAV1QbNaj4cQ.png) -无论你在数据科学是否“性感“的问题上站定何种立场,都无法忽略一个事实:数据,和我们分析数据、组织数据、确定数据上下文关系的能力正在越来越重要。凭借庞大的就业数据和员工反馈,Glassdoor(一家美国的求职社区,译者注)将数据科学家排在全美最佳的 [25 个职位](https://www.glassdoor.com/Best-Jobs-in-America-LST_KQ0,20.htm)中的第一名。因此,虽然这个角色会依然存在,但毫无疑问,数据科学家们所做的具体任务将会不断进化。随着像机器学习这样的技术的普及,还有像深度学习这样的新兴领域,获得了来自研究人员和工程师们及他们所在的公司的巨大关注,数据科学家们将继续在创新和科技进步的浪潮中乘风破浪。 +Regardless of where you stand on the matter of Data Science sexiness, it’s simply impossible to ignore the continuing importance of data, and our ability to analyze, organize, and contextualize it. Drawing on their vast stores of employment data and employee feedback, Glassdoor ranked Data Scientist #1 in their [25 Best Jobs in America](https://www.glassdoor.com/Best-Jobs-in-America-LST_KQ0,20.htm) list. So the role is here to stay, but unquestionably, the specifics of what a Data Scientist does will evolve. With technologies like Machine Learning becoming ever-more common place, and emerging fields like Deep Learning gaining significant traction amongst researchers and engineers — and the companies that hire them — Data Scientists continue to ride the crest of an incredible wave of innovation and technological progress. -尽管拥有强悍的编程能力是重要的,但数据科学并不完全是软件工程(事实上,熟悉Python的话会更容易展开工作)。数据科学家需要的是编程,分析和关键性思考的三重能力。正如 Josh Wills [所言](https://www.quora.com/What-is-the-difference-between-a-data-scientist-and-a-statistician),“**数据科学家具备比任何编程人员都更丰富的统计学知识,和比任何统计学家都强的编程能力**。”据我个人了解,太多的软件工程师想转行成为数据科学家。他们在没有完全理解数据科学理论的情况下就盲目利用机器学习框架如 TensorFlow 或者 Apache Spark 去处理数据。他们对待[统计学习](https://en.wikipedia.org/wiki/Statistical_learning_theory)这个基于统计学和泛函分析的机器学习理论框架,也是如此。 +While having a strong coding ability is important, data science isn’t all about software engineering (in fact, have a good familiarity with Python and you’re good to go). Data scientists live at the intersection of coding, statistics, and critical thinking. [As Josh Wills](https://www.quora.com/What-is-the-difference-between-a-data-scientist-and-a-statistician) put it, **“data scientist is a person who is better at statistics than any programmer and better at programming than any statistician.”** I personally know too many software engineers looking to transition into data scientist and blindly utilizing machine learning frameworks such as TensorFlow or Apache Spark to their data without a thorough understanding of statistical theories behind them. So comes the study of [statistical learning](https://en.wikipedia.org/wiki/Statistical_learning_theory), a theoretical framework for machine learning drawing from the fields of statistics and functional analysis. -**为什么要学习统计学习理论?** 理解多种技术背后的思想是很重要的,这样便于知道如何以及何时使用它们。为了掌握更复杂的方法,人们必须先理解更简单的方法。准确评估方法的性能是非常重要的,这让我们确定工作是否正常进行。并且,这是个令人兴奋的研究领域,在科技,工业和金融行业都有这非常重要的应用。归根结底,统计学习是现代数据科学家培训的基本要素。统计学习问题的例子包括有: +**Why study Statistical Learning?** It is important to understand the ideas behind the various techniques, in order to know how and when to use them. One has to understand the simpler methods first, in order to grasp the more sophisticated ones. It is important to accurately assess the performance of a method, to know how well or how badly it is working. Additionally, this is an exciting research area, having important applications in science, industry, and finance. Ultimately, statistical learning is a fundamental ingredient in the training of a modern data scientist. Examples of Statistical Learning problems include: -* 确定引起前列腺癌的危险因素。 -* 根据对数周期图对录制的音素进行分类。 -* 根据人口统计学、饮食和临床测量预测某人是否会心脏病发作。 -* 自定义电子邮件垃圾邮件检测系统。 -* 识别手写的邮政编码。 -* 将组织样本分类到几种癌症中的一种。 -* 在人口调查数据中建立薪水和人口统计变量之间的关系。 +* Identify the risk factors for prostate cancer. +* Classify a recorded phoneme based on a log-periodogram. +* Predict whether someone will have a heart attack on the basis of demographic, diet and clinical measurements. +* Customize an email spam detection system. +* Identify the numbers in a handwritten zip code. +* Classify a tissue sample into one of several cancer classes. +* Establish the relationship between salary and demographic variables in population survey data. -在大学的最后一个学期,我自学了数据挖掘。这门课的材料涵盖了这三本书的内容:[Intro to Statistical Learning](http://www-bcf.usc.edu/~gareth/ISL/) (Hastie, Tibshirani, Witten, James),[Doing Bayesian Data Analysis](https://sites.google.com/site/doingbayesiandataanalysis/)(Kruschke)和 [Time Series Analysis and Applications](http://www.stat.pitt.edu/stoffer/tsa4/)(Shumway,Stoffer)。我做了大量和贝叶斯分析,马尔可夫链,分层建模,监督和无监督学习相关的练习。这个经历加深了我对数据挖掘学术领域的兴趣,并使我确信要向更深处探索。最近,我在 Stanford Lagunita 自学了 [Statistical Learning online course](https://lagunita.stanford.edu/courses/HumanitiesSciences/StatLearning/Winter2016/about),它涵盖了 [**Intro to Statistical Learning book**](https://www.amazon.com/Introduction-Statistical-Learning-Applications-Statistics/dp/1461471370) 的全部材料。两次接触这些内容,我想分享这本书中的 10 种统计技术,我想任何数据科学家都应该学会这些技术,以便更有效地处理大数据集。 +In my last semester in college, I did an Independent Study on Data Mining. The class covers expansive materials coming from 3 books: [Intro to Statistical Learning](http://www-bcf.usc.edu/~gareth/ISL/) (Hastie, Tibshirani, Witten, James), [Doing Bayesian Data Analysis](https://sites.google.com/site/doingbayesiandataanalysis/) (Kruschke), and [Time Series Analysis and Applications](http://www.stat.pitt.edu/stoffer/tsa4/) (Shumway, Stoffer). We did a lot of exercises on Bayesian Analysis, Markov Chain Monte Carlo, Hierarchical Modeling, Supervised and Unsupervised Learning. This experience deepens my interest in the Data Mining academic field and convinces me to specialize further in it. Recently, I completed the [Statistical Learning online course](https://lagunita.stanford.edu/courses/HumanitiesSciences/StatLearning/Winter2016/about) on Stanford Lagunita, which covers all the material in the [**Intro to Statistical Learning book**](https://www.amazon.com/Introduction-Statistical-Learning-Applications-Statistics/dp/1461471370) I read in my Independent Study. Now being exposed to the content twice, I want to share the 10 statistical techniques from the book that I believe any data scientists should learn to be more effective in handling big datasets. -在开始介绍这十种技术之前,我想先区分一下统计学习和机器学习。之前我写了[机器学习中最流行的方法之一](https://gab41.lab41.org/the-10-algorithms-machine-learning-engineers-need-to-know-f4bb63f5b2fa)因此我非常自信我有能力去判断它们的差异: +Before moving on with these 10 techniques, I want to differentiate between statistical learning and machine learning. I wrote [one of the most popular Medium posts on machine learning](https://gab41.lab41.org/the-10-algorithms-machine-learning-engineers-need-to-know-f4bb63f5b2fa) before, so I am confident I have the expertise to justify these differences: -* 机器学习是人工智能的一个分支。 -* 统计学习是统计学的一个分支。 -* 机器学习非常强调大数据和预测精确度。 -* 统计学习强调模型以及它的可解释性,精确性和不确定性。 -* 但是两者的界限在变得模糊,并且还有大量的“学科交叉”。 -* 机器学习更有市场! +* Machine learning arose as a subfield of Artificial Intelligence. +* Statistical learning arose as a subfield of Statistics. +* Machine learning has a greater emphasis on large scale applications and prediction accuracy. +* Statistical learning emphasizes models and their interpretability, and precision and uncertainty. +* But the distinction has become and more blurred, and there is a great deal of “cross-fertilization.” +* Machine learning has the upper hand in Marketing! -## 1 — 线性回归: +## 1 — Linear Regression: -在统计学中,线性回归是一种通过拟合自变量和因变量之间的**最优线性函数**去预测目标变量的方法。当拟合每个点得到的值和实际观测值的距离之和最小时,我们就可以认定**最佳拟合**了。在选择形状时,在没有其他的位置会产生更少的误差的情况下,说明这个形状的拟合是”最好“的。两种主要的线性回归是**简单线性回归**和 **多元线性回归**。**简单线性回归** 通过拟合一个最优线性关系,使用单自变量去预测一个因变量。**多元线性回归**则是通过拟合一个最优线性函数,使用不止一个自变量去预测因变量。 +In statistics, linear regression is a method to predict a target variable by fitting the **best linear relationship** between the dependent and independent variable. The **best fit** is done by making sure that the sum of all the distances between the shape and the actual observations at each point is as small as possible. The fit of the shape is “best” in the sense that no other position would produce less error given the choice of shape. 2 major types of linear regression are **Simple Linear Regression** and **Multiple Linear Regression**. **Simple Linear Regression **uses a single independent variable to predict a dependent variable by fitting a best linear relationship.** Multiple Linear Regression** uses more than one independent variable to predict a dependent variable by fitting a best linear relationship. ![](https://cdn-images-1.medium.com/max/4328/1*KwdVLH5e_P9h8hEzeIPnTg.png) -可以选择你生活中的任意两个有关系的事物。比如,我有过去三年我每个月收入和支出以及出行的数据。现在我要回答如下问题: +Pick any 2 things that you use in your daily life and that are related. Like, I have data of my monthly spending, monthly income and the number of trips per month for the last 3 years. Now I need to answer the following questions: -* 我下一年的月支出将是多少? -* 哪个因素(月收入或者月出行次数)在决定我的月支出中更加重要? -* 月收入和月度出行次和月支出有怎样的相关关系?? +* What will be my monthly spending for next year? +* Which factor (monthly income or number of trips per month) is more important in deciding my monthly spending? +* How monthly income and trips per month are correlated with monthly spending? -## 2 — 分类: +## 2 — Classification: -分类是一种数据挖掘技术,它为数据集合分好类,以帮助进行更准确的预测和分析。分类有时候也被称为决策树方法,是有效分析大型数据集的几种方法之一。两种脱颖而出的主要的分类技术是**逻辑回归**和**判别分析**。 +Classification is a data mining technique that assigns categories to a collection of data in order to aid in more accurate predictions and analysis. Also sometimes called a Decision Tree, classification is one of several methods intended to make the analysis of very large datasets effective. 2 major Classification techniques stand out: **Logistic Regression** and **Discriminant Analysis****.** -当因变量是对立的(二元)时,**逻辑回归**是适当的回归分析方法。和所有的回归分析类似,逻辑回归是一种预测分析。逻辑回归用于描述数据,并解释一个因变量与一个或多个定类、定序、定距或定比自变量之间的关系。逻辑回归可以校验的问题有: +**Logistic Regression** is the appropriate regression analysis to conduct when the dependent variable is dichotomous (binary). Like all regression analyses, the logistic regression is a predictive analysis. Logistic regression is used to describe data and to explain the relationship between one dependent binary variable and one or more nominal, ordinal, interval or ratio-level independent variables. Types of questions that a logistic regression can examine: -* 每天每增加一磅体重和每多抽一包烟,患肺癌的几率(是与否)会有怎么样的变化? -* 体重、卡路里摄入量、脂肪摄入量和参与者年龄对心脏病发作有影响吗(是与否)? +* How does the probability of getting lung cancer (Yes vs No) change for every additional pound of overweight and for every pack of cigarettes smoked per day? +* Do body weight calorie intake, fat intake, and participant age have an influence on heart attacks (Yes vs No)? ![](https://cdn-images-1.medium.com/max/2000/1*_jCbRluq1_g89LhNgIujLg.png) -在**判别分析**中,两个或者更多的组或群或总体是已知先验的,而根据分析的特征,1个或者更多的观测值被划分进入已知的某一类簇中。判别分析模拟了预测因子 X 在每个响应类别中的分布,然后使用贝叶斯定理将其转换为给定 X 值的响应类别的概率估计值。这些模型可以是**线性的**,也可以是**二次的** 。 +In **Discriminant Analysis**, 2 or more groups or clusters or populations are known a priori and 1 or more new observations are classified into 1 of the known populations based on the measured characteristics. Discriminant analysis models the distribution of the predictors X separately in each of the response classes, and then uses Bayes’ theorem to flip these around into estimates for the probability of the response category given the value of X. Such models can either be **linear** or **quadratic****.** -* **线性判别分析**通过计算每个观测值的“判别分数”来对观测值进行响应变量类的归类。这些分数是通过寻找自变量的线性组合得到的。它假设每个类中的观测值都是来自一个多元高斯分布,并且预测变量的协方差在响应变量 Y 的 k 个级别上都是相同的。 -* **二次判别分析**提供了另一种方法。和 LDA 一样,QDA 假设每一类 Y 的观测值都来自高斯分布。而不同于 LDA 的是,QDA 假设每一类都有它自己的协方差矩阵。换句话说,预测变量的协方差并不假设为在响应变量 Y 的 k 个级别上都是相同的。 +* **Linear Discriminant Analysis** computes “discriminant scores” for each observation to classify what response variable class it is in. These scores are obtained by finding linear combinations of the independent variables. It assumes that the observations within each class are drawn from a multivariate Gaussian distribution and the covariance of the predictor variables are common across all k levels of the response variable Y. +* **Quadratic Discriminant Analysis** provides an alternative approach. Like LDA, QDA assumes that the observations from each class of Y are drawn from a Gaussian distribution. However, unlike LDA, QDA assumes that each class has its own covariance matrix. In other words, the predictor variables are not assumed to have common variance across each of the k levels in Y. -## 3 — 重采样方法: +## 3 — Resampling Methods: -重采样是指从原始数据样本中提取重复样本的方法。它是一种统计推断的非参数方法。换言之,重采样方法不涉及使用通用分布表来计算近似的 p 的概率值。 +Resampling is the method that consists of drawing repeated samples from the original data samples. It is a non-parametric method of statistical inference. In other words, the method of resampling does not involve the utilization of the generic distribution tables in order to compute approximate p probability values. -重采样基于实际数据生成一个唯一的抽样分布。它使用实验方法而非分析方法来生成这个唯一的抽样分布。它基于研究员所研究的所有可能结果的无偏样本来产生无偏估计。为了理解重采样的概念,你应该了解**自举法(也翻译成拔靴法,译者注)**和**交叉验证**: +Resampling generates a unique sampling distribution on the basis of the actual data. It uses experimental methods, rather than analytical methods, to generate the unique sampling distribution. It yields unbiased estimates as it is based on the unbiased samples of all the possible results of the data studied by the researcher. In order to understand the concept of resampling, you should understand the terms **Bootstrapping** and **Cross-Validation**: ![](https://cdn-images-1.medium.com/max/2000/1*SebBhTd29KMJ25JfPn2QgA.png) -* **自举法** 应用于多种场景,如验证预测性模型的表现,集成方法,偏差估计和模型方差。它的工作原理是在原始数据中执行有放回的数据抽样,使用 “**未选中**” 的数据点作为测试样例。我们可以多次执行并且计算均值来评估我们模型的性能。 -* 另一方面,交叉验证用于验证模型性能,并通过将训练数据分成 k 部分来执行。我们将前 k-1 部分作为训练集,“**留出**” 的部分作为测试集。用不同的方法重复这个步骤 k 次,最后将 k 次分值的均值用作性能评估。 +* **Bootstrapping** is a technique that helps in many situations like validation of a predictive model performance, ensemble methods, estimation of bias and variance of the model. It works by sampling with replacement from the original data, and take the “**not chosen**” data points as test cases. We can make this several times and calculate the average score as estimation of our model performance. +* On the other hand, **cross validation** is a technique for validating the model performance, and it’s done by split the training data into k parts. We take the k — 1 parts as our training set and use the “**held out**” part as our test set. We repeat that k times differently. Finally, we take the average of the k scores as our performance estimation. -通常,对于线性模型来说,普通最小二乘法是拟合数据时考虑的主要标准。下面三个方法可以替代它并且能够提供更好的预测准确率和拟合线性模型的可解释性。 +Usually for linear models, ordinary least squares is the major criteria to be considered to fit them into the data. The next 3 methods are the alternative approaches that can provide better prediction accuracy and model interpretability for fitting linear models. -## 4 — 子集选择: +## 4 — Subset Selection: -此方法确定被我们认为与响应相关的 **p** 个预测因子的一个子集。然后我们利用子集特征的最小二乘来拟合模型。 +This approach identifies a subset of the **p** predictors that we believe to be related to the response. We then fit a model using the least squares of the subset features. ![](https://cdn-images-1.medium.com/max/2000/1*R1tdVlwJX-N1qnFLzGp0pQ.png) -* **最优子集选择:** 这里,我们为 **p** 个预测因子的每个可能的组合分别拟合一个 OLS 回归,然后观察每个模型的拟合效果。该算法有两个阶段:(1)拟合包含 k 个预测因子的所有模型,其中 k 为模型的最大长度。(2)使用交叉验证预测损失来选择单个模型。重要的是使用 **验证** 或 **测试误差,** 而且不能简单地使用训练误差评估模型的拟合情况,因为 RSS 和 R² 随变量的增加而单调递增。最好的方法就是选择测试集中最高的 R² 和最低的 RSS 来选择模型并进行交叉验证。 -* **前向逐步选择**研究的是一个小得多的 p 个预测因子的子集。它从不含预测因子的模型开始,逐步添加预测因子到模型中,直到所有预测因子都包含在模型中。添加预测因子的顺序是根据不同变量对模型拟合性能提升的程度确定的,会一直添加变量,直到再没有预测因子能在交叉验证误差中提升模型。 -* **后向逐步选择**一开始就在模型内加入所有的 p 个预测因子,然后每次迭代都移除一个最无用的因子。 -* **混合法**遵循前向逐步方法。但是在添加每个新的变量之后,该方法也可能会移除那些对模型拟合无用的变量。 +* **Best-Subset Selection:** Here we fit a separate OLS regression for each possible combination of the **p** predictors and then look at the resulting model fits. The algorithm is broken up into 2 stages: (1) Fit all models that contain **k** predictors, where **k** is the max length of the models, (2) Select a single model using cross-validated prediction error. It is important to use **testing** or **validation error,** and not training error to assess model fit because RSS and R² monotonically increase with more variables. The best approach is to cross-validate and choose the model with the highest R² and lowest RSS on testing error estimates. +* **Forward Stepwise Selection** considers a much smaller subset of **p** predictors. It begins with a model containing no predictors, then adds predictors to the model, one at a time until all of the predictors are in the model. The order of the variables being added is the variable, which gives the greatest addition improvement to the fit, until no more variables improve model fit using cross-validated prediction error. +* **Backward Stepwise Selection** begins will all **p** predictors in the model, then iteratively removes the least useful predictor one at a time. +* **Hybrid Methods** follows the forward stepwise approach, however, after adding each new variable, the method may also remove variables that do not contribute to the model fit. -## 5 — 特征缩减: +## 5 — Shrinkage: -这种方法适合包含所有 p 个预测因子的模型。然而,估计系数将根据最小二乘的估值向零收敛。这种收缩也称之为正则化。它旨在减少方差以防止模型的过拟合。由于我们使用不同的收敛方法,有些系数将被估计为零。因此这种方法也能执行变量的选择,将变量收敛为零最想见的技术就是**岭回归**和 **lasso** 回归。 +This approach fits a model involving all **p** predictors, however, the estimated coefficients are shrunken towards zero relative to the least squares estimates. This shrinkage, aka **regularization** has the effect of reducing variance. Depending on what type of shrinkage is performed, some of the coefficients may be estimated to be exactly zero. Thus this method also performs variable selection. The two best-known techniques for shrinking the coefficient estimates towards zero are the **ridge regression** and the **lasso**. ![](https://cdn-images-1.medium.com/max/2000/1*nlg3Mo5du17JV8VmLQ8H4g.jpeg) -* **岭回归**非常类似于最小二乘法,只不过它通过最小化一个稍微不同的数值来估计系数。岭回归和 OLS 一样寻求减少 RSS 的系数估计。但是当系数值接近零时,它们会对这种收缩进行惩罚。这个惩罚项具有将系数估计值缩小到趋近于零的效果。不需要数学运算,就能知道岭回归通过最小的列空间方差来收敛系数是很有用的,比如主成分分析中,岭回归将数据投射 **d** 方向空间中,并且相较于高方差成分,更多的收缩低方差的成分,这两者等同于最大主成分和最小主成分。 -* 岭回归至少有一个缺点,它需要在最终模型中包含所有的 **p** 个预测因子,这主要是因为罚项将会令很多预测因子的系数逼近零,但又一定不会等于零。这对于预测准确度来说通常并不是什么问题,但却令模型的结果更难以解释。**Lasso** 则克服了这一缺点,并且能够在 **s** 足够小的情况下使一些预测因子的系数归零。由于 **s** = 1 将导致正规的 OLS 回归,当 **s** 逼近 0 时,系数将收敛到零。因此 Lasso 回归同样是执行变量选择的一个好方法。 +* **Ridge regression** is similar to least squares except that the coefficients are estimated by minimizing a slightly different quantity. Ridge regression, like OLS, seeks coefficient estimates that reduce RSS, however they also have a shrinkage penalty when the coefficients come closer to zero. This penalty has the effect of shrinking the coefficient estimates towards zero. Without going into the math, it is useful to know that ridge regression shrinks the features with the smallest column space variance. Like in prinicipal component analysis, ridge regression projects the data into **d**directional space and then shrinks the coefficients of the low-variance components more than the high variance components, which are equivalent to the largest and smallest principal components. +* Ridge regression had at least one disadvantage; it includes all **p** predictors in the final model. The penalty term will set many of them close to zero, but never **exactly** to zero. This isn’t generally a problem for prediction accuracy, but it can make the model more difficult to interpret the results. **Lasso** overcomes this disadvantage and is capable of forcing some of the coefficients to zero granted that **s** is small enough. Since **s** = 1 results in regular OLS regression, as **s** approaches 0 the coefficients shrink towards zero. Thus, Lasso regression also performs variable selection. -## 6 — 降维: +## 6 — Dimension Reduction: - 降维算法将 **p + 1** 个系数的问题简化为 **M + 1** 个系数的问题,其中 **M < p**。算法执行包括计算变量的 **M** 个不同**线性组合**或**投影**(projection)。然后将这 **M** 个投影作为预测因子,并通过最小二乘法来拟合一个线性回归模型。两个处理方法是**主成分回归(principal component regression)** 和 **偏最小二乘法(partial least squares)**。 +Dimension reduction reduces the problem of estimating **p + 1** coefficients to the simple problem of **M + 1** coefficients, where **M \< p.** This is attained by computing **M** different **linear combinations,** or **projections,** of the variables. Then these **M** projections are used as predictors to fit a linear regression model by least squares. 2 approaches for this task are **principal component regression** and **partial least squares.** ![](https://cdn-images-1.medium.com/max/2000/1*WVFe7w1rzZWsmghdvaoXag.png) -* 主成分回归(PCR)可以看成一种从大型变量集合中导出低维特征集合的方法。数据中的第一主成分(first principal component)是指观测值沿着这个变量方向的变化最大。换言之,第一主成分是最接近拟合数据的线,总共可以用 p 个不同的主成分拟合。第二主成分是和第一主成分不相关的变量的线性组合,且在该约束下有最大的方差。其主要思想是主成分能在各个互相垂直的方向使用数据的线性组合捕捉到最大的方差。使用这种方法,我们还能结合相关变量的效应从数据中获取更多的信息,毕竟在常规的最小二乘法中需要舍弃其中一个相关变量。 -* 上面描述的 PCR 方法需要提取 X 的线性组合,以获得预测因子的最优表征。由于 **X** 的输出 **Y** 不能用于帮助决定主成分方向,这些组合(**方向**)使用无监督方法提取。即,**Y** 不能**监督**主成分的提取,从而无法保证这些方向是预测器的最优表征,也无法保证能获得最优预测输出(虽然通常假定如此)。**偏最小二乘法**(PLS)是一种**监督**方法,作为 PCR 的代替方法。和 PCR 类似,PLS 也是一种降维方法,它首先提取一个新的较小的特征集合(原始特征的线性组合),然后通过最小二乘法将原来的模型拟合为一个新的具有 M 个特征的线性模型。 +* One can describe **Principal Components Regression** as an approach for deriving a low-dimensional set of features from a large set of variables. The **first** principal component direction of the data is along which the observations vary the most. In other words, the first PC is a line that fits as close as possible to the data. One can fit **p** distinct principal components. The second PC is a linear combination of the variables that is uncorrelated with the first PC, and has the largest variance subject to this constraint. The idea is that the principal components capture the most variance in the data using linear combinations of the data in subsequently orthogonal directions. In this way, we can also combine the effects of correlated variables to get more information out of the available data, whereas in regular least squares we would have to discard one of the correlated variables. +* The PCR method that we described above involves identifying linear combinations of **X** that best represent the predictors. These combinations (**directions**) are identified in an unsupervised way, since the response **Y** is not used to help determine the principal component directions. That is, the response **Y** does not **supervise** the identification of the principal components, thus there is no guarantee that the directions that best explain the predictors also are the best for predicting the response (even though that is often assumed). **Partial least square**s (PLS) are a **supervised**alternative to PCR. Like PCR, PLS is a dimension reduction method, which first identifies a new smaller set of features that are linear combinations of the original features, then fits a linear model via least squares to the new **M** features. Yet, unlike PCR, PLS makes use of the response variable in order to identify the new features. -## 7 — 非线性回归: +## 7 — Nonlinear Models: -在统计学中,非线性回归属于一种观测数据使用模型参数的非线性组合的函数(依赖于一个或多个独立变量)建模的回归分析形式。其使用逐次逼近法拟合数据。下方是几种处理非线性模型的重要技术: +In statistics, nonlinear regression is a form of regression analysis in which observational data are modeled by a function which is a nonlinear combination of the model parameters and depends on one or more independent variables. The data are fitted by a method of successive approximations. Below are a couple of important techniques to deal with nonlinear models: -* **阶梯函数(step function)**,变量为实数,可以写成区间的指示函数的有限线性组合的形式。非正式的解释是,阶梯函数是一种分段常数函数,只有有限的部分。 -* **分段函数**(piecewise function)通过多个子函数定义,每一个子函数被定义在主函数定义域的确定的区间上。分段实际上是一种表示函数的方式,而不是函数自身的特征,但通过额外的限定条件,它可以用于描述函数的本质。例如,一个**分段多项式函数**是一个在每一个子定义上为多项式的函数,其中每一个多项式都可能是不同的。 +* A function on the real numbers is called a **step function** if it can be written as a finite linear combination of indicator functions of intervals. Informally speaking, a step function is a piecewise constant function having only finitely many pieces. +* A **piecewise function** is a function which is defined by multiple sub-functions, each sub-function applying to a certain interval of the main function’s domain. Piecewise is actually a way of expressing the function, rather than a characteristic of the function itself, but with additional qualification, it can describe the nature of the function. For example, a **piecewise polynomial** function is a function that is a polynomial on each of its sub-domains, but possibly a different one on each. ![](https://cdn-images-1.medium.com/max/2000/1*_vb4tu4Vvi8b2Rg7hzf5NQ.png) -* **样条曲线(spline)** 是一种用多项式分段定义的特殊函数。在计算机图形学中,样条曲线是一种分段多项式参数化曲线。由于结构的简单性、评估的简易和高精度、通过曲线拟合和交互曲线设计以逼近复杂曲线的能力,样条曲线很常用。 -* **广义加性模型(generalized additive model)** 是一种广义线性模型,其中线性预测器线性依赖于某些预测器变量的未知平滑函数,其主要作用就是推测这些平滑函数。 +* A **spline** is a special function defined piecewise by polynomials. In computer graphics, spline refers to a piecewise polynomial parametric curve. Splines are popular curves because of the simplicity of their construction, their ease and accuracy of evaluation, and their capacity to approximate complex shapes through curve fitting and interactive curve design. +* A **generalized additive model** is a generalized linear model in which the linear predictor depends linearly on unknown smooth functions of some predictor variables, and interest focuses on inference about these smooth functions. -## 8 — 基于树的方法: +## 8 — Tree-Based Methods: -基于树的方法可以用于回归和分类问题,包括将预测因子的空间分层或分割成几个简单区域。由于用于预测器空间的分离规则集合可以总结为一个树,这类方法被称为**决策树**方法。以下的方法是几种不同的树,它们可以组合起来输出单个一致的预测。 +Tree-based methods can be used for both regression and classification problems. These involve stratifying or segmenting the predictor space into a number of simple regions. Since the set of splitting rules used to segment the predictor space can be summarized in a tree, these types of approaches are known as **decision-tree** methods. The methods below grow multiple trees which are then combined to yield a single consensus prediction. -* **Bagging** 能减少预测的方差,即通过从原始数据中生成额外的数据(通过组合和重复生成和原始数据大小相同的多段数据)用于训练。通过增大训练集无法提高模型的预测能力,只能减小方差,仔细地调整预测以得到期望的输出。 -* **Boosting** 是一种使用多个不同的模型计算输出,然后使用加权平均方法对结果进行平均的方法。将这些方法的优点和缺点结合起来,通过改变加权公式,您可以使用不同的更细致的调优模型,对更广泛的输入数据产生良好的预测力。 +* **Bagging** is the way decrease the variance of your prediction by generating additional data for training from your original dataset using combinations with repetitions to produce multistep of the same carnality/size as your original data. By increasing the size of your training set you can’t improve the model predictive force, but just decrease the variance, narrowly tuning the prediction to expected outcome. +* **Boosting** is an approach to calculate the output using several different models and then average the result using a weighted average approach. By combining the advantages and pitfalls of these approaches by varying your weighting formula you can come up with a good predictive force for a wider range of input data, using different narrowly tuned models. ![](https://cdn-images-1.medium.com/max/2000/1*W70TAcPDXVexTL6JNED6OA.png) -* **随机森林算法(random forest algorithm)** 实际上和 bagging 算法很相似,都是对训练集提取随机 bootstrap 样本。不过,除了 bootstrap 样本以外,还可以提取特征的随机子集来训练单个树;而在 bagging 中,需要给每个树提供整个特征集。由于特征选择是随机的,相比常规的 bagging 算法,每个树之间更加独立,从而通常能获得更好的预测性能(得益于更好的方差—偏差权衡)。由于每个树只需要学习特征的一个子集,所以计算速度也更快。 +* The **random forest** algorithm is actually very similar to bagging. Also here, you draw random bootstrap samples of your training set. However, in addition to the bootstrap samples, you also draw a random subset of features for training the individual trees; in bagging, you give each tree the full set of features. Due to the random feature selection, you make the trees more independent of each other compared to regular bagging, which often results in better predictive performance (due to better variance-bias trade-offs) and it’s also faster, because each tree learns only from a subset of features. -## 9 — 支持向量机: +## 9 — Support Vector Machines: ![](https://cdn-images-1.medium.com/max/2000/1*MStS2dBWSZo8iJPiL2_uXg.png) -支持向量机(SVM)是一种常用的监督学习分类技术。通俗地说,它用于寻找对两类点集做出最佳分离的超平面(hyperplane,在 2D 空间中是线,在 3D 空间中是面,在高维空间中是超平面。更正式的说法是,一个超平面是一个 n 维空间的 n-1 维子空间)。而支持向量机是保留最大的间隔的分离超平面,因此本质上,它是一个约束最优化问题,其中支持向量机的间隔在约束下被最大化,从而完美地对数据进行分类(硬间隔分类器)。 +SVM is a classification technique that is listed under supervised learning models in Machine Learning. In layman’s terms, it involves finding the hyperplane (line in 2D, plane in 3D and hyperplane in higher dimensions. More formally, a hyperplane is n-1 dimensional subspace of an n-dimensional space) that best separates two classes of points with the maximum margin. Essentially, it is a constrained optimization problem where the margin is maximized subject to the constraint that it perfectly classifies the data (hard margin). -"支持"超平面的数据点被称为"支持向量"。在上图中,填充蓝色圆和两个填充方块就是支持向量。在两类数据不是线性可分的例子中,数据点将被投射到一个更高维空间中,使得数据变得线性可分。包含多个类别的数据点的问题可以分解成多个"一对一"(one-versus-one)或"一对剩余"(one-versus-rest)的二分类问题。 +The data points that kind of “support” this hyperplane on either sides are called the “support vectors”. In the above picture, the filled blue circle and the two filled squares are the support vectors. For cases where the two classes of data are not linearly separable, the points are projected to an exploded (higher dimensional) space where linear separation may be possible. A problem involving multiple classes can be broken down into multiple one-versus-one or one-versus-rest binary classification problems. -## 10 — 无监督学习: +## 10 — Unsupervised Learning: -目前为止,我们都只讨论过监督学习技术,其中数据分类都是已知的,且提供给算法的经验都是实体和其分类的关系。当数据的分类是未知的时候,就需要使用另一种技术了。它们被称为无监督的,因为它们需要自己去发现数据中的模式。聚类(clustring)是无监督学习的一种,其中数据将根据相关性被分为多个集群。下方是几种最常用的无监督学习算法: +So far, we only have discussed supervised learning techniques, in which the groups are known and the experience provided to the algorithm is the relationship between actual entities and the group they belong to. Another set of techniques can be used when the groups (categories) of data are not known. They are called unsupervised as it is left on the learning algorithm to figure out patterns in the data provided. Clustering is an example of unsupervised learning in which different data sets are clustered into groups of closely related items. Below is the list of most widely used unsupervised learning algorithms: ![](https://cdn-images-1.medium.com/max/2000/1*DwqQu4oiGTsa5L--DD0v6Q.jpeg) -* **主成分分析** 通过识别具备最大方差和互不相关的特征之间的线性连接,帮助生成数据集的低维表示。该线性降维技术有助于理解无监督学习中的隐变量交互。 -* **k-Means 聚类**:根据数据到集群中心的距离将其分成 k 个不同的集群。 -* **层次聚类**:通过创建集群树来构建集群的多级层次结构。 +* **Principal Component Analysis** helps in producing low dimensional representation of the dataset by identifying a set of linear combination of features which have maximum variance and are mutually un-correlated. This linear dimensionality technique could be helpful in understanding latent interaction between the variable in an unsupervised setting. +* **k-Means clustering**: partitions data into k distinct clusters based on distance to the centroid of a cluster. +* **Hierarchical clustering**: builds a multilevel hierarchy of clusters by creating a cluster tree. -这是一些基本统计技术的基本运用,它们可以帮助数据科学项目经理和/或执行人员更好地了解他们的数据科学团队的内部运行情况。事实上,一些数据科学团队纯粹是通过 python 和 R 语言库运行算法。他们中的大多数甚至不需要考虑基础的数学。但是,能够理解统计分析的基础知识可以为您的团队提供更好的方法。洞察最小的部分可以使操作和抽象更容易。希望本基础数据科学统计指南能给您一个很好的理解! +This was a basic run-down of some basic statistical techniques that can help a data science program manager and or executive have a better understanding of what is running underneath the hood of their data science teams. Truthfully, some data science teams purely run algorithms through python and R libraries. Most of them don’t even have to think about the math that is underlying. However, being able to understand the basics of statistical analysis gives your teams a better approach. Have insight into the smallest parts allows for easier manipulation and abstraction. I hope this basic data science statistical guide gives you a decent understanding! -**你可以从[我的 Github 源代码]获得所有讲座的幻灯片和 RStudio 课程(https://github.com/khanhnamle1994/statistical-learning)感谢你的热情回复。 +**P.S: You can get all the lecture slides and RStudio sessions from [my GitHub source code here](https://github.com/khanhnamle1994/statistical-learning). Thanks for the overwhelming response!** > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From cf1f5dc56ae6a405d22f33a88bd7fc19ca24ceaa Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 05/58] =?UTF-8?q?Revert=20"=E7=94=B1=E6=B5=85=E5=85=A5?= =?UTF-8?q?=E6=B7=B1=E7=90=86=E8=A7=A3=E4=B8=BB=E6=88=90=E5=88=86=E5=88=86?= =?UTF-8?q?=E6=9E=90=20(#6231)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 42503bff7c17ef295cb837b52d6c226408265f88. --- ...anation-of-principal-component-analysis.md | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md b/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md index 8289643df84..24570e92cba 100644 --- a/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md +++ b/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md @@ -2,141 +2,141 @@ > * 原文作者:[Zakaria Jaadi](https://medium.com/@zakaria.jaadi) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md](https://github.com/xitu/gold-miner/blob/master/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md) -> * 译者:[Ultrasteve](https://github.com/Ultrasteve) -> * 校对者:[kasheemlew](https://github.com/kasheemlew), [TrWestdoor](https://github.com/TrWestdoor) +> * 译者: +> * 校对者: -# 由浅入深理解主成分分析 +# A step by step explanation of Principal Component Analysis ![](https://cdn-images-1.medium.com/max/2360/0*MCObvpuCqWS5-z2m) -这篇文章的目的是对主成分分析(PCA)做一个完整且简单易懂的介绍,重点会一步一步的讲解它是怎么工作的。看完这篇文章后,相信即使没有很强的数学背景的人,都能理解并使用它。 +The purpose of this post is to provide a complete and simplified explanation of Principal Component Analysis, and especially to answer how it works step by step, so that everyone can understand it and make use of it, without necessarily having a strong mathematical background. -网上已经有很多介绍 PCA 的文章,其中一些质量也很高,但很少文章会直截了当的去介绍它是怎么工作的,通常它们会过度的拘泥于 PCA 背后的技术及原理。因此,我打算以我自己的方式,来向各位简单易懂的介绍 PCA 。 +PCA is actually a widely covered method on the web, and there are some great articles about it, but only few of them go straight to the point and explain how it works without diving too much into the technicalities and the ‘why’ of things. That’s the reason why i decided to make my own post to present it in a simplified way. -在解释 PCA 之前,这篇文章会先富有逻辑性的介绍 PCA 在每一步是做什么的,同时我们会简化其背后的数学概念。我们会讲到标准化,协方差,特征向量和特征值,但我们不会专注于如何计算它们。 +Before getting to the explanation, this post provides logical explanations of what PCA is doing in each step and simplifies the mathematical concepts behind it, as standardization, covariance, eigenvectors and eigenvalues without focusing on how to compute them. -## 什么是 PCA? +## So what is Principal Component Analysis ? -PCA 是一种降维方法,常用于对高维数据集作降维。它会将一个大的变量集合转化为更少的变量集合,同时保留大的变量集合中的大部分信息。 +Principal Component Analysis, or PCA, is a dimensionality-reduction method that is often used to reduce the dimensionality of large data sets, by transforming a large set of variables into a smaller one that still contains most of the information in the large set. -减少数据的维度天然会牺牲一些精度,但降维算法的诀窍是牺牲很少的精度进行简化。这是因为维度更小的数据能更容易被探索和可视化,在数据的分析和机器学习算法中,我们将不用去处理额外的变量,这让整个过程变得高效。 +Reducing the number of variables of a data set naturally comes at the expense of accuracy, but the trick in dimensionality reduction is to trade a little accuracy for simplicity. Because smaller data sets are easier to explore and visualize and make analyzing data much easier and faster for machine learning algorithms without extraneous variables to process. -总的来说,PCA 的中心思想十分简单 —— 减少数据集的变量数目,同时尽可能保留它的大部分信息。 +So to sum up, the idea of PCA is simple — reduce the number of variables of a data set, while preserving as much information as possible. -## 逐步解释 +## Step by step explanation -### 步骤一:标准化 +### Step 1: Standardization -为了让每一个维度对分析的结果造成同样的影响,我们需要对连续的初始变量的范围作标准化。 +The aim of this step is to standardize the range of the continuous initial variables so that each one of them contributes equally to the analysis. -更具体的说,在 PCA 之前作数据标准化的原因是,后续的结果对数据的方差十分敏感。也就是说,那些取值范围较大的维度会比相对较小的维度造成更大的影响(例如,一个在 1 到 100 之间变化的维度对结果的影响,比一个 0 到 1 的更大),这会导致一个偏差较大的结果。所以,将数据转化到比较的范围可以预防这个问题。 +More specifically, the reason why it is critical to perform standardization prior to PCA, is that the latter is quite sensitive regarding the variances of the initial variables. That is, if there are large differences between the ranges of initial variables, those variables with larger ranges will dominate over those with small ranges (For example, a variable that ranges between 0 and 100 will dominate over a variable that ranges between 0 and 1), which will lead to biased results. So, transforming the data to comparable scales can prevent this problem. -从数学上来讲,我们可以通过减去数据的平均值并除以它的标准差来进行数据标准化。 +Mathematically, this can be done by subtracting the mean and dividing by the standard deviation for each value of each variable. ![](https://cdn-images-1.medium.com/max/2000/0*AgmY9auxftS9BI73.png) -一旦我们完成数据标准化,所有的数据会在同一个范围内。 +Once the standardization is done, all the variables will be transformed to the same scale. *** -如果你想更深入的了解数据标准化,我推荐你阅读我写的这篇小短文。 +if you want to get an in-depth understanding about standardization, i invite you to read this simple article i wrote about it. -* [**什么时候进行数据标准化?为什么?一篇简单的指南教你是否应该标准化你的数据。**](https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md) +* [**When and why to standardize your data ? A simple guide on when to standardize your data and when not to.**](https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md) -### 步骤二:计算协方差矩阵 +### Step 2: Covariance Matrix computation -这一步的目标是理解数据集中的变量是如何从平均值变化过来的,不同的特征之间又有什么关系。换句话说,我们想要看看特征之间是否存在某种联系。有时特征之间高度相关,因此会有一些冗余的信息。为了了解这一层关系,我们需要计算协方差矩阵。 +The aim of this step is to understand how the variables of the input data set are varying from the mean with respect to each other, or in other words, to see if there is any relationship between them. Because sometimes, variables are highly correlated in such a way that they contain redundant information. So, in order to identify these correlations, we compute the covariance matrix. -协方差矩阵是一个 **p** × **p** 的对称矩阵(**p** 是维度的数量)它涵盖了数据集中所有元组对初始值的协方差。例如,对于一个拥有三个变量 **x**、**y**、**z** 和三个维度的数据集,协方差矩阵将是一个 3 × 3 的矩阵: +The covariance matrix is a **p** × **p**** **symmetric matrix (where** p **is the number of dimensions) that has as entries the covariances associated with all possible pairs of the initial variables. For example, for a 3-dimensional data set with 3 variables** x**,** y**, and** z**, the covariance matrix is a 3×3 matrix of this from: -![三个维度数据的协方差矩阵](https://cdn-images-1.medium.com/max/2000/0*xTLQtW2XQY6P3mZf.png) +![Covariance matrix for 3-dimensional data](https://cdn-images-1.medium.com/max/2000/0*xTLQtW2XQY6P3mZf.png) -由于变量与自身的协方差等于它的方差(Cov(a,a)=Var(a)),在主对角线(左上到右下)上我们已经计算出各个变量初始值的方差。又因为协方差满足交换律(Cov(a,b)=Cov(b,a)),协方差矩阵的每一个元组关于主对角线对称,这意味着上三角部分和下三角部分是相等的。 +Since the covariance of a variable with itself is its variance (Cov(a,a)=Var(a)), in the main diagonal (Top left to bottom right) we actually have the variances of each initial variable. And since the covariance is commutative (Cov(a,b)=Cov(b,a)), the entries of the covariance matrix are symmetric with respect to the main diagonal, which means that the upper and the lower triangular portions are equal. -**协方差矩阵中的元素告诉了我们变量间什么样的关系呢?** +**What do the covariances that we have as entries of the matrix tell us about the correlations between the variables?** -让我们来看看协方差取值的含义: +It’s actually the sign of the covariance that matters : -* 如果值为正:那么两个变量呈正相关(同增同减) -* 如果值为负数:那么两个变量呈负相关(增减相反) +* if positive then : the two variables increase or decrease together (correlated) +* if negative then : One increases when the other decreases (Inversely correlated) -现在,我们知道了协方差矩阵不仅仅是对于变量之间的协方差的总结,让我们进入到下一步吧。 +Now, that we know that the covariance matrix is not more than a table that summaries the correlations between all the possible pairs of variables, let’s move to the next step. -### 步骤三:通过计算协方差矩阵的特征向量和特征值来计算出主成分 +### Step 3: Compute the eigenvectors and eigenvalues of the covariance matrix to identify the principal components -特征值和特征向量是线性代数里面的概念,为了计算出数据的**主成分**,我们需要通过协方差矩阵来计算它们。在解释如何计算这两个值之前,让我们来看看主成分的意义是什么。 +Eigenvectors and eigenvalues are the linear algebra concepts that we need to compute from the covariance matrix in order to determine the **principal components** of the data. Before getting to the explanation of these concepts, let’s first understand what do we mean by principal components. -主成分是一个新的变量,它是初始变量的线性组合。这些新的变量之间是不相关的。第一主成分中包含了初始变量的大部分信息,是初始变量的压缩和提取。例如,虽然在一个 10 维的数据集中我们算出了 10 个主成分,但大部分的信息都会被压缩在第一主成分中,剩下的大部分信息又被压缩到第二主成分中,以此类推,我们得到了下面这张图: +Principal components are new variables that are constructed as linear combinations or mixtures of the initial variables. These combinations are done in such a way that the new variables (i.e., principal components) are uncorrelated and most of the information within the initial variables is squeezed or compressed into the first components. So, the idea is 10-dimensional data gives you 10 principal components, but PCA tries to put maximum possible information in the first component, then maximum remaining information in the second and so on, until having something like shown in the scree plot below. -![每一个主成分包含着多少信息](https://cdn-images-1.medium.com/max/2304/1*JLAVaWW5609YZoJ-NYkSOA.png) +![Percentage of variance (information) for by each PC](https://cdn-images-1.medium.com/max/2304/1*JLAVaWW5609YZoJ-NYkSOA.png) -这种通过主成分来管理信息的方式,能够使我们降维的同时不会损失很多信息,同时还帮我们排除了那些信息量很少的变量。如此一来,我们就只用考虑那些主成分中压缩过的信息就可以了。 +Organizing information in principal components this way, will allow you to reduce dimensionality without losing much information, and this by discarding the components with low information and considering the remaining components as your new variables. -需要注意的一点是,这些主成分是难以解读的,由于它们是原变量的线性组合,通常它们没有实际的意义。 +An important thing to realize here is that, the principal components are less interpretable and don’t have any real meaning since they are constructed as linear combinations of the initial variables. -从理论方面来说,主成分代表着蕴含**最大方差的方向**。对于主成分来说,变量的方差越大,空间中点就越分散,空间中的点越分散,那么它包含的信息就越多。简单的讲,主成分就是一条更好的阐述数据信息的新坐标轴,因此我们更容易从中观测到差异。 +Geometrically speaking, principal components represent the directions of the data that explain a **maximal amount of variance**, that is to say, the lines that capture most information of the data. The relationship between variance and information here, is that, the larger the variance carried by a line, the larger the dispersion of the data points along it, and the larger the dispersion along a line, the more the information it has. To put all this simply, just think of principal components as new axes that provide the best angle to see and evaluate the data, so that the differences between the observations are better visible. -### PCA 算法是怎么算出主成分的? +### How PCA constructs the Principal Components? -有多少个变量就有多少个主成分。对于第一主成分来说沿着对应的坐标轴变化意味着有**最大的方差**。例如,我们将数据集用下列的散点图表示,现在你能够直接猜测出主成分应该是沿着哪一个方向的吗?这很简单,大概是图中紫色线的方向。因为它穿过了原点,而且数据映射在这条线上后,如红点所示,有着最大的方差(各点与原点距离的均方)。 +As there are as many principal components as there are variables in the data, principal components are constructed in such a manner that the first principal component accounts for the **largest possible variance** in the data set. For example, let’s assume that the scatter plot of our data set is as shown below, can we guess the first principal component ? Yes, it’s approximately the line that matches the purple marks because it goes through the origin and it’s the line in which the projection of the points (red dots) is the most spread out. Or mathematically speaking, it’s the line that maximizes the variance (the average of the squared distances from the projected points (red dots) to the origin). ![](https://cdn-images-1.medium.com/max/2000/1*UpFltkN-kT9aGqfLhOR9xg.gif) -第二主成分也是这样计算的,它与第一主成分互不相关(即互为垂直),表示了下一个方差最大的方向。 +The second principal component is calculated in the same way, with the condition that it is uncorrelated with (i.e., perpendicular to) the first principal component and that it accounts for the next highest variance. -我们重复以上步骤直到我们从原始数据中计算出所有主成分。 +This continues until a total of p principal components have been calculated, equal to the original number of variables. -现在我们知道了主成分的含义,让我们回到特征值和特征向量。你需要知道的是,它们通常成对出现,每一个特征向量对应一个特征值。它们各自的数量相等,等于原始数据的维度。例如,在一个三维数据集中,我们有三个变量,因此我们会有三个特征向量与三个特征值。 +Now that we understood what we mean by principal components, let’s go back to eigenvectors and eigenvalues. What you firstly need to know about them is that they always come in pairs, so that every eigenvector has an eigenvalue. And their number is equal to the number of dimensions of the data. For example, for a 3-dimensional data set, there are 3 variables, therefore there are 3 eigenvectors with 3 corresponding eigenvalues. -简单地说,特征矩阵和特征向量就是主成分分析背后的秘密。协方差矩阵的特征向量其实就是一系列的坐标轴,将数据映射到这些坐标轴后,我们将得到**最大的方差**(这意味这更多的信息),它们就是我们要求的主成分。特征值其实就是特征向量的系数,它代表了每个特征向量**包含了多少信息量**。 +Without further ado, it is eigenvectors and eigenvalues who are behind all the magic explained above, because the eigenvectors of the Covariance matrix are actually **the** **directions of the axes where there is the most variance** (most information) and that we call Principal Components. And eigenvalues are simply the coefficients attached to eigenvectors, which give the **amount of variance carried in each Principal Component**. -你可以根据特征值的大小对特征向量作排序,你将知道哪一个是最重要的主成分,哪一个不是。 +By ranking your eigenvectors in order of their eigenvalues, highest to lowest, you get the principal components in order of significance. -**例如:** +**Example:** -现在我们有一个数据集,有两个变量两个维度 **x,y**,它们的特征值与特征向量如下所示: +let’s suppose that our data set is 2-dimensional with 2 variables **x,y** and that the eigenvectors and eigenvalues of the covariance matrix are as follows: ![](https://cdn-images-1.medium.com/max/2000/1*3OAdlot1vJcK6qzCePlq9Q.png) -如果我们从大到小的排序特征值,我们得到 λ1>λ2,这意味着我们需要的第一主成分(PC1)是 **v1** ,第二主成分(PC2)是 **v2**。 +If we rank the eigenvalues in descending order, we get λ1>λ2, which means that the eigenvector that corresponds to the first principal component (PC1) is **v1** and the one that corresponds to the second component (PC2) is **v2.** -在得到主成分后,我们将每个特征值除以特征值的和,这样我们就得到了一个百分数。在上面的例子中,我们可以看到 PC1 和 PC2 各自携带了 96% 和 4% 信息。 +After having the principal components, to compute the percentage of variance (information) accounted for by each component, we divide the eigenvalue of each component by the sum of eigenvalues. If we apply this on the example above, we find that PC1 and PC2 carry respectively 96% and 4% of the variance of the data. -### 步骤四:主成分向量 +### Step 4: Feature vector -正如我们在前面步骤所看到的,通过计算出特征向量并让他们根据特征值的降序排列,我们能知到每个主成分的重要性。在这一步中,我们将会讨论我们是应该保留最重要的几个主成分,还是保留所有主成分。在排除那些不需要的主成分后,剩下的我们称作**主成分向量**。 +As we saw in the previous step, computing the eigenvectors and ordering them by their eigenvalues in descending order, allow us to find the principal components in order of significance. In this step, what we do is, to choose whether to keep all these components or discard those of lesser significance (of low eigenvalues), and form with the remaining ones a matrix of vectors that we call **Feature vector**. -主成分向量仅仅是一个矩阵,里面有那些我们决定保留的特征向量。这是数据降维的第一步,因为如果我们只打算在 **n** 个中保留 **p** 个特征向量(成分),那么当我们把数据映射到这些新的坐标轴上时,最后数据将只有 **p** 个维度。 +So, the feature vector is simply a matrix that has as columns the eigenvectors of the components that we decide to keep. This makes it the first step towards dimensionality reduction, because if we choose to keep only **p** eigenvectors (components) out of **n**, the final data set will have only **p** dimensions. -**例如:** +**Example**: -继续看上一步的例子,我们可以只用 **v1** 和 **v2** 来形成主成分向量: +Continuing with the example from the previous step, we can either form a feature vector with both of the eigenvectors **v**1 and **v**2: ![](https://cdn-images-1.medium.com/max/2000/0*DwiYbyXZXvU20DjB.png) -因为 **v2** 没那么重要,我们丢弃掉它,只保留 **v1**: +Or discard the eigenvector **v**2, which is the one of lesser significance, and form a feature vector with **v**1 only: ![](https://cdn-images-1.medium.com/max/2000/0*YKNYKGQaNAYf6Iln.png) -丢弃掉 **v2** 会使结果降低一个维度,当然也会造成数据的损失。但由于 **v2** 只保留了 4% 的信息,这个损失时可以忽略不计的。因为我们保留了 **v1** ,我们仍然有 96% 的信息。 +Discarding the eigenvector **v2** will reduce dimensionality by 1, and will consequently cause a loss of information in the final data set. But given that **v**2 was carrying only 4% of the information, the loss will be therefore not important and we will still have 96% of the information that is carried by **v**1. *** -如我们在结果中所见,是否丢弃没有那么重要的成分完全取决于你。如果你只想根据主成分来重新表示数据,不想进行数据将维,那么丢弃掉不重要的成分是不必要的。 +So, as we saw in the example, it’s up to you to choose whether to keep all the components or discard the ones of lesser significance, depending on what you are looking for. Because if you just want to describe your data in terms of new variables (principal components) that are uncorrelated without seeking to reduce dimensionality, leaving out lesser significant components is not needed. -### 最后一步:将数据映射到新的主成分坐标系中 +### Last step : Recast the data along the principal components axes -在前一步中,除了标准化数据,你并没有对数据作任何改变。你仅仅是选取了主成分,形成了主成分向量,但原始数据仍然在用原来的坐标系表示。 +In the previous steps, apart from standardization, you do not make any changes on the data, you just select the principal components and form the feature vector, but the input data set remains always in terms of the original axes (i.e, in terms of the initial variables). -在这最后一步中,我们将使用那些从协方差矩阵中算出来的特征向量形成主成分矩阵,并将原始数据映射到主成分矩阵对应的坐标轴上 —— 这就叫做主成分分析。具体的做法便是用原数据矩阵的转置乘以主成分矩阵的转置。 +In this step, which is the last one, the aim is to use the feature vector formed using the eigenvectors of the covariance matrix, to reorient the data from the original axes to the ones represented by the principal components (hence the name Principal Components Analysis). This can be done by multiplying the transpose of the original data set by the transpose of the feature vector. ![](https://cdn-images-1.medium.com/max/2000/0*D02r0HjB8WtCq3Cj.png) *** -如果你喜欢这篇文章,请点击 👏 按钮。并转发让更多人看到!你也可以在下面留言。 +If you enjoyed this story, please click the 👏 button as many times as you think it deserves. And share to help others find it! Feel free to leave a comment below. -### 参考文献: +### References: * [**Steven M. Holland**, **Univ. of Georgia**]: Principal Components Analysis * [**skymind.ai**]: Eigenvectors, Eigenvalues, PCA, Covariance and Entropy From 9cd6e92876715a2763e4695f958bf1c5691e4190 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 06/58] Revert "Create what-do-flutter-package-users-need-findings-from-q2-user-survey.md (#6245)" This reverts commit a91216793f0aa8572b0c4749963a5d8887acc241. --- ...users-need-findings-from-q2-user-survey.md | 98 ------------------- 1 file changed, 98 deletions(-) delete mode 100644 TODO1/what-do-flutter-package-users-need-findings-from-q2-user-survey.md diff --git a/TODO1/what-do-flutter-package-users-need-findings-from-q2-user-survey.md b/TODO1/what-do-flutter-package-users-need-findings-from-q2-user-survey.md deleted file mode 100644 index 37a82d76155..00000000000 --- a/TODO1/what-do-flutter-package-users-need-findings-from-q2-user-survey.md +++ /dev/null @@ -1,98 +0,0 @@ -> * 原文地址:[What do Flutter package users need? Findings from Q2 user survey](https://medium.com/flutter/what-do-flutter-package-users-need-6ecba57ed1d6) -> * 原文作者:[Ja Young Lee](https://medium.com/@jayoung.lee) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/what-do-flutter-package-users-need-findings-from-q2-user-survey.md](https://github.com/xitu/gold-miner/blob/master/TODO1/what-do-flutter-package-users-need-findings-from-q2-user-survey.md) -> * 译者: -> * 校对者: - -# What do Flutter package users need? Findings from Q2 user survey - -![A word cloud made with the Q2 survey comments](https://cdn-images-1.medium.com/max/3200/0*JGPtcSX7QYbN8Dvn) - -> A word cloud made with the Q2 survey comments ☁️ (link to the original [image](https://raw.githubusercontent.com/timsneath/wordcloud_flutter/master/flutter_wordcloud.png), [code](https://github.com/timsneath/wordcloud_flutter)) - -We recently ran our sixth quarterly user survey and collected responses from over 7,000 Flutter users. We found that 92.5% of the respondents are satisfied or very satisfied, which is slightly higher than the [last quarter](https://medium.com/flutter/insights-from-flutters-first-user-survey-of-2019-3659b02303a5)! We are thrilled to see a consistent level of satisfaction with Flutter. In this article, we cover some deep-dive questions around Flutter’s ecosystem, because we recognize that helping the Flutter community grow the ecosystem is important. - ---- - -As of July, 2019, you can find over 2,800 Flutter-dependent packages published on [pub.dev](https://pub.dev). At the same time last year, there were about 350 Flutter-dependent packages available, showing tremendous growth! And this does not include the thousands of additional Dart packages that are compatible with Flutter apps. - -Even though the ecosystem has been exploding, we recognize that there is still plenty of work remaining to build an excellent ecosystem around the Flutter project. To better understand the needs and struggles of our users, we asked a number of questions related to Flutter’s ecosystem in this quarter’s survey. We’re sharing the results in this article to help package authors build more useful packages that serve the needs of more users. - -Overall, 80.6% of 5,250 respondents were either **very satisfied** or **somewhat satisfied** with the Flutter ecosystem. This is not bad, but at the same time, it is one of the lower-scoring parts of the survey. - -![Satisfaction with ecosystem](https://cdn-images-1.medium.com/max/2400/0*MjrAD-ZGebXA-xaX) - -![Overall satisfaction with Flutter](https://cdn-images-1.medium.com/max/2400/0*LDgXRVH9t_ZteWDV) - -When asked about the dissatisfaction with Flutter’s ecosystem, the reason selected by the most respondents was that “critical packages I need **do not exist** yet” (18%), which is perhaps to be expected for a relatively new technology. - -However, we are happy to find that our community is actively adding to the Flutter package ecosystem. 15% of respondents had experience developing packages for Flutter, and 59% had published their packages to pub.dev, the site for sharing packages written for Flutter and Dart apps. If you’ve written a package but have not published yet, you can read [Developing packages & plugins](https://flutter.dev/docs/development/packages-and-plugins/developing-packages) on [flutter.dev](http://flutter.dev), and contribute back to the Flutter community by publishing your package. It is not difficult — of those who had published to [pub.dev](http://pub.dev), 81% thought that it was **very easy** or **somewhat easy**. - -If you can’t decide which package to share with the Flutter community, visit the Flutter repository on GitHub and search for [issues labeled with “would be a good package”](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22would+be+a+good+package%22+sort%3Areactions-%2B1-desc) to see what has been requested. You can upvote your favorite requests to increase their visibility. - -![Reasons for dissatisfaction with Flutter’s ecosystem (a multiple choice question)](https://cdn-images-1.medium.com/max/3200/0*UdtJOiVqBwXOmDl_) - -However, there is an even better way to contribute to the ecosystem, if you’re interested in helping out. Note that all other reasons start with “critical packages I need do exist…”, meaning that package users were facing challenges even when packages exist. This tells us that we can improve the ecosystem by improving what is already there — by filing bugs, improving documentation, adding missing features, implementing support for the ‘other’ platform, adding tests, and so on. Consider finding a package that has potential but has not been loved enough and contribute towards it — with tests, bug reports, feature contributions, or examples! - -The most common reason for dissatisfaction with existing packages is that “they are not well **documented**” (17%). This is another area where the community can help. The survey question “What would you like done to improve your overall experience with the package ecosystem?” resulted in the following suggestions: - -* Include more diverse code usage examples -* Include screenshots, animated gifs, or videos -* Include a link to the corresponding code repository - -Here are some relevant quotes from the comment section: - -> “There are still some packages that do not have code samples on the very first page. It should be mandatory to have at least a single simple example.” -> -> “Emphasize to package developers to give more thorough examples of how to use their package.” -> -> “Force all packages to have an animated gif or video demoing it (preferred) or a screenshot, and have an example Dart file.” -> -> “A graphic display of an example package would be helpful. Many times it’s easier to see what a package is referring to than to run the example.“ -> -> “Would like to see the Example section filled out more often. Some packages don’t have any examples. Maybe have a clearer link on this page to the corresponding GitHub repo?” - -Also, as shown in the graph above, difficulties associated with the actual use of packages (such as dependency issues, bugginess of packages, setup of packages) are relatively less concerning to users as compared to activities associated with selecting suitable packages (such as missing features, trustworthiness of publishers, guidance for choice, adequate platform support). - ---- - -The Flutter/Dart team at Google is also investigating ways to improve your experience with using, and contributing to, the ecosystem. Some of the options being considered include, but are not limited to: - -* Provide a better pub.dev search experience -* Make it easy to tell which platform(s) a package supports -* Offer more reliable quality metrics -* Improve testability - -In the meantime, it might be worth pointing out that each package on pub.dev already receives scores for popularity, health, and maintenance; these scores help users gauge the quality of a package. You can find details of the scoring system on [pub.dev/help#scoring](https://pub.dev/help#scoring). - -![Scoring example](https://cdn-images-1.medium.com/max/2000/0*DSPe0z8OcY1Dzlet) - -![Maintenance suggestions](https://cdn-images-1.medium.com/max/2000/0*Kxtw9kjb1h_6DTAK) - -With the scoring system, package authors can understand what they can do to improve the quality of the package, and package users can estimate the quality (for example, the outdatedness) of a package. - -We expect the scoring system to expand over time to help users make more informed decisions. More specifically, we’d like to see test coverage added, and we’d like to expose better information about platform coverage, especially as the list of platforms that Flutter supports expands. We’d also like to provide a mark of whether a particular package is “recommended” so that users has a clear idea of what the Flutter community thinks is worth considering. As these scoring changes come about, we’ll communicate with our package authors to make sure that they have all of the information they need to meet the rising quality bar. - ---- - -We want to convey a huge thank you to the more than 7,000 Flutter users who filled out the long survey. We learned a lot — some other highlights are listed below. - -* Some Flutter users are not completely satisfied with the animation framework, not because it’s hard to achieve intended effects, but because it’s hard to get started. Respondents, especially new users, did not know where to begin, and it’s hard for them to understand how various concepts link together. Therefore, we are investing more into the learning materials for the animation framework. -* For the API documentation on [api.flutter.dev](http://api.flutter.dev), sample code in the class doc was rated as the most useful resource. We have added full code samples to some of the classes in the API docs with the 1.7 release, but will continue expanding this feature to more classes. (We also accept PRs against the API docs on the [flutter/flutter repo](https://github.com/flutter/flutter/labels/d%3A%20api%20docs)!) - -![](https://cdn-images-1.medium.com/max/3200/0*PceEjhOlGlSQw1oK) - -* Lastly, many of you noticed that the number of unresolved issues in the GitHub repo is growing, which is an unfortunate side-effect of the exploding popularity of Flutter. While we closed over 1,250 issues in the last release, we have more work to do here. As mentioned in the Flutter 1.7 blog post, we’re working to increase staffing in this area, which will help with faster triaging of new bugs, faster attention to critical/crashing issues, closing and merging duplicate issues, and redirecting support requests to [StackOverflow](https://stackoverflow.com/questions/tagged/flutter). - -We value your responses to the survey and will use this information when determining work priorities. Please participate in our Q3 survey, which will be launched in August, and will explore new topic areas. - ---- - -Flutter’s UX research team performs a variety of user experience studies so that we can learn how to make your experience with Flutter more pleasant. If you are interested in participating, please [sign up](http://flutter.dev/research-signup) for future studies. - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 7b04738f9808057845982b9082ec6b2860902137 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 07/58] Revert "Create pika-web-a-future-without-webpack.md (#6248)" This reverts commit 5ff0eaca33f9c89bd9c998605c5c457a17b0664c. --- TODO1/pika-web-a-future-without-webpack.md | 107 --------------------- 1 file changed, 107 deletions(-) delete mode 100644 TODO1/pika-web-a-future-without-webpack.md diff --git a/TODO1/pika-web-a-future-without-webpack.md b/TODO1/pika-web-a-future-without-webpack.md deleted file mode 100644 index f28508037ce..00000000000 --- a/TODO1/pika-web-a-future-without-webpack.md +++ /dev/null @@ -1,107 +0,0 @@ -> * 原文地址:[A Future Without Webpack](https://www.pika.dev/blog/pika-web-a-future-without-webpack/) -> * 原文作者:[FredKSchott](https://twitter.com/FredKSchott) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/pika-web-a-future-without-webpack.md](https://github.com/xitu/gold-miner/blob/master/TODO1/pika-web-a-future-without-webpack.md) -> * 译者: -> * 校对者: - -# A Future Without Webpack - -> @pika/web installs npm packages that run natively in the browser. Do you still need a bundler? - -![](https://www.pika.dev/static/img/bundling-cover.jpg) - -The year is 1941. Your name is Richard Hubbell. You work at an experimental New York television studio owned by CBS. You are about to give one of the world’s first major TV news broadcasts, and you have 15 minutes to fill. What do you do? - -In a world that has only known radio, you stick to what you know. That is, you read the news. [“Most of the \[televised\] newscasts featured Hubbell reading a script with only occasional cutaways to a map or still photograph.”](https://books.google.com/books?id=yWrEDQAAQBAJ&lpg=PA132&ots=WBn6zP9HAW&dq=newscasts%20featured%20Hubbell%20reading%20a%20script%20with%20only%20occasional%20cutaways&pg=PA132#v=onepage&q=newscasts%20featured%20Hubbell%20reading%20a%20script%20with%20only%20occasional%20cutaways&f=false) It would be a while before anyone would show actual video clips on the TV news. - -As a JavaScript developer in 2019, I can relate. We have this new JavaScript module system [(ESM)](https://flaviocopes.com/es-modules/) that runs natively on the web. Yet we continue to use bundlers for every single thing that we build. Why? - -Over the last several years, JavaScript bundling has morphed from a production-only optimization into a required build step for most web applications. Whether you love this or hate it, it’s hard to deny that bundlers have added a ton of new complexity to web development – a field of development that has always taken pride in its view-source, easy-to-get-started ethos. - -##### @pika/web is an attempt to free web development from the bundler requirement. In 2019, you should use a bundler because you want to, not because you need to. - - - -**Credit: [@stylishandy](https://twitter.com/stylishandy/status/1105049564237754373)** - -### Why We Bundle - -JavaScript bundling is a modern take on an old concept. Back in the day (lol ~6 years ago) it was common to minify and concatenate JavaScript files together in production. This would speed up your site and get around [HTTP/1.1’s 2+ parallel request bottleneck](https://stackoverflow.com/a/985704). - -How did this nice-to-have optimization become an absolute dev requirement? Well, that’s the craziest part: Most web developers never specifically asked for bundling. Instead, we got bundling as a side-effect of something else, something that we wanted realllllllly badly: **npm.** - -[npm](https://npmjs.com) – which at the time stood for “Node.js Package Manager” – was on its way to becoming the largest code registry ever created. Frontend developers wanted in on the action. The only problem was that its Node.js-flavored module system (Common.js or CJS) wouldn’t run on the web without bundling. So Browserify, [Webpack](https://webpack.js.org), and the modern web bundler were all born. - -![a visualization of Create React App showing a ton of different dependencies](https://www.pika.dev/static/img/bundling-cra-graph-2.jpg) - -**[Create React App visualized:](https://npm.anvaka.com/#/view/2d/react-scripts) 1,300 dependencies to run "Hello World"** - -### Complexity Stockholm Syndrome - -Today, it’s nearly impossible to build for the web without using a bundler like [Webpack](https://webpack.js.org). Hopefully, you use something like [Create React App (CRA)](https://facebook.github.io/create-react-app/) to get started quickly, but even this will install a complex, 200.9MB `node_modules/` directory of 1,300+ different dependencies just to run ”Hello World!” - -Like Richard Hubbell, we are all so steeped in this world of bundlers that it’s easy to miss how things could be different. We have these great, modern ESM dependencies now [(almost 50,000 on npm!)](https://www.pika.dev/about/stats). What’s stopping us from running them directly on the web? - -Well, a few things. 😕 It’s easy enough to write web-native ESM code yourself, and it is true that some npm packages without dependencies can run directly on the web. Unfortunately, most will still fail to run. This can be due to either legacy dependencies of the package itself or the special way in which npm packages import dependencies by name. - -This is why [@pika/web](https://github.com/pikapkg/web) was created. - -### @pika/web: Web Apps Without the Bundler - -[@pika/web](https://github.com/pikapkg/web) installs modern npm dependencies in a way that lets them run natively in the browser, even if they have dependencies themselves. That’s it. It’s not a build tool and it’s not a bundler (in the traditional sense, anyway). @pika/web is a dependency install-time tool that lets you dramatically reduce the need for other tooling and even skip [Webpack](https://webpack.js.org) or [Parcel](https://parceljs.org/) entirely. - -```bash -npm install && npx @pika/web -✔ @pika/web installed web-native dependencies. [0.41s] -``` - -@pika/web checks your `package.json` manifest for any `"dependencies"` that export a valid ESM “module” entry point, and then installs them to a local `web_modules/` directory. @pika/web works on any ESM package, even ones with ESM & Common.js internal dependencies. - -Installed packages run in the browser because @pika/web bundles each package into a single, web-ready ESM `.js` file. For example: The entire “preact” package is installed to `web_modules/preact.js`. This takes care of anything bad that the package may be doing internally, while preserving the original package interface. - -**“Ah ha!”** you might say. **[“That just hides bundling in a different place!”](https://twitter.com/TheLarkInn/status/1102462419366891522)** - -**Exactly!** @pika/web leverages bundling internally to output web-native npm dependencies, which was the main reason that many of us started using bundlers in the first place! - -With @pika/web all the complexity of the bundler is internalized in a single install-time tool. You never need to touch another line of bundler configuration if you don’t want to. But of course, you can continue to use whatever other tools you like: Beef up your dev experience ([Babel](https://babeljs.io/), [TypeScript](https://www.typescriptlang.org)) or optimize how you ship in production ([Webpack](https://webpack.js.org), [Rollup](https://rollupjs.org/)). - -**This is the entire point of @pika/web: Bundle because you want to, not because you need to.** - -![a view-source screenshot](https://www.pika.dev/static/img/bundling-view-source.png) - -**PS: Oh yea, and [view source is back!](https://www.pika.dev/js/PackageList.js)** - -### Performance - -Installing each dependency this way (as a single JS file) gets you one big performance boost over most bundler setups: dependency caching. When you bundle all of your dependencies together into a single large `vendor.js` file, updating one dependency can force your users to re-download the entire bundle. Instead, with @pika/web, updating a single package won’t bust the rest of the user’s cache. - -@pika/web saves you from this entire class of performance footguns introduced by bundlers. [Duplicated code across bundles](https://formidable.com/blog/2018/finding-webpack-duplicates-with-inspectpack-plugin/), [slow first page load due to unused/unrelated code](https://medium.com/webpack/better-tree-shaking-with-deep-scope-analysis-a0b788c0ce77), [gotchas and bugs across upgrades to Webpack’s ecosystem](https://medium.com/@allanbaptista/the-problem-with-webpack-8a025268a761)… Entire articles and tools are devoted to solving these issues. - -To be clear, leaving your application source unbundled isn’t all sunshine and roses, either. Large JavaScript files do compress better over the wire than smaller, more granular files. And while multiple smaller files load just as well over [HTTP/2](https://developers.google.com/web/fundamentals/performance/http2/#request_and_response_multiplexing), the browser loses time parsing before then making follow-up requests for imports. - -It all comes down to a tradeoff between performance, caching efficiency, and how much complexity you feel comfortable with. And again, this is the entire point of @pika/web: Add a bundler because it makes sense to your situation, not because you have no other choice. - -![a bunch of legos](https://www.pika.dev/static/img/bundling-legos.jpg) - -### The Pika Web App Strategy - -@pika/web has completely changed our approach to web development. Here is the process we used to build [pika.dev](https://www.pika.dev/), and how we recommend you build your next web application in 2019: - -1. For new projects, skip the bundler. Write you application using modern ESM syntax and use @pika/web to install npm dependencies that runs natively on the web. No tooling required. -2. Add tooling as you go. Add [TypeScript](https://www.typescriptlang.org) if you want a type system, add [Babel](https://babeljs.io/) if you want to use experimental JavaScript features, and add [Terser](https://github.com/terser-js/terser) if you want JS minification. After 6+ months, [pika.dev](https://www.pika.dev/) is still happily at this phase. -3. When you feel the need & have the time, experiment by adding a simple bundler for your application source code. Performance test it. Is it faster on first page load? Second page load? If so, ship it! -4. Keep optimizing your bundler config as your application grows. -5. When you have enough money, hire a Webpack expert. Congratulations! If you have the resources to hire a Webpack expert you have officially made it. - -### Examples? We Got ‘em - -* A simple project: [\[Source\]](https://glitch.com/edit/#!/pika-web-example-simple) [\[Live Demo\]](https://pika-web-example-simple.glitch.me/) -* A Preact + HTM project: [\[Source\]](https://glitch.com/edit/#!/pika-web-example-preact-htm) [\[Live Demo\]](https://pika-web-example-preact-htm.glitch.me) -* Electron, Three.js… [See our full list of examples →](https://github.com/pikapkg/web/blob/master/EXAMPLES.md) - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From d47aa8ab825515e8dd34ab28b1d65d1e6ce4480a Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 08/58] Revert "Create when-every-product-of-design-is-one-of-opinion.md (#6244)" This reverts commit 5e1e8d1485d39bda355d05b12ff96579d6da915a. --- ...ery-product-of-design-is-one-of-opinion.md | 93 ------------------- 1 file changed, 93 deletions(-) delete mode 100644 TODO1/when-every-product-of-design-is-one-of-opinion.md diff --git a/TODO1/when-every-product-of-design-is-one-of-opinion.md b/TODO1/when-every-product-of-design-is-one-of-opinion.md deleted file mode 100644 index 515f7b610fa..00000000000 --- a/TODO1/when-every-product-of-design-is-one-of-opinion.md +++ /dev/null @@ -1,93 +0,0 @@ -> * 原文地址:[When every product of design is one of opinion](https://tannerchristensen.com/blog/2019/7/22/when-every-product-of-design-is-one-of-opinion) -> * 原文作者:[Tanner Christensen](https://tannerchristensen.com/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/when-every-product-of-design-is-one-of-opinion.md](https://github.com/xitu/gold-miner/blob/master/TODO1/when-every-product-of-design-is-one-of-opinion.md) -> * 译者: -> * 校对者: - -# When every product of design is one of opinion - -![Illustrations by Robbie Cathro for this article.](https://images.squarespace-cdn.com/content/v1/5b05a3300dbda3d74934c189/1562893100677-CFLC4SFZF0L9R0GMVK1H/ke17ZwdGBToddI8pDm48kBJJGUYCrQbckB0aB3i9meB7gQa3H78H3Y0txjaiv_0fDoOvxcdMmMKkDsyUqMSsMWxHk725yiiHCCLfrh8O1z4YTzHvnKhyp6Da-NYroOW3ZGjoBKy3azqku80C789l0r9YoV8ytu8SWDj21Bt3yU9Gh-U0J0GVFf0WX9Hn5Lg-0R4Sk3CvCel_jgOb6mmMUA/image-asset.jpeg) - -Every creation is a product of opinion. If you want to produce high quality design you need develop a strong opinion, but one shaped by that of many others. - -Opinion decides what goes into the work and what stays out of it, who the work is for and who it is **not** for. Opinion can shape the objectives and goals of the work while defining a line between distractions and inspiration. **To have a valuable opinion of the work is to not limit yourself to only your own, personal, knowledge or experience.** - -The work we do is only ever as good as the multitude of perspectives and ideas that go into it. If what you’re designing is going to be functional for anyone but yourself, you’re going to want to get a second opinion on how it should work, appear, or feel. - -> ”Better outcomes come from hearing a diversity of perspectives.” — Julie Zhuo, Facebook VP of Design - -Collecting opinions doesn’t mean giving up accountability or responsibility. Every decision needs a single decider, someone who can own the decisions and be held accountable when things go well, or not. - -But what gets handed off to the client, or to engineering or other partners, is often the result of the designer’s opinion; with that delivery comes a lot of bias. We don’t know what we don’t know, and it’s hard to see how a design might impact those who have different beliefs than us, or who aren’t using the same technology, or who intend to use what we design in ways we never imagined it to be used. - -**To set ourselves and the work up for success it’s in our best interest to shape our opinions of the work by gathering the opinions of others.** - -Early and often in the design process we should be building a broad perspective of the work by pulling in other’s who can add to our opinion of it. - -What problems might arise or what [edge cases](https://tannerchristensen.com/blog/2019/6/17/design-edge-cases-and-where-to-find-them) might break the design? It’s helpful to know how people might get confused, or be empowered, or **feel** as a result of what we design. We can never know **everything** about how our designs will function and be received once they’re out in the world, but we can try and learn what others think, feel, and perceive about the work before it makes its way outside our direct control. That is: before the design reaches a static state. - -How do we gain diverse perspectives without losing focus? How do we tune-into those whose opinions can be additive to what we’re building, rather than distracting? What is the best way to gather other’s opinions without sacrificing our responsibility in the work? - -## 1\. Share design early and often - -![Hide Away Image.JPG](https://images.squarespace-cdn.com/content/v1/5b05a3300dbda3d74934c189/1563667050573-XYFDXXTYUBX4C98V30BK/ke17ZwdGBToddI8pDm48kMQc8WrzSIpIwdx0OJ5nYId7gQa3H78H3Y0txjaiv_0fDoOvxcdMmMKkDsyUqMSsMWxHk725yiiHCCLfrh8O1z5QPOohDIaIeljMHgDF5CVlOqpeNLcJ80NK65_fV7S1UfsC3QUt--MhsJMY95gZ7XR1rrV0B-eWEv4c3gG4QvrMv7XJJMetc4iDSXfct4AKFA/Hide+Away+Image.JPG) - -In design critiques or one-on-one with designers, engineers, product managers, clients—anyone on your team—share your work. - -**It doesn’t matter what stage the work is in, the sooner you share it the sooner you can catch issues or shortcomings.** - -If you’re afraid your work will be unfairly evaluated or that others simply won’t understand it, remember that effective work can stand up on its own regardless of how we personally feel about it. [An effective critique](https://tannerchristensen.com/blog/2018/12/31/four-things-working-at-facebook-has-taught-me-about-design-critique) is never a critique of the designer, only the work. And you are not your work. - -Sooner or later the designs you create will have to stand up without you there to defend them. By exposing the work to other opinions early and often, you end up strengthen it early and often. Because you can take those early opinions and incorporate them, or start to build “defenses” against them into the work itself. - -**“Many designers want to take a problem and hide away with it in order to produce the work, but that usually backfires. They want to shelter their ideas and designs but end up weakening them instead. Like an immune system that hasn’t had a chance to strengthen itself against diseases. Designers who don’t collaborate well end up seeing things from a very limited perspective and that hurts the designs.” — Tanner Christensen** - -## 2\. Share your work with as many people as possible - -Sharing work early and often is good, but if you’re only ever sharing it to one person you’re limiting the perspectives and opinions that can help you evolve and strengthen the design. - -**Your goal should be to get a broad picture of the work you’re doing, and to do that you need to share it with as many people as possible. The more opinions you can get, the better. Because everyone has a unique background and lens from which they will see the work .** - -The goal is not to “design by committee”—having many people determine what should and should not get built—bur rather to solicit many different perspectives and opinions without giving up your responsibility for the work. - -If you approach others with a clear intent to learn what they think—and when everyone knows who is responsible for the final output— these conversations become easier to navigate and leverage to your advantage. - -Consider sharing your work by presenting it with one of the following introductions: - -* I respect your insights and want to hear your opinion on this, I may or may not take the feedback into the next stage but really want to hear what you think, can you take a look? -* I’m trying to ensure I have all the information I need to make an informed decision in my work, can we take five minutes to have you review it? -* I want to ensure I’m getting as many opinions as I can so I can collect what others are seeing and take action on it, can I get your perspective? -* Would it be ok if I showed you some of my latest work and walk you through what I’m thinking, I want to make sure I’m not overlooking anything? - -## 3\. Clarify and re-declare your objective whenever you can - -One of the most difficult parts of soliciting other’s opinions of your work is ensuring the feedback you do get is aligned with what your focus or goal is. - -Especially if the opinion you get from someone else is strong but not exactly what you need to hear. It can be incredibly difficult to turn away feedback or opinions without a strong rationale for why you’re doing so. If you simply reject someone’s opinion as being unhelpful they may not be so willing to share their perspective with you in the future. - -It can also be difficult to know what feedback is actually helpful or what feedback you’re rejecting simply because you don’t personally agree with it. - -![Taking Part Image.JPG](https://images.squarespace-cdn.com/content/v1/5b05a3300dbda3d74934c189/1563667434965-1I8MDJY9727PLW4HCQMY/ke17ZwdGBToddI8pDm48kEFCE1w56dqaOyGS9OEby0R7gQa3H78H3Y0txjaiv_0fDoOvxcdMmMKkDsyUqMSsMWxHk725yiiHCCLfrh8O1z4YTzHvnKhyp6Da-NYroOW3ZGjoBKy3azqku80C789l0haypLsn6iFkXbd5QrnyzAEdFvy2ejpJQpvpwZo7gjCOnuDjE-T1tqwX44-rS2kDHA/Taking+Part+Image.JPG) - -**To face the common challenges that arise when sharing work, you should always begin the conversation with what your objective is, or what the design is trying to accomplish.** - -One of the most vital indicators of whether an opinion should influence the work is whether it relates to your objective. When the objective is unclear or different than what others are thinking, the feedback will be less helpful than ideal. - -When you share the work or ideas for the sake of building a stronger opinion, state and re-state the objective so everyone can align on it. Be clear by saying something along the lines of: **“My purpose with this project is to get X result, I am not worried about Y or Z at this point but will consider anything that helps move the work toward those goals.”** - -If you feel the feedback you’re getting isn’t helpful, or that the opinions being shared are too removed from the objective of the work, take a minute to re-declare them. Not only will it help the person or people giving you their opinion, doing so will also help you re-orient yourself around the purpose of why you’re looking for feedback. - ---- - -Sharing your work and asking others for their opinion of it is not an excuse or a reason to give up responsibility. At the end of the day the designs you create are yours alone, not anyone elses. - -If you want to ensure your design succeed you’ll want to collect many different opinions of it before it’s considered “complete.” - -The more diverse opinions you can collect about your work, early and often, the more likely the design is to stand triumphantly against any challenges it faces in the future, especially once it’s left your hands. - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From a3563a4064b36c54ad875acb2d0e6d99e898cce8 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 09/58] Revert "Create a-deep-dive-on-python-type-hints.md (#6237)" This reverts commit 811ad18abd6ece341ffe287f3c53405e69d9bc58. --- TODO1/a-deep-dive-on-python-type-hints.md | 504 ---------------------- 1 file changed, 504 deletions(-) delete mode 100644 TODO1/a-deep-dive-on-python-type-hints.md diff --git a/TODO1/a-deep-dive-on-python-type-hints.md b/TODO1/a-deep-dive-on-python-type-hints.md deleted file mode 100644 index 5e3dd867048..00000000000 --- a/TODO1/a-deep-dive-on-python-type-hints.md +++ /dev/null @@ -1,504 +0,0 @@ -> * 原文地址:[A deep dive on Python type hints](https://veekaybee.github.io/2019/07/08/python-type-hints/) -> * 原文作者:[Vicki Boykis](http://www.vickiboykis.com/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/a-deep-dive-on-python-type-hints.md](https://github.com/xitu/gold-miner/blob/master/TODO1/a-deep-dive-on-python-type-hints.md) -> * 译者: -> * 校对者: - -# A deep dive on Python type hints - -![Smiley face](https://raw.githubusercontent.com/veekaybee/veekaybee.github.io/master/images/presser.png) - -Presser, Konstantin Makovsky 1900 - -## Introduction - -Since the release of Python’s type hints [in 2014](https://www.python.org/dev/peps/pep-0484/), people have been working on adopting them into their codebase. We’re now at a point where I’d gamely estimate that about 20-30% of Python 3 codebases are using hints (also sometimes called annotations). Over the past year, I’ve been seeing them pop up in more and more [books](https://www.manning.com/books/classic-computer-science-problems-in-python) and tutorials. - -> Actually, now I'm curious - if you actively develop in Python 3, are you using type annotations/hints in your code? -> -> — [Vicki Boykis (@vboykis) May 14, 2019](https://twitter.com/vboykis/status/1128324572917448704?ref_src=twsrc%5Etfw) - -Here’s the canonical example of [what code looks like with type hints](https://docs.python.org/3/library/typing.html). - -Code before type hints: - -```python -def greeting(name): - return 'Hello ' + name -``` - -Code after hints: - -```python -def greeting(name: str) -> str: - return 'Hello ' + name -``` - -The boilerplate format for hints is typically: - -```python -def function(variable: input_type) -> return_type: - pass -``` - -However, there’s still a lot of confusion around what they are (and what they’re even called - are they hints or annotations? For the sake of this article, I’ll call them hints), and how they can benefit your code base. - -When I started to investigate and weigh whether type hints made sense for me to use, I became super confused. So, like I usually do with things I don’t understand, I decided to dig in further, and am hopeful that this post will be just as helpful for others. - -As usual, if you see something and want to comment, feel free to [submit a pull request](https://github.com/veekaybee/veekaybee.github.io). - -## How Computers Build Our Code - -To understand what the Python core developers are trying to do here with type hints, let’s go down a couple levels from Python, and get a better understanding of how computers and programming languages work in general. - -Programming languages, at their core, are a way of doing things to data using the CPU, and storing both the input and output in memory. - -![](https://raw.githubusercontent.com/veekaybee/veekaybee.github.io/master/images/computer.png) - -The CPU is pretty stupid. It can do really powerful stuff, but it only understands machine language, which, at its core, is electricity. A representation of machine language is 1s and 0s. - -To get to those 1s and 0s, we need to move from our high-level, to low-level language. This is where compiled and interepreted languages come in. - -When languages are either [compiled or executed](http://openbookproject.net/thinkcs/python/english2e/ch01.html) (python is executed via interpreter), the code is turned into lower-level machine code that tells the lower-level components of the computer i.e. the hardware, what to do. - -There are a couple ways to translate your code into machine-legible code: you can either build a binary and have a compiler translate it (C++, Go, Rust, etc.), or run the code directly and have the interpreter do it. The latter is how Python (and PHP, Ruby,and similar “scripting” languages) works. - -![](https://raw.githubusercontent.com/veekaybee/veekaybee.github.io/master/images/interpret.png) - -How does the hardware know how to store those 0s and 1s in memory? The software, our code, needs to tell it how to allocate memory for that data. What kind of data? That’s dicated by the language’s choice of data types. - -Every language has data types. They’re usually one of the first things you learn when you learn how to program. - -You might see a tutorial like this (from Allen Downey’s excellent book, [“Think Like a Computer Scientist.”](http://openbookproject.net/thinkcs/python/english3e/)),that talks about what they are. Simply put, they’re different ways of representing data laid out in memory. - -![](https://raw.githubusercontent.com/veekaybee/veekaybee.github.io/master/images/datatypes.png) - -There are strings, integers, and many more, depending on which language you use. For example, [Python’s basic data types](https://en.wikibooks.org/wiki/Python_Programming/Data_Types) include: - -```plain -int, float, complex -str -bytes -tuple -frozenset -bool -array -bytearray -list -set -dict -``` - -There are also data types made up out of other data types. For example, a Python list can hold integers, strings, or both. - -In order to know how much memory to allocate, the computer needs to know what type of data is being stored. Luckily, Python has a [built-in function](https://docs.python.org/3/library/sys.html#sys.getsizeof), `getsizeof`, that tells us how big each different datatype is in bytes. - -This [fantastic SO answer](https://stackoverflow.com/a/1331541) gives us some approximations for “empty” data structures: - -```python -import sys -import decimal -import operator - -d = {"int": 0, - "float": 0.0, - "dict": dict(), - "set": set(), - "tuple": tuple(), - "list": list(), - "str": "a", - "unicode": u"a", - "decimal": decimal.Decimal(0), - "object": object(), - } - -# Create new dict that can be sorted by size -d_size = {} - -for k, v in sorted(d.items()): - d_size[k]=sys.getsizeof(v) - -sorted_x = sorted(d_size.items(), key=lambda kv: kv[1]) - -sorted_x - -[('object', 16), - ('float', 24), - ('int', 24), - ('tuple', 48), - ('str', 50), - ('unicode', 50), - ('list', 64), - ('decimal', 104), - ('set', 224), - ('dict', 240)] -``` - -If we sort it, we can see that the biggest data structure by default is an empty dictionary, followed by a set. Ints by comparison to strings are tiny. - -This gives us an idea of how much memory different types in our program take up. - -Why do we care? Some types are more efficient and better suited to different tasks than others. Other times, we need rigorous checks on these types to make sure they don’t violate some of the assumptions of our program. - -But what exactly are these types and why do we need them? - -Here’s where type systems come into play. - -## An introduction to type systems - -A [long time ago](https://homepages.inf.ed.ac.uk/wadler/topics/history.html), in a galaxy far, far, away, [people doing math by hand](https://en.wikipedia.org/wiki/Type_theory) realized that if they labeled numbers or elements of equations by “type”, they could reduce the amount of logic issues they had when doing math proofs against those elements. - -Since in the beginning computer science was, basically, doing a lot of math by hand, some of the principles carried over, and a type system became a way to reduce the number of bugs in your program by assigning different variables or elements to specific types. - -A couple examples: - -* If we’re writing software for a bank, we can’t have strings in the piece of code that’s calculating the total value of a person’s account -* If we’re working with survey data and want to understand whether someone did or did not do something, booleans with Yes/No answers will work best -* At a big search engine, we have to limit the number of characters people are allowed to put into the search field, so we need to do type validation for certain types of strings - -Today, in programming, there are two different type systems: static and dynamic. [Steve Klabnik](https://blog.steveklabnik.com/posts/2010-07-17-what-to-know-before-debating-type-systems), breaks it down: - -> A static type system is a mechanism by which a compiler examines source code and assigns labels (called “types”) to pieces of the syntax, and then uses them to infer something about the program’s behavior. A dynamic type system is a mechanism by which a compiler generates code to keep track of the sort of data (coincidentally, also called its “type”) used by the program. - -What does this mean? It means that, usually, for compiled languages, you need to have types pre-labeled so the compiler can go in and check them when the program is compiling to make sure the program makes sense. - -This is problably [the best explanation of the difference between the two](http://www.nicolas-hahn.com/python/go/rust/programming/2019/07/01/program-in-python-go-rust/) I’ve read recently: - -> I’ve used statically typed languages in the past, but my programming for the past few years has mostly been in Python. The experience was somewhat annoying at first, it felt as though it was simply slowing me down and forcing me to be excessively explicit whereas Python would just let me do what I wanted, even if I got it wrong occasionally. Somewhat like giving instructions to someone who always stops you to ask you to clarify what you mean, versus someone who always nods along and seems to understand you, though you’re not always sure they’re absorbing everything. - -A small caveat here that took me a while to understand: static and dynamically-typed languages are closely linked, but not synonymous with compiled or interpeted languages. You can have a dynamically-typed language, like Python, that is compiled, and you can have static languages, like Java, that are interpreted, for example if you use the Java REPL. - -## Data types in statically versus dynamically typed languages - -So what’s the difference between data types in these two languages? In static typing, you have to lay out your types beforehand. For example, if you’re working in Java, you’ll have a program that looks like this: - -```java -public class CreatingVariables { - public static void main(String[] args) { - int x, y, age, height; - double seconds, rainfall; - - x = 10; - y = 400; - age = 39; - height = 63; - - seconds = 4.71; - - rainfall = 23; - - double rate = calculateRainfallRate(seconds, rainfall); - - } -private static double calculateRainfallRate(double seconds, double rainfall) { - return rainfall/seconds; -} -``` - -If you’ll notice at the beginning of the program, we declare some variables that have an indicator of what those types are: - -```java -int x, y, age, height; -double seconds, rainfall; -``` - -And our methods also have to include the variables that we’re putting into them so that the code compiles correctly. In Java, you have to plan your types from the get-go so that the compiler knows what to check for when it compiles the code into machine code. - -Python hides this away from the user. The analogous Python code would be: - -```python -x = 10 -y = 400 -age = 39 -height = 63 - -seconds = 4.71 - -rainfall = 23 -rate = calculateRainfall(seconds, rainfall) - -def calculateRainfall(seconds, rainfall): - return rainfall/seconds -``` - -How does this work under the covers? - -## How does Python handle data types? - -Python is dynamically-typed, which means it only checks the types of the variables you specified when you run the program. As we saw in the sample piece of code, you don’t have to plan out the types and memory allocation beforehand. - -What happens [is that](https://nedbatchelder.com/blog/201803/is_python_interpreted_or_compiled_yes.html): - -> In Python, the source is compiled into a much simpler form called bytecode using CPython. These are instructions similar in spirit to CPU instructions, but instead of being executed by the CPU, they are executed by software called a virtual machine. (These are not VM’s that emulate entire operating systems, just a simplified CPU execution environment.) - -When CPython is building the program, how does it know which types the variables are if we don’t specify them? It doesn’t. All it knows is that the variables are objects. Everything in Python is [an Object](https://jakevdp.github.io/blog/2014/05/09/why-python-is-slow/), until it’s not (i.e. it becomes a more specific type), that is when we specifically check it. - -For types like strings, Python assumes that anything with single or double quotes around it will be a string. For numbers, Python picks a number type. If we try to do something to that type and Python can’t perform the operation, it’ll tell us later on. - -For example, if we try to do: - -```python -name = 'Vicki' -seconds = 4.71; - ---------------------------------------------------------------------------- -TypeError Traceback (most recent call last) - in - 3 - 4 -----> 5 name + seconds - -TypeError: must be str, not float -``` - -It’ll tell us that it can’t add a string and a float. It had no idea up until that second that name was a string and seconds was a float. - -In [other words](http://www.voidspace.org.uk/python/articles/duck_typing.shtml), - -> Duck typing happens because when we do the addition, Python doesn’t care what type object a is. All it cares is whether the call to it addition method returns anything sensible. If not - an error will be raised. - -So what does this mean? If we try to write a program in the same way that we do Java or C, we won’t get any errors until the CPython interpreter executes the exact line that has problems. - -This has proven to be inconvenient for teams working with larger code bases, because you’re not dealing with single variables, but classes upon classes of things that call each other, and need to be able to check everything quickly. - -If you can’t write good tests for them and have them catch the errors before you’re running in production, you can break systems. - -In general, there are [a lot of benefits](https://www.bernat.tech/the-state-of-type-hints-in-python/) of using type hints: - -> If you’re working with complicated data structures, or functions with a lot of inputs, it’s much easier to see what those inputs are a long time after you’ve written the code. If you have just a single function with a single parameter, like the examples we have here, it’s really easy. - -But what if you’re dealing with a codebase with lots of inputs, like this example from the [PyTorch docs](https://github.com/pytorch/examples/blob/1de2ff9338bacaaffa123d03ce53d7522d5dcc2e/mnist/main.py#L28)? - -```python -def train(args, model, device, train_loader, optimizer, epoch): - model.train() - for batch_idx, (data, target) in enumerate(train_loader): - data, target = data.to(device), target.to(device) - optimizer.zero_grad() - output = model(data) - loss = F.nll_loss(output, target) - loss.backward() - optimizer.step() - if batch_idx % args.log_interval == 0: - print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( - epoch, batch_idx * len(data), len(train_loader.dataset), -100. * batch_idx / len(train_loader), loss.item())) -``` - -What’s model? Ah, we can go down further into that codebase and see that it’s - -```python -model = Net().to(device) -``` - -But wouldn’t it be cool if we could just specify it in the method signature so we don’t have to do a code search? Maybe something like - -```python -def train(args, model (type Net), device, train_loader, optimizer, epoch): -``` - -How about device? - -```python -device = torch.device("cuda" if use_cuda else "cpu") -``` - -What’s torch.device? It’s a special PyTorch type. If we go to [other parts of the documentation and code](https://github.com/pytorch/pytorch/blob/a9f1d2e3711476ba4189ea804488e5264a4229a8/docs/source/tensor_attributes.rst), we find out that: - -```plain -A :class:`torch.device` is an object representing the device on which a :class:`torch.Tensor` is or will be allocated. - -The :class:`torch.device` contains a device type ('cpu' or 'cuda') and optional device ordinal for the device type. If the device ordinal is not present, this represents the current device for the device type; e.g. a :class:`torch.Tensor` constructed with device 'cuda' is equivalent to 'cuda:X' where X is the result of :func:`torch.cuda.current_device()`. - -A :class:`torch.device` can be constructed via a string or via a string and device ordinal -``` - -Wouldn’t it be nice if we could note that so we don’t necessarily have to look this up? - -```python -def train(args, model (type Net), device (type torch.Device), train_loader, optimizer, epoch): -``` - -And so on. - -So type hints are helpful for you, the person writing the code. - -Type hints are also helpful for others reading your code. It’s much easier to read someone’s code that’s already been typed instead of having to go through the search we just went through above. Type hints add legibility. - -So, what has Python done to move to the same kind of legibility as is available in statically-typed languages? - -## Python’s type hints - -Here’s where type hints come in. As a side note, the docs interchangeably call them type annotations or type hints. I’m going to go with type hints. In other languages, annotations and hints mean someting completely different. - -In Python 2 was that people started adding hints to their code to give an idea of what various functions returned. - -That code initially looked [like this](https://www.python.org/dev/peps/pep-0483/): - -```python -users = [] # type: List[UserID] -examples = {} # type: Dict[str, Any] -``` - -Type hints were previously just comments. But what happened was that Python started gradually moving towards a more uniform way of dealing with type hints, and these started to include [function annotations](https://www.python.org/dev/peps/pep-3107/): - -```plain -Function annotations, both for parameters and return values, are completely optional. - -Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time. - -By itself, Python does not attach any particular meaning or significance to annotations. Left to its own, Python simply makes these expressions available as described in Accessing Function Annotations below. - -The only way that annotations take on meaning is when they are interpreted by third-party libraries. These annotation consumers can do anything they want with a function's annotations. For example, one library might use string-based annotations to provide improved help messages, like so: -``` - -With the development of PEP 484 is that it was developed in conjunction with mypy, a project out of DropBox, which checks the types as you run the program. Remember that types are not checked at run-time. You’ll only get an issue if you try to run a method on a type that’s incompatible. For example, trying to slice a dictionary or trying to pop values from a string. - -From the implementation details, - -> While these annotations are available at runtime through the usual **annotations** attribute, no type checking happens at runtime. Instead, the proposal assumes the existence of a separate off-line type checker which users can run over their source code voluntarily. Essentially, such a type checker acts as a very powerful linter. (While it would of course be possible for individual users to employ a similar checker at run time for Design By Contract enforcement or JIT optimization, those tools are not yet as mature.) - -What does this look like in practice? - -Type hints also mean that you can more easily use IDEs. PyCharm, for example, offers [code completion and checks](https://www.jetbrains.com/help/pycharm/type-hinting-in-product.html) based on types, as does VS Code. - -Type hints are also helpful for another reason: they prevent you from making stupid mistakes. [This is a great example](https://medium.com/@ageitgey/learn-how-to-use-static-type-checking-in-python-3-6-in-10-minutes-12c86d72677b) of how: - -Let’s say we’re adding names to a dictionary - -```python -names = {'Vicki': 'Boykis', - 'Kim': 'Kardashian'} - -def append_name(dict, first_name, last_name): - dict[first_name] = last_name - -append_name(names,'Kanye',9) -``` - -If we allow this to happen, we’ll have a bunch of malformed entries in our dictionary. - -How do we fix it? - -```python -from typing import Dict - -names_new: Dict[str, str] = {'Vicki': 'Boykis', - 'Kim': 'Kardashian'} - -def append_name(dic: Dict[str, str] , first_name: str, last_name: str): - dic[first_name] = last_name - -append_name(names_new,'Kanye',9.7) - -names_new -``` - -By running mypy on it: - -```bash -(kanye) mbp-vboykis:types vboykis$ mypy kanye.py -kanye.py:9: error: Argument 3 to "append_name" has incompatible type "float"; expected "str" -``` - -We can see that mypy doesn’t allow that type. It makes sense to include mypy in a pipeline with tests in your continuous integration pipeline. - -## Type hints in IDEs - -One of the biggest benefits to using type hints is that you get the same kind of autocompletion in IDEs as you do with statically-typed languages. - -For example, let’s say you had a piece of code like this. These are just our two functions from before, wrapped into classes. - -```python -from typing import Dict - -class rainfallRate: - - def __init__(self, hours, inches): - self.hours= hours - self.inches = inches - - def calculateRate(self, inches:int, hours:int) -> float: - return inches/hours - -rainfallRate.calculateRate() - -class addNametoDict: - - def __init__(self, first_name, last_name): - self.first_name = first_name - self.last_name = last_name - self.dict = dict - - def append_name(dict:Dict[str, str], first_name:str, last_name:str): - dict[first_name] = last_name - -addNametoDict.append_name() -``` - -A neat thing is that, now that we have (liberally) added types, we can actually see what’s going on with them when we call the class methods: - -![](https://raw.githubusercontent.com/veekaybee/veekaybee.github.io/master/images/tabcomplete2.png) - -![](https://raw.githubusercontent.com/veekaybee/veekaybee.github.io/master/images/tabcomplete1.png) - -## Getting started with type hints - -The mypy docs have some [good suggestions](https://mypy.readthedocs.io/en/latest/existing_code.html) for getting started typing a codebase: - -```plain - 1. Start small – get a clean mypy build for some files, with few hints - 2. Write a mypy runner script to ensure consistent results - 3. Run mypy in Continuous Integration to prevent type errors - 4. Gradually annotate commonly imported modules - 5. Write hints as you modify existing code and write new code - 6. Use MonkeyType or PyAnnotate to automatically annotate legacy code -``` - -To get started with writing type hints for your own code, it helps to understand several things: - -First, you’ll need to [import the typing module](https://docs.python.org/3/library/typing.html) if you’re using anything beyond strings, ints, bools, and basic Python types. - -Second, that there are several complex types available through the module: - -Dict, Tuple, List, Set, and more. - -For example, Dict\[str, float\] means that you want to check for a dictionary where the key is a string and the value is a float. - -There’s also a type called Optional and Union. - -Third, that this is the format for type hints: - -```python -import typing - -def some_function(variable: type) -> return_type: - do_something -``` - -If you want to get started further with type hints, lots of smart people have written tutorials. [Here’s the best one](https://pymbook.readthedocs.io/en/latest/typehinting.html) to start with, in my opinion, and it takes you through how to set up a testing suite. - -## So, what’s the verdict? To use or not to use? - -But should you get started with type hints? - -It depends on your use case. As Guido and the mypy docs say, - -> The aim of mypy is not to convince everybody to write statically typed Python – static typing is entirely optional, now and in the future. The goal is to give more options for Python programmers, to make Python a more competitive alternative to other statically typed languages in large projects, to improve programmer productivity, and to improve software quality. - -Because of the overhead of setting up mypy and thinking through the types that you need, type hints don’t make sense for smaller codebases, and for experimentation (for example, in Jupyter notebooks). What’s a small codebase? Probably anything under 1k LOC, conservatively speaking. - -For larger codebases, places where you’re working with others, collaborating, and packages, places where you have version control and continuous integration system, it makes sense and could save a lot of time. - -My opinion is that type hints are going to become much more common, if not commonplace, over the next couple years, and it doesn’t hurt to get a head start. - -## Thanks - -**Special thanks to [Peter Baumgartner](https://twitter.com/pmbaumgartner), [Vincent Warmerdam](https://twitter.com/fishnets88), [Tim Hopper](https://tdhopper.com/), [Jowanza Joseph](https://www.jowanza.com/), and [Dan Boykis](http://danboykis.com/) for reading drafts of this post. All remaining errors are mine :)** - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 844b156b045bdb972be816c85fa4682e4b1f71fe Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 10/58] =?UTF-8?q?Revert=20"=E6=9B=B4=E6=96=B0=E4=B8=83?= =?UTF-8?q?=E6=9C=88=E4=BB=BD=E9=83=A8=E5=88=86=E6=96=87=E7=AB=A0=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF=E5=88=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 46f5e9e1fb003954b7e5d7ec2bbb9a942dc1d2ce. --- integrals.md | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/integrals.md b/integrals.md index 39fc6a8e270..8f68299992c 100644 --- a/integrals.md +++ b/integrals.md @@ -8434,11 +8434,10 @@ |[Node.js 基础知识: 没有依赖关系的 Web 服务器](https://juejin.im/post/5c88a6855188257b0b126564)|校对|1| |[Golang 数据结构:树](https://juejin.im/post/5c8e023351882545eb718c9d)|校对|3| -## 译者:[portandbridge](https://github.com/portandbridge) 历史贡献积分:80 当前积分:80 二零一九:80 +## 译者:[portandbridge](https://github.com/portandbridge) 历史贡献积分:76 当前积分:76 二零一九:76 |文章|类型|积分| |------|-------|-------| -|[作为初级开发人员,我没有学过的 7 个绝对真理](https://juejin.im/post/5d3d25dce51d457756536881)|校对|4| |[可维护的 ETL: 使管道更容易支持和扩展的技巧](https://juejin.im/post/5d08e178518825166f36bf89)|校对|5| |[在机器学习中为什么要进行 One-Hot 编码?](https://juejin.im/post/5d15840e5188255c23553204)|校对|1| |[设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f)|校对|2| @@ -8736,21 +8735,19 @@ |[ES6:理解参数默认值的实现细节](https://juejin.im/post/5cd0eab95188251b984d8abe)|翻译|3.5| |推荐英文文章一篇|奖励|1| -## 译者:[cyz980908](https://github.com/cyz980908) 历史贡献积分:13 当前积分:13 二零一九:13 +## 译者:[cyz980908](https://github.com/cyz980908) 历史贡献积分:7 当前积分:7 二零一九:7 |文章|类型|积分| |------|-------|-------| -|[作为初级开发人员,我没有学过的 7 个绝对真理](https://juejin.im/post/5d3d25dce51d457756536881)|翻译|6| |[从原型图到成品:步步深入 CSS 布局](https://juejin.im/post/5cebb52651882530be7b16a4)|校对|2| |[Web 使用 CSS Shapes 的艺术设计](https://juejin.im/post/5cdba21051882568841f0f47)|校对|2| |[在数据可视化中,我们曾经“画”下的那些错误](https://juejin.im/post/5cd39e1de51d453a3a0acb7b)|校对|1.5| |[使用 VS Code 调试 Node.js 的超简单方法](https://juejin.im/post/5cce9b976fb9a0322415aba4)|校对|1.5| -## 译者:[Baddyo](https://github.com/Baddyo) 历史贡献积分:120 当前积分:120 二零一九:120 +## 译者:[Baddyo](https://github.com/Baddyo) 历史贡献积分:116.5 当前积分:116.5 二零一九:116.5 |文章|类型|积分| |------|-------|-------| -|[通过阅读源码提高您的 JavaScript 水平](https://juejin.im/post/5d3c56c26fb9a07efd475414)|校对|3.5| |[npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca)|翻译|8| |[使用 Cypress 进行 React 应用的端到端测试](https://juejin.im/post/5d3702dcf265da1b961345d1)|校对|1.5| |[多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228)|翻译|4| @@ -8997,11 +8994,10 @@ |[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|翻译|7| |[微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a)|翻译|4| -## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:19 当前积分:19 二零一九:19 +## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:14 当前积分:14 二零一九:14 |文章|类型|积分| |------|-------|-------| -|[通过阅读源码提高您的 JavaScript 水平](https://juejin.im/post/5d3c56c26fb9a07efd475414)|翻译|5| |[npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca)|校对|2| |[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|翻译|3| |[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|翻译|3.5| @@ -9105,11 +9101,10 @@ |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| |[从 Reddit 讨论中看到的 GraphQL 现状](https://juejin.im/post/5d380909e51d4510624f98a0)|校对|1.5| -## 译者:[Ultrasteve](https://github.com/Ultrasteve) 历史贡献积分:3.5 当前积分:3.5 二零一九:3.5 +## 译者:[Ultrasteve](https://github.com/Ultrasteve) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 |文章|类型|积分| |------|-------|-------| -|[作为初级开发人员,我没有学过的 7 个绝对真理](https://juejin.im/post/5d3d25dce51d457756536881)|校对|2| |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| ## 译者:[Pingren](https://github.com/Pingren) 历史贡献积分:10.5 当前积分:10.5 二零一九:10.5 @@ -9131,9 +9126,3 @@ |文章|类型|积分| |------|-------|-------| |[npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca)|校对|2| - -## 译者:[imononoke](https://github.com/imononoke) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 - -|文章|类型|积分| -|------|-------|-------| -|[通过阅读源码提高您的 JavaScript 水平](https://juejin.im/post/5d3c56c26fb9a07efd475414)|校对|1.5| From 7db72893a3d2475016875138ef7d7c45c1ebf563 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 11/58] Revert "Update front-end.md" This reverts commit 5ed68079e326b9c32eca0a94c6c70b5f152e3bac. --- front-end.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/front-end.md b/front-end.md index 2fdaffdc637..fd678aeb96c 100644 --- a/front-end.md +++ b/front-end.md @@ -1,5 +1,3 @@ -* [通过阅读源码提高您的 JavaScript 水平](https://juejin.im/post/5d3c56c26fb9a07efd475414) ([MarchYuanx](https://github.com/MarchYuanx) 翻译) -* [作为初级开发人员,我没有学过的 7 个绝对真理](https://juejin.im/post/5d3d25dce51d457756536881) ([cyz980908](https://github.com/cyz980908) 翻译) * [仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae) ([yzw7489757](https://github.com/yzw7489757) 翻译) * [CSS 开发必知必会的 16 个调试工具技巧](https://juejin.im/post/5d39d27cf265da1bc14b6f47) ([Usey95](https://github.com/Usey95) 翻译) * [多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228) ([Baddyo](https://github.com/Baddyo) 翻译) From abdeff269c3d80f171f7e43bbc702a04dc661726 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 12/58] =?UTF-8?q?Revert=20"=E4=BD=9C=E4=B8=BA=E5=88=9D?= =?UTF-8?q?=E7=BA=A7=E5=BC=80=E5=8F=91=E4=BA=BA=E5=91=98=EF=BC=8C=E6=88=91?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=AD=A6=E8=BF=87=E7=9A=84=207=20=E4=B8=AA?= =?UTF-8?q?=E7=BB=9D=E5=AF=B9=E7=9C=9F=E7=90=86=20(#6212)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit cc0ed8c012a5248722feddccfe725875905cf189. --- ...te-truths-unlearned-as-junior-developer.md | 174 +++++++++--------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/TODO1/absolute-truths-unlearned-as-junior-developer.md b/TODO1/absolute-truths-unlearned-as-junior-developer.md index df4ea3710b0..f3f150099a1 100644 --- a/TODO1/absolute-truths-unlearned-as-junior-developer.md +++ b/TODO1/absolute-truths-unlearned-as-junior-developer.md @@ -2,182 +2,182 @@ > * 原文作者:[Monica Lent](https://monicalent.com/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/absolute-truths-unlearned-as-junior-developer.md](https://github.com/xitu/gold-miner/blob/master/TODO1/absolute-truths-unlearned-as-junior-developer.md) -> * 译者:[cyz980908](https://github.com/cyz980908) -> * 校对者:[Ultrasteve](https://github.com/Ultrasteve), [portandbridge](https://github.com/portandbridge) +> * 译者: +> * 校对者: -# 作为初级开发人员,我学会放下的 7 项真理 +# 7 absolute truths I unlearned as junior developer ![thumbnail](https://monicalent.com/images/typing-with-flowers.jpg) -明年就是我正式受雇以编程为业的第 10 个年头了。十年了!除了实际工作之外,在我生命的近三分之二的时间里,我一直在开发网站相关的东西。我几乎不记清在我的生活中何时我不知道 HTML,这样的确想是有点奇怪。有些孩子学习演奏乐器或跳芭蕾,而我却在我童年的卧室里用代码创造了一个神奇的世界。 +Next year, I’ll be entering my 10th year of being formally employed to write code. Ten years! And besides actual employment, for nearly 2⁄3 of my life, I’ve been building things on the web. I can barely remember a time in my life where I didn’t know HTML, which is kind of weird when you think about it. Some kids learn to play an instrument or dance ballet, but instead I was creating magical worlds with code in my childhood bedroom. -这是我往终端里打打奇怪文字就能定期伸手拿钱的头一个十年;回想这段时光,我打算花些时间和各位分享下**作为开发者,在此间我的一些想法的转变**。 +In reflecting on this first decade of getting regularly paid money to type weird symbols into my Terminal, I wanted to take some time to share **some of the ways my thinking shifted over the years as a developer**. -对现在的初级开发人员来说:也许你会在这里找到一些你现在相信的东西,并从中得到启发,去了解更多关于它的知识,以及为什么这个话题如此多元化。 或许你会发现这篇文章很鼓舞人心,因为你已经远远超过了我在你这个阶段的水平。 +For today’s junior developers: Maybe you’ll find something here you currently believe, and get inspired to learn more about it and why the topic is so multi-faceted. Or maybe you’ll find this post encouraging because you’re already so far ahead of where I was at your stage. -对现在的高级开发人员来说:也许你可以讲述一些有趣(或不起眼)的故事来分享你在初级开发人员的人生经历。 +For today’s senior developers: Maybe you can relate, and also have some funny (and humbling) stories to share about your own life lessons when you were a junior dev. -我澄清一下,**我认为初级开发人员是很棒的**,因为仅仅是学习就需要很大的勇气。而这篇文章是关于我自己的经历和学习,并不是要概括所有初级开发者的想法或行为。 +To be clear, **I think junior developers are awesome** and just showing up to learn already takes a ton of courage. This post is about my own journey and learnings, and isn’t meant to be a generalization about how all junior devs think or behave. -我希望你喜欢这篇文章并且可以产生一些共鸣。😄 +I hope you enjoy the post and can relate a little bit 😄 -> **感谢 [Artem](https://twitter.com/iamsapegin) 和 [Sara](https://twitter.com/NikkitaFTW) 对这篇文章的反馈!** +> **Thanks to [Artem](https://twitter.com/iamsapegin) and [Sara](https://twitter.com/NikkitaFTW) for your feedback on this post!** -## 作为初级开发人员,我学会放下的真理 +## Absolute truths I unlearned as a junior developer -### 1. 我是一个高级开发人员 +### 1\. I’m a senior developer -当我申请第一份技术工作时,我才 19 岁。我申请的职位是“学生网站管理员”。这是一个非常棒的职位,因为你可以同时被视为学生和大师(英文里学生网站管理员这个单词可以拆成学生和大师,这里是作者的冷笑话)。现在每个人都想成为一名工程师,因为工程师听起来更高级,但如果你问我,“大师”是做什么的。这么说吧,我的工作是编写 PHP 和 MySQL,维护我们的 Drupal 网站以及构建一些内部工具。 +I was 19 years old when I applied for my first technical job. The position I was applying for was called “Student Webmaster”. Which is a pretty awesome job title, because you could be considered both a “student” and a “master” at the same time. Nowadays everyone wants to be an “engineer” because it sounds fancier, but if you ask me, “master” is where it’s at. Anyways, my job was to write PHP and MySQL, and maintain our Drupal website as well as building some internal tools. -因为我在卧室里编码已经有几年了,所以我十分肯定我是有“多年的开发经验”的。所以当我被问及我有多少写 PHP 的经验时,我自信地回答,“3 或 4 年!”。 +Since I’d been coding in my bedroom for a couple of years, I was pretty sure those years counted as “years of experience”. So when I was asked about how much experience I had writing PHP, I confidently answered, “3 or 4 years!” -我以为我对 SQL 了解很多,因为我可以做外连接。 😎 +I thought I knew a lot about SQL because I could do outer joins 😎 -当我谷歌搜索它时,3-4 年的经验意味着我应该能够赚钱。 💰 +And when I googled it, 3-4 years of experience meant I should be making 💰 -快进到我最近的工作,这是我在 5 年的学生和工作经验“结合”后得到的工作(我认为这和正常的工作经历是一样的)。 然而在那个时候,我基本上从来没有审查过我的代码。 我通过 ssh 部署到服务器并运行 git pull 指令。我很确定我从来没有打开过 Pull Request。别误会,我在前两份工作中学到了很多很棒的东西,但是我从来没有真正和同一个代码库中的其他开发人员一起工作过。但是, 我申请了一个“高级前端工程师”的职位,得到了一份工作,并接受了。 +Fast forward to my latest job, which I got after 5 years of “combined” student and professional experience (which I thought was the same as normal experience). Yet in that time, I basically never had my code reviewed. I deployed by ssh-ing into a server and running git pull. I’m rather sure I never had to open a Pull Request. Don’t get me wrong, I learned a ton of awesome stuff at my first two jobs, but I’d never really worked with other devs in the same codebase. And yet, I applied for a position for “Senior Frontend Engineer”, got an offer, and accepted it. -**在那里,我是一位成熟的 24 岁高级开发人员。** +**There I was, a senior developer at the ripe age of 24 years old.** -我的意思是,要不是我有丰富的经验,他们怎么会给我这个职衔呢,对吧?当然,是我令人印象深刻的经历让我走到了这一步,人们应该听我的!我已经是处在技术生涯的巅峰,我也是办公室里最年轻的开发者。 +I mean they wouldn’t have given me this job title if I wasn’t really senior, right?! Surely, my impressive experience had brought me to this point, and people should listen to me!! Already at the pinnacle of my technical career, and the youngest developer in the office. -像老大一样。 💅 +Like a boss 💅 -> #### 我最终学到的 +> #### What I eventually learned > -> **并非所有的经验生来平等。** 我在卧室编码、学生时代的工作、计算机科学研究领域的工作以及在一家成长中的初创企业工作的经历都是很有价值的经历。但它们并不都一样。在你职业生涯的初期,你在支援到位的团队工作一年所能学到的东西,要比你一个人(或是只有少量反馈的情况下)编程五年多十倍。如果你的代码从未被其他开发人员审查过,你将无法以最快的速度学习 —— 这是一个巨大的因素。 +> **Not all experience is created equal.** My experience coding in my bedroom, working as a student, working in CS research, and working at a growing startup are all valuable kinds of experience. But they aren’t all the same. Early in your career, you can learn 10x more in a supportive team in 1 year, than coding on your own (or with minimal feedback) for 5 years. If your code is never reviewed by other developers, you will not learn as fast as you can – by an enormous factor. > -> **这就是为什么导师如此重要。** 和你一起工作的团队比你薪水中的几块钱更有价值。如果你能控制住自己的话,不要接受你将独自工作的初级职位!不要仅仅因为薪水就接受你的第一个角色(或者老实说,任何角色)。团队才是真正的价值所在。 +> **That’s why mentors are so important**, and the team you work with is worth so much more than a couple bucks in your paycheck. Don’t accept a junior position where you’ll be working alone, if you can help it! And don’t accept your first role (or, honestly, any role) based on salary alone. The team is where the real value is. > -> **我还了解到职位头衔不会给你“带来”任何东西。** 这有点像,5 人团队的首席技术官不同于 50 人或 500 人团队的首席技术官。即使头衔相同,所需的工作和技能完全不同。所以,仅仅因为我有一个“高级”职位头衔,也不能让我成为一名高级工程师。此外,等级头衔本身就有缺陷,很难跨公司比较。我认识到不要盯着职位头衔,或者说很重要的的是把它们作为一种外部验证的形式是。 +> **I also learned that job titles don’t “make” you anything.** It’s kind of like, being a CTO with a 5-person team is different than with a 50-person team or a 500-person team. The job and skills required are totally different, even if the title is identical. So just because I had a “senior” job title did not make me a senior engineer at all. Furthermore, hierarchical titles are inherently flawed, and difficult to compare cross-company. I learned it’s important not to fixate on titles, or use them as a form of external validation. -### 2. 每个人都写测试 +### 2\. Everyone writes tests -在我职业生涯的前半段,我从事研究工作。具体来说,我在一个公共资助的项目上工作了大约 3 年半,然后在一所大学担任 NLP 主席一年半。我可以告诉你的是:**学术研究中的编程与做工程和业务中的编程是完全不同**。 +For the first half of my career, I worked in research. Specifically, I worked on an publicly-funded project for about 3 1⁄2 years, and then at a university at the NLP chair for a year and a half. I can tell you one thing: **programming in research is completely different than programming in the industry**. -大多数情况下,你不是在构建应用程序。你是在研究算法或解析数据集。或者,如果你正在构建一个应用程序,那么你的工作很可能是由公共资助的,这意味着其他人可以免费使用,而且通常是开源的。某样东西是免费的话,这意味着,在很大程度上,你没有责任确保它总是完全可用。 +For the most part, you aren’t building applications. You’re working on algorithms or parsing data sets. Alternatively, if you are building an application, chances are your work is being publicly funded – which means it’s free for others to use and usually open-source. And when something is free, that means, for the most part, you are not **really** responsible to make sure it’s always perfectly available. -因为,嗯,这是免费的。 +Because, well, it’s free. -你也没有责任赚钱或产生结果,但这是一个完全不同内容的博客文章,讲述的是如何成为学术界的一名开发人员。 ✨ +You’re also not responsible to make any money or produce results, but that is an entirely different blog post ranting about being a developer in academia ✨ -**长话短说,我带着很多期望离开了学术界.** +**Long story short, I left academia with lots of expectations.** -而那都是些有关业界运作的想法。我觉得该有自动部署、拉请求和代码审查。这些都是极好的!终于实现了我梦寐以求的 [代码质量](#4-代码质量最重要)!但我坚信,除了使用**适当的标准**和**最佳实践编**写高质量代码之外,**软件行业的每个人都要写测试**。 +Expectations about how the industry would work. There would be automated deployment. Pull requests and code review. It was going to be glorious! Finally the [code quality](#4-code-quality-matters-most) I had been longing for! But beyond quality code with **proper standards** and **best practices**, I strongly believed, **everyone in the software industry writes tests**. -**呃哼。** +**Ahem.** -所以想象一下,当我在一家初创公司上班的第一天,却发现没有任何测试时,我有多么的惊讶。前端没有测试。后端没有测试。总之就是不做测试。 +So imagine my surprise when I showed up at my first day on the job at a startup and found no tests at all. No tests in the frontend. No tests in the backend. Just, no tests. -没!有!测!试! +Nada. Zip. Null. Undefined. NaN tests. -这里不仅**没有测试**,而且似乎没有人认为缺乏测试有问题!我有点天真地猜想,不做测试,是因为大家人们不知道如何为 AngularJS 编写测试。如果我教他们怎么做,一切都会好的,我们会开始测试。错了!长话短说,多年以后,我们会在向代码中添加自动化测试方面取得巨大的进步,但这并不像我想象的那样简单。 +Not only were there **no tests**, but no one seemed to have a problem with the lack of tests! With a bit of naivety, I assumed the reason there were no tests was because people just didn’t know how to write tests for AngularJS. If I taught them how, everything would be OK and we’d start to have tests. Wrong! Long story short, years and years later, we’ve made huge progress on adding automated tests to our code, and it wasn’t as straightforward as I thought it would be. -但这并不是因为人们不知道如何编写测试。 +But not because people didn’t know **how** to write the tests. -他们要么从未感受过没有测试的痛苦,要么感受过有**过时**测试的痛苦。虽然两件事我也从未亲身经历过。 +They’d either never felt the pain of not having tests, or they’d felt the pain of having **legacy** tests. Two things I’d never experienced for myself. -> #### 我最终学到的 +> #### What I eventually learned > -> **大量的公司和创业公司很少或根本没有测试。** 在努力寻找适合产品市场的产品或者在为生存而战时,很多公司都忽略了早期的测试。即使是那些看起来很复杂、有赞助会议或开源代码的公司,它们中的很多仍然是一个庞大的、粗糙的、有着很少的测试的整体,它们需要你的帮助来改进。询问那些不打算招募你的开发人员,让他们告诉你代码库的状态。 +> **Loads of companies and startups have little or no tests.** When struggling to find product market fit, or fighting for survival, a lot of companies neglect testing early on. Even companies that look fancy, sponsoring conferences or open-sourcing code – so many still have a big, gnarly monolith with minimal tests they need your help to improve. Ask devs who aren’t trying to recruit you to tell you about the state of the codebase. > -> **没有一家公司有完美的技术设置。** 每个公司都有问题,每个公司都有技术债务。问题是他们在做什么。我们求职时不应该有不切实际的想法,觉得是有工作要做的 —— 否则他们不会雇佣你 😉 +> **No company has a perfect tech setup.** Every company has problems, every company has technical debt. The question is what they’re doing about it. We should have no illusions when applying for jobs that there is work to be done – or else they wouldn’t be hiring 😉 > -> **对你缺乏现实生活经验的话题过于固执己见是相当傲慢的。** 我给人的印象是这样一个无所不知的人,坚持认为一定有测试,但几乎没有任何实际经验。不要像我一样。有原则很重要,但也要开放,真正有兴趣理解他人的经历和观点。 +> **Being overly opinionated on topics you lack real-world experience with is pretty arrogant.** I came across as SUCH a know-it-all, insisting there must be tests yet having hardly any experience on what that really looked like at scale. Don’t be like me. It’s important to have principles, but also to be open and truly interested to understand other people’s experiences and perspectives. -### 3. 我们远远落后于其他人(也就是“技术错失恐惧症”) +### 3\. We’re so far behind everyone else (AKA “tech FOMO”) -这个与单元测试的主题密切相关。尽管我的公司没有很多单元测试,**但其他公司肯定都做了,对吧?** +This one is closely related to the topic of unit testing. While my company didn’t have many unit tests, **surely all the other companies did, right?** -我读了很多博客帖子。我在 YouTube 上观看了一些会议讨论。我一直在关注“橙色网站”。好像每个人写的程序都功能精妙、质量一流、性能出色,而且动画精美,而我只是在这里修补一些东西,试图让它在我的最后期限之前及时工作。 +I read so many blog posts. I watched conference talks on YouTube. I read “that orange website” all the damn time. It seemed like everyone was writing super sophisticated and high-quality applications with great performance and fancy animations, while I was just over here patching some stuff together trying to make it work in time for my deadline. -我几乎崇拜我正在关注的所有其他公司,并且对我自己的公司和项目如此落后感到失望。 +I basically idolized all the other companies I was reading about, and felt disappointment that my own company and project was so behind. -> #### 我最终学到的 +> #### What I eventually learned > -> **许多会议讨论的是概念的证明,而不是现实世界的场景。** 仅仅因为你看到一个关于特定技术的会议,这并不意味着公司在日常工作中使用了该技术,或者他们所有的代码都处于完美状态。通常,做会议演讲的人展示的是玩具应用程序,而不是真实的案例研究,区分这两者很重要。 +> **Many conference talks cover proof of concepts rather than real-world scenarios.** Just because you see a conference talk about a specific technology, doesn’t mean that company is using that tech in their day to day work, or that all of their code is in perfect shape. Often people who give conference talks are presenting toy apps rather than real-world case studies, it’s important to distinguish the two. > -> **处理遗留问题是完全正常的。** 但是说真的, 我们很容易会觉得有的公司没有遗留问题要处理。但在花时间参加会议,与顶尖科技公司的工作人员交谈之后,我发现,我们都是同病相怜。哪个公司没有他们试图完全把控(或在某个时候不得不完全把控)的庞杂的(堆积如山的)麻烦代码?有遗留的代码是正常的,学习如何处理遗留代码常常比从头构建应用程序教会你更多的东西,因为你将更多地接触到你还不理解的概念。 +> **Dealing with legacy is completely normal.** No but seriously, it’s easy to imagine that some other company doesn’t have legacy to handle. But after spending time at conferences talking to people who work at tippy top tech companies, it becomes clear that we are all in the same boat. What company DOESN’T have a huge PHP or Ruby monolith they’re trying to tame (or had to tame at some point)? Legacy code is normal, and learning to deal with it will often teach you more than building apps from scratch because you’ll be more exposed to concepts you don’t understand yet. -### 4. 代码质量最重要 +### 4\. Code quality matters most -早些时候,**代码审查这事,我做起来是可以很不留情的**。 +Back in the day, **getting a code review from me could be brutal**. -至少,我对编码风格非常挑剔。我的编码风格,恰好是 Airbnb JavaScript 风格指南的修改版本,但符合我个人的品味。当时我最不想看到的,就是别人的编码风格和我不一样,比如缩进、格式化、命名。要是想在我不留一条注释的情况下通过我负责的代码审查,不仅要用上读心术,还要有中彩票的运气。 +At least, I was really nitpicky about coding style. MY coding style, which happened to be a modified version of the Airbnb JavaScript styleguide, but conforming to my personal tastes. Things like indendetation, formatting, naming – god forbid you did it differently than I would have. Passing a code review without at least one comment would have involved both mind-reading and winning the lottery. -想象一下在你 PR 下的 50 多条关于所有遗漏的分号评论! +Imagine 50+ comments on your PR with all the semicolons you missed! -因为我的眼睛像老鹰,这只老鹰想要那些高质量的分号。 🦅 +Because I had eyes like an eagle and this eagle wants those high-quality semicolons 🦅 -(幸运的是,在盯着电脑看了很多年后,我不再有鹰眼了,所以你们都幸免于难 —— #开玩笑) +(Luckily I no longer have eagle eyes after staring at the computer for many years, so you’re all spared – #kiddingnotkidding) -> #### 我最终学到的 +> #### What I eventually learned > -> **足够好就是足够好。** 当谈到代码需要有多“好”时,收益会有一定程度的减少。代码不需要写得非常细致完美,也可以做到既完成工作任务,又不会在维护的时候出现大麻烦。通常,有些重复或冗长的代码更容易被其他人理解。另外,“好代码”不同于“看起来是我写的代码”。 +> **Good enough is good enough.** There’s a degree of diminishing returns when it comes to how “good” code needs to be. It doesn’t have to be perfectly clean to get the job done and not be a total disaster to maintain. Often code that is a little more repetitive or a tiny bit more verbose is easier for other people to understand. Also, “good code” is not the same as “code that looks like I wrote it”. > -> **架构比吹毛求疵更重要。** 虽然可以改进一小段代码,但往后引发更大问题的,通常是体系层面的东西。我应该更关注应用程序的结构,而不是早期的一小段代码。 +> **Architecture is more important than nitpicking.** While a small line of code could be improved, the stuff that tends to cause bigger problems down the line are usually architectural. I should’ve focused more on the structure of the application than tiny bits of code early on. > -> **代码质量很重要。** 别误会我。但是代码质量并不是我想象的那样,比如语言分析和格式化,或者在我最近读到的博客文章中提倡的任何风格。 🙈 +> **Code quality is important**, don’t get me wrong. But code quality wasn’t what I thought it was, which was things like linting and formatting or whatever style was promoted in the latest blog post I had read 🙈 -### 5. 一切都必须记录在案! +### 5\. Everything must be documented!!!! -当我进入我的第一家公司,老实说,这是我第一次大量使用别人写的代码。当然,在我的第一份工作中,我已经做了一点,但是我从来没有真正进入一个现有的代码库,并弄清楚到底发生了什么。因为那一次遇到这种问题的时候,我重写了所有代码,而不是试图弄清楚它是如何工作的。 +When I entered my first company, it was honestly the first time I was working a lot with code other people had written. Sure, I had done it a little bit at my first job, but I never really had to come into an existing codebase and to figure out what the heck was going on. That’s because the one time that happened, I rewrote all the code instead of trying to figure out how it worked. -不管怎样。 +Anyways. -这都无济于事,因为它是由 Ruby 开发人员编写的 AngularJS 代码,或者说我是一个不知道自己还是个萌新的萌新开发者。 🕵🏻‍♀️ +It didn’t help that it was AngularJS code written by Ruby developers, or that I was a junior developer who didn’t know she was junior 🕵🏻‍♀️ -那么,我如何处理这 300 行让我感觉自己快要淹死的不熟悉的代码的呢? +So how did I handle the fact that 300 lines of unfamiliar code made me feel like I was drowning? -JSDoc。无处不在。 +JSDOC. EVERYWHERE. -我开始注释**一切**只是为了试图理解它。对我可以接触到的所有函数作注释。 +I started commenting **everything** just to try to make sense out of it. Annotations for every function I could get my hands on. -我学习了所有那些奇特的专用于 Angular 的 JSDoc 语法。于是我的代码总是一般的代码两倍长,因为里面有许多注解和注释。 👌 +I learned all that fancy Angular-specific JSDoc syntax. My code was always twice as long because it had so much documentation and so many comments 👌 -> #### 我最终学到的 +> #### What I eventually learned > -> **文件有时是谎言。** 我们很容易认为文档是万灵药。“我们需要文档!” 我虽然没有得出结论,认为“仅仅因为文档工作很辛苦,并不意味着它不值得做”,但也明白到,你必须用正确的方式记录正确的事情。过多地记录错误的事情往往会导致停滞不前,这对于那些试图解决问题的人来说同样令人困惑。 +> **Documentation lies sometimes.** It’s easy to think that documentation is a cure-all solution. “We need docs!” While I didn’t come to the conclusion that just because documentation is hard work, doesn’t mean it’s not worth doing at all, I learned that you have to document the right things in the right way. Over-documentation of the wrong things tends to lead to staleness, which can be just as confusing to people who are trying to fix an issue. > -> **在适当的时候更关注自动化而不是文档。** 测试或其他形式的自动化不太可能不同步。因此,我尝试将重点放在用清晰的语言编写好的测试上,这样开发人员在编写代码时就能够看到项目如何使用工作代码工作。另一个例子是用一些注释自动安装应用程序,而不是一个冗长而详细的安装指南。 +> **Focus on automation over documentation where appropriate.** Tests or other forms of automation are less likely to go out of sync. So instead I try to focus on writing good tests with clear language, so developers working on code I wrote are able to see how the project functions with working code. Another example is automating the installation of an application with a few comments, rather than a long and detailed installation guide. -### 6. 技术债务是坏的 +### 6\. Technical debt is bad -如果你看完刚才那点就觉得我很神经质的话,别急,这点我还没说呢!在我职业生涯的一段时间里,我认为任何我认为“混乱”的代码实际上都是**技术债务**。技术债务是一个有趣的术语,因为如果你让人们给你举一个例子来说明它是什么,可能会得到许多不同的解释。 +If you thought I was neurotic from the last point, just wait until this one! For a while in my career, I thought that any code I considered “messy” was in fact **technical debt**. Technical debt is a funny term because if you ask people to give you an example of what it is, there are so many different things that it could be. -因此,作为一个把任何一种杂乱的代码都视为技术债务的人,我立即试图以最严格的方式消除它! +So as someone who viewed any kind of “disorderly” code as technical debt, I immediately tried to eliminate it with the utmost rigor! -毫不夸张地说,我曾经花了一个周末手工修复了 800 个语言分析错误。 +I literally once spent a weekend manually fixing 800 linting errors. -这就是我有多神经质。 +That’s how neurotic I was. -**(免责声明:这是在自动修复成为一件事之前)** +**(Disclaimer: This was before auto-fixing was a thing)** -> #### 我最终学到的 +> #### What I eventually learned > -> **杂乱无章的代码并不等同于技术债。** 仅仅因为感觉不好并不意味着这是技术债。技术债实际上在某种程度上减缓了你的速度,或者使某些变化变得困难或者容易出错。如果代码仅仅是有点乱,那就有点乱吧。整理它可能不值得我花时间。 +> **Disorganized or messy code isn’t the same as technical debt.** Just because something doesn’t “feel nice” doesn’t mean it’s technical debt. Technical debt actually slows you down in some way, or makes certain kinds of changes difficult or error prone. If the code is just a little messy, it’s just a little messy. Tidying that up might not be worth my time. > -> **持有一些技术债是健康的。** 有时候我们走捷径是因为我们需要借时间,为此,我们放弃了未来的速度。拥有一些真正的“技术债”的代码是可以的,只要你意识到你可能需要偿还这些债。如果你认为你的代码库没有技术债务,那么你很可能过分强调**完美**而不是**交付**。呜呜呜,说的就是我! +> **Having some technical debt is healthy.** Sometimes we take a shortcut because we need to borrow time, and for that we give up some of our speed in the future. Having pieces of code that are in fact “technical debt” is okay, so long as you recognize you’ll likely need to pay that debt back. If you think your codebase is free of technical debt, there is a good chance you’re over-emphasizing **polish** instead of **delivery**. And boy did I do that! -### 7. 资历高意味着最擅长编程 +### 7\. Seniority means being the best at programming -我从很小就开始编码,大概已经精通 for 循环 15 年多了。编程本身对我来说就像呼吸一样。当一个解决方案显而易见时,我可以直接输入,然后代码就会随之而来。这就像写博客或电子邮件一样。我可以比其他人更快地编写解决方案,并且通常自己承担更复杂的项目。 +Having started at a rather young age to code, I’ve probably been proficient at doing for-loops for like 15+ years. Programming itself is like breathing to me. When a solution is apparent, I can just type away and the code will follow. It’s like writing a blog post or an email. I could code the solution faster than others, and typically took on the more complex projects for myself. -很长一段时间,我以为成为高级开发人员就是这么一回事。 +For a long time I thought that was what it meant to to be a senior developer. -难道不是吗?职位名称是高级开发人员,而不是“高级沟通者”或“高级项目经理”。我是真的搞不懂,要成为一名真正的资深开发者,还得学些什么其他技能。 +Because why not? The job title is “senior developer”, not “senior communicator” or “senior project manager”. I didn’t really understand how many other skills I could possibly need to develop in order to be truly senior. -> #### 我最终学到的 +> #### What I eventually learned > -> **除了编程,高级工程师还必须发展许多技能。** 与我所拥有的技能相比,我必须培养的技能数量简直是天文数字。从沟通和依赖管理到共享上下文、项目管理、评估,以及与非开发人员的成功协作。这些技能很难量化,需要大量的尝试和错误来纠正。 +> **Senior engineers must develop many skills besides programming.** The sheer number of skills I’ve had to develop in the mean time are astronomical, compared to what I came in with. Ranging from communication and dependency management to sharing context, project management, estimation, and successfully collaborating with non-developer peers. These skills are less quantifiable and take a lot of trial and error to get right. > -> **不是每个人都会在职业生涯中成为“高级”。** 资历高是多年积累经验的结果。然而,多年的经验是资历高的必要条件,但不是充分条件。它还必须是一种正确的经验,在这种经验中,你内化了正确的教训,并成功地将这些学习到的应用到未来。有时候,更大的教训可能需要一年或更长时间才能完全被发现 —— 这就是为什么多年的经验仍然重要,即使你是一个非常好的程序员。 +> **Not everyone will become “senior” during their career.** Seniority is the result of many accrued years of experience. And yet, years of experience is a necessary but not sufficient condition for seniority. It also has to be the right kind of experience in which you internalized the right lessons and successfully apply those learnings for the future. Sometimes bigger lessons can take a year or more to fully manifest – that’s why years of experience still matter, even if you’re a really good coder. > -> **在某些领域,我们都还年轻。** 无论你有多少经验,仍然有你知道的不多的地方。承认你所不知道的是填补这个空白并从更有经验的人那里获得帮助的第一步。 +> **We’re all still junior in some areas.** No matter how much experience you have, there are still places where you don’t know much. Admitting what you don’t know is the first step to filling in that gap and getting help from people who are more experienced. --- -**意外收获** — 我真的很喜欢这篇文章 [关于成为一名高级工程师](https://www.kitchensoap.com/2012/10/25/on-being-a-senior-engineer/) 。 如果你正在努力解决你职业生涯中的什么问题,并且发现自己在想“高级意味着什么?”,这将是一本很棒的读物。 +**Bonus** – I really enjoyed this article called [On Being a Senior Engineer](https://www.kitchensoap.com/2012/10/25/on-being-a-senior-engineer/) . It’s a great read if you’re grappling with what point you’re at in your journey and find yourself wondering, “What does it mean to be senior?” > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 456df9ea58d3f5f26b3fe28ca9e050efffbccca8 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 13/58] Revert "Update i-built-tic-tac-toe-with-javascript.md" This reverts commit da8644d134ffe4bf8cfe44660f8b943a2954383b. --- TODO1/i-built-tic-tac-toe-with-javascript.md | 178 +++++++++++++------ 1 file changed, 126 insertions(+), 52 deletions(-) diff --git a/TODO1/i-built-tic-tac-toe-with-javascript.md b/TODO1/i-built-tic-tac-toe-with-javascript.md index f1deb1c341f..07e265cc21a 100644 --- a/TODO1/i-built-tic-tac-toe-with-javascript.md +++ b/TODO1/i-built-tic-tac-toe-with-javascript.md @@ -1,10 +1,12 @@ -> * 原文地址:[I Built Tic Tac Toe With JavaScript](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/) -> * 原文作者:[MITCHUM](https://mitchum.blog/author/mitchm/) +> * 原文地址:[]() +> * 原文作者:[]() > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/i-built-tic-tac-toe-with-javascript.md](https://github.com/xitu/gold-miner/blob/master/TODO1/i-built-tic-tac-toe-with-javascript.md) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/.md](https://github.com/xitu/gold-miner/blob/master/TODO1/.md) > * 译者: > * 校对者: +Posted on [June 22, 2019July 25, 2019](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/) by [Mitchum](https://mitchum.blog/author/mitchm/) + # I Built Tic Tac Toe With JavaScript In my last post I showed you guys a [matching game](https://www.mitchum.blog/i-built-a-simple-matching-game-with-javascript/) I built using JavaScript and talked a bit about front-end [web technologies](https://mitchum.blog/how-a-dynamic-web-application-works-an-epic-tale-of-courage-and-sacrifice/). I received some positive feedback, so for this week’s post I decided to build a [tic tac toe game](https://www.mitchum.blog/games/tic-tac-toe/tic-tac-toe.html) using JavaScript and describe its construction in detail. I also took on the additional challenge of not using any external JavaScript libraries in the project. @@ -13,6 +15,8 @@ In my last post I showed you guys a [matching game](https://www.mitchum.blog/i-b There are two difficulty levels: moron and genius. Once you’ve bested the moron, see if you can defeat the tic-tac-toe genius. The genius is more formidable than the moron, but he is a little arrogant and isn’t actually all that bright. As a reader of my blog, I bet you are smart enough to exploit the flaws in his thinking. +(adsbygoogle = window.adsbygoogle || \[\]).push({}); + ## How It’s Made This tic tac toe game is built using the three basic front-end web technologies: HTML, CSS, and JavaScript. I’m going to show you the code for each and describe the role they play in creating the final game. Here are the three files: @@ -55,41 +59,41 @@ Next we have the main body of our HTML document. We are going to break it up int We are using a table tag for representing our tic-tac-toe game board. The code is shown below: ``` - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
``` We have added the class, “board” to the table in order to add styling to it. The board has three table row tags each containing three table data tags. This results in a 3×3 game board. We have assigned each square of the game board a numerical id and some classes indicating its positioning. @@ -99,20 +103,20 @@ We have added the class, “board” to the table in order to add styling to it. What I’m calling the controls section consists of a message box, a few buttons, and drop down list. The code looks like this: ``` -
-
Pick a square!
-
-
-   -
-   -
- -
+
+
Pick a square!
+
+
+   +
+   +
+ +
``` The message box is situated between two line breaks. Following the second line break is a div containing the rest of our controls. The play again button has a click handler that calls a JavaScript function in [tic-tac-toe.js](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.js). The mystery button is wrapped inside of a form tag. Finally, the select tag contains two options: moron and genius. The moron option is selected by default. @@ -241,6 +245,76 @@ If you decide to purchase it, I would be grateful if you decided to go through t Thanks for reading, and I’ll see you next time! +As always, if you are enjoying the ideas I’ve presented or you think I’m crazy and want to tell me why I’m wrong, go ahead and [subscribe ](https://mitchum.blog/subscribe/)to stay up to date with all the latest content. + +### Share this: + +* [Click to share on Twitter (Opens in new window)](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/?share=twitter "Click to share on Twitter") +* [Click to share on Facebook (Opens in new window)](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/?share=facebook "Click to share on Facebook") + +### Like this: + +Like Loading... + +### **Related** + +Categories[Software](https://mitchum.blog/category/software/) Tags[game](https://mitchum.blog/tag/game/), [javascript](https://mitchum.blog/tag/javascript/), [tic tac toe](https://mitchum.blog/tag/tic-tac-toe/) + +## 2 Replies to “I Built Tic Tac Toe With JavaScript” + +1. ![](https://secure.gravatar.com/avatar/dbdb25daedefac404589aec1529998ff?s=100&d=retro&r=g) **Jordan Simpson** says: + + [June 24, 2019 at 5:22 pm](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/#comment-6) + + Nicely done! The AI is actually really good at defense because it always wins when it can first, or prevents me from winning when it can second. Keeping with the AI only looking 1 move ahead, the only improvement to be made is what it should do third. + + The only way I can beat it right now is by starting with right-middle (AI chooses center), then going bottom middle (AI chooses “next available”, which is top-left), then when I go bottom-right, I’m left with 2 win options. Game over. + + Simple improvement: + Instead of “go middle or next available”, I would suggest a slightly more intelligent approach, which accounts for more advantageous moves even after the middle is taken. Iterating through the remaining win scenarios (ignoring win scenarios that are already blocked by the user), find which empty tile satisfies the most scenarios and choose it. It is true that the middle is most advantageous because it is included in 4 win scenarios, but corners are included in 3, while sides are only included in 2. Use this approach defensively to prevent the user from getting the good spots since the user always goes first, and then offensively if no defense is required. + + Advanced improvement: + If you want to really streamline the AI, I would rework your current algorithm (sorry, it’s nice and readable right now) to use the alpha-beta pruning algorithm. This algorithm will scale to any size board and will always choose the best move. It works by exhausting every possible outcome of the game from the current state of the board, “pruning” paths that are deemed useless to continue following. Since tic-tac-toe ends in 3 – 5 moves, this algorithm can easily and quickly calculate the best move to make and will never lose, always guaranteeing at least a tie. + + [https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning](https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning) + + [Reply](#comment-6) + + 1. ![](https://secure.gravatar.com/avatar/5c63408d823704fa6c5c9938622ba452?s=100&d=retro&r=g) **Mitchum** says: + + [June 25, 2019 at 1:42 am](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/#comment-8) + + Thanks for the feedback, Jordan! And well done on pointing out how the artificial intelligence could be improved. + + That alpha-beta pruning algorithm looks interesting. I have to admit, AI isn’t my strongest skill. I never got the chance to take a formal class on it back in college. Maybe in a future post I’ll revisit tic tac toe and try to implement this algorithm. Then I could have three difficulty levels: Moron, Genius, and God. Unfortunately, I think the Genius difficulty would be the only one that’s any fun play haha. + + [Reply](#comment-8) + + +### Leave a Reply [Cancel reply](/i-built-tic-tac-toe-with-javascript/#respond) + +Your email address will not be published. Required fields are marked * + +Comment + +Name * + +Email * + +Website + + Notify me of follow-up comments by email. + + + +jQuery( document ).ready( function( $ ) { $( '.comment-form, #registerform' ).append( '' ); } ); + +## Post navigation + +[Previous PostPrevious I Built A Simple Matching Game With JavaScript](https://mitchum.blog/i-built-a-simple-matching-game-with-javascript/) + +[Next PostNext How To Build Minesweeper With JavaScript](https://mitchum.blog/how-to-build-minesweeper-with-javascript/) + > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 --- From 7535161c366711b97f0119c980eafb19cf8cae30 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 14/58] Revert "Create i-built-tic-tac-toe-with-javascript.md" This reverts commit a5232529fe62868f1bbd031e1eae8df7828b3036. --- TODO1/i-built-tic-tac-toe-with-javascript.md | 322 ------------------- 1 file changed, 322 deletions(-) delete mode 100644 TODO1/i-built-tic-tac-toe-with-javascript.md diff --git a/TODO1/i-built-tic-tac-toe-with-javascript.md b/TODO1/i-built-tic-tac-toe-with-javascript.md deleted file mode 100644 index 07e265cc21a..00000000000 --- a/TODO1/i-built-tic-tac-toe-with-javascript.md +++ /dev/null @@ -1,322 +0,0 @@ -> * 原文地址:[]() -> * 原文作者:[]() -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/.md](https://github.com/xitu/gold-miner/blob/master/TODO1/.md) -> * 译者: -> * 校对者: - -Posted on [June 22, 2019July 25, 2019](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/) by [Mitchum](https://mitchum.blog/author/mitchm/) - -# I Built Tic Tac Toe With JavaScript - -In my last post I showed you guys a [matching game](https://www.mitchum.blog/i-built-a-simple-matching-game-with-javascript/) I built using JavaScript and talked a bit about front-end [web technologies](https://mitchum.blog/how-a-dynamic-web-application-works-an-epic-tale-of-courage-and-sacrifice/). I received some positive feedback, so for this week’s post I decided to build a [tic tac toe game](https://www.mitchum.blog/games/tic-tac-toe/tic-tac-toe.html) using JavaScript and describe its construction in detail. I also took on the additional challenge of not using any external JavaScript libraries in the project. - -[Click here](https://www.mitchum.blog/games/tic-tac-toe/tic-tac-toe.html) to play tic-tac-toe! - -There are two difficulty levels: moron and genius. Once you’ve bested the moron, see if you can defeat the tic-tac-toe genius. The genius is more formidable than the moron, but he is a little arrogant and isn’t actually all that bright. As a reader of my blog, I bet you are smart enough to exploit the flaws in his thinking. - -(adsbygoogle = window.adsbygoogle || \[\]).push({}); - -## How It’s Made - -This tic tac toe game is built using the three basic front-end web technologies: HTML, CSS, and JavaScript. I’m going to show you the code for each and describe the role they play in creating the final game. Here are the three files: - -[tic-tac-toe.html](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.html) - -[tic-tac-toe.css](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.css) - -[tic-tac-toe.js](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.js) - -### HTML - -##### The Header - -Let’s start with the head tag, shown below. This tag comes at the start of every HTML document you create. It’s a good place for including elements that affect the page as a whole. - -``` - - Tic Tac Toe - - - -``` - -Our head tag has three child tags inside of it: a title tag and two link tags. The tab of our web browser displays the contents of our title tag. In our case this is “Tic Tac Toe”. The second link tag contains a reference to the icon we want displayed in the tab of our web browser. Together, they form a tab that looks like this: - -![Browser tab for javascript Tic Tac Toe game](https://i1.wp.com/mitchum.blog/wp-content/uploads/2019/06/tab.png?w=740&ssl=1) - -The first link tag contains a reference to our [tic-tac-toe.css](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.css) file. This file is what lets us add color and positioning to our HTML document. Our game would look rather dreary without including this file. - -![Tic Tac Toe game without css applied](https://i2.wp.com/mitchum.blog/wp-content/uploads/2019/06/htmlonly-1.png?w=740&ssl=1) - -Our HTML document without any style. - -Next we have the main body of our HTML document. We are going to break it up into two sections: the board and the controls. We’ll start with the board. - -##### The Board - -We are using a table tag for representing our tic-tac-toe game board. The code is shown below: - -``` - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-``` - -We have added the class, “board” to the table in order to add styling to it. The board has three table row tags each containing three table data tags. This results in a 3×3 game board. We have assigned each square of the game board a numerical id and some classes indicating its positioning. - -##### The Controls - -What I’m calling the controls section consists of a message box, a few buttons, and drop down list. The code looks like this: - -``` -
-
Pick a square!
-
-
-   -
-   -
- -
-``` - -The message box is situated between two line breaks. Following the second line break is a div containing the rest of our controls. The play again button has a click handler that calls a JavaScript function in [tic-tac-toe.js](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.js). The mystery button is wrapped inside of a form tag. Finally, the select tag contains two options: moron and genius. The moron option is selected by default. - -Each of these HTML elements has been assigned various classes and ids which will be used for assisting in executing the game logic and for adding styling. Let’s talk about how that styling is applied. - -## CSS - -I’m going to break the explanation of the [tic-tac-toe.css](https://mitchum.blog/games/tic-tac-toe/tic-tac-toe.css) file up into several sections because I think that will make it easier to follow as a reader. - -##### Basic Elements - -The first section contains styling for the body, main, and h1 tags. The background styling on the body tag simply sets the light blue background color of the page using RGB values. - -The max-width, padding, and margin styling on the main tag centers our game on the screen. I borrowed this awesome and succinct styling from this [blog post](https://jrl.ninja/etc/1/). - -The h1 tag is contains the big “Tic Tac Toe” heading, and we add style to center it and give it that yellow coloring. - -See below: - -![CSS styling for the page](https://i2.wp.com/mitchum.blog/wp-content/uploads/2019/06/css1.png?w=740&ssl=1) - -##### The Controls - -Next we are going to talk about styling for the message box, difficulty drop down list, and the top-level controls section. - -We center the text inside the message box and color it yellow. Then we add a border with rounded corners. - -We set the size of our difficulty drop down, and add rounded corners, and then we set its font size, colors, and positioning. - -The only change we need to make to the controls div is to make sure that everything is centered. - -See below: - -![ CSS styling for the controls](https://i0.wp.com/mitchum.blog/wp-content/uploads/2019/06/css2.png?w=740&ssl=1) - -##### The Board - -Next comes the styling of our game board itself. We need to set the size, color, and text positioning of each square. More importantly, we need to make the borders visible in the appropriate locations. We added several classes for identifying where squares are located on the game board, allowing us to create the famous tic-tac-toe pattern. We also varied the size of the border to get a more three dimensional look and feel. - -![CSS styling for the tic tac toe board](https://i0.wp.com/mitchum.blog/wp-content/uploads/2019/06/css3.png?w=740&ssl=1) - -##### The Buttons - -Finally we come to the buttons. I have to confess, I borrowed these styles from [w3schools](https://www.w3schools.com/css/tryit.asp?filename=trycss_buttons_animate3). However, I did modify them slightly to match our color scheme. - -![CSS styling for the buttons](https://i0.wp.com/mitchum.blog/wp-content/uploads/2019/06/css4.png?w=740&ssl=1) - -Alright, that’s it for the CSS! Now we can finally move onto the fun part: JavaScript. - -### JavaScript - -As should be expected, the JavaScript code is the most complex part of the tic tac toe game. I’m going to describe the basic structure and the artificial intelligence, but I’m not going to describe each and every function. Instead, I’m going to leave it as an exercise for you to read the code and understand how each function was implemented. These other functions have been made **bold** for your convenience. - -If something in the code is confusing then leave a comment and I’ll do my best to explain it! If you can think of a better way to implement something then I would love to hear your feedback in the comments as well. The goal is for everyone to learn more and have fun in the process. - -##### Basic Structure - -The first thing we need to do is initialize some variables. We have a couple variables for keeping track of the game’s state: one for keeping track of if the game is over, and one for storing the chosen difficulty level. - -We also have a few more variables for storing some useful information: An array of our squares, the number of squares, and the win conditions. Our board is represented by a sequential list of numbers, and there are eight possible win conditions. So the win conditions are represented by an array containing eight arrays, one for each possible three square winning combination. - -See below: - -![initialization javascript variables](https://i2.wp.com/mitchum.blog/wp-content/uploads/2019/06/css5.png?w=740&ssl=1) - -With that in mind, let’s talk about how this program works. This game is [event-driven](https://en.wikipedia.org/wiki/Event-driven_architecture). Any action that occurs on-screen happens because you clicked somewhere, and the code responded to it. When you click on the “Play Again” button, the board is cleared and you can play another round of tic tac toe. When you change the difficulty level, the game responds by making different moves in response to yours. - -The most important event we have to respond to is when a player clicks on a square. There are lots of things that need to be checked. The bulk of this logic happens inside the top-level function I wrote called **chooseSquare**. - -See below: - -![Javascript for choosing a tic tac toe square.](https://i1.wp.com/mitchum.blog/wp-content/uploads/2019/06/js2.png?w=740&ssl=1) - -##### The Code Examined - -Let’s walk through the code from top to bottom. - -**Line 176:** The first thing we do is set the difficulty variable to whatever was chosen in the drop down list. This is important because our artificial intelligence looks at this variable to determine what move to make. - -**Line 177:** The second thing we do is check if the game is over. If it is not then we can proceed. Otherwise, there is no need to continue. - -**Lines 179 – 181:** Third, we set the message displayed to the player to the default, “Pick a square!” message. We do this by calling the **setMessageBox** function. Then we set variables for the id and the HTML of the square that was selected by the player. - -**Line 182:** We check if the square is open by calling **squareIsOpen**. If a marker has already been placed there then the player is trying to make an illegal move. In the corresponding else block, we notify him as such. - -**Lines 184 -185:** Since the square is open, we set the marker to “X”. Then we check to see if we won by calling **checkForWinCondition**. If we won we are returned an array containing the winning combination. If lost we are simply returned false. This is possible because JavaScript is not [type safe](https://en.wikipedia.org/wiki/Type_safety). - -**Line 186:** If the player didn’t win then we must continue so that his opponent can make a move. If the player did win, then the corresponding else block will handle it by setting the game over variable to true, turning the winning squares green by calling **highlightWinningSquares**, and setting the winning message. - -**Lines 188 – 189:** Now that the player’s move is finished we need to make a move for the computer. The function called **opponentMove** handles this, and it will be discussed later in detail. Then we need to check to see if the player lost by calling the same function we used on line 185, but this time passing in “O” as a parameter. Yay for reusability! - -**Line 190:** If the computer did not win then we must continue so that we can check for a draw. If the computer did win, then the corresponding else block will handle it by setting the game over variable to true, turning the winning squares red by calling **highlightWinningSquares**, and setting the losing message. - -**Lines 192 – 197:** We check for a draw by calling the **checkForDraw** function. If there are no win conditions met and there are no more available moves to be made then we must have reached a draw. If a draw has been reached then we set the game over variable to true and set the draw message. - -That’s it for the main game logic! The rest of this function is just the corresponding else blocks which we already covered. As I mentioned previously, go read through the other functions to get a fuller understanding of how the game logic works. - -##### Artificial Intelligence - -There are two difficulty levels: moron and genius. The moron always takes the first available square in order of id. He will sacrifice a win just to keep up this orderly pattern, and he will not deviate from it even to prevent a loss. He is a simpleton. - -The genius is much more sophisticated. He will take a win when its there, and he will try to prevent a loss. Going second puts him at a disadvantage, so he favors the center square for its defensive qualities. However, he does have weaknesses that can be exploited. He’s following a better set of rules, but he isn’t great at adapting to situations on the fly. When he can’t find an obvious move to make he reverts back to the same simple ways of the moron. - -See below: - -![AI top level javascript function](https://i0.wp.com/mitchum.blog/wp-content/uploads/2019/06/js4.png?w=740&ssl=1) - -The top level AI function - -![AI implementation details in javascript](https://i2.wp.com/mitchum.blog/wp-content/uploads/2019/06/js5.png?w=740&ssl=1) - -The AI implementation details - -Once you understand the algorithm let me know in the comments what changes we could make to turn our wannabe genius into a true one! - -(adsbygoogle = window.adsbygoogle || \[\]).push({}); - -### Summary - -In this post I showed off the Tic Tac Toe game I made using JavaScript. Then we looked at how it was constructed and how the artificial intelligence works. Let me know what you think, and what kind of games you would like to see me make in the future. Keep in mind though, I’m only one guy, so no asking for Call of Duty! - -If you want to learn more about how to write good programs in JavaScript, one book that I recommend is [JavaScript: The Good Parts](https://amzn.to/2XrvPrt), by the legendary Douglas Crockford. The language has been improved dramatically over the years, but it still has some odd properties because of its development history. The book does a great job of helping you navigate around its more questionable design choices. I found it helpful when I was learning the language. - -If you decide to purchase it, I would be grateful if you decided to go through the link above. I will get a commission through Amazon’s affiliate program, with no additional cost to you. It helps me keep this site up and running. - -Thanks for reading, and I’ll see you next time! - -As always, if you are enjoying the ideas I’ve presented or you think I’m crazy and want to tell me why I’m wrong, go ahead and [subscribe ](https://mitchum.blog/subscribe/)to stay up to date with all the latest content. - -### Share this: - -* [Click to share on Twitter (Opens in new window)](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/?share=twitter "Click to share on Twitter") -* [Click to share on Facebook (Opens in new window)](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/?share=facebook "Click to share on Facebook") - -### Like this: - -Like Loading... - -### **Related** - -Categories[Software](https://mitchum.blog/category/software/) Tags[game](https://mitchum.blog/tag/game/), [javascript](https://mitchum.blog/tag/javascript/), [tic tac toe](https://mitchum.blog/tag/tic-tac-toe/) - -## 2 Replies to “I Built Tic Tac Toe With JavaScript” - -1. ![](https://secure.gravatar.com/avatar/dbdb25daedefac404589aec1529998ff?s=100&d=retro&r=g) **Jordan Simpson** says: - - [June 24, 2019 at 5:22 pm](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/#comment-6) - - Nicely done! The AI is actually really good at defense because it always wins when it can first, or prevents me from winning when it can second. Keeping with the AI only looking 1 move ahead, the only improvement to be made is what it should do third. - - The only way I can beat it right now is by starting with right-middle (AI chooses center), then going bottom middle (AI chooses “next available”, which is top-left), then when I go bottom-right, I’m left with 2 win options. Game over. - - Simple improvement: - Instead of “go middle or next available”, I would suggest a slightly more intelligent approach, which accounts for more advantageous moves even after the middle is taken. Iterating through the remaining win scenarios (ignoring win scenarios that are already blocked by the user), find which empty tile satisfies the most scenarios and choose it. It is true that the middle is most advantageous because it is included in 4 win scenarios, but corners are included in 3, while sides are only included in 2. Use this approach defensively to prevent the user from getting the good spots since the user always goes first, and then offensively if no defense is required. - - Advanced improvement: - If you want to really streamline the AI, I would rework your current algorithm (sorry, it’s nice and readable right now) to use the alpha-beta pruning algorithm. This algorithm will scale to any size board and will always choose the best move. It works by exhausting every possible outcome of the game from the current state of the board, “pruning” paths that are deemed useless to continue following. Since tic-tac-toe ends in 3 – 5 moves, this algorithm can easily and quickly calculate the best move to make and will never lose, always guaranteeing at least a tie. - - [https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning](https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning) - - [Reply](#comment-6) - - 1. ![](https://secure.gravatar.com/avatar/5c63408d823704fa6c5c9938622ba452?s=100&d=retro&r=g) **Mitchum** says: - - [June 25, 2019 at 1:42 am](https://mitchum.blog/i-built-tic-tac-toe-with-javascript/#comment-8) - - Thanks for the feedback, Jordan! And well done on pointing out how the artificial intelligence could be improved. - - That alpha-beta pruning algorithm looks interesting. I have to admit, AI isn’t my strongest skill. I never got the chance to take a formal class on it back in college. Maybe in a future post I’ll revisit tic tac toe and try to implement this algorithm. Then I could have three difficulty levels: Moron, Genius, and God. Unfortunately, I think the Genius difficulty would be the only one that’s any fun play haha. - - [Reply](#comment-8) - - -### Leave a Reply [Cancel reply](/i-built-tic-tac-toe-with-javascript/#respond) - -Your email address will not be published. Required fields are marked * - -Comment - -Name * - -Email * - -Website - - Notify me of follow-up comments by email. - - - -jQuery( document ).ready( function( $ ) { $( '.comment-form, #registerform' ).append( '' ); } ); - -## Post navigation - -[Previous PostPrevious I Built A Simple Matching Game With JavaScript](https://mitchum.blog/i-built-a-simple-matching-game-with-javascript/) - -[Next PostNext How To Build Minesweeper With JavaScript](https://mitchum.blog/how-to-build-minesweeper-with-javascript/) - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From a79421da8c6567419b3703e008bc9b9c8e1b10d1 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 15/58] =?UTF-8?q?Revert=20"=E6=9B=B4=E6=96=B0=E4=B8=83?= =?UTF-8?q?=E6=9C=88=E4=BB=BD=E9=83=A8=E5=88=86=E6=96=87=E7=AB=A0=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF=E5=88=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0dfe02934da9fb79a5e49235ae35f11e1bfba90f. --- integrals.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/integrals.md b/integrals.md index 8f68299992c..6540d9259a2 100644 --- a/integrals.md +++ b/integrals.md @@ -6856,12 +6856,10 @@ |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|3| |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|1| -## 译者:[Moonliujk](https://github.com/Moonliujk) 历史贡献积分:85.5 当前积分:30.5 二零一九:20.5 +## 译者:[Moonliujk](https://github.com/Moonliujk) 历史贡献积分:83 当前积分:28 二零一九:18 |文章|类型|积分| |------|-------|-------| -|[利用 84 种认知偏见设计更好的产品 —— 第二部分](https://juejin.im/post/5d37e1816fb9a07ee1696a4e)|校对|1.5| -|[响应式设计的基本原则](https://juejin.im/post/5d2ed18af265da1ba56b5374)|校对|1| |[Git:透过命令学概念 —— 第二部分](https://juejin.im/post/5d2da05ae51d45106b15ffca)|校对|2| |[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|校对|4| |[从原型图到成品:步步深入 CSS 布局](https://juejin.im/post/5cebb52651882530be7b16a4)|校对|2| @@ -8720,11 +8718,10 @@ |[自动补全规则](https://juejin.im/post/5cd556ef6fb9a03218556cb7)|翻译|3| |[使用 PyTorch 在 MNIST 数据集上进行逻辑回归](https://juejin.im/post/5cc66d946fb9a032286173a7)|校对|1| -## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:16 当前积分:6 二零一九:16 +## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:15 当前积分:5 二零一九:15 |文章|类型|积分| |------|-------|-------| -|[响应式设计的基本原则](https://juejin.im/post/5d2ed18af265da1ba56b5374)|校对|1| |[前端 vs 后端:哪一个适合你?](https://juejin.im/post/5d36b5e3f265da1bd3059a21)|校对|1| |2019 年 7 月兑掘金 T 恤黑色 1 个|减去积分|10| |[贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb)|校对|3.5| @@ -8941,11 +8938,10 @@ |[改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560)|校对|1.5| |[揭秘变量提升](https://juejin.im/post/5d026b71518825710d2b1f63)|校对|1.5| -## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:50.5 当前积分:50.5 二零一九:50.5 +## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:43.5 当前积分:43.5 二零一九:43.5 |文章|类型|积分| |------|-------|-------| -|[利用 84 种认知偏见设计更好的产品 —— 第二部分](https://juejin.im/post/5d37e1816fb9a07ee1696a4e)|翻译|7| |[Go 语言概览](https://juejin.im/post/5d386166e51d454fd8057c6a)|校对|1.5| |[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|翻译|6| |[在 Python 中过度使用列表解析器和生成表达式](https://juejin.im/post/5d281b0ff265da1b8b2b8ae0)|校对|4| @@ -9073,11 +9069,10 @@ |[Kotlin Clean 架构](https://juejin.im/post/5d33e13be51d4555fd20a41b)|翻译|3| |[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|校对|1.5| -## 译者:[shinichi4849](https://github.com/shinichi4849) 历史贡献积分:5.5 当前积分:5.5 二零一九:5.5 +## 译者:[shinichi4849](https://github.com/shinichi4849) 历史贡献积分:3.5 当前积分:3.5 二零一九:3.5 |文章|类型|积分| |------|-------|-------| -|[利用 84 种认知偏见设计更好的产品 —— 第二部分](https://juejin.im/post/5d37e1816fb9a07ee1696a4e)|校对|2| |[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|校对|1| |[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|校对|2.5| @@ -9107,11 +9102,10 @@ |------|-------|-------| |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| -## 译者:[Pingren](https://github.com/Pingren) 历史贡献积分:10.5 当前积分:10.5 二零一九:10.5 +## 译者:[Pingren](https://github.com/Pingren) 历史贡献积分:6.5 当前积分:6.5 二零一九:6.5 |文章|类型|积分| |------|-------|-------| -|[响应式设计的基本原则](https://juejin.im/post/5d2ed18af265da1ba56b5374)|翻译|4| |[Web 端的 SwiftUI:SwiftWebUI](https://juejin.im/post/5d35e0ac5188257dc103e364)|校对|2.5| |[仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae)|校对|4| From 778beb1c25039186fe8dcdef56e22ba1361b2a4d Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 16/58] Revert "Update product.md" This reverts commit 75d062558d257500339ddc78a246b1c8d3ef6046. --- product.md | 1 - 1 file changed, 1 deletion(-) diff --git a/product.md b/product.md index 2a569b69b2a..2ff84cfde20 100644 --- a/product.md +++ b/product.md @@ -1,4 +1,3 @@ -* [利用 84 种认知偏见设计更好的产品 —— 第二部分](https://juejin.im/post/5d37e1816fb9a07ee1696a4e) ([JalanJiang](https://github.com/JalanJiang) 翻译) * [利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12) ([JalanJiang](https://github.com/JalanJiang) 翻译) * [制定良好的路线图:产品负责人的六个实施步骤](https://juejin.im/post/5cb299436fb9a068744e70a7) ([QiaoN](https://github.com/QiaoN) 翻译) * [2019 版 web 浏览器现状](https://juejin.im/post/5c89e69a51882536fe67b5b4) ([xionglong58](https://github.com/xionglong58) 翻译) From e84c075535bf1e3c73599766ebb11d3ac86f10bc Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 17/58] Revert "Update design.md" This reverts commit d103ca54b22f294cbb8fb1b4edcf28442439a1a5. --- design.md | 1 - 1 file changed, 1 deletion(-) diff --git a/design.md b/design.md index ef6f8c476a9..59ff7027a55 100644 --- a/design.md +++ b/design.md @@ -1,4 +1,3 @@ -* [响应式设计的基本原则](https://juejin.im/post/5d2ed18af265da1ba56b5374) ([Pingren](https://github.com/Pingren) 翻译) * [设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92) ([MarchYuanx](https://github.com/MarchYuanx) 翻译) * [感受 4px 基线网格带来的便利](https://juejin.im/post/5d09e5ecf265da1b60290798) ([Mcskiller](https://github.com/Mcskiller) 翻译) * [设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f) ([TiaossuP](https://github.com/TiaossuP) 翻译) From 9e95d1ec1f36d74d7386df124d932bb88c441240 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 18/58] =?UTF-8?q?Revert=20"fix=20=E7=A7=AF=E5=88=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d12190f4062ea85970d9a21faf6b9703d82382fa. --- integrals.md | 1 - 1 file changed, 1 deletion(-) diff --git a/integrals.md b/integrals.md index 6540d9259a2..2c4b1c29c9a 100644 --- a/integrals.md +++ b/integrals.md @@ -9102,7 +9102,6 @@ |------|-------|-------| |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| -## 译者:[Pingren](https://github.com/Pingren) 历史贡献积分:6.5 当前积分:6.5 二零一九:6.5 |文章|类型|积分| |------|-------|-------| From 9048855eed884844fb6d1a5ddf7637f3485ddd0c Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 19/58] =?UTF-8?q?Revert=20"=E6=9B=B4=E6=96=B0=E4=B8=83?= =?UTF-8?q?=E6=9C=88=E4=BB=BD=E9=83=A8=E5=88=86=E6=96=87=E7=AB=A0=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF=E5=88=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a2ed7932dec777e37c994c697bcb96e900bafff8. --- integrals.md | 50 +++++++++++++------------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/integrals.md b/integrals.md index 2c4b1c29c9a..758e7e73d2a 100644 --- a/integrals.md +++ b/integrals.md @@ -3477,11 +3477,10 @@ |[消息同步 —— 在 Airbnb 我们是怎样扩展移动消息的](https://juejin.im/post/593a7647128fe1006acafaf9)|校对|1| |[对元素持有弱引用的 Swift 数组](https://juejin.im/post/5927a34c0ce46300575a81e1)|校对|1| -## 译者:[feximin](https://github.com/Feximin) 历史贡献积分:49.5 当前积分:49.5 二零一九:17.5 +## 译者:[feximin](https://github.com/Feximin) 历史贡献积分:46 当前积分:46 二零一九:14 |文章|类型|积分| |------|-------|-------| -|[2019 年的 Android 网络 —— Retrofit 与 Kotlin 协程](https://juejin.im/post/5d3c2758f265da1b934e4a8c)|翻译|3.5| |[C++ 和 Android 本地 Activity 初探](https://juejin.im/post/5ce62d4851882532e9631b63)|翻译|6| |[WorkManager 基础入门](https://juejin.im/post/5ce4da6a6fb9a07ea712e712)|校对|4| |[Room 🔗 Coroutines](https://juejin.im/post/5cd12adef265da03634564e1)|翻译|2.5| @@ -3636,11 +3635,10 @@ |------|-------|-------| |[如何在无损的情况下让图片变的更小](https://juejin.im/post/5959fbe0f265da6c2518d740)|校对|2| -## 译者:[swants](https://github.com/swants) 历史贡献积分:96.5 当前积分:31.5 二零一九:28 +## 译者:[swants](https://github.com/swants) 历史贡献积分:94.5 当前积分:29.5 二零一九:26 |文章|类型|积分| |------|-------|-------| -|[Xcode 和 LLDB 高级调试教程:第 3 部分](https://juejin.im/post/5d383c7d5188257dab043145)|校对|2| |[iOS 中的 File Provider 拓展](https://juejin.im/post/5cff5b0af265da1b8b2b54c7)|校对|3| |[理解 WebView](https://juejin.im/post/5ce76ee4f265da1b8d15f700)|校对|2.5| |[为你的 iOS App 构建分离测试](https://juejin.im/post/5ccd6b55f265da037d4fbc41)|校对|1| @@ -5916,11 +5914,10 @@ |[让 Apache Cassandra 尾部延迟减小 10 倍(已开源)](https://juejin.im/post/5ac31083f265da239a5fff0c)|翻译|4| |[让我们来简化 UserDefaults 的使用](https://juejin.im/post/5abde324f265da23826e1723)|校对|0.5| -## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:184 当前积分:164 二零一九:102 +## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:178 当前积分:158 二零一九:96 |文章|类型|积分| |------|-------|-------| -|[Web 端的 SwiftUI:SwiftWebUI](https://juejin.im/post/5d35e0ac5188257dc103e364)|翻译|6| |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|翻译|6| |[时间序列数据间量化同步的四种方法](https://juejin.im/post/5d213c126fb9a07f091bc3f5)|翻译|4| |[使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8)|翻译|8| @@ -7538,12 +7535,10 @@ |------|-------|-------| |[深入理解 React 高阶组件](https://juejin.im/entry/5bdd226cf265da616f6f6cce)|校对|4| -## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:135.5 当前积分:83.5 二零一九:91.5 +## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:133 当前积分:81 二零一九:89 |文章|类型|积分| |------|-------|-------| -|[Xcode 和 LLDB 高级调试教程:第 3 部分](https://juejin.im/post/5d383c7d5188257dab043145)|校对|1| -|[Web 端的 SwiftUI:SwiftWebUI](https://juejin.im/post/5d35e0ac5188257dc103e364)|校对|1.5| |[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|校对|1| |2019 年 7 月兑小米台灯 1 个,掘金鼠标垫 1 个,掘金纪念币 2 个|减去积分|52| |[Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b)|校对|2| @@ -8174,11 +8169,10 @@ |[Widget - State - Context - InheritedWidget](https://juejin.im/post/5c768ad2f265da2dce1f535c)|校对|3| |[Swift:通过示例避免内存泄漏](https://juejin.im/post/5c6a0abaf265da2dc675a9b2)|校对|1| -## 译者:[kirinzer](https://github.com/kirinzer) 历史贡献积分:22 当前积分:22 二零一九:22 +## 译者:[kirinzer](https://github.com/kirinzer) 历史贡献积分:17 当前积分:17 二零一九:17 |文章|类型|积分| |------|-------|-------| -|[Xcode 和 LLDB 高级调试教程:第 3 部分](https://juejin.im/post/5d383c7d5188257dab043145)|翻译|5| |[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|翻译|4| |[Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b)|翻译|4| |[懒加载变量在 iOS Swift](https://juejin.im/post/5ca775b26fb9a05e3527db37)|翻译|2.5| @@ -8635,11 +8629,10 @@ |------|-------|-------| |[创意运用 Console API!](https://juejin.im/post/5cc1517e5188252e7a0247dd)|校对|2| -## 译者:[Charlo-O](https://github.com/Charlo-O) 历史贡献积分:36 当前积分:36 二零一九:36 +## 译者:[Charlo-O](https://github.com/Charlo-O) 历史贡献积分:31 当前积分:31 二零一九:31 |文章|类型|积分| |------|-------|-------| -|[敏捷也许是个问题](https://juejin.im/post/5d2dfb4ae51d45775f516b1e)|翻译|5| |[贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb)|翻译|6| |[感受 4px 基线网格带来的便利](https://juejin.im/post/5d09e5ecf265da1b60290798)|校对|1| |[微设计系统 — 打破藩篱](https://juejin.im/post/5d0335395188255ee806a5da)|翻译|7| @@ -8649,11 +8642,10 @@ |[在数据可视化中,我们曾经“画”下的那些错误](https://juejin.im/post/5cd39e1de51d453a3a0acb7b)|校对|1.5| |[伟大设计与好设计之间区别是什么?这里告诉你真相](https://juejin.im/post/5cc15d1c5188252d6a6b1886)|校对|1.5| -## 译者:[qiuyuezhong](https://github.com/qiuyuezhong) 历史贡献积分:17 当前积分:17 二零一九:17 +## 译者:[qiuyuezhong](https://github.com/qiuyuezhong) 历史贡献积分:13 当前积分:13 二零一九:13 |文章|类型|积分| |------|-------|-------| -|[Android 模拟器:Project Marble 中的改进](https://juejin.im/post/5d3870c66fb9a07f04208b41)|翻译|4| |[改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560)|翻译|3.5| |[Android Studio Project Marble: Apply Changes](https://juejin.im/post/5ce2c40d6fb9a07ec754ee13)|翻译|5.5| |[减少 Python 中循环的使用](https://juejin.im/post/5cc8e012e51d453b6d4d13fd)|翻译|2.5| @@ -8741,11 +8733,10 @@ |[在数据可视化中,我们曾经“画”下的那些错误](https://juejin.im/post/5cd39e1de51d453a3a0acb7b)|校对|1.5| |[使用 VS Code 调试 Node.js 的超简单方法](https://juejin.im/post/5cce9b976fb9a0322415aba4)|校对|1.5| -## 译者:[Baddyo](https://github.com/Baddyo) 历史贡献积分:116.5 当前积分:116.5 二零一九:116.5 +## 译者:[Baddyo](https://github.com/Baddyo) 历史贡献积分:108.5 当前积分:108.5 二零一九:108.5 |文章|类型|积分| |------|-------|-------| -|[npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca)|翻译|8| |[使用 Cypress 进行 React 应用的端到端测试](https://juejin.im/post/5d3702dcf265da1b961345d1)|校对|1.5| |[多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228)|翻译|4| |[CSS 开发必知必会的 16 个调试工具技巧](https://juejin.im/post/5d39d27cf265da1bc14b6f47)|校对|2| @@ -8990,11 +8981,10 @@ |[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|翻译|7| |[微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a)|翻译|4| -## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:14 当前积分:14 二零一九:14 +## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:12 当前积分:12 二零一九:12 |文章|类型|积分| |------|-------|-------| -|[npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca)|校对|2| |[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|翻译|3| |[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|翻译|3.5| |[自托管你的静态资源](https://juejin.im/post/5d258a77f265da1bca5202dc)|校对|2| @@ -9062,11 +9052,10 @@ |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|5| -## 译者:[JasonWu1111](https://github.com/JasonWu1111) 历史贡献积分:4.5 当前积分:4.5 二零一九:4.5 +## 译者:[JasonWu1111](https://github.com/JasonWu1111) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 |文章|类型|积分| |------|-------|-------| -|[Kotlin Clean 架构](https://juejin.im/post/5d33e13be51d4555fd20a41b)|翻译|3| |[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|校对|1.5| ## 译者:[shinichi4849](https://github.com/shinichi4849) 历史贡献积分:3.5 当前积分:3.5 二零一九:3.5 @@ -9088,11 +9077,10 @@ |------|-------|-------| |[使用 Node.js 读取超大的文件(第一部分)](https://juejin.im/post/5d3c27ccf265da1b8d1665ba)|校对|1.5| -## 译者:[yangxy81118](https://github.com/yangxy81118) 历史贡献积分:4 当前积分:4 二零一九:4 +## 译者:[yangxy81118](https://github.com/yangxy81118) 历史贡献积分:3 当前积分:3 二零一九:3 |文章|类型|积分| |------|-------|-------| -|[Kotlin Clean 架构](https://juejin.im/post/5d33e13be51d4555fd20a41b)|校对|1| |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| |[从 Reddit 讨论中看到的 GraphQL 现状](https://juejin.im/post/5d380909e51d4510624f98a0)|校对|1.5| @@ -9102,20 +9090,8 @@ |------|-------|-------| |[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| +## 译者:[Pingren](https://github.com/Pingren) 历史贡献积分:4 当前积分:4 二零一九:4 |文章|类型|积分| |------|-------|-------| -|[Web 端的 SwiftUI:SwiftWebUI](https://juejin.im/post/5d35e0ac5188257dc103e364)|校对|2.5| -|[仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae)|校对|4| - -## 译者:[redagavin](https://github.com/redagavin) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 - -|文章|类型|积分| -|------|-------|-------| -|[敏捷也许是个问题](https://juejin.im/post/5d2dfb4ae51d45775f516b1e)|校对|1.5| - -## 译者:[LanceZhu](https://github.com/LanceZhu) 历史贡献积分:2 当前积分:2 二零一九:2 - -|文章|类型|积分| -|------|-------|-------| -|[npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca)|校对|2| +|[仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae)|翻译|4| From 37fbfb376a3b1b9befee98df4ea8f99192e3d8d8 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 20/58] Revert "Update ios.md" This reverts commit e993f85a2053b1142c1806889bbfb2885311ff31. --- ios.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios.md b/ios.md index 80355849b12..4d9ce52fec2 100644 --- a/ios.md +++ b/ios.md @@ -1,5 +1,3 @@ -* [Web 端的 SwiftUI:SwiftWebUI](https://juejin.im/post/5d35e0ac5188257dc103e364) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) -* [Xcode 和 LLDB 高级调试教程:第 3 部分](https://juejin.im/post/5d383c7d5188257dab043145) ([kirinzer](https://github.com/kirinzer) 翻译) * [贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb) ([Charlo-O](https://github.com/Charlo-O) 翻译) * [Git:透过命令学概念 —— 第二部分](https://juejin.im/post/5d2da05ae51d45106b15ffca) ([Mirosalva](https://github.com/Mirosalva) 翻译) * [使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae) ([LucaslEliane](https://github.com/LucaslEliane) 翻译) From 9eea3f8c239de219c2197c16747248fb2445c3b4 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 21/58] Revert "Update android.md" This reverts commit 3152ce5f4e8e978f0a9b9a1af22eafc2b7e07e0f. --- android.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/android.md b/android.md index ce431f47108..49c6ac2e5c5 100644 --- a/android.md +++ b/android.md @@ -1,6 +1,3 @@ -* [2019 年的 Android 网络 —— Retrofit 与 Kotlin 协程](https://juejin.im/post/5d3c2758f265da1b934e4a8c) ([Feximin](https://github.com/Feximin) 翻译) -* [Android 模拟器:Project Marble 中的改进](https://juejin.im/post/5d3870c66fb9a07f04208b41) ([qiuyuezhong](https://github.com/qiuyuezhong) 翻译) -* [Kotlin Clean 架构](https://juejin.im/post/5d33e13be51d4555fd20a41b) ([JasonWu1111](https://github.com/JasonWu1111) 翻译) * [区域设置更改和 AndroidViewModel 反面模式](https://juejin.im/post/5d2db596f265da1b5f2688d3) ([solerji](https://github.com/solerji) 翻译) * [思考实践:用 Go 实现 Flutter](https://juejin.im/post/5d215b8df265da1b7b31ac8f) ([suhanyujie](https://github.com/suhanyujie) 翻译) * [Android 数据绑定库  — 从可观察域到 LiveData 仅需两步](https://juejin.im/post/5d12d76cf265da1b8b2b6d6e) ([gs666](https://github.com/gs666) 翻译) From 38337f65ed695216c738d7ff1174c48e9f1e79e5 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 22/58] Revert "Update others.md" This reverts commit b49f849ce6f3578163267252fb1578914bc08f18. --- others.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/others.md b/others.md index aac9e48d3f0..b98c51260ad 100644 --- a/others.md +++ b/others.md @@ -1,5 +1,3 @@ -* [敏捷也许是个问题](https://juejin.im/post/5d2dfb4ae51d45775f516b1e) ([Charlo-O](https://github.com/Charlo-O) 翻译) -* [npm 的经济风云 —— 下半部分](https://juejin.im/post/5d2d9e7af265da1b8b2b91ca) ([Baddyo](https://github.com/Baddyo) 翻译) * [类(Class)与数据结构(Data Structures)](https://juejin.im/post/5d12efe7e51d455c8838e193) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) * [Git:透过命令学概念 —— 第一部分](https://juejin.im/post/5d0b3c7ce51d4577531381e3) ([Baddyo](https://github.com/Baddyo) 翻译) * [10 分钟爆改终端](https://juejin.im/post/5d053fc56fb9a07ee85c283d) ([lihaobhsfer](https://github.com/lihaobhsfer) 翻译) From 9bbe992e7d3cd110a564175efbded312c26109b2 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 23/58] =?UTF-8?q?Revert=20"=E6=9B=B4=E6=96=B0=E4=B8=83?= =?UTF-8?q?=E6=9C=88=E4=BB=BD=E9=83=A8=E5=88=86=E6=96=87=E7=AB=A0=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF=E5=88=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d882cde6c20b823cfa4529e7636375cb49d7070f. --- integrals.md | 80 ++++++++++------------------------------------------ 1 file changed, 15 insertions(+), 65 deletions(-) diff --git a/integrals.md b/integrals.md index 758e7e73d2a..b9176f16ace 100644 --- a/integrals.md +++ b/integrals.md @@ -2839,11 +2839,10 @@ |[如何在 ChromeOS 下用 Go 搭建 Web 服务](https://juejin.im/post/58d9e1711b69e6006bc38b1a)|翻译|8| |[Pull request review 的十大错误](https://juejin.im/post/58ce3b3e61ff4b006c988f63)|校对|1| -## 译者:[sunui](https://github.com/sunui) 历史贡献积分:137.5 当前积分:27.5 二零一九:28.5 +## 译者:[sunui](https://github.com/sunui) 历史贡献积分:136 当前积分:26 二零一九:27 |文章|类型|积分| |------|-------|-------| -|[使用 Node.js 读取超大的文件(第一部分)](https://juejin.im/post/5d3c27ccf265da1b8d1665ba)|校对|1.5| |[WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544)|校对|1.5| |[Node.js 提供百万的活跃 WebSocket 连接](https://juejin.im/post/5cbeb2f45188250ab65f1d0c)|校对|1.5| |[移动界面设计的 10 项启发式原则](https://juejin.im/post/5cbe6d3d5188250a80187a57)|校对|3| @@ -4082,11 +4081,10 @@ |[在 HTTP/2 的世界里管理 CSS 和 JS](https://juejin.im/post/59bb463d51882519777c5a85)|校对|0.5| |[Coursera 的 GraphQL 之路](https://juejin.im/post/59b8d1d36fb9a00a3f24c439)|校对|1| -## 译者:[Usey95](https://github.com/Usey95) 历史贡献积分:56 当前积分:56 二零一九:20 +## 译者:[Usey95](https://github.com/Usey95) 历史贡献积分:50 当前积分:50 二零一九:14 |文章|类型|积分| |------|-------|-------| -|[CSS 开发必知必会的 16 个调试工具技巧](https://juejin.im/post/5d39d27cf265da1bc14b6f47)|翻译|6| |[微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0)|翻译|6| |[Git:透过命令学概念 —— 第一部分](https://juejin.im/post/5d0b3c7ce51d4577531381e3)|校对|2.5| |[JavaScript 中 JSON.stringify 的帕累托法则手册](https://juejin.im/post/5d11d8d4f265da1baf7cfa13)|校对|1| @@ -5496,11 +5494,10 @@ |[json — JavaScript 对象表示法](https://juejin.im/post/5a9432ae5188257a5c6092b0)|翻译|2.5| |[教你使用 CSS 计数器](https://juejin.im/post/5a811c6251882528b63ff8d7)|校对|0.5| -## 译者:[weberpan](https://github.com/weberpan) 历史贡献积分:41 当前积分:41 二零一九:2 +## 译者:[weberpan](https://github.com/weberpan) 历史贡献积分:39 当前积分:39 |文章|类型|积分| |------|-------|-------| -|[Google 的 Pagespeed 的工作原理:提升你的分数和搜索引擎排名](https://juejin.im/post/5d36903ce51d4510803ce491)|校对|2| |[探索 SMACSS:可扩展的模块化 CSS 框架](https://juejin.im/post/5ba234c85188255c38535a47)|校对|5| |[2018 来谈谈 Web 组件](https://juejin.im/post/5b780a98e51d4538980bf5cf)|翻译|6| |[从零开始,在 Redux 中构建时间旅行式调试](https://juejin.im/post/5b24c0bce51d4558ba1a5584)|翻译|4| @@ -5914,11 +5911,10 @@ |[让 Apache Cassandra 尾部延迟减小 10 倍(已开源)](https://juejin.im/post/5ac31083f265da239a5fff0c)|翻译|4| |[让我们来简化 UserDefaults 的使用](https://juejin.im/post/5abde324f265da23826e1723)|校对|0.5| -## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:178 当前积分:158 二零一九:96 +## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:172 当前积分:152 二零一九:90 |文章|类型|积分| |------|-------|-------| -|[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|翻译|6| |[时间序列数据间量化同步的四种方法](https://juejin.im/post/5d213c126fb9a07f091bc3f5)|翻译|4| |[使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8)|翻译|8| |[类(Class)与数据结构(Data Structures)](https://juejin.im/post/5d12efe7e51d455c8838e193)|翻译|4| @@ -6724,11 +6720,10 @@ |[苹果公司如何修复 3D Touch](https://juejin.im/post/5b35e5886fb9a00e3642724f)|校对|0.5| |[怎样使用简单的三角函数来创建更好的加载动画](https://juejin.im/post/5b33055f518825748871c590)|校对|1| -## 译者:[JackEggie](https://github.com/JackEggie) 历史贡献积分:51.5 当前积分:51.5 二零一九:48.5 +## 译者:[JackEggie](https://github.com/JackEggie) 历史贡献积分:45 当前积分:45 二零一九:42 |文章|类型|积分| |------|-------|-------| -|[Go 语言概览](https://juejin.im/post/5d386166e51d454fd8057c6a)|翻译|6.5| |[线性代数:矩阵基本运算](https://juejin.im/post/5d107b00f265da1b67211a21)|校对|1| |[我们从招聘技术经理的过程中学到了什么](https://juejin.im/post/5cdcf463f265da0392580820)|翻译|4+1.5| |[化 Markdown 为 HTML:用 Node.js 和 Express 搭建接口](https://juejin.im/post/5cdcc216e51d453a543f9e68)|校对|2| @@ -7996,11 +7991,10 @@ |[Flutter 从 0 到 1 第二部分](https://juejin.im/post/5c6ca802f265da2dce1f3af6)|校对|3| |推荐英文文章一篇|奖励|1| -## 译者:[Jerry-FD](https://github.com/Jerry-FD) 历史贡献积分:17 当前积分:17 二零一九:17 +## 译者:[Jerry-FD](https://github.com/Jerry-FD) 历史贡献积分:13 当前积分:13 二零一九:13 |文章|类型|积分| |------|-------|-------| -|[Google 的 Pagespeed 的工作原理:提升你的分数和搜索引擎排名](https://juejin.im/post/5d36903ce51d4510803ce491)|翻译|4| |[JavaScript 中 JSON.stringify 的帕累托法则手册](https://juejin.im/post/5d11d8d4f265da1baf7cfa13)|翻译|3| |[如何用 React Hooks 打造一个不到 100 行代码的异步表单校验库](https://juejin.im/post/5cf4e2c2f265da1b80202f83)|翻译|4.5| |[用 React 的钩子函数和调试工具提升应用性能](https://juejin.im/post/5ce974d76fb9a07f0420250e)|校对|1.5| @@ -8262,11 +8256,10 @@ |[如何在远程服务器上运行 Jupyter Notebooks](https://juejin.im/post/5cb5e0a9f265da036c577f24)|翻译|4| |[数据科学领域十大必知机器学习算法](https://juejin.im/post/5c73bbfff265da2da771d42a)|校对|2| -## 译者:[xionglong58](https://github.com/xionglong58) 历史贡献积分:101 当前积分:101 二零一九:101 +## 译者:[xionglong58](https://github.com/xionglong58) 历史贡献积分:100 当前积分:100 二零一九:100 |文章|类型|积分| |------|-------|-------| -|[多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228)|校对|1| |[理解 React 中的高阶组件](https://juejin.im/post/5d1037966fb9a07ee4636de3)|校对|1| |[通过一些例子深入了解 JavaScript 的 Async 和 Await](https://juejin.im/post/5cec8a475188255816489878)|翻译|7| |[帮你高效使用 VS Code 的秘诀](https://juejin.im/post/5cd8fcedf265da03761eaa45)|校对|1| @@ -8352,11 +8345,10 @@ |[谷歌搜索操作符大全(包含 42 个高级操作符)](https://juejin.im/post/5c73744ef265da2dc675c029)|校对|2.5| |[用 Rust 写一个微服务](https://juejin.im/post/5c7a3777f265da2dd773fc38)|翻译|8| -## 译者:[LucaslEliane](https://github.com/LucaslEliane) 历史贡献积分:39 当前积分:39 二零一九:39 +## 译者:[LucaslEliane](https://github.com/LucaslEliane) 历史贡献积分:34 当前积分:34 二零一九:34 |文章|类型|积分| |------|-------|-------| -|[使用 Node.js 读取超大的文件(第一部分)](https://juejin.im/post/5d3c27ccf265da1b8d1665ba)|翻译|5| |[使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae)|翻译|8| |[高效地在 Flutter 中使用 BLoC 模式](https://juejin.im/post/5cf7abf65188256bdd2ee76b)|翻译|4| |[使用 closest() 函数获取正确的 DOM 元素](https://juejin.im/post/5cc811796fb9a0321c45e5e0)|翻译|2| @@ -8404,11 +8396,10 @@ |[四个理由让你使用灰度色调进行设计](https://juejin.im/post/5c961b1fe51d456a6743109e)|校对|1.5| |[浏览器中 CSS 支持指南](https://juejin.im/post/5c87a9006fb9a049e4138c7e)|校对|2.5| -## 译者:[Endone](https://github.com/Endone) 历史贡献积分:26 当前积分:26 二零一九:26 +## 译者:[Endone](https://github.com/Endone) 历史贡献积分:24 当前积分:24 二零一九:24 |文章|类型|积分| |------|-------|-------| -|[Google 的 Pagespeed 的工作原理:提升你的分数和搜索引擎排名](https://juejin.im/post/5d36903ce51d4510803ce491)|校对|2| |[使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae)|校对|3| |[剖析 Stack Overflow,开发者遇到最多的的 Bug 是哪些?](https://juejin.im/post/5d087a32518825403d14758b)|校对|1.5| |[JavaScript 线性代数:向量](https://juejin.im/post/5cf61bf8e51d45775653674e)|校对|1.5| @@ -8550,12 +8541,10 @@ |------|-------|-------| |[使用网格布局实现响应式图片](https://juejin.im/post/5ca0ad80f265da30920059ae)|校对|1| -## 译者:[yzw7489757](https://github.com/yzw7489757) 历史贡献积分:12.5 当前积分:12.5 二零一九:12.5 +## 译者:[yzw7489757](https://github.com/yzw7489757) 历史贡献积分:8 当前积分:8 二零一九:8 |文章|类型|积分| |------|-------|-------| -|[使用 Cypress 进行 React 应用的端到端测试](https://juejin.im/post/5d3702dcf265da1b961345d1)|校对|0.5| -|[仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae)|翻译|4| |[你不能成为成功程序员的 10 个迹象](https://juejin.im/post/5ca2f5ce51882565cb5b962c)|校对|3| |[从 0 创建自定义元素](https://juejin.im/post/5cb2b5465188257abd66c354)|翻译|3| |[写给大家看的 Cache-Control 指令配置](https://juejin.im/post/5c9d92506fb9a070f5067b3d)|校对|2| @@ -8733,13 +8722,10 @@ |[在数据可视化中,我们曾经“画”下的那些错误](https://juejin.im/post/5cd39e1de51d453a3a0acb7b)|校对|1.5| |[使用 VS Code 调试 Node.js 的超简单方法](https://juejin.im/post/5cce9b976fb9a0322415aba4)|校对|1.5| -## 译者:[Baddyo](https://github.com/Baddyo) 历史贡献积分:108.5 当前积分:108.5 二零一九:108.5 +## 译者:[Baddyo](https://github.com/Baddyo) 历史贡献积分:101 当前积分:101 二零一九:101 |文章|类型|积分| |------|-------|-------| -|[使用 Cypress 进行 React 应用的端到端测试](https://juejin.im/post/5d3702dcf265da1b961345d1)|校对|1.5| -|[多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228)|翻译|4| -|[CSS 开发必知必会的 16 个调试工具技巧](https://juejin.im/post/5d39d27cf265da1bc14b6f47)|校对|2| |[10 分钟爆改终端](https://juejin.im/post/5d053fc56fb9a07ee85c283d)|校对|2| |[Git:透过命令学概念 —— 第一部分](https://juejin.im/post/5d0b3c7ce51d4577531381e3)|翻译|8.5| |[npm 的经济风云 —— 上半部分](https://juejin.im/post/5d146225e51d4556db694a4b)|翻译|7.5| @@ -8855,11 +8841,10 @@ |[WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544)|校对|1.5| |[如何在 Google Play 应用商店中发布 PWA](https://juejin.im/post/5cefe63a6fb9a07ef3764dbe)|校对|2| -## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:12.5 当前积分:12.5 二零一九:12.5 +## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:11.5 当前积分:11.5 二零一九:11.5 |文章|类型|积分| |------|-------|-------| -|[多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228)|校对|1| |[推广 PWA 安装的模式(移动端)](https://juejin.im/post/5d2746f1f265da1b7638cd1f)|校对|1.5| |[微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0)|校对|1.5| |[微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360)|校对|1.5| @@ -8929,11 +8914,10 @@ |[改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560)|校对|1.5| |[揭秘变量提升](https://juejin.im/post/5d026b71518825710d2b1f63)|校对|1.5| -## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:43.5 当前积分:43.5 二零一九:43.5 +## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:42 当前积分:42 二零一九:42 |文章|类型|积分| |------|-------|-------| -|[Go 语言概览](https://juejin.im/post/5d386166e51d454fd8057c6a)|校对|1.5| |[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|翻译|6| |[在 Python 中过度使用列表解析器和生成表达式](https://juejin.im/post/5d281b0ff265da1b8b2b8ae0)|校对|4| |[使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae)|校对|6| @@ -8953,22 +8937,19 @@ |------|-------|-------| |[如何用 React Hooks 打造一个不到 100 行代码的异步表单校验库](https://juejin.im/post/5cf4e2c2f265da1b80202f83)|校对|1.5| -## 译者:[Stevens1995](https://github.com/Stevens1995) 历史贡献积分:9 当前积分:9 二零一九:9 +## 译者:[Stevens1995](https://github.com/Stevens1995) 历史贡献积分:5.5 当前积分:5.5 二零一九:5.5 |文章|类型|积分| |------|-------|-------| -|[使用 Cypress 进行 React 应用的端到端测试](https://juejin.im/post/5d3702dcf265da1b961345d1)|翻译|3.5| |[微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360)|校对|1.5| |[微前端:未来前端开发的新趋势 — 第二部分](https://juejin.im/post/5d1a91c7e51d45775f516ab9)|校对|2| |[微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a)|校对|1| |[JavaScript 线性代数:使用 ThreeJS 制作线性变换动画](https://juejin.im/post/5d05dba86fb9a07ece67ce76)|校对|1| -## 译者:[TiaossuP](https://github.com/TiaossuP) 历史贡献积分:19 当前积分:19 二零一九:19 +## 译者:[TiaossuP](https://github.com/TiaossuP) 历史贡献积分:12 当前积分:12 二零一九:12 |文章|类型|积分| |------|-------|-------| -|[CSS 开发必知必会的 16 个调试工具技巧](https://juejin.im/post/5d39d27cf265da1bc14b6f47)|校对|1.5| -|[从 Reddit 讨论中看到的 GraphQL 现状](https://juejin.im/post/5d380909e51d4510624f98a0)|翻译|5.5| |[设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f)|翻译|7| |[npm 的经济风云 —— 上半部分](https://juejin.im/post/5d146225e51d4556db694a4b)|校对|2| |[在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603)|校对|2| @@ -9064,34 +9045,3 @@ |------|-------|-------| |[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|校对|1| |[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|校对|2.5| - -## 译者:[40m41h42t](https://github.com/40m41h42t) 历史贡献积分:2 当前积分:2 二零一九:2 - -|文章|类型|积分| -|------|-------|-------| -|[Go 语言概览](https://juejin.im/post/5d386166e51d454fd8057c6a)|校对|2| - -## 译者:[JaneLdq](https://github.com/JaneLdq) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 - -|文章|类型|积分| -|------|-------|-------| -|[使用 Node.js 读取超大的文件(第一部分)](https://juejin.im/post/5d3c27ccf265da1b8d1665ba)|校对|1.5| - -## 译者:[yangxy81118](https://github.com/yangxy81118) 历史贡献积分:3 当前积分:3 二零一九:3 - -|文章|类型|积分| -|------|-------|-------| -|[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| -|[从 Reddit 讨论中看到的 GraphQL 现状](https://juejin.im/post/5d380909e51d4510624f98a0)|校对|1.5| - -## 译者:[Ultrasteve](https://github.com/Ultrasteve) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 - -|文章|类型|积分| -|------|-------|-------| -|[喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352)|校对|1.5| - -## 译者:[Pingren](https://github.com/Pingren) 历史贡献积分:4 当前积分:4 二零一九:4 - -|文章|类型|积分| -|------|-------|-------| -|[仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae)|翻译|4| From 74d8633938f429a927aa093881093462256538b9 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 24/58] Revert "Update front-end.md" This reverts commit 99b038f043d2011e29d53f1644c820e674094ee8. --- front-end.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/front-end.md b/front-end.md index fd678aeb96c..5b19780f80c 100644 --- a/front-end.md +++ b/front-end.md @@ -1,7 +1,3 @@ -* [仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单](https://juejin.im/post/5d3c2852f265da1bac405fae) ([yzw7489757](https://github.com/yzw7489757) 翻译) -* [CSS 开发必知必会的 16 个调试工具技巧](https://juejin.im/post/5d39d27cf265da1bc14b6f47) ([Usey95](https://github.com/Usey95) 翻译) -* [多网站项目的 CSS 架构](https://juejin.im/post/5d3a58df5188251ce02ff228) ([Baddyo](https://github.com/Baddyo) 翻译) -* [使用 Cypress 进行 React 应用的端到端测试](https://juejin.im/post/5d3702dcf265da1b961345d1) ([Stevens1995](https://github.com/Stevens1995) 翻译) * [CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13) ([MarchYuanx](https://github.com/MarchYuanx) 翻译) * [使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8) ([YueYongDev](https://github.com/YueYongDev) 翻译) * [推广 PWA 安装的模式(移动端)](https://juejin.im/post/5d2746f1f265da1b7638cd1f) ([xutaogit](https://github.com/xutaogit) 翻译) From e89e97ade2df62299e66658fcf8c1b3ccae13b67 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 25/58] Revert "Update backend.md" This reverts commit b00784477d4ca42df9825cf76b2d452b3e881785. --- backend.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/backend.md b/backend.md index f84bd3d2838..cfd16356d62 100644 --- a/backend.md +++ b/backend.md @@ -1,9 +1,3 @@ -* [前端 vs 后端:哪一个适合你?](https://juejin.im/post/5d36b5e3f265da1bd3059a21) ([YueYongDev](https://github.com/YueYongDev) 翻译) -* [Go 语言概览](https://juejin.im/post/5d386166e51d454fd8057c6a) ([JackEggie](https://github.com/JackEggie) 翻译) -* [Google 的 Pagespeed 的工作原理:提升你的分数和搜索引擎排名](https://juejin.im/post/5d36903ce51d4510803ce491) ([Jerry-FD](https://github.com/Jerry-FD) 翻译) -* [使用 Node.js 读取超大的文件(第一部分)](https://juejin.im/post/5d3c27ccf265da1b8d1665ba) ([LucaslEliane](https://github.com/LucaslEliane) 翻译) -* [从 Reddit 讨论中看到的 GraphQL 现状](https://juejin.im/post/5d380909e51d4510624f98a0) ([TiaossuP](https://github.com/TiaossuP) 翻译) -* [喷泉码和动态二维码](https://juejin.im/post/5d391ae1f265da1bb0040352) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) * [Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058) ([JalanJiang](https://github.com/JalanJiang) 翻译) * [使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) * [通过 Rust 学习解析器组合器 — 第三部分](https://juejin.im/post/5d1f29f7f265da1b961324b2) ([suhanyujie](https://github.com/suhanyujie) 翻译) From d1768e780754582521284eb16de901737ee4f774 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 26/58] Revert "fix" This reverts commit 566b057a4421702d5f5dc0b100d7c81e237deec7. --- integrals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrals.md b/integrals.md index b9176f16ace..6e5a87a5267 100644 --- a/integrals.md +++ b/integrals.md @@ -8699,7 +8699,7 @@ |[自动补全规则](https://juejin.im/post/5cd556ef6fb9a03218556cb7)|翻译|3| |[使用 PyTorch 在 MNIST 数据集上进行逻辑回归](https://juejin.im/post/5cc66d946fb9a032286173a7)|校对|1| -## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:15 当前积分:5 二零一九:15 +## 译者:[](https://github.com/Chorer) 历史贡献积分:15 当前积分:5 二零一九:15 |文章|类型|积分| |------|-------|-------| From 26d16a4b8b4f453cfaa3013320b9e489ca2e5f85 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 27/58] =?UTF-8?q?Revert=20"=E6=9B=B4=E6=96=B0=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=A7=AF=E5=88=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4d73dd9fae56450b6a48768f578a34c42669c1f7. --- integrals.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/integrals.md b/integrals.md index 6e5a87a5267..913d11485ee 100644 --- a/integrals.md +++ b/integrals.md @@ -7063,11 +7063,10 @@ |[一行 JavaScript 代码竟然让 FT.com 网站慢了十倍](https://juejin.im/post/5b7bb6dfe51d4538bf55aa5f)|校对|1| |[使用 Web Beacon API 记录活动](https://juejin.im/post/5b694b5de51d4519700fa56a)|校对|1| -## 译者:[YueYongDev](https://github.com/YueYongDev) 历史贡献积分:63 当前积分:28 二零一九:34 +## 译者:[YueYongDev](https://github.com/YueYongDev) 历史贡献积分:59.5 当前积分:24.5 二零一九:30.5 |文章|类型|积分| |------|-------|-------| -|[前端 vs 后端:哪一个适合你?](https://juejin.im/post/5d36b5e3f265da1bd3059a21)|翻译|3.5| |[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|翻译|6| |[在 Android 应用中使用矢量资源](https://juejin.im/post/5c943c97e51d45288201a30c)|翻译|3| |2019 年 3 月兑米家 LED 智能台灯 1 个|减去积分|35| @@ -7346,11 +7345,10 @@ |------|-------|-------| |[为 APP 设计通知提醒](https://juejin.im/post/5ba31ee3e51d450e4115500b)|校对|1.5| -## 译者:[zx-Zhu](https://github.com/zx-Zhu) 历史贡献积分:25.5 当前积分:10.5 二零一九:0 +## 译者:[zx-Zhu](https://github.com/zx-Zhu) 历史贡献积分:25.5 当前积分:25.5 |文章|类型|积分| |------|-------|-------| -|2019 年 X 月兑 Google 月饼安卓手办 1 个|减去积分|15| |[Kotlin 协程高级使用技巧](https://juejin.im/post/5c0f11986fb9a049be5d53eb)|校对|0.5| |[同时使用多个相机流](https://juejin.im/post/5c1071ece51d4570b57af8c8)|翻译|4.5| |[当 Kotlin 中的监听器包含多个方法时,如何让它 “巧夺天工”?](https://juejin.im/post/5c1e43646fb9a04a102f45ab)|校对|1| @@ -8699,12 +8697,10 @@ |[自动补全规则](https://juejin.im/post/5cd556ef6fb9a03218556cb7)|翻译|3| |[使用 PyTorch 在 MNIST 数据集上进行逻辑回归](https://juejin.im/post/5cc66d946fb9a032286173a7)|校对|1| -## 译者:[](https://github.com/Chorer) 历史贡献积分:15 当前积分:5 二零一九:15 +## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:14 当前积分:14 二零一九:14 |文章|类型|积分| |------|-------|-------| -|[前端 vs 后端:哪一个适合你?](https://juejin.im/post/5d36b5e3f265da1bd3059a21)|校对|1| -|2019 年 7 月兑掘金 T 恤黑色 1 个|减去积分|10| |[贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb)|校对|3.5| |[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|校对|1.5| |推荐英文文章一篇|奖励|1| @@ -8822,11 +8818,10 @@ |[Android中的简易协程:viewModelScope](https://juejin.im/post/5cf35ec76fb9a07ed657bbcd)|翻译|3| |[C++ 和 Android 本地 Activity 初探](https://juejin.im/post/5ce62d4851882532e9631b63)|翻译|2| -## 译者:[ZavierTang](https://github.com/ZavierTang) 历史贡献积分:18 当前积分:18 二零一九:18 +## 译者:[ZavierTang](https://github.com/ZavierTang) 历史贡献积分:17 当前积分:17 二零一九:17 |文章|类型|积分| |------|-------|-------| -|[前端 vs 后端:哪一个适合你?](https://juejin.im/post/5d36b5e3f265da1bd3059a21)|校对|1| |[Git:透过命令学概念 —— 第一部分](https://juejin.im/post/5d0b3c7ce51d4577531381e3)|校对|2.5| |[理解 React 中的高阶组件](https://juejin.im/post/5d1037966fb9a07ee4636de3)|翻译|3| |[13 种用于 DOM 操作的 JavaScript 方法](https://juejin.im/post/5cf65369f265da1bc94edad8)|校对|2| From 9b84245ac7497167c29d00fcdd1d0ea4729c4716 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 28/58] =?UTF-8?q?Revert=20"=E9=80=9A=E8=BF=87=E9=98=85?= =?UTF-8?q?=E8=AF=BB=E6=BA=90=E7=A0=81=E6=8F=90=E9=AB=98=E4=BD=A0=E7=9A=84?= =?UTF-8?q?=20JavaScript=20=E6=B0=B4=E5=B9=B3=20(#6179)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0c3be55820d845ae81860f63071e7e4238a9b9c2. --- ...avascript-knowledge-reading-source-code.md | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/TODO1/javascript-knowledge-reading-source-code.md b/TODO1/javascript-knowledge-reading-source-code.md index da8d5dcb455..d94f0557250 100644 --- a/TODO1/javascript-knowledge-reading-source-code.md +++ b/TODO1/javascript-knowledge-reading-source-code.md @@ -2,83 +2,83 @@ > * 原文作者:[Carl Mungazi](https://www.smashingmagazine.com/author/carl-mungazi/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/javascript-knowledge-reading-source-code.md](https://github.com/xitu/gold-miner/blob/master/TODO1/javascript-knowledge-reading-source-code.md) -> * 译者:[MarchYuanx](https://github.com/MarchYuanx) -> * 校对者:[imononoke](https://github.com/imononoke), [Baddyo](https://github.com/Baddyo) +> * 译者: +> * 校对者: -# 通过阅读源码提高你的 Javascript 水平 +# Improve Your JavaScript Knowledge By Reading Source Code -快速摘要:当你还处于编程生涯的初期阶段时,深入研究开源库和框架的源代码可能是一项艰巨的任务。在本文中,Carl Mungazi 分享了他如何克服恐惧,并开始用源码来提高他的知识水平和专业技能。他还使用了 Redux 来演示他如何解构一个代码库。 +Quick summary: When you are still early on in your programming career, digging into the source code of open source libraries and frameworks can be a daunting endeavor. In this article, Carl Mungazi shares how he got over his fear and began using source code to improve his knowledge and skills. He also uses Redux to demonstrate how he approaches breaking down a library. -你还记得你第一次深入研究你常用的库或框架的源码时的情景吗?对我来说,这一刻发生在三年前我作为前端开发者的第一份工作中。 +Do you remember the first time you dug deep into the source code of a library or framework you use frequently? For me, that moment came during my first job as a frontend developer three years ago. -当时我们刚刚完成了用于创建网络学习课程的内部遗留框架的重构。在重构开始时,我们花时间研究了许多不同的解决方案,包括 Mithril、Inferno、Angular、React、Aurelia、Vue 和 Polymer。那时我仅仅只是个小萌新(我刚从新闻工作转向 web 开发),我记得我对每个框架的复杂性感到恐惧,不理解它们是如何工作的。 +We had just finished rewriting an internal legacy framework we used to create e-learning courses. At the beginning of the rewrite, we had spent time investigating a number of different solutions including Mithril, Inferno, Angular, React, Aurelia, Vue, and Polymer. As I was very much a beginner (I had just switched from journalism to web development), I remember feeling intimidated by the complexity of each framework and not understanding how each one worked. -随着对我们所选择的 Mithril 框架研究的深入,我对它的理解也逐渐加深了。从那以后,我花了很多时间深入钻研那些在工作或个人项目中日常使用的库的内部结构,这显著地提升了我对 JavaScript —— 以及通用编程思想 —— 的了解。在这篇文章中,我将分享一些方法给你,你可以使用自己喜欢的库或框架,并将其作为学习工具。 +My understanding grew when I began investigating our chosen framework, Mithril, in greater depth. Since then, my knowledge of JavaScript — and programming in general — has been greatly helped by the hours I have spent digging deep into the guts of the libraries I use daily either at work or in my own projects. In this post, I will share some of the ways you can take your favorite library or framework and use it as an educational tool. -[![Mithril 中 hyperscript 函数的源码](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png) +[![The source code for Mithril’s hyperscript function](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png) -我要介绍的第一个源码阅读示例是 Mithril 的 hyperscript 函数。([高清预览](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png)) +My first introduction to reading code was via Mithril’s hyperscript function. ([Large preview](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png)) -### 阅读源码的好处 +### The Benefits Of Reading Source Code -阅读源代码的一个主要好处是可以学到很多东西。在我第一次读 Mithril 代码库时,我对虚拟 DOM 的概念还很模糊。当我读完后,我了解到虚拟 DOM 是一种技术,它创建一个对象树,用于描述用户界面的外观。然后使用 DOM APIs(如 `document.createElement`)将对象树转换为 DOM 元素。通过创建描述用户界面的更新状态的新对象树,然后将其与旧对象树进行比较来执行更新。 +One of the major benefits of reading source code is the number of things you can learn. When I first looked into Mithril’s codebase, I had a vague idea of what the virtual DOM was. When I finished, I came away with the knowledge that the virtual DOM is a technique which involves creating a tree of objects that describe what your user interface should look like. That tree is then turned into DOM elements using DOM APIs such as `document.createElement`. Updates are performed by creating a new tree describing the future state of the user interface and then comparing it with objects from the old tree. -我在各种文章和教程中已经阅读了所有这些内容,虽然这很有帮助,但对我来说,能够在我们提供的应用程序的环境中观察到它工作是非常有启发性的。它还教会我在比较不同框架时应该考虑哪些因素。例如,我现在知道要考虑这样的问题,“每个框架执行更新的方式如何影响性能和用户体验?”,而不是只看框架在 GitHub 上 star 的数量。 +I had read about all of this in various articles and tutorials, and whilst it was helpful, being able to observe it at work in the context of an application we had shipped was very illuminating for me. It also taught me which questions to ask when comparing different frameworks. Instead of looking at GitHub stars, for example, I now knew to ask questions such as, “How does the way each framework performs updates affect performance and the user experience?” -另一个好处是你对优秀的程序架构的理解和鉴赏能力提升了。虽然大多数开源项目的存储库通常遵循相同的结构,但每个项目都包含差异。Mithril 的结构非常简单,如果你熟悉它的 API,你可以根据文件夹名称推测出其中的代码的功能,如 `render`、`router` 和 `request`。另一方面,React 的结构反映了它的新架构。维护人员将负责 UI 更新的模块(`react concerner`)与负责呈现 DOM 元素的模块(`react dom`)分开。 +Another benefit is an increase in your appreciation and understanding of good application architecture. Whilst most open-source projects generally follow the same structure with their repositories, each of them contains differences. Mithril’s structure is pretty flat and if you are familiar with its API, you can make educated guesses about the code in folders such as `render`, `router` and `request`. On the other hand, React’s structure reflects its new architecture. The maintainers have separated the module responsible for UI updates (`react-reconciler`) from the module responsible for rendering DOM elements (`react-dom`). -这样做的好处之一是,开发人员现在更容易通过挂进 `react-reconciler` 包来编写自己的[自定义渲染器](https://github.com/chentsulin/awesome-react-renderer)。我最近研究过的模块打包工具 Parcel 也有像 React 这样的 `packages` 文件夹。主模块名为 `parcel-bundler`,它包含负责创建包、启动热模块服务器和命令行工具的代码。 +One of the benefits of this is that it is now easier for developers to write their own [custom renderers](https://github.com/chentsulin/awesome-react-renderer) by hooking into the `react-reconciler` package. Parcel, a module bundler I have been studying recently, also has a `packages` folder like React. The key module is named `parcel-bundler` and it contains the code responsible for creating bundles, spinning up the hot module server and the command-line tool. -[![JavaScript 语言规范中解释 Object.prototype.toString 原理的章节](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png) +[![The section of the JavaScript specification which explains how Object.prototype.toString works](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png) -不久之后,你所阅读的源码将引导你找到 JavaScript 规范。([高清预览](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png)) +It will not be long before the source code you are reading leads you to the JavaScript specification. ([Large preview](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png)) -另一个好处 —— 令我感到惊讶的是 —— 你可以更轻松地阅读定义语言如何工作的官方 JavaScript 规范。我第一次阅读规范是在研究 `throw Error` 与 `throw new Error`(剧透警告 —— [二者没有区别](http://www.ecma-international.org/ecma-262/7.0/#sec-error-constructor))之间的区别时。我研究这个问题是因为我注意到 Mithril 在其 `m` 函数的实现中使用了 `throw Error`,我想知道这种用法是否比使用 throw new Error 更好。从那以后,我还了解了逻辑运算符 `&&` 和 `||` [不一定返回布尔值](https://tc39.es/ecma262/#prod-LogicalORExpression),找到了控制 `==` 等于运算符如何强制转换值的[规则](http://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison)和 `Object.prototype.toString.call({})` 返回 `'[object Object]'` 的[原因](http://www.ecma-international.org/ecma-262/#sec-object.prototype.tostring)。 +Yet another benefit — which came as a welcome surprise to me — is you become more comfortable reading the official JavaScript specification which defines how the language works. The first time I read the spec was when I was investigating the difference between `throw Error` and `throw new Error` (spoiler alert — there is [none](http://www.ecma-international.org/ecma-262/7.0/#sec-error-constructor)). I looked into this because I noticed that Mithril used `throw Error` in the implementation of its `m` function and I wondered if there was a benefit to using it over `throw new Error`. Since then, I have also learnt that the logical operators `&&` and `||` [do not necessarily return booleans](https://tc39.es/ecma262/#prod-LogicalORExpression), found the [rules](http://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) which govern how the `==` equality operator coerces values and the [reason](http://www.ecma-international.org/ecma-262/#sec-object.prototype.tostring) `Object.prototype.toString.call({})` returns `'[object Object]'`. -### 阅读源码的技巧 +### Techniques For Reading Source Code -有很多方法可以处理源码。我发现最简单的方法是从你选择的库中选择一个方法,并记录当你调用它时会发生什么。不要每一个步骤都记录,而是尝试理解它的整体流程和结构。 +There are many ways of approaching source code. I have found the easiest way to start is by selecting a method from your chosen library and documenting what happens when you call it. Do not document every single step but try to identify its overall flow and structure. -我最近用这个方法阅读了 ReactDOM.render 的源码,因此学到了很多关于 React Fiber 及其实现背后的一些原因。谢天谢地,由于 React 是一个流行的框架,在同样的问题上,我找到了很多其他开发者撰写的文章,这让我的学习进程快了许多。 +I did this recently with `ReactDOM.render` and consequently learned a lot about React Fiber and some of the reasons behind its implementation. Thankfully, as React is a popular framework, I came across a lot of articles written by other developers on the same issue and this sped up the process. -这次深入研究还让我明白了[合作调度](https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API)的概念、[`window.requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) 方法和一个[链接列表的实际示例](https://github.com/facebook/react/blob/v16.7.0/packages/react-reconciler/src/ReactUpdateQueue.js#L10)(React 通过将更新放入一个队列来处理它们,这个队列是一个按优先级排列的链接列表)。在研究过程中,建议使用库创建非常基本的应用程序。这使得调试更容易,因为你不必处理由其他库引起的堆栈跟踪。 +This deep dive also introduced me to the concepts of [co-operative scheduling](https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API), the `[window.requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)` method and a [real world example of linked lists](https://github.com/facebook/react/blob/v16.7.0/packages/react-reconciler/src/ReactUpdateQueue.js#L10) (React handles updates by putting them in a queue which is a linked list of prioritised updates). When doing this, it is advisable to create a very basic application using the library. This makes it easier when debugging because you do not have to deal with the stack traces caused by other libraries. -如果我不打算进行深入研究,我会打开正在开发的项目中的 /node_modules 文件夹,或者到 GitHub 仓库中去查看源码。这通常发生在我遇到一个 bug 或有趣的特性时。在 GitHub 上阅读代码时,请确保你阅读的是最新版本。你可以通过单击用于更改分支的按钮并选择“tags”来查看具有最新版本标记的提交中的代码。库和框架永远在进行更改,因此你不会想了解可能在下一版本中删除的内容。 +If I am not doing an in-depth review, I will open up the `/node_modules` folder in a project I am working on or I will go to the GitHub repository. This usually happens when I come across a bug or interesting feature. When reading code on GitHub, make sure you are reading from the latest version. You can view the code from commits with the latest version tag by clicking the button used to change branches and select “tags”. Libraries and frameworks are forever undergoing changes so you do not want to learn about something which may be dropped in the next version. -还有另一种不太复杂的阅读源码的方法,我喜欢称之为“粗略一瞥”。在我开始阅读代码的早期,我安装了 **express.js**,打开了它的 `/node_modules` 文件夹并浏览了它的依赖项。如果 `README` 没有给我一个满意的解释,我就会阅读源码。这样做让我得到了这些有趣的发现: +Another less involved way of reading source code is what I like to call the ‘cursory glance’ method. Early on when I started reading code, I installed **express.js**, opened its `/node_modules` folder and went through its dependencies. If the `README` did not provide me with a satisfactory explanation, I read the source. Doing this led me to these interesting findings: -* Express 依赖于两个模块,两个模块都合并对象,但以非常不同的方式进行合并。`merge-descriptors` 只添加直接在源对象上直接找到的属性,它还合并了不可枚举的属性,而 `utils-merge` 只迭代对象的可枚举属性以及在其原型链中找到的属性。`merge-descriptors` 使用 `Object.getOwnPropertyNames()` 和 `Object.getOwnPropertyDescriptor()` 而 `utils-merge` 使用 `for..in`; -* `setprototypeof` 模块提供了一种设置实例化对象原型的跨平台方式; -* `escape-html` 是一个有 78 行代码的模块,用于转义一系列内容,可以在 HTML 内容中进行插值。 +* Express depends on two modules which both merge objects but do so in very different ways. `merge-descriptors` only adds properties directly found directly on the source object and it also merges non-enumerable properties whilst `utils-merge` only iterates over an object’s enumerable properties as well as those found in its prototype chain. `merge-descriptors` uses `Object.getOwnPropertyNames()` and `Object.getOwnPropertyDescriptor()` whilst `utils-merge` uses `for..in`; +* The `setprototypeof` module provides a cross platform way of setting the prototype of an instantiated object; +* `escape-html` is a 78-line module for escaping a string of content so it can be interpolated in HTML content. -虽然这些发现不可能立即有用,但是对库或框架所使用的依赖关系有一个大致的了解是有用的。 +Whilst the findings are not likely to be useful immediately, having a general understanding of the dependencies used by your library or framework is useful. -在调试前端代码时,浏览器的调试工具是你最好的朋友。除此之外,它们允许你随时停止程序并检查其状态,跳过函数的执行或进入或退出程序。有时这不能立即生效,因为代码已经压缩。我倾向于将它解压并将解压的代码复制到 `/node_modules` 文件夹中的对应文件中。 +When it comes to debugging front-end code, your browser’s debugging tools are your best friend. Among other things, they allow you to stop the program at any time and inspect its state, skip a function’s execution or step into or out of it. Sometimes this will not be immediately possible because the code has been minified. I tend to unminify it and copy the unminified code into the relevant file in the `/node_modules` folder. -[![ReactDOM.render 函数的源码](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png) +[![The source code for the ReactDOM.render function](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png) -像处理任何其他应用程序一样处理调试。形成一个假设,然后测试它。([高清预览](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png)) +Approach debugging as you would any other application. Form a hypothesis and then test it. ([Large preview](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png)) -### 研究案例:Redux 的 Connect 函数 +### Case Study: Redux’s Connect Function -React-Redux 是一个用于管理 React 应用程序状态的库。在处理这些流行的库时,我首先搜索有关其实现的文章。在这个案例研究中,我找到了这篇[文章](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation)。这是阅读源码的另一个好处。研究阶段通常会引导你阅读这样的信息性文章,这些文章会提高你的思考与理解。 +React-Redux is a library used to manage the state of React applications. When dealing with popular libraries such as these, I start by searching for articles that have been written about its implementation. In doing so for this case study, I came across this [article](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation). This is another good thing about reading source code. The research phase usually leads you to informative articles such as this which only improve your own thinking and understanding. -`connect` 是一个将 React 组件连接到应用程序的 Redux 存储的 React-Redux 函数。怎么连?好的,根据[文档](https://react-redux.js.org/api/connect),它执行以下操作: +`connect` is a React-Redux function which connects React components to an application’s Redux store. How? Well, according to the [docs](https://react-redux.js.org/api/connect), it does the following: -> “...返回一个新的连接的组件类,它包装您传入的组件。” +> “...returns a new, connected component class that wraps the component you passed in.” -看完之后,我会问下列问题: +After reading this, I would ask the following questions: -* 我是否知道哪些模式或概念,其函数能够接受一个输入并将输入封装、加上附加功能再返回输出? -* 如果我知道这样的模式,我如何根据文档中给出的解释来实现它? +* Do I know any patterns or concepts in which functions take an input and then return that same input wrapped with additional functionality? +* If I know of any such patterns, how would I implement this based on the explanation given in the docs? -通常,下一步是创建一个使用 `connect` 的非常基础的示例应用程序。但是,在这种情况下,我选择使用我们在 [limejump](https://limejump.com/) 上构建的新的 React 应用程序,因为我希望在最终要进入生产环境的应用程序的上下文环境中理解 `connect`。 +Usually, the next step would be to create a very basic example app which uses `connect`. However, on this occasion I opted to use the new React app we are building at [Limejump](https://limejump.com/) because I wanted to understand `connect` within the context of an application which will eventually be going into a production environment. -我关注的组件看起来像这样: +The component I am focusing on looks like this: ``` class MarketContainer extends Component { - // 简洁起见,省略代码(code omitted for brevity) + // code omitted for brevity } const mapDispatchToProps = dispatch => { @@ -90,7 +90,7 @@ const mapDispatchToProps = dispatch => { export default connect(null, mapDispatchToProps)(MarketContainer); ``` -它是一个容器组件,包裹着四个较小的连接的组件。在导出 `connect` 方法的[文件](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/connect.js)中,你首先看到的是这个注释:**connect is a facade over connectAdvanced**。没走多远,我们就有了第一个学习的时刻:**一个观察 [facade](http://jargon.js.org/_glossary/FACADE_PATTERN.md) 设计模式的机会**。在文件末尾,我们看到 `connect` 导出了对名为 `createConnect` 的函数的调用。它的参数是一组默认值,这些默认值被这样解构: +It is a container component which wraps four smaller connected components. One of the first things you come across in the [file](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/connect.js) which exports `connect` method is this comment: **connect is a facade over connectAdvanced**. Without going far we have our first learning moment: **an opportunity to observe the [facade](http://jargon.js.org/_glossary/FACADE_PATTERN.md) design pattern in action**. At the end of the file we see that `connect` exports an invocation of a function called `createConnect`. Its parameters are a bunch of default values which have been destructured like this: ``` export function createConnect({ @@ -102,7 +102,7 @@ export function createConnect({ } = {}) ``` -同样,我们遇到了另一个学习时刻:**导出调用函数**和**解构默认函数参数**。解构部分是一个学习时刻,因为它的代码编写如下: +Again, we come across another learning moment: **exporting invoked functions** and **destructuring default function arguments**. The destructuring part is a learning moment because had the code been written like this: ``` export function createConnect({ @@ -114,29 +114,29 @@ export function createConnect({ }) ``` -它会导致这个错误 `Uncaught TypeError: Cannot destructure property 'connectHOC' of 'undefined' or 'null'.`。这是因为函数没有可供回调的默认参数。 +It would have resulted in this error `Uncaught TypeError: Cannot destructure property 'connectHOC' of 'undefined' or 'null'.` This is because the function has no default argument to fall back on. -**注意:有关这方面的更多信息,您可以阅读 David Walsh 的[文章](https://davidwalsh.name/destructuring-function-arguments)。根据你对语言的了解,一些学习时刻可能看起来微不足道,因此最好将注意力放在您以前从未见过的事情上,或需要了解更多信息的事情上。** +**Note**: **For more on this, you can read David Walsh’s [article](https://davidwalsh.name/destructuring-function-arguments). Some learning moments may seem trivial, depending on your knowledge of the language, and so it might be better to focus on things you have not seen before or need to learn more about.** -`createConnect` 在其函数内部并不执行任何操作。它只是返回一个名为 connect 的函数,也就是我在这里用到的: +`createConnect` itself does nothing in its function body. It returns a function called `connect`, the one I used here: ```javascript export default connect(null, mapDispatchToProps)(MarketContainer) ``` -它需要四个参数,都是可选的,前三个参数都通过 [match](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/connect.js#L25) 函数来帮助根据参数是否存在以及它们的值类型来定义它们的行为。现在,因为提供给 `match` 的第二个参数是导入 `connect` 的三个函数之一,我必须决定要遵循哪个线程。 +It takes four arguments, all optional, and the first three arguments each go through a `[match](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/connect.js#L25)` function which helps define their behaviour according to whether the arguments are present and their value type. Now, because the second argument provided to `match` is one of three functions imported into `connect`, I have to decide which thread to follow. -如果那些参数是函数,[代理函数](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/wrapMapToProps.js#L29)被用来将第一个参数包装为 `connect`,这是也一个学习的时刻。[isPlainObject](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/utils/isPlainObject.js) 用于检查普通对象或 [warning](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/utils/warning.js) 模块,它揭示了如何将调试器设置为[中断所有异常](https://developers.google.com/web/tools/chrome-devtools/javascript/breakpoints#exceptions)。在匹配函数之后,我们来看 `connectHOC`,这个函数接受我们的 React 组件并将它连接到 Redux。它是另一个函数调用,返回 [wrapWithConnect](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/components/connectAdvanced.js#L123),该函数实际处理将组件连接到存储的操作。 +There are learning moments with the [proxy function](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/wrapMapToProps.js#L29) used to wrap the first argument to `connect` if those arguments are functions, the `[isPlainObject](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/utils/isPlainObject.js)` utility used to check for plain objects or the `[warning](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/utils/warning.js)` module which reveals how you can set your debugger to [break on all exceptions](https://developers.google.com/web/tools/chrome-devtools/javascript/breakpoints#exceptions). After the match functions, we come to `connectHOC`, the function which takes our React component and connects it to Redux. It is another function invocation which returns `[wrapWithConnect](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/components/connectAdvanced.js#L123)`, the function which actually handles connecting the component to the store. -看看 `connectHOC` 的实现,我可以理解为什么它需要 `connect` 来隐藏它的实现细节。它是 React-Redux 的核心,包含不需要通过 `connect` 展现的逻辑。尽管我原本打算在这个地方结束对它的深度探讨,我也会继续,这将是查阅之前发现的参考资料的最佳时机,因为它包含对代码库的非常详细的解释。 +Looking at `connectHOC`’s implementation, I can appreciate why it needs `connect` to hide its implementation details. It is the heart of React-Redux and contains logic which does not need to be exposed via `connect`. Even though I will end the deep dive here, had I continued, this would have been the perfect time to consult the reference material I found earlier as it contains an incredibly detailed explanation of the codebase. -### 总结 +### Summary -阅读源码起初很困难,但与任何事情一样,随着时间的推移变得更容易。我们的目标不是理解一切,而是要获得不同的视角和新知识。关键是要对整个过程进行深思熟虑,并对所有事情充满好奇。 +Reading source code is difficult at first but as with anything, it becomes easier with time. The goal is not to understand everything but to come away with a different perspective and new knowledge. The key is to be deliberate about the entire process and intensely curious about everything. -例如,我发现 `isPlainObject` 函数很有趣,因为它使用 `if (typeof obj !== 'object' || obj === null) return false` 以确保给定的参数是普通对象。 当我第一次阅读它的实现时,我想知道为什么它没有使用 `Object.prototype.toString.call(opts) !== '[object Object]'` ,这样能用更少的代码且区分对象和对象子类型,如 Date 对象。但是,读完下一行我发现,在极小概率情况下,例如开发者使用 `connect` 时返回了 Date 对象,这将由`Object.getPrototypeOf(obj) === null` 检查处理。 +For example, I found the `isPlainObject` function interesting because it uses this `if (typeof obj !== 'object' || obj === null) return false` to make sure the given argument is a plain object. When I first read its implementation, I wondered why it did not use `Object.prototype.toString.call(opts) !== '[object Object]'`, which is less code and distinguishes between objects and object sub types such as the Date object. However, reading the next line revealed that in the extremely unlikely event that a developer using `connect` returns a Date object, for example, this will be handled by the `Object.getPrototypeOf(obj) === null` check. -`isPlainObject` 中另一个吸引人的地方是这段代码: +Another bit of intrigue in `isPlainObject` is this code: ```javascript while (Object.getPrototypeOf(baseProto) !== null) { @@ -144,9 +144,9 @@ while (Object.getPrototypeOf(baseProto) !== null) { } ``` -有些谷歌搜索结果指向这个 [StackOverflow 问答](https://stackoverflow.com/questions/51722354/the-implementation-of-isplainobject-function-in-redux/51726564#51726564)和这个在 GitHub 仓库中的 [Redux issue](https://github.com/reduxjs/redux/pull/2599#issuecomment-342849867),解释该代码如何处理诸如检查源自 iFrame 的对象这类情况。 +Some Google searching led me to [this](https://stackoverflow.com/questions/51722354/the-implementation-of-isplainobject-function-in-redux/51726564#51726564) StackOverflow thread and the Redux [issue](https://github.com/reduxjs/redux/pull/2599#issuecomment-342849867) explaining how that code handles cases such as checking against objects which originate from an iFrame. -#### 其它的阅读源码的参考链接 +#### Useful Links On Reading Source Code * “[How To Reverse Engineer Frameworks](https://blog.angularindepth.com/level-up-your-reverse-engineering-skills-8f910ae10630),” Max Koretskyi, Medium * “[How To Read Code](https://github.com/aredridel/how-to-read-code/blob/master/how-to-read-code.md),” Aria Stewart, GitHub From 65e2608f385b7295464dcd345bba7b89fbaaf836 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 29/58] Revert "Create algebraic-effects-for-the-rest-of-us.md (#6225)" This reverts commit 07bce3041f15fbfa87f112b815752726ac6d58f2. --- TODO1/algebraic-effects-for-the-rest-of-us.md | 392 ------------------ 1 file changed, 392 deletions(-) delete mode 100644 TODO1/algebraic-effects-for-the-rest-of-us.md diff --git a/TODO1/algebraic-effects-for-the-rest-of-us.md b/TODO1/algebraic-effects-for-the-rest-of-us.md deleted file mode 100644 index 3ffda6e4e02..00000000000 --- a/TODO1/algebraic-effects-for-the-rest-of-us.md +++ /dev/null @@ -1,392 +0,0 @@ -> * 原文地址:[Algebraic Effects for the Rest of Us](https://overreacted.io/algebraic-effects-for-the-rest-of-us/) -> * 原文作者:[Dan Abramov](https://overreacted.io/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/algebraic-effects-for-the-rest-of-us.md](https://github.com/xitu/gold-miner/blob/master/TODO1/algebraic-effects-for-the-rest-of-us.md) -> * 译者: -> * 校对者: - -# Algebraic Effects for the Rest of Us - -Have you heard about **algebraic effects**? - -My first attempts to figure out what they are or why I should care about them were unsuccessful. I found a [few](https://www.eff-lang.org/handlers-tutorial.pdf) [pdfs](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/algeff-tr-2016-v2.pdf) but they only confused me more. (There’s something about academic pdfs that makes me sleepy.) - -But my colleague Sebastian [kept](https://mobile.twitter.com/sebmarkbage/status/763792452289343490) [referring](https://mobile.twitter.com/sebmarkbage/status/776883429400915968) [to](https://mobile.twitter.com/sebmarkbage/status/776840575207116800) [them](https://mobile.twitter.com/sebmarkbage/status/969279885276454912) as a mental model for some things we do inside of React. (Sebastian works on the React team and came up with quite a few ideas, including Hooks and Suspense.) At some point, it became a running joke on the React team, with many of our conversations ending with: - -[![](https://overreacted.io/static/5fb19385d24afb94180b6ba9aeb2b8d4/79ad4/effects.jpg)](https://overreacted.io/static/5fb19385d24afb94180b6ba9aeb2b8d4/79ad4/effects.jpg) - -It turned out that algebraic effects are a cool concept and not as scary as I thought from those pdfs. **If you’re just using React, you don’t need to know anything about them — but if you’re feeling curious, like I was, read on.** - -**(Disclaimer: I’m not a programming language researcher, and might have messed something up in my explanation. I am not an authority on this topic so let me know!)** - -### Not Production Ready Yet - -**Algebraic Effects** are a research programming language feature. This means that **unlike `if`, functions, or even `async / await`, you probably can’t really use them in production yet.** They are only supported by a [few](https://www.eff-lang.org/) [languages](https://www.microsoft.com/en-us/research/project/koka/) that were created specifically to explore that idea. There is progress on productionizing them in OCaml which is… still [ongoing](https://github.com/ocaml-multicore/ocaml-multicore/wiki). In other words, [Can’t Touch This](https://www.youtube.com/watch?v=otCpCn0l4Wo). - -> Edit: a few people mentioned that LISP languages [do offer something similar](#learn-more), so you can use it in production if you write LISP. - -### So Why Should I Care? - -Imagine that you’re writing code with `goto`, and somebody shows you `if` and `for` statements. Or maybe you’re deep in the callback hell, and somebody shows you `async / await`. Pretty cool, huh? - -If you’re the kind of person who likes to learn about programming ideas several years before they hit the mainstream, it might be a good time to get curious about algebraic effects. Don’t feel like you **have to** though. It is a bit like thinking about `async / await` in 1999. - -### Okay, What Are Algebraic Effects? - -The name might be a bit intimidating but the idea is simple. If you’re familiar with `try / catch` blocks, you’ll figure out algebraic effects very fast. - -Let’s recap `try / catch` first. Say you have a function that throws. Maybe there’s a bunch of functions between it and the `catch` block: - -```js -function getName(user) { - let name = user.name; - if (name === null) { - throw new Error('A girl has no name'); } - return name; -} - -function makeFriends(user1, user2) { - user1.friendNames.add(getName(user2)); - user2.friendNames.add(getName(user1)); -} - -const arya = { name: null }; -const gendry = { name: 'Gendry' }; -try { - makeFriends(arya, gendry); -} catch (err) { - console.log("Oops, that didn't work out: ", err);} -``` - -We `throw` inside `getName`, but it “bubbles” up right through `makeFriends` to the closest `catch` block. This is an important property of `try / catch`. **Things in the middle don’t need to concern themselves with error handling.** - -Unlike error codes in languages like C, with `try / catch`, you don’t have to manually pass errors through every intermediate layer in the fear of losing them. They get propagated automatically. - -### What Does This Have to Do With Algebraic Effects? - -In the above example, once we hit an error, we can’t continue. When we end up in the `catch` block, there’s no way we can continue executing the original code. - -We’re done. It’s too late. The best we can do is to recover from a failure and maybe somehow retry what we were doing, but we can’t magically “go back” to where we were, and do something different. **But with algebraic effects, **we can**.** - -This is an example written in a hypothetical JavaScript dialect (let’s call it ES2025 just for kicks) that lets us **recover** from a missing `user.name`: - -```js -function getName(user) { - let name = user.name; - if (name === null) { - name = perform 'ask_name'; } - return name; -} - -function makeFriends(user1, user2) { - user1.friendNames.add(getName(user2)); - user2.friendNames.add(getName(user1)); -} - -const arya = { name: null }; -const gendry = { name: 'Gendry' }; -try { - makeFriends(arya, gendry); -} handle (effect) { - if (effect === 'ask_name') { resume with 'Arya Stark'; }} -``` - -**(I apologize to all readers from 2025 who search the web for “ES2025” and find this article. If algebraic effects are a part of JavaScript by then, I’d be happy to update it!)** - -Instead of `throw`, we use a hypothetical `perform` keyword. Similarly, instead of `try / catch`, we use a hypothetical `try / handle`. **The exact syntax doesn’t matter here — I just came up with something to illustrate the idea.** - -So what’s happening? Let’s take a closer look. - -Instead of throwing an error, we **perform an effect**. Just like we can `throw` any value, we can pass any value to `perform`. In this example, I’m passing a string, but it could be an object, or any other data type: - -```js -function getName(user) { - let name = user.name; - if (name === null) { - name = perform 'ask_name'; } - return name; -} -``` - -When we `throw` an error, the engine looks for the closest `try / catch` error handler up the call stack. Similarly, when we `perform` an effect, the engine would search for the closest `try / handle` **effect handler** up the call stack: - -```js -try { - makeFriends(arya, gendry); -} handle (effect) { if (effect === 'ask_name') { - resume with 'Arya Stark'; - } -} -``` - -This effect lets us decide how to handle the case where a name is missing. The novel part here (compared to exceptions) is the hypothetical `resume with`: - -```js -try { - makeFriends(arya, gendry); -} handle (effect) { - if (effect === 'ask_name') { - resume with 'Arya Stark'; } -} -``` - -This is the part you can’t do with `try / catch`. It lets us **jump back to where we performed the effect, and pass something back to it from the handler**. 🤯 - -```js -function getName(user) { - let name = user.name; - if (name === null) { - // 1. We perform an effect here name = perform 'ask_name'; - // 4. ...and end up back here (name is now 'Arya Stark') } - return name; -} - -// ... - -try { - makeFriends(arya, gendry); -} handle (effect) { - // 2. We jump to the handler (like try/catch) if (effect === 'ask_name') { - // 3. However, we can resume with a value (unlike try/catch!) resume with 'Arya Stark'; - } -} -``` - -This takes a bit of time to get comfortable with, but it’s really not much different conceptually from a “resumable `try / catch`”. - -Note, however, that **algebraic effects are much more flexible than `try / catch`, and recoverable errors are just one of many possible use cases.** I started with it only because I found it easiest to wrap my mind around it. - -### A Function Has No Color - -Algebraic effects have interesting implications for asynchronous code. - -In languages with an `async / await`, [functions usually have a “color”](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/). For example, in JavaScript we can’t just make `getName` asynchronous without also “infecting” `makeFriends` and its callers with being `async`. This can be a real pain if **a piece of code sometimes needs to be sync, and sometimes needs to be async**. - -```js -// If we want to make this async... -async getName(user) { - // ... -} - -// Then this has to be async too... -async function makeFriends(user1, user2) { - user1.friendNames.add(await getName(user2)); - user2.friendNames.add(await getName(user1)); -} - -// And so on... -``` - -JavaScript generators are [similar](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*): if you’re working with generators, things in the middle also have to be aware of generators. - -So how is that relevant? - -For a moment, let’s forget about `async / await` and get back to our example: - -```js -function getName(user) { - let name = user.name; - if (name === null) { - name = perform 'ask_name'; } - return name; -} - -function makeFriends(user1, user2) { - user1.friendNames.add(getName(user2)); - user2.friendNames.add(getName(user1)); -} - -const arya = { name: null }; -const gendry = { name: 'Gendry' }; -try { - makeFriends(arya, gendry); -} handle (effect) { - if (effect === 'ask_name') { resume with 'Arya Stark'; }} -``` - -What if our effect handler didn’t know the “fallback name” synchronously? What if we wanted to fetch it from a database? - -It turns out, we can call `resume with` asynchronously from our effect handler without making any changes to `getName` or `makeFriends`: - -```js -function getName(user) { - let name = user.name; - if (name === null) { - name = perform 'ask_name'; - } - return name; -} - -function makeFriends(user1, user2) { - user1.friendNames.add(getName(user2)); - user2.friendNames.add(getName(user1)); -} - -const arya = { name: null }; -const gendry = { name: 'Gendry' }; -try { - makeFriends(arya, gendry); -} handle (effect) { - if (effect === 'ask_name') { setTimeout(() => { resume with 'Arya Stark'; }, 1000); }} -``` - -In this example, we don’t call `resume with` until a second later. You can think of `resume with` as a callback which you may only call once. (You can also impress your friends by calling it a “one-shot delimited continuation.”) - -Now the mechanics of algebraic effects should be a bit clearer. When we `throw` an error, the JavaScript engine “unwinds the stack”, destroying local variables in the process. However, when we `perform` an effect, our hypothetical engine would **create a callback** with the rest of our function, and `resume with` calls it. - -**Again, a reminder: the concrete syntax and specific keywords are made up for this article. They’re not the point, the point is in the mechanics.** - -### A Note on Purity - -It’s worth noting that algebraic effects came out of functional programming research. Some of the problems they solve are unique to pure functional programming. For example, in languages that **don’t** allow arbitrary side effects (like Haskell), you have to use concepts like Monads to wire effects through your program. If you ever read a Monad tutorial, you know they’re a bit tricky to think about. Algebraic effects help do something similar with less ceremony. - -This is why so much discussion about algebraic effects is incomprehensible to me. (I [don’t know](https://overreacted.io/things-i-dont-know-as-of-2018/) Haskell and friends.) However, I do think that even in an impure language like JavaScript, **algebraic effects can be a very powerful instrument to separate the **what** from the **how** in the code.** - -They let you write code that focuses on **what** you’re doing: - -```js -function enumerateFiles(dir) { - const contents = perform OpenDirectory(dir); perform Log('Enumerating files in ', dir); for (let file of contents.files) { - perform HandleFile(file); } - perform Log('Enumerating subdirectories in ', dir); for (let directory of contents.dir) { - // We can use recursion or call other functions with effects - enumerateFiles(directory); - } - perform Log('Done');} -``` - -And later wrap it with something that specifies **how**: - -```js -let files = []; -try { - enumerateFiles('C:\\'); -} handle (effect) { - if (effect instanceof Log) { - myLoggingLibrary.log(effect.message); resume; } else if (effect instanceof OpenDirectory) { - myFileSystemImpl.openDir(effect.dirName, (contents) => { resume with contents; }); } else if (effect instanceof HandleFile) { - files.push(effect.fileName); resume; } -} -// The `files` array now has all the files -``` - -Which means that those pieces can even become librarified: - -```js -import { withMyLoggingLibrary } from 'my-log'; -import { withMyFileSystem } from 'my-fs'; - -function ourProgram() { - enumerateFiles('C:\\'); -} - -withMyLoggingLibrary(() => { - withMyFileSystem(() => { - ourProgram(); - }); -}); -``` - -Unlike `async / await` or Generators, **algebraic effects don’t require complicating functions “in the middle”**. Our `enumerateFiles` call could be deep within `ourProgram`, but as long as there’s an effect handler **somewhere above** for each of the effects it may perform, our code would still work. - -Effect handlers let us decouple the program logic from its concrete effect implementations without too much ceremony or boilerplate code. For example, we could completely override the behavior in tests to use a fake filesystem and to snapshot logs instead of outputting them to the console: - -```js -import { withFakeFileSystem } from 'fake-fs'; - -function withLogSnapshot(fn) { - let logs = []; - try { - fn(); - } handle (effect) { - if (effect instanceof Log) { - logs.push(effect.message); - resume; - } - } - // Snapshot emitted logs. - expect(logs).toMatchSnapshot(); -} - -test('my program', () => { - const fakeFiles = [/* ... */]; - withFakeFileSystem(fakeFiles, () => { withLogSnapshot(() => { ourProgram(); }); });}); -``` - -Because there is no [“function color”](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) (code in the middle doesn’t need to be aware of effects) and effect handlers are **composable** (you can nest them), you can create very expressive abstractions with them. - -### A Note on Types - -Because algebraic effects are coming from statically typed languages, much of the debate about them centers on the ways they can be expressed in types. This is no doubt important but can also make it challenging to grasp the concept. That’s why this article doesn’t talk about types at all. However, I should note that usually the fact that a function can perform an effect would be encoded into its type signature. So you shouldn’t end up in a situation where random effects are happening and you can’t trace where they’re coming from. - -You might argue that algebraic effects technically do [“give color”](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) to functions in statically typed languages because effects are a part of the type signature. That’s true. However, fixing a type annotation for an intermediate function to include a new effect is not by itself a semantic change — unlike adding `async` or turning a function into a generator. Inference can also help avoid cascading changes. An important difference is you can “bottle up” an effect by providing a noop or a mock implementation (for example, a sync call for an async effect), which lets you prevent it from reaching the outer code if necessary — or turn it into a different effect. - -### Should We Add Algebraic Effects to JavaScript? - -Honestly, I don’t know. They are very powerful, and you can make an argument that they might be **too** powerful for a language like JavaScript. - -I think they could be a great fit for a language where mutation is uncommon, and where the standard library fully embraced effects. If you primarily do `perform Timeout(1000)`, `perform Fetch('http://google.com')`, and `perform ReadFile('file.txt')`, and your language has pattern matching and static typing for effects, it might be a very nice programming environment. - -Maybe that language could even compile to JavaScript! - -### How Is All of This Relevant to React? - -Not that much. You can even say it’s a stretch. - -If you watched [my talk about Time Slicing and Suspense](https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html), the second part involves components reading data from a cache: - -```js -function MovieDetails({ id }) { - // What if it's still being fetched? - const movie = movieCache.read(id); -} -``` - -**(The talk uses a slightly different API but that’s not the point.)** - -This builds on a React feature called “Suspense”, which is in active development for the data fetching use case. The interesting part, of course, is that the data might not yet be in the `movieCache` — in which case we need to do **something** because we can’t proceed below. Technically, in that case the `read()` call throws a Promise (yes, **throws** a Promise — let that sink in). This “suspends” the execution. React catches that Promise, and remembers to retry rendering the component tree after the thrown Promise resolves. - -This isn’t an algebraic effect per se, even though this trick was [inspired](https://mobile.twitter.com/sebmarkbage/status/941214259505119232) by them. But it achieves the same goal: some code below in the call stack yields to something above in the call stack (React, in this case) without all the intermediate functions necessarily knowing about it or being “poisoned” by `async` or generators. Of course, we can’t really **resume** execution in JavaScript later, but from React’s point of view, re-rendering a component tree when the Promise resolves is pretty much the same thing. You can cheat when your programming model [assumes idempotence](https://overreacted.io/react-as-a-ui-runtime/#purity)! - -[Hooks](https://reactjs.org/docs/hooks-intro.html) are another example that might remind you of algebraic effects. One of the first questions that people ask is: how can a `useState` call possibly know which component it refers to? - -```js -function LikeButton() { - // How does useState know which component it's in? - const [isLiked, setIsLiked] = useState(false); -} -``` - -I already explained the answer [near the end of this article](https://overreacted.io/how-does-setstate-know-what-to-do/): there is a “current dispatcher” mutable state on the React object which points to the implementation you’re using right now (such as the one in `react-dom`). There is similarly a “current component” property that points to our `LikeButton`’s internal data structure. That’s how `useState` knows what to do. - -Before people get used to it, they often think it’s a bit “dirty” for an obvious reason. It doesn’t “feel right” to rely on shared mutable state. **(Side note: how do you think `try / catch` is implemented in a JavaScript engine?)** - -However, conceptually you can think of `useState()` as of being a `perform State()` effect which is handled by React when executing your component. That would “explain” why React (the thing calling your component) can provide state to it (it’s above in the call stack, so it can provide the effect handler). Indeed, [implementing state](https://github.com/ocamllabs/ocaml-effects-tutorial/#2-effectful-computations-in-a-pure-setting) is one of the most common examples in the algebraic effect tutorials I’ve encountered. - -Again, of course, that’s not how React **actually** works because we don’t have algebraic effects in JavaScript. Instead, there is a hidden field where we keep the current component, as well as a field that points to the current “dispatcher” with the `useState` implementation. As a performance optimization, there are even separate `useState` implementations [for mounts and updates](https://github.com/facebook/react/blob/2c4d61e1022ae383dd11fe237f6df8451e6f0310/packages/react-reconciler/src/ReactFiberHooks.js#L1260-L1290). But if you squint at this code very hard, you might see them as essentially effect handlers. - -To sum up, in JavaScript, throwing can serve as a crude approximation for IO effects (as long as it’s safe to re-execute the code later, and as long as it’s not CPU-bound), and having a mutable “dispatcher” field that’s restored in `try / finally` can serve as a crude approximation for synchronous effect handlers. - -You can also get a much higher fidelity effect implementation [with generators](https://dev.to/yelouafi/algebraic-effects-in-javascript-part-4---implementing-algebraic-effects-and-handlers-2703) but that means you’ll have to give up on the “transparent” nature of JavaScript functions and you’ll have to make everything a generator. Which is… yeah. - -### Learn More - -Personally, I was surprised by how much algebraic effects made sense to me. I always struggled understanding abstract concepts like Monads, but Algebraic Effects just “clicked”. I hope this article will help them “click” for you too. - -I don’t know if they’re ever going to reach mainstream adoption. I think I’ll be disappointed if they don’t catch on in any mainstream language by 2025. Remind me to check back in five years! - -I’m sure there’s so much more you can do with them — but it’s really difficult to get a sense of their power without actually writing code this way. If this post made you curious, here’s a few more resources you might want to check out: - -* [https://github.com/ocamllabs/ocaml-effects-tutorial](https://github.com/ocamllabs/ocaml-effects-tutorial) -* [https://www.janestreet.com/tech-talks/effective-programming/](https://www.janestreet.com/tech-talks/effective-programming/) -* [https://www.youtube.com/watch?v=hrBq8R_kxI0](https://www.youtube.com/watch?v=hrBq8R_kxI0) - -Many people also pointed out that if you omit the typing aspects (as I did in this article), you can find much earlier prior art for this in the [condition system](https://en.wikibooks.org/wiki/Common_Lisp/Advanced_topics/Condition_System) in Common Lisp. You might also enjoy reading James Long’s [post on continuations](https://jlongster.com/Whats-in-a-Continuation) that explains how the `call/cc` primitive can also serve as a foundation for building resumable exceptions in userland. - -If you find other useful resources on algebraic effects for people with JavaScript background, please let me know on Twitter! - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 15ff008b0d6eed40fa6ec9e215eba2520cd84501 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 30/58] Revert "Create when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md (#6226)" This reverts commit 65883e22a227530f85465dcddc045cf9cd96bdf8. --- ...te-isnt-rebuilding-slack-on-the-desktop.md | 107 ------------------ 1 file changed, 107 deletions(-) delete mode 100644 TODO1/when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md diff --git a/TODO1/when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md b/TODO1/when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md deleted file mode 100644 index f66b7cb0ba4..00000000000 --- a/TODO1/when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md +++ /dev/null @@ -1,107 +0,0 @@ -> * 原文地址:[When a rewrite isn’t: rebuilding Slack on the desktop](https://slack.engineering/rebuilding-slack-on-the-desktop-308d6fe94ae4) -> * 原文作者:[Slack Engineering](https://medium.com/@SlackEng) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md](https://github.com/xitu/gold-miner/blob/master/TODO1/when-a-rewrite-isnt-rebuilding-slack-on-the-desktop.md) -> * 译者: -> * 校对者: - -# When a rewrite isn’t: rebuilding Slack on the desktop - -**A new version of Slack is rolling out for our desktop customers, built from the ground up to be faster, more efficient, and easier to work on.** - -![Photo by [Caleb George](https://unsplash.com/@seemoris?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/7936/0*cgkWRCMtQXti3jbA) - -[Conventional wisdom](https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/) holds that you should never rewrite your code from scratch, and that’s good advice. Time spent rewriting something that already works is time that won’t be spent making our customers working lives simpler, more pleasant, and more productive. And running code knows things: hard-won knowledge gained through billions of hours of cumulative usage and tens of thousands of bug fixes. - -Still, software codebases have life spans. The desktop version of Slack is our oldest client, growing out of a period of rapid development and experimentation during the earliest moments of our company. During this period, we were optimizing for product-market fit and in a constant sprint to keep up with our customers as their use — and expectations — of the product grew. - -Today, after more than half a decade of hyper-growth, Slack is used by millions of people with larger companies working with more data than we ever could have imagined when we first started. Somewhat predictably, a few internal cracks were starting to show in the desktop client’s foundation. Additionally, the technology landscape had shifted away from the tools we chose in late 2012 (jQuery, Signals, and direct DOM manipulation) and toward a paradigm of composable interfaces and clean application abstractions. Despite [our best efforts to keep things snappy](https://slack.engineering/getting-to-slack-faster-with-incremental-boot-ff063c9222e4), it became clear that some fundamental changes would be required to evolve the desktop app and prepare it for the next wave of product development. - -The architecture of the existing desktop app had a number of shortcomings: - -1. **Manual DOM updates.** Slack’s original UI was built using HTML templates that needed to be manually rebuilt whenever the underlying data changed, making it a pain to keep the data model and user interface in sync. We wanted to adopt React, a popular JavaScript framework that made this sort of thing more automatic and less prone to potential errors. -2. **Eager data loading.** The data model was “complete”, meaning that each user session started by downloading **everything** relevant to the user. While great in theory, in practice this was prohibitively expensive for large workspaces and meant that we had to do a lot of work to keep data models up-to-date over the course of a user’s session. -3. **Multiple processes for multiple workspaces.** When signed into multiple workspaces, each of those workspaces was in fact running a stand-alone copy of the web client inside a separate Electron process, which meant that Slack used more memory than users expected. - -The [first](https://slack.engineering/rebuilding-slacks-emoji-picker-in-react-bfbd8ce6fbfe) [two](https://slack.engineering/flannel-an-application-level-edge-cache-to-make-slack-scale-b8a6400e2f6b) problems were the sort of things that we could incrementally improve over time, but getting multiple workspaces to run within a single Electron process meant changing a fundamental assumption of the original design — that there is only ever a single workspace running at a time. Although we made some [incremental improvements](https://slack.engineering/reducing-slacks-memory-footprint-4480fec7e8eb) for folks with lots of idle workspaces, truly solving the multiple process problem meant rewriting Slack’s desktop client from scratch. - -## Bit by bit - -The [Ship of Theseus](https://en.wikipedia.org/wiki/Ship_of_Theseus) is a thought experiment that considers whether an object that has had each of its pieces replaced one-by-one over time is still the same object when all is said and done. If every piece of wood in a ship has been replaced, is it the same ship? If every piece of JavaScript in an app has been replaced, is it the same app? We sure hoped so, because this seemed like the best course of action. - -Our plan was to: - -* keep the existing codebase; -* create a “modern” section of the codebase that would be future-proof and work the way we wanted it to; -* modernize the implementation of Slack bit by bit, replacing existing code with modern code incrementally; -* define rules that would enforce a strict interface between existing and modern code so it would be easy to understand their relationship; -* and continually ship all of the above with the existing app, replacing older modules with modern implementations that suited our new architecture. - -The final step — and the most important one for our purposes — was to create a modern-only version of Slack that would start out incomplete but gradually work its way toward feature completeness as modules and interfaces were modernized. - -We’ve been using this modern-only version of the app internally for much of the last year, and it is now rolling out to customers. - -## Modern times - -The first order of business was to create the modern codebase. Although this was just a new subdirectory in our codebase, it had three important rules enforced by convention and tooling, each of which was intended to address one of our existing app’s shortcomings: - -1. All UI components had to be built with React -2. All data access had to assume a lazily loaded and incomplete data model -3. All code had to be “multi-workspace aware” - -The first two rules, while time-consuming to fulfill, were relatively straightforward. However, moving to a multi-workspace architecture was quite the undertaking. We couldn’t expect every function call to pass along a workspace ID, and we couldn’t just set a global variable saying which workspace was currently visible since plenty of things continue to happen behind the scenes regardless of which workspace the user is currently looking at. - -The key to our approach ended up being [Redux](https://redux.js.org/), which we were already using to manage our data model. With a bit of consideration and the help of the [redux-thunk](https://github.com/reduxjs/redux-thunk) library, we were able to model virtually everything as actions or queries on a Redux store, allowing Redux to provide a convenient abstraction layer around the concept of individual workspaces. Each workspace would get its own Redux store with everything living within it — the workspace’s data, information about the client’s connectivity status, the WebSocket we use for real-time updates — you name it. This abstraction created a conceptual container around each workspace without having to house that container in its own Electron process, which was what the legacy client did. - -With this realization, we had our new architecture in place: - -![](https://cdn-images-1.medium.com/max/2612/1*cTUr99NpvxHSZWHfdxu-Rw.png) - -![**Architecture comparison.** New client on the right.](https://cdn-images-1.medium.com/max/2612/1*vzAu72QESmgToZY866HP8Q.png) - -## A legacy of interoperability - -At this point we had a plan and an architecture we thought would work, and we were ready to work our way through the existing codebase, modernizing everything until we were left with a brand new Slack. There was just one last problem to solve. - -We couldn’t just start replacing old code with new code willy-nilly; without some type of structure to keep the old and new code separate, they’d end up getting hopelessly tangled together and we’d never have our modern codebase. To solve this problem, we introduced a few rules and functions in a concept called legacy-interop: - -* **old code cannot directly import new code:** only new code that has been “exported” for use by the old code is available -* **new code cannot directly import old code:** only old code that has been “adapted” for use by modern code is available. - -Exporting new code to the old code was simple. Our original codebase did not use JavaScript modules or imports. Instead, it kept everything on a top-level global variable called TS. The process of exporting new code just meant calling a helper function that made the new code available in a special TS.interop part of that global namespace. For example, TS.interop.i18n.t() would call into our modern, multi-workspace aware string localization function. Since the TS.interop namespace was only used from our legacy codebase, which only loaded a single workspace at a time, we could do a simple look-up to determine the workspace ID behind the scenes without requiring the legacy code to worry about it. - -Adapting old code for new code was less trivial. Both the new code and the old code would be loaded when we were running the classic version of Slack, but the modern version would only include the new code. We needed to find a way to make it possible to conditionally tap into old code without causing errors in the new code, and we wanted the process to be as transparent to developers as possible. - -Our solution was called adaptFunctionWithFallback, which took a function’s path on our legacy TS object to run, as well as a function to use instead if we were running in the modern-only codebase. This function defaulted to a no-op, which meant that if the underlying legacy code wasn’t present, modern code that tried to call it would have no effect — and produce no errors. - -With both of these mechanisms in place, we were able to kick off our modernization effort in earnest. Legacy code could access new code as it got modernized, and new code could access old code **until** it got modernized. As you’d expect, over time there were fewer and fewer usages of old code adapted for use from the modern codebase, trending toward zero as we got ready for release. - -## Putting it all together - -This new version of Slack has been a long time coming, and it features the contributions of dozens of people who have been working through the last two years to roll it out seamlessly to customers. The key to its success is the incremental release strategy that we adopted early on in the project: as code was modernized and features were rebuilt, we released them to our customers. The first “modern” piece of the Slack app was our emoji picker, which we released more than two years ago — followed thereafter by the channel sidebar, message pane, and dozens of other features. - -Had we waited until the entirety of Slack was rewritten before releasing it, our users would have had a worse day-to-day experience with emoji, messages, channel lists, search, and countless other features before we could release a “big bang” replacement. Releasing incrementally allowed us to deliver real value to our customers as soon as possible, helped us stay focused on continuous improvement, and de-risked the release of the new client by minimizing how much completely new code was being used by our customers for the first time. - -Conventional wisdom states that rewrites are best avoided, but sometimes the benefits are too great to ignore. One of our primary metrics has been memory usage, and the new version of Slack delivers: - -![**Memory usage comparison.** New client on the right.](https://cdn-images-1.medium.com/max/5544/1*d_U8PJR0MA5q8CYddSc18A.png) - -These results have validated all of the work that we’ve put into this new version of Slack, and we look forward to continuing to iterate and make it even better as time goes on. - -When guided by strategic planning, tempered by judicious releases, and buoyed by talented contributors, incremental rewrites are a great way to right the wrongs of the past, build yourself a brand new ship, and make your users’ working lives simpler, more pleasant, and more productive. - -## Stay tuned - -We are eager to share more about what we’ve learned during this process. In the coming weeks we’ll be writing more at [https://slack.engineering](https://slack.engineering) about: - -* **Slack Kit**, our UI component library -* **“Speedy Boots”**, the prototype that launched this effort -* **Accessibility**, baked in rather than bolted on -* **Gantry**, our app launching framework -* and **rolling out a new client** **architecture** at scale - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From b14960a70116b222b235aee5f2bda19df462bd6f Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 31/58] Revert "Create 5-tips-for-using-showinstallprompt-in-your-instant-experience.md (#6219)" This reverts commit f2928d2c975821f41bad33b7ea4c69312e68ba4f. --- ...nstallprompt-in-your-instant-experience.md | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 TODO1/5-tips-for-using-showinstallprompt-in-your-instant-experience.md diff --git a/TODO1/5-tips-for-using-showinstallprompt-in-your-instant-experience.md b/TODO1/5-tips-for-using-showinstallprompt-in-your-instant-experience.md deleted file mode 100644 index 72cf1e58b22..00000000000 --- a/TODO1/5-tips-for-using-showinstallprompt-in-your-instant-experience.md +++ /dev/null @@ -1,136 +0,0 @@ -> * 原文地址:[5 tips for using showInstallPrompt in your instant experience](https://medium.com/androiddevelopers/5-tips-for-using-showinstallprompt-in-your-instant-experience-99d4681e0ae) -> * 原文作者:[Miguel Montemayor](https://medium.com/@migmontemayor) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/5-tips-for-using-showinstallprompt-in-your-instant-experience.md](https://github.com/xitu/gold-miner/blob/master/TODO1/5-tips-for-using-showinstallprompt-in-your-instant-experience.md) -> * 译者: -> * 校对者: - -# 5 tips for using showInstallPrompt in your instant experience - -![](https://cdn-images-1.medium.com/max/3200/0*5eAOuRUKrRBXEJdI) - -[Google Play Instant](https://developer.android.com/topic/google-play-instant) allows your users to try your app or game before installing. Whether launching from the Play Store or a URL, instant experiences take your users directly into a native experience of your app. - -One of the goals of your instant experience may be to drive installs from your app’s instant experience. You can achieve this more easily by making sure you are correctly using the latest APIs and best practices. - -When users decide to install your app or game, the [showInstallPrompt API](https://developers.google.com/android/reference/com/google/android/gms/instantapps/InstantApps.html#showInstallPrompt(android.app.Activity,%20android.content.Intent,%20int,%20java.lang.String)) allows you to prompt for installation from within your instant experience. After calling the API, an in-app install prompt overlay appears in your app. Once the user agrees to install, the app installation process begins. When completed, the installed app launches automatically. - -![**This animation shows the installation flow when using showInstallPrompt**](https://cdn-images-1.medium.com/max/2000/0*HaJS3sMgtdYB_TxA) - -When implementing showInstallPrompt in your instant experience, the best practices detailed below that will make transitioning your users from instant to installed app as smooth as possible. - -## 1. Make sure you’re using the latest showInstallPrompt API - -Rolled out in June 2018, the updated [showInstallPrompt API](https://developers.google.com/android/reference/com/google/android/gms/instantapps/InstantApps.html#showInstallPrompt(android.app.Activity,%20android.content.Intent,%20int,%20java.lang.String)) gains a few key benefits over the legacy API. The new API displays a smaller install prompt and improves the transition to your installed app with the addition of the postInstallIntent parameter, which specifies the activity to launch after installation. - -> Identifying the showInstallPrompt version in your instant experience - -Previously, the legacy API would launch a larger in-app install prompt. Due to the legacy showInstallPrompt deprecation, now calling it launches your Play Store listing. In order to restore in-app install prompts, you’ll need to migrate to the new API. - -If you’re not sure whether your instant experience calls the legacy API, you can quickly figure out by running your instant app and selecting the install button. If you’re taken to the Play Store listing, you’re using the legacy API. If you see an in-app overlay, you’re using the latest API. - -Alternatively, you can check your code to see if your method call includes the postInstallIntent parameter. If it does not include the postInstallIntent, you’re using the legacy API. Here’s the method signature of the new showInstallPrompt API: - -``` -public static boolean showInstallPrompt (Activity activity, Intent postInstallIntent, int requestCode, String referrer) -``` - -The `postInstallIntent` is the intent that will launch after the app has been installed. It must resolve to an activity in the installed app package, or it will not be used. - -> Migrating to the new showInstallPrompt - -To migrate to the new showInstallPrompt API, follow these steps: - -1. Make sure you are using the latest instant app client library in your project. Update the following dependency in your build.gradle file: - -``` -implementation 'com.google.android.gms:play-services-instantapps:16.0.1' -``` - -2. Update your code to use the new [showInstallPrompt API](https://developers.google.com/android/reference/com/google/android/gms/instantapps/InstantApps.html#showInstallPrompt(android.app.Activity,%20android.content.Intent,%20int,%20java.lang.String)) with the postInstallIntent parameter. - -3. Upload your instant app to the [internal test track](https://support.google.com/googleplay/android-developer/answer/3131213?hl=en) to verify that the install button now launches an in-app install prompt overlay. - -You can also look through this [sample app](https://github.com/googlesamples/android-instant-apps/tree/master/install-api) using the new showInstallPrompt API to see how this works. - -## 2. Pre-register from within your instant game - -The showInstallPrompt API is not just for installation! If your instant game supports [pre-registration](https://support.google.com/googleplay/android-developer/answer/9084187), you can prompt for pre-registration signup using the same API. - -When your app calls showInstallPrompt, the behavior for pre-registration is similar to what appears during installation. An in-app overlay appears to pre-register the user. Then the user will be able to continue where they left off in the instant game. Users who pre-register will be notified when the game is released. - -To launch the pre-registration flow, you call showInstallPrompt just as if you were going to prompt for install. - -``` -// Prompt for pre-registration -InstantApps.showInstallPrompt(activity, postInstallIntent, requestId, referrerId) -``` - -Note the `postInstallIntent` parameter is ignored after pre-registration is completed. - -## 3. Transition user state to the installed app - -Transfer the user’s state from the instant experience into the installed app. Users should be able to pick up where they left off. Any achievements or progress made in the instant experience should carry over to the installed app or game. - -![](https://cdn-images-1.medium.com/max/2000/0*r7DBqy2P92QFwOPf) - -The recommended way to persist user state is using the [Cookie API](https://developers.google.com/android/reference/com/google/android/gms/instantapps/PackageManagerCompat#getInstantAppCookie()) for migrating data after installation. The Cookie API allows you to store a small token of information on the device that can be accessed by your installable app. The API ensures that only apps with the same package ID as your instant app can access the cookie. - -In your instant app, you should always store your cookie data by using [PackageManagerCompat](https://developers.google.com/android/reference/com/google/android/gms/instantapps/PackageManagerCompat.html#setInstantAppCookie(byte[])). - -```Kotlin -// Cookie data is a simple byte array. -val cookieData: ByteArray = byteArrayOf() - -// Use PackageManagerCompat to access Cookie API -val packageManager = InstantApps.getPackageManagerCompat(applicationContext) - -// Ensure that the cookie data will fit within the store before setting -// the value. -if (cookieData.length <= packageManager.getInstantAppCookieMaxSize()) { - packageManager.setInstantAppCookie(cookieData) -} -``` - -After the user installs the app, you can access the data. - -```Kotlin -// Use PackageManagerCompat to access Cookie API. -val packageManager = InstantApps.getPackageManagerCompat(this) -val cookieData = packageManager.getInstantAppCookie() - -// Clear the cookie data once it has been read to clear it. -packageManager.setInstantAppCookie(null) -``` - -## 4. Don’t interfere with task completion - -Users shouldn’t be interrupted when working through the tasks they sought to complete when opening the instant experience. Avoid asking users to install your app when they’re partially completed with a task. - -You can call showInstallPrompt after the user has completed their task or wants to use an additional feature not available in your instant app. - -![](https://cdn-images-1.medium.com/max/2000/1*uovyCegQYpdiurkTpTL5lQ.png) - -For example, if you lead users to an instant experience through a product ad online, your instant app should allow your users to complete the checkout flow. After the purchase is completed, you can prompt for install. Avoid requiring the user to install or sign up before they can complete their purchase. - -## 5. Provide explicit installation prompts - -This last tip may seem obvious, but make sure your instant experience has explicit install prompts. Without them, users may be confused on how to install your app or may have to go to the Play Store to install. - -The install buttons should call showInstallPrompt to launch the install prompt. - -![](https://cdn-images-1.medium.com/max/2000/1*nKfEwwU4dVp08ZUndHvuIA.png) - -Use the [Material Design “get app” icon](https://material.io/icons/#ic_get_app) and the label “Install” for the installation button or “Pre-Register” for the pre-registration button. - -Don’t use any other labels like “Get the app”, “Install the full app,” or “Upgrade”. Don’t use a banner or other ad-like technique for presenting an installation prompt to users. - -*** - -If you have additional questions about implementing the showInstallPrompt API in your instant app or game, post your question on [StackOverflow](https://stackoverflow.com/questions/tagged/android-instant-apps). Learn more about [Google Play Instant](https://developer.android.com/topic/google-play-instant) and check out our other [UX best practices](https://developer.android.com/topic/google-play-instant/best-practices/apps). - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From dbdfc8aad2f2bb0c497e3d94fcb336d2cdf6949f Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 32/58] =?UTF-8?q?Revert=20"=E5=A4=9A=E7=BD=91=E7=AB=99?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=9A=84=20CSS=20=E6=9E=B6=E6=9E=84=20(#6215?= =?UTF-8?q?)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 808f82a98a8290bf1d35316598748ba4a03f14bb. --- .../css-architecture-for-multiple-websites.md | 148 +++++++++--------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/TODO1/css-architecture-for-multiple-websites.md b/TODO1/css-architecture-for-multiple-websites.md index 725bc7eb438..3b07200a949 100644 --- a/TODO1/css-architecture-for-multiple-websites.md +++ b/TODO1/css-architecture-for-multiple-websites.md @@ -2,63 +2,63 @@ > * 原文作者:[Elad Shechter](https://medium.com/@elad) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/css-architecture-for-multiple-websites.md](https://github.com/xitu/gold-miner/blob/master/TODO1/css-architecture-for-multiple-websites.md) -> * 译者:[Baddyo](https://juejin.im/user/5b0f6d4b6fb9a009e405dda1) -> * 校对者:[xionglong58](https://github.com/xionglong58),[lgh757079506](https://github.com/lgh757079506) +> * 译者: +> * 校对者: -# 多网站项目的 CSS 架构 +# CSS Architecture for Multiple Websites -> CSS 架构 —— 第三部分 +> CSS Architecture — Part 3 -复杂的 CSS 架构,可不是你在科班里能学到的东西。 +Complex CSS architecture is not something you learn in any formal institution. -我在互联网行业的第四份工作,是在我国一家领先的媒体新闻公司中任职一名 CSS/HTML 专家,我的主要职责就是开发可重用的、可扩展的、用于多网站的 CSS 架构。 +My fourth job in the web industry was as a CSS/HTML expert, in one of the leading media news companies in my country, and my primary mission was to write reusable and scalable CSS for multiple websites. ![](https://cdn-images-1.medium.com/max/2000/1*WreGgi4zIgKz_cb5vRjGTA.png) -在本文中,我将与大家分享我在构建多网站架构领域中积累的知识和经验。 +In this post, I will share with you the knowledge and experience I gained in this field of constructing a multiple website architecture. -附注:如今,正规的项目都会用到 CSS 预处理器。而在本文中,我会使用 Sass 预处理器。 +Side note: nowadays, a proper project uses a CSS preprocessor. In this post, I will be using the SASS preprocessor. -本文是我写的讨论 CSS 架构的系列文章中的第三篇。建议大家最好先读读此系列的第二篇 —— [《CSS 架构:文件夹和文件结构》](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b),有助于加深对本文的理解。 +This post is the third in a series of articles I’m writing about CSS architecture. To understand this post better, I recommend that you read at least the second post in this series, “[CSS Architecture — Folders & Files Structure](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b)”. -## 用层构建世界 +## Layers World -在开始开发一个大型项目之前,我们应该放眼全局,把多个网站的共同之处提炼出来。高楼大厦始于一砖一瓦,而项目的基石就是样式规格化、混入(Mixins)、通用图标以及局部模块层(元素、组件、图形逻辑、实体、页面……不一而足)等。 +Starting a large-scale project requires thinking globally and defining what things the sites have in common. It begins with little things, like normalize, mixins, shared icons, and the partials layer (elements, components, sequences, entities, pages, and more). -为了使多重项目(即多个网站)正常运转,我们必须决定哪些样式是通用样式、哪些是专有样式 —— 通用样式写进基础层,而专有样式写在与其对应的层中。这是一条充满摸索和碰壁的实践之路。每当思考的角度发生变化,我们都需要逐层地挪动样式代码,直到我们觉得顺眼为止,这都是家常便饭了。 +For the projects (sites) to work correctly, you have to decide which styles appear in enough of the sites to warrant defining them in the base layer, and which styles aren’t, and therefore should be defined in the specific project in which they appear. It’s a practice you will achieve by trial and error. It often happens that you move styles from layer to layer when your perspective changes, until you get them balanced in a way that suits you. -理解了这项原则后,我们就可以开始着手构建作为基础的全局层了。这个全局层是整个多重项目(多个网站)的起始点。 +After understanding this principle, you can begin making the underlying global layer. This global layer will be the starting point for all the projects (sites). -下面的示例图向我们演示了彼时我司的项目需求。 +Here’s an example of a diagram that demonstrates the requirements of the company I worked for at that time. -![层架构](https://cdn-images-1.medium.com/max/2000/1*zYZV-QHyYrA_1XwxibQw2A.png) +![Layer Structures](https://cdn-images-1.medium.com/max/2000/1*zYZV-QHyYrA_1XwxibQw2A.png) -基础层要保持轻量,其中只包含 CSS 初始化、基本的 SASS mixins、通用图标、通用字体(如需)以及功能类,如果某些网格布局适用于所有网站,就将其作为通用网格添加到基础层中。在 `_partials.scss` 层(元素、组件等)中,我们主要用到的是 `_elements.scss` 层,该层中包含诸如通用弹窗、通用表单和通用标题等此类局部模块。我们应该在基础样式中添加的是所有(或者大多数)底层样式共有的部分。(更多关于文件夹和文件结构的细节,参见我的[上一篇文章](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b)) +The base layer should be thin, only containing CSS resets, base SASS mixins, shared icons, general font (if needed), utility classes, and maybe shared grids if it suits all projects. For the `_partials.scss` layer (elements, components, etc.), you will mostly use the `_elements.scss` layer which has partials like common-popup, common forms, and common titles. You only add styles that are shared by all, or most, of the lower layers. ([More about Folders & Files Structure is detailed in my previous post](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b)) -#### 如何组织多个层 +#### How to Structure the Layers -在我们的架构中,每个层都至少包含三个文件:两个私有文件(局部样式文件和配置文件,称之为私有是因为它们不会被编译成一个 CSS 文件)和一个公共文件(本层的主文件)。每层的配置文件 **`_config.scss`** 通常包含变量。**`_local.scss`** 文件则包含内容样式,为当前层充当控制器或者包管理器的角色。而**第三个**文件(layer-name.scss)会**调用**前二者。 +In our architecture, each layer has at least three files; 2 private files (local and config; I call them private because they don’t get compiled into a CSS file) and one public file (the primary layer file). The layer’s configuration file, **`_config.scss`** usually contains variables. The **`_local.scss`** file includes the content styles and acts as a kind of controller or a package manager for the layer. **The third** file **calls** those first two files **(layer-name.scss**). -**layer-name.scss 文件:** +**layer-name.scss file:** ``` @import "config"; @import "local"; ``` -另外一个我们要给自己定下的原则就是,尽可能把每个文件都拆分成尽可能小的部分(小文件)。这个原则会让重构非常方便。 +Another principle that we should set for ourselves is to try and divide everything into the smallest parts (small files) possible. This principle will become very handy when you get to refactoring. -在每一层中,都要保证**只编译 layer-name.scss 文件**,即使某些层代表的是一个“虚拟项目”(如上面示例图中的“基础层框架”)。 +In every layer, **compile only the layer-name.scss** file. You should do this even in layers representing a ‘Virtual Project’ like the ‘Base Layer Framework’ in the above diagram. -对于不会被编译成单独文件的私有文件,我们用一个下划线(`_`)作为其文件名的前缀。这里的下划线代表着此文件不能单独存在。 +For the private files, which aren’t compiled to a separate CSS file, we use an underscore (“`_`”) as a prefix in all file names. The underscore symbols a file which can’t stand on its own. -**注意:**当导入私有文件时,我们书写其文件名时可以不必带上前缀下划线。 +**Notice:** When importing private files, you can write their names without the underscore prefix. -**层架构示例:** +**Example of layer structure:** -![**_local.scss 文件导入了 local 文件夹中所有的 *.scss 文件**,而这些 local 文件夹中的 *.scss 文件按序调用私有文件夹中所有的 *.scss 文件。同理,**_config.scss 文件调用 config 文件夹中所有的 *.scss 文件**。](https://cdn-images-1.medium.com/max/2000/1*0hwUrfXGWkZR-aTVfoojyA.png) +![The **_local.scss file includes all *.scss files in the local folder**, which, in turn, calls **all *.scss files in the private folders**. The **_config.scss file calls all files in the config folders**.](https://cdn-images-1.medium.com/max/2000/1*0hwUrfXGWkZR-aTVfoojyA.png) -**文件夹结构长这样:** +**How it looks in the folders** ``` sass/ @@ -68,19 +68,19 @@ sass/ |- local/ |- _config.scss |- _local.scss - |- base-layer.css (编译后的层样式) + |- base-layer.css (compiled layer style) |- base-layer.scss ``` -## 继承 +## Inheritance -假设我们想要从基础层开始创建一个项目。我们需要根据 base-layer 文件夹的内部结构,用新项目的名称照猫画虎地克隆一套出来。在后续例子中,我们把这个新项目称为 **inherited-project**。 +Imagine we want to create a project from the base layer. We will have to build a parallel folder with the project’s name. In the following example, we’ll call it **inherited-project**. -**提示**:把所有的层目录和项目目录都放在 Sass 的根目录中。 +**Note**: Locate all layers and projects in the SASS root folder. -该项目至少包含一个 **`_config.scss`** 文件、一个 **`_local.scss`** 文件和此层的核心 Sass 文件 —— 在本例中即为 **`inherited-project.scss`**。 +The project has at least one **`_config.scss`** file, one **`_local.scss`** file, and the layer’s central sass file named, in our example, **`inherited-project.scss`**. -所有的层和项目都位于 Sass 的根目录中。 +All layers/projects sit in the root folder of SASS. ``` sass/ @@ -90,7 +90,7 @@ sass/ | |- local/ | |- _config.scss | |- _local.scss - | |- base-layer.css (编译后的层样式) + | |- base-layer.css (compiled layer style) | |- base-layer.scss | |- inherited-project @@ -98,59 +98,59 @@ sass/ |- local/ |- _config.scss |- _local.scss - |- inherited-project.css (编译后的层样式) + |- inherited-project.css (compiled layer style) |- inherited-project.scss ``` -项目 **inherited-project** 的配置文件引入了 **base-layer** 中的配置文件。这样一来,我们就能增加新变量或者覆写上层(**base-layer**)中的已有变量了。 +The **inherited-project**’s config file imports the **base-layer**’s config file. This way, we can add new variables, or override existing ones from the layer above (**base-layer**). -以下为 **inherited-project/_config.scss** 的一个**例子**: +Here’s an **example** of the- **inherited-project/_config.scss**: ``` -/*加载 base-layer 配置信息 */ +/*load base-layer configuration */ @import "../base-layer/config.scss"; -/** 局部的 Config 层 (按需添加或覆写变量)**/ +/** local Config layer (add or override variables if needed)**/ @import "config/directions.scss"; ``` -内容样式文件 **inherited-project/_local.scss** 亦同理: +The same goes for the **inherited-project/_local.scss** content files of the layer. ``` -/* 导入 base-layer 局部组件 */ +/* import base-layer local components */ @import "../base-layer/local.scss"; -/* 局部字体 */ +/* local font */ @import "local/font-almoni.scss"; -/* 局部组件 */ +/* local components*/ @import "local/elements.scss"; @import "local/components.scss"; ``` -如果要创建的新层既有通用样式又有独特样式,那么从 `base-layer` 文件夹继承基础层样式再合适不过了。 +Inheriting from the base-project folder is the right way to build a new layer that has its unique style, based on the inheritance from the base layer. -这一层会创建一个名为 **`inherited-project.css`** 的 CSS 文件。 +This layer will create one CSS file, called **`inherited-project.css`**. -#### 在内部层中覆写变量 +#### Override variable in inner layer -使用“层”的方式覆写变量非常简单。 +It’s straightforward to override variables using the ‘layers’ method. -比方说在基础层中有一个名为 **`$base-color`** 的变量,其值为 blue(**`$base-color: blue;`**)。要想覆写此变量,就需要在**局部文件** `_config.scss` 中更新它的值。现在,所有使用该变量的组件 —— 不论是继承于**基础层**还是定义于**局部层** —— 都会更新对应变量的的颜色值。 +Let’s say we have a variable named **`$base-color`** in the base layer and its value is blue (**`$base-color: blue`**;). Overriding this variable requires updating its value in the **local** `_config.scss`. Now all the components that use this variable — whether inherited from the **base layer** or defined in the **local layer** — will be updated with the value of the overridden color. -## Global Story 全局 +## Global Story -某些模块并非在所有层中都会用到,因此如果你在基础层中定义它们,其他项目就会导入冗余代码。为了解决这个问题,我走了另一条路线,采用了**全局模块**的概念。 +Some partials aren’t used in all layers, and therefore if you define them in the base layer, the other projects will import unnecessary code. To solve this problem, I implemented another idea of **global partials** concept. -这个概念是说,把仅用于某些层的模块放置于一个新的根目录(`_partials`)中,这个新的根目录位于所有层之外。然后,任何层都可以从全局目录 `_partials` 中导入所需模块。 +This concept is that partials which are used only in some layers will be placed in another new root folder (`_partials`) outside of any layer. Then, any layer that needs these partials can import them from the `_partials` global folder. -**下图**展示了将模块分离的例子: +**This diagram** illustrates **an example** of separated partials: ![](https://cdn-images-1.medium.com/max/2000/1*F43F_4fEqXCCTLNz07nrqg.png) -每一层都可以按需从全局目录 **`_partials`** 中调用一个或多个模块。 +Every layer can call a single partial or multiple ones from the global **`_partials`** folder, as needed. -**全局目录 **`_partials`** 示例:** +**Example of a global _partials folder:** ``` sass/ @@ -160,45 +160,45 @@ sass/ |- inherited-project/ ``` -**从 **`_partials`** 导入模块的 local.scss 文件:** +**local.scss file view of — import global partial:** ``` -/* 导入 base-layer 中的局部组件 */ +/* import base-layer local components */ @import "../base-layer/local.scss"; -/* 局部组件 */ +/*local components*/ @import "local/partials.scss"; -/* 添加全局模块 */ +/* add global partial */ @import "../_partials/last-connection"; ``` -**些许额外忠告** +**Few extra guidelines** -* **组织结构要有条理**。要一直记得以满足需求的方式规划项目、保持最佳结构。 -* **别重蹈覆辙**。仅用 `@import` 即可轻松导入另一层的组件。比如说,某些组件定义在一个“体育”项目中,而这些组件与另一个项目中的“新闻”网站有关联。那我们就可以直接把这些组件 `@import` 进“新闻”网站中。(网站 = 层 = 项目) -* **充分利用 IDE 快捷方式**。选用一款便于重构的编辑器,免于导致报错或故障。 -* **立新不可破旧**。在开发和后续重构中,每次都要把所有 Sass 根文件一同编译,以免新旧脱节。 +* **Be well organized**. Always organize your projects and maintain the best structure in a way that fits your needs. +* **Don’t repeat yourself**. You can import other layers’ components by simply `@import`ing them directly. For example, let’s say some components are defined in the ‘sports’ project, and they’re relevant to the ‘news’ site of another project. We can `@import` those components to the ‘news’ site. (site = layer = project) +* **Utilize IDE shortcuts**. Use a code editor that enables easy refactoring without causing errors or bugs. +* **Make sure you don’t break anything while you work**. Compile all root SASS files while working and continuously refactor the code to see that nothing breaks. -## 总结 +## To Summarize -在本文中,我向大家展示了针对多网站项目的 CSS 体系结构的构建方法,这套思想提炼于我经年积累的知识和经验。 +In this post, I showed my CSS Architecture approach for a multiple-websites architecture, based on the knowledge and experience I’ve gained over the years. -本文是系列文章 **CSS 架构文章新篇**的第三篇,我会每隔几周跟大家分享后续篇章。 +This post is the third in a **new series of articles on CSS Architecture** I have written, and I will share with you every few weeks. -如果觉得本文有趣,欢迎在 [**twitter**](https://twitter.com/eladsc) 上或者 [**medium**](https://medium.com/@elad) 上关注我。 +If it is interesting you , you are welcome to follow me on [**twitter**](https://twitter.com/eladsc) or [**medium**](https://medium.com/@elad). -## 我的 CSS 架构系列文章: +## My CSS Architecture Series: -1. [规格化 CSS 还是 CSS 重置?!](https://medium.com/@elad/normalize-css-or-css-reset-9d75175c5d1e) -2. [CSS 架构 —— 文件夹和文件架构](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b) -3. [多网站项目的 CSS 架构](https://medium.com/@elad/css-architecture-for-multiple-websites-ad696c9d334) +1. [Normalize CSS or CSS Reset?!](https://medium.com/@elad/normalize-css-or-css-reset-9d75175c5d1e) +2. [CSS Architecture — Folders & Files Structure](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b) +3. [CSS Architecture for Multiple Websites](https://medium.com/@elad/css-architecture-for-multiple-websites-ad696c9d334) -## 结束语 +## Final Words -好了,这次就分享到这里。 -衷心希望大家喜欢本文,并能从我的经验中获益一二。 -如果你喜欢本文,请点赞并和大家分享你的心得,我将不胜感激。:-) +That’s all; +I hope you’ve enjoyed this article and learned from my experience. +If you like this post, I would appreciate applause and sharing :-) > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 7cb7819e2922222ddf3f08e2ae853e68821315a6 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 33/58] =?UTF-8?q?Revert=20"CSS=20=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E5=BF=85=E7=9F=A5=E5=BF=85=E4=BC=9A=E7=9A=84=2016=20=E4=B8=AA?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E5=B7=A5=E5=85=B7=E6=8A=80=E5=B7=A7=20(#6180?= =?UTF-8?q?)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8ac4b1ebbd278b672ef80e0711dda23f0bfcb878. --- ...tricks-every-css-developer-need-to-know.md | 212 +++++++++--------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md b/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md index 808ccf9206f..09187dde69b 100644 --- a/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md +++ b/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md @@ -2,208 +2,208 @@ > * 原文作者:[Louis Lazaris](https://www.heartinternet.uk/blog/author/louis-lazaris/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md](https://github.com/xitu/gold-miner/blob/master/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md) -> * 译者:[DEARPORK](https://github.com/Usey95) -> * 校对者:[TiaossuP](https://github.com/TiaossuP), [Baddyo](https://github.com/Baddyo) +> * 译者: +> * 校对者 -# CSS 开发必知必会的 16 个调试工具技巧 +# 16 DevTools tips and tricks every CSS developer needs to know -大多数开发者基本都使用浏览器的开发者工具调试前端,但即使用了好几年 Chrome 的开发者工具,我仍然会遇到从未见过的技巧和功能。 +When it comes to debugging the front-end, if you're like many developers, you basically live in your browser's developer tools. But even after having worked in Chrome's developer tools for quite a few years, I still come across tips, tricks, and features that I haven't seen before. -在本文中,我写了许多在开发者工具中与 CSS 相关的功能和技巧,我认为它们将把你的 CSS 开发水平提升至一个新的台阶。其中一些技巧不仅仅针对 CSS,但是我还是把它们放在了一起。 +In this article, I've compiled a number of CSS-related features and tricks available via developer tools that I think will take your CSS development to a new level. Some of these tips aren't specifically only for CSS, but I'll present them in a CSS context. -一些是有关于工作流与调试的简单技巧,另一些则是最近几年推出的新功能。它们大多数基于 Chrome 的开发者工具,但也涵盖了一些 Firefox 的技巧。 +Some are simple tips for workflow and debugging, while others are new features that have rolled out in recent years. Most are based on Chrome's DevTools, but I've also included a few Firefox tips. -## 审查通过 JavaScript 显示的元素的 CSS +## Examine CSS for an element that appears via JavaScript -在开发者工具的 Elements 面板查找大多数元素的 CSS 并不困难。大多数情况下你只需要右键该元素,点击检查,然后(如有必要)仔细点在 Elements 面板找到它。一旦元素被选中,它的 CSS 会出现在 Styles 面板,随时可以编辑。 +Finding the CSS for most elements in the DevTools Elements panel isn't difficult. In most cases you can just right-click the element, inspect it, then (if necessary) drill down to find it in the Elements panel. Once it's selected, the CSS will appear in the Styles panel, ready to edit. -有时一个元素会因为一些基于 JavaScript 的用户操作动态显示,例如 click 或者 mouseover。审查它们最直观的方法是暂时更改你的 JavaScript 或 CSS 使它们默认可见,以便于你在无需模仿用户操作的情况下处理它。 +Sometimes an element appears dynamically as a result of some JavaScript-based user action like click or mouseover. The most obvious way to make the element appear is to temporarily alter your JavaScript and/or CSS to make the element visible by default, so you can deal with it without needing to mimic the user action. -但如果你在寻找一种更快捷的方法仅使用开发者工具让元素可见,可以遵循以下步骤: +But if you're looking for a quick way to make the element visible using just your DevTools, here are the steps to do this: -1. 打开开发者工具 -2. 打开 Sources 面板 -3. 执行用户操作让对象可见(例如鼠标悬停) -4. 在元素可见的时候按下 F8(与“暂停脚本执行”按钮相同) -5. 点击开发者工具左上角的“选取元素”按钮 -6. 点击页面上的元素 +1. Open DevTools +2. Open the Sources panel +3. Carry out the user action to make the element visible (e.g. mouseover) +4. Hit F8 (same as "Pause script execution" button) while the element is visible +5. Click the "Select an element..." button in DevTools +6. Click the element on the page -我们可以通过 [Bootstrap 的 tooltips](https://getbootstrap.com/docs/3.3/javascript/#tooltips) 测试,只有鼠标悬浮在链接上触发 JavaScript 它才会显示,下面是演示: +We can test this using [Bootstrap's tooltips](https://getbootstrap.com/docs/3.3/javascript/#tooltips), which only appear when hovering over a link, triggered via JavaScript. Here's a demonstration: -![GIF 动图:使用 Bootstrap 的 tooltips 时如何选中元素](https://www.heartinternet.uk/blog/wp-content/uploads/bootstrap-tool-tips-example.gif) +![Animated GIF showing how to select an element using Bootstrap's tooltips](https://www.heartinternet.uk/blog/wp-content/uploads/bootstrap-tool-tips-example.gif) -如你所见在 GIF 的开头,我一开始无法选中元素来审查它,因为鼠标一旦移开它就消失了。但如果我在它可见的时候停止脚本运行,它将保持可见状态以便我可以正确地检查它。当然,如果元素只是简单的 CSS `:hover` 效果,那么我可以用 Styles 面板的 “Toggle Element State”(:hov 按钮)切换状态来让它显示。但由 JavaScript 切换样式的情况下,停止脚本也许是获取它们 CSS 样式的最佳方法。 +As you can see at the start of the video, I can't initially reach the element to inspect it, because it disappears on mouseout. But if I stop script execution while it's visible, it will stay visible so I can inspect it properly. Of course, if it was simply a CSS `:hover` effect, then I could make it appear using the "Toggle element state" section of the Styles panel. But in this case, this is probably the best way to grab the styles of an element whose visibility is triggered via JavaScript. -## 通过 CSS 选择器寻找元素 +## Search for an element using CSS selectors -你也许知道你可以用内置功能(CTRL + F 或者 CMD + F)在 Elements 面板搜索一个元素。但注意看 “find” 栏,它会给你以下提示: +You might know that if you want to search for an element in the Elements panel, you can do this using a find feature (CTRL-F/CMD-F) that's built in. But notice when you view the "find" field, it gives you the following instructions: -![在 Elements 面板使用 CSS 选择器寻找元素的截图](https://www.heartinternet.uk/blog/wp-content/uploads/search-for-a-css-element.png) +![Image showing where you can search for an element using CSS selectors](https://www.heartinternet.uk/blog/wp-content/uploads/search-for-a-css-element.png) -正如我在截图中指出的那样,你可以通过字符串、选择器以及 XPath 寻找元素。之前我一直都在使用字符串,直到最近我才意识到我可以使用选择器。 +As I've indicated in the screenshot, you can find an element "by string, selector, or XPath". I've used "string" many times before but only recently realised I can use a selector for this. -你不一定要使用你 CSS 中用过的选择器,它可以是任意合法的 CSS 选择器。查找功能将告诉你选择器是否与任何元素匹配。这对查找元素很有用,还有助于测试选择器是否有效。 +You don't have to use a selector that's in use in the CSS, it could be any valid CSS selector. The find feature will tell you if your selector matches any elements. This could be useful for finding elements but might also help for testing selectors to see what works. -下面是一个使用 `body > div` 选择器来搜索以及遍历 `body` 所有直接子 `div` 元素的 demo: +Below is a demo that uses the selector `body>div` to search and cycle through all the `div` elements on the page that are direct children of the `body` element: -![演示如何通过指定 CSS 选择器搜索元素的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/body-div-seach-example.gif) +![Animated GIF showing how to search through specific selectors in your CSS](https://www.heartinternet.uk/blog/wp-content/uploads/body-div-seach-example.gif) -如上所述,这些搜索可以通过任意合法选择器完成,类似于 JavsScript 的 `querySelector()` 和 `querySelectorAll()` 方法。 +As mentioned, this search can be done with any valid selector, which would be similar to using JavaScript's `querySelector()` or `querySelectorAll()` methods. -## 直接编辑盒模型 +## Edit the box model directly -盒模型是你开始使用 CSS 首先学习的东西之一。由于这是 CSS 布局的一个重要部分,开发者工具允许你直接编辑盒模型。 +The box model is one of the first things you learn when you start out with CSS. As this is an important aspect of CSS layouts, the DevTools allow you to edit the box model directly. -如果你审查了页面上的一个元素,请在右侧面板单击 Styles 面板旁的 Computed 面板。你将看到该元素的可视化盒模型图示,上面有各部分的数值: +If you inspect an element on the page, in the right panel click the "Computed" panel next to the "Styles" panel. This shows you a visual interpretation of the box model for that element, with the values as part of the graphic: -![特定元素盒模型的可视化图示](https://www.heartinternet.uk/blog/wp-content/uploads/model-box-example.png) +![An image showing the visual representation of the box model for that particular element](https://www.heartinternet.uk/blog/wp-content/uploads/model-box-example.png) -也许你不知道,你可以通过双击任意编辑它们的值: +But maybe you didn't know that you can edit any of those values in place by double clicking them: -![演示如何编辑盒模型值的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/model-box-editing-example.gif) +![Animated GIF showing how you can edit the values of the box model within the representation](https://www.heartinternet.uk/blog/wp-content/uploads/model-box-editing-example.gif) -所做的任何更改都会以与在 Styles 面板中编辑 CSS 时相同的方式反映在页面上。 +Any changes made are reflected on the page in the same way as when you edit the CSS in the Styles panel. -## 在 Styles 面板递增或递减属性值 +## Increment/decrement values in the Styles panel -你可能已经意识到可以在 Styles 面板中编辑 CSS。只需单击属性或值,然后键入更改即可。 +You are probably already aware that you can edit your CSS in the Styles panel. Just click on a property or value and type in your changes. -但也许你没有意识到数值可以以不同的方式递增或递减。 +But maybe you didn't realise that numerical values can be incremented or decremented in different ways. -- 上方向键 / 下方向键可以使属性值以 1 递增 / 递减 -- ALT + 上方向键 / 下方向键可以使属性值以 0.1 递增 / 递减 -- SHIFT + 上方向键 / 下方向键可以使属性值以 10 递增 / 递减 -- CTRL + 上方向键 / 下方向键可以使属性值以 100 递增 / 递减 +- Up/Down arrow keys increment/decrement by 1 +- ALT+Up/Down arrow keys increment/decrement by 0.1 +- SHIFT+Up/Down arrow keys increment/decrement by 10 +- CTRL+Up/Down arrow keys increment/decrement by 100 -![演示如何用方向键递增或递减属性值的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/incrementing-values-in-the-styles-panel-example.gif) +![Animated GIF showing how you can increase or decrease the values using the arrow keys](https://www.heartinternet.uk/blog/wp-content/uploads/incrementing-values-in-the-styles-panel-example.gif) -你也可以使用 Page Up 或 Page Down 按钮代替方向键。 +You can also use the Page Up or Page Down buttons instead of the arrow keys. -## Sources 面板的文本编辑器功能 +## Text editor-like features in Sources panel -比起别的地方,你也许更熟悉在 Styles 面板进行编辑,然而 Sources 面板是开发者工具中被高度低估一个功能,它模仿了常规代码编辑器和 IDE 的工作方式。 +You're probably more familiar with making edits in the Styles panel than anywhere else. The Sources panel, however, is a highly underrated feature of the DevTools because of how closely it mimics working in a regular code editor or IDE. -以下是一些你可以在 Sources 面板(打开开发者工具并点击 “Sources” 按钮)可以做的有用的事情。 +Here are some of the useful things you can do in the Source panel (which you can view by opening DevTools and clicking the "Sources" tab). -### 使用 CTRL 键进行多项选择 +### Make multiple selections with the CTRL key -如果需要在单个文件中选择多个区域,可以通过按住 CTRL 键并选择所需内容来完成此操作,即使它不是连续文本也是如此。 +If you need to select multiple areas in a single file, you can do this by holding the CTRL key and selecting what you want, even if it's not contiguous text. -![演示如何通过按住 CRTL 键进行多项选择的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/multiple-selections-with-ctrl-key.gif) +![Animated GIF showing how multiple selections can be made by holding the CRTL key](https://www.heartinternet.uk/blog/wp-content/uploads/multiple-selections-with-ctrl-key.gif) -在上面的 demo 中,我在 Sources 面板中选择了 main.css 文件的三个任意部分,然后将它们粘贴回文档中。此外,你还可以通过多个光标在多个地方进行同时输入,使用 CTRL 键单击多个位置即可。 +In the above demo I'm selecting three arbitrary parts of the main.css file in the Sources panel and pasting them back into the document. In addition, you can also type in multiple spots at the same time with multiple carets. Again, use the CTRL key to click in multiple spots to make identical edits to all indicated locations. -### 使用 ALT 键选择列 +### Column selection with ALT key -有的时候,你可能希望选择一列文本,但通常情况下无法办到。某些文本编辑器允许你使用 ALT 键来完成此操作,在 Sources 面板中也是如此。 +In some cases, you might want to select a column of text, which you can't normally do by default. Some text editors allow you to use the ALT key to accomplish this, and the same is true in the Sources panel. -![演示如何使用 ALT 键选择整列的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/column-selection-with-alt-key.gif) +![Animated GIF showing how an entire column can be selected by using the ALT key](https://www.heartinternet.uk/blog/wp-content/uploads/column-selection-with-alt-key.gif) -## 使用 CTRL + SHIFT + O 组合键通过 CSS 选择器搜索元素 +## Search by CSS selector with CTRL-SHIFT-O -在 Sources 面板打开文件后,按下 CTRL + SHIFT + O 组合键,可以打开一个输入框让你跳转到任意地方,这是 Sublime 一个著名的功能。 +With a file open in the Sources panel, press CTRL-SHIFT-O on your keyboard to open up the "Goto Anything" box, which is a well-known feature in Sublime Text Editor. -按下 CTRL + SHIFT + O 之后,你可以输入你在本文件中想查找元素的 CSS 选择器,开发者工具会给你提供匹配选项,点击可跳转到文件的指定位置。 +After hitting CTRL-SHIFT-O, you can type a CSS selector that you want to find in the file, and the DevTools will give you options to choose to jump to a specific part of the file. -![演示如何在文件中查找特定 CSS 选择器的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/search-with-css-selector-shortcut.gif) +![Animated GIF showing how to find a specific CSS selector in the file](https://www.heartinternet.uk/blog/wp-content/uploads/search-with-css-selector-shortcut.gif) -## Chrome 和 Firefox 的响应式设计功能 +## Responsive design features in Chrome and Firefox -你也许已经看过一些让你只需点击几下就得以测试你的响应式布局的网站,其实,你可以用 Chrome 的设备模式做同样的事情。 +You've probably seen one of those websites that lets you test the responsiveness of your layout right in the browser with a few clicks. Well, you can do the same thing with Chrome's Device Mode. -打开你的开发者工具,点击左上角的 “Toggle device toolbar” 按钮(快捷键 CTRL + SHIFT + M): +Just open the DevTools and click the "Toggle device toolbar" button in the top-left area of the DevTools (CTRL-SHIFT-M will also do it): -![演示如何在 Chrome 的设备模式测试响应式网站的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/testing-responsive-design.gif) +![Animated GIF showing how to test the responsiveness of a site in Chrome's Device Mode](https://www.heartinternet.uk/blog/wp-content/uploads/testing-responsive-design.gif) -如你所见,设备工具栏有多个选项可根据设备大小和设备类型更改视图,你甚至可以通过手动调整宽度和高度数值或拖动视口区域中的手柄来手动进行更改。 +As you can see, the device toolbar has multiple options to change the view according to device size and device type, and you can even make those changes manually by adjusting the width and height numbers or by dragging the handles in the viewport area. -Firefox 附加的 “@media rules” 面板具有类似的功能,它允许你从站点的样式表中单击断点。你可以在下面的 demo 中看到我在我的一个网站上使用它。 +Firefox has a similar feature with the added "@media rules" panel that allows you to click on a breakpoint from the site's stylesheet. You can see me using this on one of my websites in the demo below. -![演示如何在 Firefox 测试响应式网站的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/firefox-responsive-design-test.gif) +![Animated GIF showing how to test the responsiveness of a site in Firefox](https://www.heartinternet.uk/blog/wp-content/uploads/firefox-responsive-design-test.gif) -## 开发者工具的颜色功能 +## Colour features in DevTools -在 CSS 中处理颜色值是常态。开发者工具让可以你更简单地编辑、测试颜色值。以下是你可以做的事情: +Dealing with colour values in CSS is a constant. DevTools makes it so much easier to edit, test, and otherwise fiddle with colour values. Here are some things you can do. -### 对比度 +### Contrast ratio -首先,开发者工具有查看可访问性功能,当你在 Styles 面板看到 Color 属性值时,你可以点击颜色值旁边的方块打开颜色采集器。在颜色采集器里面,你将看到对比度选项指示你所选择的文本颜色搭配背景是否有可访问的对比度。 +First, there are the accessibility features. When you see a colour value in the Styles panel, you can click the swatch next to the colour value to open the colour picker. Inside the colour picker, you'll see a contrast ratio option that indicates whether your choice of text colour has an accessible contrast in relation to the background. -![演示特定颜色的可访问对比度的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/css-contrast-ratio.gif) +![Animated GIF showing the accessible contrast for a particular colour](https://www.heartinternet.uk/blog/wp-content/uploads/css-contrast-ratio.gif) -正如你在上面 demo 所看到的,颜色采集器在色谱中显示出弯曲的白线。这个线表示最小可接受对比度开始和结束的位置。当我将颜色值移到白线上方时,对比度旁的绿勾将会消失,表明对比度较差。 +As you can see in the above demo, the colour picker shows a curved white line in the colour spectrum. This line indicates where the minimum acceptable contrast ratio begins and ends. When I move the colour value above the white line, the contrast ratio value loses its green checkmark, indicating poor contrast. -### 调色板 +### Colour palettes -除了查看可访问性的功能之外,你还可以访问不同的调色板,包括 Material Design 调色板以及与当前查看页面关联的调色板。 +In addition to the accessibility features, you also have access to different colour palettes, including a Material Design palette and one associated with the currently viewed page. -![演示特定颜色调色盘的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/css-colour-palettes.gif) +![Animated GIF showing a colour palette for a particular colour](https://www.heartinternet.uk/blog/wp-content/uploads/css-colour-palettes.gif) -### 切换颜色值语法 +### Toggling colour value syntax -最后,在开发者工具中一个鲜为人知的小知识是在查看颜色值时你可以切换颜色值的语法。默认情况下,Styles 面板会显示 CSS 里写的颜色的语法。但是开发者工具允许你按住 shift,点击颜色值左边的小方块,在 hex、RGBA 以及 HSLA 之间切换颜色值的语法: +Finally, one little-known tidbit on viewing colour values in DevTools is the ability to switch the syntax of a specific colour value. By default, the Styles panel will show the syntax for the colour as it was written in the CSS. But the DevTools let you toggle between hex, RGBA, an HSLA by  holding the shift key and clicking the swatch next to the colour value: -![演示如何切换颜色值语法的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/toggling-colour-value-syntax.gif) +![Animated GIF showing how you can toggle the syntax of a colour value](https://www.heartinternet.uk/blog/wp-content/uploads/toggling-colour-value-syntax.gif) -## 编辑 CSS 阴影 +## Editing CSS shadows -text-shadow 和 box-shadow 的 CSS 手写起来很乏味,语法很容易忘记,且两种阴影的语法略有不同。 +Text shadows and box shadows can be tedious to try to write out in your CSS by hand. The syntax is easy to forget and the two kinds of shadows have slightly differing syntax. -方便的是,Chrome 的开发者工具允许你使用可视化编辑器添加 text-shadow 或 box-shadow。 +Conveniently, the Chrome DevTools allow you to add a text shadow or box shadow using a visual editor. -![演示如何编辑阴影效果的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/editing-css-shadows.gif) +![Animated GIF showing how to edit a shadow effect in Chrome DevTools](https://www.heartinternet.uk/blog/wp-content/uploads/editing-css-shadows.gif) -正如 demo 中显示的,你可以用 Styles 面板中任意样式右下角的选项栏给任意元素添加 text-shadow 或 box-shadow。阴影添加后,你可以用可视化编辑器编辑不同的属性值。已存在的阴影可以通过点击属性值左边的小方块重新呼出可视化编辑器。 +As shown in the demo, you can add a box shadow or text shadow to any element using the option bar that appears at the bottom right corner of any style rule in the Styles panel. After the shadow is added, you can modify the shadow's various properties using a visual editor. This editor can then be brought up again for any existing shadow using the "Open shadow editor" option next to the property name. -## Firefox 的 Grid 布局检查器 +## Grid Layout Inspector in Firefox -现在大多数常用的浏览器都支持 Grid 布局,越来越多的开发者将它们用作默认的布局方法。Firefox 的开发者工具如今把 Grid 选项作为特色功能放到了 Layout 选项卡中。 +Now that Grid Layout is supported in the majority of in-use browsers, more and more developers are using it as their default layout method. Firefox's developer tools now feature a "Grid" section in the "Layout" tab of the developer tools. -![演示在 Firefox 中如何使用 Grid 布局检查器的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/grid-layout-inspector-in-firefox.gif) +![Animated GIF showing how to use the Grid Layout Inspector in Firefox](https://www.heartinternet.uk/blog/wp-content/uploads/grid-layout-inspector-in-firefox.gif) -这个功能允许你开启一个全覆盖的网格帮助可视化 Grid 布局的不同部分。你还可以显示行号、区域名称,甚至可以选择无限延伸网格线 —— 如果这对你有用的话。在示例 demo 中,我在使用 Jen Simmons 的示例网站,它是响应式的,因此当布局因为不同视口改变时,你可以看到可视化网格的好处。 +This feature allows you to enable an overlay grid that helps to visualise the different parts of your grid layout. You can also display line numbers, area names, and you can choose to extend the grid lines infinitely if that helps you. In the example demo, I'm using this example site by Jen Simmons, which is responsive, so you can see the benefits of the overlay when the layout changes for different sized viewports. -## Firefox 的 CSS filter 编辑器 +## CSS filters editor in Firefox -filter 是现在几乎在移动端和 PC 端都支持的另一个新功能。Firefox 再次提供了一个好用的小工具帮助你编辑 filter 的值。 +Filters are another feature that now have near-universal support in both mobile and desktop browsers. Once again, Firefox offers a handy little tool to help you edit your filter values. -一旦你代码里有 filter(提示:如果你不知道实际语法,你可以先写上 `filter: none`),你将注意到 filter 值左边有一个黑白相间的堆叠方块,点击它可以打开 filter 编辑器。 +Once you've got a filter in place in your code (tip: you can start with `filter: none` if you don't know the syntax for an actual filter from memory), you'll notice a gray and white swatch next to the filter's value. Click that swatch to open the filter editor. -![演示如何使用 Firefox CSS filter 编辑器的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/css-filter-editor-in-firefox.gif) +![Animaged GIF showing how to use the Firefox CSS filters editor](https://www.heartinternet.uk/blog/wp-content/uploads/css-filter-editor-in-firefox.gif) -你可以给单个值加不同的 filter,删除单个 filter 值,或者拖动 filter 重新排列它们的层次。 +You can add multiple filters to a single value, delete individual filter values, and you can drag and drop individual filters to rearrange the order in which they're applied. -![演示如何拖动单个 filter 的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/css-multiple-filters-in-firefox.gif) +![Aniamted GIF showing how to drag and drop individual filters on a single element](https://www.heartinternet.uk/blog/wp-content/uploads/css-multiple-filters-in-firefox.gif) -## 在 Chrome 的 Styles 面板编辑 CSS 动画 +## Edit CSS animations in Chrome's Styles panel -在 Chrome 的 Styles 面板编辑静态元素非常简单,那么编辑使用 `animation` 属性以及 `@keyframes` 创建的动画呢? +Static elements are fairly straightforward to edit in the Styles panel in Chrome's DevTools. But what about animations created using the `animation` property and the `@keyframes` at-rule? -开发者工具有两种编辑动画的方法。首先,当你审查一个元素或者在 Elements 面板选择一个元素,该元素的所有样式都会出现在 Styles 面板 —— 包括已定义的 `@keyframes`。在下面的 demo 中,我选择了一个带动画的元素,然后调整了一些关键帧设置。 +DevTools has two ways you can edit animations. First, when you inspect an element or select it in the Elements panel, all the element's styles appear in the Styles panel -- including the defined `@keyframes`. In the following demo, I'm selecting an animated element, then adjusting and fiddling with some of the keyframe settings. -![演示如何在 Chrome 的 Styles 面板编辑 CSS 动画的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/editing-animation-keyframe-settings-in-chrome.gif) +![Animated GIF showing how to edit CSS animations in Chrome's Styles panel](https://www.heartinternet.uk/blog/wp-content/uploads/editing-animation-keyframe-settings-in-chrome.gif) -但这并不是全部,Chrome 的开发者工具提供了一个 Animation 面板让你可以使用可视化时间线编辑一个动画及它的各个不同部分。你可以通过点击开发者工具右上方的 “Customize and control DevTools” 按钮(三个竖点按钮),选择更多工具,开启 Animations 面板。 +But that's not all. Chrome's DevTools also features an Animation panel that allows you to edit an animation and its various parts using a visual timeline. You can access this feature by going into the "Customize and control DevTools" option (the three dots), choosing "More tools", and selecting "Animations". -![演示 Chrome 开发者工具的 Animations 面板的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/editting-css-animations-in-chrome-style-panel.gif) +![Animated GIF showing the Animations panel in Chrome's DevTools](https://www.heartinternet.uk/blog/wp-content/uploads/editting-css-animations-in-chrome-style-panel.gif) -如上所示,你可以编辑每个动画元素的时间轴,然后在完成编辑后,你可以浏览动画以查看页面上的更改。这是设计和调试复杂 CSS 动画的一个很酷的功能! +As shown above, you can edit each individual animated element's timeline, then once your edits are complete, you can scrub through the animation to see the changes live on the page. This is a cool feature for designing and debugging complex CSS animations! -## 在开发者工具中查看未使用的 CSS +## View unused CSS in DevTools -最近有大量工具可以帮助你追踪未在特定页面上使用的 CSS。这样你就可以选择完全删除它们或仅在必要时加载它们。这将具有明显的性能优势。 +Lately there's been an influx of tools that help you track down parts of your CSS not used on specific pages. This way you can choose to either remove them altogether or load them only when necessary. This will have clear performance advantages. -Chrome 允许你通过开发者工具的 “Coverage” 面板查看未使用的 CSS。这个面板可以通过上文提到的点击开发者面板右上角的 “Customize and control DevTools” 选项(三个竖点按钮),选择“更多工具”,找到 “Coverage” 开启。 +Chrome allows you to view unused CSS right inside the DevTools by means of the "Coverage" panel. This panel can be opened by clicking the "Customize and control DevTools" (three dots) option, then "More tools", then "Coverage". -![演示如何自定义你的 Chrome 开发者工具的 GIF 动图](https://www.heartinternet.uk/blog/wp-content/uploads/view-unused-css-in-dev-tools.gif) +![Animated GIF showing how to customise your menu on Chrome's DevTools](https://www.heartinternet.uk/blog/wp-content/uploads/view-unused-css-in-dev-tools.gif) -如 demo 所示,一旦你打开了 Coverage 面板,你可以在 Sources 面板打开一个源文件。当文件打开时,你将注意到 CSS 文件中每条样式右侧都有绿色或红色的线,指示样式是否在当前页面被应用。 +As shown in the demo, once you're in the Coverage panel, you can select a source file to open it in the "Sources" panel. When the file opens in Sources, you'll notice green and red indicator lines next to each style rule in the CSS file. These indicate if that part of the CSS was used on the current page. -## 总结 +## Conclusion -你的浏览器开发工具是 CSS 编辑和调试的宝库。当你将以上建议与 Chrome 的功能例如 —— Workspaces(允许你把在开发者工具所做的变更保存到本地文件)—— 结合,整个调试过程会变得更加完整。 +Your browser's developer tools are a treasure trove of CSS editing and debugging features. And when you combine these suggestions with a feature in Chrome like Workspaces (which allows you to save changes made in DevTools to a local file), the process of debugging becomes even more complete. -我希望这些技巧与建议将提升你在未来的项目中编辑与调试 CSS 的能力。 +I hope these tips and suggestions will enhance your CSS editing and debugging abilities in your future projects. > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 2f10e1459bb6460d63964b1834dda8c4e18414fd Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 34/58] Revert "Create writing-a-compiler-in-rust.md (#6216)" This reverts commit 98d9d101cc9aad50ff0519f5a497cd6484b626c4. --- TODO1/writing-a-compiler-in-rust.md | 304 ---------------------------- 1 file changed, 304 deletions(-) delete mode 100644 TODO1/writing-a-compiler-in-rust.md diff --git a/TODO1/writing-a-compiler-in-rust.md b/TODO1/writing-a-compiler-in-rust.md deleted file mode 100644 index 5e1c8761cd1..00000000000 --- a/TODO1/writing-a-compiler-in-rust.md +++ /dev/null @@ -1,304 +0,0 @@ -> * 原文地址:[Writing a Compiler in Rust](http://thume.ca/2019/04/18/writing-a-compiler-in-rust/) -> * 原文作者:[Tristan Hume](https://github.com/trishume/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/writing-a-compiler-in-rust.md](https://github.com/xitu/gold-miner/blob/master/TODO1/writing-a-compiler-in-rust.md) -> * 译者: -> * 校对者: - -# Writing a Compiler in Rust - -During my final term at UWaterloo I took [the CS444 compilers class](https://www.student.cs.uwaterloo.ca/~cs444/) with a project to write a compiler from a substantial subset of Java to x86, with a language and two teammates of your choice. My group of three chose to write our compiler in Rust and it was a fun experience. We spent time coming to design decisions that worked out really well and used Rust’s strengths. Our compiler ended up being around 6800 lines of Rust and I personally put in around 60 hours of solid coding and more on code review and design. In this post I’ll go over some of the design decisions we made and some thoughts on what it was like using Rust. - -## Lexing and Parsing - -The lectures for the course recommended writing an NFA to DFA compiler to implement the lexer, and writing an [LR(1)](https://en.wikipedia.org/wiki/Canonical_LR_parser) parser generator for the parser, then having a separate “weeding” pass to construct a final AST ([Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)) and validate it in various ways. - -I suggested that we should try using a hand-written lexer and recursive descent parser instead, and my teammates agreed. A recursive descent parser allowed us to put all the code to parse, validate, and create the AST node in one place. We figured writing a pass to rewrite and validate the raw parse tree into a strongly typed AST would be about as much code as a recursive descent parser, except with the additional work of having to implement an LR(1) parser generator. - -The AST we produced made good use of Rust’s type system, including extensive use of `enum` sum types to handle variants of types, expressions and statements. We also used `Option` and `Vec` extensively, as well as `Box` to allow type recursion. Our AST types looked like this: - -```rust -// We preserve source span information using a `Spanned` struct -pub type Type = Spanned; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum TypeKind { - Array(Box), - Ref(TypeRef), - Int, - Byte, - // ... -} - -// ... - -#[derive(Clone, Debug)] -pub struct InterfaceDecl { - pub name: String, - pub extends: Vec, - pub methods: Vec, -} -``` - -We produced this using a `Parser` struct with functions for parsing different constructs that could also return parse errors. The `Parser` struct had a number of helper functions to easily consume and inspect tokens, using the power of abstraction present in a full programming language to get closer to the brevity of a parser generator grammar DSL. Here’s an example of what our parser looked like: - -```rust -#[derive(Clone, Debug)] -pub enum ParseError { - Unexpected(SpannedToken), - DuplicateModifier(SpannedToken), - MultipleVisibilities, - // ... -} - -pub struct Parser<'a> { - tokens: &'a [SpannedToken], - pos: usize, -} - -// ... - -fn parse_for_statement(&mut self) -> PResult { - self.eat(&Token::For)?; - self.eat(&Token::LParen)?; - let init = self.parse_unless_and_eat(Token::Semicolon, Self::parse_for_init)?; - let condition = self.parse_unless_and_eat(Token::Semicolon, Self::parse_expr)?; - let update = self.parse_unless_and_eat(Token::RParen, Self::parse_statement_expr)?; - let body = self.parse_statement()?; - Ok(ForStatement { init, condition, update, body }) -} - -// ... -``` - -### Backtracking - -Mostly our parser takes the form of an [LL(1) parser](https://en.wikipedia.org/wiki/LL_parser), which looks ahead one token to decide how it should parse. But some constructs require unlimited lookahead to parse. For example `(java.lang.String)a` should parse as a parenthesized field access chain on the ‘java’ variable except for the `a` at the end, which makes it a cast expression. In fact even `LR(1)` parsers can’t parse this specific case properly, and the recommended hack is to parse the inside of the parens as an “expression” and then just validate in the weeder that the expression is actually a type. - -We solve this problem using backtracking, which is where we can save a position in the token stream, speculatively parse the following input as one construct, and then roll back to that saved position if that parsing fails. This can cause non-linear parse times on pathological input, but pathological cases don’t occur non-maliciously in practice, especially if backtracking is only used in some situations rather than for the whole parser. - -An alternative strategy to backtracking that works in some situations is to parse the common elements of both nonterminals that could follow, then once the parser reaches the point where it can decide, it calls the specific non-terminal function passing what has been parsed so far as arguments. We use this strategy for deciding between parsing classes and interfaces and between parsing methods and constructors, by parsing the modifiers first, then looking ahead, then parsing the rest passing the parsed modifiers as arguments. - -We have Rust helper functions that make backtracking really easy by trying one parse and then trying another if the first parse returns an `Err`: - -```rust -// Unlike the Java spec, we can have arguments like `allow_minus` to avoid -// massive duplication in the case of minor special cases. -// `allow_minus` makes sure `(a)-b` parses as `int-int` rather than `(Type)(-int)` -fn parse_prim_expr(&mut self, allow_minus: AllowMinus) -> PResult> { - let cur = &self.tokens[self.pos]; - let mut lhs = match &cur.tok { - Token::LParen => self.one_of(Self::parse_cast_expr, Self::parse_paren_expr), - // ... - }; - // ... -} -``` - -### Pratt expression parsing - -Instead of parsing expressions with precedence using many grammar levels, we use a [Pratt parsing / precedence climbing](https://www.oilshell.org/blog/2017/03/31.html) system. This algorithm allows specifying the operators as a table with a “binding power” integer, with higher binding power for operators with higher precedence. This is both easier and more efficient for parsing expressions with many levels of precedence. - -Instead of using data tables like in the canonical Pratt parser implementation, we used Rust functions with match statements, which fill the same purpose but with more power and no need to keep a data structure around: - -```rust -fn binding_power(cur: &SpannedToken) -> Option { - match &cur.tok { - Token::Operator(op) => match op { - Op::Times | Op::Divide | Op::Modulo => Some(12), - Op::Plus | Op::Minus => Some(11), - Op::Greater | Op::GreaterEqual | Op::Less | Op::LessEqual => Some(9), - Op::Equal | Op::NotEqual => Some(8), - Op::And => Some(7), - // ... - }, - Token::Instanceof => Some(9), - _ => None, - } -} -``` - -## Snapshot testing - -Starting when we did our parser and continuing for the rest of our compiler, we made extensive use of snapshot testing with the [insta crate](https://github.com/mitsuhiko/insta). Snapshot testing (similar to [expect tests](https://blog.janestreet.com/testing-with-expectations/)) allows you to write tests which just provide the resulting data structure of some process and the testing system will create a “snapshot” of the result of that test in a file, and if the result ever changes it will cause a test failure and show you the diff between the snapshot file and the result it got. If the change was expected, you can then run a command to update the snapshot files that changed. - -This was super useful for writing our parser, before we could parse full files and do anything with them, we could parse short snippets into AST types implementing the Rust `Debug` trait, and `insta` would create pretty-printed snapshots that we could inspect for correctness, and then commit to check for future regressions. - -```rust -#[test] -fn test_statement() { - let mut lexer = file_lexer("testdata/statements.java"); - let tokens = lexer.lex_all().unwrap(); - let mut parser = Parser::create(&tokens); - - let statement = parser.parse_statement(); - assert_debug_snapshot_matches!("statements", statement); -} -``` - -Later during the code generation phase we used this extensively to check our assembly output on test programs. - -## Semantic analysis - -About half of our compiler is in the middle-end passes which compute information necessary for code generation and verify various correctness properties. This includes: - -* Resolving variable and type names. -* Folding constant expressions like `5*3+2` into numbers. -* Checking many different constraints of the Java class/interface hierarchy. -* Checking that all statements are reachable and all non-`void` functions return. -* Resolving types of all expressions and checking their correctness. - -### Visitor infrastructure - -Most of the passes in the middle of our compiler only care about certain AST nodes, but need to act on those nodes anywhere they might occur in the AST. One way to do this would be to pattern match through the whole AST in every patch, but there’s a lot of nodes so that would involve a lot of duplication. - -Instead we have a `Visitor` trait (like an interface in other languages) which can be implemented by a compiler pass. It has callbacks only for the events we actually need, which can run code at various points in the traversal of the AST, as well as modify the AST in place. All the callbacks have default implementations that do nothing so that passes only need to implement the methods they need. - -```rust -// We use a dynamic error type here so we don't have to make the visitor generic and -// instantiate it a bunch for every error type -pub type VResult = Result<(), Box>; - -pub trait Visitor { - // used for resolving variable references - fn visit_var_ref(&mut self, _t: &mut VarRef) -> VResult { - Ok(()) - } - - fn start_method(&mut self, _t: &mut Method) -> VResult { - Ok(()) - } - - // `finish_` methods get passed the result of traversing their body so that they - // can wrap errors to provide better location information - fn finish_method(&mut self, _t: &mut Method, res: VResult) -> VResult { - res - } - - // like a `finish_` method except it doesn't need the result - fn post_expr(&mut self, _t: &mut Expr) -> VResult { - Ok(()) - } - - // ... a bunch of other methods -} -``` - -Passes that implement `Visitor` are driven by dynamically dispatched calls from the `Visitable` trait, which is implemented by every AST node and traverses the whole tree in evaluation order. A cool Rust feature we make good use of is “blanket impls” which make the logic for handling AST children that are in containers clean and uniform. - -```rust -pub trait Visitable { - fn visit(&mut self, v: &mut dyn Visitor) -> VResult; -} - -impl Visitable for Vec { - fn visit(&mut self, v: &mut dyn Visitor) -> VResult { - for t in self { - t.visit(v)?; - } - Ok(()) - } -} - -// ... other blanket impls for Option and Box - -impl Visitable for TypeKind { - fn visit(&mut self, v: &mut dyn Visitor) -> VResult { - match self { - TypeKind::Array(t) => t.visit(v)?, - TypeKind::Ref(t) => t.visit(v)?, - _ => (), - } - Ok(()) - } -} - -impl Visitable for ForStatement { - fn visit(&mut self, v: &mut dyn Visitor) -> VResult { - v.start_for_statement(self)?; - // closure allows us to use ? to combine results - let res = (|| { - self.init.visit(v)?; - self.condition.visit(v)?; - self.update.visit(v)?; - self.body.visit(v) - })(); - v.finish_for_statement(self, res) - } -} - -// ... many other Visitable implementations -``` - -This made a lot of our passes much easier. For example constant folding just overrides the `post_expr` method, checks if the children of an expression are constants and if so uses `mem::replace` to replace the node with a constant. - -### Resolving names - -One discussion we had is how to handle resolving type and variable names. The most obvious way was doing so by mutating the AST using an `Option` field that’s initially `None`. However our functional programmer instincts felt icky about this so we tried to think of a better way. Using an optional field also had the problem that we knew by the code generation phase that all variables would be resolved but the type system would still think they could be `None` so we’d need to `unwrap()` them every time we wanted to access them. - -We first considered using a side table where we’d give every named reference an ID or hash it, then have a map from ID to resolved location that we created during the resolution stage. But we didn’t like how this would make debugging harder since we could no longer just print out our AST types with `Debug` to see all their information including resolutions. It also would require passing around quite a few side tables and doing lots of lookups in them by the later stages. It didn’t even solve the need for `unwrap` since the table access could theoretically not find the corresponding element. - -Next we considered making all of our AST types generic with an annotation type parameter that started out as `()` but changed as the AST progressed through stages where it gained more info. The main problem with this is that each pass would need to re-build the entire AST, which would make easy visitor infrastructure much harder. Maybe if Rust had something like [an automatically derivable `Functor` implementation](https://gitlab.haskell.org/ghc/ghc/wikis/commentary/compiler/derive-functor) it wouldn’t have been bad, but barring that it would need a lot of boilerplate. There were also multiple things we needed to annotate at various stages necessitating many parameters, and a lot of AST types, which would require a lot of refactoring our AST and parser to add a multitude of parameters. - -So instead we just bit the bullet and used `Option` type fields, and I think it worked out well. We implemented a nice `Reference` generic that had a `raw` and `resolved` field. We used it for both variable and type references. It had `Hash` and `PartialEq` implementations that only looked at the resolved value because that’s what mattered for data structures in later passes. It also had a special `Debug` implementation that made the output in snapshot tests nicer: - -```rust -impl fmt::Debug for Reference { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(r) = &self.resolved { - write!(f, "{:#?} => {:#?}", self.raw, r) - } else { - write!(f, "{:?}", self.raw) // only print the raw if not resolved yet - } - } -} -``` - -### Reference counting - -In a number of different places, especially the class hierarchy checking and type checking phases, a lot of things needed to have the same pieces of information propagated to them. For example types bubbling up an expression or inherited methods bubbling down a tree. In a language like Java we’d just have multiple references to the same object, but in Rust for ownership reasons we couldn’t do that straightforwardly. We started out in some places by `clone`ing things which worked fine, but I realized I could just switch everything to use `Rc` to allow sharing. - -I had an interesting moment where I thought “man it sucks that this code has to do all this reference count manipulation, that’s unnecessarily slow, maybe I should refactor this to use an arena or something”. Then I realized that if I had been writing in Swift I wouldn’t have given this a second thought because **everything** would be ref-counted, and even worse than the Rust version, **atomically** ref-counted. Writing code in Rust makes me feel like I have an obligation to make code as fast as possible in a way other languages don’t, just by surfacing the costs better. Sometimes I need to remind myself that actually it’s fast enough already. - -## Code Generation - -The course requires that we generate textual NASM x86 assembly files. Given that we only need to output to those, we decided we didn’t need an intermediate abstraction for generating assembly, and our code generation stage could just use Rust string formatting. This would make our code simpler, easier and also allow us to more easily include comments in the generated assembly. - -The fact that we preserved source span information through our whole compiler and could generate comments came in handy because we could output comments containing the source expression/statement location for every single generated piece of code. This made it much easier to track down exactly which piece of code was causing a bug. - -A somewhat annoying Rust thing we ran into is that we could find two easy ways of formatting to a string, both of which had an issue: - -```rust -let mut s = String::new(); -// Requires a Result return type or unwrap, even though it won't ever fail. -// Generates a bunch of garbage error handling LLVM needs to optimize out. -writeln!(s, "mov eax, {}", val)?; -// Allocates an intermediate String which it then immediately frees -s.push_str(format!("mov eax, {}", val)); -``` - -My two teammates worked on the initial stages of code generation in parallel and each of them chose a different fork of this tradeoff, and by that close to the end of the course our consistency standards had relaxed, so our code generation has both. - -### Usercorn - -Our compiler was supposed to output Linux ELF binaries and link to a runtime that made Linux syscalls. However, our entire team used macOS. Rewriting the runtime for macOS would have been somewhat annoying since syscalls aren’t always as easy and well documented on macOS as Linux. It also would have added an annoying delay to running our tests and made the harness more complex if we had to `scp` the binaries to a Linux server or VM. - -I remembered that my internet friend had written a cool tool called [usercorn](https://github.com/lunixbochs/usercorn) that used the [Unicorn CPU emulator](https://www.unicorn-engine.org/) plus some fanciness to run Linux binaries on macOS as if they were normal macOS binaries (or vice versa and a bunch of other things). It was straightforward to build a self-contained version that I could check into our repository and use in our tests to run our binaries. My teammate then got together a macOS build of `ld` that could link Linux ELF binaries and included it. - -We could also use `usercorn` to output a trace of all the instructions executed and registers modified by our programs, and this came in handy quite a few times for debugging our code generation. - -I ran into one problem where a test program that did a lot of allocation was 1000x slower under usercorn than on a real Linux server. Luckily I knew the author and I just sent him the offending binary and he quickly figured it was due to an inefficient implementation of the `brk` syscall which reasonable programs don’t use for every single memory allocation like the runtime the course provided did. He quickly figured out how to make it more efficient and pushed a fix later that evening which solved my problem. He’s pretty awesome, [subscribe to his Patreon!](https://www.patreon.com/lunixbochs/overview) - -I then shared our pre-compiled bundle of `usercorn` and `ld` (with the bug fix for the assignment tests) with a few other teams I knew who used macOS so they could have an easier time testing as well. - -## Conclusion - -Overall I’m proud of how our compiler turned out. It was a fun project and my teammates were excellent. I also think Rust ended up being a good choice of implementation language, especially the powerful `enum`s and pattern matching. The main downsides of Rust were the long compile times (although apparently comparable to a group that did their compiler in C++), and the fact that sometimes we had to do somewhat more work to satisfy the borrow checker. - -One of the most interesting learning experiences from the project was when afterwards I talked to some other teams and got to compare what it was like to do the same project in different languages and with different design decisions. I’ll talk about that in an upcoming post! - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From ca6c1a092a4fe1344a7ca949e9091416b40ae227 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 35/58] Revert "Create typescript-impossible-states-irrepresentable.md (#6208)" This reverts commit db5ed8e80e333cbfb385122d35bfc5f3af2fbf9d. --- ...cript-impossible-states-irrepresentable.md | 183 ------------------ 1 file changed, 183 deletions(-) delete mode 100644 TODO1/typescript-impossible-states-irrepresentable.md diff --git a/TODO1/typescript-impossible-states-irrepresentable.md b/TODO1/typescript-impossible-states-irrepresentable.md deleted file mode 100644 index 036b99d6a72..00000000000 --- a/TODO1/typescript-impossible-states-irrepresentable.md +++ /dev/null @@ -1,183 +0,0 @@ -> * 原文地址:[Using Typescript to make invalid states irrepresentable](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable) -> * 原文作者:[Javier Casas](http://www.javiercasas.com) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/typescript-impossible-states-irrepresentable.md](https://github.com/xitu/gold-miner/blob/master/TODO1/typescript-impossible-states-irrepresentable.md) -> * 译者: -> * 校对者: - -# Using Typescript to make invalid states irrepresentable - -One of the principles of good Haskell, and in general good Typed Functional Programming, is the principle of making invalid states irrepresentable. What does this mean? We use the typesystem to craft types that impose constraints on our data and our state, so that it's impossible to represent these states that should not exist. Now that, at the type level, we managed to banish invalid states, the typesystem will step in and give us trouble every time we try to construct an invalid state. If we can't construct an invalid state, it's very hard for our program to end up in an invalid state, because in order to reach that invalid state the program should have followed a chain of actions that construct the invalid state. But such program would be invalid at the type level, and the typechecker would happily step in and tell us we are doing something wrong. This is great, because the typesystem will happily remember for us the constraints our data has, so we don't have to trust our flaky memory to remember them. - -Fortunately, many of the results of this technique can be adapted to other programming languages, and today we are going to experiment with it in Typescript. - -## A sample problem - -Let's work on a sample problem so we can try to understand how we can use this. We are going to constraint a type for a function using Algebraic Data Types, so that we can prevent invalid parameters to it. Our toy problem is as follows: - -* We have a function that accepts a single parameter: an object with potentially two fields, called `field1` and `field2`. -* The object may not have neither of the two fields. -* The object may have only `field1`, and not `field2`. -* Only if the object has `field1`, then it can have `field2`. -* Therefore, an object with `field2`, but not `field1`, is invalid. -* For simplicity, when `field1` or `field2` exist, they will be of type `string`, but they could be of any type. - -### Naive solution - -Let's start with the simplest approach. Because both `field1` and `field2` can exist, or not, we just make them optional. - -```typescript -interface Fields { - field1?: string; - field2?: string; -}; - -function receiver(f: Fields) { - if (f.field1 === undefined && f.field2 !== undefined) { - throw new Error("Oh noes, this should be impossible!"); - } - // do stuff -} -``` - -Unfortunately, this doesn't prevent anything at compile time, and requires checking for that possible error at runtime. - -```typescript -// This will not raise any errors at compile time -// so we will have to find at runtime that it's broken -receiver({field2: "Hahaha, I didn't put a field1!"}) -``` - -### Basic ADT solution - -So we called `receiver` with the wrong fields several times in a row, our application exploded in flames, and we are not happy. Time to do something about it. Let's enumerate the cases again, so that we can see if we can make a type with the right shape: - -* The object may not have neither of the two fields. -* The object may have only `field1`, and not `field2`. -* Only if the object has `field1`, then it can have `field2`. Therefore, in this case, the object has both `field1` and `field2`. -* An object with `field2`, but not `field1`, is invalid. - -Let's transcribe this into types: - -```typescript -interface NoFields {}; - -interface Field1Only { - field1: string; -}; - -interface BothField1AndField2 { - field1: string; - field2: string; -}; - -interface InvalidObject { - field2: string; -}; -``` - -We decided to also include here `InvalidObject`, but it's a bit silly writing it, because we don't want it to really exist. We may keep it around as documentation, or we may remove it so that to affirm even more that it is not supposed to exist. Now let's write a type for `Fields`: - -```typescript -type Fields = NoFields | Field1Only | BothField1AndField2; // I deliberately forgot to put here InvalidObject -``` - -With this disposition, it's harder to send to `receiver` an `InvalidObject`: - -```typescript -receiver({field2: "Hahaha, I didn't put a field1!"}); // Type error! This object doesn't match the type `Fields` -``` - -We also need to tweak the `receiver` function a little bit, mostly because the fields may not exist now, and the typechecker now requires proof that you are going to read fields that actually exist: - -```typescript -function receiver(f: Fields) { - if ("field1" in f) { - if ("field2" in f) { - // do something with f.field1 and f.field2 - } else { - // do something with f.field1, but f.field2 doesn't exist - } - } else { - // f is an empty Fields - } -} -``` - -#### Limitations of structural typing - -Unfortunately, for good or for bad, Typescript is a structural type system, and this allows us to bypass some of the safety if we are not careful. The `NoFields` type (empty object, `{}`), in Typescript, means something totally different to what we want it to do. Actually when we write: - -```typescript -interface Foo { - field: string; -}; -``` - -Typescript understands that any `object` with a `field` of type `string` is good, except for the case where we create a new object, like: - -```typescript -const myFoo : Foo = { field: "asdf" }; // In this case we can't add more fields -``` - -But, on assignment, Typescript tests using structural typing, and that means our objects may end with more fields that what we would like them to have: - -```typescript -const getReady = { field: "asdf", unexpectedField: "hehehe" }; -const myFoo : Foo = getReady; // This is not an error -``` - -So, when we extend this idea to the empty object `{}`, turns out that on assignment, Typescript will accept any value as long as that value is an object, and has all the fields demanded. Because the type demands no fields, this second condition succeeds trivially for any `object`, which is totally not what we wanted it to do. - -### Banning unexpected fields - -Let's try to make a type for objects with no fields, so that we actually have to go out of our way to fool the typechecker. We already know `never`, the type that can never be satisfied. Now we need another ingredient to say "every possible field". And this ingredient is: `[key: string]: type`. With these two we can construct the object with no fields: - -```typescript -type NoFields = { - [key: string]: never; -}; -``` - -This type means: this is an object, whose fields are of type `never`. Because you can't construct a `never`, there is no way to make valid values for the fields of this object. Therefore, the only solution is an object with no fields. Now, we have to be more deliberate to break the types: - -```typescript -type NoFields = { - [key: string]: never; -}; - -interface Field1Only { - field1: string; -}; - -interface BothField1AndField2 { - field1: string; - field2: string; -}; - -type Fields = NoFields | Field1Only | BothField1AndField2; - -const broken = {field2: "asdf"}; - -// Bypass1: go through an empty object type -// Empty object is a well known code smell in Typescript -const bypass1 : {} = broken; -const brokenThroughBypass1 : Fields = bypass1; - -// Bypass2: use the `any` escape hatch -// any is another well known code smell in Typescript -const bypass2 : any = broken; -const brokenThroughBypass2 : Fields = bypass2; -``` - -It looks like now we need two very specific steps to break the system, so it will be definitely quite harder to do it, and we should notice something wrong if we have to go to such deep ways to construct a program. - -## Conclusion - -Today we saw an approach to the great promise of program correctness through types, applied to a more mainstream language: Typescript. Although Typescript can't promise the same level of safety as Haskell, that doesn't prevent us from applying a few ideas from Haskell to Typescript. - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 9fb912868875d3ef748e3403e490322fc0aca2bd Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 36/58] =?UTF-8?q?Revert=20"Go=20=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E6=A6=82=E8=A7=88=20(#6175)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 33cf639036e4852acd39517ba6e586012efa9a3c. --- TODO1/birdseye-go.md | 134 +++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/TODO1/birdseye-go.md b/TODO1/birdseye-go.md index 5b0785b0f13..47cf7f8c70d 100644 --- a/TODO1/birdseye-go.md +++ b/TODO1/birdseye-go.md @@ -2,110 +2,110 @@ > * 原文作者:[Axel Wagner](https://blog.merovius.de) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/birdseye-go.md](https://github.com/xitu/gold-miner/blob/master/TODO1/birdseye-go.md) -> * 译者:[JackEggie](https://github.com/JackEggie) -> * 校对者:[40m41h42t](https://github.com/40m41h42t), [JalanJiang](https://github.com/JalanJiang) +> * 译者: +> * 校对者: -# Go 语言概览 +# A bird's eye view of Go -**本文摘要:本文非常笼统地总结了 Go 语言的定义、生态系统和实现方式,也尽力给出了与不同的需求所对应的参考文档,详情参见本文末尾。** +**tl;dr: I provide a very high-level overview of what Go-the-language means vs. Go-the-ecosystem vs. Go-an-implementation. I also try to provide specific references to what documentation is most useful for what purpose. See the bottom-most section for that.** -每当我们说起“Go 语言”的时候,可能会因为场景的不同聊到很多完全不同的东西。因此,我尝试着对 Go 语言和其生态系统做一个概述,并在各部分内容中都列出相关的文档(这可能有点像是大杂烩,其中还包含了我最近实际遇到的许多问题)。让我们开始吧: +When we talk about "Go", depending on context, we can mean very different things. This is my attempt at providing a very high-level overview of the language and ecosystem and to link to the relevant documentation about how each part fits together (it's also a bit of a hodgepodge though, addressing individual random questions I encountered recently). So, let's dive in: -#### Go 编程语言 +#### The Go programming language -Go 语言是一种编程语言。作为一种权威,[Go 语言规范](https://golang.org/ref/spec)中定义了代码的格式规范和代码所代表的含义。不符合该规范的都不是 Go 语言。同样地,该规范中**没有**提到的内容不视为该语言的一部分。目前由 Go 语言开发团队维护该规范,每半年发布一个新版本。在我写这篇文章的时候最新的版本是 `1.12`。 +The bottom turtle is Go, the programming language. It defines the format and meaning of source code and the authoritative source is [the Go language specification](https://golang.org/ref/spec). If something doesn't conform to the spec, it's not "Go". And conversely, if something **isn't** mentioned in the spec it's not part of the language. The language spec is maintained by the Go team and versioned, with a new release roughly every six months. At the time I wrote this post, the newest release was version `1.12`. -Go 语言规范规定了: +The domain of the spec are -* 语法 -* 变量的类型、值,及其语义 -* 预先声明的标识符及其含义 -* Go 程序的运行方式 -* 特殊的 [unsafe 包](https://golang.org/ref/spec#Package_unsafe)(虽然没有包含所有的语义) +* The grammar of the language +* Types, values and their semantics +* What identifiers are predeclared and what their meaning is +* How Go programs get executed +* The special package [unsafe](https://golang.org/ref/spec#Package_unsafe) (though not all of its semantics) -该规范**应该**已经足够让你实现一个 Go 语言的编译器了。实际上,已经有很多人基于此实现了许多不同的编译器。 +The spec alone **should** enable you to write a compiler for Go. And indeed, there are many different compilers. -#### Go 编译器及其运行时 +#### A Go compiler and runtime -该语言规范只是一份文本文档,它本身不太有用。你需要的是实现了这些语义的软件,即编译器(分析、检查源代码,并将其转换为可执行的形式)和运行时(提供运行代码时所需的环境)。有很多这样的软件组合,他们都或多或少有些不同。示例如下: +The language spec is a text document, which is not useful in and of itself. For that you need software that actually implements these semantics. This is done by a compiler (which analyzes and checks the source code and transforms it into an executable format) and a runtime (which provides the necessary environment to actually run the code). There are many such combinations and they all differ a bit more or a bit less. Examples are -* `gc`,Go 语言开发团队自己开发的纯 Go 语言实现的(有一小部分汇编实现)编译器和运行时。它随着 Go 语言一起发布。与其他此类工具不同的是,`gc` 并不**严格**区分编译器、组装器和链接器 —— 它们在实现的时候共享了大量的代码,并且会共享或传递一些重要职责。因此,通常无法链接由不同版本的 `gc` 所编译的包。 -* [gccgo 和 libgo](https://golang.org/doc/install/gccgo),gcc 的前端和其运行时。它是用 C 实现的,并且也由 Go 开发团队维护。然而,由于它是 gcc 组织的一部分,并根据 gcc 的发布周期发布,因此通常会稍微落后于 Go 语言规范的“最新”版本。 -* [llgo](https://llvm.org/svn/llvm-project/llgo/trunk/README.TXT),LLVM 的前端。我对其不太了解。 -* [gopherjs](https://github.com/gopherjs/gopherjs),将 Go 代码编译为 JavaScript,并使用一个 JavaScript VM 和一些自定义代码作为运行时。长远来看,由于 `gc` 获得了 WebAssembly 的原生支持,它有可能会被淘汰。 -* [tinygo](https://tinygo.org/),针对小规模编程的不完整实现。它可以通过自定义一个运行时运行在微控制器(裸机)或者 WebAssembly 虚拟机上。由于它的局限性,**技术上来说**它并没有实现 Go 语言的所有特性 —— 主要体现在它缺少垃圾回收器、并发和反射。 +* `gc`, a compiler and runtime written in pure Go (with some assembly) by the Go team themselves and versioned and released together with the language. Unlike other such tools, `gc` doesn't **strictly** separate the compiler, assembler and linker - they end up sharing a lot of code and some of the classical responsibilities move or are shared between them. As such, it's in general not possible to e.g. link packages compiled by different versions of `gc`. +* [gccgo and libgo](https://golang.org/doc/install/gccgo), a frontend for gcc and a runtime. It's written in C and maintained by the Go team. It lives in the gcc organization though and is released according to the gcc release schedule and thus often lags a bit behind the "latest" version of the Go spec. +* [llgo](https://llvm.org/svn/llvm-project/llgo/trunk/README.TXT), a frontend for LLVM. I don't know much else about it. +* [gopherjs](https://github.com/gopherjs/gopherjs), compiling Go code into javascript and using a javascript VM plus some custom code as a runtime. Long-term, it'll probably be made obsolete by `gc` gaining native support for WebAssembly. +* [tinygo](https://tinygo.org/), an incomplete implementation targeting small code size. Runs on either bare-metal micro-controllers or WebAssembly VMs, with a custom runtime. Due to its limitations it doesn't **technically** implement Go - notably, it doesn't include a garbage collector, concurrency or reflection. -还有更多其他的实现,但这已经足以让你了解不同的实现方式。以上每一种方法都使用了不同的方式来实现 Go 语言,并具有自己与众不同的特性。他们可能存在的不同之处有(为了说明这一点,下面的某些说法可能会有点奇特): +There are more, but this gives you an overview over the variety of implementations. Each of these made potentially different choices for how to implement the language and have their own idiosyncrasies. Examples (some of them a bit exotic, to illustrate) where they might differ are: -* `int`/`uint` 的大小 —— 长度可能为 32 位或 64 位。 -* 运行时中基础功能的实现方式,如内存分配、垃圾回收和并发的实现。 -* 遍历 `map` 的顺序并没有在 Go 语言中定义 —— `gc` 显然会将这类操作随机化,而 `gopherjs` 会用你使用的 JavaScript 实现遍历。 -* `append` 操作分配的所需额外内存空间大小 —— 但是,**在分配额外空间时**,**不会**再次分配更多的内存空间。 -* `unsafe.Pointer` 与 `uintptr` 之间的转换方式。特别指出,`gc` 对于该转换何时应该生效有自己的[规则](https://godoc.org/unsafe#Pointer)。通常情况下,`unsafe` 包是虚拟的,它会在编译器中被实现。 +* Size of `int`/`uint` \- the language allows them to be either 32 or 64 bit wide. +* How fundamental functionalities of the runtime, like allocation, garbage collection or concurrency are implemented. +* The order of ranging over a `map` isn't defined in the language - `gc` famously explicitly randomizes it, `gopherjs` uses (last time I checked) whatever the javascript implementation you are running on uses. +* How much extra space `append` allocates if it needs to - **not** however, **when** it allocates extra space. +* How conversions between `unsafe.Pointer` and `uintptr` happen. `gc`, in particular, comes with its own [set of rules](https://godoc.org/unsafe#Pointer) regarding when these conversions are valid and when they aren't. In general, the `unsafe` package is virtual and implemented in the compiler. -一般来说,根据规范中没有提到的某些细节(尤其是上面提到的那些细节)可以使你的程序用不同的编译器也能**编译**,但往往程序不会像你预期的那样**正常工作**。因此,你应该尽力避免此类事情发生。 +In general, relying on details not mentioned in the spec (in particular the ones mentioned here) makes your program **compile** with different compilers, but not **work** as expected. So you should avoid it if possible. -如果你的 Go 语言是通过“正常”渠道安装的话(在官网上下载安装,或是通过软件包管理器安装),那么你会得到 Go 开发团队提供的 `gc` 和正式的运行时。在本文中,当我们在讨论“Go 是如何做的”时,若没有在上下文特别指明,我们通常就是在谈论 `gc`。因为它是最重要的一个实现。 +If you install Go via a "normal" way (by downloading it from the website, or installing it via a package manager), you'll get `gc` and the official runtime by the Go team. And if the context doesn't imply otherwise, when we talk about how "Go does things", we usually refer to `gc`. It's the main implementation. -#### 标准库 +#### The standard library -[标准库](https://golang.org/pkg/#stdlib)是 Go 语言中附带的一组依赖包,它可以被用来立即构建许多实用的应用程序。它也由 Go 开发团队维护,并且会随着 Go 语言和编译器一起发布。一般来说,标准库的某种实现只能依赖与其共同发布的编译器才能正常使用。因为大部分(但不是所有)运行时都是标准库的一部分(主要包含在 `runtime`、`reflect`、`syscall` 包中)。由于编译器在编译时需要兼容当前使用的运行时,因此它们的版本要相同。标准库的 **API** 是稳定的,不会以不兼容的方式改变,所以基于某个指定版本的标准库编写的 Go 程序在编译器的未来版本中也可以正常运行。 +[The standard library](https://golang.org/pkg/#stdlib) is a set of packages that come with Go and can be relied upon to immediately build useful applications with. It too is maintained by the Go team and versioned and released together with the language and compiler. In general the standard library of one implementation will only work with the compiler it comes with. The reason is that most (but not all) of the runtime is part of the standard library (mainly in the packages `runtime`, `reflect`, `syscall`). As the compiler needs to generate code compatible with the used runtime, both need to come from the same version. The **API** of the standard library is stable and won't change in incompatible ways, so a Go program written against a given version of the standard library will continue to work as expected with future versions of the compiler. -有些标准库会完全自己实现整个库中的所有内容,而有些则只实现一部分 —— 开发者尤其会在 `runtime`、`reflect`、`unsafe` 和 `syscall` 包中实现自定义的功能。举个例子,我相信 [AppEngine 标准库](https://cloud.google.com/appengine/docs/standard/go/)是出于安全考虑重新实现了标准库的部分功能的。这类重新实现的部分通常会尽量对用户保持透明。 +Some implementations use their own version of some or all of the standard library - in particular, the `runtime`, `reflect`, `unsafe` and `syscall` packages are completely implementation-defined. As an example, I believe that [AppEngine Standard](https://cloud.google.com/appengine/docs/standard/go/) used to re-define parts of the standard library for security and safety. In general, implementations try to make that transparent to the user. -还存在一种[标准库以外的独立库](https://golang.org/pkg/#subrepo),通俗地说这就是 `x` 或者说是“扩展库”。这种库包含了 Go 开发团队同时开发和维护的部分代码,但是**不会**与 Go 语言有相同的发布周期,并且相比于 [Go 语言本身](https://golang.org/doc/go1compat),兼容性也会较差(功能性和维护性也会较差)。其中的代码要么是实验性的(在未来可能会包含在标准库中),要么是比起标准库中的功能还不够泛用,或者是在某些罕见的情况下,提供一种开发者们可以与 Go 开发团队同步进行代码审查的方式。 +There is also a [separate set of repositories](https://golang.org/pkg/#subrepo), colloquially referred to as `x` or "the subrepositories". They contain packages which are developed and maintained by the Go team with all the same processes, but are **not** on the same release schedule as the language and have less strict compatibility guarantees (and commitment to maintainership) than [Go itself](https://golang.org/doc/go1compat). The packages in there are either experimental (for potential future inclusion in the standard library), not widely useful enough to be included in the standard library or, in rare cases, a way for people on the Go team to work on code using the same review processes they are used to. -再一次强调,如果没有额外地指出,在提到“标准库”时,我们指的是官方维护和发布的、托管在 [golang.org](https://golang.org/pkg) 上的 Go 标准库。 +Again, when referring to "the standard library" devoid of extra context, we mean the officially maintained and distributed one, hosted on [golang.org](https://golang.org/pkg). -#### 代码构建工具 +#### The build tool -我们需要代码构建工具来使 Go 语言易于使用。构建工具的主要职责是找到需要编译的包和所有的依赖项,并依据必要的参数调用编译器和链接器。Go 语言有[对包的支持](https://golang.org/ref/spec#Packages),允许在编译时把多个源代码文件视为一个单元。这也定义了导入和使用其他包的方式。但重要的是,这并没有定义导入包的路径与源文件的映射方式,也没有定义导入包在磁盘中的位置。因此,每种构建工具对于该问题都有不同的处理方式。你可以使用通用构建工具(如 Make 命令),但也有许多专门为 Go 语言而生的构建工具: +To make the language user-friendly, you need a build tool. The primary role of this tool is to find the package you want to compile, find all of its dependencies, and execute the compiler and linker with the arguments necessary to build them. Go (the language) has [support for packages](https://golang.org/ref/spec#Packages), which combine multiple source files into one unit of compilation. And it defines how to import and use other packages. But importantly, it doesn't define how import paths map to source files or how they are laid out on disk. As such, each build tool comes with its own ideas for this. It's possible to use a generic build tool (like Make) for this purpose, but there are a bunch of Go-specific ones: -* [Go 语言工具](https://golang.org/cmd/go/)[1]是 Go 开发团队官方维护的构建工具。它与 Go 语言(`gc` 和标准库)有相同的发布周期。它需要一个名为 `GOROOT` 的目录(该值从环境变量中获取,会在安装时产生一个默认值)来存放编译器、标准库和其他各种工具。它要求所有的源代码都要存放在一个名为 `GOPATH` 的目录下(该值也从环境变量中获取,默认为 `$HOME/go` 或是一个与其相等的值)。举例来说,包 `a/b` 的源代码应该位于诸如 `$GOPATH/src/a/b/c.go` 的路径下。并且 `$GOPATH/src/a/b` 路径下应该**只**包含一个包下的源文件。在分布式的模式下,有一种机制可以[从任意服务器上递归地下载某个包及其依赖项](https://golang.org/cmd/go/#hdr-Remote_import_paths),即使这种机制不支持版本控制或是下载校验。Go 语言工具中也包含了许多其他工具包,包括用于测试 Go 代码的工具、阅读文档的工具([golang.org](https://golang.org) 是用 Go 语言工具部署的)、提交 bug 的工具和其他各种小工具。 -* [gopherjs](https://github.com/gopherjs/gopherjs) 自带的构建工具,它在很大程度上模仿了 Go 语言工具。 -* [gomobile](https://github.com/golang/go/wiki/Mobile) 是一个专门为移动操作系统构建 Go 代码的工具。 -* [dep](https://github.com/golang/dep)、[gb](https://getgb.io/)、[glide](https://glide.sh/) 等等是社区开发的构建和依赖项管理工具,它们各自都有自己独特的文件布局方式(有些可以与 Go 语言工具兼容,有些则不兼容)和依赖项声明方式。 -* [bazel](https://bazel.build/) 是谷歌内部构建工具的开源版本。虽然它的使用实际上并不限于 Go 语言,但我之所以把它列为单独的一项,是因为人们常说 Go 语言工具旨在为谷歌服务,而与社区的需求相冲突。然而,Go 语言工具(和其他许多开放的工具)是无法被谷歌所使用的,原因是 bazel 使用了不兼容的文件布局方式。 +* [The go tool](https://golang.org/cmd/go/)[1] is the build tool officially maintained by the Go team. It is versioned and released with the language (and `gc` and the standard library). It expects a directory called `GOROOT` (from an environment variable, with a compiled default) to contain the compiler, the standard library and various other tools. And it expects all source code in a single directory called `GOPATH` (from an environment variable, defaulting to `$HOME/go` or equivalent). Specifically, package `a/b` is expected to have its source at `$GOPATH/src/a/b/c.go` etc. And `$GOPATH/src/a/b` is expected to **only** contain source files of one package. It also has a mechanism to [download a package and its dependencies recursively from an arbitrary server](https://golang.org/cmd/go/#hdr-Remote_import_paths), in a fully decentralized scheme, though it does not support versioning or verification of downloads. The go tool also contains extra tooling for testing Go code, reading documentation ([golang.org](https://golang.org) is served by the Go tool), file bugs, run various tools… +* [gopherjs](https://github.com/gopherjs/gopherjs) comes with its own build tool, that largely mimics the Go tool. +* [gomobile](https://github.com/golang/go/wiki/Mobile) is a build tool specifically to build Go code for mobile operating systems. +* [dep](https://github.com/golang/dep), [gb](https://getgb.io/), [glide](https://glide.sh/),… are community-developed build-tools and dependency managers, each with their own approach to file layout (some are compatible with the go tool, some aren't) and dependency declarations. +* [bazel](https://bazel.build/) is the open source version of Google's own build system. While it's not actually Go-specific, I'm mentioning it explicitly due to common claims that idiosyncrasies of the go tool are intended to serve Google's own use cases, in conflict with the needs of the community. However, the go tool (and many public tools) can't be used at Google, because bazel uses an incompatible file layout. -代码构建工具是大多数用户在编写代码时直接使用的重要工具,因此它很大程度上决定了 **Go 语言生态系统**的方方面面,也决定了包的组合方式,这也将影响 Go 程序员之间的沟通和交流方式。如上所述,Go 语言工具是被隐式引用的(除非指定了其他的运行环境),因此它的设计会让公众对 “Go 语言”的看法造成很大的影响。虽然有许多替代工具可供使用,这些工具也已经在如公司内部使用等场景被广泛使用,但是开源社区**通常**希望 Go 语言工具与 Go 语言的使用方式相契合,这意味着: +The build tool is what most users directly interface with and as such, it's what largely determines aspects of the **Go ecosystem** and how packages can be combined and thus how different Go programmers interact. As above, the go tool is what's implicitly referred to (unless other context is specified) and thus its design decisions significantly influence public opinion about "Go". While there are alternative tools and they have wide adoption for use cases like company-internal code, the open source community **in general** expects code to conform to the expectations of the go tool, which (among other things) means: -* 可以获取源代码。Go 语言工具对包的二进制分发只做了极其有限的支持,并且仅有的支持将会在将来的版本中移除。 -* 要依据 [Go 官方文档编排格式](https://blog.golang.org/godoc-documenting-go-code)来撰写文档。 -* 要[包含测试用例](https://golang.org/pkg/testing/#pkg-overview),并且能通过 `go test` 运行测试。 -* 可以完全通过 `go build` 来编译(与后面所述的特征共同被称为“可以通过 Go 得到的” —— “go-gettable”)。特别指出,如果需要生成源代码或是元编程,则使用 [go generate](https://golang.org/pkg/cmd/go/internal/generate/) 并提交生成的构件。 -* 通过命名空间导入的路径其第一部分是一个域名,该域名可以是一个代码托管服务器或者是该服务器上运行的一个 Web 服务,则 Go 代码可以找到源代码和其依赖,并且可以[正常工作](https://golang.org/cmd/go/#hdr-Remote_import_paths)。 -* 每个目录都只有一个包,并且可以使用[代码构建约束条件](https://golang.org/pkg/go/build/#hdr-Build_Constraints)进行条件编译。 +* Be available as source code. The go tool has little support for binary distribution of packages, and what little it has is going to be removed soon. +* Be documented according to [the godoc format](https://blog.golang.org/godoc-documenting-go-code). +* [Have tests](https://golang.org/pkg/testing/#pkg-overview) that can be run via `go test`. +* Be fully compilable by a `go build` (together with the next one, this is usually called being "go-gettable"). In particular, to use [go generate](https://golang.org/pkg/cmd/go/internal/generate/) if generating source-code or metaprogramming is required and commit the generated artifacts. +* Namespace import paths with a domain-name as the first component and have that domain-name either be a well-known code hoster or have a webserver running on it, so that [go get works](https://golang.org/cmd/go/#hdr-Remote_import_paths) and can find the source code of dependencies. +* Have one package per directory and use [build constraints](https://golang.org/pkg/go/build/#hdr-Build_Constraints) for conditional compilation. -[Go 语言工具的文档](https://golang.org/cmd/go)非常全面,它是一个学习 Go 如何实现各种生态系统的良好起点。 +The [documentation of the go tool](https://golang.org/cmd/go) is very comprehensive and probably a good starting point to learn how Go implements various ecosystem aspects. -#### 其他工具 +#### Tools -Go 语言的标准库包含了[一些可以与 Go 源代码交互的包](https://golang.org/pkg/go/)和[包含了更多功能的 x/tools 扩展库](https://godoc.org/golang.org/x/tools/go)。Go 语言也因此在社区中有非常强的第三方工具开发文化(由于官方强烈地想要保持 Go 语言本身的精简)。这些工具通常需要知道源代码的位置,可能还需要获取类型信息。[go/build](https://golang.org/pkg/go/build/) 包遵循了 Go 语言工具的约定,因此它本身就可以作为其部分构建过程的文档。缺点则是,构建在它之上的工具有时与基于其他构建工具的代码不兼容。因此有[一个新的包正在开发中](https://godoc.org/golang.org/x/tools/go/packages),它可以与其他构建工具很好地集成。 +Go's standard library includes [several packages to interact with Go source code](https://golang.org/pkg/go/) and the [x/tools subrepo contains even more](https://godoc.org/golang.org/x/tools/go). As a result (and due to a strong desire to keep the canonical Go distribution lean), Go has developed a strong culture of developing third-party tools. In general, these tools need to know where to find source code, and might need access to type information. The [go/build](https://golang.org/pkg/go/build/) package implements the conventions used by the Go tool, and can thus also serve as documentation for parts of its build process. The downside is that tools built on top of it sometimes don't work with code relying on other build tools. That's why there is a [new package in development](https://godoc.org/golang.org/x/tools/go/packages) which integrates nicely with other build tools. -实际上 Go 语言的工具有非常多,并且每个人都有自己的偏好。但大致如下: +By its nature the list of Go tools is long and everybody has their own preferences. But broadly, they contain: -* [Go 语言开发团队所研发的工具,与 Go 语言有相同的发布周期](https://golang.org/cmd/)。 -* 它包含[代码自动格式化工具](https://golang.org/cmd/gofmt/)、[测试覆盖率工具](https://golang.org/cmd/cover/)、[运行时追踪工具](https://golang.org/cmd/trace/)、[信息收集工具](https://golang.org/cmd/pprof/)、[针对常见错误的静态分析器](https://golang.org/cmd/vet/)、[一款已经废弃的 Go 代码升级工具](https://golang.org/cmd/fix/)。这些工具都可以通过 `go tool ` 命令来访问。 -* [由 Go 开发团队所维护,但不随 Go 语言一起发布的工具](https://godoc.org/golang.org/x/tools/cmd)。[博客文章编写工具和演示工具](https://godoc.org/golang.org/x/tools/cmd/present)、[大型代码重构工具](https://godoc.org/golang.org/x/tools/cmd/eg)、[导入路径自动修正工具](https://godoc.org/golang.org/x/tools/cmd/goimports)和[语言服务器](https://godoc.org/golang.org/x/tools/cmd/gopls)。 -* 第三方工具 —— 实在太多了。有很多关于第三方工具的列表,例如[这个](https://github.com/avelino/awesome-go#tools)。 +* [Tools developed by the Go team and released as part of the distribution](https://golang.org/cmd/). +* This includes tools for [automatically formatting source code](https://golang.org/cmd/gofmt/), [coverage testing](https://golang.org/cmd/cover/), [runtime tracing](https://golang.org/cmd/trace/) and [profiling](https://golang.org/cmd/pprof/), a [static analyzer for common mistakes](https://golang.org/cmd/vet/) and [a mostly obsolete tool to migrate code to new Go versions](https://golang.org/cmd/fix/). These are generally accesed via `go tool `. +* [Tools developed by the Go team and maintained out-of-tree](https://godoc.org/golang.org/x/tools/cmd). This includes tools to [write blog posts and presentations](https://godoc.org/golang.org/x/tools/cmd/present), [easily do large refactors](https://godoc.org/golang.org/x/tools/cmd/eg), [automatically find and fix import paths](https://godoc.org/golang.org/x/tools/cmd/goimports) and a [language server](https://godoc.org/golang.org/x/tools/cmd/gopls). +* Third-party tools - too many to count. There are many lists of these; [here is one](https://github.com/avelino/awesome-go#tools). -#### 总结 +#### In Summary -我想用一个简短的参考文献列表来结束这篇文章,列表的内容是为那些感到迷茫的初学者准备的。请点击下面的链接: +I wanted to end this with a short list of references for beginners who feel lost. So this is where you should go, if you: -* [开始学习 Go 语言](https://tour.golang.org/welcome/1)。 -* [理解 Go 语言的工作方式](https://golang.org/doc/effective_go.html)。 -* [什么是合法的 Go 代码及其原因](https://golang.org/ref/spec)。 -* [Go 语言工具及其文档](https://golang.org/cmd/go/),也可以通过 `go help` 查看。有时会涉及到其他内容,你也可以查看[这些不够精细的内容](https://golang.org/pkg/cmd/go/internal/help/)。 -* [编写符合社区标准的代码](https://github.com/golang/go/wiki/CodeReviewComments)。 -* [对代码进行测试](https://golang.org/pkg/testing/#pkg-overview)。 -* [寻找新的依赖包或查看公用包的文档](https://godoc.org/)。 +* [Want to start learning Go](https://tour.golang.org/welcome/1). +* [Want to understand how a specific language construct works](https://golang.org/doc/effective_go.html). +* [Want to nitpick what is or is not valid Go and why](https://golang.org/ref/spec). +* [Want documentation about what the go tool does](https://golang.org/cmd/go/) Also available via `go help`. It sometimes references other topics, that you can also [see on the web](https://golang.org/pkg/cmd/go/internal/help/), but not nicely. +* [Want to write code that adheres to community standards](https://github.com/golang/go/wiki/CodeReviewComments). +* [Want to test your code](https://golang.org/pkg/testing/#pkg-overview). +* [Want to find new packages or look at documentation of public packages](https://godoc.org/). -除此以外还有许多有价值的文档可以作为补充,但这些应该已经足够让你有一个良好的开端了。作为一个 Go 语言的初学者,如果你发现本文有任何遗漏之处(我可能会补充更多的细节)或者你找到了任何有价值的参考资料,请[通过 Twitter 联系我](https://twitter.com/TheMerovius)。如果你已经是一个经验丰富的 Go 语言开发者,并且你发现我遗漏了某些重要的内容(但是我有意忽略了一些重要的参考资料,使得初学者们可以感受到 Go 语言学习中的新鲜感:smile:),也请给我留言。 +There are many more useful supplementary documents, but this should serve as a good start. Please [let me know on Twitter](https://twitter.com/TheMerovius) if you are a beginner and there's an area of Go you are missing from this overview (I might follow this up with more specific topics), or a specific reference you found helpful. You can also drop me a note if you're a more experienced Gopher and think I missed something important (but keep in mind that I intentionally left out most references, so as to keep the ways forward crisp and clear :) ). --- -[1] 注:Go 开发团队目前正在对**模块**做一些支持,模块是包之上的代码分发单元,这些支持包括版本控制和一些可以使“传统” Go 语言工具解决问题的基础工作。等这些支持完成以后,这一段中的所有内容基本上就都过时了。对模块的支持**目前**是有的,但还不是 Go 语言的一部分。由于本文的核心内容是对 Go 语言的不同组成部分进行简要介绍,这些内容是不太容易发生变化的,**目前来看**我认为理解这些历史问题也是很有必要的。 +\[1\] Note: The Go team is currently rolling out support for **modules**, which is a unit of code distribution above packages, including support for versioning and more infrastructure to solve some issues with the "traditional" go tool. With that, basically everything in that paragraph becomes obsolete. However, **for now** the module support exists but is opt-in. And as the point of this article is to provide an overview of the separation of concerns, which doesn't actually change, I felt it was better to stay within ye olden days - **for now**. > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 37f3d36cc10d35ac137eee2d1df2d429908ee46a Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 37/58] Revert "Create learn-enough-docker-to-be-useful-1.md (#6206)" This reverts commit 50c15af7acdd1158e05c6fcd1b214e994dc5f0f6. --- TODO1/learn-enough-docker-to-be-useful-1.md | 166 -------------------- 1 file changed, 166 deletions(-) delete mode 100644 TODO1/learn-enough-docker-to-be-useful-1.md diff --git a/TODO1/learn-enough-docker-to-be-useful-1.md b/TODO1/learn-enough-docker-to-be-useful-1.md deleted file mode 100644 index a63f3e3c268..00000000000 --- a/TODO1/learn-enough-docker-to-be-useful-1.md +++ /dev/null @@ -1,166 +0,0 @@ -> * 原文地址:[Learn Enough Docker to be Useful](https://towardsdatascience.com/learn-enough-docker-to-be-useful-b7ba70caeb4b) -> * 原文作者:[Jeff Hale](https://medium.com/@jeffhale) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/learn-enough-docker-to-be-useful-1.md](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-enough-docker-to-be-useful-1.md) -> * 译者: -> * 校对者: - -# Learn Enough Docker to be Useful - -### Part 1: The Conceptual Landscape - -![](https://cdn-images-1.medium.com/max/3840/1*4eXBePb2oLVPxHyocCNmlw.jpeg) - -Containers are hugely helpful for improving security, reproducibility, and scalability in software development and data science. Their rise is one of the most important trends in technology today. - -Docker is a platform to develop, deploy, and run applications inside containers. Docker is essentially synonymous with containerization. If you’re a current or aspiring software developer or data scientist, Docker is in your future. - -![](https://cdn-images-1.medium.com/max/2000/1*EJx9QN4ENSPKZuz51rC39w.png) - -Don’t fret if you aren’t yet up to speed — this article will help you understand the conceptual landscape — and you’ll get to make some pizza along the way. - -In the next five articles in this series we’ll jump into Docker terms, Dockerfiles, Docker images, Docker commands, and data storage. Part 2 is now live: - -* [**Learn Enough Docker to be Useful Part 2: A Delicious Dozen Docker Terms You Need to Know**](https://towardsdatascience.com/learn-enough-docker-to-be-useful-1c40ea269fa8) - -By the end of the series (and with a little practice) you should know enough Docker to be useful 😃! - -## Docker Metaphors - -First, I’m going to shed some light on Docker metaphors. - -![[They’re everywhere! Just check out this book.](https://www.goodreads.com/book/show/34459.Metaphors_We_Live_By)](https://cdn-images-1.medium.com/max/2000/1*poqn_j2R9xTk940n9wE9Lw.jpeg) - -[Google’s second definition for Metaphor](https://www.google.com/search?q=metaphor+definition&oq=metaphor+defini&aqs=chrome.0.0j69i57j0l4.2999j1j4&sourceid=chrome&ie=UTF-8) is what we want: - -> a thing regarded as representative or symbolic of something else, especially something abstract. - -Metaphors help us make sense of new things. For example, the metaphor of a physical container helps us quickly grasp the essence of a virtual container. - -![A physical container](https://cdn-images-1.medium.com/max/2000/1*ndncU4a3uNsQ_oy2YrNLBA.jpeg) - -### Container - -Like a physical plastic container, a Docker container: - -1. **Holds things** — Something is either inside the container or outside the container. - -2. **Is portable** — It can be used on your local machine, your coworker’s machine, or a cloud provider’s servers (e.g. AWS). Sort of like that box of childhood knickknacks you keep moving with you from home to home. - -3. **Has clear interfaces for access** — Our physical container has a lid for opening and putting things in and taking things out. Similarly, a Docker container has several mechanisms for interfacing with the outside world. It has ports that can be opened for interacting through the browser. You can configure it to interact with data through the command line. - -4. **Can be obtained from a remote location** — You can get another empty plastic container from Amazon.com when you need it. Amazon gets its plastic containers from manufacturers who stamp them out by the thousands from a single mold. In the case of a Docker container, an offsite registry keeps an image, which is like a mold, for your container. Then when you need a container you can make one from the image. - -Unlike a virtual Docker container, a new plastic container from Amazon will cost you money and won’t come with a copy of your goods inside. Sorry 💸. - -### Living Instance - -A second way you can think of a Docker container is as **an instance of a living thing**. An instance is something that exists in some form. It’s not just code. It’s code that has brought something to life. Like other living things, the instance will eventually die — meaning the container will shut down. - -![An instance of a monster](https://cdn-images-1.medium.com/max/2000/1*t-uVUfbywQsDnwQoYAEbgA.jpeg) - -A Docker container is a Docker image brought to life. - -### Software - -In addition to the container metaphor and the living instance metaphor, you can think of a Docker container as **a software program**. After all, it is software. At its most basic level a container is a set of instructions that manipulate other bits. - -![Containers are code](https://cdn-images-1.medium.com/max/2000/1*0D45gdLlWgvMBu9Xwr0RrA.jpeg) - -While a Docker container is running, it generally has programs running inside it. The programs in a container perform actions so your application will do something. - -For example, the code in a Docker container might have sent you the content you are reading on this webpage right now. Or it might take your voice command to Amazon Alexa and decode it into instructions another program in a different container will use. - -With Docker you can run multiple containers simultaneously on a host machine. And like other software programs, Docker containers can be run, inspected, stopped, and deleted. - -## Concepts - -### Virtual Machines - -Virtual machines are the precursors to Docker containers. Virtual machines also isolate an application and its dependencies. However, Docker containers are superior to virtual machines because they take fewer resources, are very portable, and are faster to spin up. Check out [this article](https://medium.freecodecamp.org/a-beginner-friendly-introduction-to-containers-vms-and-docker-79a9e3e119b) for a great discussion of the similarities and differences. - -### Docker Image - -I mentioned images above. What’s an image? I’m glad you asked! The meaning of the term **image** in the context of Docker doesn’t map all that well to a physical image. - -![Images](https://cdn-images-1.medium.com/max/2000/1*Wv9nvbm0XRLSGQ9nqTzpdA.jpeg) - -Docker images are more like blueprints, cookie cutters, or molds. Images are the immutable master template that is used to pump out containers that are all exactly alike. - -![Cookie cutters](https://cdn-images-1.medium.com/max/2000/1*n53WlDyD9mxVcOu17Rj86Q.jpeg) - -An image contains the Dockerfile, libraries, and code your application needs to run, all bundled together. - -### Dockerfile - -A [Dockerfile](https://docs.docker.com/engine/reference/builder/) is a file with instructions for how Docker should build your image. - -The Dockerfile refers to a base image that is used to build the initial image layer. Popular official base images include [python](https://hub.docker.com/_/python/), [ubuntu](https://hub.docker.com/_/ubuntu), and [alpine](https://hub.docker.com/_/alpine). - -Additional layers can then be stacked on top of the base image layers, according to the instructions in the Dockerfile. For example, a Dockerfile for a machine learning application could tell Docker to add NumPy, Pandas, and Scikit-learn in an intermediate layer. - -Finally, a thin, writable layer is stacked on top of the other layers according to the Dockerfile code. (You understand that a thin layer is small in size because you intuitively understand the **thin** metaphor, right 😃?) - -I’ll explore Dockerfiles in more depth in future articles in this series. - -### Docker Container - -A Docker image plus the command `docker run image_name` creates and starts a container from an image. - -### Container Registry - -If you want other people to be able to make containers from your image, you send the image to a container registry. [Docker Hub](https://hub.docker.com/) is the largest registry and the default. - -Phew! That’s a lot of pieces. Let’s put this all together in terms of making a pizza. - -## Cooking with Docker - -![Landscape Metaphor](https://cdn-images-1.medium.com/max/2000/1*v6WWacmOsrPYtkGXUu-cbA.jpeg) - -* The recipe is like the **Dockerfile**. It tells you what to do to get to your end goal. - -* The ingredients are the **layers**. You’ve got crust, sauce, and cheese for this pizza. - -Think of the recipe and the ingredients combined as an all-in-one pizza-making-kit. It’s the **Docker image**. - -The recipe (Dockerfile) tells us what we’re going to do. Here’s the plan: - -* The crust is preformed and immutable, it’s like a basic ubuntu parent image. It’s the **bottom layer** and gets built first. - -* Then you’ll add some cheese. Adding this second layer to the pizza is like **installing an external library** — for example NumPy. - -* Then you’ll sprinkle on some basil. The basil is like the **code in a file** that you wrote to run your app. - -Alright, let’s get cooking. - -![Oven](https://cdn-images-1.medium.com/max/2000/1*rihuhM7hCvWaJhuw7Hjvzg.jpeg) - -* The oven that bakes the pizza is like the Docker platform. You installed the oven into your house when you moved in so you could make things with it. Similarly, you installed Docker onto your computer so you could cook up containers. - -* You start your oven by turning a knob. The `docker run image_name` command is like your knob — it creates and starts your container. - -* The cooked pizza is like a Docker container. - -* Eating the pizza is like using your app. - -Like making a pizza, making an app in a Docker container takes some work, but at the end you have something great. Enjoy 🍕! - -## Wrap - -That’s the conceptual framework. In [Part 2 of this series](https://towardsdatascience.com/learn-enough-docker-to-be-useful-1c40ea269fa8) I clarify some of the terms you’ll see in the Docker ecosystem. Follow me to make sure you don’t miss it! - -Hopefully this overview has helped you better understand the Docker landscape. I also hope it has also opened your eyes to the value of metaphors in understanding new technologies. - -If you found this helpful please share it on your favorite social media so other people can find it, too. 👏 - -I write about Python, Docker, data science, and more. If any of that’s of interest to you, read more [here](https://medium.com/@jeffhale) and follow me on Medium. 😄 - -![](https://cdn-images-1.medium.com/max/NaN/1*oPkqiu1rrt-hC_lDMK-jQg.png) - -Thanks for reading! - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 1b7b7fdc7c12dd5ee2affb1d0be933584e858b21 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 38/58] Revert "Create the-open-source-conundrum-how-do-we-keep-the-lights-on.md (#6201)" This reverts commit 207baf16282be4e7d50fa51888e36052821bf12f. --- ...-conundrum-how-do-we-keep-the-lights-on.md | 132 ------------------ 1 file changed, 132 deletions(-) delete mode 100644 TODO1/the-open-source-conundrum-how-do-we-keep-the-lights-on.md diff --git a/TODO1/the-open-source-conundrum-how-do-we-keep-the-lights-on.md b/TODO1/the-open-source-conundrum-how-do-we-keep-the-lights-on.md deleted file mode 100644 index 391ecc54d7d..00000000000 --- a/TODO1/the-open-source-conundrum-how-do-we-keep-the-lights-on.md +++ /dev/null @@ -1,132 +0,0 @@ -> * 原文地址:[The Open Source Conundrum: How Do We Keep the Lights On?](https://codefund.io/blog/the-open-source-conundrum-how-do-we-keep-the-lights-on) -> * 原文作者:[Eric Berry](https://codefund.io/author/eric-berry) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-open-source-conundrum-how-do-we-keep-the-lights-on.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-open-source-conundrum-how-do-we-keep-the-lights-on.md) -> * 译者: -> * 校对者: - -# The Open Source Conundrum: How Do We Keep the Lights On? - -## Thoughts behind Open Source funding need to change if open source is to remain sustainable well into the future. - -Open source software (OSS) can be defined using a myriad of criteria in today's complex development ecosystem. In this article, it’s defined as those programs that enable royalty-free distribution, the release of the source code, and the requirement for all modifications to be distributed under the same terms as the original software license. With the appropriate context established, let’s explore the challenges facing OSS. - -Since the beginning of the Internet, OSS has contributed tremendous value to the tech community. By sharing source code with the broader development community, applications are typically pushed through the [software development lifecycle](https://stackify.com/what-is-sdlc/) (SDLC) faster and at less cost. This source code also acts as a launching pad for countless other projects that are spared the time and expense of starting from scratch. - -However, the profitability of OSS is exponentially [lower than proprietary software development](https://www.forbes.com/sites/forbestechcouncil/2018/02/22/why-pay-for-something-when-its-free/#6335810a3169). This situation is ironic given that [96% of applications have open source components](https://www.synopsys.com/content/dam/synopsys/sig-assets/reports/2018-ossra.pdf). In addition, the code base for those applications contains 57% open source code on average. These numbers suggest there is a blatant disconnect in the ecosystem. While end users capture immense value, those in the trenches continue to struggle. - -**But what is OSS worth?** Based on an estimate calculated by Black Duck more than 6 years ago, it’s likely that the combined global value of open source software amounts to significantly more than $387 billion today. - -![undefined](https://cdn.buttercms.com/ZnftGc3OS4qxkF9Aa9My) - -Source: [2018 Open Source Security and Risk Analysis](https://www.synopsys.com/content/dam/synopsys/sig-assets/reports/2018-ossra.pdf) - -#### The Motivation Behind Open Source Software - -You may be wondering why any developer would choose to work in OSS when proprietary development is far more profitable. **So, what exactly motivates developers to continue working on open source projects?** Unlike extrinsic motivation that is typically driven by the fear of consequence and the desire for reward, [OSS is pushed forward by intrinsic factors](https://www.zdnet.com/article/its-an-open-source-world-78-percent-of-companies-run-open-source-software/). - -Open source developers are often **personally invested** in pushing a project forward because it’s a hobby, they have fun doing it and enjoy learning, or they want to create something for the good of all. This altruistic approach puts people before profits and is in complete contrast to the profitability pursued by proprietary applications. - -#### Roles in the Open Source Ecosystem - -For those altruistic developers that continue to support open source initiatives, there are defined roles that typically characterize participants. - -##### Contributors - -Contributors can be described as those that give back to a program or application in some capacity. Their contributions might include bug fixes, building features, enhancing documentation, and fixing typos. - -##### Maintainers - -Those that fall under this category are participants that drive the vision of a project and manage its organizational components. Tasks may include performing bug triage, reviewing pull requests (PR), and directing the overall project. This work is an ongoing obligation that is often unappreciated by the greater community. - -What most don’t realize is that 65% of all OSS repositories [generate a Truck Factor of 2](https://medium.com/@aserg.ufmg/what-is-the-truck-factor-of-github-projects-bb0d5f019a6f). A Truck Factor reflects the number of developers that must leave (get hit by a truck, win the lottery, etc.) before a project becomes unsustainable. This description paints a precarious picture of the open source ecosystem and its future viability. Semantic UI is only one example of a project that is no longer sustainable, its sole developer stating: - -> **“After having spent ~ 3 years of my life trying to make OS work with part-time proprietary work, or just plain being broke. I don’t think I know any other way that seems reasonable without compromising the software. Unfortunately, it means I have to push back development until I can find the means of financing to sustain it.”** - -#### Using Money as an Incentive for Open Source - -While OSS may appear “free,” to the greater community, maintainers and contributors bear the brunt of user demands with minimal resources. To bring attention to this concerning trend, a group of 100 people gathered in San Francisco last year in hopes of changing the way we think about the sustainability of open source software. - -In their resulting report, this group reiterated how a small group of individuals supports critical pieces of OSS with no financial support or contractual obligation. As a result, the goodwill of a few can no longer sustain the increasing demands of the greater OS ecosystem. **But what is the answer to this dilemma?** It might seem simple, but the concept is a relatively new one. - -**Use money as an incentive for open source.** - -And that’s precisely what companies like [Gitcoin](https://gitcoin.co/) and [CodeFund](https://codesponsor.io/) are trying to do. While their strategies differ, the end goal is the same - exploring how to better #fundl open source development. - -#### Incentivizing Open Source Software - -When it comes to incentivizing OSS development, there are several mechanisms to explore. While some have been tried before, others are more progressive. Each has its pros and cons, and all aim to better #fundl open source. - -![oss-conundrum-2.png](https://cdn.buttercms.com/E89ikPXhRTu5MktD3FHx) - -#### Donations - -Donations are quite straight-forward; developers can ask for money from others to fund projects. Donations encompass grants, sponsorships, donation buttons, and the establishment of foundations. While donations present a low barrier of entry and allow developers to focus on code, there are considerable drawbacks to this approach. - -For instance, without consistent active fundraising, there is likely to be a drop off in donations. Also, without a broad audience, developers may not be able to attract enough attention to garner substantial funding. - -![oss-conundrum-3.png](https://cdn.buttercms.com/tOHkRk9QnmUIwUpKNHNw) - -Support - -Developers can sell time, training material, and merchandise using this method. When funding projects using this approach, programs can offer end users books, paid training, merchandise, and even consulting services. This method can be leveraged as a useful marketing tool while also giving developers a clear understanding of end-user needs throughout the project. - -However, smaller OSS projects may not benefit from this funding method due to their inability to support a large user base. Also, paid training is rarely in demand and this heavily customer-centric approach can take valuable time away from coding. - -![oss-conundrum-4.png](https://cdn.buttercms.com/6Vwd374mQJGUzxRO1uGB) - -License and Usage - -Projects can sell licenses, features, or paid hosting using this model. Developers might seek out venture capital, copyleft, open core, SaaS, and restricted license opportunities under this approach. The scalability of this funding model is promising if successful and has the potential to provide a full-time income. - -While this is a promising option, once again small OSS projects may not benefit due to their limited user base and exposure. This approach can also become extremely time consuming and requires a strong entrepreneurial mindset, making it a challenging option for some developers. - -![oss-conundrum-5.png](https://cdn.buttercms.com/9mMl8dvcRgGh8kapTWtg) - -#### Areas of Overlap in Open Source Funding - -Within the same framework, there are evident areas of overlap. These are the areas that CodeFund and Gitcoin have begun to exploit. - -![oss-conundrum-6.png](https://cdn.buttercms.com/UnAIRsBATRKuaMTwhmpZ) - -Crowdfunding - -Crowdfunding involves asking for one-time or recurring donations. In using this funding model, there are very few strings attached. It’s also easy to administer through the use of several platforms designed for this specific purpose - Patreon, Liberapay, Open Collective, and Flattr to name a few. - -While appealing, recurring crowdfunding is hard to achieve as many don’t want to commit. Crowdfunding also generates fewer funds than other options and is typically not suitable for small OSS projects with less exposure. - -![oss-conundrum-7.png](https://cdn.buttercms.com/d9bDglWoTm67NXQhTHEB) - -Bounties - -Bounties provide a financial incentive to those willing to contribute to open source. Bounty programs are highly inclusive and leverage a truly global talent pool. Also, the monetary reward is directly linked to a specific task or required project metric. This transparent process can often lead to full-time employment for those developers taking part. Gitcoin is one example of a successful bounty platform that delivers these benefits. - -And while the benefits are significant, there are some additional considerations. Bounties typically provide developers with minimal reward funds depending on the project and are usually not recurring. Bug bounty programs might also require developers to possess specialized security skills to work on a job. - -![oss-conundrum-8.png](https://cdn.buttercms.com/Bfrcc7j7SROcksfKis12) - -Advertising - -Advertising provides the most stable source of passive, recurring income out of all funding methods. However, there are several factors to consider when choosing to employ this funding method. - -Most importantly, a broad audience is required to ensure advertising is justifiable. Developers that use advertising must also consider the potential for lost trust and ethical concerns regarding marketing efforts. Conflict of interest is also possible should the advertisements displayed have any connection to the developer's project. - -In response to these challenges, CodeFund is working to eliminate the ethical concerns associated with developer advertising. In building an ethical advertising platform, CodeFund aims to help developers earn passive income in a trusted, reputable environment. - -![oss-conundrum-8.png](https://cdn.buttercms.com/Bfrcc7j7SROcksfKis12) - -#### Changing the Trajectory of Open Source - -Open source software has long been lauded as the birthplace of countless high profile applications. However, while proprietary software continues to generate billions, OSS continues to languish with little to no funding. This reality needs to change if open source is to remain sustainable well into the future - this is critical. - -Moving forward, this small community of OSS maintainers and contributors needs to be robust and well-funded. If we fail to acknowledge the tremendous burden placed on those carrying the open source community, we’ll all suffer the consequences. Promising projects will be canceled, talented developers will flock to proprietary development, and the ecosystem that once generated immense innovative will cease to be. - -But not all is lost, we can do our part to keep the lights on. While developers must take action to fund their projects, we as consumers must shift our cultural perspective of OSS. Although open source may appear to be free, countless hours of human effort and burnt-out developers most certainly suggest otherwise. - -[Find out](https://codefund.io/publishers) if CodeFund can help fund your blog, application, or documentation website! - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From e4d29db902545dca676f7a8bb8fea5180591dd5a Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 39/58] Revert "Create how-data-sharding-works-in-a-distributed-sql-database.md (#6200)" This reverts commit b2d305f24a7e7921a4933f24993b4e0f8d360244. --- ...ing-works-in-a-distributed-sql-database.md | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 TODO1/how-data-sharding-works-in-a-distributed-sql-database.md diff --git a/TODO1/how-data-sharding-works-in-a-distributed-sql-database.md b/TODO1/how-data-sharding-works-in-a-distributed-sql-database.md deleted file mode 100644 index 2e836f274ec..00000000000 --- a/TODO1/how-data-sharding-works-in-a-distributed-sql-database.md +++ /dev/null @@ -1,108 +0,0 @@ -> * 原文地址:[How Data Sharding Works in a Distributed SQL Database](https://blog.yugabyte.com/how-data-sharding-works-in-a-distributed-sql-database/) -> * 原文作者:[Sid Choudhury](https://blog.yugabyte.com/author/sidchoudhury/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-data-sharding-works-in-a-distributed-sql-database.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-data-sharding-works-in-a-distributed-sql-database.md) -> * 译者: -> * 校对者: - -# How Data Sharding Works in a Distributed SQL Database - -Enterprises of all sizes are embracing rapid modernization of user-facing applications as part of their broader digital transformation strategy. The relational database (RDBMS) infrastructure that such applications rely on suddenly needs to support much larger data sizes and transaction volumes. However, a monolithic RDBMS tends to quickly get overloaded in such scenarios. One of the most common architectures to get more performance and scalability in an RDBMS is to “shard” the data. In this blog, we will learn what sharding is and how it can be used to scale a database. We will also review the pros and cons of common sharding architectures, plus explore how sharding is implemented in distributed SQL-based RDBMS like [YugaByte DB.](https://github.com/YugaByte/yugabyte-db) - -## What is Data Sharding? - -Sharding is the process of breaking up large tables into smaller chunks called **shards** that are spread across multiple servers. A **shard** is essentially a horizontal data partition that contains a subset of the total data set, and hence is responsible for serving a portion of the overall workload. The idea is to distribute data that can’t fit on a single node onto a **cluster** of database nodes. Sharding is also referred to as **horizontal partitioning**. The distinction between horizontal and vertical comes from the traditional tabular view of a database. A database can be split vertically — storing different table columns in a separate database, or horizontally — storing rows of the same table in multiple database nodes. - -![](https://3lr6t13cowm230cj0q42yphj-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/data-sharding-distributed-sql-1.png) - -**Figure 1 : Vertical and Horizontal Data Partitioning (Source: Medium)** - -## Why Shard a Database? - -Business applications that rely on monolithic RDBMS hit bottlenecks as they grow. With limited CPU, storage capacity and memory, database performance is bound to suffer. Query performance and routine maintenance of an unsharded database becomes extremely slow. When it comes to adding resources to support database operations, vertical scaling (aka scaling up) has its own set of limits and eventually reaches a point of diminishing returns. - -On the other hand, horizontally partitioning a table means more compute capacity to serve incoming queries, and therefore you end up with faster query response times and index builds. By continuously balancing the load and data set over additional nodes, sharding also allows easy expansion to accommodate more capacity. Moreover, a network of smaller, cheaper servers may be more cost effective in the long term than maintaining one big server. - -Besides resolving scaling challenges, sharding can potentially alleviate the impact of unplanned outages. During downtime, all the data in an unsharded database is inaccessible, which can be disruptive or downright disastrous. When done right, sharding can provide high availability: even if one or two nodes hosting a few shards are down, the rest of the database is still available for read/write operations as long as the other nodes (hosting the remaining shards) run in different failure domains. Overall, sharding can increase total cluster storage capacity, speed up processing, and offer higher availability at a lower cost than vertical scaling. - -## The Perils of Manual Sharding - -Sharding, including the day-1 creation and day-2 rebalancing, when completely automated can be a boon to high-volume data apps. Unfortunately, monolithic databases like Oracle, PostgreSQL, MySQL and even newer distributed SQL databases like Amazon Aurora do not support sharding automatically. This means manual sharding at the application layer if you want to continue to use these databases. The net result is a massive increase in development complexity. Your application has to have additional sharding logic to know exactly how your data is distributed, and how to fetch it. You also have to decide what sharding approach to adopt, how many shards to create, and how many nodes to use. And also account for shard key as well as even sharding approach changes if your business needs change. - -One of the most significant challenges with manual sharding is uneven shard allocation. Disproportionate distribution of data could cause shards to become unbalanced, with some overloaded while others remain relatively empty. It’s best to avoid accruing too much data on a shard, because a hotspot can lead to slowdowns and server crashes. This problem could also arise from a small shard set, which forces data to be spread across too few shards. This is acceptable in development and testing environments, but not in production. Uneven data distribution, hotspots, and storing data on too few shards can all cause shard and server resource exhaustion. - -Finally, manual sharding can complicate operational processes. Backups will now have to be performed for multiple servers. Data migration and schema changes must be carefully coordinated to ensure all shards have the same schema copy. Without sufficient optimization, database joins across multiple servers could highly inefficient and difficult to perform. - -## Common Auto-Sharding Architectures - -Sharding has been around for a long time, and over the years different sharding architectures and implementations have been used to build large scale systems. In this section, we will go over the three most common ones. - -### Hash-based Sharding - -Hash-based sharding takes a shard key’s value and generates a hash value from it. The hash value is then used to determine in which shard the data should reside. With a uniform hashing algorithm such as ketama, the hash function can evenly distribute data across servers, reducing the risk of hotspots. With this approach, data with close shard keys are unlikely to be placed on the same shard. This architecture is thus great for targeted data operations. - -![](https://3lr6t13cowm230cj0q42yphj-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/data-sharding-distributed-sql-2.png) - -**Figure 2: Hash-based sharding (Source: MongoDB Docs)** - -### Range-based Sharding - -Range-based sharding divides data based on ranges of the data value (aka the keyspace). Shard keys with nearby values are more likely to fall into the same range and onto the same shards. Each shard essentially preserves the same schema from the original database. Sharding becomes as easy as identifying the data’s appropriate range and placing it on the corresponding shard. - -![](https://3lr6t13cowm230cj0q42yphj-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/Sharding-Image-copy.jpg) - -**Figure 3 : Range-based sharding example** - -Range-based sharding allows for efficient queries that reads target data within a contiguous range or range queries. However, range-based sharding needs the user to apriori choose the shard keys, and poorly chosen shard keys could result in database hotspots. - -A good rule-of-thumb is to pick shard keys that have large cardinality, low recurring frequency, and that do not increase, or decrease, monotonically. Without proper shard key selections, data could be unevenly distributed across shards, and specific data could be queried more compared to the others, creating potential system bottlenecks in the shards that get a heavier workload. - -The ideal solution to uneven shard sizes is to perform automatic shard splitting and merging. If the shard becomes to big or hosts a frequently accessed row, then breaking the shard into multiple shards and then rebalancing them across all the available nodes leads to better performance. Similarly, the opposite process can be undertaken when there are too many small shards. - -### Geo-based Sharding - -In geo-based (aka location-aware) sharding, data is partitioned according to a user-specified column that maps range shards to specific regions and the nodes in those regions. For example, a cluster that runs across 3 regions in the US, UK and the EU can rely on the Country_Code column of the User table to map the user’s row to the nearest region that is in conformance with GDPR rules. - -## Sharding in YugaByte DB - -YugaByte DB is an auto-sharded, ultra-resilient, high-performance, geo-distributed SQL database built with inspiration from Google Spanner. It currently supports hash-based sharding by default. Range-based sharding is an active work-in-progress project while geo-based sharding is on the roadmap for later this year. Each data shard is called a tablet, and it resides on a corresponding tablet server. - -### Hash-based Sharding - -For hash-based sharding, tables are allocated a hash space between 0x0000 to 0xFFFF (the 2-byte range), accommodating as many as 64K tablets in very large data sets or cluster sizes. Consider a table with 16 tablets as shown in Figure 4. We take the overall hash space \[0x0000 to 0xFFFF), and divide it into 16 segments — one for each tablet. - -![](https://3lr6t13cowm230cj0q42yphj-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/data-sharding-distributed-sql-4.png) - -**Figure 4: Hash-based sharding in YugaByte DB** - -In read/write operations, the primary keys are first converted into internal keys and their corresponding hash values. The operation is served by collecting data from the appropriate tablets. (Figure 3) - -![](https://3lr6t13cowm230cj0q42yphj-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/data-sharding-distributed-sql-5.png) - -**Figure 5: Figuring out which tablet to use in Yugabyte DB** - -As an example, suppose you want to insert a key k, with a value v into a table as shown in Figure 6, the hash value of k is computed, and then the corresponding tablet is looked up, followed by the relevant tablet server. The request is then sent directly to that tablet server for processing. - -![](https://3lr6t13cowm230cj0q42yphj-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/data-sharding-distributed-sql-6.png) - -**Figure 6 : Storing value of k in YugaByte DB** - -### Range-based Sharding - -SQL tables can be created with ASC and DESC directives for the first column of a primary key as well as first of the indexed columns. This will lead to the data getting stored in the chosen order on a single shard (aka tablet). Work is in progress to dynamically [split the tablets](https://github.com/YugaByte/yugabyte-db/issues/1004) (based on various criteria such as range boundary and load) as well as enhance the [SQL syntax](https://github.com/YugaByte/yugabyte-db/issues/1486) to specify the exact ranges. - -## Summary - -Data sharding is a solution for business applications with large data sets and scale needs. There are a variety of sharding architectures to choose from, each of which provides different capabilities. Before settling on a sharding architecture, the needs and workload requirements of your app must be mapped out. Manual sharding should be avoided in most circumstances given significant increase in application logic complexity. [YugaByte DB](https://github.com/YugaByte/yugabyte-db) is an auto-sharded distributed SQL database with support for hash-based sharding today and support for range-based/geo-based sharding coming soon. You can see YugaByte DB’s automatic sharding in action in this [tutorial.](https://docs.yugabyte.com/latest/explore/auto-sharding/) - -## What’s Next? - -* [Compare](https://docs.yugabyte.com/latest/comparisons/) YugaByte DB in depth to databases like [CockroachDB](https://www.yugabyte.com/yugabyte-db-vs-cockroachdb/), Google Cloud Spanner and MongoDB. -* [Get started](https://docs.yugabyte.com/latest/quick-start/) with YugaByte DB on macOS, Linux, Docker, and Kubernetes. -* [Contact us](https://www.yugabyte.com/about/contact/) to learn more about licensing, pricing or to schedule a technical overview. - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From bf489af5fdc5bcbab2a03cc44d038178469c860e Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 40/58] Revert "Create reverse-engineering-how-you-can-build-a-test-library.md (#6199)" This reverts commit 98c90f4301b0819aaf499cf5951d2009cdcde30a. --- ...eering-how-you-can-build-a-test-library.md | 468 ------------------ 1 file changed, 468 deletions(-) delete mode 100644 TODO1/reverse-engineering-how-you-can-build-a-test-library.md diff --git a/TODO1/reverse-engineering-how-you-can-build-a-test-library.md b/TODO1/reverse-engineering-how-you-can-build-a-test-library.md deleted file mode 100644 index a252d025641..00000000000 --- a/TODO1/reverse-engineering-how-you-can-build-a-test-library.md +++ /dev/null @@ -1,468 +0,0 @@ -> * 原文地址:[Reverse Engineering, how YOU can build a testing library in JavaScript](https://dev.to/itnext/reverse-engineering-how-you-can-build-a-test-library-53e3) -> * 原文作者:[Chris Noring](https://dev.to/softchris) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/reverse-engineering-how-you-can-build-a-test-library.md](https://github.com/xitu/gold-miner/blob/master/TODO1/reverse-engineering-how-you-can-build-a-test-library.md) -> * 译者: -> * 校对者: - -# Reverse Engineering, how YOU can build a testing library in JavaScript - -I know what you are thinking. Building my own testing library with so many out there?? Hear me out. This article is about being able to do **reverse engineering** and understand what might go on under the hood. Why? Simply to gain more understanding and a deeper appreciation of the libraries you use. - -Just to make it clear. I'm not about to implement a test library fully, just have a look at the public API and understand roughly what's going on and start implementing it. By doing so I hope to gain some understanding of the overall architecture, both how to line it out but also how to extend it and also appreciate what parts are tricky vs easy. - -I hope you enjoy the ride :) - -We will cover the following: - -* **The WHY**, try to explain all the benefits to reverse engineering -* **The WHAT**, what we will build and not build -* **Constructing**, slowly take you through the steps of building it out - -## WHY - -Many years ago, in the beginning of my career as a software developer, I asked a senior developer how they got better. It wasn't just one answer but one thing stood out, namely **reverse engineering** or rather recreating libraries or frameworks they were using or were curious about. - -> Sounds to me like you are trying to reinvent the wheel. What's good about that, don't we have enough libraries that do the same thing already? - -Of course, there is merit to this argument. Don't build things primarily cause you don't like the exact flavoring of a library, unless you reeeeally need to, sometimes you do need to though. - -> So when? - -When it's about trying to become better at your profession. - -> Sounds vague - -Well, yes it partly is. There are many ways to become better. I'm of the opinion that to truly understand something it's not enough to just use it - **you need to build it**. - -> What, all of it? - -Depends on the size of the library or framework. Some are small enough that it's worth building all of it. Most are not though. There is a lot of value in trying to implement something though, a lot can be understood by just starting **if only to get stuck**. That's what this exercise is, to try to understand more. - -## [](#the-what)The WHAT - -We mentioned building a testing library in the beginning. What testing library? Well, let's have a look at how most testing libraries look like in JavaScript. They tend to look like this: - -```js -describe('suite', () => { - it('should be true', () => { - expect(2 > 1).toBe(true) - }) -}) -``` - -This is the scope of what we will be building, getting the above to work and in the process comment on the architecture and maybe throw in a library to make it pretty :) - -Let's get started. - -## Constructing - -Ok then. **If you build it they will come**. - -> Sure? - -You know, the movie Field of Dreams? - -> Whatever grandpa **bored** - -### Expect, assert our values - -Let's begin from our most inner statement, the `expect()` function. By looking at an invocation we can learn a lot: - -```js -expect(2 > 1).toBe(true) -``` - -`expect()` looks like a function taking a `boolean`. It seems to be returning an object that has a method `toBe()` on it that additionally is able to compare the value in `expect()` by what `toBe()` is fed with. Let's try to sketch this: - -```js -function expect(actual) { - return { - toBe(expected) { - if(actual === expected){ - /* do something*/ - } else { - /* do something else*/ - } - } - } -} -``` - -Additionally, we should consider that this should produce some kind of statement if the matching is a success or if it's a failure. So some more code is needed: - -```js -function expect(actual) { - return { - toBe(expected) { - if(expected === actual){ - console.log(`Succeeded`) - } else { - console.log(`Fail - Actual: ${val}, Expected: ${expected}`) - } - } - } -} - -expect(true).toBe(true) // Succeeded -expect(3).toBe(2) // Fail - Actual: 3, Expected: 2 -``` - -Note, how the `else` statement has a bit more specialized message and gives us a hint on what failed. - -Methods like this comparing two values to each other like `toBe()` are called `matchers`. Let's try to add another matcher `toBeTruthy()`. The reason is that the term **truthy** matches a lot of values in JavaScript and we would rather not have to use the `toBe()` matcher for everything. - -> So we are being lazy? - -YES, best reason there is :) - -The rules for this one is that anything considered truthy in JavaScript should succeed and anything else should render in failure. Let's cheat a bit by going to MDN and see what's considered **truthy**: - -```js -if (true) -if ({}) -if ([]) -if (42) -if ("0") -if ("false") -if (new Date()) -if (-42) -if (12n) -if (3.14) -if (-3.14) -if (Infinity) -if (-Infinity) -``` - -Ok, so everything within an `if` statement that evaluates to `true`. Time to add said method: - -```js -function expect(actual) { - return { - toBe(expected) { - if(expected === actual){ - console.log(`Succeeded`) - } else { - console.log(`Fail - Actual: ${val}, Expected: ${expected}`) - } - }, - toBeTruthy() { - if(actual) { - console.log(`Succeeded`) - } else { - console.log(`Fail - Expected value to be truthy but got ${actual}`) - } - } - } -} - -expect(true).toBe(true) // Succeeded -expect(3).toBe(2) // Fail - Actual: 3, Expected: 2 -expect('abc').toBeTruthy(); -``` - -I don't know about you, but I feel like my `expect()` function is starting to contain a lot of things. So let's move out our `matchers` to a `Matchers` class, like so: - -```js -class Matchers { - constructor(actual) { - this.actual = actual; - } - - toBe(expected) { - if(expected === this.actual){ - console.log(`Succeeded`) - } else { - console.log(`Fail - Actual: ${this.actual}, Expected: ${expected}`) - } - } - - toBeTruthy() { - if(this.actual) { - console.log(`Succeeded`) - } else { - console.log(`Fail - Expected value to be truthy but got ${this.actual}`) - } - } -} - -function expect(actual) { - return new Matchers(actual); -} -``` - -### it, our test method - -Looking at our vision it should be working like so: - -```js -it('test method', () => { - expect(3).toBe(2) -}) -``` - -Ok, reverse engineering this bit we can pretty much write our `it()` method: - -```js -function it(testName, fn) { - console.log(`test: ${testName}`); - fn(); -} -``` - -Ok, let's stop here a bit and think. What kind of behavior do we want? I've definitely seen unit testing libraries that quits running the tests if something fails. I guess if you have 200 unit tests (not that you should have 200 tests in one file :), you don't want to wait for them to finish, better to tell me directly what's wrong so I can fix it. For the latter to be possible we need to adjust our matchers a little: - -```js -class Matchers { - constructor(actual) { - this.actual = actual; - } - - toBe(expected) { - if(expected === actual){ - console.log(`Succeeded`) - } else { - throw new Error(`Fail - Actual: ${val}, Expected: ${expected}`) - } - } - - toBeTruthy() { - if(actual) { - console.log(`Succeeded`) - } else { - console.log(`Fail - Expected value to be truthy but got ${actual}`) - throw new Error(`Fail - Expected value to be truthy but got ${actual}`) - } - } -} -``` - -This means that our `it()` function needs to capture any erros like so: - -```js -function it(testName, fn) { - console.log(`test: ${testName}`); - try { - fn(); - } catch(err) { - console.log(err); - throw new Error('test run failed'); - } - -} -``` - -As you can see above we not only capture the error and logs it but we rethrow it to put an end to the run itself. Again, main reason was that we saw no point in continuing. You can implement this the way you see fit. - -### Describe, our test suite - -Ok, we covered writing `it()` and `expect()` and even threw in a couple of matcher functions. All testing libraries should have a suite concept though, something that says this is a group of tests that belong together. - -Let's look at what the code can look like: - -```js -describe('our suite', () => { - it('should fail 2 != 1', () => { - expect(2).toBe(1); - }) - - it('should succeed', () => { // technically it wouldn't get here, it would crash out after the first test - expect('abc').toBeTruthy(); - }) -}) -``` - -As for the implementation, we know that tests that fail throws errors so we need to capture that to not crash the whole program: - -```js -function describe(suiteName, fn) { - try { - console.log(`suite: ${suiteName}`); - fn(); - } catch(err) { - console.log(err.message); - } -} -``` - -### Running the code - -At this point our full code should look like this: - -```js -// app.js - -class Matchers { - constructor(actual) { - this.actual = actual; - } - - toBe(expected) { - if (expected === this.actual) { - console.log(`Succeeded`) - } else { - throw new Error(`Fail - Actual: ${this.actual}, Expected: ${expected}`) - } - } - - toBeTruthy() { - if (actual) { - console.log(`Succeeded`) - } else { - console.log(`Fail - Expected value to be truthy but got ${this.actual}`) - throw new Error(`Fail - Expected value to be truthy but got ${this.actual}`) - } - } -} - -function expect(actual) { - return new Matchers(actual); -} - -function describe(suiteName, fn) { - try { - console.log(`suite: ${suiteName}`); - fn(); - } catch(err) { - console.log(err.message); - } -} - -function it(testName, fn) { - console.log(`test: ${testName}`); - try { - fn(); - } catch (err) { - console.log(err); - throw new Error('test run failed'); - } -} - -describe('a suite', () => { - it('a test that will fail', () => { - expect(true).toBe(false); - }) - - it('a test that will never run', () => { - expect(1).toBe(1); - }) -}) - -describe('another suite', () => { - it('should succeed, true === true', () => { - expect(true).toBe(true); - }) - - it('should succeed, 1 === 1', () => { - expect(1).toBe(1); - }) -}) -``` - -and when run in the terminal with `node app.js`, should render like so: - -[![](https://res.cloudinary.com/practicaldev/image/fetch/s--AU3RQVD8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y3hmyys7hsph5gbg16bb.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--AU3RQVD8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y3hmyys7hsph5gbg16bb.png) - -## Making it pretty - -Now the above seems to be working but it looks **sooo** boring. So what can we do about it? Colors, plenty of colors will make this better. Using the library `chalk` we can really induce some life into this: - -```js -npm install chalk --save -``` - -Ok, next let's add some colors and some tabs and spaces and our code should look like so: - -```js -const chalk = require('chalk'); - -class Matchers { - constructor(actual) { - this.actual = actual; - } - - toBe(expected) { - if (expected === this.actual) { - console.log(chalk.greenBright(` Succeeded`)) - } else { - throw new Error(`Fail - Actual: ${this.actual}, Expected: ${expected}`) - } - } - - toBeTruthy() { - if (actual) { - console.log(chalk.greenBright(` Succeeded`)) - } else { - throw new Error(`Fail - Expected value to be truthy but got ${this.actual}`) - } - } -} - -function expect(actual) { - return new Matchers(actual); -} - -function describe(suiteName, fn) { - try { - console.log('\n'); - console.log(`suite: ${chalk.green(suiteName)}`); - fn(); - } catch (err) { - console.log(chalk.redBright(`[${err.message.toUpperCase()}]`)); - } -} - -function it(testName, fn) { - console.log(` test: ${chalk.yellow(testName)}`); - try { - fn(); - } catch (err) { - console.log(` ${chalk.redBright(err)}`); - throw new Error('test run failed'); - } -} - -describe('a suite', () => { - it('a test that will fail', () => { - expect(true).toBe(false); - }) - - it('a test that will never run', () => { - expect(1).toBe(1); - }) -}) - -describe('another suite', () => { - it('should succeed, true === true', () => { - expect(true).toBe(true); - }) - - it('should succeed, 1 === 1', () => { - expect(1).toBe(1); - }) -}) -``` - -and render like so, when run: - -[![](https://res.cloudinary.com/practicaldev/image/fetch/s--Gt0KQDcz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nusgojpo4vmi22r8q7zx.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--Gt0KQDcz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nusgojpo4vmi22r8q7zx.png) - -## Summary - -We aimed at looking at a fairly small library like a unit testing library. By looking at the code we could deduce what it might look like underneath. - -We created something, a starting point. Having said that we need to realize that most unit testing libraries come with a lot of other things as well like, handling asynchronous tests, multiple test suites, mocking, spies a ton more `matchers` and so on. There is a lot to be gained by trying to understand what you use on a daily basis but please realize that you don't have to completely reinvent it to gain a lot of insight. - -My hope is that you can use this code as a starting point and maybe play around with it, start from the beginning or extend, the choice is yours. - -Another outcome of this might be that you understand enough to help out with OSS and improve one of the existing libraries out there. - -Remember, if you build they will come: - -[![](https://res.cloudinary.com/practicaldev/image/fetch/s--YY1Wgcm0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vndsyrcrelnklmbamhhy.jpg)](https://res.cloudinary.com/practicaldev/image/fetch/s--YY1Wgcm0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vndsyrcrelnklmbamhhy.jpg) - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From b19f953cc3bf1055f74ec61dea67289d0aedbccd Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 41/58] Revert "Create is-postmessage-slow.md (#6198)" This reverts commit 14bc3034167e4489c055deb53cbad92c79f90429. --- TODO1/is-postmessage-slow.md | 261 ----------------------------------- 1 file changed, 261 deletions(-) delete mode 100644 TODO1/is-postmessage-slow.md diff --git a/TODO1/is-postmessage-slow.md b/TODO1/is-postmessage-slow.md deleted file mode 100644 index 62d5f0cb500..00000000000 --- a/TODO1/is-postmessage-slow.md +++ /dev/null @@ -1,261 +0,0 @@ -> * 原文地址:[Is postMessage slow?](https://dassur.ma/things/is-postmessage-slow/) -> * 原文作者:[Surma](https://dassur.ma) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/is-postmessage-slow.md](https://github.com/xitu/gold-miner/blob/master/TODO1/is-postmessage-slow.md) -> * 译者: -> * 校对者: - -# Is postMessage slow? - -No, not really. (It depends.) - -What does “slow” mean? [I said it before](/things/less-snakeoil/), and I will say it again: If you didn’t measure it, it is not slow, and even if you measure it, the numbers are meaningless without context. - -That being said, the fact that people will not even consider adopting [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker) because of their concerns about the performance of `postMessage()`, means that this is worth investigating. [My last blog post](/things/when-workers/) on workers got [responses](https://twitter.com/dfabu/status/1139567716052930561) along these lines, too. Let’s put actual numbers to the performance of `postMessage()` and see at what point you risk blowing your budgets. What can you do if vanilla `postMessage()` is too slow for your use-case? - -Ready? Go. - -## How postMessage works - -Before we start measuring, we need to understand **what** `postMessage()` is and which part of it we want to measure. Otherwise, [we’ll end up gathering meaningless data](/things/deep-copy/) and drawing meaningless conclusions. - -`postMessage()` is part of the [HTML spec](https://html.spec.whatwg.org/multipage/) (not [ECMA-262](http://www.ecma-international.org/ecma-262/10.0/index.html#Title)!). As I mentioned in my [deep-copy post](/things/deep-copy/), `postMessage()` relies on structured cloning to copy the message from one JavaScript realm to another. Taking a closer look at [the specification of `postMessage()`](https://html.spec.whatwg.org/multipage/web-messaging.html#message-port-post-message-steps), it turns out that structured cloning is a two-step process: - -### Structured Clone algorithm - -1. Run `StructuredSerialize()` on the message -2. Queue a task in the receiving realm, that will execute the following steps: - 1. Run `StructuredDeserialize()` on the serialized message - 2. Create a `MessageEvent` and dispatch a `MessageEvent` with the deserialized message on the receiving port - -This is a simplified version of the algorithm so we can focus on the parts that matter for this blog post. It’s **technically** incorrect, but catches the spirit, if you will. For example, `StructuredSerialize()` and `StructuredDeserialize()` are not real functions in the sense that they are not exposed via JavaScript ([yet](https://github.com/whatwg/html/pull/3414)). What do these two functions actually do? For now, **you can think of `StructuredSerialize()` and `StructuredDeserialize()` as smarter versions of `JSON.stringify()` and `JSON.parse()`**, respectively. They are smarter in the sense that they handle cyclical data structures, built-in data types like `Map`, `Set` and `ArrayBuffer` etc. But do these smarts come at a cost? We’ll get back to that later. - -Something that the algorithm above doesn’t spell out explicitly is the fact that **serialization blocks the sending realm, while deserialization blocks the receiving realm.** And there’s more: It turns out that both Chrome and Safari defer running `StructuredDeserialize()` until you actually access the `.data` property on the `MessageEvent`. Firefox on the other hand deserializes before dispatching the event. - -> **Note:** Both of these behaviors **are** spec-compatible and perfectly valid. [I opened a bug with Mozilla](https://bugzilla.mozilla.org/show_bug.cgi?id=1564880), asking if they are willing to align their implementation, as it puts the developer in control when to take the “performance hit” of deserializing big payloads. - -With that in mind, we have to make a choice **what** to benchmark: We could measure end-to-end, so measuring how much time it takes to send a message from a worker to the main thread. However, that number would capture the sum of serialization and deserialization, each of which are happening in different realms. Remember: **This whole spiel with workers is motivated by wanting to keep the main thread free and responsive.** Alternatively, we could limit the benchmarks to Chrome and Safari and measure how long it takes to access the `.data` property to measure `StructuredDeserialize()` in isolation, which would exclude Firefox from the benchmark. I also haven’t found a way to measure `StructuredSerialize()` in isolation, short of running a trace. Neither of these choices are ideal, but in the spirit of building resilient web apps, **I decided to run the end-to-end benchmark to provide an **upper bound** for the cost of `postMessage()`.** - -Armed with a conceptual understanding of `postMessage()` and the determination to measure, I shall use microbenchmarks. Please mind the gap between these numbers and reality. - -## Benchmark 1: How long does sending a message take? - -![Two JSON objects showing depth and breadth](https://dassur.ma/things/is-postmessage-slow/breadth-depth.svg) - -Depth and breadth vary between 1 and 6. For each permutation, 1000 objects will be generated. - -The benchmark will generate an object with a specific “breadth” and “depth”. The values for breadth and depth lie between 1 and 6. **For each combination of breadth and depth, 1000 unique objects will be `postMessage()`’d from a worker to the main thread**. The property names of these objects are random 16-digit hexadecimal numbers as a string, the values are either a random boolean, a random float or again a random string in the from of a 16-digit hexadecimal number. **The benchmark will measure the transfer time and calculate the 95th percentile.** - -### Results - -![](https://dassur.ma/things/is-postmessage-slow/nokia2-chrome.svg) - -![](https://dassur.ma/things/is-postmessage-slow/pixel3-chrome.svg) - -![](https://dassur.ma/things/is-postmessage-slow/macbook-chrome.svg) - -![](https://dassur.ma/things/is-postmessage-slow/macbook-firefox.svg) - -![](https://dassur.ma/things/is-postmessage-slow/macbook-safari.svg) - -The benchmark was run on Firefox, Safari and Chrome on a 2018 MacBook Pro, on Chrome on a Pixel 3XL and on Chrome on a Nokia 2. - -> **Note:** You can find the benchmark data, to code to generate it and the code for the visualization in [this gist](https://gist.github.com/surma/08923b78c42fab88065461f9f507ee96). Also, this was the first time in my life writing Python. Don’t be too harsh on me. - -The benchmark data from the Pixel 3 and especially Safari might look a bit suspicious to you. When Spectre & Meltdown were discovered, all browsers disabled [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) and reduced the precision of timers like [`performance.now()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now), which I use to measure. Only Chrome was able to revert some these changes since they shipped [Site Isolation](https://www.chromium.org/Home/chromium-security/site-isolation) to Chrome on desktop. More concretely that means that browsers clamp the precision of `performance.now()` to the following values: - -* Chrome (desktop): 5µs -* Chrome (Android): 100µs -* Firefox (desktop): 1ms (clamping can be disabled flag, which I did) -* Safari (desktop): 1ms - -The data shows that the complexity of the object is a strong factor in how long it takes to serialize and deserialize an object. This should not be surprising: Both the serialization and deserialization process have to traverse the entire object one way or another. The data indicates furthermore that the size of the JSON representation of an object is a good predictor for how long it takes to transfer that object. - -## Benchmark 2: What makes postMessage slow? - -To verify, I modified the benchmark: I generate all permutations of breadth and depth between 1 and 6, but in addition all leaf properties have a string value with a length between 16 bytes and 2KiB. - -### Results - -![A graph showing the correlation between payload size and transfer time for postMessage](https://dassur.ma/things/is-postmessage-slow/correlation.svg) - -Transfer time has a strong correlation with the length of the string returned by `JSON.stringify()`. - -I think the correlation is strong enough to issue a rule of thumb: **The stringified JSON representation of an object is roughly proportional to its transfer time.** However, even more important to note is the fact that **this correlation only becomes relevant for big objects**, and by big I mean anything over 100KiB. While the correlation holds mathematically, the divergence is much more visible at smaller payloads. - -## Evaluation: It’s about sending a message - -We have data, but it’s meaningless if we don’t contextualize it. If we want to draw **meaningful** conclusions, we need to define “slow”. Budgets are a helpful tool here, and I will once again go back to the [RAIL](https://developers.google.com/web/fundamentals/performance/rail) guidelines to establish our budgets. - -In my experience, a web worker’s core responsibility is, at the very least, managing your app’s state object. State often only changes when the user interacts with your app. According to RAIL, we have 100ms to react to user interactions, which means that **even on the slowest devices, you can `postMessage()` objects up to 100KiB and stay within your budget.** - -This changes when you have JS-driven animations running. The RAIL budget for animations is 16ms, as the visuals need to get updated every frame. If we send a message from the worker that would block the main thread for longer than that, we are in trouble. Looking at the numbers from our benchmarks, everything up to 10KiB will not pose a risk to your animation budget. That being said, **this is a strong reason to prefer CSS animations and transitions over main-thread JS-driven animations.** CSS animations and transitions runs on a separate thread — the compositor thread — and are not affected by a blocked main thread. - -## Must. send. moar. data. - -In my experience, `postMessage()` is not the bottleneck for most apps that are adopting an off-main-thread architecture. I will admit, however, that there might be setups where your messages are either really big or you need to send a lot of them at a high frequency. What can you do if vanilla `postMessage()` is too slow for you? - -### Patching - -In the case of state objects, the objects themselves can be quite big, but it’s often only a handful of deeply nested properties that change. We encountered this problem in [PROXX](https://proxx.app), our PWA Minesweeper clone: The game state consists of a 2-dimensional array for the game grid. Each cell stores whether it’s a mine, if it’s been revealed or if it’s been flagged: - -```typescript -interface Cell { - hasMine: boolean; - flagged: boolean; - revealed: boolean; - touchingMines: number; - touchingFlags: number; -} -``` - -That means the biggest possible grid of 40 by 40 cells adds up to ~134KiB of JSON. Sending an entire state object is out of the question. **Instead of sending the entire new state object whenever something changes, we chose to record the changes and send a patchset instead.** While we didn’t use [ImmerJS](https://github.com/immerjs/immer), a library for working with immutable objects, it does provide a quick way to generate and apply such patchsets: - -```js -// worker.js -immer.produce(stateObject, draftState => { - // Manipulate `draftState` here -}, patches => { - postMessage(patches); -}); - -// main.js -worker.addEventListener("message", ({data}) => { - state = immer.applyPatches(state, data); - // React to new state -} -``` - -The patches that ImmerJS generates look like this: - -```json -[ - { - "op": "remove", - "path": [ "socials", "gplus" ] - }, - { - "op": "add", - "path": [ "socials", "twitter" ], - "value": "@DasSurma" - }, - { - "op": "replace", - "path": [ "name" ], - "value": "Surma" - } -] -``` - -This means that the amount that needs to get transferred is proportional to the size of your changes, not the size of the object. - -### Chunking - -As I said, for state objects it’s **often** only a handful of properties that change. But not always. In fact, [PROXX](https://proxx.app) has a scenario where patchsets could turn out quite big: The first reveal can affect up to 80% of the game field, which adds up to a patchset of about ~70KiB. When targeting feature phones, that is too much, especially as we might have JS-driven WebGL animations running. - -We asked ourselves an architectural question: Can our app support partial updates? Patchsets are a collection of patches. **Instead of sending all patches in the patchset at once, you can “chunk” the patchset into smaller partitions and apply them sequentially.** Send patches 1-10 in the first message, 11-20 on the next, and so on. If you take this to the extreme, you are effectively **streaming** your patches, allowing you to use all the patterns you might know and love from reactive programming. - -Of course, this can result in incomplete or even broken visuals if you don’t pay attention. However, you are in control of how the chunking happens and could reorder the patches to avoid any undesired effects. For example, you could make sure that the first chunk contains all patches affecting on-screen elements, and put the remaining patches in to a couple of patchsets to give the main thread room to breathe. - -We do chunking in [PROXX](https://proxx.app). When the user taps a field, the worker iterates over the entire grid to figure out which fields need to be updated and collects them in a list. If that list grows over a certain threshold, we send what we have so far to the main thread, empty the list and continue iterating the game field. These patchsets are small enough that even on a feature phone the cost of `postMessage()` is negligible and we still have enough main thread budget time to update our game’s UI. The iteration algorithm works from the first tile outwards, meaning our patches are ordered in the same fashion. If the main thread can only fit one message into its frame budget (like on the Nokia 8110), the partial updates disguise as a reveal animation. If we are on a powerful machine, the main thread will keep processing message events until it runs out of budget just by nature JavaScript’s event loop. - -视频链接:https://dassur.ma/things/is-postmessage-slow/proxx-reveal.mp4 - -Classic sleight of hand: In [PROXX], the chunking of the patchset looks like an animation. This is especially visible on low-end mobile phones or on desktop with 6x CPU throttling enabled. - -### Maybe JSON? - -`JSON.parse()` and `JSON.stringify()` are incredibly fast. JSON is a small subset of JavaScript, so the parser has fewer cases to handle. Because of their frequent usage, they have also been heavily optimized. [Mathias recently pointed out](https://twitter.com/mathias/status/1143551692732030979), that you can sometimes reduce parse time of your JavaScript by wrapping big objects into `JSON.parse()`. **Maybe we can use JSON to speed up `postMessage()` as well? Sadly, the answer seems to be no:** - -![A graph comparing the duration of sending an object to serializing, sending, and deserializing an object.](https://dassur.ma/things/is-postmessage-slow/serialize.svg) - -Comparing the performance of manual JSON serialization to vanilla `postMessage()` yields no clear result. - -While there is no clear winner, vanilla `postMessage()` seems to perform better in the best case, and equally bad in the worst case. - -### Binary formats - -Another way to deal with the performance impact of structured cloning is to not use it at all. Apart from structured cloning objects, `postMessage()` can also **transfer** certain types. `ArrayBuffer` is one of these [transferable](https://developer.mozilla.org/en-US/docs/Web/API/Transferable) types. As the name implies, transferring an `ArrayBuffer` does not involve copying. The sending realm actually loses access to the buffer and it is now owned by the receiving realm. **Transferring an `ArrayBuffer` is extremely fast and independent of the size of the `ArrayBuffer`**. The downside is that `ArrayBuffer` are just a continuous chunk of memory. We are not working with objects and properties anymore. For an `ArrayBuffer` to be useful we have to decide how our data is marshalled ourselves. This in itself has a cost, but by knowing the shape or structure of our data at build time we can potentially tap into many optimizations that are unavailable to a generic cloning algorithm. - -One format that allows you to tap into these optimizations are [FlatBuffers](https://google.github.io/flatbuffers/). FlatBuffers have compilers for JavaScript (and other languages) that turn schema descriptions into code. That code contains functions to serialize and deserialize your data. Even more interestingly: FlatBuffers don’t need to parse (or “unpack”) the entire `ArrayBuffer` to return a value it contains. - -### WebAssembly - -What about everyone’s favorite: WebAssembly? One approach is to use WebAssembly to look at serialization libraries in the ecosystems of other languages. [CBOR](https://cbor.io), a JSON-inspired binary object format, has been implemented in many languages. [ProtoBuffers](https://developers.google.com/protocol-buffers/) and the aforementioned [FlatBuffers](https://google.github.io/flatbuffers/) have wide language support as well. - -However, we can be more cheeky here: We can rely on the memory layout of the language as our serialization format. I wrote [a little example](./binary-state-rust) using [Rust](https://www.rust-lang.org): It defines a `State` struct (symbolic for whatever your app’s state looks like) with some getter and setter methods so I can inspect and manipulate the state from JavaScript. To “serialize” the state object, I just copy the chunk of memory occupied by the struct. To deserialize, I allocate a new `State` object, and overwrite it with the data passed to the deserialization function. Since I’m using the same WebAssembly module in both cases, the memory layout will be identical. - -> This is just a proof-of-concept. You can easily tap into undefined behavior if your struct contains pointers (like `Vec` and `String` do). There’s also some unnecessary copying going on. Code responsibly! - -```rust -pub struct State { - counters: [u8; NUM_COUNTERS] -} - -#[wasm_bindgen] -impl State { - // Constructors, getters and setter... - - pub fn serialize(&self) -> Vec { - let size = size_of::(); - let mut r = Vec::with_capacity(size); - r.resize(size, 0); - unsafe { - std::ptr::copy_nonoverlapping( - self as *const State as *const u8, - r.as_mut_ptr(), - size - ); - }; - r - } -} - -#[wasm_bindgen] -pub fn deserialize(vec: Vec) -> Option { - let size = size_of::(); - if vec.len() != size { - return None; - } - - let mut s = State::new(); - unsafe { - std::ptr::copy_nonoverlapping( - vec.as_ptr(), - &mut s as *mut State as *mut u8, - size - ); - } - Some(s) -} -``` - -> **Note:** [Ingvar](https://twitter.com/rreverser) pointed me to [Abomonation](https://github.com/TimelyDataflow/abomonation), a seriously questionable serialization library that works even **with** pointers. His advice: “Do \[not\] try this!”. - -The WebAssembly module ends up at about 3KiB gzip’d, most of which stems from memory management and some core library functions. The entire state object is sent whenever something changes, but due to the transferability of `ArrayBuffers`, this is extremely cheap. In other words: **This technique should have near-constant transfer time, regardless of state size.** It will, however, be more costly to access state data. There’s always a tradeoff! - -This technique also requires that the state struct does not make any use of indirection like pointers, as those values will become invalid when copied to a new WebAssembly module instance. As a result, you will probably struggle to use this approach with higher-level languages. My recommendations are C, Rust and AssemblyScript, as you are in full control and have sufficient insight into memory layout. - -### SABs & WebAssembly - -> **Heads up:** This section works with `SharedArrayBuffer`, which have been disabled in all browsers except Chrome on desktop. This is being worked on, but no ETA can be given on this. - -Especially from game developers, I have heard multiple requests to give JavaScript the capability to share objects across multiple threads. I think this is unlikely to ever be added to JavaScript itself, as it breaks one of the fundamentals assumptions of JavaScript engines. However, there is an exception to this called [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) (“SABs”). SABs behave exactly like `ArrayBuffers`, but instead of one realm losing access when being transferred , they can be cloned and **both** realms will have access to the same underlying chunk of memory. **SABs allows the JavaScript realms to adopt a shared memory model.** For synchronization between realms, there’s [`Atomics`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics) which provide Mutexes and atomic operations. - -With SABs, you’d only have to transfer a chunk of memory once at the start of your app. However, in addition to the binary representation problem, you’d have to use `Atomics` to prevent one realm from reading the state object while the other realm is still writing and vice-versa. This can have a considerable performance impact. - -As an alternative to using SABs and serializing/deserializing data manually, you could embrace **threaded** WebAssembly. WebAssembly has standardized support for threads, but is gated on the availability of SABs. **With threaded WebAssembly you can write code with the exact same patterns you are used to from threaded programming languages.** This, of course, comes at the cost of development complexity, orchestration and potentially bigger and monolithic modules that need to get shipped. - -## Conclusion - -Here’s my verdict: Even on the slowest devices, you can `postMessage()` objects up to 100KiB and stay within your 100ms response budget. If you have JS-driven animations, payloads up to 10KiB are risk-free. This should be sufficient for most apps. **`postMessage()` does have a cost, but not the extent that it makes off-main-thread architectures unviable.** - -If your payloads are bigger than this, you can try sending patches or switching to a binary format. **Considering state layout, transferability and patchability as an architectural decision from the start can help your app run on a wider spectrum of devices.** If you feel like a shared memory model is your best bet, WebAssembly will pave that way for you in the near future. - -As I already hinted at in [an older blog post](/things/actormodel/) about the Actor Model, I strongly believe we can implement performant off-main-thread architectures on the web **today**, but this requires us leaving our comfort zone of threaded languages and the web’s all-on-main-by-default. We need to explore alternative architectures and models that **embrace** the constraints of the Web and JavaScript. The benefits are worth it. - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From dd5bc82192085486e8c62b6affdb541fbd973434 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 42/58] =?UTF-8?q?Revert=20"=E5=96=B7=E6=B3=89=E7=A0=81?= =?UTF-8?q?=E5=92=8C=E5=8A=A8=E6=80=81=E4=BA=8C=E7=BB=B4=E7=A0=81=20(#6166?= =?UTF-8?q?)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6f7f099fd1ec4ca556c799ffbe21f352fbf08c94. --- TODO1/fountaincodes.md | 100 ++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/TODO1/fountaincodes.md b/TODO1/fountaincodes.md index e2cfb48c1ee..5002fa9006a 100644 --- a/TODO1/fountaincodes.md +++ b/TODO1/fountaincodes.md @@ -2,80 +2,80 @@ > * 原文作者:[Ivan Daniluk](https://github.com/divan) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/fountaincodes.md](https://github.com/xitu/gold-miner/blob/master/TODO1/fountaincodes.md) -> * 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) -> * 校对者:[40m41h42t](https://github.com/40m41h42t),[Ultrasteve](https://github.com/Ultrasteve) +> * 译者: +> * 校对者: -# 喷泉码和动态二维码 +# Fountain codes and animated QR ![fountain](https://divan.dev/images/fountain.jpg) -(图像来源:[Anders Sune Berg](https://olafureliasson.net/archive/artwork/WEK110140/waterfall)) +(source: [Anders Sune Berg](https://olafureliasson.net/archive/artwork/WEK110140/waterfall)) -在[前一篇文章](https://divan.dev/posts/animatedqr/)中,我讲解了一个我在周末完成的项目:[txqr](https://github.com/divan/txqr),它使用了动态二维码序列,可以用于单向的状态传输。最简单直接的方法就是不停重复的编码数据序列,直到接收者获取到了完整的数据。这样简单的重复代码足够初学者用于起步学习,并且很容易执行,但方案还同时引入一定的延迟来防止接收者遗漏任何一帧的信息,在实际应用过程中,错失信息的情况经常出现。 +In the [previous article](https://divan.dev/posts/animatedqr/) I’ve described a weekend project called [txqr](https://github.com/divan/txqr) for unidirectional data transfer using animated sequence of QR codes. The straightforward approach was to repeat the encoded sequence over and over until the receiver gets complete data. This simple repetition code was good enough for starter and trivial to implement, but also introduced long delays in case the receiver missed at least one frame. And in practice, it often did, of course. -对于如何解决以上这种在有噪信道中传输数据的问题,已经有十分完整的理论研究,那就是编码理论。 +This sort of problems of sending data over a noisy communication channel is quite well studied, and there is a whole theory exists to deal solve it – coding theory. -在前一篇文章的评论中,[Bojtos Kiskutya](https://disqus.com/by/bojtoskiskutya/) 提到了 LT 码,它可以让 **txqr** 得出更佳结果。这正是我乐意看到的评论 —— 不仅是优化的建议,同时也让我能发现一些新的有趣的内容。由于我从没有接触过 LT 编码,在接下来的几天内我尽我所能的学习了相关的内容。 +In the comments to the previous article, [Bojtos Kiskutya](https://disqus.com/by/bojtoskiskutya/) mentioned LT codes that can yield much better results for **txqr**. That was exactly the type of comments I expect – not only suggestion for improvement, but also discovering some new and exciting stuff. And as I have never heard about LT codes before, next couple of days I’ve spent reading everything I could find about them. -于是我知道了,[LT codes](https://en.wikipedia.org/wiki/Luby_transform_code)(**LT** 是 卢比变换(**L**uby **T**ransform)的简写)是一个更大的编码方式:[喷泉码](https://en.wikipedia.org/wiki/Fountain_code)的一种实现方式。它是[纠删码](https://en.wikipedia.org/wiki/Erasure_codes)中的一类,它可以从源信息块(K 个)中产生无限数量的数据块,并且它接收比 K 个编码块稍多的信息就足以正确解码信息。接收者可以从任意位置开始接收数据块,也可以按任意顺序接收,并可以设置任意的擦除概率 —— 当你接收到 K 个以上不同的数据块,喷泉码就可以开始工作。这实际上就是“喷泉”这个名字的由来 —— 我们将装满水桶这个行为比作接收信息,喷泉喷出水滴这个行为比作发送一系列编码块,换句话说,你可以在不知晓你当前接收到的是哪一个水滴的情况下,装满你的水桶。 +It turned out, [LT codes](https://en.wikipedia.org/wiki/Luby_transform_code) (**LT** stands for **L**uby **T**ransform) are just one of the implementations of the broader family of codes called [fountain codes](https://en.wikipedia.org/wiki/Fountain_code). It’s a class of [erasure codes](https://en.wikipedia.org/wiki/Erasure_codes) that can produce a potentially infinite amount of blocks from the source message blocks (K), and it’s enough to receive slightly more than K encoded blocks to decode the message successfully. The receiver can start receiving blocks from any point, receive blocks in any order, with any erasure probability – fountain codes will work as soon as you received K+ different blocks. That’s where name “fountain” comes from, actually – fountain’s water drops represent encoded blocks, and you just have to fill the bucket with enough drops, without actually caring which drops exactly you get. -将它用于我的项目简直再合适不过了,所以我快速的搜索了基于 Go 的实现方式:[google/gofountain](https://github.com/google/gofountain),并将我之前的初级版重复编码的代码替换成了卢比变换的实现。代码替换后的测试结果非常优秀,于是在这篇文章中,我将会分享一些 LT 算法的细节,以及使用 **gofountain** 包容易犯错的地方,最后我还会给出两种代码最终测试结果的对比。 +That was a perfect match for my case, so I quickly found Go based implementation [google/gofountain](https://github.com/google/gofountain), and replaced my naive repetition code with Luby transform code implementation. Results were quite impressive, and in this post, I’ll share details about LT algorithm, gotchas in using **gofountain** package and the final results with comparison with original ones. -# 喷泉码牛逼! +# Fountain codes are damn cool -如果你和我一样,还从未听说过喷泉码,也不用担心 —— 因为喷泉码还属于比较新的技术,目前只能解决一小部分很专业的问题。但是喷泉码其实非常酷。它完美的结合了随机性、数学逻辑以及概率分布,从而达成了它的最终目的。 +If you, like myself, have never heard about fountain codes before, don’t worry – they are relatively new and solve very narrow and specific problem. They’re also extremely cool. They beautifully harness properties of randomness, mathematical logic and clever probability distribution tuning to achieve their goal. -虽然我主要介绍 LT 编码,但是在这个编码系统中其实还有很多其他算法 —— 比如 [Online codes](https://en.wikipedia.org/wiki/Online_codes)、[Tornado codes](https://en.wikipedia.org/wiki/Tornado_codes)、[Raptor codes](https://en.wikipedia.org/wiki/Raptor_code#Legal_complexity) 等等,这其中 Raptor codes 在除了合法性之外的几乎所有方面都更胜一筹。但是它们似乎都受到严格的专利保护,所以并未得到广泛的应用。 +While I’m going to describe mostly LT codes, there are many other algorithms in this family – [Online codes](https://en.wikipedia.org/wiki/Online_codes), [Tornado codes](https://en.wikipedia.org/wiki/Tornado_codes), [Raptor codes](https://en.wikipedia.org/wiki/Raptor_code#Legal_complexity), etc., with Raptor codes being superior in almost every aspect, excluding a legal one. They seem to be heavily covered by patents and not widely implemented. -LT 编码的原理相对简单 —— 编码器将信息分割为多个**源信息块**,然后持续的创建**编码块**,这些编码块包含了 1 个或 2 个源信息块,或者更随机的选择**源信息块**并将所有被选择的源信息块作异或操作,得到一个输出。用于创建每个新的**编码块**的 ID 被随机的保存在其中。 +So the idea behind LT codes is relatively simple – encoder splits message into **source blocks** , and continuously creates **encoded blocks** that consist of 1, 2 or more randomly selected **source blocks** XOR-ed with each other. Block IDs used to construct each new **encoded block** are stored in it in an arbitrary way. ![lt encoder](https://divan.dev/images/ltcodes.gif) -在这一轮计算中,编码器会收集所有的**编码块**(就像喷泉中的水珠)—— 它们有的仅包含一个**源信息块**,有的包含两个或者更多 —— 然后将它们和已经解码的块做异或操作来解码还原成新的信息块。 +Decoder, in its turn, collects all the **encoded blocks** (like a drops from a fountain)  – some consisting from just 1 **source block** , some from 2 or more – and tries to restore new blocks by XORing them back with already decoded blocks. -所以,当解码器接收到了仅由一个**源信息块**组成的**编码块** —— 它就将它添加到解码块队列中,不需要其他操作。而如果它接收到了使用两个**源信息块**异或组成的编码快,解码器会检查它们传输时附带的 ID,如果其中一个已经在解码队列中了 —— 那么根据异或操作的性质,恢复这个编码快也就非常简单了。解码两个以上**源信息块**组成的**编码块**也同理 —— 一旦你能获取到一个解码块 —— 只需要继续做异或操作就可以了。 +So when decoder sees the **encoded block** with just 1 **source block** – it does nothing else than adding it to the list of decoded blocks. Once it catches the block with 2 XOR-ed **source blocks** , it checks the IDs, and if one of them is already in the list of decoded blocks – easily restore it due to the property of XOR. The same applies to decoding **encoded blocks** with more than 2 **source blocks** – once you have all but one blocks decoded – just XOR it. -### 孤子分布 +### Solition distribution -最酷的地方在于如何选择多少编码块仅由一个**源信息块**编码而来,以及多少是用两个或更多**源信息块**编码而来。如果有太多的单源信息块编码包,你可能会损失需要的冗余度。而如果太多的多源信息块编码包 —— 那么在一个有噪信道获取单源信息块会花费过多的时间。因此 Luby 编码的命名者,[Michael Luby](https://en.wikipedia.org/wiki/Michael_Luby) 称[孤子分布](https://en.wikipedia.org/wiki/Soliton_distribution)几乎是解决这个问题最完美的分布方式,它能保证你得到足够多的单源信息块编码包,同时也有**很多**的双源信息块编码包,它还有一个很长的尾数,可用于多源信息块编码包直到 N 源信息块编码包,其中 N 是**源信息块**的数量。 +The coolness lays in choosing how to decide how many blocks should be encoded with just 1 **source block** , with 2 or more. Too many single block packets and you lose the redundancy needed. Too many multiple-blocks packets – and it’ll take too much time to get single blocks in a very noisy channel. That’s what [Michael Luby](https://en.wikipedia.org/wiki/Michael_Luby), after which Luby codes are named, calls the [Solition Distribution](https://en.wikipedia.org/wiki/Soliton_distribution) \- almost perfect distribution, which ensures that you have enough single block packets, definitely have **a lot** of 2-blocks packets and also a long tail of many-blocks packets up to N-block ones, where N is the total amount of **source blocks** . ![solition distribution](https://divan.dev/images/solition.png) -这是对分布头部数据的更清晰的展示: +Here is a closer look at the head of the distribution: ![solition distribution zoom](https://divan.dev/images/solition_zoom.png) -你可以看到,这里有一些非零数量的单源信息编码包,其中双源信息编码包占据了分布总量的很大一部分(精确地来说是一半),余下的数量被递减的分布在多源信息编码包中,一个块中包含的源信息块数量越多,这样的编码块就越少。 +You can see here some non-zero amount of single block packets, then 2-blocks packets take a fair share of all distribution (it’s exactly a half to be precise), and the rest is distributed in descending way between the packets with a higher number of XOR-ed blocks. -所有这些特性,让 LT 编码具有了不依赖于发送频率或模式通信信道丢包率的特性。 +All that give LT codes a lovely property of not depending on how often or in which pattern communication channel loses packets. -对于我的 txqr 项目这就意味着,无论使用何种编码和传输参数,使用喷泉码都能够减少平均编码时间。 +For my txqr project, it means that fountain code should give on average much lower decoding time with any given encoding/transmission parameter. -# google 的 gofountain +# google/gofountain -谷歌研发的 gofountain 包使用 Go 语言实现了几个喷泉编码,其中包括 Luby 变换码。它的 [API 都很轻量](https://godoc.org/github.com/google/gofountain)(对于库来说,这是一个好兆头)—— 基本只包含了 `Codec` 接口以及一些实现代码、`EncodeLTBlocks()` 函数,和一些作为伪随机生成器的帮助函数。 +The google’s package gofountain implements several fountain codes in Go, including Luby transform code. It has [tiny API](https://godoc.org/github.com/google/gofountain) (which is a good sign for the library) – basically just `Codec` interface with a few implementations and `EncodeLTBlocks()` function, plus a few pseudorandom generators helpers. -但是,在试图理解 `EncodeLTBlocks()` 的第二个参数是什么意义的时候,我有些迷惑了: +However, I stuck while trying to understand what does the second parameter of `EncodeLTBlocks()` is supposed to mean: ``` func EncodeLTBlocks(message []byte, encodedBlockIDs []int64, c Codec) []LTBlock ``` -为什么我需要将数据块 ID 提供给编码器,我甚至不希望关注数据块的其他属性,因为实现算法应该是库本身而不是使用库用户需要关注的问题。所以最开始我猜测只需传输所有数据块 ID —— `1..N`。 +Why would I want to provide block IDs to the encoder, I don’t even want to worry about blocks’ properties, as it’s an algorithm implementation and not the library user’s concern. So my first guess was just to pass all block IDs – `1..N`. -我猜测的和事实很接近 —— 测试的调试输出编码块正如我想要的,但解码过程却总不能正确的执行。 +It was close enough – debug output from tests produced blocks that looked like what I expected, but decoding had never been finishing. -我查看了 [gofountain 的文档页](https://godoc.org/github.com/google/gofountain),想看看还有什么其他包使用了它,结果发现了一个开源的用于在有损网络环境下传输大型文件的库 —— [pump](https://github.com/sudhirj/pump),其作者是 [Sudhir Jonathan](https://github.com/sudhirj),于是我决定借助一下友好的 Gopher 社区的力量,并试着在 Gopher slack 上联系了 Sudhir,询问他是否能帮助我弄明白这些 ID 的用途。 +I checked [GoDoc page for gofountain](https://godoc.org/github.com/google/gofountain) to see what other packages use it, and found only one open-source library for transmitting large files over lossy networks - [pump](https://github.com/sudhirj/pump) by [Sudhir Jonathan](https://github.com/sudhirj), I decided to leverage the power of friendly Gophers community and contacted Sudhir in Gophers slack, asking if he could help me to clarify those IDs usage. -后来我成功的联系到了 Sudhir,他给了我很缜密的答案并解除了我所有的疑惑,这对我帮助非常大。使用这个库正确的方式是将数据块 ID 以递增的顺序连续的发送 —— 例如,`1..N`、`N..2N`、`2N..3N` 等等。因为一般情况下,我们并不知道信道的噪声级别,所以总要生成新的数据块,这是非常重要的。 +And that worked, Sudhir was extremely helpful and gave me an elaborate answer, which clarified all my doubts. The right way to use this library was to pass incremental IDs for blocks continuously – for example, `1..N`, `N..2N`, `2N..3N`, etc. As generally we don’t know how noisy the channel is, it’s important to generate new blocks all the time. -所以这些 ID 正确的用途应该是循环生成 ID 块,并在一个循环中调用 `EncodeLTBlocks` 函数。但是为了实现这个功能,我必须确保二维码编码速度足够快,能在运行中及时生成新的数据块。对于每秒 15 帧的速率,编码下一个数据块以及生成新的二维码的总时间应小于 1/15 秒,也就是 66ms。很明显这是可行的,但是需要仔细地进行基准测试并优化,以保证对于浏览器上的单核 GopherJS-transpiled 版本也满足这个条件。 +So proper usage would be to generate chunks of IDs, and call `EncodeLTBlocks` in a loop. But in order to achieve that, I had to ensure that QR encoding speed is good enough to generate new blocks on the fly. For 15 frame per second rate, the total time for encoding next block and generating a new QR code should be less than 1s/15 = 66ms. Which is obviously doable, but would require careful benchmarking and optimizing to ensure this is true for GopherJS-transpiled version on single core in the browser. -另外,目前还有一些设计方面的限制 —— `txqr.Encode()` API 期望能返回一个具体的数字,它表示了将有多少个块会被编码为二维码帧,还有 `txqr-tester` 会生成动态 GIF 文件,确保在浏览器运行时帧率的可靠性,所以我决定现在还是不要打破 API 的限制,使用有冗余因子的方法。 +Plus, there were current design limitations – `txqr.Encode()` API expects to return a concrete number of chunks to be encoded as QR frames, plus `txqr-tester` generate animated GIF file upfront to ensure reliable framerate when displayed in the browser, so, I decided not to break API for now and went with redundancy factor approach. -冗余因子方法基于假设:在我的项目中,噪音多少是可以预测的 —— 跳帧不会多于 20%。我们可以生成 `N*redundancyFactor` 个帧,然后像循环代码方法那样做循环,在常规案例中,这是个次优的方案,但是对于我的项目需求和受掌控外部条件,这已经足够了。所以关于 `encodedBlockIDs` 参数,我是用了一个简单的帮助函数: +Redundancy factor approach is based on the assumption that in my case noise estimations is more or less predictable – I’d say no more than 20% of skipped frames. We can generate `N*redundancyFactor` frames and just loop it over as in repetitive codes approach. This approach is sub-optimal in general case, but for my task and controlled environment it was good enough. So for `encodedBlockIDs` parameter, I use a simple helper function: ``` -// ids 函数使用 0..n 中的值生成多个 ID 切片 +// ids create slice with IDs for 0..n values. func ids(n int) []int64 { ids := make([]int64, n) for i := int64(0); i < int64(n); i++ { @@ -85,7 +85,7 @@ func ids(n int) []int64 { } ``` -通过如下方式调用: +and call it via: ``` codec := fountain.NewLubyCodec(N, rand.New(fountain.NewMersenneTwister(200)), solitonDistribution(N)) @@ -94,49 +94,49 @@ func ids(n int) []int64 { lubyBlocks := fountain.EncodeLTBlocks(msg, idsToEncode, codec) ``` -对于不感兴趣 `gofountain` 的读者,这部分可能是一个非必需并且有些无聊的部分,但是我希望对那些也被这个 API 所迷惑的人有帮助,这样他们就可以通过搜索结果找到这篇文章了。 +That’s probably a needlessly boring part for the reader not interested in working with `gofountain`, but I hope it’ll be helpful for someone struggling with its API, coming to this post via search results. -# 测试结果 +# Testing results -由于我保存了原始包的 API,余下的工作就非常容易了。你也许记得在[前一篇文章](https://divan.dev/posts/animatedqr/)中,我在 web 端的应用使用了名为 `txqr-tester` 的 txqr 项目的 Go 语言包,它可以在浏览器中运行。在这里,Go 的可跨平台的特性又一次让我感到很兴奋!我只需要切换到包含有新的编码和解码实现的 `fountain-codes` 分支,运行 `go generate` 来执行 `gomobile` 和 `gopherjs` 命令,然后只需要几秒钟,喷泉码应用就可以在 Swift 和浏览器中使用了。 +As I preserved the API of the initial package, the rest was a breeze. As you might remember from the [previous article](https://divan.dev/posts/animatedqr/), I used the txqr Go package in both iOS app and web app called `txqr-tester`, running in the browser. That’s where mindblowing cross-platform nature of Go paid off again! I just switched to my `fountain-codes` branch, with the new implementation of encoder and decoder, run `go generate` to execute both `gomobile` and `gopherjs` commands, and in a couple of seconds, I had fountain codes implementation ready to use with Swift and in the browser. -我想,恐怕没有其他的语言能够做到了吧? +I wonder if there any other language that can do this? -接下来我启动了测试程序,包括启动三脚架上的手机以及外界显示器,配置测试参数,以及启动自动测试,这个过程会持续将近半天的时间。这次我没有为了节省时间而修改二维码错误级别,因为似乎这个参数对结果的影响基本可以忽略。 +I launched my test setup, consisting of the phone on a tripod and external monitor, configured testing parameters and launched automated testing, which lasted nearly half a day. This time I didn’t change QR error recovery levels to save time, as they seem to have a negligible effect on the result. -结果让我非常震撼。 +The results were more than impressive. -测试传输大概 13KB 数据所记录的时间现在只有半秒,准确的说是 **501ms** —— 传输速率就接近 25kbps。这组记录配置的是 12FPS、每个二维码 1850 字节信息,以及低错误矫正等级。解码所需要的时间差异显著下降,因为“需要循环迭代”以及重复代码的部分在这一版本中都没有了。如下是对比**重复代码**和**喷泉码**的解码时间直方图: +The record time for transferring ~13KB of data is now is half a second or **501ms** to be precise – it’s almost 25kbps. This record was set for 12FPS and 1850 bytes per QR code with low error correction level. The variance of the time needed to decode plummeted significantly as there were no “expect the whole loop iteration” part as with repetition codes. Compare decoding times histogram between **repetition code** and **fountain code** : [![time_histogram](https://plot.ly/~divan0/15.png?share_key=t8DizOL9dynI6NTcLA88Xi)](https://plot.ly/~divan0/15/?share_key=t8DizOL9dynI6NTcLA88Xi "time_histogram") -如你所见,大多数配置了不同 FPS 和数据块大小的值的解码测试时间都集中在时间轴上数字比较小的位置 —— 大多数都小于 4 秒。 +As you can see, most of the decoding attempts, with different values for FPS and chunk size, are still concentrated in the lower part of time axis – most of them less them 4 seconds. -这是一个更加详细的结果: +And here is more detailed results breakdown: [![time_vs_size](https://plot.ly/~divan0/16.png?share_key=t8DizOL9dynI6NTcLA88Xi)](https://plot.ly/~divan0/16/?share_key=t8DizOL9dynI6NTcLA88Xi "time_vs_size") -测试结果非常优秀,所以我决定使用大于 1000 字节的块来运行测试 —— 块大小最高可以达到 2000 字节。这为我呈现了非常有趣的结果:很多块大小在 1400 到 1700 字节的测试超时了,但是 1800-2000 字节的块的结果确是目前来说最好的: +Results were so good, so I decided to run tests with chunk sizes greater than 1000 bytes - up to 2000 bytes. And that brought me interesting results – there were a lot of decoding timeouts with chunk sizes between 1400 and 1700, but 1800-2000 bytes actually showed one of the best results so far: [![time_vs_size_2k](https://plot.ly/~divan0/7.png?share_key=t8DizOL9dynI6NTcLA88Xi)](https://plot.ly/~divan0/7/?share_key=t8DizOL9dynI6NTcLA88Xi "time_vs_size_2k") -在这次测试中,FPS 的影响似乎显得更加微不足道了,但是却可以得出所有配置中最好的结果,我甚至可以将其提升到 15FPS: +FPS effect seemed to be even more negligible this time but showed much better results within all values overall, and I even was able to increase the upper cap to 15FPS: [![time_vs_fps](https://plot.ly/~divan0/9.png?share_key=t8DizOL9dynI6NTcLA88Xi)](https://plot.ly/~divan0/9/?share_key=t8DizOL9dynI6NTcLA88Xi "time_vs_fps") -如下是测试结果的完整的可交互 3D 图: +Here is a complete interactive 3D interactive graph with results: [![3d_results](https://plot.ly/~divan0/18.png?share_key=t8DizOL9dynI6NTcLA88Xi)](https://plot.ly/~divan0/18/?share_key=t8DizOL9dynI6NTcLA88Xi "3d_results") -# 结论 +# Conclusion -使用喷泉码绝对是一件让人兴奋的事情。它很出色但是又很简单,虽然应用的范围比较小,但却非常实用、巧妙和快捷,它们绝对是“超酷算法”中的一份子。而当你一旦明白了它们的工作原理,它们就是那些让你敬佩的算法之一了。 +Fountain codes are definitely an exciting thing to work with. Being non-trivial, but simple, narrow scoped, but extremely useful, clever and fast, they undoubtedly fall into the “cool algorithms” bucket. They’re one of those algorithms that can give you awe once you understand how they work. -对于 txqr 项目,它们也为之带来了性能和可靠性的提升,我期待着可以使用比 LT 编码还要有效率的算法,并实现能适用于喷泉码流线特性的 API。 +For txqr, they offered dramatic performance and reliability improvements, and I’m looking forward to play with more efficient algorithms than LT codes, and implement proper API adapted to the streamline nature of fountain codes. -而 Gomobile 和 Gopherjs 则通过最大可能的减少了使用在浏览器和移动平台中已经编写和测试过的代码的麻烦,又一次展现了它们惊人的一面。 +Gomobile and Gopherjs, again, showed their awesomeness by decreasing the hassle of using already written and tested code in the browser and mobile platforms to a lowest possible minimum. -# 参考链接 +# Links * [Wikipedia: LT Codes](https://en.wikipedia.org/wiki/Luby_transform_code) * [Wikipedia: Fountain Codes](https://en.wikipedia.org/wiki/Fountain_code) From 48de3ace1877090bf2da18ba9039c3999b128e84 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 43/58] =?UTF-8?q?Revert=20"Xcode=20=E5=92=8C=20LLDB=20?= =?UTF-8?q?=E9=AB=98=E7=BA=A7=E8=B0=83=E8=AF=95=E6=95=99=E7=A8=8B=EF=BC=9A?= =?UTF-8?q?=E7=AC=AC=203=20=E9=83=A8=E5=88=86=20(#6104)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ced32bab6043690eaaa394899c90e7068ef2c82b. --- ...lldb-advanced-debugging-tutorial-part-3.md | 146 +++++++++--------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-3.md b/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-3.md index ebdc637b3c8..49fc7bfafd3 100644 --- a/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-3.md +++ b/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-3.md @@ -2,46 +2,46 @@ > * 原文作者:[Fady Derias](https://medium.com/@fadiderias) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-3.md](https://github.com/xitu/gold-miner/blob/master/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-3.md) -> * 译者:[kirinzer](https://github.com/kirinzer) -> * 校对者:[swants](https://github.com/swants), [iWeslie](https://github.com/iWeslie) +> * 译者: +> * 校对者: -# [译] Xcode 和 LLDB 高级调试教程:第 3 部分 +# Xcode and LLDB Advanced Debugging Tutorial: Part 3 -在这三部分教程的第一部分和第二部分中,我们已经介绍了如何利用 Xcode 断点来控制一个存在的属性值,并且通过表达式语句注入新的代码行。我们还探索了观察点这种特殊类型的断点。 +In the first and second parts of this three parts tutorial, we’ve covered how to utilize Xcode breakpoints to manipulate an existing property value and inject a new line of code via expression statements. We’ve also explored watchpoints that are a special type of breakpoints. -我开发了一个特意带有几个错误的演示项目,详细说明了如何使用不同类型的断点配合 LLDB 来修复项目/应用程序中的错误。 +I developed a demo project with several intentional bugs to elaborate on how to use different types of breakpoints alongside the LLDB to fix bugs in your project/application. -如果你还没有看过本教程的 **[第一部分](https://github.com/xitu/gold-miner/blob/master/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-1.md)** 和 **[第二部分](https://github.com/xitu/gold-miner/blob/master/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-2.md)**,最好先看过它们再继续阅读本文。 +If you didn’t go through **[part 1](https://github.com/xitu/gold-miner/blob/master/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-1.md)** and **[part 2](https://github.com/xitu/gold-miner/blob/master/TODO1/xcode-and-lldb-advanced-debugging-tutorial-part-2.md)** undefinedof this tutorial, it’s crucial to check them before proceeding with this final part. -最后,本教程的指导原则是: +One last time, the golden rule of this tutorial: -第一次运行应用程序后,你不必停止编译器或重新运行应用程序,你将在运行时修复这些错误。 +You’re not to stop the compiler or re-run the application after running it for the very first time. You’re fixing the bugs at runtime. -## 符号断点 🔶 +## Symbolic Breakpoints 🔶 -到目前为止我们还要做什么? +How are we doing so far? -> 4. 导航栏左侧指示用户加载次数的标签没有更新。 +> 4. The left navigation bar label that indicates how many times the user did load posts is not being updated. -这里有一些步骤可以复现这最后一个需要处理的错误: +Here are the steps to reproduce the last bug you’re to deal with: -✦ 滚动到表视图的顶部,然后下拉刷新。 +✦ Scroll to the top of the table view, and pull down to refresh. -✦ 滚动到表视图的底部去加载更多文章。[7 次 😉] +✦ Scroll to the bottom of the table view to load new posts. [for 7 times 😉] -✦ 在每次成功获取到新的文章之后,左侧标签并没有被更新。 +✦ The left label is not being updated for every time new posts are successfully retrieved. -需要指出的是整型属性 `pageNumber` 回答了这个问题,用户已经加载了文章多少次?(换句话说,导航栏左侧的标签应该被 `pageNumber` 属性的值更新)。我们可以确信的是,在之前的修复中 `pageNumber` 属性的值已经可以更新了。因此现在的问题在于没有将它的值设置给导航栏左侧的标签。 +It’s important to point out that the integer `pageNumber` property answers the question, how many times the user did load posts..? (i.e. the left label on the navigation bar should be updated by the value of the `pageNumber` property). We’re quite sure from the previous fixes that the `pageNumber` property is updated properly, hence the problem is with setting its value to the dedicated label on the navigation bar. -在这种情况下,符号断点会介入。想象一下,符号断点就像调试器在玩寻宝,而你会提供一些寻宝的线索。对你来说,这会发生在更新导航栏左侧标签的代码片段中。 +In such cases, symbolic breakpoints strike in. Think of symbolic breakpoints as if the debugger is playing treasure hunt and you’re providing it with clues to get to that treasure. In your case, that happens to be the piece of code that updates the left label on the navigation bar. -让我告诉你接下来怎么做。 +Let me show you how to do that. -展开 Breakpoint navigator,接着点击左下角 + 按钮,选择 Symbolic Breakpoint。 +Show the breakpoint navigator, and click on the + button on the bottom left corner. Select symbolic breakpoint. ![](https://cdn-images-1.medium.com/max/2000/1*nI_n_rCvxBS5ZILJqDVzrA.png) -在 Symbol 栏添加如下符号 +Add the following symbol ``` [UILabel setText:] @@ -49,19 +49,19 @@ ![](https://cdn-images-1.medium.com/max/2052/1*bd0Xm4s2qxGAAlPafpuHgQ.png) -**不要** 勾选 “Automatically continue after evaluating actions” 选项框。 +**Don’t** check the “Automatically continue after evaluating actions” box. -我们所做的只是告诉调试器,当任何一个 UILabel 的 setText 方法被调用的时候,它就会暂停。注意这里在创建了一个符号断点之后,一个子断点会被添加。 +What we’re simply doing here is informing the debugger that whenever the setText function of any UILabel is called, it should pause. Notice that after creating the symbolic breakpoint, a child has been added. ![](https://cdn-images-1.medium.com/max/2000/1*pCPLepbfpWKJrNUfpprfow.png) -这是来自调试器的反馈,它能够解析这个创建的符号断点到 `UIKitCore` 框架的特定位置。在其他情况下,调试器也许会解析到多个位置。 +It’s a feedback from the debugger that it was able to resolve the created symbolic breakpoint to a specific location inside `UIKitCore` framework. In other cases, the debugger might resolve the symbolic breakpoint to multiple locations. -现在一切就绪,下拉以刷新表视图的文章。当你释放之后,调试器就会暂停,接着你会看到如下图的东西: +Now you’re all set, pull down to refresh the posts table view. As soon as you release, the debugger will pause, and you’ll be seeing something like this: ![](https://cdn-images-1.medium.com/max/5676/1*qxcTdnmPUempljXsANz62Q.png) -在这时你会看到一些 UIKitCore 框架的汇编代码,在左侧的是导致调试器暂停的堆栈信息。下一步我们要做的是,检查在调试器暂停的位置传入 Objective-C 消息的参数。在 lldb 控制台输入下面的命令: +At this point, you’re looking at some assembly code of the UIKitCore framework and on the left side is the stack trace that did cause the debugger to pause. The next thing we want to do is to inspect the arguments passed into the Objective-C message the debugger did pause at. In the lldb console, type the following: ``` po $arg1 @@ -69,9 +69,9 @@ po $arg1 ![](https://cdn-images-1.medium.com/max/4448/1*V33e1RQgoWtwNI8qy-AVJQ.png) -这会指出持有第一个参数的寄存器。我们能清楚的看到接受这个 Objective-C 消息的是一个 UILabel 实例。这个 UILabel 实例有一个文本值指向一个文章的标签。这不是我们所感兴趣的,不过让我们继续寄存器检查。 +This does point out to the register that holds the first argument. We can clearly see that the receiver of that Objective-C message is a UILabel instance. The UILabel instance has a text value that refers to a post label. It’s not what we are interested in, but let’s proceed with the registers inspection. -在 lldb 控制台,输入如下指令: +In the lldb console, type the following: ``` po $arg2 @@ -79,9 +79,9 @@ po $arg2 ![](https://cdn-images-1.medium.com/max/2000/1*RF7qzO66OUAAZ61TwKg2GA.png) -$arg2 始终指向 Objective-C 消息的选择器。在某些情况下,lldb 并不完全的清楚参数的类型,因此我们需要做一些类型转换的工作。 +The $arg2 does always refer to the selector of the Objective-C message. In some cases, the lldb doesn’t implicitly know the types of the arguments, and hence we need to do some typecasting. -在 lldb 控制台,输入如下指令: +In the lldb console, type the following: ``` po (SEL)$arg2 @@ -89,9 +89,9 @@ po (SEL)$arg2 ![](https://cdn-images-1.medium.com/max/2000/1*f7lc9OC3NZGDTpOssJ3PBQ.png) -现在我们很清楚的看到了当前 Objective-C 消息的选择器。 +Now, we can clearly see the selector of the current Obj-c message. -在 lldb 控制台,输入如下指令: +In the lldb console, type the following: ``` po $arg3 @@ -99,21 +99,21 @@ po $arg3 ![](https://cdn-images-1.medium.com/max/2000/1*saKLYWOujvPhkmf3qcBD5g.png) -$arg3 始终指向传入方法的第一个参数。在我们的情形下,传入 setText 方法的参数一个字符串。 +The $arg3 does always refer to the first parameter passed into the method. In our case, that is the string that is passed to the setText method. -继续执行程序。调试器会再次暂停。重复前面的步骤,最终,你发现这个 Objective-C 消息属于在表视图里的另一个文章标签。直到我们找到我们感兴趣的那个 UILabel 实例前,一遍又一遍的做这个事情确实很无趣。肯定有更好的方式。 +Continue the execution of the program. The debugger will pause again. Repeat the above steps and eventually, you’ll figure out that the objective-c message belongs to another label of a post in the table view. It’s quite nonsense to keep doing this over and over again till we reach the UILabel instance that we are interested in. Things can definitely be better. -你能够做的一件事就是为符号断点设置条件,以便在成功或满足条件时暂停调试器。它能够检查布尔值或者等待条件达成诸如此类。 +One thing you can do is to set a condition for the symbolic breakpoint to pause the debugger upon the success/fulfilment of that condition. This can be checking on a boolean value or waiting for a specific state to be reached .. etc. ![](https://cdn-images-1.medium.com/max/2060/1*bDOd5KQn_VzWy8mA6OEcVA.png) -然而,我们采用一种不同的方法。 +However, we’re going for a different approach. ### One Shot! -将我们创建的符号断点设置为不可用。 +Disable the symbolic breakpoint you’ve created. -讲道理,导航栏左侧的标签指示了用户加载文章的次数,它会在 HTTP GET 请求成功完成之后被更新。找到有 pragma mark `Networking` 的部分。在 `loadPosts` 成功完成的回调里放置一个断点。这个断点应该放在如下的位置: +Logically speaking, the left navigation bar label that indicates how many times the user did load posts is updated after the posts are successfully retrieved via the HTTP GET request. Navigate to the section with the pragma mark `Networking`. Place a breakpoint inside the success completion handler of `loadPosts`. It should be **below**: **Objective-C** @@ -131,9 +131,9 @@ self.tableView.reloadData() ![](https://cdn-images-1.medium.com/max/2776/1*I69SoCZ3fAlaviM0WUWTXA.png) -这会确保符号断点只有在表视图重新加载数据之后才会被触发,所有相等的标签都已经被更新。 +This will assure that the symbolic breakpoint will get triggered only after the table view has been reloaded and all of its equivalent labels have been updated. -**不要** 勾选 “Automatically continue after evaluating actions” 选项框。添加如下的调试器命令动作: +**Don’t** check the “Automatically continue after evaluating actions” box. Add the following debugger command action: ``` breakpoint set --one-shot true -name '-[UILabel setText:]' @@ -141,39 +141,39 @@ breakpoint set --one-shot true -name '-[UILabel setText:]' 🤨🧐🤔 -让我们拆解这个命令: +Let’s break that command: -1. breakpoint set --one-shot true 会创建一个 “one-short” 断点。one-shot 断点是一种创建之后,首次触发就会自动删除的断点。 +1. breakpoint set --one-shot true does create a “one-shot” breakpoint. A one-shot breakpoint is a type of breakpoint that only exists till it’s triggered then it gets automatically deleted. -2. `-name ‘- [UILabel setText:]’` 给创建的 one-shot 断点设置了一个符号名。这和你上一节所做的非常相似。 +2. `-name ‘- [UILabel setText:]’` does set a symbolic name to the created one-shot breakpoint. It’s quite similar to the one you created in the last section. -让我总结一下这一部分。你所做的有: +Let me recap this part. Here’s what you did: -1. 在发起 GET 请求成功完成的回调里添加断点(A)。 +1. Adding a breakpoint (A) in the success completion handler of the function that executes the posts GET request. -2. 添加调试器命令动作去 **创建** 符号断点(B)和上一节创建的很相似。这个符号是 `UILabel` `setText` 方法。 +2. Adding a debugger command action to ****create**** a symbolic breakpoint (B) similar to the one you created the last section. Its symbol is the `UILabel` `setText` function. -3. 将你创建的符号断点(B)设置为一个 one-shot 断点。one-shot 断点在触发后会被自动删除,这意味着符号断点只会暂停调试器一次。 +3. Setting the symbolic breakpoint (B) you created to be a one-shot breakpoint. It’s guaranteed that the symbolic breakpoint will pause the debugger only once since a one-shot breakpoint gets deleted automatically after it has been triggered. -4. 断点(A)被放置在表视图加载完成之后,因此创建的符号断点(B)不会因任何和表视图相关联的标签而暂停调试器。 +4. Breakpoint (A) is located after reloading the table view so that the created symbolic breakpoint (B) doesn’t pause the debugger for any of the labels related to the table view. -现在下拉表视图去刷新。我们会得到如下内容: +Now pull down the table view to refresh. Here’s what you’ll get: ![Objective-C](https://cdn-images-1.medium.com/max/2332/1*JLBQAj7srx3twyCnScnVSg.png) ![Swift](https://cdn-images-1.medium.com/max/2044/1*2gcJPkL-VZ3HIebwOsqMZA.png) -由于设置了 one-shot 断点调试器停在了断点(A)的位置。 +The debugger did pause at the breakpoint (A) and hence setting the one-shot symbolic breakpoint. -继续执行程序。 +Continue the program execution. -你会返回到 UIKitCore 框架的汇编代码。 +You’re back to the assembly code of the UIKitCore framework. ![](https://cdn-images-1.medium.com/max/5676/1*qxcTdnmPUempljXsANz62Q.png) -让我们检查一下符号断点参数的 Objective-C 消息。 +Let’s inspect the Objective-C message of the symbolic breakpoint arguments. -在 lldb 控制台,输入如下的指令: +In the lldb console, type the following: ``` po $arg1 @@ -181,19 +181,19 @@ po $arg1 ![](https://cdn-images-1.medium.com/max/3712/1*U7on9rNp2KTxH0vBu_5pwg.png) -哇哦,看起来你找到了宝藏! 🥇🏆🎉 +WELL WELL WELL, looks like you finally found your treasure !! 🥇🏆🎉 -是时候把我们的目光转移到堆栈跟踪信息了。**走到点 1 的位置。** +Time to shift our sights to the stack trace. **Step to point 1.** ![Objective-C](https://cdn-images-1.medium.com/max/4728/1*kx3XCFR0kcnpD5XC1tqtng.png) ![Swift](https://cdn-images-1.medium.com/max/3788/1*42LvhyQXygvMOF0dWphR2g.png) -它会引导你到这块更新 `pageNumberLabel` 文本的代码。这块代码很明显为文本始终设置了整型值为 `0` 而不是 `pageNumber` 属性的格式字符串。让我们在实际修改代码前先测试一下。 +It led you to the piece of code that is updating the `pageNumberLabel` text. It’s quite obvious that the text is always set to a string with a format of integer value `0` rather than the `pageNumber` property. Let’s test it before we make actual changes to our code. -你现在已经是行家了 🧢 +You’re an expert now 🧢 -在已标记的代码分隔线下添加一个断点。添加如下的调试器命令动作: +Add a breakpoint in a separate line below the marked line of code. Add the following debugger command action: **Objective-C** @@ -211,35 +211,35 @@ expression pageNumberLabel.text = String(format: "Page %tu", pageNumber) ![](https://cdn-images-1.medium.com/max/3564/1*IreVT3ZC9rTiC8B60WcxSw.png) -移除或者停用断点(A),相应地,断点(B)也会被停用。 +Remove/Disable breakpoint(A), accordingly, this will disable breakpoint(B) -现在下拉刷新和加载更多文章。左侧导航栏标签将会被更新。 🎉 +Now pull to refresh and scroll to load more posts. The left navigation bar label is being updated. 🎉 -任务完成! 💪 💪 +Mission Accomplished !! 💪 💪 -现在你可以停止编译器并且在代码中去修复我们讨论的这些问题。 +You can now stop the compiler and add the fixes we discussed in your code. -### 总结 +### Summary -在这个教程里,你学会了 +In this tutorial, you’ve learned -1. 如何使用断点配合调试器动作表达式去控制存在的属性值。 +1. How to use breakpoints alongside debugger action expression statements to manipulate existing values/properties. -2. 如何使用断点配合调试器动作表达式注入代码。 +2. How to use breakpoints alongside debugger action expression statements to inject lines of code. -3. 如何为某个属性设置观察点监视属性值的变化。 +3. How to set watchpoints to certain properties to monitor their values when being updated. -4. 如何基于定义的符号使用符号断点暂停调试器。 +4. How to use symbolic breakpoints to pause the debugger based on defined symbols. -5. 如何使用 one-shot 断点。 +5. How to use one-shot breakpoints. -6. 如何使用 one-shot 断点配合符号断点。 +6. How to use one-shot breakpoints alongside symbolic breakpoint. -调试愉快! 😊 +Happy Debugging!! 😊 -### 第三方工具 +### Third-party tools -为了本教程的方便,我使用了下面的第三方工具。 +I’ve used the following third-party tools for the convenience of this tutorial * [typicode](https://github.com/typicode)/[json-server](https://github.com/typicode/json-server) From f66b4b669599f4d7a0459209a2c87ff99878a432 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 44/58] =?UTF-8?q?Revert=20"=E4=BB=8E=20Reddit=20=E8=AE=A8?= =?UTF-8?q?=E8=AE=BA=E4=B8=AD=E7=9C=8B=E5=88=B0=E7=9A=84=20GraphQL=20?= =?UTF-8?q?=E7=8E=B0=E7=8A=B6=20(#6140)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 0acdfa052c8000239fe7594ca24d3b303d09981e. --- TODO1/the-state-of-graphql-by-reddit.md | 138 +++++++++++------------- 1 file changed, 63 insertions(+), 75 deletions(-) diff --git a/TODO1/the-state-of-graphql-by-reddit.md b/TODO1/the-state-of-graphql-by-reddit.md index 333caf3607c..19874dc9352 100644 --- a/TODO1/the-state-of-graphql-by-reddit.md +++ b/TODO1/the-state-of-graphql-by-reddit.md @@ -2,129 +2,117 @@ > * 原文作者:[Robert Matyszewski](https://twitter.com/intent/follow?screen_name=iamrobmat) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-state-of-graphql-by-reddit.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-state-of-graphql-by-reddit.md) -> * 译者:[TiaossuP](https://github.com/TiaossuP) -> * 校对者:[yangxy81118](https://github.com/yangxy81118) +> * 译者: +> * 校对者: -# 从 Reddit 讨论中看到的 GraphQL 现状 +# The state of GraphQL by Reddit -一提到 GraphQL,就会看到各类炒作文章以及将它与 REST 进行比较的争论。GraphQL 现在处于全球流行、全面使用的早期阶段,没有人确切知道它会在哪里在止步。通过在互联网上的调研,我发现了许多对这项新技术的安利文章。这只是对第一印象的炒作吗? +There’s a lot of hype on GraphQL and debate to compare it to REST. GraphQL is in the early stage of adoption globally and no one exactly knows where it will end. Researching on the internet, I was able to find many positive articles presenting this new piece of tech. Is it just a hype of the first impression? -我研究了 Reddit 上关于 GraphQL 的评论,并挑选了其中一些最受欢迎的内容。本文旨在以透明、客观的态度来讨论这个主题,所以对于每个派别的不同观点,我都挑选了一些用户的探讨与争论的内容。下面的每个评论引用都有一个指向其作者的链接,同时()中的为该评论的点赞 **(译注:原文为 upvote)** 数量。不过要注意,我撰写与发表本文以后,点赞数字可能会发生变化。 +I’ve researched Reddit and selected the most upvoted comments on GraphQL. My goal was to write down as much transparent and objective article on the topic. I’ve used discussions and arguments between users to present a different point of view on each aspect. Each comment quoter below has a link to its author and number of upvotes in (). Keep in mind that upvote numbers might change since I’ve written this article. -## 议程 +## Agenda -* 整体状况 -* React & Apollo 状况 -* 大公司 & GraphQL -* 缓存 -* 请求数据 -* 总结 +* General review +* React & Apollo review +* Big Boys & GraphQL +* Caching +* Data Fetching +* Summary. -# 整体状况 +# General review -从整体出发,我选择了两个实践例子。首先,[SwiftOneSpeaks](https://www.reddit.com/user/SwiftOneSpeaks/) 显示了前端开发人员的视角和潜在的市场改进。其次,[Scruffles360](https://www.reddit.com/user/scruffles360/) 展示了团队如何适应 graphql 以及他们使用具体工具的策略趋势。稍后您会发现更多他的评论。第二个评论是本文中选择的赞最少的评论。 +Starting from a general view I’ve chosen two cases. First, one - [SwiftOneSpeaks](https://www.reddit.com/user/SwiftOneSpeaks/) shows front end developer perspective and potential improvements in time to market. Secondly, [Scruffles360](https://www.reddit.com/user/scruffles360/) presents strategy trends on how teams adapt graphql and which one they used. Later on you’ll find more about his case. The second comment was the least upvoted one that I’ve chosen in the article. -[SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/)(23)说: +[SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/) (23) says: -> 当我与后端开发团队合作时,他们更倾向于提供新的查询来满足我的需求,因为这不会影响他们必须支持的现有查询。(也就是说,我不知道随着时间的推移,这个查询的扩展性会怎样)。GraphQL 还减少了我必须重新解析为可用(满足我的需要)的数据结构的糟糕响应的数量。(例如,我得到了 3 个数组,我必须将它们关联起来并压缩到一组对象中。尽管后端仍然需要有一些工作要做,但使用 GraphQL,我可以有更丰富的能力来要求数据格式。) +> **When I was working with a backend dev team, they were far more willing to provide new queries to match my needs because it didn’t impact existing queries they had to support. (That said, I have no idea how well this scales over time). It also reduced the number of crappy responses I had to reparse into usable (for my needs) data structures. (example, I’d get 3 arrays back that I had to relate and zip together into a single set of objects. With GraphQL, I had more ability to demand data in a useful shape, though the backend still has to do their part).** -[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enpb6fg/)(8)描述了他在 GraphQL 领域内看到的三个发展方向: +[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enpb6fg/) (8) explains his three trends in GraphQL scope: -> * 巨石应用:这就是阿波罗现在所推动的。每家公司都有一个且只有一个 api endpoint 和 schema 代理其他所有东西([https://principledgraphql.com/](https://principledgraphql.com/))。我完完全全不同意这个思路,但不会在这里重复我的论点(如果想了解的话,您可以挖掘我的评论历史) -> * 数据库 api:出于一些奇怪的原因,人们已经开始向数据库添加插件,这些插件可以通过 graphql 直接访问数据库。由于很多原因,Graphql 非常棒,但是它还不能与原生数据库查询语言相媲美。更重要的是,这去掉了您的业务层,使调用者可以直接访问您的 store。除了一个微服务应用,其余任何人都不应拥有访问store的权限 —— 其他人应该通过您的 api 调用服务。 -> * 中间路线:经典的 API 做法,每个应用程序都有自己的 API(在本例中为 GraphQL)。它可能将业务逻辑或代理隔离到微服务(通过 rest 或通过 schema stitching 到另一个 Graphql 架构)。这是我们走的路,目前为止还没让我后悔。 +> * The monolith - which is what Apollo is pushing now. Every company has one and only one api endpoint and schema that proxies to everything else ([https://principledgraphql.com/](https://principledgraphql.com/)). I disagree with this wholeheartedly, but won’t repeat my arguments here (you can dig through my comment history if you want) +> * The database api - for some strange reason people have started adding plugins to databases that give you direct database access via graphql. Graphql is wonderful for so many reasons, but it doesn’t come close to competing with a native database query language. More importantly, this takes away your business layer giving callers direct access to your store. No one should have access to your store except one single microservice. Everyone else should be calling through your api. +> * The medium approach - The classic API approach where each app has their own API (graphql in this case). It might isolate business logic or proxy to microservices (via rest or by schema stitching another Graphql schema). That’s the route we went, and I don’t regret a thing. -# React & Apollo 状况 +# React & Apollo review -React 和 Apollo 的组合获得了很多关注。此外 [Wronglyzorro](https://www.reddit.com/user/wronglyzorro/) 和 [Livelierepeat](https://www.reddit.com/user/livelierepeat/) 讨论为什么后端开发可能不喜欢 GraphQL。一位更有经验的开发人员的回应获得了更多的赞。另外,我选择了一个更长但非常详细的评论。 +React & Apollo combination review request gained a lot of attention. Additionally [Wronglyzorro](https://www.reddit.com/user/wronglyzorro/) and [Livelierepeat](https://www.reddit.com/user/livelierepeat/) argued about why backend developers might not like GraphQL. The response from more experienced developer gained triple more upvotes! Additionally I’ve choosen one longer but very detailed review. -[Wronglyzorro](https://www.reddit.com/r/reactjs/comments/9nmj0w/what_is_your_experience_with_react_apollo_graphql/e7nkk73/)(12): +[Wronglyzorro](https://www.reddit.com/r/reactjs/comments/9nmj0w/what_is_your_experience_with_react_apollo_graphql/e7nkk73/) (12) says: -> 我们在网络应用程序上严格使用 react + apollo。我们也强迫移动客户端也使用它。虽然听起来有些荒诞,但这确实是大趋势。后端开发人员当然讨厌它,因为他们习惯了自己原有的方式,不喜欢改变。然而,在我们过去一年中出现的故障中,从没有 GraphQL 导致的,崩溃的总是遗留的后端服务。 +> **We strictly use react + apollo on our web app. We’re also forcing the mobile clients to use it as well. It’s incredible and the future. Backend devs of course hate it because they are set in their ways and don’t like change. However over the last year when there have been any sort of outages it was never graphql that was a point of weakness. It was always the legacy backend services that crapped out.** -[Livelierepeat](https://www.reddit.com/r/reactjs/comments/9nmj0w/what_is_your_experience_with_react_apollo_graphql/e7o92o5/)(40)回复: +[Livelierepeat](https://www.reddit.com/r/reactjs/comments/9nmj0w/what_is_your_experience_with_react_apollo_graphql/e7o92o5/) (40) responded: -> 后端开发人员当然讨厌它,因为他们习惯了自己原有的方式,不喜欢改变。 -> -> 您可能想要了解我的观点,我曾经是一个年轻的开发人员,使用所有最新的工具,并嘲笑那些「适应不了」的人。我了解到,通常有比「人们讨厌改变」更有趣的原因。比如 GraphQL 的抽象是否太过复杂?他们抗拒工作量的增加,可究竟增加了什么? -> -> 有时,所有的工具都是最新的,反而可能不能让这些工具的效用得到最大凸显。更关键的还是在于理解代码和参与开发的人。 +> **You might want to gain a little more insight that that. I used to be a young dev who used all of the latest tools and scoffed at those who “couldn’t adapt”. I’ve learned that there are often much more interesting reasons than, “people hate change”. Like does GraphQl create burdensome abstractions? What is getting added to their workload that they are resisting? At some point using all of the latest tools loses its luster. More power comes from understanding the code and the people processes as well.** -[Capaj](https://www.reddit.com/r/reactjs/comments/9nmj0w/what_is_your_experience_with_react_apollo_graphql/e7nlgrn/) (11)的详细总结: +[Capaj](https://www.reddit.com/r/reactjs/comments/9nmj0w/what_is_your_experience_with_react_apollo_graphql/e7nlgrn/) (11) detailed review: -> 我们从五月开始在生产环境中使用 GraphQL。我们是一个全栈团队,所以我们不需要仰仗合作的后端团队的怜悯。说服每个人并不容易,不过 GQL 内置了一个示例,每个人都认为它看起来比 REST 更好。Graphiql 对此有很大帮助。 -> -> 这挺好的。我们在后端使用 apollo 引擎,我非常喜欢在生产环境中使用指标捕捉 API 错误。我们使用 [decapi](https://github.com/capaj/decapi) 来装饰我们的 [objection.js](https://github.com/Vincit/objection.js) DB Model。我们在单独的地方定义 model,然后不需要做什么就可以自动生成 GQL。 -> -> 在前端,我们使用 apollo-client,但到目前为止我们还没有使用缓存。我们的前端重点是摆脱之前的 angular.js 代码,所以我们还没有时间去试验前端缓存。 -> -> 我尚未使用 apollo 进行客户端状态管理,因为到目前为止我听到的所有反馈都表明它尚未准备好面对生产环境。另外,我不得不说它看起来很啰嗦,写起来也很啰嗦。相反,我希望我可以扩展 https://github.com/mhaagens/gql-to-mobx 并将其用于我们的状态管理需求。MST 使用 typescript 表现的很好。如果我们可以在编辑 GQL 查询时动态地从查询中生成 MST 模型,我们可以大大提高我们的工作效率。 +> **We use it in production since May. We’re a fullstack team so we’re not on the mercy of some other team doing the backend. It wasn’t easy persuading everyone, but with a single sample feature built in GQL everyone agreed it looked way better than REST. Graphiql helped a lot with that. It’s been quite good. We have apollo engine enabled on the backend and I really enjoy using metrics to hunt API bugs in prod. We use decapi to decorate our objection.js DB models. We have a single place where we define our models and GQL gets generated almost for free. On the frontend we use apollo-client, but we don’t use caching so far. Our FE focus is on getting rid of our legacy angular.js code, so we don’t have time yet to experiment with FE caching. I don’t event consider using apollo for client side state management, because all the feedback I’ve heard so far was that it’s not production ready yet. Also I have to say it looks quite verbose for what it really does. Instead I am hoping I can extend [https://github.com/mhaagens/gql-to-mobx](https://github.com/mhaagens/gql-to-mobx) and use that for our state management needs. MST works wonders with typescript. If we can generate MST models from our queries on the fly while editing our GQL queries we can boost our productivity considerably.** -# 缓存 +# Caching -我已经从 [SwiftOneSpeaks](https://www.reddit.com/user/SwiftOneSpeaks/) 和 [Scruffles360](https://www.reddit.com/user/scruffles360/) 中看到了很多很棒的和高赞的评论,这些评论已经在上文提到了。以下是他们讨论的缓存问题和潜在解决方案。 +I’ve found a lot of great and upvoted comments from [SwiftOneSpeaks](https://www.reddit.com/user/SwiftOneSpeaks/) and [Scruffles360](https://www.reddit.com/user/scruffles360/) which had been already mentioned here. Here’s what they discussed on lack of caching and potential solution. -[SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/) (23) 写道: +[SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/) (23) writes: -> 虽然您可以将 GraphQL 配置为以各种方式工作,但实际上它们始终是 POST 请求。这意味着所有依赖于 GET 幂等而 POST 不幂等这一约定的浏览器缓存、CDN 缓存、代理缓存在默认情况下都将失效。一切都被视为新请求。虽然您可以在客户端自行做一些更智能的缓存,但这实际上只是在解决您自己产生(指引入GraphQL)的问题。 +> **While you can configure GraphQL to work differently, as a practical matter they will always be POST requests. Which means all your browser/CDN/proxy caches that rely on GET being idempotent and POST not being now don’t work by default. Everything is treated as a new request. You can set the client to do more smart caching, but that’s literally solving a problem you created.** -[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enokkzb/) (11) 回复: +[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enokkzb/) (11) respoded: -> 阿波罗有一个解决方案 —— 「动态持久化查询」,不过我还没有尝试过。大致来说,客户端将使用 GET 请求(将 query 哈希),如果失败,则回退到 POST。接下来的 GET 调用将成功并应用到任何代理缓存。 +> **Apollo has a solution for this. I haven’t tried it, but its called Dynamic Persisted Queries if you want to read up. Basically the client makes a GET call with a hash of the query and if it fails, falls back to POST. The next GET call will succeed and populate any proxy caches.** -# 请求数据 +# Data Fetching -这些人还对数据提取提出了不同的观点。在 [graphql-vs-rest](https://www.imaginarycloud.com/blog/graphql-vs-rest/) **([译文在此](https://juejin.im/post/59793f625188253ded721c70))** 中,作者描述了一个具有多个作者的博客应用程序示例以及使用 GraphQL 与 REST 的可能性。 +Those guys also presented a different points of view on data fetching. In the [original article](https://www.imaginarycloud.com/blog/graphql-vs-rest/) writer describes an example of blog app with multiple authors and possibility of using GraphQL vs REST. -[SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/) (23) 说: +[SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/) (23) says: -> 每个人都强调「过度请求」问题 **(译注:原文为 Over fetching,意指请求的数据中有很多您并不需要的字段。还有一个类似的概念:Under fetching,意指某个接口的返回数据不够,部分字段缺失,导致还需要请求第二个接口,这两种情况的问题都在于浪费了不必要的网络资源)** 问题。我觉得这根本不是设计出糟糕服务的借口(事实上问题在于 —— 如果开发者一直很菜,那么他写出来的 GraphQL 服务也不可能就突然好用了)。这很容易解决,只需要在原有服务前面加一个服务就行 —— GraphQL 可以胜任这项工作,但用别的东西也可以。问题不在于是否过度请求,而是中央服务**与**解决缓存问题。 +> **Everyone emphasizes the “over fetching” problem. I feel like that’s a red herring outside of poorly designed services (and that sort of points out the flaw - don’t expect GraphQL services from poor service developers to suddenly not be poor) It’s easy to resolve if you put a service in front - GraphQL can be that service, but so can something else. The issue isn’t over fetching vs not, it’s having a central service AND solving the caching issues.** -[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enokkzb/)(12)回复: +[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enokkzb/) (12) responded: -> 过度请求确实是一个问题。当您有数百个客户端时,每个客户端都以不同的方式调用您的系统,则向 rest api 添加一个属性就会导致极大的效率降低。许多人提出为移动客户端和 web 端提供不同的门面接口,但这样扩展性很差。我的项目由数百个客户端调用,每个客户端的请求方式与请求内容都有些许不同。 +> **Over fetching is a real problem. When you have hundreds of clients, each calling your system in different ways, adding a property to a rest api causes massive inefficiencies. Many people point out using client-centric facades for their mobile client vs web for example, but that doesn’t scale. My project is called by hundreds of clients, each asking for slightly different data in different ways.** -# 大公司 & GraphQL +# Big Boys & GraphQL -每个人都对 Facebook、Netflix 和 Coursera 等大公司以及他们对 GraphQL 的使用情况感兴趣。对于这篇 [graphql-vs-rest](https://www.imaginarycloud.com/blog/graphql-vs-rest/) 在 Reddit 中的评论中,我们可以找到两个主要原因作为作者 - 状态。提出的第一条评论是我发现的最受欢迎的评论。 +Everyone is interested in big companies like Facebook, Netflix and Coursera and their adaptation of GraphQL. In the [article](https://www.imaginarycloud.com/blog/graphql-vs-rest/) commented on Reddit we can find two main reasons as an author - states. The first comment presented was the most upvoted comment that I’ve found. -* 在 2010 年初,移动用户数量激增,出现了与低功耗设备和缓慢网络相关的一些问题。REST 不是处理这些问题的最佳选择; -* 随着移动使用量的增加,运行客户端应用程序的不同前端框架和平台的数量也在增加。由于 REST 不够灵活,导致我们更难开发出一个能够满足所有终端应用需求的单一 API。 +* In the early 2010s there was a boom in mobile usage, which led to some issues with low-powered devices and sloppy networks. REST isn’t optimal to deal with those problems; +* As mobile usage increased, so did the number of different front-end frameworks and platforms that run client applications. Given REST’s inflexibility, it was harder to develop a single API that could fit the requirements of every client. -[Greulich](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/ennt7ak/)(62)回复这篇文章: +[Greulich](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/ennt7ak/) (62) responded to article: -> 这离题太远,没有意义。构造请求的另一种方法不会使这些请求所在的网络更好或更差。 -> 我认为作者的描述的是 endpoint 而不是 API,因为任何 endpoint,无论有多少 endpoint,都只是 API 的一部分。假设是这样,为什么我们只需要一个 endpoint? +> * This is so tangential as to be pointless. A different method of structuring your requests, does not render the network on which those requests better or worse. +> * I think the author means endpoint rather than API, because any endpoint, no matter how many there are, is part of the API. Assuming that is the case, why do we NEED only one endpoint? -[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enohrog/)(16)回复 Greulich: +[Scruffles360](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enohrog/) (16) responded to Greulich: -> 文章中的前两点措辞并不好,但仍然是正确的。REST API 可以是通用、可复用的,也可以是专门为已知客户端设计。在第一种情况下,当您需要不断再次调用系统以获取更多数据时 **(译者注:即上面提到的 Under fetching)**(尤其是像10年前我们在移动设备上那样的高延迟网络),将无法获得良好的性能。如果您为特定客户端制作 API,则显然会遇到可扩展性问题。 +> **The first two points weren’t worded well in the article, but are still valid. A REST API can either be generic and reusable or crafted specifically for a known client. In the first case, you aren’t going to get good performance when you keep making calls back to the system for more data (especially on high-latency networks like we had on mobile 10 years ago). If you craft your API for a specific client, you obviously run into scalability problems.** -# 总结 +# Summary -在选择正确的评论来总结 GraphQL 的状态时,有很多话要说或选择。**直到今天关于 reddit 的最受欢迎的 submissions 是 facebook 或 netflix 的案例研究**,但这些 submission 的评论并不多。这给了我们以 reddit 对 GraphQL 的看法的一个很好的总结。想到各位开发者的日常生活后,我无法忽略 Kdesign(36)写下的内容: +There’s a lot to say or choose when picking the right comment to summarize the state of GraphQL. **Till today the most popular submissions on reddit are case studies of facebook or netflix yet they aren’t much commented**. This gives us already a good summary on reddit’s view on GraphQL. From a daily developer life I couldn’t skip what [Kdesign](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/enn9sdf/) (36) wrote: -> ### **GraphQL 让您的工作更安全,这是肯定的。** -> -> 您必须花时间在前端和实际数据存储之间的所有 N 多层中,寻找数据的位置。 +> ### **GraphQL provides job security, that’s for sure**. -[Kollektiv](https://www.reddit.com/r/node/comments/bozsb1/graphql_vs_rest_putting_rest_to_rest/enng3ba/)(44)列出了很多GraphQL问题: +[Kollektiv](https://www.reddit.com/r/node/comments/bozsb1/graphql_vs_rest_putting_rest_to_rest/enng3ba/) (44) stated a great list of GraphQL issues: -> * 查询限速和权限评估等很难实现。 -> * 类型和数据加载器的工作方式,如果不编写完整的 module 就分组查询,则难以有效的方式将查询绑定到数据库层。 -> * 验证仅检查类型,因此您仍需要某种 JSON schema 来执行其他格式验证。 -> * GraphQL 查询只允许 left join,因此像 INNER JOIN 加过滤这种重新创建 SQL 就变得很棘手了。 -> * 来自像 Relay 这样的框架强加的分页(连接)还是一团糟。 +> * Things like query rate limiting and credit estimation are difficult. +> * The way type and dataloaders work, it’s difficult to bind queries to the database layer in an efficient way by grouping queries without writing a full module for it. +> * Validation only checks types so you still need some kind of JSON schema to do additional format validation. +> * GraphQL queries only allow for left joins so recreating SQL like INNER JOINs together with filters quickly becomes awkward. +> * The imposed pagination (connections) from frameworks like Relay are a mess. -关于我对GraphQL的初步研究 [SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/)(24)写道: +Regarding my initial research on GraphQL [SwiftOneSpeaks](https://www.reddit.com/r/reactjs/comments/bozrg1/graphql_vs_rest_putting_rest_to_rest/eno3ovb/) (24) wrote: -> 我认为我们看到了很多「GraphQL 很棒」报告主要是因为**任何新服务都很棒** —— 随着时间的推移,因为假设条件被违背 **(译注:假设条件的概念可以参考[浅谈Architectural Assumption(软件架构设计的假设条件)](https://blog.csdn.net/ytomc/article/details/80728132))**、需求变更和代码变更,它们肯定会变得逐渐笨拙。不过这并不意味着 GraphQL不好 —— 只是说意味着我不能过多地信任早期报告。 +> I expect many of the “GraphQL is great” reports we are seeing is mainly because ANY new service is great - they only get klunky over time, as assumptions are violated and needs change and code changes accrue. This doesn’t mean GraphQL won’t be great - it just means I can’t trust early reports too much. -最后,我选择了 [Mando0975](https://www.reddit.com/r/node/comments/bozsb1/graphql_vs_rest_putting_rest_to_rest/enopzpk/) (28)的观点来总结这篇文章: +And finally, I’ve chosen [Mando0975](https://www.reddit.com/r/node/comments/bozsb1/graphql_vs_rest_putting_rest_to_rest/enopzpk/) (28) opinion to summarize this article: -> **开发始终是为工作挑选合适的工具。GraphQL 不是银弹。REST 并没有死,GraphQL 也不会杀掉它。** +> **Development should always be about picking the right tool for the job. GraphQL isn’t always the right tool. REST isn’t dead and GraphQL isn’t going to kill it.** -### 您的 GraphQL 体验如何? +### What’s your experience with GraphQL? > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 1d0a4df9561a99706fe26e072b89d18e85b9264c Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 45/58] =?UTF-8?q?Revert=20"=E5=88=A9=E7=94=A8=2084=20?= =?UTF-8?q?=E7=A7=8D=E8=AE=A4=E7=9F=A5=E5=81=8F=E8=A7=81=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=9B=B4=E5=A5=BD=E7=9A=84=E4=BA=A7=E5=93=81=20=E2=80=94?= =?UTF-8?q?=E2=80=94=20=E7=AC=AC=E4=BA=8C=E9=83=A8=E5=88=86=20(#6168)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 907e9610fabd8d40fb78d5c1176bf7c31aa9d88e. --- ...ollection-cognitive-biases-how-to-use-2.md | 311 +++++++++--------- 1 file changed, 155 insertions(+), 156 deletions(-) diff --git a/TODO1/collection-cognitive-biases-how-to-use-2.md b/TODO1/collection-cognitive-biases-how-to-use-2.md index 1bee932cd75..ae06901608c 100644 --- a/TODO1/collection-cognitive-biases-how-to-use-2.md +++ b/TODO1/collection-cognitive-biases-how-to-use-2.md @@ -2,306 +2,305 @@ > * 原文作者:[@gilbouhnick](https://twitter.com/GilBouhnick) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md) -> * 译者:[江五渣](http://jalan.space) -> * 校对者:[shinichi4849](https://github.com/shinichi4849),[Moonliujk](https://github.com/Moonliujk) +> * 译者: +> * 校对者: -# 利用 84 种认知偏见设计更好的产品 —— 第二部分 +# 84 cognitive biases you should exploit to design better products - Part 2 ![](https://2.bp.blogspot.com/-JvOvFjdlVfE/XMhvVVa0R4I/AAAAAAAAPrM/KaVBcSKDdPgb1PLug4TlVOx07uY6YHShQCLcBGAs/s640/Cognitive%2Bbiases.png) --- -> * [利用 84 种认知偏见设计更好的产品 —— 第一部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md) -> * **[利用 84 种认知偏见设计更好的产品 —— 第二部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md)** -> * [利用 84 种认知偏见设计更好的产品 —— 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-3.md) +> * [84 cognitive biases you should exploit to design better products - Part 1](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md) +> * **[84 cognitive biases you should exploit to design better products - Part 2](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md)** +> * [84 cognitive biases you should exploit to design better products - Part 3](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-3.md) --- -## 易被说服 +## Easily convinced -说服的艺术!这里有一些(性价比高的?)技巧可以帮助你更好地传达你的信息。 +The art of persuasion! Here are some (cheap?) tricks to help you better convey your message. -### 22. 锚定效应 +### 22. Anchoring Effect: -我们常常过分依赖最初获得的讯息(“锚定”)来做出后续的决策。 +We tend to rely too heavily on the first piece of information presented to us (the “anchor”) to make subsequent decisions. -**如何使用**:书中最老套的把戏是:把一个高的价格作为锚,然后划掉它,在它旁边设定一个更低的价格。 +**How to use it**: The oldest trick in the book: you add a high price as the anchor and then cross it out and set a much lower price next to it. -看看史蒂夫·乔布斯在 iPad 发布会上是如何使用它的,我必须问问:它真的有效吗? +Looking at how Steve Jobs used it with the iPad launch I must ask: did it really work? -![锚定效应 —— 由苹果公司的斯蒂夫·乔布斯在 iPad 发布会上展示](https://alexdenk.eu/blogtouch?id=1hE2NPk3sVnoS3eO1RqMCAltJj9qtDTR2 "锚定效应 —— 由苹果公司的斯蒂夫·乔布斯在 iPad 发布会上展示") +![Anchoring Effect - Performed by Apple's Steve Jobs during the iPad launch](https://alexdenk.eu/blogtouch?id=1hE2NPk3sVnoS3eO1RqMCAltJj9qtDTR2 "Anchoring Effect - Performed by Apple's Steve Jobs during the iPad launch") -史蒂夫·乔布斯展示了锚定效应 +Steve Jobs demonstrating the Anchoring Effect -然而,这是我对锚定效应的看法: +Nevertheless, here’s my take on the Anchoring Effect:  -![锚定效应示例 —— The Mobile Spoon](https://alexdenk.eu/blogtouch?id=1p3CIvG4gYfBNWVzo04dM1K280ABxyah0 "锚定效应示例 —— The Mobile Spoon") +![Anchoring Effect Example - The Mobile Spoon](https://alexdenk.eu/blogtouch?id=1p3CIvG4gYfBNWVzo04dM1K280ABxyah0 "Anchoring Effect Example - The Mobile Spoon") -产品定价中的锚定效应 +Anchoring Effect in product pricing -### 23. 双曲贴现 +### 23. Hyperbolic Discounting -我们更喜欢及时的回报(尽管回报很小),而不是更大的后期回报。 +We prefer an immediate payoff (even if it’s small) rather than a larger-later reward. -**技巧**: 为立即支付提供小额折扣(或免费送货服务),而不是为将来的购买行为提供大额折扣。 +**Tip**: offer a small discount (or free shipment) for immediate purchase, rather than a larger discount for future purchases. -![双曲贴现 —— 为立即支付提供小额折扣](https://alexdenk.eu/blogtouch?id=1qjNmi6biEjEB9hP8HexcdV_d410gyVKo "双曲贴现 —— 为立即支付提供小额折扣") +![Hyperbolic Discounting - Offer small discounts for an immediate purchase](https://alexdenk.eu/blogtouch?id=1qjNmi6biEjEB9hP8HexcdV_d410gyVKo "Hyperbolic Discounting - Offer small discounts for an immediate purchase") -为立即支付提供小额折扣 +Offer small discounts for an immediate purchase  -### 24. 社会认同 +### 24. Social Proof -如果像我们这样的人正在使用它 —— 它一定是好的产品! +If people like us are using it - it must be good! -这对吗? +Right? -**如何使用**:社会认同是建立信任的好方法。以下是一些可以在网站、[应用商店页](http://www.mobilespoon.net/2019/04/lessons-learned-app-store-screenshots.html "根据从应用商店截图中汲取的经验教训重新设计 —— the mobile spoon") 中使用的示例: +**How to use it**: Social proof is a great way to build trust. Here are a few examples that can be used in your website, [App Store product page](http://www.mobilespoon.net/2019/04/lessons-learned-app-store-screenshots.html "Lessons learned from our app store screenshots redesign - the mobile spoon"): -1. 知名客户标识 -2. 知名合作伙伴标识 -3. 客户的推荐(来自目标受众) -4. 数据(客户数据、交易数据、会话数据 —— 来自任何工作方式) -5. 媒体提及和引用(“如……所见”) -6. 知名机构的赞扬(即 WIRED 最热门创业公司) -7. 认证 -8. 与实际数字关联的研究案例 +1. Well known customer logos +2. Well known partner logos +3. Testimonials and authentic recommendations by customers (from the target audience) +4. Counters (number of customers, deals, sessions - whatever works) +5. Media mentions and quotes (“as seen on…”) +6. Awards by well-known organizations (i.e. hottest startup by WIRED) +7. Certifications +8. Links to case studies with actual numbers -![社会认同 —— 使用数据和客户标识建立信任](https://alexdenk.eu/blogtouch?id=1Q9lHYKquIMlrpRXvWohwcC1Rp4-g7kiG "社会认同 —— 使用数据和客户标识建立信任") +![Social Proof - Build trust using counters and customer logos](https://alexdenk.eu/blogtouch?id=1Q9lHYKquIMlrpRXvWohwcC1Rp4-g7kiG "Social Proof - Build trust using counters and customer logos") -不要说:“我们太赞了,因为……”,相反地,让别人来证明这一点: +Don’t say: “we are awesome because… “, and instead, let others testify: -![社会认同 —— 不要说:“我们太赞了,因为……”,相反地,让别人来证明这一点](https://alexdenk.eu/blogtouch?id=1XaHyFHqHFmG-XlW_81CF8ZZlh24iVW7m "社会认同 —— 不要说:“我们太赞了,因为……”,相反地,让别人来证明这一点") +![Social Proof - Don’t say “We are awesome because…” and instead, let others testify](https://alexdenk.eu/blogtouch?id=1XaHyFHqHFmG-XlW_81CF8ZZlh24iVW7m "Social Proof - Don’t say “We are awesome because…” and instead, let others testify") -### 25. 权威偏差 +### 25. Authority Bias  -我们认为权威人士给出的建议准确性更高,并且更容易受到该建议的影响(即使事物主体与该人物的权威性无关)。 +We attribute greater accuracy to the opinion of an authority figure and be more influenced by that opinion (even if the subject is unrelated to the person’s authority). -**如何使用**:通过宣传产品的知名推荐来建立权威: +**How to use**: Build your authority by promoting your famous references: -1. 联系有影响力的人,让他们免费使用你的产品或服务 -2. 突出显示知名客户或品牌 -3. 展示来自社会名流的推荐语 -4. 在产品展示和陈述中使用权威人物(例如医生、教授等) +1. Contact influencers and give them free access to your product or service +2. Highlight famous customers or brands +3. Testimonials by famous people and celebrities +4. Use authority figures in your visuals and statements (i.e. doctors, professors, etc.) -![权威偏见 —— 宣传最有力的推荐](https://alexdenk.eu/blogtouch?id=1nU53Zted9Esavdatf_VCeoiAFP1f8ps9 "权威偏见 —— 宣传最有力的推荐") +![Authority Bias - Promote your strongest references](https://alexdenk.eu/blogtouch?id=1nU53Zted9Esavdatf_VCeoiAFP1f8ps9 "Authority Bias - Promote your strongest references") -### 26. 从众效应(羊群效应) +### 26. Bandwagon Effect (Herd Behavior) -我们做某件事的原因经常只是因为“每个人都在做这件事!”(每个人都是生酮饮食者吗?) +We often do things only because “everyone is doing it!” (Ketogenic diet anyone?). -我们会根据支持观点人数多寡来改变自己的想法。 +We change our opinions, according to the number of people thinking the same way. -**营销技巧**:使人们相信每个人都在使用你的产品,那么更多的人将会使用它。 +**Marketing tip**: convince that everyone is using your product and more people will indeed use it. -![从众效应 —— 使人们相信其他人都在使用它](https://alexdenk.eu/blogtouch?id=1eV9oOXnVE-fNunuBH4vryKtwWQinGLoc) +![Bandwagon Effect - Convince that others are using it](https://alexdenk.eu/blogtouch?id=1eV9oOXnVE-fNunuBH4vryKtwWQinGLoc) -这是在产品中使用从众效应的另一个例子: +And here’s another example for the using the Bandwagon Effect in your product: -![从众效应 —— “最流行”的案例](https://alexdenk.eu/blogtouch?id=1rcPAf96BP_A9YfBa8AdFJJnd4eSRli9r "从众效应 —— “最流行”的案例") +![Bandwagon Effect - The “Most Popular” Example](https://alexdenk.eu/blogtouch?id=1rcPAf96BP_A9YfBa8AdFJJnd4eSRli9r "Bandwagon Effect - The “Most Popular” Example") -“最流行”的案例 +The “Most Popular” example  -### 27. 归属偏差 +### 27. Belonging Bias -我们都是社会性动物,为了成为群体的一部分,我们经常像群体内其他成员一样行事。如果所有开发人员都在使用 Slack —— 你可能也应该这样做,对吧? +We are all social creatures and in order to feel part of a group we often act like other members in that group. If all the software developers are using Slack - you probably should too, right? -**模仿技巧**:使用数据,例如客户数量、会话数量、提供的服务次数,来说服群众你的产品是标配。 +**Copy tip**: Use counters such as the number of customers, sessions, services provided, to convince the audience that your product is a standard. -### 28. 团体偏差 +### 28. In-group Bias -一旦我们成为某个群体中的一部分,我们几乎会“自动”倾向于我们群体中的成员,而不是其他群体中的成员。 +Once we are part of a group, we almost “automatically” favor members in our group over members in other groups. -群体内的偏袒也意味着我们更经常帮助自己群体内的成员。 +In-group favoritism also means we help members of our group more often than those in other groups. -![团体偏差](https://alexdenk.eu/blogtouch?id=1f8GJpWMyrUWOlAr1PctUrs-B2BLJlABp "团体偏差") +![In-group Bias](https://alexdenk.eu/blogtouch?id=1f8GJpWMyrUWOlAr1PctUrs-B2BLJlABp "In-group Bias") -### 29. 非我所创 +### 29. Not-Invented-Here Syndrome -我们避免使用(或购买)已经存在的产品,因为它们来源于外部,我们更愿意自己发明类似的产品。 +We avoid using (or buying) already existing products, because of their external origins, and prefer to invent similar products internally. -举个例子:当你的工程团队倾向于从零开始开发某些东西而不是使用现有产品时。 +Example: When your engineering team prefers to develop something from scratch instead of using an existing product. -**管理技巧**:通过赞美你的团队来遏制这一现象。他们的专业知识需要用来开发产品的核心功能,而不能“浪费”在“造轮子”上。 +**Management tip**: Tackle this phenomenon by complimenting your team. Their expertise is needed to develop the core of the product and their precious time cannot be “wasted” on side modules, or services that are already invented by others. -阅读:[在尝试成为数据驱动型公司中学到的 11 个经验教训](https://www.mobilespoon.net/2019/06/11-data-related-lessons-we-learned-hard.html) +Read: [11 lessons learned while trying to become a data-driven company](https://www.mobilespoon.net/2019/06/11-data-related-lessons-we-learned-hard.html) -### 30. 信念偏差 +### 30. Belief Bias -我们更容易接受一个与我们先前所知知识一致的结论,同时拒绝接受与该结论相悖的论点。 +We are more likely to accept an argument that supports a conclusion that aligns with our prior knowledge while rejecting counter-arguments to the conclusion. -**写作技巧**:当谈论产品好处时,不要夸大其词。 +**Writing tip**: When talking about the benefits of your product - don’t exaggerate. -如果好得令人难以置信,人们就会怀疑它的真实性。 +If it’s too good to be true - people will not believe it. -### 31. 登门坎效应 +### 31. Foot-in-the-door Technique -当我们与用户通过小的协议建立起联系后,我们更容易与用户在更大的协议上达成一致。 +A small agreement creates a bond between us and the requester and potentially makes it easier for us to agree on bigger agreements. -**订阅技巧**:通过提供免费试用与用户建立联系。 +**Subscription tip**: Give your users a free trial to hook them up with something small. -**引导流程 UX 设计技巧**:不要用过于复杂的引导流程让用户感到不耐烦。 +**Onboarding UX tip**: Don’t overwhelm the user with over-complicated onboarding tasks. -把大块的内容分解成小块或容易解决的内容,让用户保持开心和参与度。 +Break it into small/easy chunks to achieve small wins and keep the user happy and engaged. -![登门坎效应 —— 通过创造“小成就”让引导流程变得容易](https://alexdenk.eu/blogtouch?id=1MpxTvnkZoqBD8Wtqy3gx708rpl0axjxa "登门坎效应 —— 通过创造“小成就”让引导流程变得容易") +![Foot-in-the-door Technique - Make onboarding easy by creating “small wins"](https://alexdenk.eu/blogtouch?id=1MpxTvnkZoqBD8Wtqy3gx708rpl0axjxa "Foot-in-the-door Technique - Make onboarding easy by creating “small wins"") -在引导流程中取得小成就 +Achieve small wins during the onboarding process  -### 32. 多变的奖赏 +### 32. Variable Reward -我们能从意外之礼中收获更多的快乐。 +Our joy from getting rewards increases when the rewards are unexpected. -**产品技巧:**用每日优惠、免费奖金、荣誉积分等奖品吸引用户。 +**Product tip:** Hook your users with daily offers, free bonuses, reputation points, prizes and more.  -用户每天的“行为”越多,越会觉得自己与产品的联系密切。 +The more “action” you create on a daily basis - the more your users will feel connected and hooked to the product. -![多变的奖赏 —— 用每日优惠、免费奖金、荣誉积分等奖品吸引用户。](https://alexdenk.eu/blogtouch?id=1aNyfch7QCQln-rC_IhxZY077ucAyDX14 "多变的奖赏 —— 用每日优惠、免费奖金、荣誉积分等奖品吸引用户。") +![Variable Reward - Hook your users with daily offers, free bonuses, reputation points, prizes and more.](https://alexdenk.eu/blogtouch?id=1aNyfch7QCQln-rC_IhxZY077ucAyDX14 "Variable Reward - Hook your users with daily offers, free bonuses, reputation points, prizes and more.") -用每日优惠、免费奖金、荣誉积分等奖品吸引用户 +Hook your users with daily offers, free bonuses, reputation points, prizes and more  -## 并非我们所想的那么理性 +## Not as rational as we think -做出理性的选择并不像看上去那么容易。 +Make rational decisions is not as easy as it seems. -### 33. 赌徒谬误 +### 33. The Gambler's Fallacy -我们错误地认为,如果在某一时间段一件事发生的频率高于常态,那么在将来它的发生频率就会降低。 +We mistakenly believe that if something happens more frequently than normal during a given period, it will happen less frequently in the future. -**工作相关技巧**:坚持事实论据。依赖数据而非直觉。 +**Work-related tip**: Stick with facts. Less intuition, more numbers. -### 34. 确认偏差 +### 34. Confirmation Bias: -我们搜寻和偏爱那些能印证我们最初想法与先入之见的信息。 +We seek and favorite information that confirms our initial belief and preconception. -**问**:你是否反复操作过你的 KPI 报告,从几周到几个月,从几个月再到几个季度,直到找到你想要的结论? +**Question**: Did you ever manipulated your KPI reports, again and again, switching from weeks to months, from months to quarters, until finding the conclusion you were looking for? -### 35. 不确定偏差 +### 35. Disconfirmation Bias -我们倾向于忽视与我们想法相悖的证据。 +We tend to dismiss evidence that refutes our beliefs. -**问**:你是否曾(在面试中)非常青睐一位候选人,以至于忽略了一些他/她的缺点? +**Question**: Did you ever like a candidate (during a job interview) so much that you ignored some warning signs? -阅读:[从 B2B 到 B2C 的 5 个 产品管理经验](https://www.mobilespoon.net/2018/04/5-things-i-learned-in-transition-from.html) +Read: [5 product management lessons I've learned when moving from B2B to B2C](https://www.mobilespoon.net/2018/04/5-things-i-learned-in-transition-from.html) -### 36. 框架效应 +### 36. Framing Effect -我们抉择的过程并非总能如想象中那样理性,我们会受到信息呈现方式的影响(正面与负面框架)。 +Our decision-making processes are not always as rational as we think and we are influenced by the way the information is presented (positive vs negative frames). -产品技巧:在大多数情况下,正面框架(即玻璃杯是半满,而非半空)有更高的转化率。 +Product tip: In most cases, positive frames (i.e. glass is half full) convert better. -![框架效应 —— 尝试使用“玻璃杯半满”的表达方式来提高转换率](https://alexdenk.eu/blogtouch?id=1Lc6vP6HPyktJ8kk2XaoPVqLQKxiu7LNO "框架效应 —— 尝试使用“玻璃杯半满”的表达方式来提高转换率") +![Framing Effect - Try to present the full half of the cup for better conversion](https://alexdenk.eu/blogtouch?id=1Lc6vP6HPyktJ8kk2XaoPVqLQKxiu7LNO "Framing Effect - Try to present the full half of the cup for better conversion") -正面框架的实践 +A positive frame in action  -### 37. 境联效应 +### 37. Context Effect -我们对事物的感知受到事物呈现或发生时所在场景的影响。 +Our perception of things is influenced by the context in which they are presented or happening. -在视觉设计中,人们对物体的颜色或大小的感知因其呈现位置与方式的不同而不同。 +In visual design, a color or a size of an object can be perceived differently depending on where and how they are presented. -![设计中的境联效应](https://alexdenk.eu/blogtouch?id=1HGFPufdKBckl6ADB9QPFEYgrBiq1SBSX "设计中的境联效应") +![Context Effect in design](https://alexdenk.eu/blogtouch?id=1HGFPufdKBckl6ADB9QPFEYgrBiq1SBSX "Context Effect in design") -### 38. 选择性知觉 +### 38. Selective Perception -我们对事物的看法很大程度上受到我们期望的影响。 +Our perception of things is highly influenced by our expectations. -**产品和营销技巧**:产品的转化漏斗并非从用户登录开始,而是从用户第一次看到你的广告时就开始了。 +**Product & marketing tip**: Your product’s conversion funnel doesn’t begin when the user boards, it begins when the user sees your ad for the first time. -营销信息与产品提供内容之间的不一致会让你的用户大失所望,从而导致转化率的下降。 +Misalignment between the marketing message and what the product delivers will disappoint your users and result in poor conversion rates. -在所有媒介上(漏斗的各个阶段)的消息一致将为用户带来正确的期望,从而提高转化率。 +Aligning the messages across all media (and stages of the funnel) will bring users with the right expectations and as a result - improve conversion rates. -### 39. 热手谬误 +### 39. Hot-hand Fallacy -一种错误的观点认为一个经历过成功的人更有机会取得进一步的成功。 +A fallacious belief that a person who has experienced success has a greater chance of further success in additional attempts. -**如何使用**:强调一系列成功案例来打造品牌。 +**How to use**: Emphasize a list of successes stories to build your strong brand. -![热手谬误 —— 如果阿什顿·库彻投资这家创业公司,那这家公司一定不错,对吧?](https://alexdenk.eu/blogtouch?id=1USmoXr5h3-KqGtaLIoi4XWV6V9MuASsj "热手谬误 —— 如果阿什顿·库彻投资这家创业公司,那这家公司一定不错,对吧?") +![Hot Hand Fallacy - If Ashton Kutcher invested in this startup it must be good, right? ](https://alexdenk.eu/blogtouch?id=1USmoXr5h3-KqGtaLIoi4XWV6V9MuASsj "Hot Hand Fallacy - If Ashton Kutcher invested in this startup it must be good, right? ") -如果阿什顿·库彻投资这家创业公司,那这家公司一定不错,对吧? +If Ashton Kutcher invested in this startup it must be good, right?  -### 40. 期待 +### 40. Anticipation: -期待积极的体验使我们的大脑感到兴奋,这有助于提升我们的幸福感。 +Our brain is wired to anticipate positive experiences. Anticipation contributes to our happiness. -**例如**:需要等上几个月的假期往往有更好的体验。提前买好《复仇者联盟·终局之战》的电影票让我们感到兴奋和快乐。 +**Examples**: Waiting a few months for a big vacation contributes to the overall experience. Buying early tickets to Avengers: Endgame makes us all excited and happy. -**产品技巧**:通过提前宣布(或发布)你的新产品来创造预期。创造一个积极的话题和一些值得期待的东西,确保你的观众对此感到兴奋。 +**Product tip**: Create anticipation for your new product (or release) by announcing it ahead of time. Create a positive buzz, something to look forward to, ensuring your audience will remain excited. -### 41. 信息偏倚 +### 41. Information Bias -即使信息无法影响我们的行为,我们也会寻求信息。 +We seek information even when it cannot affect our action. -**产品技巧**:当展示产品或服务时(在你的网站或在你的产品上)—— 请务必在照片中附上详细说明。你在产品描述中添加的信息越多,你的用户获得的保证就越大。 +**Product tip**: When presenting products or services (in your website or within your product itself) - make sure to include photos a detailed description. The more information you can pile on to product description, the greater the assurance your users will get. -## 并非我们所说的那样确信 +## Not as thorough as we say we are -当然,我们在做出决定前做了些研究,我们只是在前行的路上忘记了一些事情,就是这样…… +Sure, we did the research before making the decisions, we just forgot a few things along the way, that's all...  -### 42. 获得性启发(获得性偏差) +### 42. Availability Heuristic (Availability Bias) -我们认为那些迅速跃入脑海的事情比那些不容易想起的事情更为常见和重要。因此,最近发生的、频繁发生的、极端的、被记住的事情比大多数信息更有影响力。 +We think that things that jump quickly to mind are more common and important than the things that do not easily come to mind. As a result, what’s recent, frequent, extreme, remembered, is more influential than the majority of information. -**UI 技巧**:通过创建一些与众不同的东西(在不影响一致性和熟悉度的情况下)让你的设计被记住。 +**UI tip**: make your design rememberable by creating something unique that stands out (without compromising on consistency and familiarity). -![获得性启发(获得性偏差)](https://alexdenk.eu/blogtouch?id=1OPjCDK5T34-JUt-9UcwnMvql4CgLkBP4 "获得性启发(获得性偏差)") +![Availability Heuristic (Availability Bias)](https://alexdenk.eu/blogtouch?id=1OPjCDK5T34-JUt-9UcwnMvql4CgLkBP4 "Availability Heuristic (Availability Bias)") -### 43. 注意力偏误 +### 43. Attentional Bias -在检查所有可能的结果时 —— 我们倾向于关注那些看起来理性而熟悉的,从而忽略了其他影响。 +When examining all possible outcomes - we tend to focus on a few that seem rational and familiar and ignore all others. -**问**:你是否曾经发布过一项功能,你认为它会带来确切的结果,但却发现它带来了你未能预料到的副作用? +**Question**: Did you ever release a feature thinking that it would lead to a certain outcome only to discover it caused a side effect you failed to anticipate? -![注意力偏误](https://alexdenk.eu/blogtouch?id=1SKWO5ipZk7aZbiy1pxDIGIoa7SyWx8a8 "注意力偏误") +![Attentional Bias](https://alexdenk.eu/blogtouch?id=1SKWO5ipZk7aZbiy1pxDIGIoa7SyWx8a8 "Attentional Bias") -### 44. 流畅性启发 +### 44. Fluency Heuristic -我们认为那些处理速度更快、更流畅、更顺利的事物具有更高的价值。 +We give higher value to things that are processed faster, fluently, and smoothly. -有时不合逻辑的论点在沟通良好的情况下(由有权威和经验的人提出)也可能会赢得胜利。 +Sometimes illogical argument, if communicated well (by someone who has authority and experience presenting things) might win. -![流畅性启发](https://alexdenk.eu/blogtouch?id=1w7igcsMZwzEdFPgPCfrzXnTrlYjZ9p80 "流畅性启发") +![Fluency heuristic](https://alexdenk.eu/blogtouch?id=1w7igcsMZwzEdFPgPCfrzXnTrlYjZ9p80 "Fluency heuristic") -这里有一个相关术语“**心理捷径**” —— 人们经常使用启发式来做决定,你应该在你的设计中充分利用它们。 +A related term here is “**Mental Shortcuts**” - people frequently use heuristics to make decisions; you should use them to your advantage in your design. -**产品技巧:** +**Product tips:** -1. 为用户提供便利(快捷、简单和易于理解的导航) -2. 使内容易于浏览(图像,易读的字体) -3. 创建“心理捷径”,它将吸引用户,促使他们把你的产品作为首选 -4. 提供有意义的默认选项,因为用户会认为你考虑了他们的最大利益,并将尽可能选择默认选项 -5. 添加功能强大的跨产品搜索,以简化特定主题的查找 +1. Make things easy for your users (fast, simple and easy navigation) +2. Make content easily scannable (images, readable fonts) +3. Create “mental shortcuts” that will hook your users and promote the preferred behavior. +4. Provide meaningful defaults, because users assume you have their best interests in mind and will select the default options whenever possible. +5. Add a powerful cross-product search to ease the finding of specific topics. -举例: +Examples: -下面是为用户提供便利的两个示例,促使他们把你的产品作为首选。 +Here are 2 examples for making things easier for the user, in order to promote the preferred behavior for the business. -![用户希望产品可以提供现成的默认值](https://alexdenk.eu/blogtouch?id=1VqWeo2F654LMST8Ipcko1KUgNeMmwZ1r "用户希望产品可以提供现成的默认值") +![Users expect products to provide ready-to-use defaults](https://alexdenk.eu/blogtouch?id=1VqWeo2F654LMST8Ipcko1KUgNeMmwZ1r "Users expect products to provide ready-to-use defaults") -用户希望产品可以提供现成的默认值 +Users expect products to provide ready-to-use defaults -![产品设计中的流畅性启发 —— 为最近的操作提供简单的快捷方式](https://alexdenk.eu/blogtouch?id=1Wg1I6YLnShB81rgKM2JVQ_L0FYPTwhJw "产品设计中的流畅性启发 —— 为最近的操作提供简单的快捷方式") +![Fluency Heuristic in product design - providing easy shortcuts for recent activity](https://alexdenk.eu/blogtouch?id=1Wg1I6YLnShB81rgKM2JVQ_L0FYPTwhJw "Fluency Heuristic in product design - providing easy shortcuts for recent activity") -为最近的操作提供简单的快捷方式 +Provide simple shortcuts for recent activities  -**工作相关技巧**:一定要做好功课:收集足够的数据,写下你的观点并思考如何表达你的观点,在设计上下苦功。 +**Work-related tip**: Always do your homework: collect enough data, write down your points, think about how to present your points, invest in the design of things. -### 45. 谷歌效应(亦名:数码失忆) +### 45. The Google Effect (AKA: Digital Amnesia) -我们可以在网上轻松找回遗忘的信息。 - -**问**:你还记得你最好的朋友或孩子的电话号码吗? +We forget information that can be easily found online. +**Question**: Do you remember the phone number of your best friend or kid? --- -> * [利用 84 种认知偏见设计更好的产品 —— 第一部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md) -> * **[利用 84 种认知偏见设计更好的产品 —— 第二部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md)** -> * [利用 84 种认知偏见设计更好的产品 —— 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-3.md) +> * [84 cognitive biases you should exploit to design better products - Part 1](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md) +> * **[84 cognitive biases you should exploit to design better products - Part 2](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md)** +> * [84 cognitive biases you should exploit to design better products - Part 3](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-3.md) --- -在 Twitter 上关注我 [@gilbouhnick](https://twitter.com/GilBouhnick),或 [订阅我的简报](https://mailchi.mp/b9c664dfafa3/mobilespoon),可以将帖子直接发送到你的收件箱。 +Follow me on twitter [@gilbouhnick](https://twitter.com/GilBouhnick), or [subscribe to my newsletter](https://mailchi.mp/b9c664dfafa3/mobilespoon) to get some occasional posts directly to your inbox. > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 4d3712a659ce84cf6d82ed74b44a37db3775281f Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 46/58] Revert "Delete hermes.md" This reverts commit b23043e11e8f65a11b94ce5e4c1a882406bbead7. --- TODO1/hermes.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 TODO1/hermes.md diff --git a/TODO1/hermes.md b/TODO1/hermes.md new file mode 100644 index 00000000000..d97e0d1bc80 --- /dev/null +++ b/TODO1/hermes.md @@ -0,0 +1,104 @@ +> * 原文地址:[Hermes: An open source JavaScript engine optimized for mobile apps, starting with React Native](https://code.fb.com/android/hermes/) +> * 原文作者:[Marc Horowitz](https://code.fb.com/android/hermes/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/hermes.md](https://github.com/xitu/gold-miner/blob/master/TODO1/hermes.md) +> * 译者: +> * 校对者: + +# Hermes: An open source JavaScript engine optimized for mobile apps, starting with React Native + +![](https://code.fb.com/wp-content/uploads/2019/07/HermesOSSChainReact_blog_FIN_1-1.gif) + +Mobile applications are growing larger and more complex. Larger apps using JavaScript frameworks often experience performance issues as developers add features and complexity. These issues are generated from various spots, but the people using these apps expect them to run smoothly, regardless of the device they are on. + +To increase the performance of Facebook’s apps, we have teams that continuously improve our JavaScript code and platforms. As we analyzed performance data, we noticed that the JavaScript engine itself was a significant factor in startup performance and download size. With this data in hand, we knew we had to optimize JavaScript performance in the more constrained environments of a mobile phone compared with a desktop or laptop. After exploring other options, we built a new JavaScript engine we call Hermes. It is designed to improve app performance, focusing on our React Native apps, even on mass-market devices with limited memory, slow storage, and reduced computing power. + +At [Chain React 2019](https://infinite.red/ChainReactConf), we announced the Hermes JavaScript engine. We have [open-sourced the Hermes engine](https://github.com/facebook/hermes), as well as [integration with Hermes for React Native](https://facebook.github.io/react-native/docs/hermes/). We are excited to work with the open source community and have developers start using Hermes today. + +## How Hermes improves React Native performance + +For JavaScript-based mobile applications, user experience benefits from attention to a few primary metrics: + +* The time it takes for the app to become usable, called time to interact (TTI) +* The download size (on Android, APK size) +* Memory utilization + +![](https://code.fb.com/wp-content/uploads/2019/07/hermesstats-1.jpg) + +Metrics for MatterMost React Native app running on a Google Pixel, similar in performance to popular phones in markets like India. + +Notably, our primary metrics are relatively insensitive to the engine’s CPU usage when executing JavaScript code. Focusing on these metrics leads to strategies and trade-offs that differ from most existing JavaScript engines today. Consequently, our team designed and built Hermes from scratch. As a result of this focus, our implementation provides substantial improvement for React Native applications.  + +Because Hermes is optimized for mobile apps, we do not have plans to integrate it with any browsers or with server infrastructure such as Node.js. Existing JavaScript engines remain preferable in those environments. + +## Key Hermes architectural decisions + +Mobile device limitations, such as smaller amounts of RAM and slower flash, led us to make certain architectural decisions. To optimize for this environment, we implemented the following: + +### Bytecode precompilation + +Commonly, a JavaScript engine will parse the JavaScript source after it is loaded, generating bytecode. This step delays the start of JavaScript execution. To skip this step, Hermes uses an ahead-of-time compiler, which runs as part of the mobile application build process. As a result, more time can be spent optimizing the bytecode, so the bytecode is smaller and more efficient. Whole-program optimizations can be performed, such as function deduplication and string table packing. + +The bytecode is designed so that at runtime, it can be mapped into memory and interpreted without needing to eagerly read the entire file. Flash memory I/O adds significant latency on many medium and low-end mobile devices, so loading bytecode from flash only when needed and optimizing bytecode for size leads to significant TTI improvements. In addition, because the memory is mapped read-only and backed by a file, mobile operating systems that don’t swap, such as Android, can still evict these pages under memory pressure. This reduces out-of-memory process kills on memory constrained devices. + +![](https://code.fb.com/wp-content/uploads/2019/07/HermesOSSChainReact_blog_FIN_1-1.gif) + +Although compressed bytecode is a bit larger than compressed JavaScript source code, because Hermes’s native code size is smaller, Hermes decreases overall application size for Android React Native apps. + +### No JIT + +To speed execution, most widely used JavaScript engines can lazily compile frequently interpreted code to machine code. This work is performed by a just-in-time (JIT) compiler. + +Hermes today has no JIT compiler. This means that Hermes underperforms some benchmarks, especially those that depend on CPU performance. This was an intentional choice: These benchmarks are generally not representative of mobile application workloads. We have done some experimentation with JITs, but we believe that it would be quite challenging to achieve beneficial speed improvements without regressing our primary metrics. Because JITs must warm up when an application starts, they have trouble improving TTI and may even hurt TTI. Also, a JIT adds to native code size and memory consumption, which negatively affects our primary metrics. A JIT is likely to hurt the metrics we care about most, so we chose not to implement a JIT. Instead, we focused on interpreter performance as the right trade-off for Hermes. + +### Garbage collector strategy + +On mobile devices, efficient use of memory is especially important. Lower-end devices have limited memory, OS swapping does not generally exist, and operating systems aggressively kill applications that use too much memory. When apps are killed, slow restarts are required and background functionality suffers. In early testing, we learned that virtual address (VA) space, especially contiguous VA space, can be a limited resource in large applications on 32-bit devices even with lazy allocation of physical pages.  + +To minimize memory and VA space used by the engine, we have built a garbage collector with the following features: + +* On-demand allocation: Allocates VA space in chunks only as needed. +* Noncontiguous: VA space need not be in a single memory range, which avoids resource limits on 32-bit devices. +* Moving: Being able to move objects means memory can be defragmented and chunks that are no longer needed are returned to the operating system. +* Generational: Not scanning the entire JavaScript heap every GC reduces GC times. + +## Developer experience + +To start using Hermes, developers will need to make a few changes to their `build.gradle` files and recompile the app. See the [full instructions for the migration to use Hermes on React Native.](https://facebook.github.io/react-native/docs/hermes/) + +```javascript + project.ext.react = [ + entryFile: "index.js", + enableHermes: true +] +``` + +### Lazy compilation + +Iteration speed is one of the main benefits of a JavaScript-based platform, but compiling bytecode in advance would chip away at this advantage. To keep reloads fast, Hermes debug builds don’t use ahead-of-time compilation; instead, they generate bytecode lazily on device. This allows for rapid iteration using Metro or another source of plain JavaScript code to run. The trade-off is that lazy-compiled bytecode does not include all the optimizations of a production build. In practice, although we can measure the difference in performance, we have found this approach is sufficient to provide a good developer experience without affecting production metrics. + +### Standards-compliant + +Hermes currently targets the ES6 specification, and we intend to keep current with the JavaScript specification as it evolves. To keep the engine’s size small, we have chosen not to support a few language features that do not appear to be commonly used in React Native apps, such as proxies and local `eval()`. A [complete list can be found at our GitHub](https://github.com/facebook/hermes/blob/master/doc/Features.md#excluded-from-support). + +### Debugging + +To provide a great debugging experience, we implemented support for Chrome remote debugging via the DevTools protocol. Until today, React Native has supported debugging using only an in-app proxy to run the application JavaScript code in Chrome. This support made it possible to debug apps, but not synchronous native calls in the React Native bridge. Support for the remote debugging protocol allows developers to attach to the Hermes engine running on their device and debug their applications natively, using the same engine as in production. We are also looking into implementing additional support for the Chrome DevTools protocol besides debugging. + +![](https://code.fb.com/wp-content/uploads/2019/07/Hermes-screenshot.jpg) + +## Enabling improvements to React Native + +To ease migration efforts to Hermes and continue supporting JavaScriptCore on iOS, we built JSI, a lightweight API for embedding a JavaScript engine in a C++ application. This API has made it possible for React Native engineers to implement their own infrastructure improvements. JSI is used by Fabric, which allows for preemption of React Native rendering, and by TurboModules, which allow for lighter weight native modules that can be lazy loaded as needed by a React Native application. + +React Native was our initial use case and has informed much of our work to date, but we aren’t stopping there. We intend to build time and memory profiling tools to make it easier for developers to improve their applications. We would like to fully support the Visual Studio Code debugger protocol, including completion and other features not available today. We’d also like to see other mobile use cases.  + +No open source project can be successful without engagement from the community. We’d love for you to [try Hermes in your React Native apps](https://facebook.github.io/react-native/docs/hermes/), see how it works, and help us [make Hermes better for everyone](https://github.com/facebook/hermes/issues). We are especially interested in seeing which use cases the community finds useful, both inside and outside of React Native. + +We’d like to thank Tzvetan Mikov, Will Holen, and the rest of the Hermes team for their work to build and open-source Hermes. + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From c691ae207d60574f357a010e03bca83649fe36f8 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 47/58] =?UTF-8?q?Revert=20"=E4=BD=BF=E7=94=A8=20Cypress=20?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=20React=20=E5=BA=94=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E7=AB=AF=E5=88=B0=E7=AB=AF=E6=B5=8B=E8=AF=95=20(#6137)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 208922e333d0bca2c97036a7bf80d3210c922bae. --- TODO1/testing-react-apps-with-cypress.md | 99 ++++++++++++------------ 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/TODO1/testing-react-apps-with-cypress.md b/TODO1/testing-react-apps-with-cypress.md index 1b968ba098c..b91e7068995 100644 --- a/TODO1/testing-react-apps-with-cypress.md +++ b/TODO1/testing-react-apps-with-cypress.md @@ -2,75 +2,75 @@ > * 原文作者:[Rajat S](https://medium.com/@geeky_writer_) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/testing-react-apps-with-cypress.md](https://github.com/xitu/gold-miner/blob/master/TODO1/testing-react-apps-with-cypress.md) -> * 译者:[stevens1995](https://github.com/stevens1995) -> * 校对者:[yzw7489757](https://github.com/yzw7489757),[Baddyo](https://github.com/Baddyo) +> * 译者: +> * 校对者: -# 使用 Cypress 进行 React 应用的端到端测试 +# End to End Testing React Apps With Cypress -> 关于如何使用 Cypress 对 React 应用进行端到端的测试的简要指南。 +> A brief guide on how to run End-To-End testing on React apps with Cypress. ![](https://cdn-images-1.medium.com/max/2562/1*FoCFnUGcQvE2zqiFxitXUg.png) -当我还是一个初级开发者的时候我经常害怕测试我的应用。测试并不容易。但是在正确工具的帮助下,编写测试代码绝对能够变得更容易和有趣。 +When I was a junior dev I used to cringe at the thought of testing my apps. Testing is not easy. But with the help of right tools, writing tests can certainly be simpler and more fun. -Cypress 是一个端到端的 JavaScript 测试框架,它使设置、编写、运行、调试测试变得非常简单。 +Cypress is a JavaScript End-to-End Testing Framework that makes it really simple to setup, write, run, and debug tests. -如果你已经尝试过类似 Puppeteer 的端到端测试框架,你会注意到这些框架把浏览器变成一个气隙系统。当我们的应用变得越复杂,测试也会变得越来越难通过。这也是大多数测试人员更喜欢手动运行测试的原因。 +If you have tried other End-to-End Testing Frameworks like Puppeteer, you will notice that these frameworks turn the browser into an air-gapped system. The more complex our app gets, the harder it will get to pass our tests. This is why most testers prefer to run there tests manually. -在本文中,我将向你展示 Cypress 如何帮助你构建在一个真实浏览器中运行的测试。Cypress 提供了一个非常易于使用的用于自动化测试的 API。 +In this post, I will show you how Cypress can help you build tests that will run in a real browser. Cypress provides with an API for test automation that is really easy to use. -相比于看着一个充满乱七八糟命令的平淡的终端,Cypress 带有自己的仪表盘,可以准确地向我们展示测试中发生了什么。而且,由于 Cypress 在真实的浏览器中工作,我们可以在使用 Cypress 的同时使用浏览器的开发工具。 +Instead of looking at a bland command terminal filled gibberish, Cypress comes with its own dashboard that will show us exactly what is happening during our tests. And, because Cypress works in the actual browser, we can also use the browser’s dev tools side-by-side with Cypress. -**提示**:使用 **React 组件**时,你可能想要记住组件单元测试的重要性。使用 [**Bit**](https://bit.dev/),你可以在你的项目中创造一个可重复使用的组件目录,并添加独立运行和展示可视化结果的组件测试。快试一试吧。 +**Tip**: When working with **React components** you might want to remember the importance of component unit-testing. Using [**Bit**](https://bit.dev/) you can create a reusable component catalog to use in your projects, and add component tests which will run in isolation and present with visual results. Check it out. -[**组件发现与协作 · Bit**](https://bit.dev/) +[**Component Discovery and Collaboration · Bit**](https://bit.dev/) -我们开始吧。 +Let’s dive in. --- -## 建立 +## Setup -我将使用一个已经存在的项目并在上面运行我的 Cypress 测试,而不是创建一个全新的项目。 +Instead of creating a whole new app, I am going to use a pre-existing project and run my Cypress tests on it. -这里,我有一个用 React 编写的简单的 ToDo 应用。 +Here, I have a simple ToDo app in React. [**rajatgeekyants/ToDo-List**](https://github.com/rajatgeekyants/ToDo-List) ![](https://cdn-images-1.medium.com/max/2000/1*sFtLSBvrjPNJkb66q1mIxQ.png) -在你的系统中克隆这个应用并且运行 `yarn install` 安装依赖。 +Clone this app into your system and run `yarn install` to install the dependencies. ``` $ git clone https://github.com/rajatgeekyants/ToDo-List.git $ yarn install ``` -**注意**:你也可以在 Bit 上查看这个应用。你可以在这里导入应用中任何特定的组件,而不需要关心其他部分。 +**Note**: You can also checkout the app on Bit. Here you will be able to import any particular component of the app, without having to care about the rest. [**todo by geekrajat · Bit**](https://bit.dev/geekrajat/todo) -有了这个,我现在可以进入应用的测试阶段。让我们安装 Cypress 作为应用的 `dev dependency`。 +With that out of the way, I can now get to the test phase of the app. Let’s install Cypress as a **dev dependency** to our app. ``` $ yarn add cypress -D ``` -现在打开 Cypress,我们所要做的就是运行这个命令。 +Now to open Cypress, all we have to do is run this command. ``` $ node_modules/.bin/cypress open ``` -这将在你的系统上打开 Cypress CLI(命令行界面)(或者仪表盘),并且在你应用的根目录创建一个 `cypress.json` 文件和 `cypress` 文件夹。`cypress` 文件夹就是我们将要编写测试的地方。 +This will open the Cypress CLI (or dashboard) on your system and also create a `cypress.json` file and a `cypress` folder in your app’s root directory. The `cypress` folder is where we will be writing our tests. -如果你觉得打开 Cypress 的命令太长或者太难记,你可以在 `package.json` 中创建一个新的脚本: +If you feel that the command to open Cypress is too long or hard to remember, you can go to `package.json` and create a new script: ``` "cypress": "cypress open" ``` -因此,如果你使用 NPM/Yarn 运行这个脚本,应该会打开 Cypress CLI(命令行界面)。在 Cypress 文件夹下的 integration 文件夹中创建一个新的测试文件。与普通的我们命名为类似 `App.test.js` 的测试文件不同,在 Cypress 中,测试文件的扩展名为是 `.spec.js`。 +So if you run this script with NPM/Yarn, it should open the Cypress CLI. Inside the `cypress` folder, create a test file inside the `integration` folder. Unlike your normal test files where we name them something like `App.test.js`, in Cypress, the extension for the test file is `.spec.js`. ```JavaScript describe ('First Test', () => { @@ -80,16 +80,17 @@ describe ('First Test', () => { }); ``` -这是一个非常简单的测试,只检查 `true` 是否等于 `true` (明显是)。如果你打开 Cypress CLI(命令行界面),你会看到新的测试文件自动列在那里。点击测试文件将会运行测试并且在浏览器中打开仪表盘,你可以在其中看到测试结果。 +This is a very simple test that is only check it `true` is equal to `true` (which it obviously is). If you open the Cypress CLI, you will see that the new test file will be automatically listed there. Clicking on it will run the test and open the dashboard in the browser where you will be able to see the test result. ![](https://cdn-images-1.medium.com/max/2000/1*Wh46Wi_P9a90q6nwwzKJ9w.png) -这个测试与 ToDo 应用无关。我只是展示下如何使用 Cypress 运行测试。现在我们开始编写我们实际应用的测试。 +This test had nothing to do with the ToDo app. It just showed how tests are run using Cypress. Let’s start writing test for our actual app now. + --- -## Cypress 中的页面访问 +## Page Visits in Cypress -Cypress 测试中的第一步是允许 Cypress 在浏览器中访问应用。让我们创建一个新的测试文件并在其中编写下面的代码。 +The first step in a Cypress test is to allow Cypress to visit the app in a browser. Let’s create a new test file and write the following code in it. ```JavaScript describe ('Second Test', () => { @@ -99,7 +100,7 @@ describe ('Second Test', () => { }); ``` -在上面的代码中,我有一个叫 `cy` 的对象。这是一个全局对象,使我们可以访问所有在 Cypress API 中展示的命令。我正在使用 `cy` 访问 `visit` 命令。在这个命令中,我将要传入 `'/'`。回到根目录,转到 `cypress.json` 文件,并在文件中写下这个: +In the above code, I have an object named `cy`. This is a global object and gives us access to all the commands present in the Cypress API. I am using `cy` to access the `visit` command. Inside this command I am just going to pass `'/'`. Back in the root directory, go to the `cypress.json` file and write this in there: ``` { @@ -107,19 +108,19 @@ describe ('Second Test', () => { } ``` -现在,确保你使用 `start` 脚本运行应用。然后打开 Cypress CLI(命令行界面)并运行这个新的测试文件。你会看到仪表盘在浏览器中打开,在仪表盘中我们的应用像这样运行: +Now, make sure that you are running the app using the `start` script. Next open the Cypress CLI and run this new test file. You will see the dashboard open in the browser, and inside the dashboard our app will run like this: ![](https://cdn-images-1.medium.com/max/2400/1*kpUn1HNHVpKEXAUNPOg3CA.png) -如果你注意到左边的命令日志,你会看到 Cypress 正在调用 XHR,以便让应用在其中打开。 +If you notice the command log on the left, you will see that Cypress is making an XHR call in order to get the app to open inside it. --- -## 检查焦点 +## Check For Focus -这里,我将要运行一个测试来检查加载后焦点是否在输入区域。 +Here, I am going to run a test that will check if the browser is focused on the input field when it loads. -在我们做这个之前,确保在 src/components/TodoList/index.js 中的 `输入(input)` 区域有值为 `new task` 的 `className` 属性以及 `autoFocus` 属性。 +Before we do this, make sure that the `input` field in src/components/TodoList/index.js has a `className` property of `new task` alongwith the `autoFocus` property. ``` { /> ``` -没有这些属性,我们的测试肯定会失败。创建一个新的包含下面代码的测试文件: +Without these properties, our test will definitely fail. Create a new test file with the following code: ```JavaScript describe ('Third Test', () => { @@ -141,15 +142,15 @@ describe ('Third Test', () => { }); ``` -首先,我在 Cypress 的仪表盘中 `visit`应用。一旦应用在仪表盘中打开,我就检查 `focused` 元素是否有 `new task` `类(class)`。 +First, I `visit` the app inside the Cypress Dashboard. Once the app opens inside the dashboard, I am checking if the `focused` element has a `class` named `new task`. ![](https://cdn-images-1.medium.com/max/2000/1*egylykedXJ2NpY0K0_t4Ag.png) --- -## 测试受控输入 +## Testing Controlled Input -在这个测试中,我将会检查受控输入是否接收文本并且正确设置其值。 +In this test, I am going to check if the controlled input accepts text and has its value set appropriately. ```JavaScript describe ('Third Test', () => { @@ -161,37 +162,37 @@ describe ('Third Test', () => { }); ``` -在这个测试中,我首先在 Cypress 仪表盘中 `visit` 应用。现在我想要 Cypress 在输入区域中输入一些内容。为了找到输入区域正确的选择器,点击 `Open Selector Playground` 按钮并且点击输入区域。 +In this test, I first `visit` the app inside the Cypress Dashboard. Now I want Cypress to type something inside the input field. In order to find the correct selector for input field, click on the `Open Selector Playground` button and click on the input field. ![](https://cdn-images-1.medium.com/max/2000/1*MfeHIpz2_SYwcUY0raMdaQ.gif) -获得输入区域的选择器之后,我将会让 Cypress 在里面输入一些文本。为了确保 Cypress 输入正确的文本,我使用了 `should` 命令。 +After getting the selector for the input field, I will make the cypress `type` some text inside it. To make sure Cypress has typed the correct text, I have used the `should` command. ![](https://cdn-images-1.medium.com/max/2000/1*pVSMDn4gdsvA3iWtucBhJg.png) --- -## 运行不包含任何 UI 的测试 +## Running Tests without any UI -在包含大量测试的情况下, UI 会使我们的应用运行起来很慢。反正在持续集成期间看不到任何 UI,那为什么还要加载它呢? +The UI can make our app run slowly in case of large number of tests. The UI is anyway not going to be seen during Continuous Integration, so why load it at all? -要在不启动任何 Cypress UI 的情况下运行我们的测试,我们首先在 `package.json` 文件中添加一个新的脚本。 +To run our tests without launching Cypress UI, we first add a new script in `package.json` file. ``` "cypress:all": "cypress run" ``` -通过运行这个脚本,Cypress 将会运行所有的测试,并直接在命令终端本身提供结果。 +By running this script, cypress will run all the tests and provide the results directly in the command terminal itself. ![](https://cdn-images-1.medium.com/max/2000/1*5ohUyAlbFGgkmO7_K2hI0w.png) -如果你担心自己实际上没有看到 Cypress 进行测试,Cypress 甚至会录制测试视频供你观看。 +If you are worried that you did not actually get to see Cypress do the testing, Cypress will even record a video of the test that you can watch. --- -## 在 Cypress 中创建端到端的测试 +## Create End-to-End Tests in Cypress -当我们将它用于集成测试时,Cypress 最有用。但是端到端测试可以确保整个应用不遗漏任何内容。 +Cypress is most useful when we use it for integration tests. But an End-to-End test makes sure that nothing is missed in the entire app. ```JavaScript describe ('Sixth Tests', () => { @@ -204,19 +205,19 @@ describe ('Sixth Tests', () => { }); ``` -这里,我创建了一个端到端测试。我首先让 Cypress `visit` 该应用。然后 Cypress 会用文本框(input)的选择器 .new 获取到它。再然后 Cypress 将会输入文本 `New Todo`。最后我让 Cypress 模拟键入 enter(回车),因此创造了一个新的 Todo。 +Here, I have created an end-to-end test. I first get Cypress to `visit` the app. Then Cypress will get the input using its selector `.new`. Cypress will then type the text `New Todo`. Finally I will get Cypress to mock an `enter` action, hence creating a new Todo. ![](https://cdn-images-1.medium.com/max/2000/1*wcIDOda8sVB_ROYIlCEdww.gif) --- -## 下一步是什么? +## What’s Next? -Cypress 还可以做许多其他事情。比如,我们可以测试一个功能的变化,或者我们可以访问测试的逐步日志。此外,Cypress 可以在 React 服务端渲染应用做许多其他事情,我将会在下一篇文章中介绍这些内容。 +There are many other things that we can do with Cypress. For example, we can test variations of a feature, or we can access step-by-step logs of our tests. Plus, there is a whole lot of other things that Cypress can do with a Server-Side Rendered React App that I will try to cover in my next post. --- -## 拓展学习 +## Learn more * [**5 Tools For Faster Development In React**](https://blog.bitsrc.io/5-tools-for-faster-development-in-react-676f134050f2) * [**Testing your React App with Puppeteer and Jest**](https://blog.bitsrc.io/testing-your-react-app-with-puppeteer-and-jest-c72b3dfcde59) From 0f1714a3be9cf9e11c6ad2a74e70c13454bbe79a Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 48/58] =?UTF-8?q?Revert=20"=E4=BD=BF=E7=94=A8=20Node.js=20?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E8=B6=85=E5=A4=A7=E7=9A=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=EF=BC=88=E7=AC=AC=E4=B8=80=E9=83=A8=E5=88=86=EF=BC=89=20(#6157?= =?UTF-8?q?)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 08b590f19a1a4b71b3611605470f06966de6bb23. --- ...-to-read-really-really-large-files-pt-1.md | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/TODO1/using-node-js-to-read-really-really-large-files-pt-1.md b/TODO1/using-node-js-to-read-really-really-large-files-pt-1.md index 86199ebb88c..e4af21e65ac 100644 --- a/TODO1/using-node-js-to-read-really-really-large-files-pt-1.md +++ b/TODO1/using-node-js-to-read-really-really-large-files-pt-1.md @@ -2,128 +2,128 @@ > * 原文作者:[Paige Niedringhaus](https://medium.com/@paigen11) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/using-node-js-to-read-really-really-large-files-pt-1.md](https://github.com/xitu/gold-miner/blob/master/TODO1/using-node-js-to-read-really-really-large-files-pt-1.md) -> * 译者:[lucasleliane](https://github.com/lucasleliane) -> * 校对者:[sunui](https://github.com/sunui),[Jane Liao](https://github.com/JaneLdq) +> * 译者: +> * 校对者: -# 使用 Node.js 读取超大的文件(第一部分) +# Using Node.js to Read Really, Really Large Files (Pt 1) ![](https://cdn-images-1.medium.com/max/3686/1*-Nq1fQSPq9aeoWxn4WFbhg.png) -这篇博文有一个非常有趣的启发点。上周,某个人在我的 Slack 频道上发布了一个编码挑战,这个挑战是他在申请一家保险技术公司的开发岗位时收到的。 +This blog post has an interesting inspiration point. Last week, someone in one of my Slack channels, posted a coding challenge he’d received for a developer position with an insurance technology company. -这个挑战激起了我的兴趣,这个挑战要求读取联邦选举委员会的大量数据文件,并且展示这些文件中的某些特定数据。由于我没有做过什么和原始数据相关的工作,并且我总是乐于接受新的挑战,所以我决定用 Node.js 来解决这个问题,看看我是否能够完成这个挑战,并且从中找到乐趣。 +It piqued my interest as the challenge involved reading through very large files of data from the Federal Elections Commission and displaying back specific data from those files. Since I’ve not worked much with raw data, and I’m always up for a new challenge, I decided to tackle this with Node.js and see if I could complete the challenge myself, for the fun of it. -下面是提出的四个问题,以及这个程序需要解析的数据集的链接。 +Here’s the 4 questions asked, and a link to the data set that the program was to parse through. -* 实现一个可以打印出文件总行数的程序。 -* 注意,第八列包含了人的名字。编写一个程序来加载这些数据,并且创建一个数组,将所有的名字字符串保存进去。打印出第 432 个以及第 43243 个名字。 -* 注意,第五列包含了格式化的时间。计算每个月的捐赠数,并且打印出结果。 -* 注意,第八列包含了人的名字。创建一个数组来保存每个 first name。标记出数据中最常使用的 first name,以及其出现的次数。 +* Write a program that will print out the total number of lines in the file. +* Notice that the 8th column contains a person’s name. Write a program that loads in this data and creates an array with all name strings. Print out the 432nd and 43243rd names. +* Notice that the 5th column contains a form of date. Count how many donations occurred in each month and print out the results. +* Notice that the 8th column contains a person’s name. Create an array with each first name. Identify the most common first name in the data and how many times it occurs. -数据的链接:[https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip](https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip) +Link to the data: ​[https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip](https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip) -当你解压完这个文件夹,你可以看到一个大小为 2.55 GB 的 `.txt` 主文件,以及一个包含了主文件部分数据的文件夹(这个是我在跑主文件之前,用来测试我的解决方案的)。 +When you unzip the folder, you should see one main `.txt` file that’s 2.55GB and a folder containing smaller pieces of that main file (which is what I used while testing my solutions before moving to the main file). -不是非常可怕,对吧?似乎是可行的。所以让我们看看我是怎么实现的。 +Not too terrible, right? Seems doable. So let’s talk about how I approached this. -#### 我想出来的两个原生 Node.js 解决方案 +#### The Two Original Node.js Solutions I Came Up With -处理大型文件对于 JavaScript 来说并不是什么新鲜事了,实际上,在 Node.js 的核心功能当中,有很多标准的解决方案可以进行文件的读写。 +Processing large files is nothing new to JavaScript, in fact, in the core functionality of Node.js, there are a number of standard solutions for reading and writing to and from files. -其中,最直接的就是 [`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback),这个方法会将整个文件读入到内存当中,然后在 Node 读取完成后立即执行操作,第二个选择是 [`fs.createReadStream()`](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options),这个方法以数据流的形式处理数据的输入输出,类似于 Python 或者是 Java。 +The most straightforward is [`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback) wherein, the whole file is read into memory and then acted upon once Node has read it, and the second option is [`fs.createReadStream()`](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options), which streams the data in (and out) similar to other languages like Python and Java. -#### 我使用的解决方案以及我为什么要使用它 +#### The Solution I Chose to Run With & Why -由于我的解决方案涉及到计算行的总数以及解析每一行的数据来获取捐赠名和日期,所以我选择第二个方法:`fs.createReadStream()`。然后在遍历文件的时候,我可以使用 [`rl.on('line',...)`](https://nodejs.org/api/readline.html#readline_event_line) 函数来从文件的每一行中获取必要的数据。 +Since my solution needed to involve such things as counting the total number of lines and parsing through each line to get donation names and dates, I chose to use the second method: `fs.createReadStream()`. Then, I could use the [`rl.on(‘line’,...)`](https://nodejs.org/api/readline.html#readline_event_line) function to get the necessary data from each line of code as I streamed through the document. -对我来说,这比将整个文件读入到内存中,然后再逐行读取更加简单。 +It seemed easier to me, than having to split apart the whole file once it was read in and run through the lines that way. -#### Node.js CreateReadStream() 和 ReadFile() 代码实现 +#### Node.js CreateReadStream() & ReadFile() Code Implementation -下面是我用 Node.js 的 `fs.createReadStream()` 函数实现的代码。我会在下面将其分解。 +Below is the code I came up with using Node.js’s `fs.createReadStream()` function. I’ll break it down below. ![](https://cdn-images-1.medium.com/max/2704/1*szFus-f7Xllx17AuSc_TQw.png) -我所要做的第一件事就是从 Node.js 中导入需要的函数:`fs`(文件系统),`readline`,以及 `stream`。导入这些内容后,我就可以创建一个 `instream` 和 `outstream` 然后调用 `readLine.createInterface()`,它们让我可以逐行读取流,并且从中打印出数据。 +The very first things I had to do to set this up, were import the required functions from Node.js: `fs` (file system), `readline`, and `stream`. These imports allowed me to then create an `instream` and `outstream` and then the `readLine.createInterface()`, which would let me read through the stream line by line and print out data from it. -我还添加了一些变量(和注释)来保存各种数据:一个 `lineCount`、`names` 数组、`donation` 数组和对象,以及 `firstNames` 数组和 `dupeNames` 对象。你可以稍后看到它们发挥作用。 +I also added some variables (and comments) to hold various bits of data: a `lineCount`, `names` array, `donation` array and object, and `firstNames` array and `dupeNames` object. You’ll see where these come into play a little later. -在 `rl.on('line',...)`函数里面,我可以完成数据的逐行分析。在这里,我为数据流的每一行都进行了 `lineCount` 的递增。我用 JavaScript 的 `split()` 方法来解析每一个名字,并且将其添加到 `names` 数组当中。我会进一步将每个名字都缩减为 first name,同时在 JavaScript 的 `trim()`,`includes()` 以及 `split()` 方法的帮助下,计算 middle name 的首字母,以及名字出现的次数等信息。然后我将时间列的年份和时间进行分割,将其格式化为更加易读的 `YYYY-MM` 格式,并且添加到 `dateDonationCount` 数组当中。 +Inside of the `rl.on('line',...)` function, I was able to do all of my line-by-line data parsing. In here, I incremented the `lineCount` variable for each line it streamed through. I used the JavaScript `split()` method to parse out each name and added it to my `names` array. I further reduced each name down to just first names, while accounting for middle initials, multiple names, etc. along with the first name with the help of the JavaScript `trim()`, `includes()` and `split()` methods. And I sliced the year and date out of date column, reformatted those to a more readable `YYYY-MM` format, and added them to the `dateDonationCount` array. -在 `rl.on('close',...)` 函数中,我对我收集到数组中的数据进行了转换,并且在 `console.log` 的帮助下将我的所有数据展示给用户。 +In the `rl.on('close',...)` function, I did all the transformations on the data I’d gathered into arrays and `console.log`ged out all my data for the user to see. -找到第 432 个以及第 43243 个下标处的 `lineCount` 和 `names` 不需要进一步的操作了。而找到最常出现的名字和每个月的捐款数量比较棘手。 +The `lineCount` and `names` at the 432nd and 43,243rd index, required no further manipulation. Finding the most common name and the number of donations for each month was a little trickier. -对于最常见的名字,我首先需要创建一个键值对对象用于存储每个名字(作为 key)和这个名字出现的次数(作为 value),然后我用 ES6 的函数 `Object.entries()` 来将其转换为数组。之后再对这个数组进行排序并且打印出最大值,就是一件非常简单的事情了。 +For the most common first name, I first had to create an object of key value pairs for each name (the key) and the number of times it appeared (the value), then I transformed that into an array of arrays using the ES6 function `Object.entries()`. From there, it was a simple task to sort the names by their value and print the largest value. -获取捐赠数量也需要一个类似的键值对对象,我们创建一个 `logDateElements()` 函数,我们可以使用 ES6 的字符串插值来展示每个月捐赠数量的键值。然后,创建一个 `new Map()` 将 `dateDonations` 对象转换为嵌套数组,并且对于每个数组元素调用 `logDateElements()` 函数。呼!并不像我开始想的那么简单。 +Donations also required me to make a similar object of key value pairs, create a `logDateElements()` function where I could nicely using ES6’s string interpolation to display the keys and values for each donation month. And then create a `new Map()` transforming the `dateDonations` object into an array of arrays, and looping through each array calling the `logDateElements()` function on it. Whew! Not quite as simple as I first thought. -至少对于我测试用的 400 MB 大小的文件是奏效的…… +But it worked. At least with the smaller 400MB file I was using for testing… -在用 `fs.createReadStream()` 方法完成后,我回过头来尝试使用 `fs.readFile()` 来实现我的解决方案,看看有什么不同。下面是这个方法的代码,但是我不会在这里详细介绍所有细节。这段代码和第一个代码片十分相似,只是看起来更加同步(除非你使用 `fs.readFileSync()` 方法,但是不用担心,JavaScript 会和运行其他异步代码一样执行这段代码)。 +After I’d done that with `fs.createReadStream()`, I went back and also implemented my solutions with `fs.readFile()`, to see the differences. Here’s the code for that, but I won’t go through all the details here — it’s pretty similar to the first snippet, just more synchronous looking (unless you use the `fs.readFileSync()` function, though, JavaScript will run this code just as asynchronously as all its other code, not to worry. ![](https://cdn-images-1.medium.com/max/2704/1*mLYx43qMKJBpbZ8TUp_qrA.png) -如果你想要看我的代码的完整版,可以在[这里](https://github.com/paigen11/file-read-challenge)找到。 +If you’d like to see my full repo with all my code, you can see it [here](https://github.com/paigen11/file-read-challenge). -#### Node.js 的初始结果 +#### Initial Results from Node.js -使用我的解决方案,我将传入到 `readFileStream.js` 的文件路径替换成了那个 2.55 GB 的怪物文件,并且看着我的 Node 服务器因为 `JavaScript heap out of memory` 错误而崩溃。 +With my working solution, I added the file path into `readFileStream.js` file for the 2.55GB monster file, and watched my Node server crash with a `JavaScript heap out of memory` error. ![Fail. Whomp whomp…](https://cdn-images-1.medium.com/max/5572/1*S26hQHQCuzlPDHMnDR_s3g.png) -事实证明,虽然 Node.js 采用流来进行文件的读写,但是其仍然会尝试将整个文件内容保存在内存中,而这对于这个文件的大小来说是无法做到的。Node 可以一次容纳最大 1.5 GB 的内容,但是不能够再大了。 +As it turns out, although Node.js is streaming the file input and output, in between it is still attempting to hold the entire file contents in memory, which it can’t do with a file that size. Node can hold up to 1.5GB in memory at one time, but no more. -因此,我目前的解决方案都不能够完成这整个挑战。 +So neither of my current solutions was up for the full challenge. -我需要一个新的解决方案。一个基于 Node 的,能够处理更大的数据集的解决方案。 +I needed a new solution. A solution for even larger datasets running through Node. -#### 新的数据流解决方案 +#### The New Data Streaming Solution -[`EventStream`](https://www.npmjs.com/package/event-stream) 是一个目前很流行的 NPM 模块,它每周有超过 200 万的下载量,号称能够“让流的创建和使用更加简单”。 +I found my solution in the form of [`EventStream`](https://www.npmjs.com/package/event-stream), a popular NPM module with over 2 million weekly downloads and a promise “to make creating and working with streams easy”. -在 EventStream 文档的帮助下,我再次弄清楚了如何逐行读取代码,并且以更加 CPU 友好的方式来实现。 +With a little help from EventStream’s documentation, I was able to figure out how to, once again, read the code line by line and do what needed to be done, hopefully, in a more CPU friendly way to Node. -#### EventStream 代码实现 +#### EventStream Code Implementation -这个是我使用 EventStream NPM 模块实现的新代码。 +Here’s my code new code using the NPM module EventStream. ![](https://cdn-images-1.medium.com/max/2704/1*iZFzB0v46FoAaMTR0ANrCQ.png) -最大的变化是以文件开头的管道命令 —— 所有这些语法,都是 EventStream 文档所建议的方法,通过 `.txt` 文件每一行末尾的 `\n` 字符来进行流的分解。 +The biggest change was the pipe commands at the beginning of the file — all of that syntax is the way EventStream’s documentation recommends you break up the stream into chunks delimited by the `\n` character at the end of each line of the `.txt` file. -我唯一改变的内容是修改了 `names` 的结果。我不得不实话实说,因为我尝试将 1300 万个名字放到数组里面,结果还是发生了内存不足的问题。我绕过了这个问题,只收集了第 432 个和第 43243 个名字,并且将它们加入到了它们自己的数组当中。并不是因为其他什么原因,我只是想有点自己的创意。 +The only other thing I had to change was the `names` answer. I had to fudge that a little bit since if I tried to add all 13MM names into an array, I again, hit the out of memory issue. I got around it, by just collecting the 432nd and 43,243rd names and adding them to their own array. Not quite what was being asked, but hey, I had to get a little creative. -#### Node.js 和 EventStream 的实现成果:第二回合 +#### Results from Node.js & EventStream: Round 2 -好了,新的解决方案实现好了,又一次,我使用 2.55 GB 的文件启动了 Node.js,同时双手合十起到这次能够成功。来让我们看看结果。 +Ok, with the new solution implemented, I again, fired up Node.js with my 2.55GB file and my fingers crossed this would work. Check out the results. ![Woo hoo!](https://cdn-images-1.medium.com/max/2000/1*HJBlTYxNUCPXCDeKI9RTMg.png) -成功了! +Success! -#### 结论 +#### Conclusion -最后,Node.js 的纯文件和大数据处理功能与我需要的能力还有些差距,但是只要使用一个额外的 NPM 模块,比如 EventStream,我就能够解析巨大的数据而不会造成 Node 服务器的崩溃。 +In the end, Node.js’s pure file and big data handling functions fell a little short of what I needed, but with just one extra NPM package, EventStream, I was able to parse through a massive dataset without crashing the Node server. -请继续关注本系列的[第二部分](https://bit.ly/2JdcO2g),我对在 Node.js 中读取数据的三种方式的性能进行了测试和比较,看看哪一种方式的性能能够优于其他方式。结果变得非常瞩目 —— 特别是随着数据量的变大…… +Stay tuned for [part two](https://bit.ly/2JdcO2g) of this series where I compare my three different ways of reading data in Node.js with performance testing to see which one is truly superior to the others. The results are pretty eye opening — especially as the data gets larger… -感谢你的阅读,我希望本文能够帮助你了解如何使用 Node.js 来处理大量数据。感谢你的点赞和关注! +Thanks for reading, I hope this gives you an idea of how to handle large amounts of data with Node.js. Claps and shares are very much appreciated! -**如果您喜欢阅读本文,你可能还会喜欢我的其他一些博客:** +**If you enjoyed reading this, you may also enjoy some of my other blogs:** -* [Postman vs. Insomnia:API 测试工具的比较](https://medium.com/@paigen11/postman-vs-insomnia-comparing-the-api-testing-tools-4f12099275c1) -* [如何使用 Netflix 的 Eureka 和 Spring Cloud 来进行服务注册](https://medium.com/@paigen11/how-to-use-netflixs-eureka-and-spring-cloud-for-service-registry-8b43c8acdf4e) -* [Jib:在不了解 Docker 的情况下得到专家级的 Docker 成果](https://medium.com/@paigen11/jib-getting-expert-docker-results-without-any-knowledge-of-docker-ef5cba294e05) +* [Postman vs. Insomnia: Comparing the API Testing Tools](https://medium.com/@paigen11/postman-vs-insomnia-comparing-the-api-testing-tools-4f12099275c1) +* [How to Use Netflix’s Eureka and Spring Cloud for Service Registry](https://medium.com/@paigen11/how-to-use-netflixs-eureka-and-spring-cloud-for-service-registry-8b43c8acdf4e) +* [Jib: Getting Expert Docker Results Without Any Knowledge of Docker](https://medium.com/@paigen11/jib-getting-expert-docker-results-without-any-knowledge-of-docker-ef5cba294e05) --- -**引用和继续阅读资源:** +**References and Further Resources:** -* Node.js 文档,文件系统:[https://nodejs.org/api/fs.html](https://nodejs.org/api/fs.html) -* Node.js 文档,Readline:[https://nodejs.org/api/readline.html#readline_event_line](https://nodejs.org/api/readline.html#readline_event_line) -* Github, Read File Repo:[https://github.com/paigen11/file-read-challenge](https://github.com/paigen11/file-read-challenge) -* NPM, EventSream:[https://www.npmjs.com/package/event-stream](https://www.npmjs.com/package/event-stream) +* Node.js Documentation, File System: [https://nodejs.org/api/fs.html](https://nodejs.org/api/fs.html) +* Node.js Documentation, Readline: [https://nodejs.org/api/readline.html#readline_event_line](https://nodejs.org/api/readline.html#readline_event_line) +* Github, Read File Repo: [https://github.com/paigen11/file-read-challenge](https://github.com/paigen11/file-read-challenge) +* NPM, EventSream: [https://www.npmjs.com/package/event-stream](https://www.npmjs.com/package/event-stream) > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 7d8a56ab346e7913881d85c982f4cda80b4eb736 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 49/58] =?UTF-8?q?Revert=20"=E4=BB=85=E4=BD=BF=E7=94=A8=20H?= =?UTF-8?q?TML=20=E5=92=8C=20CSS=20=E5=88=9B=E5=BB=BA=E5=A4=9A=E7=BA=A7?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=BC=B9=E5=87=BA=E5=BC=8F=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=20(#6154)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 7e0f1a2af63fd370de730c714e0aac3b8b46e1fa. --- ...navigation-menu-using-only-html-and-css.md | 166 +++++++++--------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md b/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md index 3cd26ce1a97..ac466bf2381 100644 --- a/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md +++ b/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md @@ -2,48 +2,48 @@ > * 原文作者:[Abhishek Ghosh](https://www.ghosh.dev/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md](https://github.com/xitu/gold-miner/blob/master/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md) -> * 译者:[Seven](https://github.com/yzw7489757) -> * 校对者:[Pingren](https://github.com/Pingren) +> * 译者: +> * 校对者: -# 仅使用 HTML 和 CSS 创建多级嵌套弹出式导航菜单 +# Creating a multi-level hierarchical flyout navigation menu using only HTML and CSS -![alt](https://www.ghosh.dev/static/media/css-nav-menu-1.jpg) +![](https://www.ghosh.dev/static/media/css-nav-menu-1.jpg) -今天,我将为你提供一个关于如何创建分层导航弹出式菜单的快速教程,该菜单可以跨多个级别进行深层嵌套。 +Today I am going to give you a quick tutorial on how to create a hierarchical navigation flyout menu that can go nested deep down across multiple levels. -作为抛砖引玉,我们将从一个具体的实际用例开始 —— 一个桌面应用程序的示例菜单栏。我将选择 Chrome 浏览器菜单栏中的一个子列表来说明这一点。 +As an inspiration, we’ll start off with a concrete practical use-case of an example menu bar for a desktop application. I’ll pick a subset of the Chrome browser’s menu bar to illustrate this. -我们将从一个简单的界面和外观入手,源自经典的 Windows™ 主题,这里有个短视频告诉你它长什么样: +We’ll begin with a simple quite look-and-feel, something that goes back to the classic Windows™ theme. Here’s a short video on how that would look like: [css-nav-menu-3.mp4](https://www.ghosh.dev/static/media/css-nav-menu-3.mp4) -在最后,我们会增加一些样式,让它有点像 MacOS™ 的感觉。 +Towards the end, we’ll make it a bit fancier by adding some more styling to give it a MacOS™ like feel. -### 基础 +### The Basics -让我们先了解一下菜单项通常由什么组成。它们应该具有以下属性: +Let’s start off by understanding what our menu items would typically constitute of. They should have the following properties: -* **Label**:(**必选**)这基本上是菜单项的显示名称 -* **Target**:(**可选**)超链接,将用户带到一个页面,作为对单击菜单项的响应。我们现在将坚持它只是链接。在页面中添加更多的动态特性需要用到JavaScript,我们暂时不需要这么做。这是你以后可以随时轻松添加的东西。 -* **Shortcut**:(**可选**)在我们的例子中,显示一个可用于此菜单项的快捷键组合。例如,“文件 > 新建”在Mac上会是 “Cmd + N”(⌘N)。 -* **Children**:(**可选**)指的是此菜单项的子菜单。想想我们的菜单和子菜单的形式 **递归结构**,从视觉效果来说,具有子菜单的菜单项上还应具有箭头图标 (▶)指示悬停时它可以展开。 -* **Disabled**:(**可选**)指示菜单项是否可以进行交互。 -* 一个概念 **Type** 参数吗?(**可选**)可以用这个模拟不同类型的菜单项。比如,菜单列表中的一些条目应该只起分隔符的作用。 +* **Label**: (**required**) which is basically the name of the menu item that is displayed +* **Target**: (**optional**) a hyperlink that takes the user to a page as a response to clicking on the menu item. We’ll stick to just links right now. Adding more dynamic in-page features would require JavaScript which we’ll stay away from at the moment. It’s something you can always go and easily add later. +* **Shortcut**: (**optional**) in our case, displays a keyboard shortcut that could be used for this menu item. For example, “File > New” would be “Cmd + N” (⌘N) on Mac. +* **Children**: (**optional**) which refers to the sub-menu for this menu item. Think of our menus and sub-menus in the form of a **recursive structure**. Visually, a menu item having a sub-menu should also have an arrow icon on it (▶) to indicate that it can expand when hovered. +* **Disabled**: (optiona), a state indicating if the menu item can be interacted with. +* A conceptual **Type** parameter? (**optional**) that could emulate different types of menu items with this. Say, some entries in the list of menus should act as just a **separator** line. -请注意,我们可以继续向菜单添加更复杂的行为。例如,某个菜单可以是一个 **切换** 项,所以,需要某种形式的记号(✔)或与之关联的复选框,以指示其打开/关闭状态。 +Note that we could go ahead and add more complex behaviours to our menus. For example, a certain menu could be a **Toggle** item, and so, would need to have some form of a tick mark (✔) or checkbox associated with it to indicate its on/off state. -我们将使用 **CSS classes** 在 HTML 标记上指示这些属性,并编写一些巧妙的样式来传递所有相应的行为。 +We’ll use **CSS classes** on our HTML markup to indicate such properties and write some clever styling to impart all the corresponding behaviours. -### 构建 HTML +### Structuring the HTML -基于上文,我们的基本菜单 HTML 应该是什么样子: +Based on the above, this is how our basic menu HTML should look like: -1. 菜单列表由 HTML `ul` 元素定义,单个菜单项当然是 `li`。 -2. **label** 和 **shortcut** 将作为 `span` 元素放置在 `li` 中的锚(`a`)标签内并带有相应 CSS 类(`label` 或 `shortcut`),所以点击它会调用导航事件,还可以提供一些 UI 反馈,例如在 **Hover** 时突出显示菜单项。 -3. 当菜单项目包含一栏 **子菜单**(Children)们将该子菜单放在当前菜单 `li` 元素(父)中的另一个 `ul` 元素中,依此类推。这个特定的菜单项包含一个子菜单,并且能够添加一些特定的样式以使其正常工作(以及诸如 ▶ 指示符之类的可视元素,)们将向 `li` 此父级添加 `has-children` CSS 类。 -4. 对于像这样的子项 **分隔符**,我们将在 `li` 上中添加一个名为 `separator` 的相应 CSS 类来表示它。 -5. 菜单项可以被 **禁用**,在这种情况下,我们将添加相应的 `disabled` CSS 类。它的作用是使此项无法响应鼠标事件,如悬停或点击。 -6. 我们将把所有东西包装在一个 HTML `nav` 容器元素中。(这样[语义化](https://en.wikipedia.org/wiki/Semantic_HTML)很好)并为其添加 `flyout-nav` 类,以获取我们将添加的CSS样式的一些基本命名空间。 +1. A list of menus is defined by an HTML `ul` element, with individual items being the obvious `li`. +2. The **label** and **shortcut** will be placed as `span` elements with their corresponding CSS classes (`label` or `shortcut`) inside an anchor (`a`) tag inside the `li`, so that clicking on it causes the navigation action, as well as be able to provide some UI feedback such as highlighting the menu item on **hover**. +3. When a menu item contains a list of **sub-menu** (children), we’ll put that sub-menu in another `ul` element inside the current menu `li` element (parent) and so on. To describe that this particular menu item contains a sub-menu and also be able to add some specific styling to make it functional (as well as visual elements like the ▶ indicator), we’ll add the `has-children` CSS class to this parent `li`. +4. For items like the **separator**, we’ll add a corresponding CSS class called `separator` to the `li` item denoting it. +5. A menu item can be **disabled**, in which case we’ll add the corresponding `disabled` CSS class. It’s job is to make this item non-responsive to pointer events like hover or clicks. +6. We’ll wrap everything off inside a container HTML `nav` element (it’s good to be [semantic](https://en.wikipedia.org/wiki/Semantic_HTML)) and add the `flyout-nav` class to it for some basic namespacing of the CSS styles that we’ll add. ```html ``` -### 在 CSS 中添加行为 +### Adding behaviours in CSS -我撒了谎。我们将使用 [SCSS](https://sass-lang.com/) 代替。 +I lied. We’ll use [SCSS](https://sass-lang.com/) instead. -不开玩笑了,有趣的部分来了! +Jokes aside, here comes the interesting part! -默认情况下应该 **隐藏** 菜单(第一级 `导航菜单条` 除外)。 +The menu (except the first-level “horizontal bar”), should be **hidden** by default. -只有在使用鼠标指针悬停相应的菜单项时,才应显示第一级下的任何内容。你可能已经猜到了,为了这个我们将严重依赖 CSS 的 [`hover`伪类](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover)。 +Anything below the first level should only be displayed when the corresponding menu item is hovered upon using the mouse pointer. As you may have already guessed, we’ll heavily rely on the CSS [`hover` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover) for this. -#### 排列菜单和子菜单元素 +##### Arranging menu and sub-menu elements -理解我们如何使子菜单位置的正确并将其自身与父菜单项对齐也许是整个谜题中最棘手的一点。这就是 CSS [定位](https://developer.mozilla.org/en-us/docs/web/css/position)的一些知识来源。让我们看看这个。 +Perhaps the trickiest bit in this whole puzzle is to understand how we make the sub-menu position and align itself with respect to their parent menu item correctly. This is where some knowledge of CSS [positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/position) comes in. Let’s look at that. -我们之所以选择将子菜单 `ul` 元素放在“父” `li` 元素中是有原因的。当然,它有助于我们在逻辑上适当地将分层内容的标记组合在一起。它还有另一个目的,即允许我们轻松编写一些 CSS 来**相对**于父元素的位置定位子元素。然后我们将这个概念一直延伸到根元素 `ul` 和 `li`。 +There was a reason why we chose to put the sub-menu `ul` element inside a “parent” `li` element. Of course, it helps us to logically appropriately put together the markup for our hierarchical content, but it also serves another purpose of allowing us to easily write some CSS to position a child element **relative** to the position of a parent element. Then we take this concept all the way to the root `ul` and `li` elements. -为此,我们将使用 `absolute` 定位和 `top` 的组合,`left` CSS 属性将帮助我们相对于其最近的**非静态定位祖先(closest non-static positioned ancestor)** 定位子元素定义[包含块](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block)。非静态(non-static)的意思是元素的 CSS position 属性不是 `static`(这默认发生在 HTML 文档流中),但它是 `relative`、`absolute`、`fixed` 或者 `sticky` 其中之一。为了确保这一点,我们将把 position `relative` 分配给 `li` 元素,并将其子元素 `ul` 的 position 设置为 `absolute`。 +For doing this, we’ll use a combination of `absolute` positioning and `top`, `left` CSS properties that will help us to position a child element relative to its **closest non-static positioned ancestor** defining the [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block). By non-static, we mean that the CSS position property for an element is not `static` (which happens by default in the HTML document flow), but instead is one of `relative`, `absolute`, `fixed` or `sticky`. To make sure of that, we’ll assign the position `relative` to the `li` elements with their child `ul` elements positioned `absolute`. ```scss .flyout-nav { - // 任何级别的菜单项列表 + // list of menu items at any level ul { margin: 0; padding: 0; @@ -116,13 +116,13 @@ list-style-type: none; } - // 菜单项 + // a menu item li { position: relative; display: block; - // 显示上的下一级下拉列表 - // 在同一高度的右边 + // show the next level drop-down on + // the right at the same height &:hover { & > ul { display: block; @@ -133,25 +133,25 @@ } ``` -其效果如下图所示,并在红色框中突出显示以供说明。为了使图片看起来更漂亮,我们在图片中添加了一些用于视觉样式的 CSS,但是核心行为是由上面的内容定义的。这使其在 N 层嵌套内(在实用性的限制范围内)保持良好的工作状态。 +The effect of this is shown in the image below, highlighted in the red box for illustration. Some additional CSS for visual styling has been done in the image to make it look all nice, but the core behaviour is defined by what we have above. This keeps working great to N-levels deep (within limits of practicality). -![子菜单位置](https://www.ghosh.dev/static/media/css-nav-menu-4.jpg) +![Sub-menu positioning](https://www.ghosh.dev/static/media/css-nav-menu-4.jpg) -但有一个例外,即第一级菜单项列表(在我们的示例中,File、Edit、View...),其子菜单项需要放在 **下方** 而不是右侧。为了处理这个问题,我们添加了一些新的样式重写了之前的 CSS。 +There’s one exception to this though, which is the first-level list of menu items (File, Edit, View… in our example), whose children menu items need to be positioned **below** instead of right. To handle that, we add some style overrides to our previous CSS. ```scss .flyout-nav { - // ... 其他的东西 + // ... other stuff - // 一级行为的覆盖(导航菜单条) + // overrides for first-level behaviour (horizontal bar) & > ul { display: flex; flex-flow: row nowrap; justify-content: flex-start; align-items: stretch; - // 应显示第一级下拉列表 - // 在同一左侧位置 + // first-level drop-down should appear + // below at the same left position & > li:hover > ul { top: 100%; left: 0; @@ -160,18 +160,18 @@ } ``` -请注意,在这里不一定非要使用弹性盒子 `flex-box`,这只是我做的选择。你也可以使用其他方法实现类似的行为,例如在 `ul` 和 `li` 项上组合 `display: block` 和 `display: inline-block`。 +Note that using a flex-box here was not imperative, rather just something I did out of choice. You could achieve similar behaviour using other approaches such as a combination of `display: block` and `display: inline-block` on the `ul` and `li` items as well. -##### UI 美化 +##### [](#UI-polishing)UI polishing -一旦我们完成了对菜单项定位的基本操作,我们将继续编写一些额外的样式,如字体、大小、颜色、背景和阴影等,以使 UI 感觉更好。 +Once we’re done handling the basics of positioning the menu items, we’ll go on about writing some additional styles such as fonts, sizes, colours, backgrounds, shadow and such for making the UI feel all nice and better. -为了一致性和重用,我们采取使用一组 SCSS 变量定义和共享了这些值。像这样... +For consistency and reuse, let’s also assume that we have such values defined and shared using a bunch of SCSS variables. Something like… ```scss -// 变量 +// variables $page-bg: #607d8b; -$base-font-size: 16px; // 变成 1rem +$base-font-size: 16px; // becomes 1rem $menu-silver: #eee; $menu-border: #dedede; $menu-focused: #1e88e5; @@ -187,18 +187,18 @@ $menu-border-radius: 0.5rem; $menu-top-padding: 0.25rem; ``` -我们还剩下一些部分要添加合适的样式和特性。我们现在将会快速地把它们过一遍。 +There are some pieces that we’re left adding the appropriate styles and behaviours for. We’ll go over them quickly now. -##### Anchors、Labels 和 Shortcuts —— 真正的视觉元素 +##### Anchors, Labels and Shortcuts - the actual visual elements ```scss .flyout-nav { - // ... 其他的东西 + // ... other stuff li { - // ... 其他的东西 + // ... other stuff - // 菜单项-文本、快捷方式信息和悬停效果(蓝色背景) + // the menu items - text, shortcut info and hover effect (blue bg) a { text-decoration: none; color: $menu-text-color; @@ -221,7 +221,7 @@ $menu-top-padding: 0.25rem; cursor: pointer; } - // 对于切换的菜单项 + // for menu items that are toggles input[type='checkbox'] { display: none; } @@ -248,13 +248,13 @@ $menu-top-padding: 0.25rem; } ``` -这段代码的大部分内容都是简单明了的。但是,你注意到什么有趣的事情了吗?关于 `input[type='checkbox']` ? +Most of this code is pretty self-explanatory. However, did you notice anything interesting? The bit about `input[type='checkbox']`? -##### 切换项 +##### [](#Toggle-Items)Toggle Items -对于切换,我们使用隐藏的 HTML 复选框元素来维护状态(打开或关闭)并相应地使用 [`::before`伪元素](https://developer.mozilla.org/en-US/docs/Web/CSS/::before)为标签设置样式。我们可以使用一个简单的 CSS [相邻兄弟选择器](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator)来做到这一点。 +For toggles, we use a hidden HTML `checkbox` element to maintain state (on or off) and style the `label` with [`::before` pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) accordingly. We are able to do that using a simple CSS [adjacent sibling selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator). -该菜单项的相应 HTML 标记如下所示: +The corresponding HTML markup for that menu item would look something like this: ```html
  • @@ -266,16 +266,16 @@ $menu-top-padding: 0.25rem;
  • ``` -##### 分隔符 +##### Separators ```scss .flyout-nav { - // ... 其他的东西 + // ... other stuff li { - // ... 其他的东西 + // ... other stuff - // 分隔符项 + // the separator item &.separator { margin-bottom: $menu-top-padding; border-bottom: $menu-border-width solid $menu-separator; @@ -285,17 +285,17 @@ $menu-top-padding: 0.25rem; } ``` -##### 禁用 +##### Disabled ```scss .flyout-nav { - // ... 其他的东西 + // ... other stuff li { - // ... 其他的东西 + // ... other stuff - // 不要让禁用的选项响应 hover - // 或者点击并给它们涂上不同的颜色 + // don't let disabled options respond to hover + // or click and color them different &.disabled { .label, .shortcut { @@ -307,28 +307,28 @@ $menu-top-padding: 0.25rem; } ``` -CSS [pointer-events](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) 在这有个实用的技巧。将其设置为 `none` 将变成不可选的鼠标事件目标对象。 +CSS [pointer-events](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) does the actual trick here. Setting it to `none` makes it invisible as a target for any pointer events. -### 把它们组合一起... +### Putting it all together… -现在我们已经了解了这些构造块,让我们把它们组合一起。这里有一个 CodePen 链接到我们的多层次弹出式导航菜单的行动! +Now that we’ve gained some understanding of the building blocks, let’s put it all together. Here’s a Codepen link to our multi-level hierarchical flyout navigation menu in action! -示例:[仅限于CSS的多级嵌套弹出式导航菜单](https://codepen.io/abhishekcghosh/pen/WqjOaX) +Demo:[CSS-only multi-level hierarchical navigation flyout menu](https://codepen.io/abhishekcghosh/pen/WqjOaX) -#### 更漂亮的主题 +##### Fancier theming -如果你不喜欢复古 Windows 的外观,这是同一代码的另一个版本,对 CSS 进行了一些细微的调整,使其看起来和感觉更像 MacOS。 +If you are not a fan of the retro Windows look, here’s another version of the same code with some minor tweaks to the CSS to make it look and feel more like MacOS. -示例:[仅限于 CSS 的多级嵌套弹出式导航菜单(类似于 MacOS)](https://codepen.io/abhishekcghosh/pen/qzmEWd) +Demo:[CSS-only multi-level hierarchical navigation flyout menu (MacOS lookalike)](https://codepen.io/abhishekcghosh/pen/qzmEWd) -### 什么不管用? +### What doesn’t work? -有一些事情我们还没有处理。首先, +There are a few things we haven’t handled. For starters, -* 如果你对此非常挑剔的话,虽然大多数效果都很好,但刻意只使用 CSS 的方法有局限性,与现实世界的 Windows 和 MacOS 应用程序菜单不同,我们的菜单会在鼠标移出外部时立即隐藏。为了使用起来更方便,通常我们想要做的是在点击之后再隐藏(总是可以用一点 JS 来实现)。 -* 如果菜单中的项目列表太长怎么办?以书签列表为例。在某些情况下,可能需要将其限制在可滚动视图中,例如按视口高度的某个百分比表示。归根结底,它取决你正在构建的用户体验,但我也想把这些讲清楚。 +* If you’re nitpicky about it, while most of the behaviour works great, a limitation of the deliberate CSS-only approach is that unlike the real-world Windows and MacOS application menus, our menu hides immediately as soon as the pointer goes outside. For more comfortable usage, typically what we’d want to do is wait for a click before hiding (can be always achieved with a bit of JS). +* What if the list of items in a menu is super long? Imagine a bookmarks list as an example. At some point, it might need to be capped into a scrollable view, say at some percentage of the viewport height. At the end of the day, it’s really a choice of the user experience you’re building, but something I wanted to put out there as well. -希望这是有用的。干杯! +Hope this was useful. Cheers! > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From d77be53586b821deb55b2742227a10da171e669d Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 50/58] =?UTF-8?q?Revert=20"=E5=89=8D=E7=AB=AF=20vs=20?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=EF=BC=9A=E5=93=AA=E4=B8=80=E4=B8=AA=E9=80=82?= =?UTF-8?q?=E5=90=88=E4=BD=A0=EF=BC=9F=20(#6164)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a5a79d007159778540e3a4be44edd3deed00ac91. --- ...d-vs-backend-which-one-is-right-for-you.md | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/TODO1/frontend-vs-backend-which-one-is-right-for-you.md b/TODO1/frontend-vs-backend-which-one-is-right-for-you.md index 2382642943f..bd78bd06e9f 100644 --- a/TODO1/frontend-vs-backend-which-one-is-right-for-you.md +++ b/TODO1/frontend-vs-backend-which-one-is-right-for-you.md @@ -2,68 +2,68 @@ > * 原文作者:[Molly Struve](https://dev.to/molly_struve) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/frontend-vs-backend-which-one-is-right-for-you.md](https://github.com/xitu/gold-miner/blob/master/TODO1/frontend-vs-backend-which-one-is-right-for-you.md) -> * 译者:[YueYong](https://github.com/YueYongDev) -> * 校对者:[Chorer](https://github.com/Chorer),[Zavier Tang](https://github.com/ZavierTang) +> * 译者: +> * 校对者: -# 前端 vs 后端:哪一个适合你? +# Frontend vs Backend: Which One Is Right For You? ![](https://res.cloudinary.com/practicaldev/image/fetch/s--sQXuMr9C--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/xtuhivk785yvj2pden2g.png) -经常会有初学者来问我刚开始学习编程的时候应该学些什么?问这个问题就跟一个医学生询问应该专注研究哪个领域一样。根本没有一个标准答案。但我还是想提供一些指导,并就这个问题提出一些自己的看法。希望这篇文章可以给刚开始职业生涯的你一些值得思考的东西。 +I have been asked many times by new developers what should I study and focus on when I am learning to code? Asking that question is the equivalent of a med student asking what area they should specialize in. There is simply no one size fits all answer. However, I would like to give some guidance and offer some of my own thoughts on the topic. Hopefully, if you are at the start of your career this post will give you a few things to think about. -## 定义 +## Definitions -在刚开始学习软件开发的时候,首先要经历的心理斗争就是我应该把关注点放在哪,前端还是后端?在我们深入了解两个领域的特征之前,我们先来看看它们的定义。 +The first internal debate that usually arises when starting in software is where should I focus, the frontend or the backend? Before we dive into the characteristics of each specialty lets first define them. -### 前端 +### Frontend -> 指的是网站的表示层以及它与后端数据的交互方式。例如 HTML、CSS、JavaScript 和 Angular 等。 +> refers to the presentation layer of a website and how that interacts with the data from the backend. Think HTML, CSS, Javascript, Angular, etc. [![](https://res.cloudinary.com/practicaldev/image/fetch/s--rYiDNsAL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e0vm7fc5bzuqxuhmt80f.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--rYiDNsAL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e0vm7fc5bzuqxuhmt80f.png) -### 后端 +### Backend -> 指的是应用程序的数据处理层。这一层负责与数据库通信,并确定将哪些信息发送到要显示的前端。例如 Ruby、Rails、Python、Java 等。 +> refers to the data processing layer of an application. This is the layer that talks to the database and determine's what information gets sent to the frontend to be displayed. Think Ruby, Rails, Python, Java, etc. [![](https://res.cloudinary.com/practicaldev/image/fetch/s--K81Tz4o2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bqj0p9v42macnqlis6ow.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--K81Tz4o2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bqj0p9v42macnqlis6ow.png) -好的,现在我们知道它们是什么了,但是你又该如何选择哪一个作为职业的方向呢?老实说,它取决于你的个人喜好以及你选择成为一个开发者的初衷。 +Ok, now we know what they are, but how do you choose which one you want to work with for your career? Honestly, it comes down to personal preference and why you choose to become a dev in the first place. -## 职业满足感 +## Job Satisfaction -如果你选择成为一名开发人员是因为你想获得职业满足感,并做一些你喜欢的事情,那么我的建议是,当你开始时,前后端都要做。同时涉猎前端和后端,这样你就能感受到你更喜欢的是什么。这么做会很辛苦吗?当然会,但是这也会极大地增加你找到喜欢做的事情的机会。 +If you choose to become a dev because you wanted career satisfaction and you wanted to do something you enjoy, then my advice is to do both when you start. Dabble in the frontend and the backend, that way you can get a feel for what you enjoy more. Will it be more work? Definitely, but you will greatly increase your chances of finding a part of the stack you enjoy working with. -在前端和后端生态系统中,仍然有许多你可以选择并且能做得非常出色的专业。当你开始的时候,试着去了解一些基本的东西,不要太担心会沉迷其中。试一试水,看看当你用它的时候,其中一个方向是否真的能吸引到你。同时,你要意识到,无论你选择哪个,一开始都会很困难。我想说的是,在你决定要把重点放在哪里之前,给自己一年或两年的时间来研究整个流程。这将给你足够的时间来解决最初的“哇,这太糟糕了,因为它很难”的问题,同时还能让你真正评估它是否是你喜欢使用的技术。 +Within the frontend and backend ecosystems, there are still many specialties you can branch off and do which can be overwhelming. When you are starting, try to get a feel for the basics and don't worry too much about diving all the way in. Test out the water and see if either one really grabs you when you work with it. However, be aware when you are starting out that no matter where you begin it is going to be tough at first. I would say give yourself a year or two of working across the entire stack before you decide where you would like to focus. That will give you enough time to get over the initial "Wow, this sucks because it is hard" hump and into the time when you can really assess if it is a technology you enjoy working with. -虽然每个人都有不同的品味,但是看看其他开发人员喜欢使用哪些语言和技术也是很有趣的。2019 年 StackOverflow 调查了[最受欢迎的语言](https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages)。 +While everyone has different tastes, you might find it interesting to see what languages and technologies other devs enjoy working with. The 2019 StackOverflow Survey looked at [what languages were most loved](https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages). [![](https://res.cloudinary.com/practicaldev/image/fetch/s--Jzs_nPT6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/85q0iiaxn4q1gfx9w2ny.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--Jzs_nPT6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/85q0iiaxn4q1gfx9w2ny.png) -前后端通吃的另一个好处是,你可以了解它们之间是如何协同工作的。无论你决定在未来关注哪个方面,这都非常有用。如果你了解另一半的工作原理,那么你就可以在项目中创建更好的代码和接口。 +Another advantage to working across the entire stack to start is that you get a feel for how everything works together. This can be immensely useful no matter where you decide to focus on in the future. If you have knowledge about how the other half works that will only allow you to create better code and interfaces within your specialty. -最后,当你在工作时横跨前后端,你可能会决定不进行选择了!你可能希望通吃前后端,并成为一个全栈工程师。这也是完全可以的! +Lastly, when working across the entire stack you might decide you don't want to choose! You may want to work across the entire stack and be a fullstack engineer for your career. That is completely valid as well! -## 工资/稳定性 +## Salary/Stability -如果你从事开发的职业动机是为了工资和稳定,那么同时学习这两个方向可能是在浪费你的时间。如果你想尽快从事一行职业,那么就对你想从事的领域做一些调查。找出前端和后端的工资趋势。此外,尝试找出市场上最需要哪种类型的开发人员。 +If your motivation for moving to a dev career was for the salary and stability it offers, then studying both might be a waste of your time. If you want to get into a career as fast as possible then do some research for the area you want to work in. Find out what the trends are in salary for frontend vs backend. Also, try to find out which type of dev is in the most demand. -我不知道前端和后端哪个工资更高,但有一些调查试图回答这个问题。我们可以看看 2019 年 StackOverflow 的调查,该调查将开发者的[薪资按类型](https://insights.stackoverflow.com/survey/2019#work-_-salary-by-developer-type)进行了细分。 +I don't claim to know whether the frontend or backend is paid more, but there are some surveys out there that have tried to answer this question. Once again, we can look at the 2019 StackOverflow Survey which broke down [salary of devs by type](https://insights.stackoverflow.com/survey/2019#work-_-salary-by-developer-type). -### 全球 +### Global -1. 全栈工程师 $57k -2. 后端工程师 $56k -3. 前端工程师 $52k +1. Fullstack $57k +2. Backend $56k +3. Frontend $52k -### 美国 +### United States -1. 后端工程师 $116k -2. 全栈工程师 $110k -3. 前端工程师 $103k +1. Backend $116k +2. Fullstack $110k +3. Frontend $103k -此外,它还[根据技术](https://insights.stackoverflow.com/survey/2019#top-paying-technologies)细分了薪资。下面是每项调查的样本。 +In addition, it broke down salaries [based on technology](https://insights.stackoverflow.com/survey/2019#top-paying-technologies). Here is a sampling from each of those surveys. -### 全球 +### Global * Clojure $90k * Go $80k @@ -72,7 +72,7 @@ * JavaScript $56k * HTML/CSS $55k -### 美国 +### United States * Scala $143k * Clojure $139k @@ -82,21 +82,21 @@ * JavaScript $110k * HTML/CSS $105k -需要注意的是,这些工资和趋势可能会因你的工作地点和是否在寻找远程工作而有所不同。因此,你需要自己做好调查。这很简单,只需要查看求职公告板并搜索后端和前端技术,看看都有哪些。 +It is important to note that these salaries and trends may be different depending on where you work and whether you are looking for a remote job or not. Definitely, do your research. It could be as simple as looking at job boards and running a search for backend and frontend technologies and seeing how many results are returned. -## 我为什么选择后端 +## Why I Choose the Backend -我想我应该在这里加上一段为什么我最终选择了后端,希望它可以在其他人做决定时帮助他们。当我转行成为一名开发人员时,我寻求工作满足感,并决定开始跨整个工作栈。在真正转向后端之前,我做了 3 年的全栈开发。吸引我来到后台的是 Ruby 的简洁。JavaScript 和前端语言对我来说总是缺乏组织性。我还热衷于优化代码性能。我喜欢想办法让事情运行得更好更快。后端似乎给了我更多的机会。 +I figured I would throw in here why I ended up choosing the backend in hopes that it might inform others when making their decision. I was seeking job satisfaction when I made the career switch to being a dev and decided to start out working across the entire stack. I worked as a full-stack dev for 3 years before I started to really shift towards the backend. What drew me to the backend was the cleanliness of Ruby. Javascript and frontend languages have always felt less organized to me. I also thrive on optimizing code performance. I love trying to find ways to make things run better and faster. The backend seemed to give me more opportunities to do that. -最后,我不是一个非常注重视觉或艺术的人。有些人可以看看网页,然后想办法把它放在什么地方。我从来都不擅长这个,所以后端让我更自然、更舒服。 +Finally, I am not a very visual or artistic person. Some people can look at a webpage and figure out how to lay it out and where everything should go. I never was good at that so the backend felt more natural and comfortable for me. -如果你想深入了解其他人的观点,请查看这个讨论前端和后端 Web 开发的 [CodeNewbie Chat](https://wakelet.com/wake/7d71f467-89ba-49cb-a196-4e32657369ac)。你还可以查看周二开始的 dev.to thread,我将会询问人们如何选择在前端还是后端工作以及其原因。 +If you want more insight into other's opinions, check out this [CodeNewbie Chat](https://wakelet.com/wake/7d71f467-89ba-49cb-a196-4e32657369ac) which discusses Frontend vs Backend web development. You can also check out the dev.to thread I started Tuesday asking people what part of the stack they choose to work in and why. [Frontend vs Backend, which do you prefer and why?](https://dev.to/molly_struve/frontend-vs-backend-which-do-you-prefer-and-why-5a9e) -## 没有什么是永恒的 +## Nothing Is Forever -无论你决定专注于什么,要知道没有什么是永恒的。如果你走错了一条路,你总是可以悬崖勒马的。软件工程的一个伟大之处在于,它把所有的东西都整合在一起。了解一个领域只会帮助你的成长并在另一个领域做得更好。 +No matter what you decide to focus on, know that nothing is forever. If you go down one path and decide that it was the wrong one, you can always switch. One of the great things about software engineering is that it all fits together. Knowing a lot about one area will only help you learn and be better at another. > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 9eb05616a0cb226092e1bd0988e0a2ba320d3237 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 51/58] Revert "Create the-rise-of-the-meta-designer.md (#6189)" This reverts commit 6213f3493ba5b6a7bb0388fe798b73f91de5169f. --- TODO1/the-rise-of-the-meta-designer.md | 59 -------------------------- 1 file changed, 59 deletions(-) delete mode 100644 TODO1/the-rise-of-the-meta-designer.md diff --git a/TODO1/the-rise-of-the-meta-designer.md b/TODO1/the-rise-of-the-meta-designer.md deleted file mode 100644 index d5ad06f2b37..00000000000 --- a/TODO1/the-rise-of-the-meta-designer.md +++ /dev/null @@ -1,59 +0,0 @@ -> * 原文地址:[Rise of the meta-designer](https://interactions.acm.org/archive/view/july-august-2019/the-rise-of-the-meta-designer) -> * 原文作者:[Uday Gajendar](http://ghostinthepixel.com/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-rise-of-the-meta-designer.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-rise-of-the-meta-designer.md) -> * 译者: -> * 校对者: - -# Rise of the meta-designer - -It’s often said that one should not “go meta” because things simply get too abstract and unwieldy in the mind, trying to make sense of it all at varying levels or dimensions. But perhaps it’s exactly the right time for designers to go meta, at least as a useful respite from the daily grind of that which is becoming automated and instrumented **ad nauseam**—and maybe more dramatically, for our professional survival, with a reframing of authentic, substantive design value. - -Algorithms, automation, instrumentation: Designing is becoming increasingly routine, predictable, and systematic for the sake of efficiency and reliability, to safely ensure high-velocity outputs. The poetics of human-computer interaction have evolved into prescriptions for scaled, scheduled delivery. From distributed symbol libraries to computerized usability tests, what then becomes of the designer? What is now the reason for being? But perhaps the true realm for a designer’s value is starting to reveal itself. Against this backdrop, I wonder if there will be the rise of what I call **meta-designers**, whose fundamental aspects are strategic, humanistic, and—dare I say—philosophic. - -We see this starting to happen somewhat with the emergence of design thinking in corporate boardrooms, with newly appointed customer experience chiefs charged with defining a holistic model that ties disparate business elements together for delivering customer-based market value. In effect, such trends signal the daunting challenge of, well, **designing design** itself—the meta-design that makes such customer-centric capabilities and sensibilities possible within organizations. The design thinking movement scratches the surface of this notion of meta-design, with the initial breaking of conventional business management chains to free up attitudes in support of problem reframing and “fast fail” iteration, backed by empathy-friendly mindsets. Meanwhile, customer experience provides a business-friendly governance framework to spin up an “outside-in” value-making machine (i.e., making money!) that focuses on the customer. It involves highly complex coordinations among the marketing, sales, customer support, and product management teams with an integrated sense of how to ensure cohesive messaging, workflows, and branding, where the customer is ostensibly at the center of decision making, or at least of ROI analysis. Finally, there’s user experience, which, as a product development concept, is basically a remnant of the dotcom boom of the late 1990s (“the UX of websites”). It has now popularly come to refer to the delivery or use of any and all digital products or services reflecting best practices drawn from HCI, cognitive psychology, interaction design, and other related fields molded into a compelling interactive encounter (for a website, application, smart device, etc.). - -Tying together all these levels of design into an organizational apparatus that lives, breathes, evolves, and amplifies itself into a force-multiplier of sorts—this is the profound challenge of meta-design: actually designing the conditions for good design to emerge and thrive, for the long term, with a sustained sense of continuity and value, not some random spark of luck or defined by a single strong personality. There’s a sense of legacy here—something that endures beyond any specific group of people themselves. - ---- - -> **This is the profound challenge of meta-design: actually designing the conditions for good design to emerge and thrive.** - ---- - -How does someone (or a globally distributed multidisciplinary team) **design** the essential services, systems, structures, cultural vibe, and process models that both **operationalize** design into something tactical and shippable and also **spiritualize** design into something meaningful and critical, pursuing deep questions about the organization’s value and purpose and adding to a sorely lacking humanist dimension? Hmm, that’s quite daunting. And, let’s admit, these are not necessarily the typical wonderings of a modern design leader. But such thinking will be necessary to ensure the long-term vitality and value of teams beyond daily routines. Otherwise you will be constantly on defense, reacting to shifting circumstances, per the whims of others (i.e., business and tech executives) or aspects outside the field of view—the blindspots of daily delivery-focused myopia—rather than being proactive. We expect our products and apps to be anticipatory in providing a great experience for their users, so why not expect that of our design organizational ethos? How can we get to that place where we’re anticipating what’s next as a culture and spirit? Where we’re iteratively and critically designing designing? - -[![ins01.gif](https://deliveryimages.acm.org/10.1145/3340000/3338285/ins01.gif)](http://deliveryimages.acm.org/10.1145/3340000/3338285/ins01.gif) - -Part of that involves knowing what it takes to be a meta-designer. Sure, much of this could sound like just being an astute, proactive, resourceful design leader—and to some degree, yes, that’s certainly important! But it’s always reframing your perspectives and actions in a designerly way that fertilizes the ground for both peers and challengers (from other departments or teams). In this respect, the core principles that guide such a meta-designer may be the following: - -* **Reflection in action: Consideration of inferred relationships and potential consequences in the midst of designing.** This is impacted by today’s high-velocity needs to execute outputs with little time allocated for such reflection, a “ship it now” mentality of moving fast and breaking things for the sake of vanity metrics and investor dollars. Yet such reflection would yield benefits for the individuals (their career growth), the team (habits of excellence), and the product too (are we building the right thing for the right people for the right reasons?). -* **Strategic forethought: Looking ahead to connections among an organization’s disparate functions, decisions, attitudes, and outcomes.** This is made difficult by deeply siloed and globally distributed teams or the lack of true partnership and collaborative contexts with honest questioning of dependencies, which could lead to a disastrous convergence, or even dire misalignment. Nobody wants a rude surprise at the end of a laborious journey; seeing connections (or the lack of them) can help prevent that surprise, preparing teams to get proper processes and maybe hold useful “intervention” moments before things get crazy. -* **Intellectual humanism: Lending a nuanced vocabulary informed by critical lenses and depth of analysis around problems.** This is heavily challenged by the rapid-fire cadence of thoughtless tech talk optimized for efficient communications (thanks, high-velocity delivery deadlines!) filled with internal jargon, acronyms, and clinical or even stereotyped descriptions of customers—often with little patience for questioning their motivations. Yet enrichening daily discourse with language that speaks to a desire to go deep into unraveling the layers of complexity, shaped by genuine human curiosity—that might make eyes widen with realization, and sharpen our mindsets a bit. -* **Creative provocation: Suggesting that which is radical and speculative to spark risky dialogues and enable fresh perspectives.** This absolutely runs counter to attempts to ensure the safe, reliable, predictable efficiency of output generation—but is essential to the lifeblood of any organization that values human-centered innovation! To provoke is to challenge the current/mainstay and inspire alternative ideas that push the boundaries of tolerable risk. But that’s often the point of why we keep demanding that we “change existing situations into preferred ones,” as Herb Simon said of design’s core characteristic. - -Following these principles are actions that typify a meta-designer’s practical work: - -* Foster critical thinking, questioning, and argumentation around design process, system, culture, and values as interrelated and dynamically impacting elements. -* Create, interpret, and apply frameworks or models of thought toward such aforementioned issues. This will necessarily require quite a bit of mapping/diagramming/storytelling, as it’s abstract and needs to somehow become more concrete to engage with non-design peers, help them be active in the meta-designing—and hopefully see the utility of the outcomes. -* Define integrative design systems that are both material and cultural: The origin, creation, application, and adaptation of them are what matter. This will require designing for multiple teams (again, often buried within silos) and having that cross-functional overlap vibe, with full transparency across teams. This will foster a sense of trust and team unity. -* Have a strong aptitude for lifecycles, ecosystems, platforms, and journeys, with an unapologetically humanist outlook, asking: How are human values championed and supported end to end, across the horizons and paths? This is crucial as we blaze forward into an algorithmically driven future of optimizing efficiencies for a system. Humans are messy and values are fuzzy—it’s like cutting cubes from clouds! Negotiating such inherent humanistic fuzziness while upholding basic human values and needs is vital and must not be compromised while system and business architectures are being developed. -* Critically and robustly tackle the political/social/ethical aspects of business and technology; it’s a key aspect of advocating humanist values in thought and practice. It’s not enough to just deliver something meaningful; that something (and the culture and process enabling its existence) must be respectful and supportive of people, without causing harm—or creating the opportunity for damage and abuse. For instance, someone on Twitter had suggested teams should write a **Black Mirror** episode for their product—tell the tale of the worst possible abuse and then work backward to prevent it from happening. Constantly raising those questions, shaking that red flag in the face of… well, it’s not really opposition, per se, but the runaway train of high-velocity delivery for the sake of metrics. - ---- - -> **It’s not enough to just deliver something meaningful; that something must be respectful and supportive of people.** - ---- - -Of course, some of this sounds like organizational behavior and psychology or simply being a corporate therapist of sorts. There’s no doubt about that! Yet the notion of meta-**design** is key here, because of the **confluence of both intention and significance with practical consequence**—which happens in **action**, not mere rhapsodizing on a whiteboard with multicolored stickies and voting dots. There is a deliberate shaping of forces and frameworks to empower and enable pivotal outcomes, which is not a one-size-fits-all approach. Every organization, from startups to multinationals, must be regarded differently, on its own terms, requiring variations of process and culture to support customer-driven goals. This means a deep questioning of the context and purpose, and of viable outcomes. - -[![ins02.gif](https://deliveryimages.acm.org/10.1145/3340000/3338285/ins02.gif)](http://deliveryimages.acm.org/10.1145/3340000/3338285/ins02.gif) - -Meta-designing in this sense could be the next grand frontier of design practice, imbued with a strategic sense for humanism and intellectualism, which are necessary elements if we are to make design thinking + customer experience + user experience into more than a checklist of ingredients for a “successful business.” What will you do to advance this approach? It’s admittedly aspirational and fuzzy to tackle, but that doesn’t mean it’s not feasible or valuable. - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 5a17da5a6a6ca244c31714c95a4ff4cee19f5541 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 52/58] Revert "Create why-ai-is-here-to-stay.md (#6187)" This reverts commit d51e300f0c1ceaed1886be575d157483c0c012ac. --- TODO1/why-ai-is-here-to-stay.md | 76 --------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 TODO1/why-ai-is-here-to-stay.md diff --git a/TODO1/why-ai-is-here-to-stay.md b/TODO1/why-ai-is-here-to-stay.md deleted file mode 100644 index b88fc03e21c..00000000000 --- a/TODO1/why-ai-is-here-to-stay.md +++ /dev/null @@ -1,76 +0,0 @@ -> * 原文地址:[Why AI is here to stay](https://medium.com/hackernoon/why-ai-is-here-to-stay-9c75b1868b9b) -> * 原文作者:[Cassie Kozyrkov](https://medium.com/@kozyrkov) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/why-ai-is-here-to-stay.md](https://github.com/xitu/gold-miner/blob/master/TODO1/why-ai-is-here-to-stay.md) -> * 译者: -> * 校对者: - -# Why AI is here to stay - -> A revolution in communication between people and computers - -If you’ve ever attended an [AI](http://bit.ly/quaesita_ai) conference, I bet you passed under the placid gaze of a chrome-plated humanoid, lovingly selected from an [ocean of creepy robot stock images](https://twitter.com/hmason/status/1123750831587827713) that marketing teams can’t resist pasting on every billboard these days. - -![](https://miro.medium.com/max/1400/1*YO5HhWqmVtefTl_aXi5xKw.png) - -Clearly, I’m personally guilty of using [octarine](http://bit.ly/octarineai)-blue sci-fi art to lure weary travelers to my [blog](https://medium.com/@kozyrkov). It certainly works, which is why it’s a pity that those images have [next to nothing](http://bit.ly/quaesita_ai) to do with [AI](http://bit.ly/quaesita_ai). - -> ### Robots are very exciting but mostly useless. - -> ### Today’s AI is mostly boring but very useful. - -You’d think we’d all be more ashamed of ourselves, but don’t worry, [AI](http://bit.ly/quaesita_ai) is [too useful](http://bit.ly/quaesita_island) to go away, no matter how much we all cry wolf. Here’s why. - -> ### AI gives programmers an alternative way to tell computers what to do. - -Marketing folk run around trying to get your attention with sci-fi gimmicks, but the reason you’ll stick around long enough to buy into [AI](http://bit.ly/quaesita_ai) is entirely different. The real story is about communication with machines. - -[AI gives programmers an alternative way to tell computers what to do.](http://bit.ly/quaesita_simplest) To understand why this new way to talk to machines is so useful and why it’s a technological revolution, let’s forget machines for a moment and talk about people. I’ll lay the cards flat on the table so there’s no more purple mystery about what AI is for. - -#### How people talk to one another - -We express our wishes to other people in two ways. One is with explicit instructions, the other is with examples. - -If you wanted to learn how to predict my Starbucks order, you could follow me around on my travels and you’d probably notice that the quad espresso I order in US airports becomes a latte in Taipei, Mumbai, and Nairobi. What’s up with that? Given a few more examples, you’d probably figure out the rule yourself. That’s what AI does — [turns examples into instructions](http://bit.ly/quaesita_emperor). There’s no way you’d figure it out if you only saw me order Starbucks only once or twice (not enough [data](http://bit.ly/quaesita_hist)) or if you only observed 50 counts of me ordering my usual cappuccino at the place on my street ([irrelevant data](http://bit.ly/quaesita_biasdef), since that place is not Starbucks). Same goes for AI. - -![](https://cdn-images-1.medium.com/max/2000/0*KrYh8NjTt_xPqTM6.jpg) - -Of course, I could also have just told you my Starbucks rule explicitly since I can express it easily: **“If they have half-and-half, order 4 shots of espresso in a tall cup, then fill ‘er up with half-and-half.** (Don’t judge me!) **If they don’t, order a tall latte with an extra shot.”** - -The point here is that if I’m teaching a human travel companion, it’s awfully nice to have access to both modes of communication. When explicit instructions are easy to come up with and express, I can program a friend the way people have been talking to computers for decades: **if this, do that**. - -But what if I don’t even know why I order a cappuccino on some New York days and flat white on others? I can’t give you the formula because **even I don’t know it**. But I can ask you to watch me and see if **you** can figure out the pattern. Maybe there is one, maybe there isn’t, but it’s awesome that you could at least **try** work it out. Without [ML/AI](http://bit.ly/quaesita_emperor), a computer can’t **try** to [find a pattern](http://bit.ly/quaesita_emperor). It’s explicit instructions or bust. - -> ### AI is about human self-expression. - -Maybe you’d realize that some places have a smell that does it. You might not know why that works (perhaps the smell triggers a feeling related to drinking cappuccinos with my father after the theatre, but you don’t have access to those information) but you’ll realize that you’re able to predict what I’ll do accurately. Eventually, you’ll feel confident enough to say, **“Flat white this time? Yeah, I got this.”** I’d be standing there with my jaw dropped because I don’t know how you know that. After a while I won’t worry about it, I’ll just [trust you](http://bit.ly/quaesita_donttrust). And as long as my preferences don’t change, you’ll keep getting it right, even if [neither of us knows why](http://bit.ly/quaesita_xai). - -> ### My ability to give you my explicit instructions is [traditional programming](http://bit.ly/quaesita_simplest). My ability to ask you to learn from relevant examples is the [essence of machine learning and AI](http://bit.ly/quaesita_emperor). - -So here’s why [AI is not a fad](http://bit.ly/quaesita_fad): in real life, there’s no way I’m giving up my ability to fall back on teaching with examples if I’m not clever enough to come up with the instructions. Absolutely not! I’m pretty sure I use examples more than instructions to communicate with other humans when I stumble around the real world. - -AI means I can communicate with computers that second way — via examples — not only by instructions, are you seriously asking me to suddenly gag my own mouth? Remember, in the old days we had to rely primarily on instructions only because we couldn’t do it the other way, in part because processing all those examples would [strain the meager CPUs of last century’s poor desktops](http://bit.ly/forbes_ai). - -But now that humanity has unlocked its ability to express itself to machines via examples, why would we suddenly give that option up entirely? A second way of talking to computers is too important to drop like yesterday’s shoulderpads. - -![](https://cdn-images-1.medium.com/max/2000/0*dkk5YgpnY2U9DYJE.jpg) - -What we should drop is our expectation that there’s a one-size-fits-all way of communicating with computers about every problem. Say what you mean and say it the way that works best. Sometimes you want to give instructions and sometimes you want to feed in a bunch of examples instead. - -> ### Some tasks are so complicated that you can’t hold their instructions in your memory. - -Because AI allows you to automate the ineffable, it’s our only option for those situations where you can’t fathom the instructions. Where you’re not smart enough to work out what those patterns mean yourself or where the instructions are so complicated that you forgot the first line by the time you got to the seven thousandth one. - -![Want to memorize all this? Me neither. Computers don’t mind, though.](https://cdn-images-1.medium.com/max/2000/0*7LuSlPgyJ-B5VLGh.JPG) - -Computers don’t mind memorizing long boring [example sets ](http://bit.ly/quaesita_hist)or instruction manuals. They can churn through those examples even though that’s a task you wouldn’t want to touch with a bargepole. [Some tasks are so complicated that you can’t hold their instructions in your memory.](http://bit.ly/quaesita_fad) When all the [low hanging fruit tasks](http://bit.ly/quaesita_fad) are automated with straightforward explicit instructions, progress will demand working on the complicated ones. In that zone, it’ll be AI or nothing. - -If those tasks are very complicated, you’ll probably not be able to automate them flawlessly, but with AI you still might do better than nothing. (Don’t forget to [build safety nets](http://bit.ly/quaesita_policy).) If you do get flawless performance, my first instinct is to wonder whether your task [might be so simple that you really ***should*** have solved it the traditional way instead](http://bit.ly/quaesita_fad). Don’t convert between dollars and cents with AI… seriously, what are you doing?! It’s where the task is too hard the old way that you might turn to AI. That’s also why the [first step in AI](http://bit.ly/quaesita_first) is to start with the task and double-check that you can’t solve it without AI first. - -**If you’re keen to get started with letting AI make itself useful to you, [here’s a guide](http://bit.ly/quaesita_dmguide) that decision-makers should read before anyone even thinks about data or technical nitty-gritty.** - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From ede26f2ddd427784c8b3c585fca4d25dc6f96f1e Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 53/58] =?UTF-8?q?Revert=20"2019=20=E5=B9=B4=E7=9A=84=20And?= =?UTF-8?q?roid=20=E7=BD=91=E7=BB=9C=20=E2=80=94=E2=80=94=20Retrofit=20?= =?UTF-8?q?=E4=B8=8E=20Kotlin=20=E5=8D=8F=E7=A8=8B=20(#6147)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b501fc7485b899db60ccddca3fd6ca15db6c2f18. --- ...n-2019-retrofit-with-kotlins-coroutines.md | 109 +++++++++--------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/TODO1/android-networking-in-2019-retrofit-with-kotlins-coroutines.md b/TODO1/android-networking-in-2019-retrofit-with-kotlins-coroutines.md index edf0463b3a6..f575e0151ba 100644 --- a/TODO1/android-networking-in-2019-retrofit-with-kotlins-coroutines.md +++ b/TODO1/android-networking-in-2019-retrofit-with-kotlins-coroutines.md @@ -2,53 +2,54 @@ > * 原文作者:[Navendra Jha](https://medium.com/@navendra) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/android-networking-in-2019-retrofit-with-kotlins-coroutines.md](https://github.com/xitu/gold-miner/blob/master/TODO1/android-networking-in-2019-retrofit-with-kotlins-coroutines.md) -> * 译者:[feximin](https://github.com/Feximin) +> * 译者: +> * 校对者: -# 2019 年的 Android 网络 —— Retrofit 与 Kotlin 协程 +# Android Networking in 2019 — Retrofit with Kotlin’s Coroutines -2018 年,Android 圈发生了许多翻天覆地的变化,尤其是在 Android 网络方面。稳定版本的 Kotlin 协程的发布极大地推动了 Android 在处理多线程方面从 RxJava 到 Kotlin 协程的发展。 -本文中,我们将讨论在 Android 中使用 [Retrofit2](https://square.github.io/retrofit/) 和 [Kotlin 协程](https://kotlinlang.org/docs/reference/coroutines-overview.html) 进行网络 API 调用。我们将调用 [TMDB API](https://developers.themoviedb.org/3) 来获取热门电影列表。 +The year 2018 saw a lot of big changes in the Android World, especially in terms of Android Networking. The launch of a stable version of Kotlin Coroutines fueled a lot of movement from RxJava to Kotlin Coroutines for handling multithreading in Android. +In this article, we will be talking about making Networking API calls in Android using [Retrofit2](https://square.github.io/retrofit/) and [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html). We will be making a networking call to [TMDB API](https://developers.themoviedb.org/3) to fetch popular movies. ![](https://cdn-images-1.medium.com/max/2000/1*un0xtxGU3IEh8KBXcAXGQA.png) -### 概念我都懂,给我看代码!! +#### I know all these concepts, Show me the code!! -如果你在 Android 网络方面有经验并且在使用 Retrofit 之前进行过网络调用,但可能使用的是 RxJava 而不是 Kotlin 协程,并且你只想看看实现方式,[请查看 Github 上的 readme 文件](https://github.com/navi25/RetrofitKotlinDeferred/)。 +If you are experienced with Android Networking and have made networking calls before using Retrofit but probably with other libraries viz RxJava, instead of Kotlin Coroutines and just want to check out the implementation, [check out this code readme on Github.](https://github.com/navi25/RetrofitKotlinDeferred/) -### Android 网络简述 +#### Android Networking in Nutshell -简而言之,Android 网络或者任何网络的工作方式如下: +In a nutshell, android networking or any networking works in the following way: -* **请求** —— 使用正确的头信息向一个 URL(终端)发出一个 HTTP 请求,如有需要,通常会携带授权的 Key。 -* **响应** —— 请求会返回错误或者成功的响应。在成功的情况下,响应会包含终端的内容(通常是 JSON 格式)。 -* **解析和存储** —— 解析 JSON 并获取所需的值,然后将其存入数据类中。 +* **Request—** Make an HTTP request to an URL (called as endpoint) with proper headers generally with Authorisation Key if required. +* **Response —** The Request will return a response which can be error or success. In the case of success, the response will contain the contents of the endpoint (generally they are in JSON format) +* **Parse & Store —** We will parse this JSON and get the required values and store them in our data class. -Android 中,我们使用: +In Android, we use — -* [Okhttp](http://square.github.io/okhttp/) —— 用于创建具有合适头信息的 HTTP 请求。 -* [Retrofit](https://square.github.io/retrofit/) —— 发送请求。 -* [Moshi](https://github.com/square/moshi)/ [GSON](https://github.com/google/gson) —— 解析 JSON 数据。 -* [Kotlin 协程](https://kotlinlang.org/docs/reference/coroutines-overview.html) —— 用于发出非阻塞(主线程)的网络请求。 -* [Picasso](http://square.github.io/picasso/) / [Glide](https://bumptech.github.io/glide/) —— 下载网络图片并将其设置给 ImageView。 +* [Okhttp](http://square.github.io/okhttp/) — For creating an HTTP request with all the proper headers. +* [Retrofit](https://square.github.io/retrofit/) — For making the request +* [Moshi ](https://github.com/square/moshi)/ [GSON ](https://github.com/google/gson)— For parsing the JSON data +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) — For making non-blocking (main thread) network requests. +* [Picasso](http://square.github.io/picasso/) / [Glide](https://bumptech.github.io/glide/)— For downloading an image from the internet and setting it into an ImageView. -显然这些只是一些热门的库,也有其他类似的库。此外这些库都是由 [Square 公司](https://en.wikipedia.org/wiki/Square,_Inc) 的牛人开发的。点击 [Square 团队的开源项目](http://square.github.io/) 查看更多。 +Obviously, these are just some of the popular libraries but there are others too. Also, most of these libraries are developed by awesome folks at [Square Inc.](https://en.wikipedia.org/wiki/Square,_Inc.) Check out this for more [open source project by the Square Team](http://square.github.io/). -## 开始吧 +## Let's get started -Movie Database(TMDb)API 包含所有热门的、即将上映的、正在上映的电影和电视节目列表。这也是最流行的 API 之一。 +The Movie Database (TMDb) API contains a list of all popular, upcoming, latest, now showing movies and tv shows. This is one of the most popular API to play with too. -TMDB API 需要 API 密钥才能请求。为此: +TMDB API requires an API key to make requests. For that:- -* 在 [TMDB](https://www.themoviedb.org/) 建一个账号 -* [按照这里的步骤注册一个 API 密钥](https://developers.themoviedb.org/3/getting-started/introduction)。 +* Make an account at [TMDB](https://www.themoviedb.org/) +* [Follow steps described here to register for an API key](https://developers.themoviedb.org/3/getting-started/introduction). -### 在版本控制系统中隐藏 API 密钥(可选但推荐) +#### Hiding API key in Version Control (Optional but Recommended) -获取 API 密钥后,按照下述步骤将其在 VCS 中隐藏。 +Once you have the API key, do the following steps to hide it in VCS. -* 将你的密钥添加到根目录下的 **local.properties** 文件中。 -* 在 **build.gradle** 中用代码来访问密钥。 -* 之后在程序中通过 **BuildConfig** 就可以使用密钥了。 +* Add your key in **local.properties** present in the root folder. +* Get access to the key in **build.gradle** programmatically. +* Then the key is available to you in the program though **BuildConfig**. ```Gradle //In local.properties @@ -70,9 +71,9 @@ buildTypes.each { var tmdbApiKey = BuildConfig.TMDB_API_KEY ``` -## 设置项目 +## Setting up the Project -为了设置项目,我们首先会将所有必需的依赖项添加到 **build.gradle (Module: app)** 文件中: +For setting up the project, we will first add all the required dependencies in **build.gradle (Module: app):-** ```Gradle // build.gradle(Module: app) @@ -111,7 +112,7 @@ dependencies { } ``` -### 现在创建我们的 TmdbAPI 服务 +#### Now let’s create our TmdbAPI service ```Kotlin //ApiFactory to create TMDB Api @@ -152,18 +153,18 @@ object Apifactory{ } ``` -看一下我们在 ApiFactory.kt 文件中做了什么。 +Let’s see what we are doing here in ApiFactory.kt. -* 首先,我们创建了一个用以给所有请求添加 api_key 参数的网络拦截器,名为 **authInterceptor**。 -* 然后我们用 OkHttp 创建了一个网络客户端,并添加了 authInterceptor。 -* 接下来,我们用 Retrofit 将所有内容连接起来构建 Http 请求的构造器和处理器。此处我们加入了之前创建好的网络客户端、基础 URL、一个转换器和一个适配器工厂。 - 首先是 MoshiConverter,用以辅助 JSON 解析并将响应的 JSON 转化为 Kotlin 数据类,如有需要,可进行选择性解析。 -第二个是 CoroutineCallAdaptor,它的类型是 Retorofit2 中的 `CallAdapter.Factory`,用于处理 [Kotlin 协程中的](https://kotlinlang.org/docs/reference/coroutines.html) `Deferred`。 -* 最后,我们只需将 **TmdbApi 类(下节中创建)** 的一个引用传入之前建好的 retrofit 类中就可以创建我们的 tmdbApi。 +* First, we are creating a Network Interceptor to add api_key in all the request as **authInterceptor.** +* Then we are creating a networking client using OkHttp and add our authInterceptor. +* Next, we join everything together to create our HTTP Request builder and handler using Retrofit. Here we add our previously created networking client, base URL, and add a converter and an adapter factory. +First is MoshiConverter which assist in JSON parsing and converts Response JSON into Kotlin data class with selective parsing if required. +The second one is CoroutineCallAdaptor which is aRetrofit2 `CallAdapter.Factory` for [Kotlin coroutine's](https://kotlinlang.org/docs/reference/coroutines.html) `Deferred`. +* Finally, we simply create our tmdbApi by passing a reference of **TmdbApi class (This is created in the next section)** to the previously created retrofit class. -### 探索 Tmdb API +#### Exploring the Tmdb API -调用 **/movie/popular** 接口我们得到了如下响应。该响应中返回了 **results**,这是一个 movie 对象的数组。这正是我们关注的地方。 +We get the following response for **/movie/popular** endpoint. The response returns **results** which is an array of movie object. This is a point of interest for us. ```JSON { @@ -216,7 +217,7 @@ object Apifactory{ } ``` -因此现在我们可以根据该 JSON 创建我们的 Movie 数据类和 MovieResponse 类。 +So now let’s create our Movie data class and MovieResponse class as per the json. ```Kotlin // Data Model for TMDB Movie item @@ -240,10 +241,8 @@ interface TmdbApi{ } ``` - -**TmdbApi 接口:** - -创建了数据类后,我们创建 TmdbApi 接口,在前面的小节中我们已经将其引用添加至 retrofit 构建器中。在该接口中,我们添加了所有必需的 API 调用,如有必要,可以给这些调用添加任意参数。例如,为了能够根据 id 获取一部电影,我们在接口中添加了如下方法: +**TmdbApi interface** +After creating data classes, we create TmdbApi interface whose reference we added in the retrofit builder in the earlier section. In this interface, we add all the required API calls with any query parameter if necessary. For example, for getting a movie by id we will add the following method to our interface: ```Kotlin interface TmdbApi{ @@ -257,13 +256,13 @@ interface TmdbApi{ } ``` -## 最后,进行网络调用 +## Finally making a Networking Call -接着,我们最终发出一个用以获取所需数据的请求,我们可以在 DataRepository 或者 ViewModel 或者直接在 Activity 中进行此调用。 +Next, we finally make a networking call to get the required data, we can make this call in DataRepository or in ViewModel or directly in Activity too. -#### 密封 Result 类 +#### Sealed Result Class -这是用来处理网络响应的类。它可能成功返回所需的数据,也可能发生异常而出错。 +Class to handle Network response. It either can be Success with the required data or Error with an exception. ```Kotlin sealed class Result { @@ -272,7 +271,7 @@ sealed class Result { } ``` -#### 构建用来处理 safeApiCall 调用的 BaseRepository +#### Building BaseRepository to handle safeApiCall ```Kotlin open class BaseRepository{ @@ -304,7 +303,7 @@ open class BaseRepository{ } ``` -#### 构建 MovieRepository +#### Building MovieRepository ```Kotlin class MovieRepository(private val api : TmdbApi) : BaseRepository() { @@ -324,7 +323,7 @@ class MovieRepository(private val api : TmdbApi) : BaseRepository() { } ``` -#### 创建 ViewModel 来获取数据 +#### Creating the View Model to fetch data ```Kotlin class TmdbViewModel : ViewModel(){ @@ -354,7 +353,7 @@ class TmdbViewModel : ViewModel(){ } ``` -#### 在 Activity 中使用 ViewModel 更新 UI +#### Using ViewModel in Activity to Update UI ```Kotlin class MovieActivity : AppCompatActivity(){ @@ -379,9 +378,9 @@ class MovieActivity : AppCompatActivity(){ } ``` -本文是 Android 中一个基础但却全面的产品级别的 API 调用的介绍。[更多示例,请访问此处](https://github.com/navi25/RetrofitKotlinDeferred)。 +This is a basic introductory but full production level API calls on Android. [For more examples, visit here.](https://github.com/navi25/RetrofitKotlinDeferred) -祝编程愉快! +Happy Coding! > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From bbc76b286ae1f59acee32b6a054294b7f1a3a716 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 54/58] =?UTF-8?q?Revert=20"Android=20=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E5=99=A8=EF=BC=9AProject=20Marble=20=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E6=94=B9=E8=BF=9B=20(#6134)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ed519994bc7b00808137749c5cb593dd973eceaa. --- ...id-emulator-project-marble-improvements.md | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/TODO1/android-emulator-project-marble-improvements.md b/TODO1/android-emulator-project-marble-improvements.md index 32a81e307ae..0863af1fefe 100644 --- a/TODO1/android-emulator-project-marble-improvements.md +++ b/TODO1/android-emulator-project-marble-improvements.md @@ -1,88 +1,91 @@ -> * 原文地址:[Android Emulator: Project Marble Improvements](https://medium.com/androiddevelopers/android-emulator-project-marble-improvements-1175a934941e) +> * 原文地址:[Android Emulator : Project Marble Improvements](https://medium.com/androiddevelopers/android-emulator-project-marble-improvements-1175a934941e) > * 原文作者:[Android Developers](https://medium.com/@AndroidDev) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/android-emulator-project-marble-improvements.md](https://github.com/xitu/gold-miner/blob/master/TODO1/android-emulator-project-marble-improvements.md) -> * 译者:[qiuyuezhong](https://github.com/qiuyuezhong) +> * 译者: +> * 校对者: -# Android 模拟器:Project Marble 中的改进 +# Android Emulator : Project Marble Improvements + +Posted by: Sam Lin, Product Manager, Android ![](https://cdn-images-1.medium.com/max/3200/0*YXbEJNUcY1n4S5N1) -这是 Android Studio 团队一系列博客文章中第三篇,深入探讨了 [Project Marble](https://android-developers.googleblog.com/2019/01/android-studio-33.html) 中的细节和幕后情况。本文是由模拟器团队的 Sam Lin(产品经理),Lingfeng Yang(技术主管)和 Bo Hu(技术主管)撰写的。 +This is the third in a series of blog posts by the Android Studio team diving into some of the details and behind the scenes of [Project Marble](https://android-developers.googleblog.com/2019/01/android-studio-33.html). The following post was written by Sam Lin (product manager), Lingfeng Yang (tech lead), and Bo Hu (tech lead) on the Emulator team. -今天我们很高兴地向您介绍我们在 Project Marble 期间在 Android 模拟器上取得的最新进展。我们的核心目标之一是使 Android 模拟器成为应用程序开发的必选设备。物理 Android 设备非常棒,但我们的目标是增加功能和性能,使您在开发和测试 Android 应用程序时更加高效。 +Today we are excited to give you an update on the progress we have made in the Android Emulator during Project Marble. One of the core goals we have is to make the Android Emulator the go-to device for app development. Physical Android devices are great, but we aim to add features and performance that make you even more efficient when developing and testing your Android apps. -我们听说很多应用程序开发者喜欢我们最近对模拟器所做的改进,从 2 秒的启动时间,GPU 图形加速,再到[屏幕快照](https://developer.android.com/studio/run/emulator#snapshots)。然而,我们也听说 Android 模拟器消耗了您开发电脑上的太多系统资源。为了解决这个问题,我们在 Project Marble 中创建了一个任务来优化 Android 模拟器的 CPU 使用率。在过去几个月的 Project Marble 中,在不违背原本设计原则的情况下,Android 模拟器的能效和绘制速度有了显著提升。在本文中,我们将介绍到目前为止在 [Canary Channel](https://developer.android.com/studio/preview/install-preview#change_your_update_channel) 上 Android Emulator 28.1 发布的一些进展。 +We have heard that many app developers like the recent improvements we have made to the emulator, from 2 second start time, GPU graphics acceleration, to [snapshots](https://developer.android.com/studio/run/emulator#snapshots). However, we have also heard that the Android Emulator consumes too many system resources on your development computer. To address this issue, we created an effort in Project Marble to optimize the CPU usage for the Android Emulator. Without deviating from the original design principles, we’ve made significant improvements to the power efficiency and draw rate of the Android Emulator over the last few months during Project Marble. In this post, we will shed some light on the progress we release so far in Android Emulator 28.1 on [the canary channel](https://developer.android.com/studio/preview/install-preview#change_your_update_channel). -## 在减少开销的同时保持原本设计原则 +## Preserving Design Principles While Reducing Overhead -Android 模拟器的最大好处在于为开发者提供了一种可扩展的方法,通过各种设备配置和屏幕分辨率来测试最新 Android API,而无需为每个配置购买物理设备。因此,在 Android 模拟器上测试应用程序应该尽可能贴近在物理设备上的测试,并同时保持虚拟设备的优势。 +The main benefit of the Android Emulator is to provide developers a scalable way to test the latest Android APIs across a variety of device configurations and screen resolutions, without buying physical devices for every configuration. As such, testing apps on Android Emulator should be as close as possible as testing on a physical device, while keeping the benefits of virtual devices. -为了支持最新的系统映像,我们特意设计一个尽可能接近物理设备的 Android 模拟器,而不只是一个仿真器,这种方法可以确保 API 的正确性以及 Android 系统行为和交互的高保真度。当一个新的 Android 版本推出时,我们只需要确保我们的硬件抽象层(HALs)和内核与模拟器和新的系统映像兼容,而不需要从头开始为新的 Android 版本重新实现 Android API 中的所有更改。这种体系结构最终大大地加快了模拟器采用新的系统映像的速度。 +In order to support the latest system images as soon as they are developed, we intentionally decided to design Android Emulator to be as close to a physical device as possible, as an emulator not a simulator. This approach ensures API correctness and high fidelity of Android system behaviors and interaction. When a new Android version comes out, we only need to ensure that our hardware abstraction layers (HALs) and kernel are compatible with the emulator and new system images, rather than have to re-implement all changes in the Android API for the new Android version from scratch ourselves. The net result of this architecture is that it greatly speeds up adoption of new system images for emulators. -然而,这种完整的系统模拟方法在 CPU 周期和内存访问上的开销都会增加。相比之下,基于模拟器的方法在主机系统上包装类似的 API,开销可能会更低。因此,我们的挑战在于,在降低 CPU 和内存开销的同时,保持完整系统模拟的准确性和维护优势。 +However, such a full system emulation approach imposes overhead in both CPU cycles and memory access. In contrast, a simulator based approach would wrap analogous APIs on the host system with possibly less overhead. Therefore, our challenge is to preserve the accuracy and maintenance benefits of full system emulation while reducing the CPU and memory overhead. -## 对 Android 模拟器架构的研究 +## Investigation into the Android Emulator Architecture -Android 模拟器在称为 Android 虚拟设备(AVD)的虚拟机上运行 Android 操作系统。AVD 包含了完整的 [Android 软件栈](https://source.android.com/devices/architecture),运行时就像在物理设备上一样。总体架构图如下。 +The Android Emulator runs the Android operating system in a virtual machine called an Android Virtual Device (AVD). The AVD contains the full [Android software stack](https://source.android.com/devices/architecture), and it runs as if it were on a physical device. The high-level architecture diagram is as follows. ![**Android Emulator System Architecture**](https://cdn-images-1.medium.com/max/2262/0*H8Y7VKtH1vckbx5M) -由于整个 Android 操作系统的运行和主机的操作系统完全分离,因此运行 Android 模拟器可能会导致主机机器上的后台活动,即便没有任何输入。在进行了一些技术调查之后发现,当 AVD 空闲时,如下一些任务是 CPU 周期的主要消耗者: +Since the entire Android OS runs separately from the host OS, running the Android Emulator can cause background activity on the host’s machine even without any user input. After doing some technical investigation, the following tasks were some of the major consumers of CPU cycles when an AVD is idle: -* Google Play Store —— 当有新版本时,应用程序会自动更新。 -* 后台服务 —— 当它认为设备在充电时,一些响应式的服务会使 CPU 使用率保持在较高水平。 -* 动画 —— 例如[实况壁纸](https://android-developers.googleblog.com/2010/02/live-wallpapers.html) +* Google Play Store — app updates happen automatically when new versions are available. +* Background services — several on-demand services kept the CPU usage high when it assume the device was charging. +* Animations — such as [Live wallpapers](https://android-developers.googleblog.com/2010/02/live-wallpapers.html). -对于这些领域我们进行了更深入的技术研究并找到了以下 5 个解决方案来优化 Android 模拟器。 +For these areas we did a deep set of technical investigations and landed on the following top five solutions to optimize the Android Emulator. -1. 默认电池模式 -2. 模拟器的暂停/恢复 -3. 减少绘制调用的开销 -4. 减少 macOS 上主循环的 IO 开销 -5. Headless 构建 +1. Battery mode by default +2. Emulator pause/resume +3. Draw call overhead reduction +4. macOS main loop IO overhead reduction +5. Headless Build -### 改进 #1 —— 默认电池模式 +> Improvement #1 — Battery mode by default -之前,Android 模拟器把 AVD 的电池模式设置为[充电模式](https://developer.android.com/reference/android/os/BatteryManager.html#BATTERY_STATUS_CHARGING)。经过深思熟虑的讨论和数据分析,我们得出结论,最好将 AVD 默认设置为电池模式。因为大多数 Android framework,服务和应用程序都经过了优化以节省电池寿命,这些优化都只在设备(物理设备或虚拟设备)认为它在使用电池而不是充电时才开始。 +Previously, the Android Emulator set the AVD’s battery charging mode to be on [AC power](https://developer.android.com/reference/android/os/BatteryManager.html#BATTERY_STATUS_CHARGING). After thoughtful discussions and data analysis, we concluded that it is best to set an AVD on battery mode by default. This is because most of the Android framework, services and apps are optimized to save battery life, and these optimizations only kick in if the device (either physical or virtual) thinks it’s using the battery rather than charging off AC power. -然而,仅仅默认 AVD 使用电池还不够。因为处于电池模式会导致屏幕在一段时间之后自动关闭。这对于在笔记本电脑或者台式机上使用 Android 模拟器的用户来说会有一点困惑,因为他们期望应用程序不会随机进入睡眠状态,需要被唤醒。为了防止这种情况,Android 模拟器将在每次冷启动完成时用 [ADB shell 命令](https://developer.android.com/reference/android/provider/Settings.System#SCREEN_OFF_TIMEOUT)将屏幕关闭的时间设置为最大值(~24 天)。 +However, it’s not enough to just default the AVD to use battery. That’s because being on battery mode also causes the screen to turn off automatically after a period of time. This can be confusing to users who are using the emulator on laptops or desktop, where there is an expectation that apps don’t randomly go to sleep and need to be woken up. To avoid this condition, Android Emulator will set screen off timeout using an [ADB shell command](https://developer.android.com/reference/android/provider/Settings.System#SCREEN_OFF_TIMEOUT) to the maximum (~24 days) at each cold boot complete. -有了这些改变,Google Play Store 不会在电池模式再自动更新应用程序,避免了系统开销。然而,在切回充电模式之后,[应用程序的自动升级] (https://support.google.com/googleplay/answer/113412?hl=en) 仍然可以被触发。这实际上让开发者可以控制何时自动更新应用程序。这可以防止对关键用例的干扰,比如当用户只想构建和测试单个应用程序的时候。下表比较了电池模式和充电模式下的 CPU 使用状况: +With these changes, Google Play Store will not update apps automatically on the battery mode, and avoid overloading the system. However, [the auto-updating of apps](https://support.google.com/googleplay/answer/113412?hl=en) can still be triggered by switching back to AC charging mode. This actually gives developers control on when to update apps automatically. Which can prevent interference in critical use cases, such as when the user simply wants to build and test a single app. The following chart compares CPU usage on battery versus on AC power: ![**AVD CPU Usage: Auto-update app vs Idle**](https://cdn-images-1.medium.com/max/2444/0*gt4ov7MOkjcvhFYP) -### 改进 #2 —— 模拟器暂停/恢复 +> Improvement #2 — Emulator pause/resume -在很多情况下,你可能需要立即保证模拟器不会在关键任务期间(比如编辑/生成/部署)在后台占用 CPU 周期。为了解决这个问题,我们正在研究一个控制台命令和接口,用于完全暂停模拟器 CPU 的使用。这可以通过以下控制台命令显示暂停/恢复 AVD 来完成。 +In many cases, you may want an immediate guarantee that the emulator isn’t chewing up CPU cycles in the background during critical tasks such as the edit and build steps of the edit / build / deploy loop. To address this, we’re working on a console command and interface for pausing the emulator’s CPU usage completely. This can be accomplished by following console commands to pause/resume the AVD explicitly. ![**Android Emulator: Pause command line options**](https://cdn-images-1.medium.com/max/2808/1*Q77jcfo5jiRqRwhW2l2NgA.png) -这里的挑战是如何协调 Android Studio 和 Android 模拟器状态的改变。所以当在部署应用程序时,我们会自动恢复模拟器。我们还在研究这个机制,很高兴听到您的[想法和反馈](https://source.android.com/setup/contribute/report-bugs#developer-tools)。 +The challenge here is how to coordinate this Android Emulator state change with Android Studio. So when an app deploy happens, we auto resume the emulator. We are still working on this mechanism, and happy to hear your [thoughts and feedback](https://source.android.com/setup/contribute/report-bugs#developer-tools). -### 改进 #3 —— 减少绘制调用的开销 +> Improvement #3 — Draw call overhead reduction -我们还对 Android 模拟器的引擎进行了修改,使其更高效的绘图,从而在测试屏幕上有很多对象的图形密集型应用程序时获得更流畅的用户体验。比如,模拟器 v28.1.10 在[GPU 模拟压力测试应用程序](https://github.com/google/gpu-emulation-stress-test)上的绘制速度比 v28.0.23 提升了 8%。我们还在 Android Q 上进行进一步的优化,并将在 [Android Q preview](https://developer.android.com/preview) 期间共享其他更新。 +We’ve also made changes in the Android Emulator engine that make it more efficient at drawing, which results in a smoother user experience when testing graphics-heavy apps with many objects on screen. For example, Emulator v28.1.10 draws 8% faster on [GPU emulation stress test app](https://github.com/google/gpu-emulation-stress-test) compared with that in v28.0.23. We are also working on further optimizations in Android Q, and will share additional updates during the [Android Q preview](https://developer.android.com/preview). ![**Emulator OpenGL ES FPS: 28.0.23 vs 28.1.10**](https://cdn-images-1.medium.com/max/3200/0*9SgQAdVAIYAHR_eD) -### 改进 #4 —— 减少 macOS 上主循环的 IO 开销 +> Improvement #4 — macOS main loop IO overhead reduction -完整的系统模拟器必须维护一些方法,以通知虚拟操作系统磁盘和网络上的 I/O 已经完成。Android 模拟器基于 [QEMU](https://www.qemu.org/),使用主循环和 IO 线程来做到这一点。这在 Linux 和 Windows 上的开销都比较低。然而在 macOS 上我们看到,由于使用了 select() 系统调用,主循环的 CPU 使用率更高。这通常没有高效的实现方式。macOS 提供了一个低开销的方式来等待 I/O:[kqueue](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html)。我们发现当前基于 select() 主 I/O 循环,可以替换为基于 [kqueue](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html) 的主 I/O 循环。这大幅降低了主循环中的 CPU 使用率,从 10% 降低到 3%。由于这并不能说明所有空闲 CPU 使用率的情况,下面的图表没有显示太多的变化。然而,这种差异仍然是可以观察到的。 +A full system emulator must maintain some kind of method to notify the virtual OS that I/O operations on disk & network complete. Android Emulator is based on [QEMU](https://www.qemu.org/), and uses a main loop and iothreads to accomplish this. It has low overhead on Linux and Windows. However on macOS, we have seen higher CPU usage of the main loop due to its usage of the select() system call. Which is often not implemented efficiently. macOS does provide a low overhead method to wait on I/O: [kqueue](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html). We’ve found that the main I/O loop, that is currently based on select() can be replaced with a main I/O loop based on [kqueue](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/kqueue.2.html). This greatly decreased the CPU usage of the main loop, from ~10% to ~3%. Since this does not account for all idle CPU usage, the chart (below) does not show much change. Nevertheless, the difference is still observable. ![AVD Idle CPU Usage — Emulator 28.0.23 vs 28.1.10](https://cdn-images-1.medium.com/max/2444/0*O_gCbgpsbOadRFV9) -### 改进 #5 —— Headless 构建 +> Improvement #5 — Headless Build -对于那些在 Android 应用程序构建中使用持续集成系统的用户,我们也在这方面进行了性能改进。通过关闭 Android 模拟器的用户界面,您可以使用新的模拟器 Headless 模式。这种新的模式在后台运行测试,并使用更少的内存。它大概还需要 100MB,主要是因为我们在用户界面使用的 [Qt](https://www.qt.io/) 库没有加载。当不需要用户界面和交互时,这也是运行自动化测试的一个好选择。增量可以类似如下那样启动两个模拟器 AVD 实例来测量。注意,命令行示范显式地指定主机的 GPU 模式,以确保在相同的条件下进行比较。 +For those of you using continuous integration systems for your Android app builds, we also worked on performance improvements in this area as well. By turning off the user interface in the Android Emulator, you can use access a new emulator-headless mode. This new mode runs tests in the background and uses less memory. It also takes about 100MB less, mainly because the [Qt](https://www.qt.io/) libraries we use for the user interface are not loaded. This is also a good choice to run automated tests when UI and user interactions are not required. The delta can be measured by starting 2 Emulator AVD instances as follows. Note that, the command line example specifies host GPU mode explicitly to ensure the comparison is under the same conditions. ![**Android Emulator: Headless emulator command line option**](https://cdn-images-1.medium.com/max/2808/1*qhp25FXwP_K4gE8ggOQQbQ.png) ![**AVD Idle Memory Usage — emulator vs emulator-headless**](https://cdn-images-1.medium.com/max/2402/0*DZ20pZNiqKnaydzW) -### 接下来 +> Next Steps -要使用本文中介绍的性能和资源优化,请在 [Canary Channel](https://developer.android.com/studio/preview/install-preview#change_your_update_channel) 下载 Android Emulator 28.1。我们很高兴能与您分享这次提前的进展,但我们肯定还没有完成。我们今天邀请您尝试 Android Emulator 的最新更新,并向我们发送您的[反馈](https://developer.android.com/studio/report-bugs.html#emulator-bugs)。 +To use the performance and resource optimization covered in this blog, download Android Emulator 28.1 available today on [the canary channel](https://developer.android.com/studio/preview/install-preview#change_your_update_channel). We are excited to share this early checkin-in on the progress with you, but we are definitely not done yet. We invite you to try the latest updates of Android Emulator today, and send us your [feedback](https://developer.android.com/studio/report-bugs.html#emulator-bugs). > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 50f03e5f28a62a9863e569012fdc17a03688b3ff Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 55/58] Revert "Create absolute-truths-unlearned-as-junior-developer.md (#6183)" This reverts commit bd090595405854720cdbce6357630317f52be49a. --- ...te-truths-unlearned-as-junior-developer.md | 186 ------------------ 1 file changed, 186 deletions(-) delete mode 100644 TODO1/absolute-truths-unlearned-as-junior-developer.md diff --git a/TODO1/absolute-truths-unlearned-as-junior-developer.md b/TODO1/absolute-truths-unlearned-as-junior-developer.md deleted file mode 100644 index f3f150099a1..00000000000 --- a/TODO1/absolute-truths-unlearned-as-junior-developer.md +++ /dev/null @@ -1,186 +0,0 @@ -> * 原文地址:[7 absolute truths I unlearned as junior developer](https://monicalent.com/blog/2019/06/03/absolute-truths-unlearned-as-junior-developer/) -> * 原文作者:[Monica Lent](https://monicalent.com/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/absolute-truths-unlearned-as-junior-developer.md](https://github.com/xitu/gold-miner/blob/master/TODO1/absolute-truths-unlearned-as-junior-developer.md) -> * 译者: -> * 校对者: - -# 7 absolute truths I unlearned as junior developer - -![thumbnail](https://monicalent.com/images/typing-with-flowers.jpg) - -Next year, I’ll be entering my 10th year of being formally employed to write code. Ten years! And besides actual employment, for nearly 2⁄3 of my life, I’ve been building things on the web. I can barely remember a time in my life where I didn’t know HTML, which is kind of weird when you think about it. Some kids learn to play an instrument or dance ballet, but instead I was creating magical worlds with code in my childhood bedroom. - -In reflecting on this first decade of getting regularly paid money to type weird symbols into my Terminal, I wanted to take some time to share **some of the ways my thinking shifted over the years as a developer**. - -For today’s junior developers: Maybe you’ll find something here you currently believe, and get inspired to learn more about it and why the topic is so multi-faceted. Or maybe you’ll find this post encouraging because you’re already so far ahead of where I was at your stage. - -For today’s senior developers: Maybe you can relate, and also have some funny (and humbling) stories to share about your own life lessons when you were a junior dev. - -To be clear, **I think junior developers are awesome** and just showing up to learn already takes a ton of courage. This post is about my own journey and learnings, and isn’t meant to be a generalization about how all junior devs think or behave. - -I hope you enjoy the post and can relate a little bit 😄 - -> **Thanks to [Artem](https://twitter.com/iamsapegin) and [Sara](https://twitter.com/NikkitaFTW) for your feedback on this post!** - -## Absolute truths I unlearned as a junior developer - -### 1\. I’m a senior developer - -I was 19 years old when I applied for my first technical job. The position I was applying for was called “Student Webmaster”. Which is a pretty awesome job title, because you could be considered both a “student” and a “master” at the same time. Nowadays everyone wants to be an “engineer” because it sounds fancier, but if you ask me, “master” is where it’s at. Anyways, my job was to write PHP and MySQL, and maintain our Drupal website as well as building some internal tools. - -Since I’d been coding in my bedroom for a couple of years, I was pretty sure those years counted as “years of experience”. So when I was asked about how much experience I had writing PHP, I confidently answered, “3 or 4 years!” - -I thought I knew a lot about SQL because I could do outer joins 😎 - -And when I googled it, 3-4 years of experience meant I should be making 💰 - -Fast forward to my latest job, which I got after 5 years of “combined” student and professional experience (which I thought was the same as normal experience). Yet in that time, I basically never had my code reviewed. I deployed by ssh-ing into a server and running git pull. I’m rather sure I never had to open a Pull Request. Don’t get me wrong, I learned a ton of awesome stuff at my first two jobs, but I’d never really worked with other devs in the same codebase. And yet, I applied for a position for “Senior Frontend Engineer”, got an offer, and accepted it. - -**There I was, a senior developer at the ripe age of 24 years old.** - -I mean they wouldn’t have given me this job title if I wasn’t really senior, right?! Surely, my impressive experience had brought me to this point, and people should listen to me!! Already at the pinnacle of my technical career, and the youngest developer in the office. - -Like a boss 💅 - -> #### What I eventually learned -> -> **Not all experience is created equal.** My experience coding in my bedroom, working as a student, working in CS research, and working at a growing startup are all valuable kinds of experience. But they aren’t all the same. Early in your career, you can learn 10x more in a supportive team in 1 year, than coding on your own (or with minimal feedback) for 5 years. If your code is never reviewed by other developers, you will not learn as fast as you can – by an enormous factor. -> -> **That’s why mentors are so important**, and the team you work with is worth so much more than a couple bucks in your paycheck. Don’t accept a junior position where you’ll be working alone, if you can help it! And don’t accept your first role (or, honestly, any role) based on salary alone. The team is where the real value is. -> -> **I also learned that job titles don’t “make” you anything.** It’s kind of like, being a CTO with a 5-person team is different than with a 50-person team or a 500-person team. The job and skills required are totally different, even if the title is identical. So just because I had a “senior” job title did not make me a senior engineer at all. Furthermore, hierarchical titles are inherently flawed, and difficult to compare cross-company. I learned it’s important not to fixate on titles, or use them as a form of external validation. - -### 2\. Everyone writes tests - -For the first half of my career, I worked in research. Specifically, I worked on an publicly-funded project for about 3 1⁄2 years, and then at a university at the NLP chair for a year and a half. I can tell you one thing: **programming in research is completely different than programming in the industry**. - -For the most part, you aren’t building applications. You’re working on algorithms or parsing data sets. Alternatively, if you are building an application, chances are your work is being publicly funded – which means it’s free for others to use and usually open-source. And when something is free, that means, for the most part, you are not **really** responsible to make sure it’s always perfectly available. - -Because, well, it’s free. - -You’re also not responsible to make any money or produce results, but that is an entirely different blog post ranting about being a developer in academia ✨ - -**Long story short, I left academia with lots of expectations.** - -Expectations about how the industry would work. There would be automated deployment. Pull requests and code review. It was going to be glorious! Finally the [code quality](#4-code-quality-matters-most) I had been longing for! But beyond quality code with **proper standards** and **best practices**, I strongly believed, **everyone in the software industry writes tests**. - -**Ahem.** - -So imagine my surprise when I showed up at my first day on the job at a startup and found no tests at all. No tests in the frontend. No tests in the backend. Just, no tests. - -Nada. Zip. Null. Undefined. NaN tests. - -Not only were there **no tests**, but no one seemed to have a problem with the lack of tests! With a bit of naivety, I assumed the reason there were no tests was because people just didn’t know how to write tests for AngularJS. If I taught them how, everything would be OK and we’d start to have tests. Wrong! Long story short, years and years later, we’ve made huge progress on adding automated tests to our code, and it wasn’t as straightforward as I thought it would be. - -But not because people didn’t know **how** to write the tests. - -They’d either never felt the pain of not having tests, or they’d felt the pain of having **legacy** tests. Two things I’d never experienced for myself. - -> #### What I eventually learned -> -> **Loads of companies and startups have little or no tests.** When struggling to find product market fit, or fighting for survival, a lot of companies neglect testing early on. Even companies that look fancy, sponsoring conferences or open-sourcing code – so many still have a big, gnarly monolith with minimal tests they need your help to improve. Ask devs who aren’t trying to recruit you to tell you about the state of the codebase. -> -> **No company has a perfect tech setup.** Every company has problems, every company has technical debt. The question is what they’re doing about it. We should have no illusions when applying for jobs that there is work to be done – or else they wouldn’t be hiring 😉 -> -> **Being overly opinionated on topics you lack real-world experience with is pretty arrogant.** I came across as SUCH a know-it-all, insisting there must be tests yet having hardly any experience on what that really looked like at scale. Don’t be like me. It’s important to have principles, but also to be open and truly interested to understand other people’s experiences and perspectives. - -### 3\. We’re so far behind everyone else (AKA “tech FOMO”) - -This one is closely related to the topic of unit testing. While my company didn’t have many unit tests, **surely all the other companies did, right?** - -I read so many blog posts. I watched conference talks on YouTube. I read “that orange website” all the damn time. It seemed like everyone was writing super sophisticated and high-quality applications with great performance and fancy animations, while I was just over here patching some stuff together trying to make it work in time for my deadline. - -I basically idolized all the other companies I was reading about, and felt disappointment that my own company and project was so behind. - -> #### What I eventually learned -> -> **Many conference talks cover proof of concepts rather than real-world scenarios.** Just because you see a conference talk about a specific technology, doesn’t mean that company is using that tech in their day to day work, or that all of their code is in perfect shape. Often people who give conference talks are presenting toy apps rather than real-world case studies, it’s important to distinguish the two. -> -> **Dealing with legacy is completely normal.** No but seriously, it’s easy to imagine that some other company doesn’t have legacy to handle. But after spending time at conferences talking to people who work at tippy top tech companies, it becomes clear that we are all in the same boat. What company DOESN’T have a huge PHP or Ruby monolith they’re trying to tame (or had to tame at some point)? Legacy code is normal, and learning to deal with it will often teach you more than building apps from scratch because you’ll be more exposed to concepts you don’t understand yet. - -### 4\. Code quality matters most - -Back in the day, **getting a code review from me could be brutal**. - -At least, I was really nitpicky about coding style. MY coding style, which happened to be a modified version of the Airbnb JavaScript styleguide, but conforming to my personal tastes. Things like indendetation, formatting, naming – god forbid you did it differently than I would have. Passing a code review without at least one comment would have involved both mind-reading and winning the lottery. - -Imagine 50+ comments on your PR with all the semicolons you missed! - -Because I had eyes like an eagle and this eagle wants those high-quality semicolons 🦅 - -(Luckily I no longer have eagle eyes after staring at the computer for many years, so you’re all spared – #kiddingnotkidding) - -> #### What I eventually learned -> -> **Good enough is good enough.** There’s a degree of diminishing returns when it comes to how “good” code needs to be. It doesn’t have to be perfectly clean to get the job done and not be a total disaster to maintain. Often code that is a little more repetitive or a tiny bit more verbose is easier for other people to understand. Also, “good code” is not the same as “code that looks like I wrote it”. -> -> **Architecture is more important than nitpicking.** While a small line of code could be improved, the stuff that tends to cause bigger problems down the line are usually architectural. I should’ve focused more on the structure of the application than tiny bits of code early on. -> -> **Code quality is important**, don’t get me wrong. But code quality wasn’t what I thought it was, which was things like linting and formatting or whatever style was promoted in the latest blog post I had read 🙈 - -### 5\. Everything must be documented!!!! - -When I entered my first company, it was honestly the first time I was working a lot with code other people had written. Sure, I had done it a little bit at my first job, but I never really had to come into an existing codebase and to figure out what the heck was going on. That’s because the one time that happened, I rewrote all the code instead of trying to figure out how it worked. - -Anyways. - -It didn’t help that it was AngularJS code written by Ruby developers, or that I was a junior developer who didn’t know she was junior 🕵🏻‍♀️ - -So how did I handle the fact that 300 lines of unfamiliar code made me feel like I was drowning? - -JSDOC. EVERYWHERE. - -I started commenting **everything** just to try to make sense out of it. Annotations for every function I could get my hands on. - -I learned all that fancy Angular-specific JSDoc syntax. My code was always twice as long because it had so much documentation and so many comments 👌 - -> #### What I eventually learned -> -> **Documentation lies sometimes.** It’s easy to think that documentation is a cure-all solution. “We need docs!” While I didn’t come to the conclusion that just because documentation is hard work, doesn’t mean it’s not worth doing at all, I learned that you have to document the right things in the right way. Over-documentation of the wrong things tends to lead to staleness, which can be just as confusing to people who are trying to fix an issue. -> -> **Focus on automation over documentation where appropriate.** Tests or other forms of automation are less likely to go out of sync. So instead I try to focus on writing good tests with clear language, so developers working on code I wrote are able to see how the project functions with working code. Another example is automating the installation of an application with a few comments, rather than a long and detailed installation guide. - -### 6\. Technical debt is bad - -If you thought I was neurotic from the last point, just wait until this one! For a while in my career, I thought that any code I considered “messy” was in fact **technical debt**. Technical debt is a funny term because if you ask people to give you an example of what it is, there are so many different things that it could be. - -So as someone who viewed any kind of “disorderly” code as technical debt, I immediately tried to eliminate it with the utmost rigor! - -I literally once spent a weekend manually fixing 800 linting errors. - -That’s how neurotic I was. - -**(Disclaimer: This was before auto-fixing was a thing)** - -> #### What I eventually learned -> -> **Disorganized or messy code isn’t the same as technical debt.** Just because something doesn’t “feel nice” doesn’t mean it’s technical debt. Technical debt actually slows you down in some way, or makes certain kinds of changes difficult or error prone. If the code is just a little messy, it’s just a little messy. Tidying that up might not be worth my time. -> -> **Having some technical debt is healthy.** Sometimes we take a shortcut because we need to borrow time, and for that we give up some of our speed in the future. Having pieces of code that are in fact “technical debt” is okay, so long as you recognize you’ll likely need to pay that debt back. If you think your codebase is free of technical debt, there is a good chance you’re over-emphasizing **polish** instead of **delivery**. And boy did I do that! - -### 7\. Seniority means being the best at programming - -Having started at a rather young age to code, I’ve probably been proficient at doing for-loops for like 15+ years. Programming itself is like breathing to me. When a solution is apparent, I can just type away and the code will follow. It’s like writing a blog post or an email. I could code the solution faster than others, and typically took on the more complex projects for myself. - -For a long time I thought that was what it meant to to be a senior developer. - -Because why not? The job title is “senior developer”, not “senior communicator” or “senior project manager”. I didn’t really understand how many other skills I could possibly need to develop in order to be truly senior. - -> #### What I eventually learned -> -> **Senior engineers must develop many skills besides programming.** The sheer number of skills I’ve had to develop in the mean time are astronomical, compared to what I came in with. Ranging from communication and dependency management to sharing context, project management, estimation, and successfully collaborating with non-developer peers. These skills are less quantifiable and take a lot of trial and error to get right. -> -> **Not everyone will become “senior” during their career.** Seniority is the result of many accrued years of experience. And yet, years of experience is a necessary but not sufficient condition for seniority. It also has to be the right kind of experience in which you internalized the right lessons and successfully apply those learnings for the future. Sometimes bigger lessons can take a year or more to fully manifest – that’s why years of experience still matter, even if you’re a really good coder. -> -> **We’re all still junior in some areas.** No matter how much experience you have, there are still places where you don’t know much. Admitting what you don’t know is the first step to filling in that gap and getting help from people who are more experienced. - ---- - -**Bonus** – I really enjoyed this article called [On Being a Senior Engineer](https://www.kitchensoap.com/2012/10/25/on-being-a-senior-engineer/) . It’s a great read if you’re grappling with what point you’re at in your journey and find yourself wondering, “What does it mean to be senior?” - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 71464d470fcc315540f207ffbceda214897922c4 Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 56/58] =?UTF-8?q?Revert=20"Google=20=E7=9A=84=20Pagespeed?= =?UTF-8?q?=20=E7=9A=84=E5=B7=A5=E4=BD=9C=E5=8E=9F=E7=90=86=EF=BC=9A?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E4=BD=A0=E7=9A=84=E5=88=86=E6=95=B0=E5=92=8C?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=BC=95=E6=93=8E=E6=8E=92=E5=90=8D=20(#6153?= =?UTF-8?q?)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b54e7fd3fb3fd66a13488bc6a190fbebb96e025e. --- TODO1/how-pagespeed-works.md | 166 +++++++++++++++++------------------ 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/TODO1/how-pagespeed-works.md b/TODO1/how-pagespeed-works.md index 52c4fc7dc87..965179b540c 100644 --- a/TODO1/how-pagespeed-works.md +++ b/TODO1/how-pagespeed-works.md @@ -1,157 +1,157 @@ -> * 原文地址:[Google 的 Pagespeed 的工作原理:提升你的分数和搜索引擎排名](https://calibreapp.com/blog/how-pagespeed-works/) +> * 原文地址:[How Google Pagespeed works: Improve Your Score and Search Engine Ranking](https://calibreapp.com/blog/how-pagespeed-works/) > * 原文作者:[Ben Schwarz](https://calibreapp.com/blog/author/ben-schwarz) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-pagespeed-works.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-pagespeed-works.md) -> * 译者:[Jerry-FD](https://github.com/Jerry-FD/) -> * 校对者:[weberpan](https://github.com/weberpan/),[Endone](https://github.com/Endone/) +> * 译者: +> * 校对者: -# Google 的 Pagespeed 的工作原理:提升你的页面分数和搜索引擎排名 +# How Google Pagespeed works: Improve Your Score and Search Engine Ranking ![](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/1.png) -通过这篇文章,我们将揭开 PageSpeed 最为重要的页面速度评分的计算方法。 +In this article, we uncover how PageSpeed calculates it’s critical speed score. -毫无疑问,页面的加载速度已经成了提升页面收益和降低流失率的关键性因素。由于 Google 已经将页面的加载速度列入影响其搜索排名的因素,现在更多的企业和组织都把目光聚焦在提升页面性能上了。 +It’s no secret that speed has become a crucial factor in increasing revenue and lowering abandonment rates. Now that Google uses page speed as a ranking factor, many organisations have become laser-focused on performance. -去年 **Google 针对他们的搜索排名算法做了两个重大的调整**: +Last year **Google made two significant changes to their search indexing and ranking algorithms**: -* 三月,[搜索结果排名以移动端版本的页面为基础](https://webmasters.googleblog.com/2018/03/rolling-out-mobile-first-indexing.html),取代之前的桌面端版本。 -* [七月,SEO 排名算法](https://webmasters.googleblog.com/2018/01/using-page-speed-in-mobile-search.html)更新为,增加页面的加载速度作为影响其搜索排名的因素,如移动端页面排名[和广告排名。](https://developers.google.com/web/updates/2018/07/search-ads-speed#the_mobile_speed_score_for_ads_landing_pages) +* In March, [indexing became based on the mobile version of a page](https://webmasters.googleblog.com/2018/03/rolling-out-mobile-first-indexing.html), rather than desktop. +* [In July, the SEO ranking algorithm](https://webmasters.googleblog.com/2018/01/using-page-speed-in-mobile-search.html) was updated to include page speed as a ranking factor for both mobile pages [and ads.](https://developers.google.com/web/updates/2018/07/search-ads-speed#the_mobile_speed_score_for_ads_landing_pages) -通过这些改变,我们可以总结出两个结论: +From this, we’re able to state two truths: -* **手机端页面的加载速度会影响你整站的 SEO 排名。** -* 如果你的页面加载很慢,就会降低你的广告质量分,进而你的**广告费会更贵。** +* **The speed of your site on mobile will affect your overall SEO ranking.** +* If your pages load slowly, it will reduce your ad quality score, and **ads will cost more.** -Google 道: +Google wrote: -> 更快的加载速度不仅仅会提升我们的体验;最近的数据显示,提升页面的加载速度也会降低操作成本。和我们一样,我们的用户就很重视速度 — 这就是我们决定将页面的速度这一因素,加入搜索排名计算的原因。 +> Faster sites don’t just improve user experience; recent data shows that improving site speed also reduces operating costs. Like us, our users place a lot of value in speed — that’s why we’ve decided to take site speed into account in our search rankings. -为了从页面性能的角度搞清楚这些变化给我们带来了什么影响,我们需要掌握这些基础知识。[PageSpeed 5.0](https://developers.google.com/speed/docs/insights/release_notes) 是之前版本的一次颠覆性的改动。现在由 Lighthouse 和 [CrUX](https://developers.google.com/web/updates/2017/12/crux) 提供技术支持(Chrome 用户体验报告部)。 +To understand how these changes affect us from a performance perspective, we need to grasp the underlying technology. [PageSpeed 5.0](https://developers.google.com/speed/docs/insights/release_notes) is a complete overhaul of previous editions. It’s now being powered by Lighthouse and [CrUX](https://developers.google.com/web/updates/2017/12/crux) (Chrome User Experience Report). -**这次升级使用了新的评分算法,将会使获得 PageSpeed 高分更加困难。** +**This upgrade also brings a new scoring algorithm that makes it far more challenging to receive a high PageSpeed score.** -### PageSpeed 5.0 有哪些变化? +### What changed in PageSpeed 5.0? -5.0 之前,PageSpeed 会针对测试的页面给出一系列指导意见。如果页面里有很大的、未经压缩的图片,PageSpeed 会建议对图片压缩。再比如,漏掉了 Cache-Headers,会建议加上。 +Before 5.0, PageSpeed ran a series of heuristics against a given page. If the page has large, uncompressed images, PageSpeed would suggest image compression. No Cache-Headers missing? Add them. -这些建议是与一些**指导方针**对应的,如果遵从这些指导方针,**很可能**会提升你的页面性能,但这些也仅仅是表层的,它不会分析用户在真实场景下的加载和渲染页面的体验。 +These heuristics were coupled with a set of **guidelines** that would **likely** result in better performance if followed, but were merely superficial and didn’t actually analyse the load and render experience that real visitors face. -在 PageSpeed 5.0 中,页面在 Lighthouse 的控制下被载入到真实的 Chrome 浏览器中。Lighthouse 从浏览器中获取记录各项指标,把这些指标套入得分模型里计算,最后展示一个整体的性能分。根据具体的分数指标来给出优化的指导方针。 +In PageSpeed 5.0, pages are loaded in a real Chrome browser that is controlled by Lighthouse. Lighthouse records metrics from the browser, applies a scoring model to them and presents an overall performance score. Guidelines for improvement are suggested based on how specific metrics score. -和 PageSpeed 类似,Lighthouse 也有一个性能分。在 PageSpeed 5.0 中,性能分直接从 Lighthouse 里获取。所以**现在 PageSpeed 的速度分和 Lighthouse 的性能分一样了。** +Like PageSpeed, Lighthouse also has a performance score. In PageSpeed 5.0, the performance score is taken from Lighthouse directly. **PageSpeed’s speed score is now the same as Lighthouse’s Performance score.** -![Calibre 在 Google 的 Pagespeed 上获得了 97 分](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/calibre-pagespeed.png) +![Calibre scores 97 on Google’s Pagespeed](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/calibre-pagespeed.png) -既然我们知道了 PageSpeed 的分数从哪里来,接下来我们就来仔细研究它是如何计算的,以及我们该如何有效的提高页面的性能。 +Now that we know where the PageSpeed score comes from, let’s dive into how it’s calculated, and how we can make meaningful improvements. -### Google Lighthouse 是什么? +### What is Google Lighthouse? -[Lighthouse](https://calibreapp.com/blog/lighthouse-reasons/) 是一个开源项目,由一只来自 Google Chrome 的优秀团队运作。在过去的几年里,它已逐步变成免费的性能分析工具。 +[Lighthouse](https://calibreapp.com/blog/lighthouse-reasons/) is an open source project run by a dedicated team from Google Chrome. Over the past couple of years, it has become **the** go-to free performance analysis tool. -Lighthouse 使用 Chrome 的远程调试协议来获取网络请求的信息、计算 JavaScript 的性能、评估无障碍化级别以及计算用户关注的时间指标,比如 [首次内容绘制时间 First Contentful Paint](https://calibreapp.com/docs/metrics/paint-based-metrics)、[可交互时间 Time to Interactive](https://calibreapp.com/docs/metrics/time-to-interactive) 和速度指标。 +Lighthouse uses Chrome’s Remote Debugging Protocol to read network request information, measure JavaScript performance, observe accessibility standards and measure user-focused timing metrics like [First Contentful Paint](https://calibreapp.com/docs/metrics/paint-based-metrics), [Time to Interactive](https://calibreapp.com/docs/metrics/time-to-interactive) or Speed Index. -如果你想要深入了解 Lighthouse 的整体架构,请看来自官方的[教程](https://github.com/GoogleChrome/lighthouse/blob/master/docs/architecture.md)。 +If you’re interested in a high-level overview of Lighthouse architecture, [read this guide](https://github.com/GoogleChrome/lighthouse/blob/master/docs/architecture.md) from the official repository. -### Lighthouse 如何计算性能分数 +### How Lighthouse calculates the Performance Score -在性能测试中,Lighthouse 聚焦于用户所见和用户体验,记录了很多指标。 +During performance tests, Lighthouse records many metrics focused on what a user sees and experiences. -下面这 6 个指标构成了性能分数的大体部分。他们是: +There are 6 metrics used to create the overall performance score. They are: -* 可交互时间 Time to Interactive (TTI) -* 速度指标 Speed Index -* 首次内容绘制时间 First Contentful Paint (FCP) -* 首次 CPU 空闲时间 First CPU Idle -* 首次有效绘制 First Meaningful Paint (FMP) -* 预计输入延迟时间 Estimated Input Latency +* Time to Interactive (TTI) +* Speed Index +* First Contentful Paint (FCP) +* First CPU Idle +* First Meaningful Paint (FMP) +* Estimated Input Latency -Lighthouse 会针对这些指标运用一个 0 – 100 的分数模型。 这个过程会收集移动端第 75 和第 90 百分位的 [HTTP 档案](https://httparchive.org/),然后输入到`对数正太分布`函数(校对者注:这样的话只要性能数据低于 25% 的线上移动端页面,也就是排位在 75% 以下,都给 0 分,而只要比 95% 的移动端页面得分高,就得满分)。 +Lighthouse will apply a 0 – 100 scoring model to each of these metrics. This process works by obtaining mobile 75th and 95th percentiles from [HTTP Archive](https://httparchive.org/), then applying a `log normal` function. -[根据算法和可交互时间的计算所得数据](https://www.desmos.com/calculator/2t1ugwykrl),我们可以发现,如果一个页面在 2.1 秒内成为“可交互的”,那么它的可交互时间分数指标是 92/100。 +[Following the algorithm and reference data used to calculate Time to Interactive](https://www.desmos.com/calculator/2t1ugwykrl), we can see that if a page managed to become “interactive” in 2.1 seconds, the Time to Interactive metric score would be 92/100. ![](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/scoring-curve.png) -当每个指标完成计分后会被分配一个权重,用权重调整后算出页面整体的性能分数。权重规则如下: +Once each metric is scored, it’s assigned a weighting which is used as a modifier in calculating the overall performance score. The weightings are as follows: -| 指标 | 权重 | +| Metric | Weighting | | ------------------------- | --------- | -| 可交互时间 (TTI) | 5 | -| 速度指标 | 4 | -| 首次内容绘制时间 | 3 | -| 首次 CPU 空闲时间 | 2 | -| 首次有效绘制 | 1 | -| 预计输入延迟时间 | 0 | +| Time to Interactive (TTI) | 5 | +| Speed Index | 4 | +| First Contentful Paint | 3 | +| First CPU Idle | 2 | +| First Meaningful Paint | 1 | +| Estimated Input Latency | 0 | -这些权重取决于每个指标对移动端用户的体验的影响程度。 +These weightings refer to the impact of each metric in regards to mobile user experience. -在未来,这些权重在参考来自于 Chrome 用户体验报告的用户观测数据之后,还可能会被进一步优化。 +In the future, this may also be enhanced by the inclusion of user-observed data from the Chrome User Experience Report dataset. -你可能想知道究竟这每一个指标的权重是如何影响整体得分的。Lighthouse 团队[打造了一款实用的 Google 电子表格计算器](https://docs.google.com/spreadsheets/d/1Cxzhy5ecqJCucdf1M0iOzM8mIxNc7mmx107o5nj38Eo/edit#gid=0)来阐述具体的细节: +You may be wondering how the weighting of each metric affects the overall performance score. The Lighthouse team [have created a useful Google Spreadsheet calculator](https://docs.google.com/spreadsheets/d/1Cxzhy5ecqJCucdf1M0iOzM8mIxNc7mmx107o5nj38Eo/edit#gid=0) explaining this process: -![这张电子表格的图片可以用来计算性能分数](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/weightings.png) +![Picture of a spreadsheet that can be used to calculate performance scores](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/weightings.png) -使用上面的例子,如果我们把可交互时间从 5 秒 变为 17 秒 (全球移动端平均 TTI),我们的分数会降低到 56% (也就是 100 分之中的 56 分)。 +Using the example above, if we change (time to) interactive from 5 seconds to 17 seconds (the global average mobile TTI), our score drops to 56% (aka 56 out of 100). -然而,如果我们把首次内容绘制时间变为 17 秒,我们的分数会是 62%。 +Whereas, if we change First Contentful Paint to 17 seconds, we’d score 62%. -**可交互时间 (TTI) 是对你的性能分数影响最大的指标。** +**Time to Interactive (TTI) is the most impactful metric to your performance score.** -因此,想要得到 PageSpeed 的高分,你**最需要**的是降低 TTI。 +Therefore, to receive a high PageSpeed score, you will **need** a speedy TTI measurement. -### 剑指 TTI +### Moving the needle on TTI -深入来说,有两个对 TTI 影响极大的重要因素: +At a high level, there are two significant factors that hugely influence TTI: -* 传输到页面的 JavaScript 代码的总大小 -* 主线程上 JavaScript 的运行时间 +* The amount of JavaScript delivered to the page +* The run time of JavaScript tasks on the main thread -我们的[可交互时间](https://calibreapp.com/blog/time-to-interactive/)文章详细说明了 TTI 的工作原理,但如果你想要一些快速无脑的优化,我们建议: +Our [Time to Interactive](https://calibreapp.com/blog/time-to-interactive/) guide explains how TTI works in great detail, but if you’re looking for some quick no-research wins, we’d suggest: -**降低 JavaScript 总大小** +**Reducing the amount of JavaScript** -尽可能地,移除无用的 JavaScript 代码,或者只传输当前页面会执行的代码。这可能意味着要移除老的 polyfills 或者尽量采用更小、更新的第三方库。 +Where possible, remove unused JavaScript code or focus on only delivering a script that will be run by the current page. That might mean removing old polyfills or replacing third-party libraries with smaller, more modern alternatives. -你需要记住的是 [JavaScript 花费的](https://medium.com/reloading/javascript-start-up-performance-69200f43b201) 不仅仅是下载它所需要的时间。浏览器需要解压、解析、编译然后才最终执行,这些过程都会消耗不容忽视的时间,尤其在移动设备上。 +It’s important to remember that [the cost of JavaScript](https://medium.com/reloading/javascript-start-up-performance-69200f43b201) is not only the time it takes to download it. The browser needs to decompress, parse, compile and eventually execute it, which takes non-trivial time, especially in mobile devices. -能降低你的页面脚本总大小的有效措施是: +Effective measures for reducing the amount of script from your pages: -* 检查并移除对你的用户来说并不需要的 polyfills。 -* 搞清楚每一个第三方 JavaScript 库所花费的时间。使用 [webpack-bundle-analyser](https://www.npmjs.com/package/webpack-bundle-analyzer) 或者 [source-map-explorer](https://www.npmjs.com/package/source-map-explorer) 来可视化分析他们的大小。 -* 现代 JavaScript 工具(比如 webpack)可以把大的 JavaScript 应用分解成许多小的 bundles,随着用户的浏览而动态加载。这就是所谓的 [code splitting](https://webpack.js.org/guides/code-splitting/),它会**极大地优化 TTI。** -* [Service workers 会缓存解析和编译后所得的字节码](https://v8.dev/blog/code-caching-for-devs)。如果善加利用这个特性,用户只需花费一次解析和编译代码带来的时间损耗,在那之后的结果就会被缓存优化。 +* Review and remove polyfills that are no longer required for your audience. +* Understand the cost of each third-party JavaScript library. Use [webpack-bundle-analyser](https://www.npmjs.com/package/webpack-bundle-analyzer) or [source-map-explorer](https://www.npmjs.com/package/source-map-explorer) to visualise the how large each library is. +* Modern JavaScript tooling (like Webpack) can break-up large JavaScript applications into a series of small bundles that are automatically loaded as a user navigates. This approach is known as [code splitting](https://webpack.js.org/guides/code-splitting/) and is **extremely effective in improving TTI.** +* [Service workers will cache the bytecode result of a parsed + compiled script](https://v8.dev/blog/code-caching-for-devs). If you’re able to make use of this, visitors will pay a one-time performance cost for parse and compilation, after that it’ll be mitigated by cache. -### 监控可交互时间 +### Monitoring Time to Interactive -为了较好的展示用户体验的差异性,我们建议使用监控系统(比如 [Calibre](https://calibreapp.com/)),它可以测试页面在两个不同设备上的最小评分;一个较快的桌面端设备和一个中等速度的移动端设备。 +To successfully uncover significant differences in user experience, we suggest using a performance monitoring system (like [Calibre](https://calibreapp.com/)!) that allows for testing a minimum of two devices; a fast desktop and a low-mid range mobile phone. -这样的话,你就可以得到你的用户可能体验到的最好和最差两种情况下的数据。是时候意识到,你的用户并没有使用和你一样强大的设备了。 +That way, you’ll have the data for both the best and worst case of what your customers experience. It’s time to come to terms that your customers aren’t using the same powerful hardware as you. -### 深度剖析 +### In-depth manual profiling -为了获得剖析 JavaScript 性能的最好结果,可以刻意使用较慢的移动设备来测试你的页面。如果你的抽屉里有一部老手机,你会发现一片新的天地。 +To get the best results in profiling JavaScript performance, test pages using intentionally slow mobile devices. If you have an old phone in a desk drawer, this is a great second-life for it. -Chrome DevTools 的硬件仿真模块可以很好的替代真实设备来进行测试,我们写了一个详细的[性能剖析指南](https://calibreapp.com/blog/react-performance-profiling-optimization/)来帮你开始学习分析运行时的性能。 +An excellent substitute for using a real device is to use Chrome DevTools hardware emulation mode. We’ve written an extensive [performance profiling guide](https://calibreapp.com/blog/react-performance-profiling-optimization/) to help you get started with runtime performance. -## 其他的指标呢? +## What about the other metrics? -速度指标、首次内容绘制时间和首次有效绘制都是以浏览器绘制为基础的指标。他们的影响因素很相似,往往可以被同时优化。 +Speed Index, First Contentful Paint and First Meaningful Paint are all browser-paint based metrics. They’re influenced by similar factors and can often be improved at the same time. -显然,优化这些指标会相对比较容易,因为他们是通过记录页面的渲染速度来计算的。仔细遵从 Lighthouse 的性能考核准则就能优化这些指标。 +It’s objectively easier to improve these metrics as they are calculated by how quickly a page renders. Following the Lighthouse Performance audit rules closely will result in these metrics improving. -如果你还没有对字体进行预加载或者优化那些关键请求,那从这里入手会是一些很好的切入点。我们的文章,[关键请求](https://calibreapp.com/blog/critical-request/),详细说明了浏览器针对你的页面是如何发起请求以及渲染关键资源的。 +If you aren’t already preloading your fonts or optimising for critical requests, that is an excellent place to start a performance journey. Our article, [The Critical Request](https://calibreapp.com/blog/critical-request/), explains in great detail how the browser fetches and renders critical resources used to render your pages. -## 跟踪过程做出优化 +## Tracking your progress and making meaningful improvements -Google 最近更新了搜索控制台、Lighthouse 和 PageSpeed Insights 针对你的页面的首屏的性能分析有独到之处,但是对于那些需要持续跟踪页面来提升页面性能的团队来说,就显得捉襟见肘了。 +Google’s newly updated search console, Lighthouse and PageSpeed Insights are a great way to get initial visibility into the performance of your pages but fall short for teams who need to continuously track and improve the performance of their pages. -[持续的性能监控](https://calibreapp.com/features) 可以保证速度优化,当页面又变差的时候团队也会立刻知晓。人为的测试会对结果引入大量的不可预期的变量,在不同区域、不同设备上的测试在没有专业的实验室环境下几乎是不可能完成的。 +[Continuous performance monitoring](https://calibreapp.com/features) is essential to ensuring speed improvements last, and teams get instantly notified when regressions happen. Manual testing introduces unexpected variability in results and makes testing from different regions as well as on various devices nearly impossible without a dedicated lab environment. -速度已经变成影响了 SEO 排名的关键因素,尤其是目前大约 50% 的页面流量来自于移动设备。 +Speed has become a crucial factor for SEO rankings, especially now that nearly 50% of Web traffic comes from mobile devices. -为了避免排名下降,确保你正在使用最新的性能分析套件来跟踪你的关键页面(哈,我们打造了 [Calibre](https://calibreapp.com/blog/release-notes-lighthouse-4/) 来做你的性能提升伙伴。他以 Lighthouse 为基础。每天都有很多来自全球的团队在使用它)。 +To avoid losing positioning, ensure you’re using an up-to-date performance suite to track key pages (pssst, we built [Calibre](https://calibreapp.com/blog/release-notes-lighthouse-4/) to be your performance companion. It has Lighthouse built-in. Hundreds of teams from around the globe are using it every day). -### 相关文章 +### Related Articles * [About Time to Interactive](https://calibreapp.com/blog/time-to-interactive/) * [How to optimise the performance of a JavaScript application](https://calibreapp.com/blog/react-performance-profiling-optimization/) From 7af0b60a01b17b2daab9906070fc99e34d6ad9cb Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 57/58] Revert "Create the-problem-with-web-components.md (#6181)" This reverts commit c13adf7e388b4071b20d6918627f541d60cb38ec. --- TODO1/the-problem-with-web-components.md | 199 ----------------------- 1 file changed, 199 deletions(-) delete mode 100644 TODO1/the-problem-with-web-components.md diff --git a/TODO1/the-problem-with-web-components.md b/TODO1/the-problem-with-web-components.md deleted file mode 100644 index 8c5a12022a0..00000000000 --- a/TODO1/the-problem-with-web-components.md +++ /dev/null @@ -1,199 +0,0 @@ -> * 原文地址:[The problem with web components](https://adamsilver.io/articles/the-problem-with-web-components/) -> * 原文作者:[Adam Silver](https://adamsilver.io/) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-problem-with-web-components.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-problem-with-web-components.md) -> * 译者: -> * 校对者: - -# The problem with web components - -[Web components](https://www.webcomponents.org/introduction) are becoming increasingly popular within the web community. They offer a way to standardise and encapsulate JavaScript-enhanced components without a framework. - -However, web components have a number of drawbacks. For instance, they have a number of technical limitations and are easy to misuse in a way that excludes users. - -It’s possible—and certainly my hope—that web components will improve over time and these issues will be resolved. But for now, I’m holding fire on them. - -In this article I’ll explain why that is, and suggest an alternative way to develop components in the meantime. - -## They are constraining - -In his [criticism of web components](https://thenewobjective.com/a-criticism-of-web-components/), Michael Haufe explains that: - -* custom CSS pseudo selectors can’t be used with web components -* they don’t work seamlessly with native elements and their associated APIs -* if we wanted to create a custom button, for example, we can’t extend the [HTMLButtonElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement) directly, we have to extend the [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) - -Additionally, web components have to be defined with ES2015 classes which means they can't be transpiled to give more people the enhanced experience. - -So, straight off the bat, there are a number of technical constraints to work around when it comes to using web components. - -## They are not widely supported - -Currently, web components have relatively poor cross-browser support, so the enhanced experienced won’t work for everyone. - -![Web component support on caniuse.com](https://adamsilver.io/assets/images/web-component-can-i-use.png) - -That doesn’t mean we can’t use them, it just means we’ll need to [provide a baseline experience that works for everyone else](https://adamsilver.io/articles/thinking-differently-about-progressive-enhancement/). That’s progressive enhancement. - -But we should think seriously about whether the choice to use web components is the most inclusive option. If we don’t use web components, we can provide the same rich experience to a significantly wider group of people. I’ll explain how later. - -Polyfills offer a way to provide broader support. But they are [slow, unreliable and hard to work](https://adamsilver.io/articles/the-disadvantages-of-javascript-polyfills/) with in general, and have a number of [specific limitations](https://www.webcomponents.org/polyfills#known-limitations) when used to make web components work more broadly. - -So while it may be preferable for us as code authors to use standards-based technologies, it’s not necessarily beneficial to our users—which should always be our first priority. - -## They are easily misunderstood and misused - -Jeff Atwood said that any application that can be written in JavaScript, will eventually be written in JavaScript. - -But just because we can use JavaScript to do something, doesn’t mean we should. There’s even a W3 principle that says we should use the [least powerful tool for the job](https://www.w3.org/2001/tag/doc/leastPower.html). - -Web components are made up of JavaScript APIs which means we should use them only when we need JavaScript. But as Jeff Atwood predicted, people sometimes use web components when they don’t need to. - -When we make JavaScript a dependency and don’t provide a fallback, users get a broken experience. Even [webcomponents.org](http://webcomponents.org), built using web components, shows a blank web page [when JavaScript isn’t available](https://kryogenix.org/code/browser/everyonehasjs.html). - -![Completely broken experience on webcomponents.org when experiencing a JavaScript failure.](https://adamsilver.io/assets/images/web-components-org-no-js.png) - -By the same token, it can encourage people to make components that request their data with AJAX and render themselves, like little iframes. - -This type of approach causes a number of avoidable issues which I’ll explain by way of an example. - -Imagine we want to load a table showing the sales figures for a product our website sells using AJAX like this: - -```html - -``` - -Firstly, it’s just a table. There’s no column sorting and therefore no need for JavaScript. Browsers provide the `` element for this exact purpose and it works everywhere. - -Secondly, as mentioned above, when a browser doesn’t support web components, or JavaScript fails to run, users won’t see anything. - -To make our table work in these situations, we would need to put a `
    ` inside ``. This is known as graceful degradation. - -```html - -
    ...
    - -``` - -If the component already has a populated table on the page when the page loads, wrapping `` around it gives us and our users nothing. - -Finally, using AJAX can introduce a number of usability and accessibility issues. - -1. [AJAX is often slower than a page refresh](https://jakearchibald.com/2016/fun-hacks-faster-content/), not faster. -2. We’ll need to create custom loading indicators, which are usually inaccurate and unfamiliar to users, unlike browsers’ loading indicators. -3. We’ll need to [make AJAX work cross-domain](https://zinoui.com/blog/cross-domain-ajax-request), which isn’t straightforward. -4. As the components load the page will jump around causing [visual glitches](https://twitter.com/chriscoyier/status/1057303249902952448) and potentially making users click the wrong thing. You may have heard about [skeleton interfaces](https://medium.com/@rohit971/boost-your-ux-with-skeleton-pattern-b8721929239f) as a way to solve this problem. They are placeholders put where the components will end up being shown once loaded. But while they help a bit, they don’t fully solve the problem because they can’t always predict the exact size of the content that will load. -5. Point 4 affects screen reader users too because they won’t know whether the components have loaded, have failed to load or are in the process of loading. ARIA live regions provide a way to communicate these states to screen readers. But when several components are being loaded, the user will be bombarded with announcements. - -Scale this up to several web components on a screen and we risk giving users a very unpleasant, exclusive and slow experience to contend with. - -Components that depend on AJAX requests to the server are no longer framework agnostic and therefore interoperable. This somewhat defeats the object of using web components, given that interoperability and technology agnosticism are 2 of the main benefits they aim to provide. - -Importantly, none of these problems are the fault of web components per se. We could easily develop components to work like this without web components. But, as demonstrated, it’s easy to misinterpret web components and unknowingly use them in a way that hurts both users and code authors. - -## They are hard to compose - -Let’s say we have just 2 web components. One for sortable tables and another for expandable rows. - -```html - - ...
    -
    - - - ...
    -
    -``` - -But if we want a sortable table with expandable rows then we need to nest the components like this: - -```html - - - ...
    -
    -
    -``` - -The relationship between `` and `` is unclear. For example, it’s hard to tell whether `` is operating on the `
    ` or the ``. - -The order matters, too. If each component enhances the table it could create a conflict. Also, it's not clear which component initialises first—the inside one or the outside one. - -**(Note: you may have heard about the `is` attribute as a way around this but Jeremy Keith explains that browsers aren’t going to implement this in [extensible web components](https://medium.com/@adactio/extensible-web-components-e794559b8c2e).)** - -## They can’t just be dropped into an application - -One of the supposed benefits of web components is that we can drop one script per component onto the page and they just work—regardless of the application or tech stack. - -But unlike standard elements, we may need to add additional code to get them to work properly. In some ways this is a bit like adding a framework or library. - -One example of this is polyfills which I mentioned earlier. If you choose to use a polyfill to provide broader support, then that code needs to be ready and waiting in your web page. - -Another example would be when you need to stop JavaScript-enhanced components from making the [page judder while initialising](https://twitter.com/adambsilver/status/1119123828884434945). - -This is usually fixed by adding a script in the `` of your document to [provide a hook for CSS](https://css-tricks.com/snippets/javascript/css-for-when-javascript-is-enabled/). This in turn is used to style the component based on JavaScript being available and avoids the page judder. - -This is perhaps of little consequence overall, but it does considerably negate one of the supposed benefits of using web components. - -## Framework agnostic components without web components - -You may have heard [web components being sold as an alternative to using frameworks](https://medium.com/@oneeezy/frameworks-vs-web-components-9a7bd89da9d4). - -While I’m in favour of creating interfaces without client-side frameworks, this is misleading for a number of reasons. - -Firstly, client-side frameworks usually provide additional features besides enhancing pieces of the interface. - -Secondly, web components can be used in tandem with frameworks. - -Lastly, we’ve been able to create JavaScript-enhanced components without frameworks and web components for a very long time. - -By creating components like this we can avoid the drawbacks I’ve described in this article. - -Let’s use the same sortable table and row expander to do this. - -Firstly, we need to create a JavaScript file for each component—the same as if we were using web components. We can define the `SortableTable` and `RowExpander` classes inside. - -```js -SortableTable.js // define SortableTable class and behaviour -RowExpander.js // define RowExpander class and behaviour -``` - -Once that’s done, we can initialise the components like this: - -```js -// grab table -var table = document.querySelector('table'); - -// initialise sortable table -var sortable = new SortableTable(table); - -// initialise row expander -var expander = new RowExpander(table); -``` - -We can make these components fire events just like web components. Something like this: - -```js -sortable.addEventListener(‘sort’, fn); -expander.addEventListener(‘expand’, fn); -``` - -By using regular JavaScript in this way, not only can we write clean code, free from technical constraints, but we get to give that code to a significantly wider user base. - -## In conclusion - -Web components hold a lot of promise because they give code authors a way to create interoperable components based on standards. - -As a result, it should be easier to understand other people’s code and create components that can be reused across projects. - -But even if we choose to provide enhancements exclusively for cutting edge browsers that support them, there’s still several limitations and issues we need to tackle. - -My hope is that web components get better in future. But until then, I’m sticking with regular JavaScript to avoid the current technical limitations and provide the most equitable experience to users. - -**Huge thanks to [Amy Hupe](https://amyhupe.co.uk/) who not only edited this article from top to bottom, but also made it as simple and inclusive as possible. Not an easy feat for an article on web components of all things.** 🙌 - -> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 - ---- - -> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From f63d7ec5b15abed35eaff753a8c903c5eb0eaeab Mon Sep 17 00:00:00 2001 From: Pingren Nie Date: Thu, 1 Aug 2019 00:35:52 +0800 Subject: [PATCH 58/58] =?UTF-8?q?Revert=20"=E4=BB=A3=E7=A0=81=E9=81=97?= =?UTF-8?q?=E6=BC=8F=E6=9B=B4=E6=AD=A3"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2bc5dabd453654e9a0e99c459e471aee47405f90. --- ...nc-programming-5-ways-to-better-coding-with.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md b/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md index 3d8e5587043..4c20825ec74 100644 --- a/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md +++ b/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md @@ -649,21 +649,6 @@ var response = await rp(‘https://api.example.com/endpoint1'); 2. **错误处理:** Async/await 使得我们可以使用相同的代码结构处理同步或者异步的错误 —— 著名的 try/catch 语句。让我们看看用 Promises 是怎么实现的: -```js -function loadData() { - try { // Catches synchronous errors. - getJSON().then(function(response) { - var parsed = JSON.parse(response); - console.log(parsed); - }).catch(function(e) { // Catches asynchronous errors - console.log(e); - }); - } catch(e) { - console.log(e); - } -} -``` - 对比: ```js